torch.nn中提供了三个不同维度的卷积类,依次是Conv1d, Conv2d, Conv3d,其输入参数如下
参数含义
其中,kernel_size的可选值及其含义如下
输入 | Conv1d | Conv2d | Conv3d |
---|---|---|---|
整数 N N N | N N N | N × N N\times N N×N | N × N × N N\times N\times N N×N×N |
元组形式 | N N N | ( N 1 , N 2 ) (N_1,N_2) (N1?,N2?) | ( N 1 , N 2 , N 3 ) (N_1, N_2, N_3) (N1?,N2?,N3?) |
卷积核尺寸 | N N N | N 1 × N 2 N_1\times N_2 N1?×N2? | N 1 × N 2 × N 3 N_1\times N_2\times N_3 N1?×N2?×N3? |
填充方式padding_mode表示,当卷积计算导致边缘像素被侵蚀的时候,在这些被侵蚀的区域的填充模式
毫无疑问,nn.Conv1d是最简单的卷积操作,毕竟只涉及到一个维度,下面便以此为例,来初探卷积类的使用方法。
import torch
from torch import nn
c1 = nn.Conv1d(1,1,3)
w1 = c1.weight # 卷积核的权重
print(w1)
# tensor([[[-0.2224, -0.4318, -0.2760]]], requires_grad=True)
r = torch.randn(1, 1, 5)
print(r)
# tensor([[[-0.1509, -1.0857, -0.4598, -0.4435, 0.8506]]])
c1(r)
# tensor([[[ 0.2265, 0.1597, -0.3437]]], grad_fn=<ConvolutionBackward0>)
其中, c 1 c_1 c1?是一个一维的卷积对象,其输入输出的通道均为 1 1 1,并且卷积核的尺寸为 3 3 3,调用其内部的weight成员,得到卷积核的真实面貌 w 1 w_1 w1?。
然后新建一个随机张量 r r r,c1?的返回值也是一个张量,相当于计算卷积 w 1 ? r w_1*r w1??r,而所谓卷积,即下面的过程
for i in range(3):
torch.sum(w1*r[:,:,i:i+3]) + c1.bias
'''
tensor([0.2265], grad_fn=<AddBackward0>)
tensor([0.1597], grad_fn=<AddBackward0>)
tensor([-0.3437], grad_fn=<AddBackward0>)
'''
其返回值与c1?完全一致。
上面的示例为了理解卷积的运算过程,而将输入核输出通道均设为1,下面将二者放开,看看会发生什么。
放开这两个参数之后,数据量会陡然倍增,所以不再关注其具体的值的变化,而把目光聚焦在其形状变化。下面以Conv2d为例,来说明这个过程。
c2 = nn.Conv2d(2, 5, 3)
print(c2.weight.shape) # torch.Size([5, 2, 3, 3])
mat = torch.randn(1, 2, 5, 5)
c2(mat).shape # torch.Size([1, 5, 3, 3])
其中, c 2 c_2 c2?的卷积核尺寸为 5 × 2 × 3 × 3 5\times2\times3\times3 5×2×3×3,其作用在 1 × 2 × 5 × 5 1\times2\times5\times5 1×2×5×5的张量上,计算结果为 1 × 5 × 3 × 3 1\times5\times3\times3 1×5×3×3的张量。
其中,后面的 3 × 3 3\times3 3×3,作用在后面的 5 × 5 5\times5 5×5上,使之边缘萎缩,变成 3 × 3 3\times3 3×3的尺寸。前面的 2 × 5 2\times5 2×5,则作用在 1 × 2 1\times2 1×2上,将其 2 2 2变成了 5 5 5。故而mat的尺寸需要与 c 2 c_2 c2?的尺寸相契合,即其第二个维度与 c 2 c_2 c2?的第一个维度必须相等。