本文从信号处理中的互相关运算引入深度学习中的卷积
然后介绍了不同的卷积类型,以及如何在pytorch中使用这些卷积层。

(在看pytorch文档中的Conv1D/2D/3D的时候感到比较困惑,又很好奇深度学习中各种各样的卷积操作。于是结合整理几乎包含深度学习中所有的卷积操作,主要参考的有《Dive into Deep learning》, cs231, pytorch的官网文档,stackoverflow以及csdn和知乎上的介绍…简单记录一下)

文章目录

    • 本文从信号处理中的`互相关运算`引入`深度学习中的卷积`。 然后介绍了`不同的卷积类型`,以及如何在`pytorch`中使用这些卷积层。
  • 一、前言
    • 1.1 数学中的卷积操作
    • 1.2 信号处理中的互相关运算
  • 二、深度学习中的卷积
    • 2.1 卷积操作
    • 2.2. 填充和步幅
      • Padding
      • Stride
    • 2.3 多输入通道和多输出通道
      • 多输入通道
      • 多输出通道
  • 三、卷积类型
    • 3.1 1D/2D/3D 卷积
      • 1D 卷积
      • 2D卷积
      • 3D卷积
    • 3.2 1x1卷积
    • 3.3 转置卷积 (反卷积)
      • 1D反卷积
      • 2D反卷积
      • 3D反卷积
    • 3.4 扩张卷积(空洞卷积)
    • 3.5 可分离卷积
      • 空间可分离卷积
      • 深度可分离卷积
    • 3.6 扁平卷积
    • 3.7 分组卷积
  • 参考

一、前言

1.1 数学中的卷积操作

图像中的卷积操作由数学中卷积的演化而来,所以我们先了解下数学中的卷积操作


直观理解
在信号/图像处理中,卷积定义为
两个函数在反转和移位后的乘积的积分,以下可视化展示了这一过程:


数值理解

  • 连续函数的卷积
    在数学中, 两个函数(比如 f , g f, g f,g : R d → R \mathbb{R}^d \rightarrow \mathbb{R} Rd→R )之间的 “卷积”被定义为
    ( f ∗ g ) ( x ) = ∫ f ( z ) g ( x − z ) d z . (f * g)(\mathbf{x})=\int f(\mathbf{z}) g(\mathbf{x}-\mathbf{z}) d \mathbf{z} . (f∗g)(x)=∫f(z)g(x−z)dz.
    也就是说, 卷积是当把函数g “翻转” 并移位 x \mathbf{x} x时, 测量 f f f和 g g g之间的乘积
  • 离散函数的卷积
    当为离散对象时, 积分就变成求 和。例如:对于由索引为 Z \mathbb{Z} Z的、平方可和的、无限维向量集合中抽取的向量,我们得到以下定义:

( f ∗ g ) ( i ) = ∑ a f ( a ) g ( i − a ) . (f * g)(i)=\sum_a f(a) g(i-a) . (f∗g)(i)=a∑​f(a)g(i−a).

  • 二维函数的卷积
    对于二维张量, 则为 f f f的索引 ( a , b ) (a, b) (a,b)和 g g g的索引 ( i − a , j − b ) (i-a, j-b) (i−a,j−b)上的对应加和:
    ( f ∗ g ) ( i , j ) = ∑ a ∑ b f ( a , b ) g ( i − a , j − b ) . (f * g)(i, j)=\sum_a \sum_b f(a, b) g(i-a, j-b) . (f∗g)(i,j)=∑a​∑b​f(a,b)g(i−a,j−b).

1.2 信号处理中的互相关运算

严格来说,卷积层是个错误的叫法,因为它所表达的运算其实是互相关运算(cross-correlation),⽽不是卷积运算。在卷积层中,输⼊张量和核张量通过互相关运算产⽣输出张量。


直观理解
互相关被称为滑动点积或两个函数的滑动内积。互相关的filters不需要反转,它直接在函数f中滑动。f和g之间的交叉区域是互相关,下图显示了相关性和互相关之间的差异:


数值理解:
在深度学习中,卷积中的filters是不需要反转的。严格来说,它们是互相关的,本质上是执行逐元素的乘法和加法,在深度学习中我们称之为卷积。

二、深度学习中的卷积

2.1 卷积操作

我们先以最简单的单通道的卷积为例,来讲解深度学习中的卷积操作~


单通道的卷积操作
首先,我们使用3x3滤波器进行二维卷积运算:
左边是卷积层的输入,例如输入图像。右边是卷积滤波器(Filter),也叫核(Kernel)。由于滤波器的形状是3x3,这被称为3x3卷积。

我们通过在输入上滑动这个滤波器来执行卷积运算。在每个位置,我们都进行逐元素矩阵乘法并对结果求和。这个总和进入特征图(feature map)。卷积运算发生的绿色区域被称为感受野(receptive field)。由于滤波器的大小,感受野也是3x3。

这里的卷积核在左上角,卷积运算“4”的输出显示在结果的特征图中。
然后我们将滤波器向右滑动并执行相同的操作,将结果也添加到特征映射中。

我们继续这样做,并在特征图中聚合卷积结果。下面的动画展示了整个卷积运算。

2.2. 填充和步幅

Padding

在应用多层卷积时,我们常常丢失边缘像素。 由于我们通常使用小卷积核,因此对于任何单个卷积,我们可能只会丢失几个像素。 但随着我们应用许多连续卷积层,累积丢失的像素数就多了。 解决这个问题的简单方法即为填充(padding)

在输入图像的边界填充元素(通常填充元素是0)的方法叫做填充。


先看个动画直观感受下~ 灰色的区域是填充的地方


再来看看带填充的二维互相关运算~

通常, 如果我们添加 p h p_h ph​行填充 (大约一半在顶部, 一半在底部)和 p w p_w pw​列填充(左侧大约 一半, 右侧一半), 则输出形状将为

( n h − k h + p h + 1 ) × ( n w − k w + p w + 1 ) 。 \left(n_h-k_h+p_h+1\right) \times\left(n_w-k_w+p_w+1\right) 。 (nh​−kh​+ph​+1)×(nw​−kw​+pw​+1)。
这意味着输出的高度和宽度将分别增加 p h p_h ph​ 和 p w p_{w} pw​ 在许多情况下, 我们需要设置 p h = k h − 1 p_h=k_h-1 ph​=kh​−1 和 p w = k w − 1 p_w=k_w-1 pw​=kw​−1, 使输入和输出具有相同的高 度和宽度。这样可以在构建网络时更容易地预测每个图层的输出形状。假设 k h k_h kh​是奇数, 我们将在高度的两侧填充 p h / 2 p_h / 2 ph​/2行。如果 k h k_h kh​是偶数, 则一种可能性是在输入顶部填充 ⌈ p h / 2 ⌉ \left\lceil p_h / 2\right\rceil ⌈ph​/2⌉行, 在底部填充 ⌊ p h / 2 ⌋ \left\lfloor p_h / 2\right\rfloor ⌊ph​/2⌋行。同理, 我们填充宽度的两侧。

卷积神经网络中卷积核的高度和宽度通常为奇数,例如1、3、5或7。 选择奇数的好处是,保持空间维度的同时,我们可以在顶部和底部填充相同数量的行,在左侧和右侧填充相同数量的列。

此外,对于任何二维张量X,当满足: 1. 卷积核的大小是奇数; 2. 所有边的填充行数和列数相同; 3. 输出与输入具有相同高度和宽度 则可以得出:输出Y[i, j]是通过以输X[i, j]为中心,与卷积核进行互相关计算得到的。

Stride

在计算互相关时,卷积窗口从输入张量的左上角开始,向下、向右滑动。 在前面的例子中,我们默认每次滑动一个元素。 但是,有时候为了高效计算或是缩减采样次数,卷积窗口可以跳过中间位置,每次滑动多个元素。

我们将每次滑动元素的数量称为步幅(stride)。


先看个动画直观感受下~ 注意观察stride变大时,输出的特征图变小

  • stride =1
  • stride=2

再来看看不同步幅的二维互相关运算~
下图是垂直步幅为3,水平步幅为2的二维互相关运算。
着色部分是输出元素以及用于输出计算的输入和内核张量元素:0×0+0×1+1×2+2×3=8、0×0+6×1+0×2+0×3=6。

可以看到,为了计算输出中第一列的第二个元素和第一行的第二个元素,卷积窗口分别向下滑动三行和向右滑动两列。但是,当卷积窗口继续向右滑动两列时,没有输出,因为输入元素无法填充窗口(除非我们添加另一列填充)
通常, 当垂直步幅为 s h s_h sh​ 、水平步幅为 s w s_w sw​时, 输出形状为

⌊ ( n h − k h + p h + s h ) / s h ⌋ × ⌊ ( n w − k w + p w + s w ) / s w ⌋ . \left\lfloor\left(n_h-k_h+p_h+s_h\right) / s_h\right\rfloor \times\left\lfloor\left(n_w-k_w+p_w+s_w\right) / s_w\right\rfloor . ⌊(nh​−kh​+ph​+sh​)/sh​⌋×⌊(nw​−kw​+pw​+sw​)/sw​⌋.

如果我们设置了 p h = k h − 1 p_h=k_h-1 ph​=kh​−1和 p w = k w − 1 p_w=k_w-1 pw​=kw​−1, 则输出形状将简化为
⌊ ( n h + s h − 1 ) / s h ⌋ × ⌊ ( n w + s w − 1 ) / s w ⌋ \left\lfloor\left(n_h+s_h-1\right) / s_h\right\rfloor \times\left\lfloor\left(n_w+s_w-1\right) / s_w\right\rfloor ⌊(nh​+sh​−1)/sh​⌋×⌊(nw​+sw​−1)/sw​⌋ 。 更进一步, 如果输入的高度和宽度可以 被垂直和水平步幅整除, 则输出形状将为 ( n h / s h ) × ( n w / s w ) \left(n_h / s_h\right) \times\left(n_w / s_w\right) (nh​/sh​)×(nw​/sw​) 。


不同Padding和Stride 的卷积效果

No padding, no strides Arbitrary padding, no strides Half padding, no strides Full padding, no strides
No padding, strides Padding, strides Padding, strides (odd)

2.3 多输入通道和多输出通道

多输入通道

在许多应用程序中,我们处理的是具有多个通道的图像。

  • Eg1: RGB图像
    图像被表示为具有高度、宽度和深度的3D矩阵,其中深度对应于颜色通道(RGB)。
  • Eg2: 卷积神经网络的图层
    卷积网络层通常包含多个通道(通常为数百个通道),每个通道描述了上一层中不同的特征。

术语解释

在上文中, 我们简单的把filter 和kernel等同。但是本质上有所区别。在介绍多通道之前,我们有必要分清这些术语的差别。

  • kernels”指的是2D-权重矩阵。
  • filters”用于堆叠在一起的多个kernels的3D-结构。

对于2D-filters,filters与kernels相同。 但是对于3D-filters和深度学习中的大多数卷积而言,filters是kernels的集合。 每个kernels都是独一无二的,强调了输入通道的不同特征。


多输入通道卷积过程如下

  • 每个kernels应用到前一层的每个输入通道上,以生成一个输出通道。

  • 我们为所有kernels重复这样的过程以生成多个输出通道。

  • 然后将输出通道中的加在一起以形成单个输出通道。


计算多输入通道的互相关运算

输入包含多个通道时,需要构造一个与输入数据具有相同输入通道数的卷积核,以便与输入数据进行互相关运算。
假设输入的通道数为 c i c_i ci​,那么卷积核的输入通道数也需要为 c i c_i ci​。如果卷积核的窗口形状是 k h × k w k_h×k_w kh​×kw​,那么当 c i = 1 c_i=1 ci​=1时,我们可以把卷积核看作形状为 k h × k w k_h×k_w kh​×kw​的二维张量。
然而,当 c i > 1 c_i>1 ci​>1时,我们卷积核的每个输入通道将包含形状为 k h × k w k_h×k_w kh​×kw​的张量。将这些张量 c i c_i ci​连结在一起可以得到形状为 c i × k h × k w c_i×k_h×k_w ci​×kh​×kw​的卷积核。
由于输入和卷积核都有 c i c_i ci​个通道,我们可以对每个通道输入的二维张量和卷积核的二维张量进行互相关运算,再对通道求和(将ci的结果相加)得到二维张量。这是多通道输入和多输入通道卷积核之间进行二维互相关运算的结果。


再来看个动画直观感受下趴~

假设我们有一个32x32x3的图像,我们使用一个大小为5x5x3的滤波器(注意卷积滤波器的深度与图像的深度相匹配,都是3)。当滤波器位于特定位置时,它覆盖了输入的一小部分,然后我们执行上面描述的卷积操作。唯一不同的是,这次我们在3D而不是2D中做矩阵相乘的和,但结果仍然是一个标量。我们像上面那样在输入上滑动过滤器,并在每个位置执行卷积,将结果聚合在特征图中。该特征映射的大小为32x32x1,如图所示 :

多输出通道

如果我们使用10个不同的滤波器,我们将得到10个大小为32x32x1的特征映射,并将它们沿着深度维度堆叠,将得到卷积层的最终输出:大小为32x32x10的体积,如右边的大蓝框所示。
下面我们可以看到两个特征映射是如何沿着深度维度堆叠的。每个滤波器的卷积操作是独立执行的,得到的特征映射是不相交的。


让我们再来看看多输出通道的互相关运算~
如下图,我们采用具有3个输入通道和2个输出通道的1x1卷积核。 (输出通道数和核函数的组数相同,如浅蓝色和深蓝色两组,每一组核函数对应一个输出的2维特征图)

用 c i c_i ci​和 c o c_o co​分别表示输入和输出通道的数目,并让 k h k_h kh​和 k w k_w kw​为卷积核的高度和宽度。为了获得多个通道的输出,我们可以为每个输出通道创建一个形状为 c i × k h × k w c_i×k_h×k_w ci​×kh​×kw​的卷积核张量,这样卷积核的形状是 c o × c i × k h × k w c_o×c_i×k_h×k_w co​×ci​×kh​×kw​。在互相关运算中,每个输出通道先获取所有输入通道,再以对应该输出通道的卷积核计算出结果。

三、卷积类型

3.1 1D/2D/3D 卷积

  • 1D卷积: 主要用于输入是连续的,如文本或音频。
  • 2D卷积: 主要用于输入图像的地方。
  • 3D卷积-主要用于三维医学成像或检测视频中的事件。

卷积的维度是怎么定义的呢?这里卷积的维度可不是输入或者卷积核的维度哦~ 而是由卷积核的移动的维度来定的!
如下图所示: 当卷积核只能沿着x轴移动时,就是1D卷积; 当沿着x,y两个轴移动时就是2D卷积…以此类推

1D卷积 2D卷积 3D卷积
卷积核只能沿着x轴移动 卷积核可以沿着x轴,y轴移动 卷积核可以沿着x轴,y轴,z轴移动

为了说明卷积的维度和输入、卷积核、输出的维度无关,只和卷积核的移动维度有关,我们来看看几个例子 :


1D卷积-1D输入

  • 输入:一维
  • 卷积核:一维
  • 输出:一维
  • 卷积核向右移动

1D卷积-2D输入

  • 输入是二维的: input =[W,L]
  • 卷积核是二维的,卷积核的高度(L)和输入的高度(L)相同:filter = [k,L]
  • 输出时1维的:output = [W]
  • 如果有N个卷积核,则输出的大小是二维的(1DxN但此时依然是1D卷积(因为卷积核的移动方向只有1个维度)

2D卷积-3D输入 (LeNet,VGG都会用到这种卷积)

  • 输入是3D张量:input = [W,H,L]
  • 卷积核也是3D的,且卷积核的深度(L)和输入的通道数(L)相同: filter = [k,k,L]
  • 输出是一个二维张量: output = [W,H]
  • 此时卷积核只朝着x,y轴两个方向移动,因此是2D卷积。
  • 如果此时有N个卷积核,那么输出是3D张量(2DxN)。

假如有2个卷积核,则输出的大小是2Dx2。如下图所示:

1D 卷积


直观理解

在PyTorch中,分别在torch.nntorch.nn.functional两个模块都有conv1dconv2dconv3d;从计算过程来说,两者本身没有太大区别;但是torch.nn下的都是卷积层,conv的参数都是经过训练得到;torch.nn.functional下的都是函数,其参数可以人为设置。本文中,我们以torch.nn为例


torch.nn.Conv1d

# Class
torch.nn.Conv1d(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True, padding_mode='zeros', device=None, dtype=None)
  • 功能: 对由多个输入平面组成的输入信号应用1D卷积。

输入大小 ( N , C i n , L ) \left(N, C_{\mathrm{in}}, L\right) (N,Cin​,L) 输出大小 ( N , C out  , L out  ) \left(N, C_{\text {out }}, L_{\text {out }}\right) (N,Cout ​,Lout ​) 计算过程如下:

out ⁡ ( N i , C out  j ) = bias ⁡ ( C out  j ) + ∑ k = 0 C i n − 1 weight  ( C out  j , k ) ⋆ input ⁡ ( N i , k ) \operatorname{out}\left(N_i, C_{\text {out }j}\right)=\operatorname{bias}\left(C_{\text {out }j}\right)+\sum_{k=0}^{C_{i n}-1} \text { weight }\left(C_{\text {out }_j}, k\right) \star \operatorname{input}\left(N_i, k\right) out(Ni​,Cout j​)=bias(Cout j​)+k=0∑Cin​−1​ weight (Cout j​​,k)⋆input(Ni​,k)

其中 ⋆ \star ⋆ 是互相关(cross-correlation)运算符, N N N 是批大小(batch size), C C C表示通道数, L L L 是信号序列的长度。

  • 参数

    • in_channels (int) 输入图像中的通道数
    • out_channels (int) 由卷积产生的通道数
    • kernel_size (int or tuple) 卷积核的大小
    • stride (int or tuple, optional) 卷积的步幅。默认值:1
    • padding (int, tuple or str, optional) 在输入的两边添加填充。默认值:0
    • padding_mode (str*, optional*)'zeros', 'reflect', 'replicate' 或者 'circular'. 默认:'zeros'
    • dilation (int *or tuple, optional*) 核元素之间的间距。默认值:1 (具体理解见后文的【空洞卷积】)
    • groups (int*, optional*) 从输入通道到输出通道的阻塞连接数。默认值:1
    • bias (bool*, optional*) 如果为True,则向输出添加一个可学习偏差。默认值:True

上述的参数中,比较特殊的参数是:group
groups参数的含义:假设卷积操作的输入通道数是in_channels,输出通道数是out_channles,分组数是groups,分组卷积就是把原本的整体卷积操作分成groups个小组来分别处理,其中每个分组的输入通道数是in_channles / groups,输出通道数是out_channles / groups,最后将所有分组的输出通道数concat,得到最终的输出通道数
groups的值必须能整除in_channelsout_channels (具体理解请看本文【3.7分组卷积】)

  • group=1时,每一层输出由所有输入分别与卷积核卷积的累加得到
  • groups=2时,该操作等价于有两个并行的conv层,每个层看到一半的输入通道并产生一半的输出通道,然后两者都连接起来。
  • group=In_channel时,每个输入通道都与它自己的一组滤波器( s i z e = out_channels  in_channels  size=\frac{\text { out\_channels }}{\text { in\_channels }} size= in_channels  out_channels ​)进行卷积
group=1 group=3
  • 形状

    • 输入: ( N , C i n , L i n ) \left(N, C_{i n}, L_{i n}\right) (N,Cin​,Lin​) 或者 ( C i n , L i n ) \left(C_{i n}, L_{i n}\right) (Cin​,Lin​)
    • 输出: ( N , C out  , L out  ) \left(N, C_{\text {out }}, L_{\text {out }}\right) (N,Cout ​,Lout ​) 或者 ( C out  , L out  ) \left(C_{\text {out }}, L_{\text {out }}\right) (Cout ​,Lout ​), 其中

L out  = ⌊ L i n + 2 × padding  − dilation  × ( kernel_size  − 1 ) − 1 stride  + 1 ⌋ L_{\text {out }}=\left\lfloor\frac{L_{i n}+2 \times \text { padding }-\text { dilation } \times(\text { kernel\_size }-1)-1}{\text { stride }}+1\right\rfloor Lout ​=⌊ stride Lin​+2× padding − dilation ×( kernel_size −1)−1​+1⌋

  • 变量

    • weight (Tensor) - 模型的可学习权重,大小为 ( o u t _ c h a n n e l s , in_channels  groups  , k e r n e l _ s i z e ) (out\_channels, \frac{\text { in\_channels }}{\text { groups }}, kernel\_size) (out_channels, groups  in_channels ​,kernel_size). 这些权重是从 U ( − k , k ) \mathcal{U}(-\sqrt{k}, \sqrt{k}) U(−k ​,k ​) 中采样得到的。其中 k = groups  C in  ∗ kernel_size  k=\frac{\text { groups }}{C_{\text {in }} * \text { kernel\_size }} k=Cin ​∗ kernel_size  groups ​
    • bias (Tensor) - 模型的可学习偏置,大小为 ( o u t _ c h a n n e l s ) (out\_channels) (out_channels). 如果biasTrue, 那么这个值是从 U ( − k , k ) \mathcal{U}(-\sqrt{k}, \sqrt{k}) U(−k ​,k ​)中采样得到,其中 k = groups  C in  ∗ kernel_size  k=\frac{\text { groups }}{C_{\text {in }} * \text { kernel\_size }} k=Cin ​∗ kernel_size  groups ​
  • 例子

m = nn.Conv1d(16, 33, 3, stride=2)
input = torch.randn(20, 16, 50)
output = m(input)

2D卷积


直观理解

  • 单Filter
    如下图所示,可以将这个过程视作将一个3D-filters矩阵滑动通过输入层。注意,这个输入层和filters的深度都是相同的(即通道数=卷积核数)。
    这个 3D-filters仅沿着 2 个方向(图像的高和宽)移动(这也是为什么 3D-filters即使通常用于处理3D-体积数据,但这样的操作还是被称为 2D-卷积)。

  • 多Filters
    多Filters可实现在不同深度的层之间实现过渡
    假设输入层有 Din 个通道,而想让输出层的通道数量变成 Dout,我们需要做的仅仅是将 Dout个filters应用到输入层中。每一个filters都有Din个卷积核,都提供一个输出通道。在应用Dout个filters后,Dout个通道可以共同组成一个输出层。标准 2D-卷积,通过使用 Dout 个filters,将深度为 Din 的层映射为另一个深度为 Dout 的层。

数值理解


torch.nn.Conv2d

CLASS
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)
  • 功能: 对由多个输入平面组成的输入信号应用2D卷积。

输入大小 ( N , C i n , H , W ) \left(N, C_{\mathrm{in}}, H,W\right) (N,Cin​,H,W) 输出大小 ( N , C out  , H out  , W o u t ) \left(N, C_{\text {out }}, H_{\text {out }},W_{out}\right) (N,Cout ​,Hout ​,Wout​) 计算过程如下:

out ⁡ ( N i , C out  j ) = bias ⁡ ( C out  j ) + ∑ k = 0 C i n − 1 weight  ( C out  j , k ) ⋆ input ⁡ ( N i , k ) \operatorname{out}\left(N_i, C_{\text {out }j}\right)=\operatorname{bias}\left(C_{\text {out }j}\right)+\sum_{k=0}^{C_{i n}-1} \text { weight }\left(C_{\text {out }_j}, k\right) \star \operatorname{input}\left(N_i, k\right) out(Ni​,Cout j​)=bias(Cout j​)+k=0∑Cin​−1​ weight (Cout j​​,k)⋆input(Ni​,k)

其中 ⋆ \star ⋆ 是2D互相关(cross-correlation)运算符, N N N 是批大小(batch size), C C C表示通道数, L L L为输入平面的高度(像素), W W W为宽度(像素)。

  • 参数

    • in_channels (int) 输入图像中的通道数
    • out_channels (int) 由卷积产生的通道数
    • **kernel_size (int or tuple) –** 卷积核的大小
    • stride (int or tuple, optional) 卷积的步幅。默认值:1
    • padding (int, tuple or str, optional) 在输入的两边添加填充。默认值:0
    • padding_mode (str*, optional*)'zeros', 'reflect', 'replicate' 或者 'circular'. 默认:'zeros'
    • dilation (int *or tuple, optional*) 核元素之间的间距。默认值:1
    • groups (int*, optional*) 从输入通道到输出通道的阻塞连接数。默认值:1
    • bias (bool*, optional*) 如果为True,则向输出添加一个可学习偏差。默认值:True
  • 形状

    • 输入: ( N , C i n , H i n , W i n ) \left(N, C_{i n}, H_{in},W_{in}\right) (N,Cin​,Hin​,Win​) 或者 ( C i n , H i n , W i n ) \left(C_{i n}, H_{i n},W_{in}\right) (Cin​,Hin​,Win​)
    • 输出: ( N , C out  , H out  , W o u t ) \left(N, C_{\text {out }}, H_{\text {out }},W_{out}\right) (N,Cout ​,Hout ​,Wout​) 或者 ( C out  , H out  , W o u t ) \left(C_{\text {out }}, H_{\text {out }},W_{out}\right) (Cout ​,Hout ​,Wout​), 其中

H out  = ⌊ H in  + 2 × padding  [ 0 ] − dilation  [ 0 ] × ( kernel_size  [ 0 ] − 1 ) − 1 stride  [ 0 ] + 1 ⌋ W out  = ⌊ W in  + 2 × padding  [ 1 ] − dilation  [ 1 ] × ( kernel_size  [ 1 ] − 1 ) − 1 stride  [ 1 ] + 1 ∣ \begin{aligned}& H_{\text {out }}=\left\lfloor\frac{H_{\text {in }}+2 \times \text { padding }[0]-\text { dilation }[0] \times(\text { kernel\_size }[0]-1)-1}{\text { stride }[0]}+1\right\rfloor \\& W_{\text {out }}=\left\lfloor\frac{W_{\text {in }}+2 \times \text { padding }[1]-\text { dilation }[1] \times(\text { kernel\_size }[1]-1)-1}{\text { stride }[1]}+1 \mid\right.\end{aligned} ​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∣​

  • 变量

    • weight (Tensor) - 模型的可学习权重,大小为 ( o u t _ c h a n n e l s , in_channels  groups  , 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{\text { in\_channels }}{\text { groups }},kernel\_size[0], kernel\_size[1]) (out_channels, groups  in_channels ​,kernel_size[0],kernel_size[1]). 这些权重是从 U ( − k , k ) \mathcal{U}(-\sqrt{k}, \sqrt{k}) U(−k ​,k ​) 中采样得到的。其中 k = groups  C in  ∗ kernel_size  k=\frac{\text { groups }}{C_{\text {in }} * \text { kernel\_size }} k=Cin ​∗ kernel_size  groups ​
    • bias (Tensor) - 模型的可学习偏置,大小为 ( o u t _ c h a n n e l s ) (out\_channels) (out_channels). 如果biasTrue, 那么这个值是从 U ( − k , k ) \mathcal{U}(-\sqrt{k}, \sqrt{k}) U(−k ​,k ​)中采样得到,其中 groups  C i n ∗ ∏ i = 0 1 kernel_size  [ i ] \frac{\text { groups }}{C_{\mathrm{in}} * \prod_{i=0}^1 \text { kernel\_size }[i]} Cin​∗∏i=01​ kernel_size [i] groups ​
  • 例子

>>> # With square kernels and equal stride
>>> m = nn.Conv2d(16, 33, 3, stride=2)
>>> # non-square kernels and unequal stride and with padding
>>> m = nn.Conv2d(16, 33, (3, 5), stride=(2, 1), padding=(4, 2))
>>> # non-square kernels and unequal stride and with padding and dilation
>>> 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)

3D卷积

通过将2D-卷积的推广,在3D-卷积定义为filters的深度小于输入层的深度(即卷积核的个数小于输入层通道数),故3D-filters需要在三个维度上滑动(输入层的长、宽、高)。
在filters上滑动的每个位置执行一次卷积操作,得到一个数值。当filters滑过整个3D空间,输出的结构也是3D的。

3D卷积在执行时不仅在各自的通道中共享卷积核,而且在各帧(连续k帧)之间也共享卷积核;

  • 2D convolution: 使用场景一般是单通道的数据(例如MNIST),输出也是单通道,对整个通道同时执行卷积操作;
  • 2D convolution on multiple frames: 使用场景一般是多通道的数据(例如cifar-10),输出也是单通道,对整个通道同时执行卷积操作;2D卷积在执行时是在各自的通道中共享卷积核
  • 3D convolution: 使用场景一般是多帧(单/多通道)的frame-like数据(视频帧),且输出也是多帧,依次对连续k帧的整个通道同时执行卷积操作;

视觉角度: 先看个动画直观感受下~
如下图,一共有4个卷积核,其中frame1和frame2共享一个卷积核,frame2和frame3共享一个卷积核… 每个卷积核对应一个输出通道。


计算角度
假设现在有一个3帧的画面,且每一帧有2个通道,在时间维度的跨度为2帧,卷积核的宽度为3。

由于在时间维度的跨度为2帧,且每帧有2个通道,所以从“矩阵”个数来看的话,我们的卷积核应该有4矩阵。


torch.nn.Conv3d

CLASS
torch.nn.Conv3d(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True, padding_mode='zeros', device=None, dtype=None)
  • 功能: 对由多个输入平面组成的输入信号应用3D卷积。

输入大小 ( N , C i n , D , H , W ) \left(N, C_{\mathrm{in}},D, H,W\right) (N,Cin​,D,H,W) 输出大小 ( N , C out  , D o u t , H out  , W o u t ) \left(N, C_{\text {out }},D_{out}, H_{\text {out }},W_{out}\right) (N,Cout ​,Dout​,Hout ​,Wout​) 计算过程如下:

out ⁡ ( N i , C out  j ) = bias ⁡ ( C out  j ) + ∑ k = 0 C i n − 1 weight  ( C out  j , k ) ⋆ input ⁡ ( N i , k ) \operatorname{out}\left(N_i, C_{\text {out }j}\right)=\operatorname{bias}\left(C_{\text {out }j}\right)+\sum_{k=0}^{C_{i n}-1} \text { weight }\left(C_{\text {out }_j}, k\right) \star \operatorname{input}\left(N_i, k\right) out(Ni​,Cout j​)=bias(Cout j​)+k=0∑Cin​−1​ weight (Cout j​​,k)⋆input(Ni​,k)

其中 ⋆ \star ⋆ 是3D互相关(cross-correlation)运算符, N N N 是批大小(batch size), C C C表示通道数, L L L为输入平面的高度(像素), W W W为宽度(像素)。

  • 参数

    • in_channels (int) 输入图像中的通道数
    • out_channels (int) 由卷积产生的通道数
    • kernel_size (int or tuple) 卷积核的大小
    • stride (int or tuple, optional) 卷积的步幅。默认值:1
    • padding (int, tuple or str, optional) 在输入的两边添加填充。默认值:0
    • padding_mode (str*, optional*)'zeros', 'reflect', 'replicate' 或者 'circular'. 默认:'zeros'
    • dilation (int *or tuple, optional*) 核元素之间的间距。默认值:1
    • groups (int*, optional*) 从输入通道到输出通道的阻塞连接数。默认值:1
    • bias (bool*, optional*) 如果为True,则向输出添加一个可学习偏差。默认值:True
  • 形状

    • 输入: ( N , C i n , D i n , H i n , W i n ) \left(N, C_{i n}, D_{in},H_{in},W_{in}\right) (N,Cin​,Din​,Hin​,Win​) 或者 ( C i n , D i n , H i n , W i n ) \left(C_{i n}, D_{in},H_{i n},W_{in}\right) (Cin​,Din​,Hin​,Win​)
    • 输出: ( N , C out  , D o u t , H out  , W o u t ) \left(N, C_{\text {out }},D_{out}, H_{\text {out }},W_{out}\right) (N,Cout ​,Dout​,Hout ​,Wout​) 或者 ( C out  , D o u t , H out  , W o u t ) \left(C_{\text {out }}, D_{out},H_{\text {out }},W_{out}\right) (Cout ​,Dout​,Hout ​,Wout​), 其中

D out  = ⌊ D in  + 2 × padding  [ 0 ] − dilation  [ 0 ] × ( kernel_size  [ 0 ] − 1 ) − 1 stride  [ 0 ] + 1 ⌋ H out  = ⌊ H in  + 2 × padding  [ 1 ] − dilation ⁡ [ 1 ] × ( kernel_size  [ 1 ] − 1 ) − 1 stride  [ 1 ] + 1 ⌋ W out  = ⌊ W in  + 2 × padding  [ 2 ] − dilation ⁡ [ 2 ] × ( kernel_size  [ 2 ] − 1 ) − 1 stride  [ 2 ] + 1 ⌋ \begin{aligned}& D_{\text {out }}=\left\lfloor\frac{D_{\text {in }}+2 \times \text { padding }[0]-\text { dilation }[0] \times(\text { kernel\_size }[0]-1)-1}{\text { stride }[0]}+1\right\rfloor \\& H_{\text {out }}=\left\lfloor\frac{H_{\text {in }}+2 \times \text { padding }[1]-\operatorname{dilation}[1] \times(\text { kernel\_size }[1]-1)-1}{\text { stride }[1]}+1\right\rfloor \\& W_{\text {out }}=\left\lfloor\frac{W_{\text {in }}+2 \times \text { padding }[2]-\operatorname{dilation}[2] \times(\text { kernel\_size }[2]-1)-1}{\text { stride }[2]}+1\right\rfloor\end{aligned} ​Dout ​=⌊ stride [0]Din ​+2× padding [0]− dilation [0]×( kernel_size [0]−1)−1​+1⌋Hout ​=⌊ stride [1]Hin ​+2× padding [1]−dilation[1]×( kernel_size [1]−1)−1​+1⌋Wout ​=⌊ stride [2]Win ​+2× padding [2]−dilation[2]×( kernel_size [2]−1)−1​+1⌋​

  • 变量

    • weight (Tensor) - 模型的可学习权重,大小为 ( o u t _ c h a n n e l s , in_channels  groups  , 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{\text { in\_channels }}{\text { groups }},kernel\_size[0], kernel\_size[1]) (out_channels, groups  in_channels ​,kernel_size[0],kernel_size[1]). 这些权重是从 U ( − k , k ) \mathcal{U}(-\sqrt{k}, \sqrt{k}) U(−k ​,k ​) 中采样得到的。其中 k = groups  C i n ∗ ∏ i = 0 2 kernel_size  [ i ] k=\frac{\text { groups }}{C_{\mathrm{in}} * \prod_{i=0}^2 \text { kernel\_size }[i]} k=Cin​∗∏i=02​ kernel_size [i] groups ​
    • bias (Tensor) - 模型的可学习偏置,大小为 ( o u t _ c h a n n e l s ) (out\_channels) (out_channels). 如果biasTrue, 那么这个值是从 U ( − k , k ) \mathcal{U}(-\sqrt{k}, \sqrt{k}) U(−k ​,k ​)中采样得到,其中 groups  C i n ∗ ∏ i = 0 2 kernel_size  [ i ] \frac{\text { groups }}{C_{\mathrm{in}} * \prod_{i=0}^2 \text { kernel\_size }[i]} Cin​∗∏i=02​ kernel_size [i] groups ​
  • 例子

>>> # With square kernels and equal stride
>>> m = nn.Conv3d(16, 33, 3, stride=2)
>>> # non-square kernels and unequal stride and with padding
>>> m = nn.Conv3d(16, 33, (3, 5, 2), stride=(2, 1, 1), padding=(4, 2, 0))
>>> input = torch.randn(20, 16, 10, 50, 100)
>>> output = m(input)

3.2 1x1卷积

在这篇论文中 《Network In Network》 首次提出的了1x1的卷积

1×1卷积,即 k h = k w = 1 k_h=k_w=1 kh​=kw​=1,看起来似乎没有多大意义。 毕竟,卷积的本质是有效提取相邻像素间的相关特征,而1×1卷积显然没有此作用。 尽管如此,1×1仍然十分流行,经常包含在复杂深层网络的设计中。
因为使用了最小窗口,1×1卷积失去了卷积层的特有能力——在高度和宽度维度上,识别相邻元素间相互作用的能力。 其实1×1卷积的唯一计算发生在通道上。


直观理解
下图中描述了:在一个维度为 H x W x D的输入层上的操作方式。经过大小为 1 x 1 x D 的filters的 1 x 1 卷积,输出通道的维度为 H x W x 1。如果我们执行 N 次这样的 1 x 1 卷积,然后将这些结果结合起来,我们能得到一个维度为 H x W x N 的输出层。


1x1 卷积的优点

从上图来看,1x1的卷积表面上好像只是feature maps中的每个值乘了一个数,但实际上不仅仅如此,首先由于会经过激活层,所以实际上是进行了非线性映射,其次就是可以改变feature maps的channel数目。
在执行计算昂贵的 3 x 3 卷积和 5 x 5 卷积前,往往会使用 1 x 1 卷积来减少计算量。此外,它们也可以利用调整后的非线性激活函数来实现双重用途。

1 x 1卷积的一些优点是:

  • 降维以实现高效计算 (经过1 x 1卷积后,我们在深度方向上减小了尺度即减少通道数)
  • 高效的低维嵌入或特征池(假设原始输入有200个通道,则1 x 1卷积会将这些通道嵌入到单个通道中。)。
  • 卷积后再次应用非线性(在1 x 1卷积之后,可以添加非线性激活函数,例如ReLU,非线性允许网络学习更复杂的功能)。

3.3 转置卷积 (反卷积)

反卷积可以应用在生成对抗网络(GAN),的生成器上,大家可以参考DCGAN进行理解。

反卷积(deconvolution)也可以称为卷积转置或转置卷积(transposed convolution),但其并非卷积操作的反向操作。由上边的介绍可以看出,卷积操作会将输入映射到一个更小的特征图中,那么反卷积则可以将这个小的特征图映射为一个大的特征图。我们可以将其理解为上采样。


卷积矩阵
先理解一个概念,卷积矩阵:把卷积操作写成一个矩阵的形式,通过一次矩阵乘法就可以完成整个卷积操作。
卷积矩阵的构造是通过对卷积核的重排列构造的。

例如,对于一个3x3的卷积核
[ 1 4 1 1 4 3 3 3 1 ] \left[\begin{array}{lll} 1 & 4 & 1 \\ 1 & 4 & 3 \\ 3 & 3 & 1 \end{array}\right] ​113​443​131​ ​
可以重排得到卷积矩阵
[ 1 4 1 0 1 4 3 0 3 3 1 0 0 0 0 0 0 1 4 1 0 1 4 3 0 3 3 1 0 0 0 0 0 0 0 0 1 4 1 0 1 4 3 0 3 3 1 0 0 0 0 0 0 1 4 1 0 1 4 3 0 3 3 1 ] \left[\begin{array}{llllllllllllllll} 1 & 4 & 1 & 0 & 1 & 4 & 3 & 0 & 3 & 3 & 1 & 0 & 0 & 0 & 0 & 0 \\ 0 & 1 & 4 & 1 & 0 & 1 & 4 & 3 & 0 & 3 & 3 & 1 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 1 & 4 & 1 & 0 & 1 & 4 & 3 & 0 & 3 & 3 & 1 & 0 \\ 0 & 0 & 0 & 0 & 0 & 1 & 4 & 1 & 0 & 1 & 4 & 3 & 0 & 3 & 3 & 1 \end{array}\right] ​1000​4100​1400​0100​1010​4141​3414​0301​3010​3341​1334​0103​0030​0033​0013​0001​ ​
假设输入矩阵是:
[ 4 5 8 7 1 8 8 8 3 6 6 4 6 5 7 8 ] \left[\begin{array}{llll} 4 & 5 & 8 & 7 \\ 1 & 8 & 8 & 8 \\ 3 & 6 & 6 & 4 \\ 6 & 5 & 7 & 8 \end{array}\right] ​4136​5865​8867​7848​ ​

将输入矩阵转换为一个1x16的列向量
[ 4 5 8 7 1 8 8 8 3 6 6 4 6 5 7 8 ] \left[\begin{array}{llllllllllllllll} 4 & 5 & 8 & 7 & 1 & 8 & 8 & 8 & 3 & 6 & 6 & 4 & 6 & 5 & 7 & 8 \end{array}\right] [4​5​8​7​1​8​8​8​3​6​6​4​6​5​7​8​]
与卷积矩阵相乘后得:
[ 122 148 126 134 ] \left[\begin{array}{llll} 122 & 148 & 126 & 134 \end{array}\right] [122​148​126​134​]
再reshape成:
[ 122 148 126 134 ] \left[\begin{array}{ll} 122 & 148 \\ 126 & 134 \end{array}\right] [122126​148134​]

对比原始的卷积操作,以1为步长没有填充,那么卷积结果也为:
[ 122 148 126 134 ] \left[\begin{array}{ll} 122 & 148 \\ 126 & 134 \end{array}\right] [122126​148134​]


反卷积的操作

由此,我们可以得出:
当我们将反卷积矩阵进行转置,那么就可以得到一个16x4的转置卷积矩阵,对于输出的2x2的feature map,reshape为4x1,再将二者相乘即可得到一个16x1的转置卷积的结果
[ 1 0 0 0 4 1 0 0 1 4 0 0 0 1 0 0 1 0 1 0 4 1 4 1 3 4 1 4 0 3 0 1 3 0 1 0 3 3 4 1 1 3 3 4 0 1 0 3 0 0 3 0 0 0 3 3 0 0 1 3 0 0 0 1 ] × [ 122 148 126 134 ] = [ 2 9 6 1 6 29 30 7 10 29 33 13 12 24 16 4 ] ,  \left[\begin{array}{llll} 1 & 0 & 0 & 0 \\ 4 & 1 & 0 & 0 \\ 1 & 4 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 1 & 0 & 1 & 0 \\ 4 & 1 & 4 & 1 \\ 3 & 4 & 1 & 4 \\ 0 & 3 & 0 & 1 \\ 3 & 0 & 1 & 0 \\ 3 & 3 & 4 & 1 \\ 1 & 3 & 3 & 4 \\ 0 & 1 & 0 & 3 \\ 0 & 0 & 3 & 0 \\ 0 & 0 & 3 & 3 \\ 0 & 0 & 1 & 3 \\ 0 & 0 & 0 & 1 \end{array}\right] \times\left[\begin{array}{c} 122 \\ 148 \\ 126 \\ 134 \end{array}\right]=\left[\begin{array}{c} 2 \\ 9 \\ 6 \\ 1 \\ 6 \\ 29 \\ 30 \\ 7 \\ 10 \\ 29 \\ 33 \\ 13 \\ 12 \\ 24 \\ 16 \\ 4 \end{array}\right] \text {, } ​1410143033100000​0141014303310000​0000141014303310​0000014101430331​ ​× ​122148126134​ ​= ​2961629307102933131224164​ ​,

此时再reshape即可得到一个4x4的输出。
[ 2 9 6 1 6 29 30 7 10 29 33 13 12 24 16 4 ] \left[\begin{array}{cccc} 2 & 9 & 6 & 1 \\ 6 & 29 & 30 & 7 \\ 10 & 29 & 33 & 13 \\ 12 & 24 & 16 & 4 \end{array}\right] ​261012​9292924​6303316​17134​ ​
这样就通过转置卷积将2x2的矩阵反卷为一个4x4的矩阵,但是从结果也可以看出反卷积的结果与原始输入信号不同。只是保留了位置信息,以及得到了想要的形状。


不同Padding和Stride 的反卷积效果:

No padding, no strides, transposed Arbitrary padding, no strides, transposed Half padding, no strides, transposed Full padding, no strides, transposed
No padding, strides, transposed Padding, strides, transposed Padding, strides, transposed (odd)

1D反卷积


torch.nn.ConvTranspose1d

CLASS
torch.nn.ConvTranspose1d(in_channels, out_channels, kernel_size, stride=1, padding=0, output_padding=0, groups=1, bias=True, dilation=1, padding_mode='zeros', device=None, dtype=None)
  • 功能: 对由多个输入平面组成的输入信号应用1D反卷积。

  • 参数

    • in_channels (int) 输入图像中的通道数
    • out_channels (int) 由卷积产生的通道数
    • **kernel_size (int or tuple) –** 卷积核的大小
    • stride (int or tuple, optional) 卷积的步幅。默认值:1
    • padding (int, tuple or str, optional) 在输入的两边添加填充。默认值:0
    • padding_mode (str*, optional*)'zeros', 'reflect', 'replicate' 或者 'circular'. 默认:'zeros'
    • dilation (int *or tuple, optional*) 核元素之间的间距。默认值:1
    • groups (int*, optional*) 从输入通道到输出通道的阻塞连接数。默认值:1
    • bias (bool*, optional*) 如果为True,则向输出添加一个可学习偏差。默认值:True
  • 形状

    • 输入: ( N , C i n , L i n ) \left(N, C_{i n}, L_{i n}\right) (N,Cin​,Lin​) 或者 ( C i n , L i n ) \left(C_{i n}, L_{i n}\right) (Cin​,Lin​)
    • 输出: ( N , C out  , L out  ) \left(N, C_{\text {out }}, L_{\text {out }}\right) (N,Cout ​,Lout ​) 或者 ( C out  , L out  ) \left(C_{\text {out }}, L_{\text {out }}\right) (Cout ​,Lout ​), 其中

L o u t = ( L i n − 1 ) × s t r i d e − 2 × p a d d i n g + d i l a t i o n × ( k e r n e l _ s i z e − 1 ) + o u t p u t _ p a d d i n g + 1 L_{out}=(L_{in}−1)×stride−2×padding+dilation×(kernel\_size−1)+output\_padding+1 Lout​=(Lin​−1)×stride−2×padding+dilation×(kernel_size−1)+output_padding+1

  • 变量

    • weight (Tensor) - 模型的可学习权重,大小为 ( i n _ c h a n n e l s , out_channels  groups  , k e r n e l _ s i z e ) (in\_channels, \frac{\text { out\_channels }}{\text { groups }}, kernel\_size) (in_channels, groups  out_channels ​,kernel_size). 这些权重是从 U ( − k , k ) \mathcal{U}(-\sqrt{k}, \sqrt{k}) U(−k ​,k ​) 中采样得到的。其中 k = groups  C out  ∗ kernel_size  k=\frac{\text { groups }}{C_{\text {out }} * \text { kernel\_size }} k=Cout ​∗ kernel_size  groups ​
    • bias (Tensor) - 模型的可学习偏置,大小为 ( o u t _ c h a n n e l s ) (out\_channels) (out_channels). 如果biasTrue, 那么这个值是从 U ( − k , k ) \mathcal{U}(-\sqrt{k}, \sqrt{k}) U(−k ​,k ​)中采样得到,其中 k = groups  C out  ∗ kernel_size  k=\frac{\text { groups }}{C_{\text {out }} * \text { kernel\_size }} k=Cout ​∗ kernel_size  groups ​

2D反卷积


torch.nn.ConvTranspose2d

CLASS
torch.nn.ConvTranspose2d(in_channels, out_channels, kernel_size, stride=1, padding=0, output_padding=0, groups=1, bias=True, dilation=1, padding_mode='zeros', device=None, dtype=None)
  • 功能: 对由多个输入平面组成的输入信号应用2D反卷积。

  • 参数

    • in_channels (int) 输入图像中的通道数
    • out_channels (int) 由卷积产生的通道数
    • **kernel_size (int or tuple) –** 卷积核的大小
    • stride (int or tuple, optional) 卷积的步幅。默认值:1
    • padding (int, tuple or str, optional) 在输入的两边添加填充。默认值:0
    • padding_mode (str*, optional*)'zeros', 'reflect', 'replicate' 或者 'circular'. 默认:'zeros'
    • dilation (int *or tuple, optional*) 核元素之间的间距。默认值:1
    • groups (int*, optional*) 从输入通道到输出通道的阻塞连接数。默认值:1
    • bias (bool*, optional*) 如果为True,则向输出添加一个可学习偏差。默认值:True
  • 形状

    • 输入: ( N , C i n , H i n , W i n ) \left(N, C_{i n}, H_{in},W_{in}\right) (N,Cin​,Hin​,Win​) 或者 ( C i n , H i n , W i n ) \left(C_{i n}, H_{i n},W_{in}\right) (Cin​,Hin​,Win​)
    • 输出: ( N , C out  , H out  , W o u t ) \left(N, C_{\text {out }}, H_{\text {out }},W_{out}\right) (N,Cout ​,Hout ​,Wout​) 或者 ( C out  , H out  , W o u t ) \left(C_{\text {out }}, H_{\text {out }},W_{out}\right) (Cout ​,Hout ​,Wout​), 其中

H o u t = ( H i n − 1 ) × s t r i d e [ 0 ] − 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 ) + o u t p u t _ p a d d i n g [ 0 ] + 1 H_{out}=(H_{in}−1)×stride[0]−2×padding[0]+dilation[0]×(kernel\_size[0]−1)+output\_padding[0]+1 Hout​=(Hin​−1)×stride[0]−2×padding[0]+dilation[0]×(kernel_size[0]−1)+output_padding[0]+1

W o u t = ( W i n − 1 ) × s t r i d e [ 1 ] − 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 ) + o u t p u t _ p a d d i n g [ 1 ] + 1 W_{out}=(W_{in}−1)×stride[1]−2×padding[1]+dilation[1]×(kernel\_size[1]−1)+output\_padding[1]+1 Wout​=(Win​−1)×stride[1]−2×padding[1]+dilation[1]×(kernel_size[1]−1)+output_padding[1]+1

  • 变量

    • weight (Tensor) - 模型的可学习权重,大小为 ( i n _ c h a n n e l s , out_channels  groups  , k e r n e l _ s i z e [ 0 ] , k e r n e l _ s i z e [ 1 ] ) (in\_channels, \frac{\text { out\_channels }}{\text { groups }},kernel\_size[0], kernel\_size[1]) (in_channels, groups  out_channels ​,kernel_size[0],kernel_size[1]). 这些权重是从 U ( − k , k ) \mathcal{U}(-\sqrt{k}, \sqrt{k}) U(−k ​,k ​) 中采样得到的。其中 k = groups  C out  ∗ kernel_size  k=\frac{\text { groups }}{C_{\text {out }} * \text { kernel\_size }} k=Cout ​∗ kernel_size  groups ​
    • bias (Tensor) - 模型的可学习偏置,大小为 ( o u t _ c h a n n e l s ) (out\_channels) (out_channels). 如果biasTrue, 那么这个值是从 U ( − k , k ) \mathcal{U}(-\sqrt{k}, \sqrt{k}) U(−k ​,k ​)中采样得到,其中 groups  C o u t ∗ ∏ i = 0 1 kernel_size  [ i ] \frac{\text { groups }}{C_{\mathrm{out}} * \prod_{i=0}^1 \text { kernel\_size }[i]} Cout​∗∏i=01​ kernel_size [i] groups ​
  • 例子

>>> # With square kernels and equal stride
>>> m = nn.ConvTranspose2d(16, 33, 3, stride=2)
>>> # non-square kernels and unequal stride and with padding
>>> m = nn.ConvTranspose2d(16, 33, (3, 5), stride=(2, 1), padding=(4, 2))
>>> input = torch.randn(20, 16, 50, 100)
>>> output = m(input)
>>> # exact output size can be also specified as an argument
>>> input = torch.randn(1, 16, 12, 12)
>>> downsample = nn.Conv2d(16, 16, 3, stride=2, padding=1)
>>> upsample = nn.ConvTranspose2d(16, 16, 3, stride=2, padding=1)
>>> h = downsample(input)
>>> h.size()
torch.Size([1, 16, 6, 6])
>>> output = upsample(h, output_size=input.size())
>>> output.size()
torch.Size([1, 16, 12, 12])

3D反卷积


torch.nn.ConvTranspose2d

CLASS
torch.nn.ConvTranspose3d(in_channels, out_channels, kernel_size, stride=1, padding=0, output_padding=0, groups=1, bias=True, dilation=1, padding_mode='zeros', device=None, dtype=None)
  • 功能: 对由多个输入平面组成的输入信号应用3D反卷积。

  • 参数

    • in_channels (int) 输入图像中的通道数
    • out_channels (int) 由卷积产生的通道数
    • **kernel_size (int or tuple) –** 卷积核的大小
    • stride (int or tuple, optional) 卷积的步幅。默认值:1
    • padding (int, tuple or str, optional) 在输入的两边添加填充。默认值:0
    • padding_mode (str*, optional*)'zeros', 'reflect', 'replicate' 或者 'circular'. 默认:'zeros'
    • dilation (int *or tuple, optional*) 核元素之间的间距。默认值:1
    • groups (int*, optional*) 从输入通道到输出通道的阻塞连接数。默认值:1
    • bias (bool*, optional*) 如果为True,则向输出添加一个可学习偏差。默认值:True
  • 形状

    • 输入: ( N , C i n , D i n , H i n , W i n ) \left(N, C_{i n}, D_{in},H_{in},W_{in}\right) (N,Cin​,Din​,Hin​,Win​) 或者 ( C i n , D i n , H i n , W i n ) \left(C_{i n}, D_{in},H_{i n},W_{in}\right) (Cin​,Din​,Hin​,Win​)
    • 输出: ( N , C out  , D o u t , H out  , W o u t ) \left(N, C_{\text {out }},D_{out}, H_{\text {out }},W_{out}\right) (N,Cout ​,Dout​,Hout ​,Wout​) 或者 ( C out  , D o u t , H out  , W o u t ) \left(C_{\text {out }}, D_{out},H_{\text {out }},W_{out}\right) (Cout ​,Dout​,Hout ​,Wout​), 其中

D o u t = ( D i n − 1 ) × s t r i d e [ 0 ] − 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 ) + o u t p u t _ p a d d i n g [ 0 ] + 1 H o u t = ( H i n − 1 ) × s t r i d e [ 1 ] − 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 ) + o u t p u t _ p a d d i n g [ 1 ] + 1 W o u t = ( W i n − 1 ) × s t r i d e [ 2 ] − 2 × p a d d i n g [ 2 ] + d i l a t i o n [ 2 ] × ( k e r n e l _ s i z e [ 2 ] − 1 ) + o u t p u t _ p a d d i n g [ 2 ] + 1 D_{out}=(D_{in}−1)×stride[0]−2×padding[0]+dilation[0]×(kernel\_size[0]−1)+output\_padding[0]+1\\H_{out}=(H_{in}−1)×stride[1]−2×padding[1]+dilation[1]×(kernel\_size[1]−1)+output\_padding[1]+1\\W_{out}=(W_{in}−1)×stride[2]−2×padding[2]+dilation[2]×(kernel\_size[2]−1)+output\_padding[2]+1 Dout​=(Din​−1)×stride[0]−2×padding[0]+dilation[0]×(kernel_size[0]−1)+output_padding[0]+1Hout​=(Hin​−1)×stride[1]−2×padding[1]+dilation[1]×(kernel_size[1]−1)+output_padding[1]+1Wout​=(Win​−1)×stride[2]−2×padding[2]+dilation[2]×(kernel_size[2]−1)+output_padding[2]+1

  • 变量

    • weight (Tensor) - 模型的可学习权重,大小为 ( o u t _ c h a n n e l s , out_channels  groups  , 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{\text { out\_channels }}{\text { groups }},kernel\_size[0], kernel\_size[1]) (out_channels, groups  out_channels ​,kernel_size[0],kernel_size[1]). 这些权重是从 U ( − k , k ) \mathcal{U}(-\sqrt{k}, \sqrt{k}) U(−k ​,k ​) 中采样得到的。其中 k = groups  C o u t ∗ ∏ i = 0 2 kernel_size  [ i ] k=\frac{\text { groups }}{C_{\mathrm{out}} * \prod_{i=0}^2 \text { kernel\_size }[i]} k=Cout​∗∏i=02​ kernel_size [i] groups ​
    • bias (Tensor) - 模型的可学习偏置,大小为 ( o u t _ c h a n n e l s ) (out\_channels) (out_channels). 如果biasTrue, 那么这个值是从 U ( − k , k ) \mathcal{U}(-\sqrt{k}, \sqrt{k}) U(−k ​,k ​)中采样得到,其中 groups  C o u t ∗ ∏ i = 0 2 kernel_size  [ i ] \frac{\text { groups }}{C_{\mathrm{out}} * \prod_{i=0}^2 \text { kernel\_size }[i]} Cout​∗∏i=02​ kernel_size [i] groups ​
  • 例子

>>> # With square kernels and equal stride
>>> m = nn.ConvTranspose3d(16, 33, 3, stride=2)
>>> # non-square kernels and unequal stride and with padding
>>> m = nn.ConvTranspose3d(16, 33, (3, 5, 2), stride=(2, 1, 1), padding=(0, 4, 2))
>>> input = torch.randn(20, 16, 10, 50, 100)
>>> output = m(input)

3.4 扩张卷积(空洞卷积)

系统能以相同的计算成本,提供更大的感受野,扩张卷积在实时分割领域特别受欢迎。 在需要更大的观察范围,且无法承受多个卷积或更大的kennels,可以用它。

这篇论文中介绍了扩张卷积: 《Semantic Image Segmentation with Deep Convolutional Nets and Fully Connected CRFs》


直观理解:
直观上,空洞卷积通过在卷积核部分之间插入空间让卷积核膨胀。这个增加的参数 l (空洞率)表明了我们想要将卷积核放宽到多大。下图显示了当 l=1,2,4 时的卷积核大小(当 l=1 时,空洞卷积就变成了一个标准的卷积)。

  • (a) 图 对应3x31-dilated conv,和普通的卷积操作一样;
  • (b)图 对应3x32-dilated conv,实际的卷积 kernel size 还是 3x3,但是空洞为1,也就是对于一个7x7的图像patch,只有9个红色的点和3x3的kernel发生卷积操作,其余的点略过。也可以理解为kernel的size为7x7,但是只有图中的9个点的权重不为0,其余都为0。 可以看到虽然kernel size只有3x3,但是这个卷积的感受野已经增大到了7x7(如果考虑到这个2-dilated conv的前一层是一个1-dilated conv的话,那么每个红点就是1-dilated的卷积输出,所以感受野为3x3,所以1-dilated和2-dilated合起来就能达到7x7的conv);
  • (c )图 对应3x34-dilated conv操作,能达到15x15的感受野。对比传统的conv操作,3层3x3的卷积加起来,stride为1的话,只能达到(kernel-1)*layer+1=7的感受野,也就是和层数layer成线性关系,而dilated conv的感受野是指数级的增长。

由此,我们可以得出:
l=1时,感受野为 3 x 3l=2 时,感受野是 7 x 7l=3时,感受野增至 15x15。有趣的是,这些操作的参数数量本质上是相同的,不需要增加参数运算成本就能观察大的感受野。正因为此,空洞卷积常被用以低成本地增加输出单元上的感受野,同时还不需要增加卷积核大小,当多个空洞卷积一个接一个堆叠在一起时,这种方式是非常有效的。

3.5 可分离卷积

可分离卷积用于某些神经网络体系结构中,例如MobileNet。可以在空间上(空间可分离卷积)或在深度上(深度可分离卷积)进行可分离卷积。

空间可分离卷积


直观理解:
空间可分离卷积在图像的2D-空间维度(即高度和宽度)上运行。从概念上讲,空间可分离卷积将卷积分解为两个单独的运算。

单通道标准卷积 单通道可分离卷积

如上图所示 : 空间可分离卷积先用hx1的filter在高度上进行卷积,得到中间输出,然后再在该输出上使用1xw 的filter进行卷积。
空间可分离卷积就是2D卷积kernels的分解(在WH上的分解)。


数值理解:
对于下面显示的示例,将Sobel的kennel(3x3的kennel)分为3x1和1x3的kennel。
[ − 1 0 1 − 2 0 2 − 1 0 1 ] = [ 1 2 1 ] × [ − 1 0 1 ] \left[\begin{array}{lll} -1 & 0 & 1 \\ -2 & 0 & 2 \\ -1 & 0 & 1 \end{array}\right]=\left[\begin{array}{l} 1 \\ 2 \\ 1 \end{array}\right] \times\left[\begin{array}{lll} -1 & 0 & 1 \end{array}\right] ​−1−2−1​000​121​ ​= ​121​ ​×[−1​0​1​]
在原始卷积中,3x3的kennel直接与图像卷积。在空间可分离卷积中,3x1的kennel首先与图像进行卷积,然后应用1x3的kennel。在执行相同操作时,空间可分离卷积只需要6个参数而不是9个参数。

  • 原始卷积:用3 x 3的kennel(步长= 1,填充= 0)在5 x 5图像上进行卷积,需要在水平3个位置(垂直3个位置)上扫描的kennel,总共9个位置(在下图中以点表示)。在每个位置上,将应用9个按元素的乘法,总体来说,这是9 x 9 = 81个乘法运算。
  • 空间可分离卷积:我们首先在5 x 5图像上应用3 x 1的filter。我们在水平5个位置和垂直3个位置扫描这样的kennel,总的位置是5×3 = 15(表示为下面的图像上的点)。在每个位置,应用3个逐元素的乘法,那就是15 x 3 = 45个乘法运算。现在,我们获得了一个3 x 5的矩阵,此矩阵与1 x 3的kennel卷积,该kennel在水平3个位置和垂直3个位置扫描矩阵,总共9个位置。对于这9个位置中的每一个,将应用3个按元素的乘法,此步骤需要9 x 3 = 27个乘法运算。
  • 空间可分离卷积可以节省参数和运算成本

尽管空间可分离卷积节省了成本,但很少在深度学习中使用它。主要原因之一是并非所有kennels都可以分为两个较小的kennels。如果用空间可分离卷积代替所有传统的卷积,在训练过程中,我们将限制卷积核的类型,训练结果可能不是最佳的。

深度可分离卷积


直观理解:

标准的2D卷积 深度可分离卷积
使用128个3x3x3的filters 先分别使用3 个3x3x1卷积核, 然后再使用128个1

深度可分离卷积就是3D卷积kernels的分解(在深度channel上的分解


深度可分离卷积具体步骤:

  • 第一步:Depthwise Convolution
    这一步的卷积完全是在二维平面内进行,且Filter的数量与上一层的Depth相同。所以一个三通道的图像经过运算后生成了3个Feature map。
    我们在2D-卷积中分别使用 3 个卷积核(每个filter的大小为 3×3×1),而不使用大小为 3×3×3 的单个filter。每个卷积核仅对输入层的 1 个通道做卷积,这样的卷积每次都得出大小为 5×5×1的映射,之后再将这些映射堆叠在一起创建一个 5×5×3的特征图,最终得出一个大小为 5×5×3 的输出图像。这样的话,图像的深度保持与原来的一样。

Depthwise Convolution完成后的Feature map数量与输入层的depth相同,但是这种运算对输入层的每个channel独立进行卷积运算后就结束了,没有有效的利用不同map在相同空间位置上的信息。因此需要增加另外一步操作来将这些map进行组合生成新的Feature map,即接下来的Pointwise Convolution。

  • 第二步:Pointwise Convolution
    Pointwise Convolution的运算与常规卷积运算非常相似,不同之处在于卷积核的尺寸为 1×1×M,M为上一层的depth。所以这里的卷积运算会将上一步的map在深度方向上进行加权组合,生成新的Feature map。有几个Filter就有几个Feature map。
    我们用大小为 1×1×3卷积核做 1x1 卷积。每个 1×1×3卷积核对 5×5×3输入图像做卷积后都得出一个大小为 5×5×1的特征图。

    这样的话,做 128 次1x1 卷积后,就可以得出一个大小为 5×5×128 的层

参数对比
常规卷积的参数个数是:

N = 128 × 3 × 3 × 3 = 3456
# 卷积核的个数*卷积核的长*卷积核的宽*卷积核的通道数

Separable Convolution的参数由两部分相加得到:

N_depthwise = 3 × 3 × 3 = 27
N_pointwise = 1 × 1 × 3 × 128 = 384
N_separable = N_depthwise + N_pointwise = 411

相同的输入,同样是得到4张Feature map,深度可分离卷积的参数个数要比常规卷积小的多。因此,在参数量相同的前提下,采用深度可分离卷积的神经网络层数可以做的更深。

3.6 扁平卷积

论文 《Flattened Convolutional Neural Networks for Feedforward Acceleration》 介绍了扁平卷积(Flattened Convolution)。该论文认为通过使用由3D空间中所有方向上的1D-filters的连续序列组成的扁平化网络进行训练,可以提供与标准卷积网络相当的性能,并且由于学习参数的显着减少,计算成本要低得多。


直观理解:

标准的3D卷积 深度可分离卷积
应用一个标准filter将输入层映射到输出层 将标准filter分为3个1D-filters

扁平卷积与上述空间可分离卷积中的想法相似,其中空间filters是由两个rank-1 filters近似得到的。

3.7 分组卷积

分组卷积(Grouped convolution ),最早在AlexNet中出现,由于当时的硬件资源有限,训练AlexNet时卷积操作不能全部放在同一个GPU处理,因此作者把feature maps分给多个GPU分别进行处理,最后把多个GPU的结果进行融合。


直观理解:
在分组卷积中,filters被拆分为不同的组,每一个组都负责具有一定深度的传统 2D 卷积的工作。下图的例子表示得更清晰一些:

标准的2D卷积 具有两个filters的分组卷积

上图表示的是被拆分为 2 个filters组的分组卷积。在每个filters组中,其深度仅为传统2D-卷积的一 半 ( D i n / 2 ) \left(D_{i n} / 2\right) (Din​/2) ,而每个filters组都包含 D out  / 2 D_{\text {out }} / 2 Dout ​/2 个filters。第一个filters组 (红色) 对输入层的前 半部分做卷积 ( [ : , : , 0 : D i n / 2 ] ) \left.\left[:,:, 0: D_{i n} / 2\right]\right) [:,:,0:Din​/2]) ,第二个filters组 (蓝色) 对输入层的后半部分做卷积( [ : , : , D in  / 2 : D in  ] ) \left.\left[:,:, D_{\text {in }} / 2: D_{\text {in }}\right]\right) [:,:,Din ​/2:Din ​]) 。最终,每个filters组都输出了 D out  / 2 D_{\text {out }} / 2 Dout ​/2 个通道。整体上,两个组输出的通 道数为 2 × D out  / 2 = D out  2 \times D_{\text {out }} / 2=D_{\text {out }} 2×Dout ​/2=Dout ​ 。之后, 我们再将这些通道堆叠到输出层中,输出层就有了 D out  D_{\text {out }} Dout ​ 个 通道。


分组卷积可以节省参数量

卷积参数量的计算公式是:
(输入通道数 × 输出通道数 × 卷积核大 小 2 ) / 组数 = i n _ c h a n n e l s × o u t _ c h a n n e l s × k 2 / g r o u p s (输入通道数 \times 输出通道数 \times 卷积核大小^2 )/ 组数 =in\_channels\times out\_channels\times k^2/groups (输入通道数×输出通道数×卷积核大小2)/组数=in_channels×out_channels×k2/groups

不分组
分两组
分四组

用 k 、 c 1 、 c 2 k、c_1、c_2 k、c1​、c2​ 分别表示卷积核的宽度、输入通道数、输出通道数。
不分组卷积需要的参数量是: n = k 2 c 1 c 2 n=k^2c_1c_2 n=k2c1​c2​
分两组卷积需要的参数量是: n = k 2 c 1 c 2 2 n= \frac{k^2c_1c_2}{2} n=2k2c1​c2​​
分四组卷积需要的参数量是: n = k 2 c 1 c 2 4 n= \frac{k^2c_1c_2}{4} n=4k2c1​c2​​
以此类推,如果使用分组卷积,分为 m m m组,则卷积核的权重参数量为 n = k 2 c 1 c 2 m n=\frac {k^2c_1c_2}{m} n=mk2c1​c2​​,但是偏置值是不会被节省的,都是 c2 个。


代码理解

首先定义输入数据data

# 输入数据的维度[N, C, H, W] -> [1, 4, 1, 1]
data = torch.arange(4, dtype=torch.float32).view(1,4, 1, 1)
'''
data :
tensor([[[[0.]],[[1.]],[[2.]],[[3.]]]])
'''
  • group =1
    然后定义权重大小kernel_weight
kernel_weight = torch.nn.Parameter(torch.arange(16, dtype=torch.float32).view(4, 4, 1, 1))
'''
Parameter containing:
tensor([[[[ 0.]],[[ 1.]],[[ 2.]],[[ 3.]]],[[[ 4.]],[[ 5.]],[[ 6.]],[[ 7.]]],[[[ 8.]],[[ 9.]],[[10.]],[[11.]]],[[[12.]],[[13.]],[[14.]],[[15.]]]], requires_grad=True)
'''

groups=1,out_channels=4, 卷积核的大小是[4,4,1,1],每个卷积核的通道数:in_channels/groups=4/1=4
所有输入通道和每个卷积核进行卷积操作。

conv_groups_1 = nn.Conv2d(in_channels=4, out_channels=4, kernel_size=1, groups=1, bias=False)
conv_groups_1.weight = kernel_weight
conv_groups_1(data)
'''
tensor([[[[14.]],[[38.]],[[62.]],[[86.]]]], grad_fn=<ThnnConv2DBackward>)
'''

四个卷积核对应的权重分别是 k e r n e l 1 = [ 0 , 1 , 2 , 3 ] , k e r n e l 2 = [ 4 , 5 , 6 , 7 ] 、 k e r n e l 3 = [ 8 , 9 , 10 , 11 ] 、 k e r n e l 4 = [ 12 , 13 , 14 , 15 ] kernel_1=[0,1,2,3],kernel_2=[4,5,6,7]、kernel_3=[8,9,10,11]、kernel_4=[12,13,14,15] kernel1​=[0,1,2,3],kernel2​=[4,5,6,7]、kernel3​=[8,9,10,11]、kernel4​=[12,13,14,15]
四个通道的结果分别是:
输入通道1、2、3、4和卷积核1的卷积结果:
o u t _ c h a n n e l _ 1 = 0 × 0 + 1 × 1 + 2 × 2 + 3 × 3 = 14 out\_channel\_1 =0\times 0+1\times1+2\times2+3\times3=14 out_channel_1=0×0+1×1+2×2+3×3=14
输入通道1、2、3、4和卷积核2的卷积结果:
o u t _ c h a n n e l _ 2 = 0 × 4 + 1 × 5 + 2 × 6 + 3 × 7 = 38 out\_channel\_2 =0\times4+1\times5+2\times6+3\times7=38 out_channel_2=0×4+1×5+2×6+3×7=38
输入通道1、2、3、4和卷积核3的卷积结果:
o u t _ c h a n n e l _ 1 = 0 × 8 + 1 × 9 + 2 × 10 + 3 × 11 = 62 out\_channel\_1 =0\times8+1\times9+2\times10+3\times11=62 out_channel_1=0×8+1×9+2×10+3×11=62
输入通道1、2、3、4和卷积核4的卷积结果:
o u t _ c h a n n e l _ 2 = 0 × 12 + 1 × 13 + 2 × 14 + 3 × 15 = 85 out\_channel\_2 =0\times12+1\times13+2\times14+3\times15=85 out_channel_2=0×12+1×13+2×14+3×15=85

  • group=2
    定义权重大小kernel_weight_2, 卷积核的通道数 = i n _ c h a n n e l s / g r o u p s = 2 =in\_channels/groups=2 =in_channels/groups=2。
kernel_weight_2 = torch.nn.Parameter(torch.arange(8, dtype=torch.float32).view(4, 2, 1, 1))
'''
Parameter containing:
tensor([[[[0.]],[[1.]]],[[[2.]],[[3.]]],[[[4.]],[[5.]]],[[[6.]],[[7.]]]], requires_grad=True)
'''

输入通道和卷积核kernel通道都分成2组,前两个输入通道和前两组的卷积核进行卷积操作,后2个输入通道和后2组卷积核进行卷积操作。

conv_groups_2 = nn.Conv2d(in_channels=4, out_channels=4, kernel_size=1, groups=2, bias=False)
conv_groups_2.weight = kernel_weight_2
conv_groups_2(data)
'''
tensor([[[[ 1.]],[[ 3.]],[[23.]],[[33.]]]], grad_fn=<MkldnnConvolutionBackward>)
'''

前两个输入通道形状 [ 1 , 2 , 1 , 1 ] [1,2,1,1] [1,2,1,1],前两组卷积核形状 [ 2 , 2 , 1 , 1 ] [2,2,1,1] [2,2,1,1],卷积输出结果形状 [ 1 , 2 , 1 , 1 ] [1,2,1,1] [1,2,1,1]
后两个输入通道形状 [ 1 , 2 , 1 , 1 ] [1,2,1,1] [1,2,1,1],前两组卷积核形状 [ 2 , 2 , 1 , 1 ] [2,2,1,1] [2,2,1,1],卷积输出结果形状 [ 1 , 2 , 1 , 1 ] [1,2,1,1] [1,2,1,1]
将两组卷积结果进行concat,最终得到结果形状 [ 1 , 4 , 1 , 1 ] [1,4,1,1] [1,4,1,1]
4个卷积核对应的权重分别是 k e r n e l 1 = [ 0 , 1 ] , k e r n e l 2 = [ 2 , 3 ] , k e r n e l 3 = [ 4 , 5 ] , k e r n e l 4 = [ 6 , 7 ] kernel1=[0,1], kernel_2=[2,3], kernel_3=[4,5], kernel_4=[6,7] kernel1=[0,1],kernel2​=[2,3],kernel3​=[4,5],kernel4​=[6,7]
输入通道1、2和卷积核1的卷积结果: o u t _ c h a n n e l s _ 1 = 0 × 0 + 1 × 1 = 1 out\_channels\_1=0\times 0+1\times 1=1 out_channels_1=0×0+1×1=1
输入通道1、2和卷积核2的卷积结果: o u t _ c h a n n e l s _ 2 = 0 × 2 + 1 × 3 = 3 out\_channels\_2=0\times 2+1\times 3 =3 out_channels_2=0×2+1×3=3
输入通道3、4和卷积核3的卷积结果: o u t _ c h a n n e l s _ 3 = 2 × 4 + 3 × 5 = 23 out\_channels\_3=2\times 4+3\times 5=23 out_channels_3=2×4+3×5=23
输入通道3、4和卷积核4的卷积结果: o u t _ c h a n n e l s _ 4 = 2 × 6 + 3 × 7 = 33 out\_channels\_4=2 \times6+3\times 7=33 out_channels_4=2×6+3×7=33
这是groups=2的场景,groups=2时有2个分组,每个分组中包含2个卷积核,每组卷积核和2个输入通道进行卷积操作得到一组输出结果,将两组输出结果进行叠加得到最终的卷积结果

  • group =4
    g r o u p s = 4 , o u t c h a n n e l s = 4 groups=4,out_channels=4 groups=4,outc​hannels=4, 卷积核kernel大小为 [ 4 , 1 , 1 , 1 ] [4,1,1,1] [4,1,1,1], 每个卷积核的通道数: i n _ c h a n n e l s / g r o u p s = 1 in\_channels/groups =1 in_channels/groups=1
    4个卷积核对应的权重分别是 k e r n e l 1 = [ 0 ] , k e r n e l 2 = [ 1 ] , k e r n e l 3 = [ 2 ] , k e r n e l 4 = [ 3 ] kernel_1=[0],kernel_2=[1],kernel_3=[2],kernel_4=[3] kernel1​=[0],kernel2​=[1],kernel3​=[2],kernel4​=[3]
kernel_weight_4 = torch.nn.Parameter(torch.arange(4, dtype=torch.float32).view(4, 1, 1, 1))
'''
Parameter containing:
tensor([[[[0.]]],[[[1.]]],[[[2.]]],[[[3.]]]], requires_grad=True)
'''

输入通道1和卷积核1的卷积结果: o u t _ c h a n n e l _ 1 = 0 × 0 = 0 out\_channel\_1=0\times 0 =0 out_channel_1=0×0=0
输入通道2和卷积核2的卷积结果: o u t _ c h a n n e l _ 2 = 1 × 1 = 1 out\_channel\_2=1\times 1 =1 out_channel_2=1×1=1
输入通道3和卷积核3的卷积结果: o u t _ c h a n n e l _ 3 = 2 × 2 = 4 out\_channel\_3=2\times 2 =4 out_channel_3=2×2=4
输入通道4和卷积核4的卷积结果: o u t _ c h a n n e l _ 4 = 3 × 3 = 9 out\_channel\_4=3\times 3 =9 out_channel_4=3×3=9

conv_groups_4 = nn.Conv2d(in_channels=4, out_channels=4, kernel_size=1, groups=4, bias=False)
conv_groups_4.weight = kernel_weight_4
conv_groups_4(data)
'''
tensor([[[[0.]],[[1.]],[[4.]],[[9.]]]], grad_fn=<MkldnnConvolutionBackward>)
'''

groups=4时有4个分组,每个分组中包含1个卷积核,每组卷积核和1个输入通道进行卷积操作得到一组输出结果,将4组输出结果进行叠加得到最终的卷积结果。
groups=4的场景,也是groups等于输入通道数的场景和3.5节中的深度分离卷积相同


分组卷积的优点:

  • 第一个优点是有效的训练。由于卷积被划分为多个路径,因此每个路径可以由不同的GPU分别处理,此过程允许以并行方式在多个GPU上进行模型训练。与使用一个GPU进行所有训练相比,通过多GPU进行的模型并行化,可以将更多图像传到网络中。模型并行化被认为比数据并行化更好的方式,最终将数据集分成多个批次,然后我们对每个批次进行训练。但是,当批次大小变得太小时,与batch梯度下降相比,我们实际上是随机的,这将导致收敛变慢,有时甚至变差。
  • 第二个优点是模型更有效,即模型参数随着filters组数的增加而减小。
  • 第三个优点分组卷积可以提供比标准2D卷积更好的模型

参考


在整理的过程中,感谢如下文章对我的帮助和启发~

https://cs231n.github.io/convolutional-networks/
https://towardsdatascience.com/pytorch-basics-how-to-train-your-neural-net-intro-to-cnn-26a14c2ea29
https://arxiv.org/pdf/1603.07285.pdf
https://github.com/vdumoulin/conv_arithmetic
https://stackoverflow.com/questions/42883547/intuitive-understanding-of-1d-2d-and-3d-convolutions-in-convolutional-neural-n
https://www.zhihu.com/question/54149221
https://pytorch.org/docs/stable/nn.html#convolution-layers
https://zh.d2l.ai/chapter_convolutional-neural-networks/channels.html
https://blog.csdn.net/u012348774/article/details/104695411
https://blog.csdn.net/cxx654/article/details/109681004
https://blog.csdn.net/weixin_43135178/article/details/122426414

深度学习中的卷积操作相关推荐

  1. 一文详细介绍深度学习的各种卷积操作

    关注上方"深度学习技术前沿",选择"星标公众号", 资源干货,第一时间送达! 转自: 机器之心 我们都知道卷积的重要性,但你知道深度学习领域的卷积究竟是什么,又 ...

  2. odoo pivot中去掉求和_一文读懂深度学习中的卷积运算与图像处理

    华为人工智能认证讲师 袁梦 在人工智能深度学习技术中,有一个很重要的概念就是卷积神经网络 CNN(Convolutional Neural Networks).卷积神经网络被广泛地运用到计算机视觉中, ...

  3. (图解)一步一步使用CPP实现深度学习中的卷积

    (图解)一步一步使用CPP实现深度学习中的卷积 导语 卷积操作在深度学习中的重要性,想必大家都很清楚了.接下来将通过图解的方式,使用cpp一步一步从简单到复杂来实现卷积操作. 符号约定 F为输入; w ...

  4. 【人工智能】图文详解深度学习中的卷积神经网络(CNN)

    [人工智能]图文详解深度学习中的卷积神经网络(CNN) 概念和原理 为什么要使用卷积神经网络? 卷积神经网络简介 卷积神经网络的数学公式 池化操作: 全连接层: 激活函数 卷积神经网络的 C++ 实现 ...

  5. 干货|一文全解深度学习中的卷积

    来源:1024深度学习 概要:卷积现在可能是深度学习中最重要的概念.正是靠着卷积和卷积神经网络,深度学习才超越了几乎其他所有的机器学习手段. 译自Tim Dettmers的Understanding ...

  6. 卷积为什么如此强大?一文全解深度学习中的卷积

    卷积为什么如此强大?一文全解深度学习中的卷积 2018年05月10日 15:52:41 七月在线实验室 阅读数:17112 作者:Tim Dettmers(Understanding Convolut ...

  7. 卷积为什么如此强大?理解深度学习中的卷积

    译自Tim Dettmers的Understanding Convolution in Deep Learning有太多的公开课.教程在反复传颂卷积神经网络的好,却都没有讲什么是"卷积&qu ...

  8. 深度 | 理解深度学习中的卷积

    译者按:本文译自 Tim Dettmers 的 Understanding Convolution in Deep Learning.有太多的公开课.教程在反复传颂卷积神经网络的好,却都没有讲什么是「 ...

  9. 理解深度学习中的卷积

    译者按:本文译自 Tim Dettmers 的 Understanding Convolution in Deep Learning.有太多的公开课.教程在反复传颂卷积神经网络的好,却都没有讲什么是「 ...

最新文章

  1. bitmap设置图片尺寸缩小,避免内存溢出/OutOfMemoryError的优化方法
  2. [bzoj5405]platform
  3. argparse模块_Argparse:一个具体案例教会你python命令行参数解析
  4. 绿网天下:上云解决系统安全和安全合规
  5. EM算法最完整易懂讲解
  6. Oracle字符函数length substr concat实例
  7. 互联网轻量级框架SSM-查缺补漏第九天
  8. c语言中eles后面分号的作用,C语言 if else 语句详细讲解
  9. 专访 | 阿里前辈李智慧:如何培养架构思维,我把20年的架构经讲给你听
  10. 《我的眼睛--图灵识别》第八章:训练:图像字符切割
  11. java 调用 pb dll_[转载]一个java调用delphi写的dll问题,郁闷了一天一晚解决
  12. 空手套白狼的典型案例,利用各方资源一年狂赚300万!
  13. Jetson AGX Xavier 固态硬盘安装并挂载到/home与无线模块安装
  14. 科技大学计算机基础试卷答案,大学计算机基础期末考试试卷(带答案)
  15. 高通骁龙855发布,5G大幕拉开,新一轮手机大战在即
  16. HGOI 20190821 慈溪一中互测
  17. python 读取传入参数
  18. 支藏人元及五行四时旺衰
  19. android pak文件_xpak是什么文件 怎么安装xapk文件 和apk有什么区别
  20. visio2007 uml模板包

热门文章

  1. 广州女大学生价值观调查:近六成愿嫁“富二代”
  2. get请求URL传参url编码工具类
  3. session中的cookies设置及使用
  4. 如何用python画帆船_python 游戏(船只寻宝)
  5. websocket中自动生成身份编号(获取sessionID,将sid值设置为sessionID的方法),并在页面刷新时沿用sid的解决方案
  6. 史上巨大的在线开源英语近义词词典plus obsidian
  7. W5500EVB介绍
  8. 前端面试 vue生命周期钩子是如何实现的?理解vue中模板编译原理?
  9. 通过xlwings PIL 将excel图片导出
  10. VR局域网对战【捕鱼猎手】实战视频教程(上)-杨显峰-专题视频课程