官方文档链接:Conv1d — PyTorch 2.4 documentation
输入参数:
in_channels | 输入通道 |
out_channels | 输出通道 |
kernel_size | 卷积核长度 |
stride | 卷积步长 |
padding | 填充尺寸 |
假设batchsize为4,通道个数channels是3,长度Length为5,那么输入大小为(4,3,5)。
创建in_channels为3(与输入通道个数匹配),输出通道数为6,卷积核长度为2的Conv1d实例。
可以看到,输出大小为(4,6,44)。即batchsize不变,(特征)通道数变为6个,Length为4。
import torch
import torch.nn as nn
#生成随即输入
input=torch.randn(4,3,5)
#创建Conv1d实例
conv=nn.Conv1d(in_channels=3,out_channels=6,kernel_size=2,stride=1)
#输出卷积结果
output=conv(input)
print("卷积核权重尺寸",conv.weight.shape)
print("卷积核偏置尺寸",conv.bias.shape)
print("输入向量尺寸",input.shape)
print("输出向量尺寸",output.shape)
#卷积核权重尺寸 torch.Size([6, 3, 2])
#卷积核偏置尺寸 torch.Size([6])
#输入向量尺寸 torch.Size([4, 3, 5])
#输出向量尺寸 torch.Size([4, 6, 4])
那么Conv1d对input进行了什么操作呢?
查看输入,输入尺寸大小为(4,3,5),输入数组input
的样本数是4。也就是说,有4条数据。卷积运算分别在input[0]
、input[1]
、input[2]
和input[3]
上独立进行,然后得到output[0]
、output[1]
、output[2]
和output[3]
。
#打印输入
print(input)
'''
tensor([[[-1.0504, 0.0743, -2.0240, 0.0505, -0.3626],
[-0.3500, -0.9494, 0.5001, 0.3458, -1.3169],
[-1.0906, 0.6251, -1.4102, 0.2388, -0.4803]],
[[-2.0675, 0.4946, 0.6750, 0.3717, 0.8047],
[ 1.4561, -0.2659, 0.6562, -0.0371, -0.3642],
[ 1.3600, -0.1198, -0.3751, 2.4989, 1.0306]],
[[-1.0623, 1.5776, 0.8904, -1.2844, 1.1537],
[ 1.0405, 1.5645, -0.4233, 0.2157, -1.5303],
[ 0.2614, 1.4998, -0.2125, -0.7992, 0.2720]],
[[ 1.9871, -2.1469, 0.1769, 1.0169, -1.4672],
[ 0.5555, 0.0401, 1.1435, -0.2834, -2.2624],
[-1.2195, -2.0573, 0.1966, -0.7486, 0.7481]]])
'''
查看权重,权重大小为(6,3,2),6与输出特征通道对应,3与输入通道对应,2是卷积长度。那么就可以理解为:输出通道个数与卷积核个数相对应,因此有6个卷积核,每个卷积核的大小都是(3,2)。
#打印权重
print(conv.weights)
'''
Parameter containing:
tensor([[[-0.0703, -0.3091],
[-0.3326, -0.1672],
[ 0.3482, 0.1891]],
[[-0.1985, -0.3260],
[-0.0978, -0.1230],
[-0.3030, -0.1296]],
[[-0.3404, -0.2009],
[ 0.4038, 0.3667],
[-0.1873, -0.1717]],
[[-0.2960, -0.1086],
[-0.3795, -0.2489],
[-0.0070, 0.1420]],
[[-0.0850, -0.3686],
[ 0.0176, -0.3713],
[ 0.1902, -0.3868]],
[[ 0.3904, 0.2429],
[-0.2972, 0.4068],
[-0.0862, -0.0369]]], requires_grad=True)
'''
如图所示,橙色部分为参与卷积的input(与卷积核尺寸相同,卷积核长度*in_channels),6个不同的颜色分别对应于6个卷积核的weight和bias。
卷积操作如下,第1个卷积核在输入上水平移动,并进行点积+bias操作,每移动1次,获得1个值,最终根据输出尺寸计算公式(官方文档由公式),得到4个输出(黑色);第2个卷积核在输入上水平移动,得到4个输出(灰色);....以此类推。
图中以第1个样本的第1个通道的第一个输出为例进行计算
#第一例数据
print(input[0])
'''
tensor([[-1.0504, 0.0743, -2.0240, 0.0505, -0.3626],
[-0.3500, -0.9494, 0.5001, 0.3458, -1.3169],
[-1.0906, 0.6251, -1.4102, 0.2388, -0.4803]])
'''
#第一个卷积核的权重
print(conv.weight[0])
'''
tensor([[-0.0703, -0.3091],
[-0.3326, -0.1672],
[ 0.3482, 0.1891]], grad_fn=<SelectBackward0>)
'''
#第一个卷积核的偏置
print(conv.bias[0])
'''
tensor(-0.0734, grad_fn=<SelectBackward0>)
'''
#第一例数据的输出
print(output[0])
'''
tensor([[-0.0088, 0.7302, -0.6166, 0.1327],
[ 0.5693, 0.6543, 0.6749, 0.2109],
[ 0.0821, 0.4383, 1.3626, -0.1179],
[ 1.1748, 0.6352, 0.7680, 0.5574],
[ 0.0929, 1.3357, -0.1929, 0.9896],
[-0.4120, 0.2123, -0.4818, -0.5184]], grad_fn=<SelectBackward0>)
'''
#一个batch中的第1例数据的第1个通道的前2个点,与weights的第1个卷积核的第1行进行点积运算
o1=input[0][0][0:2].dot(conv.weight[0][0])
#一个batch中的第1例数据的第2个通道的前2个点,与weights的第1个卷积核的第2行进行点积运算
o2=input[0][1][0:2].dot(conv.weight[0][1])
#一个batch中的第1例数据的第3个通道的前2个点,与weights的第1个卷积核的第3行进行点积运算
o3=input[0][2][0:2].dot(conv.weight[0][2])
print(o1+o2+o3+conv.bias[0])
#tensor(-0.0088, grad_fn=<AddBackward0>)
print(output[0][0][0])
#tensor(-0.0088, grad_fn=<SelectBackward0>)
卷积后的长度如何计算?
根据官方文档给出的公式,这里的dilation是空洞卷积的超参数,空洞卷积中文名也叫膨胀卷积或者扩张卷积,与正常的卷积不同的是,空洞卷积引入了一个称为 “扩张率(dilation rate)”。具体解释参见:吃透空洞卷积(Dilated Convolutions) - 知乎 (zhihu.com)。