torch.nn.Conv2d详解

torch.nn.Conv2d详解首先看一下这个类的定义 classConv2d ConvNd 初始化函数 这里主要了解有哪些参数传进来就可以了 def init self in channels int out channels int kernel size size 2 t stride size 2 t 1 padding size 2 t 0 d

注意:这里展示的是本篇博文写时的版本最新的实现,但是后续会代码可能会迭代更新,建议对照 官方文档 进行学习。

首先看一下这个类的定义:

class Conv2d(_ConvNd): # 初始化函数,这里主要了解有哪些参数传进来就可以了 def __init__( self, in_channels: int, out_channels: int, kernel_size: _size_2_t, stride: _size_2_t = 1, padding: _size_2_t = 0, dilation: _size_2_t = 1, groups: int = 1, bias: bool = True, padding_mode: str = 'zeros' # TODO: refine this type ): #————————————————看到这就可以了———————————————————— kernel_size = _pair(kernel_size) stride = _pair(stride) padding = _pair(padding) dilation = _pair(dilation) super(Conv2d, self).__init__( in_channels, out_channels, kernel_size, stride, padding, dilation, False, _pair(0), groups, bias, padding_mode) def _conv_forward(self, input, weight): if self.padding_mode != 'zeros': return F.conv2d(F.pad(input, self._reversed_padding_repeated_twice, mode=self.padding_mode), weight, self.bias, self.stride, _pair(0), self.dilation, self.groups) return F.conv2d(input, weight, self.bias, self.stride, self.padding, self.dilation, self.groups) # 前向传播计算 def forward(self, input: Tensor) -> Tensor: return self._conv_forward(input, self.weight) 

上面就是Cov2d这个类的源码实现,是不是感觉代码特别少?是不是突然对掌握它信心倍增!

从整体上来看:

Conv2d是一个类,它包含了做卷积运算所需要的参数(__init__函数),以及卷积操作(forward函数)。

再来看一下它的详细参数:

一共九个参数,一般用前三个就可以处理一般的任务:

  1. in_channels :输入通道数目
  2. out_channels :输出通道数目
  3. kernel_size :卷积核大小,如果输入是一个值,比如 3 3 3,那么卷积核大小就是 3 × 3 3 \times 3 3×3 ,如果不想卷积核宽和高相等,还可以输入tuple类型数据,比如: ( 3 , 5 ) (3, 5) (3,5)
  4. stride :步长大小,跟上面卷积核参数一样,如果输入是一个值,比如 2 2 2 ,步长就是 2 × 2 2 \times 2 2×2 ,还可以输入元组 ( 2 , 1 ) (2, 1) (2,1) ,表示卷积核每次向右移动 1 1 1 个步长,向下移动 2 2 2 个步长。
  5. padding :填充,参数表示在周围补0的情况。补0的方向为上、下、左、右四个方向。如果是输入是单个值,比如1,就是在上下左右四个方向补一圈0。如果输入是元组比如 (2,1) ,表示在上下两个方向各补两行0,在左右两个方向各补一列0。
  6. dilation :进行扩展卷积需要的参数。
  7. groups :进行分组卷积需要的参数。(有需要自行深入了解)
  8. bias :偏置,布尔类型,默认为 True ,即增加一个学习的偏置项。
  9. padding_mode :填充的模式,默认是 zero ,还可以选择 reflectreplicatecircular 。(有需要自行深入了解)

首先是关于输入通道数 in_channels 和输出通道数 out_channels 的解释

如果对通道是什么还不太理解可以去看我的这篇博客:如何理解卷积神经网络中的通道(channel)

如果卷积核输入的是原始的图片,那么我们可以根据图片类型决定图片的输入通道数,如果是RGB图片, in_channels=3 ,如果是灰度图像,in_channels=1 。输出通道数需要我们自己指定,具体指定多少需要根据经验。

如果是第二层或者更多层卷积,当前层的输入通道数就是上一层卷积的输出通道数,当前层的输出通道数需要自己指定。

关于卷积核的解释

这里的卷积核参数只是指定卷积核的宽度和高度,对于多通道的卷积,代码内部会根据输入通道数初始化对应的卷积核数目,即:如果输入是3通道,那么就会有三个我们指定宽高的卷积核做相应的卷积操作。

关于步长的解释

步长指的是卷积核在输入矩阵的上每次移动几步。移动方向由两个,向右或者向下。分别由两个值指定。

关于填充的解释

填充的主要目的是为了充分利用输入图片的边缘信息。

关于为什么需要填充,可以参考这篇博客:CNN基础知识——卷积(Convolution)、填充(Padding)、步长(Stride)

关于扩张(dilation)参数的解释

本质上就是扩大卷积核,比如将 3 × 3 3 \times 3 3×3 的扩大为 7 × 7 7 \times 7 7×7 的,然后对扩大的部分用0进行填充,

目的是扩大感受野,捕获多尺度上下文信息。

具体怎么扩张,详细请参考:

【1】如何理解扩张卷积(dilated convolution)

【2】总结-空洞卷积(Dilated/Atrous Convolution)

关于分组(group)的解释

对于一般的卷积神经网络,假设通道数有100个,那么我们需要100个卷积核对这100个通道分别做卷积操作,然后求和,最后得到1个特征图。如果我们需要得到30个特征图,就需要30*100=3000个卷积核。参数量非常大。

而对于分组卷积,依然假设通道数有100个,我们分成两组,每组50个通道数,分别对每组单独做卷积操作,每组需要50个卷积核,总的卷积核数目没变,但是我们每组可以得到2个特征图。如果我们分成4组,每组25个通道数,最终可以得到4个特征图。

也就是说,我们在不增加参数量的情况下,得到了更多的特征图。这是分组的优势之一,对于分组的通道,我们可以在不同的GPU上并行训练,加速训练过程。

更详细的解释参考:

卷积网络基础知识—Group Convolution分组卷积

关于偏置项的解释

这个项是个布尔类型值,如果为True,就会在训练的时候加上偏置项,反之,不会。默认是True。

关于填充模式的解释

我们一般情况下对于填充的数据,默认是0。还可以选择常数填充、镜像填充、重复填充.。

常数填充就是指定一个常数值进行填充,如果为0,等价于零填充;

镜像填充是指对图像边缘进行镜像对称的填充;

重复填充指的是用图像边缘的像素值进行填充。

详细参考:PyTorch中的padding(边缘填充)操作

OK,到这里就对所有的参数解释完了。下面我们解释一下,经过一次卷积操作之后,图像形状如何进行变化。

图像形状的变化规则

卷积神经网络的输入: N , C i n , H i n , W i n N,C_{in},H_{in},W_{in} N,Cin,Hin,Win

卷积神经网络的输出: N , C o u t , H o u t , W o u t N,C_{out},H_{out},W_{out} N,Cout,Hout,Wout

其中, N N N 表示批处理的大小,即batch_size。 C i n C_{in} Cin C o u t C_{out} Cout 分别表示输入和输出通道数。 H i n H_{in} Hin W i n W_{in} Win 表示输入图片的高和宽,以像素为单位。同理, H o u t H_{out} Hout W o u t W_{out} Wout 表示输出图片的高和宽。

OK,我们来看一下输入输出是怎么变化的。

首先是batch_size,卷积神经网络并不会改变这个参数;然后是 C i n C_{in} Cin C o u t C_{out} Cout 这两个参数是我们初始化的时候就指定的;实际上卷积神经网络改变的是图片的尺寸,图片尺寸变化公式为:
H o u t = ⌊ H i n + 2 × p a d d i n g ⌊ 0 ⌋ − d i l a t i o n ⌊ 0 ⌋ × ( k e r n e l _ s i z e ⌊ 0 ⌋ − 1 ) − 1 s t r i d e ⌊ 0 ⌋ + 1 ⌋ H_{out}=\lfloor \frac{H_{in} + 2 \times padding\lfloor 0 \rfloor – dilation \lfloor 0 \rfloor \times (kernel\_size\lfloor 0 \rfloor – 1)-1}{stride\lfloor 0 \rfloor} + 1 \rfloor Hout=stride0Hin+2×padding0dilation0×(kernel_size01)1+1

W o u t = ⌊ W i n + 2 × p a d d i n g ⌊ 1 ⌋ − d i l a t i o n ⌊ 1 ⌋ × ( k e r n e l _ s i z e ⌊ 1 ⌋ − 1 ) − 1 s t r i d e ⌊ 1 ⌋ + 1 ⌋ W_{out}=\lfloor \frac{W_{in} + 2 \times padding\lfloor 1 \rfloor – dilation \lfloor 1 \rfloor \times (kernel\_size\lfloor 1 \rfloor – 1)-1}{stride\lfloor 1 \rfloor} + 1 \rfloor Wout=stride1Win+2×padding1dilation1×(kernel_size11)1+1

然后我们用一段代码测试一下:

import torch import torch.nn as nn # in_channels=16, out_channels=33, kernel_size=(3, 5) m = nn.Conv2d(16, 33, (3, 5), stride=(2, 1), padding=(4, 2), dilation=(3, 1)) print(m) # 我们随机构造输入数据 # N=20, C_in=16, H_in=50, W_in=100 # 对应上面说的 (N, C, H, W) input = torch.randn(20, 16, 50, 100) output = m(input) print(output.shape) 

结果:

Conv2d(16, 33, kernel_size=(3, 5), stride=(2, 1), padding=(4, 2), dilation=(3, 1)) torch.Size([20, 33, 26, 100]) 

上面的torch.size表示输出的形状:

b a t c h _ s i z e = 20 batch\_size=20 batch_size=20

o u t _ c h a n n e l = 33 out\_channel=33 out_channel=33

H o u t = 26 H_{out}=26 Hout=26

W o u t = 100 W_{out}=100 Wout=100

W o u t = ⌊ 100 + 2 × 2 − 1 × ( 5 − 1 ) − 1 1 + 1 ⌋ = 100 W_{out}=\lfloor \frac{100 + 2 \times 2 – 1 \times (5 – 1) – 1}{1} + 1 \rfloor = 100 Wout=1100+2×21×(51)1+1=100

Conv2d中的权重和偏置项

最后我们有必要了解一下Conv2d的两个变量:

~Conv2d.weight

其形状为: ( o u t _ c h a n n e l s , i n _ c h a n n e l s g r o u p , k e r n e l _ s i z e [ 0 ] , k e r n e l _ s i z e [ 1 ] ) (out\_channels, \frac{in\_channels}{group},kernel\_size[0], kernel\_size[1]) (out_channels,groupin_channels,kernel_size[0],kernel_size[1])

数据从 U ( − k , k ) \mathcal{U}(-\sqrt{k}, \sqrt{k}) U(k
,k
)
中取样,其中 k = g r o u p s C i n ∗ ∏ i = 0 1 k e r n e l _ s i z e [ i ] k=\frac{groups}{C_{in}*\prod_{i=0}^1kernel\_size[i]} k=Cini=01kernel_size[i]groups

~Conv2d.bias

其形状为: ( o u t _ c h a n n e l s ) (out\_channels) (out_channels)

数据从 U ( − k , k ) \mathcal{U}(-\sqrt{k}, \sqrt{k}) U(k
,k
)
中取样,其中 k = g r o u p s C i n ∗ ∏ i = 0 1 k e r n e l _ s i z e [ i ] k=\frac{groups}{C_{in}*\prod_{i=0}^1kernel\_size[i]} k=Cini=01kernel_size[i]groups

还是上面的代码,我们可以查看这个卷积神经网络的权重和偏置的形状:

import torch import torch.nn as nn # in_channels=16, out_channels=33, kernel_size=(3, 5) m = nn.Conv2d(16, 33, (3, 5), stride=(2, 1), padding=(4, 2), dilation=(3, 1)) print(m) # 我们随机构造输入数据 # N=20, C_in=16, H_in=50, W_in=100 # 对应上面说的 (N, C, H, W) input = torch.randn(20, 16, 50, 100) output = m(input) print(m.weight.shape) print(m.bias.shape) 

输出:

torch.Size([33, 16, 3, 5]) torch.Size([33]) 
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/231897.html原文链接:https://javaforall.net

(0)
上一篇 2025年11月14日 下午8:01
下一篇 2025年11月14日 下午8:22


相关推荐

  • pythoncharm怎么改字体大小_pycharm更改字体大小

    pythoncharm怎么改字体大小_pycharm更改字体大小pycharm在File->settings中设置font大小时发现size框无法输入。查了下资料点击下上面的saveas按钮创建一个信息的模块名称就可以了。

    2022年8月26日
    16
  • ring0获取指定进程的PEB

    ring0获取指定进程的PEBifndefTYPEDE H defineTYPEDE HtypedefPPEB stdcall P PsGetProcess PEPROCESS typedefunsig typedefstruc RTL USER PROCESS PARAMETERS BYTEReserved 16

    2026年3月18日
    1
  • .ipynb与.py文件快速互转[通俗易懂]

    .ipynb与.py文件快速互转[通俗易懂].ipynb代码转为.py:法一:在xxx.ipynb所在目录下,打开终端,并输入命令:jupyternbconvert–toscriptxxx.ipynb其中xxx.ipynb是要转换文件的名字,转换后在该目录下出现xxx.py文件(有的版本是xxx.txt文件,再改下后缀即可)。法二:在Jupyternotebook或GoogleColab中打开ipynb文件,然后选择file–downloadas–pythonfile.py代码转为.ipynb:

    2022年8月29日
    4
  • ON DUPLICATE KEY UPDATE 用法与说明

    ON DUPLICATE KEY UPDATE 用法与说明ONDUPLICATEK 作用先声明一点 ONDUPLICATEK 为 Mysql 特有语法 这是个坑语句的作用 当 insert 已经存在的记录时 执行 Update 用法什么意思 举个例子 user admin t 表中有一条数据如下表中的主键为 id 现要插入一条数据 id 为 1 password 为 第一次插入的密码 正常写法为 INSE

    2026年3月20日
    1
  • linux+tar怎样解压zip文件内容,linux tar压缩解压文件[通俗易懂]

    linux+tar怎样解压zip文件内容,linux tar压缩解压文件[通俗易懂]时间:2016-08-30作者:admin阅读:次-c:建立压缩档案-x:解压-t:查看内容-r:向压缩归档文件末尾追加文件-u:更新原压缩包中的文件这五个是独立的命令,压缩解压都要用到其中一个,可以和别的命令连用但只能用其中一个。下面的参数是根据需要在压缩或解压档案时可选的。下面的参数-f是必须的-f:使用档案名字,切记,这个参数是最后一个参数,后面只能接档案名。#tar-cf…

    2022年5月9日
    111
  • C#的继承_继承关系中的被继承人

    C#的继承_继承关系中的被继承人继承详解:一.继承的概念:二.基类和派生类:三.继承的作用:四.继承的特点:五.举例说明:六.继承注意事项:一.继承的概念:继承:是面向对象程序设计中最重要的概念之一。继承允许我们根据一个类来定义另一个类,这使得创建和维护应用程序变得更容易。同时也有利于重用代码和节省开发时间。当创建一个类时,不需要完全重新编写新的数据成员和成员函数,只需要设计一个新的类,继承了已有的类的成员即可。这个已有的类被称为的基类/父类,这个新的类被称为派生类/子类。二.基类和派生类:基类/父类:也可以理解成生活中的父亲

    2025年10月15日
    6

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号