下面我们用一个具体的数字例子,从输入到输出一步步说明 Conv1d 是如何计算的。假设你有一条时间序列数据,其形状为
(batch*ts_d, seg_num, d_model) = (1*1, 4, 3)
(为了方便,我们这里设 batch*ts_d=1,即只有一个样本;seg_num=4 表示有 4 个分块,d_model=3 表示每个分块有 3 个特征。)
转置后,数据会变成形状
(1, d_model, seg_num) = (1, 3, 4)
也就是说,每个样本有 3 个通道,每个通道有 4 个时序位置。假设这 3 个通道的数据如下:
- 通道 0(特征 1):[1, 2, 3, 4]
- 通道 1(特征 2):[5, 6, 7, 8]
- 通道 2(特征 3):[9, 10, 11, 12]
我们选用一个 Conv1d 层,参数如下:
- in_channels = 3(输入通道数,即 d_model)
- out_channels = 2(输出通道数,我们随便设成 2)
- kernel_size = 3
- stride = 1
- padding = 1(为了保持输出长度与输入长度相同)
这样,Conv1d 内部会有 2 个卷积核,每个卷积核的大小为
(in_channels, kernel_size) = (3, 3)
此外,每个卷积核还对应一个偏置。
我们来指定这两个卷积核的权重和偏置为例(数字只是举例,实际训练中会随机初始化):
卷积核权重和偏置
输出通道 0 的卷积核权重:
- 对应通道 0 的权重:[1, 0, -1]
- 对应通道 1 的权重:[2, 1, 0]
- 对应通道 2 的权重:[0, -1, -2]
偏置:1
输出通道 1 的卷积核权重:
- 对应通道 0 的权重:[0, 1, 0]
- 对应通道 1 的权重:[1, 0, -1]
- 对应通道 2 的权重:[2, -1, 0]
偏置:0
由于我们设置了 padding=1,输入在序列两端会自动填充 0。例如,对于序列长度为 4 的数据,填充后实际序列索引变成:索引 -1、0、1、2、3、4,其中索引 -1 和 4 都为 0。
接下来,我们计算每个输出位置的值。注意:卷积核在每个时刻取连续 3 个位置的数据(kernel_size=3),计算公式为:
[
\text{输出} = \sum_{\text{通道}=0}{2}\sum_{k=0} (\text{输入}[c, i+k-\text{padding}] \times \text{权重}[c, k]) + \text{偏置}
]
计算过程
【输出通道 0】
-
位置 i = 0
卷积窗口覆盖输入索引:-1、0、1- 对通道 0:
数据:[0(填充), 1, 2]
权重:[1, 0, -1]
计算:0*1 + 1*0 + 2*(-1) = 0 + 0 - 2 = -2 - 对通道 1:
数据:[0, 5, 6]
权重:[2, 1, 0]
计算:0*2 + 5*1 + 6*0 = 0 + 5 + 0 = 5 - 对通道 2:
数据:[0, 9, 10]
权重:[0, -1, -2]
计算:0*0 + 9*(-1) + 10*(-2) = 0 - 9 - 20 = -29
总和: -2 + 5 - 29 = -26,再加上偏置 1,得到 -25。
- 对通道 0:
-
位置 i = 1
卷积窗口覆盖索引:0、1、2- 通道 0:
数据:[1, 2, 3]
权重:[1, 0, -1]
计算:1*1 + 2*0 + 3*(-1) = 1 + 0 - 3 = -2 - 通道 1:
数据:[5, 6, 7]
权重:[2, 1, 0]
计算:5*2 + 6*1 + 7*0 = 10 + 6 + 0 = 16 - 通道 2:
数据:[9, 10, 11]
权重:[0, -1, -2]
计算:9*0 + 10*(-1) + 11*(-2) = 0 - 10 - 22 = -32
总和: -2 + 16 - 32 = -18,加偏置 1,得到 -17。
- 通道 0:
-
位置 i = 2
卷积窗口覆盖索引:1、2、3- 通道 0:
数据:[2, 3, 4]
权重:[1, 0, -1]
计算:2*1 + 3*0 + 4*(-1) = 2 + 0 - 4 = -2 - 通道 1:
数据:[6, 7, 8]
权重:[2, 1, 0]
计算:6*2 + 7*1 + 8*0 = 12 + 7 + 0 = 19 - 通道 2:
数据:[10, 11, 12]
权重:[0, -1, -2]
计算:10*0 + 11*(-1) + 12*(-2) = 0 - 11 - 24 = -35
总和: -2 + 19 - 35 = -18,加偏置 1,得到 -17。
- 通道 0:
-
位置 i = 3
卷积窗口覆盖索引:2、3、4(索引4为填充0)- 通道 0:
数据:[3, 4, 0]
权重:[1, 0, -1]
计算:3*1 + 4*0 + 0*(-1) = 3 + 0 + 0 = 3 - 通道 1:
数据:[7, 8, 0]
权重:[2, 1, 0]
计算:7*2 + 8*1 + 0*0 = 14 + 8 + 0 = 22 - 通道 2:
数据:[11, 12, 0]
权重:[0, -1, -2]
计算:11*0 + 12*(-1) + 0*(-2) = 0 - 12 + 0 = -12
总和: 3 + 22 - 12 = 13,加偏置 1,得到 14。
- 通道 0:
因此,输出通道 0 得到的结果序列为:
[-25, -17, -17, 14]
【输出通道 1】
同理,我们用输出通道 1 的权重和偏置来计算。
-
位置 i = 0(窗口索引 -1、0、1)
- 通道 0:
数据:[0, 1, 2]
权重:[0, 1, 0]
计算:0*0 + 1*1 + 2*0 = 0 + 1 + 0 = 1 - 通道 1:
数据:[0, 5, 6]
权重:[1, 0, -1]
计算:0*1 + 5*0 + 6*(-1) = 0 + 0 - 6 = -6 - 通道 2:
数据:[0, 9, 10]
权重:[2, -1, 0]
计算:0*2 + 9*(-1) + 10*0 = 0 - 9 + 0 = -9
总和: 1 - 6 - 9 = -14(偏置为 0)。
- 通道 0:
-
位置 i = 1(窗口索引 0、1、2)
- 通道 0:
数据:[1, 2, 3]
权重:[0, 1, 0]
计算:1*0 + 2*1 + 3*0 = 0 + 2 + 0 = 2 - 通道 1:
数据:[5, 6, 7]
权重:[1, 0, -1]
计算:5*1 + 6*0 + 7*(-1) = 5 + 0 - 7 = -2 - 通道 2:
数据:[9, 10, 11]
权重:[2, -1, 0]
计算:9*2 + 10*(-1) + 11*0 = 18 - 10 + 0 = 8
总和: 2 - 2 + 8 = 8。
- 通道 0:
-
位置 i = 2(窗口索引 1、2、3)
- 通道 0:
数据:[2, 3, 4]
权重:[0, 1, 0]
计算:2*0 + 3*1 + 4*0 = 0 + 3 + 0 = 3 - 通道 1:
数据:[6, 7, 8]
权重:[1, 0, -1]
计算:6*1 + 7*0 + 8*(-1) = 6 + 0 - 8 = -2 - 通道 2:
数据:[10, 11, 12]
权重:[2, -1, 0]
计算:10*2 + 11*(-1) + 12*0 = 20 - 11 + 0 = 9
总和: 3 - 2 + 9 = 10。
- 通道 0:
-
位置 i = 3(窗口索引 2、3、4,其中索引4为0)
- 通道 0:
数据:[3, 4, 0]
权重:[0, 1, 0]
计算:3*0 + 4*1 + 0*0 = 0 + 4 + 0 = 4 - 通道 1:
数据:[7, 8, 0]
权重:[1, 0, -1]
计算:7*1 + 8*0 + 0*(-1) = 7 + 0 + 0 = 7 - 通道 2:
数据:[11, 12, 0]
权重:[2, -1, 0]
计算:11*2 + 12*(-1) + 0*0 = 22 - 12 + 0 = 10
总和: 4 + 7 + 10 = 21。
- 通道 0:
因此,输出通道 1 得到的结果序列为:
[-14, 8, 10, 21]
最终输出
经过 Conv1d 之后,一个样本的输出就变成了两个通道,每个通道 4 个数,即输出形状为
(1, out_channels, seg_num) = (1, 2, 4)
如果你的 batch*ts_d > 1,那么同样的计算会独立作用于每个样本。最终,经过转置(如果需要恢复到原始格式)后,数据的形状会根据模型需要做相应调整。
这个例子展示了如何将输入的每个“分块序列”(seg_num 个时刻,每个时刻 d_model 个特征)利用大小为 3 的卷积核滑动计算,得到新的特征表示。希望通过具体的数字计算,你能更直观地理解 Conv1d 的操作过程。