参考链接
1.pytorch官方文档
应用一个2D卷积在由多个输入平面组成的输入信号上。
在最简单的情况下,具有输入大小 (N,Cin,H,W) 和输出 (N,Cout,Hout,Wout) 的层的输出值可以精确描述为:
out(Ni,Coutj)=bias(Coutj)+k=0∑Cin−1weight(Coutj,k)∗input(Ni,k)
- *是有效的2D互相关运算符
- N 是批大小
- C 表示通道数
- H 是像素中输入平面的高度
- W 是像素中的宽度。
1
| torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True, padding_mode='zeros', device=None, dtype=None)
|
stride
: 控制交叉相关的步长,可以是一个数字或一个元组。
padding
: 控制应用于输入的填充量。可以是字符串 {'valid', 'same'}
或一个整数/整数元组,指定在两侧应用的隐式填充量。
dilation
: 控制核心点之间的间距;也称为à trous算法。这个比较难描述,但是这个链接有一个很好的视觉化示例,展示了dilation
的作用。
groups
: 控制输入和输出之间的连接。in_channels
和 out_channels
必须都能被 groups
整除。例如:
- 当
groups=1
时,所有输入都与所有输出进行卷积。
- 当
groups=2
时,操作相当于并行地拥有两个卷积层,每个层看到一半的输入通道并产出一半的输出通道,然后将两者的输出连接起来。
- 当
groups=in_channels
时,每个输入通道都与其自己的一组滤波器(尺寸为 (in−channelsout−channels) 进行卷积。
参数 kernel_size
、stride
、padding
、dilation
可以是:
- 一个
int
单个整数 - 在这种情况下,相同的值用于高度和宽度维度
- 一个由两个整数组成的
tuple
元组 - 在这种情况下,第一个 int
用于高度维度,第二个 int
用于宽度维度
1.1 注意
-
注意:当 groups == in_channels
且 out_channels == K * in_channels
,其中K是一个正整数时,该操作也被称为“深度可分卷积”。
- 换句话说,对于输入大小为(N,Cin,Lin) 的情况,可以使用深度乘数K进行深度可分卷积,其参数为 (Cin=Cin,Cout=Cin×K,...,groups=Cin)。
-
注意:在某些情况下,当在CUDA设备上给定张量并使用CuDNN时,此操作可能选择一个非确定性算法以提高性能。如果这不可取,你可以尝试通过设置 torch.backends.cudnn.deterministic = True
来使操作变为确定性(可能以牺牲性能为代价)。更多信息请参见可重现性。
-
注意:padding='valid'
相当于无填充。padding='same'
会填充输入,使输出具有与输入相同的形状。然而,这种模式不支持步长值不为1的任何情况。
-
注意:该模块支持复杂的数据类型,即 complex32
,complex64
,complex128
。
1.2 参数
in_channels
(int) - 输入图像的通道数。
out_channels
(int) - 卷积产生的通道数。
kernel_size
(int 或 tuple) - 卷积核的大小。
stride
(int 或 tuple, 可选) - 卷积的步长。默认值:1。
padding
(int, tuple 或 str, 可选) - 填充到输入的所有四边。默认值:0。
padding_mode
(str, 可选) - 'zeros'
, 'reflect'
, 'replicate'
或 'circular'
。默认:'zeros'
。
dilation
(int 或 tuple, 可选) - 核元素之间的间距。默认值:1。
groups
(int, 可选) - 从输入通道到输出通道的阻塞连接数。默认值:1。
bias
(bool, 可选) - 如果为 True
,则向输出添加可学习的偏置。默认值:True
。
1.3 形状
- 输入:(N,Cin,Hin,Win) 或 (Cin,Hin,Win)
- 输出:(N,Cout,Hout,Wout) 或 (Cout,Hout,Wout),其中
Hout=stride[0]Hin+2×padding[0]−dilation[0]×(kernel_size[0]−1)−1+1
Wout=stride[1]Win+2×padding[1]−dilation[1]×(kernel_size[1]−1)−1+1
1.4 变量
weight
(Tensor): 模块可学习的权重,其形状为 (out_channels, in_channels / groups, kernel_size[0], kernel_size[1])。这些权重的值是从均匀分布 U(-√k, √k) 中采样的,其中 k = Cin∗∏i=0groupskernel_size[i]1。
bias
(Tensor): 如果 bias
为 True
,则模块形状为 (out_channels) 的可学习偏置。这些权重的值是从 U(-√k, √k) 中采样的,其中 k = Cin∗∏i=0groupskernel_size[i]1。
1.5 示例
1 2 3 4 5 6 7 8 9 10
| m = nn.Conv2d(16, 33, 3, stride=2)
m = nn.Conv2d(16, 33, (3, 5), stride=(2, 1), padding=(4, 2))
m = nn.Conv2d(16, 33, (3, 5), stride=(2, 1), padding=(4, 2), dilation=(3, 1))
input = torch.randn(20, 16, 50, 100)
output = m(input)
|
2.测试
2.1 nn.Conv2d(类式接口)
基本用法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| import torch from torch import nn
layer = nn.Conv2d(1, 3, kernel_size=3, stride=1, padding=0)
x = torch.rand(1, 1, 28, 28)
out = layer.forward(x)
print(out.shape)
layer = nn.Conv2d(1, 3, kernel_size=3, stride=1, padding=1) print(layer.forward(x).shape)
layer = nn.Conv2d(1, 3, kernel_size=3, stride=2, padding=1)
print(layer.forward(x).shape)
out = layer(x) print(out.shape)
|
运行结果:
1 2 3 4
| torch.Size([1, 3, 26, 26]) torch.Size([1, 3, 28, 28]) torch.Size([1, 3, 14, 14]) torch.Size([1, 3, 14, 14])
|
特别注意,在使用时应该直接使用layer(x)
而不是layer.forward(x)
,因为前者实际是调用了__call__()
,而PyTorch在这个函数中定义了一些hooks,如果要使用这些钩子的功能就只能用前者了!它会先运行hooks再运行.forward()
函数。
在前面定义的卷积层的基础上,查看一下卷积层的权重(即卷积核)信息和偏置信息:
1 2
| print(layer.weight) print(layer.bias)
|
运行结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| tensor([[[[ 0.1277, -0.1672, 0.1102], [ 0.3176, 0.0236, 0.2537], [ 0.0737, 0.0904, 0.0261]]],
[[[ 0.0349, -0.2042, 0.1766], [-0.0938, -0.0470, 0.2011], [-0.2460, 0.0876, 0.3124]]],
[[[-0.2361, -0.0971, -0.1031], [-0.0756, -0.3073, 0.3227], [-0.1951, -0.2395, -0.0769]]]], requires_grad=True) Parameter containing: tensor([ 0.0790, -0.3261, 0.0697], requires_grad=True)
|
查看一下shape:
1 2
| print(layer.weight.shape) print(layer.bias.shape)
|
运行结果:
1 2
| torch.Size([3, 1, 3, 3]) torch.Size([3])
|
2.2 F.conv2d(函数式接口)
PyTorch里一般小写的都是函数式的接口,相应的大写的是类式接口。函数式的更加low-level一些,如果不需要做特别复杂的配置只要用类式接口就够了。
可以这样理解:nn.Conv2d
是[2D卷积层],而F.conv2d
是[2D卷积操作]。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import torch from torch.nn import functional as F
w = torch.rand(16, 3, 5, 5) b = torch.rand(16)
x = torch.randn(1, 3, 28, 28)
out = F.conv2d(x, w, b, stride=1, padding=1) print(out.shape)
out = F.conv2d(x, w, b, stride=2, padding=2) print(out.shape)
|
运行结果:
1 2
| torch.Size([1, 16, 26, 26]) torch.Size([1, 16, 14, 14])
|