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)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • JQuery的安装与下载教程

    JQuery的安装与下载教程网页中添加jQuery可以通过多种方法在网页中添加jQuery。您可以使用以下方法:从jquery.com下载jQuery库 从CDN中载入jQuery,如从Google中加载jQuery下载jQuery有两个版本的jQuery可供下载:Productionversion-用于实际的网站中,已被精简和压缩。 Developmentversion-用于测试和开发(未压缩,是可读的代码)以上两个版本都可以从jquery.com中下载。…

    2022年5月3日
    44
  • stl文件格式特点_常见文件格式

    stl文件格式特点_常见文件格式一、介绍STL文件格式(stereolithography,光固化立体造型术的缩写)是由3DSYSTEMS公司于1988年制定的一个接口协议,是一种为快速原型制造技术服务的三维图形文件格式。S

    2022年8月3日
    8
  • mysql中一条insert语句批量插入多条记录

    mysql中一条insert语句批量插入多条记录插入语句常用写法: INSERTINTOitems(name,city,price,number,picture)VALUES(‘耐克运动鞋’,’广州’,500,1000,’003.jpg’); 这种方式只能够一次插入一条数据,要想插入多条数据,就得多次调用此sql语句,意味着多次与数据库建立连接。但是这样一来,就会增加服务器的负荷,因为,执行每一次SQL服务器都要同样对SQL进行分析、优化等操作。幸好MySQL提供了另一种解决方案,就是使用一条INSERT语句来插入多条记录。这并不是标准的SQ

    2022年6月11日
    33
  • java 415_415错误[通俗易懂]

    java 415_415错误[通俗易懂]WhitelabelErrorPageThisapplicationhasnoexplicitmappingfor/error,soyouareseeingthisasafallback.TueNov1314:41:09CST2018Therewasanunexpectederror(type=UnsupportedMediaType,s…

    2022年6月12日
    176
  • Java线程池-ThreadPoolExecutor原理分析与实战

    Java线程池-ThreadPoolExecutor原理分析与实战1.为什么要用线程池减少资源的开销  减少了每次创建线程、销毁线程的开销。 提高响应速度,每次请求到来时,由于线程的创建已经完成,故可以直接执行任务,因此提高了响应速度。 提高线程的可管理性,线程是一种稀缺资源,若不加以限制,不仅会占用大量资源,而且会影响系统的稳定性。 因此,线程池可以对线程的创建与停止、线程数量等等因素加以控制,使得线程在一种可控的范围内运行,不仅能保证系统稳定…

    2022年7月13日
    18
  • java中jbpm工作流_activity工作流

    java中jbpm工作流_activity工作流中文指南,供大家下载。。

    2025年10月11日
    1

发表回复

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

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