参考:
Pytorch 从 0 开始学(6)——Conv2d 详解 - 知乎 (zhihu.com)
Conv1d — PyTorch 1.11.0 documentation
本文结合图例说明Conv1d的基本计算过程。
Conv1d
torch.nn.Conv1d(in_channels, out_channels, kernel_size,
stride=1, padding=0, dilation=1, groups=1,
bias=True, padding_mode='zeros', device=None, dtype=None)
输入维度(N, Cin, Lin)
输出维度(N, Cout, Lout)
这里,N为batchsize,
C
i
n
,
C
o
u
t
C_{in}, C_{out}
Cin,Cout分别表示输入输出channel数,
L
i
n
,
L
o
u
t
L_{in}, L_{out}
Lin,Lout分别表示输入输出的channel内信号长度。
下文简写作Cin、Cout、Lin、Lout。
关键参数如下:
- in_channels, out_channels: 输入输出通道数
- kernel_size: 卷积核长度
- stride=1: 卷积核步长
- padding=0, padding_mode=‘zeros’: 边沿扩充, 目前略过
- dilation=1: 采样间隔
- bias=True: 是否加入bias
计算过程
输入输出维度
本文关注简单情形,假设采用默认参数(即 stride=1, padding=0, dilation=1),
则
L
o
u
t
=
L
i
n
−
k
e
r
n
e
l
_
s
i
z
e
+
1
L_{out} = L_{in} - kernel\_size + 1
Lout=Lin−kernel_size+1。
参数假设:
- 模型:Cin = 4,Cout = 3,kernel_size = 2。
- 输入:batchsize=1, Cin = 4, Lin = 3,即shape为(1, 4, 3)
Lout 计算
由上述公式:
Lout = 3 - 2 + 1 = 2,见下图:
因此输出维度 (Cout, Lout) 为 (3, 2)。
下面将由这个例子出发,说明计算过程。
in, out分别表示输入、输出tensor。
模型参数
模型有两类可学习参数:
- weight: shape = (Cout, Cin, kernel_size) = (3, 4, 2)
- bias: bias=True 时有效, shape = (Cout) = (3)
互相关(cross-correlation)计算
根据 pytorch 的计算公式:
o u t ( j ) = b i a s ( j ) + ∑ i = 0 C i n − 1 w e i g h t ( j , i ) ⋆ i n ( i ) out(j) = bias(j) + \sum_{i=0}^{C_{in}-1} weight(j, i) \star in(i) out(j)=bias(j)+i=0∑Cin−1weight(j,i)⋆in(i)
其中 ⋆ \star ⋆ 表示互相关算符(cross-correlation operator), 下面以 w e i g h t ( j , i ) ⋆ i n ( i ) weight(j, i) \star in(i) weight(j,i)⋆in(i) 为例, 取 j=1, i=2。
求和符号 Σ 后每一项的计算过程如下图:
weight 以"窗口滑动"的方式, 与 in(2) 中的元素(i0, i1, i2)依次运算。
计算结果为(j1, j2),构成输出的一部分。
最终 out(j) 的计算方式如下图:
步骤如下:
- 求 out(1),需要用到 weight(1,*) 和 bias(1)。
- 对输入的每个 channel i,与 weight(1,i) 运算后得到 Cin 个新向量,这里是 4 个 (1, 2) 向量[蓝/白色]。
- 4 个向量直接相加,合并为 1 个向量[黄色]。
- 合并后的向量各个位置加上 bias(1) [灰色],就得到了最终的 out(1)[橙色]。
过程总结
最终的out由out(1)、out(2)、out(3)拼接而成, 整体过程总结如下:
对输入的每个 channel,都算出一个与输出 shape 一致的 tensor,然后叠加。最后再加上 bias,即为输出。
- Cin 维度上:每个 in_channel 影响所有 out_channel,反过来每个 out_channel 也可包含所有 in_channel 的信息。
- Lin 维度上:通过 weight 将长为 kernel_size 的一段元素联系起来。
验证
python 代码如下,可以验证上述过程:
import numpy as np
import torch
import torch.nn as nn
#print(torch.__version__) #1.8.0+cu111
#模型设置与参数赋值
conv = nn.Conv1d(in_channels=4, out_channels=3, kernel_size=2)
bias = torch.FloatTensor([0.1, 0.2, 0.3])
weight = np.arange(24).reshape(3,4,2)
weight = torch.FloatTensor(weight)
for name, param in conv.named_parameters():
if name == 'weight':
param.data = weight
if name == 'bias':
param.data = bias
print(name, param.shape, param)
#构造输入并计算输出
input = [
[0,0,0],
[0,0,1],
[0,1,1],
[1,1,1]
]
input = torch.FloatTensor(input).reshape(1,4,3)
with torch.no_grad():
print('\ninput:\n', input)
output = conv(input)
print('output:\n', output)
输出结果为:
#输出结果
weight torch.Size([3, 4, 2]) Parameter containing:
tensor([[[ 0., 1.],
[ 2., 3.],
[ 4., 5.],
[ 6., 7.]],
[[ 8., 9.],
[10., 11.],
[12., 13.],
[14., 15.]],
[[16., 17.],
[18., 19.],
[20., 21.],
[22., 23.]]], requires_grad=True)
bias torch.Size([3]) Parameter containing:
tensor([0.1000, 0.2000, 0.3000], requires_grad=True)
input:
tensor([[[0., 0., 0.],
[0., 0., 1.],
[0., 1., 1.],
[1., 1., 1.]]])
output:
tensor([[[ 18.1000, 25.1000],
[ 42.2000, 65.2000],
[ 66.3000, 105.3000]]])