深度学习中的卷积操作

时间:2022-12-15 22:52:34

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

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

一、前言

1.1 数学中的卷积操作

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


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


数值理解

  • 连续函数的卷积
    在数学中, 两个函数(比如 f , g f, g f,g : R d → R \mathbb{R}^d \rightarrow \mathbb{R} RdR )之间的 “卷积”被定义为
    ( 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} . (fg)(x)=f(z)g(xz)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) . (fg)(i)=af(a)g(ia).

  • 二维函数的卷积
    对于二维张量, 则为 f f f的索引 ( a , b ) (a, b) (a,b) g g g的索引 ( i − a , j − b ) (i-a, j-b) (ia,jb)上的对应加和:
    ( 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) . (fg)(i,j)=abf(a,b)g(ia,jb).

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) 。 (nhkh+ph+1)×(nwkw+pw+1)
这意味着输出的高度和宽度将分别增加 p h p_h ph p w p_{w} pw 在许多情况下, 我们需要设置 p h = k h − 1 p_h=k_h-1 ph=kh1 p w = k w − 1 p_w=k_w-1 pw=kw1, 使输入和输出具有相同的高 度和宽度。这样可以在构建网络时更容易地预测每个图层的输出形状。假设 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 . (nhkh+ph+sh)/sh×(nwkw+pw+sw)/sw.

如果我们设置了 p h = k h − 1 p_h=k_h-1 ph=kh1 p w = k w − 1 p_w=k_w-1 pw=kw1, 则输出形状将简化为
⌊ ( 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+sh1)/sh×(nw+sw1)/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=0Cin1 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

  • 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,HW) 输出大小 ( 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=0Cin1 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+1Wout = 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]} Cini=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,DHW) 输出大小 ( 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=0Cin1 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+1Hout = stride [1]Hin +2× padding [1]dilation[1]×( kernel_size [1]1)1+1Wout = 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=Cini=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]} Cini=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] 113443131
可以重排得到卷积矩阵
[ 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] 1000410014000100101041413414030130103341133401030030003300130001
假设输入矩阵是:
[ 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] 4136586588677848

将输入矩阵转换为一个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] [4587188836646578]
与卷积矩阵相乘后得:
[ 122 148 126 134 ] \left[\begin{array}{llll} 122 & 148 & 126 & 134 \end{array}\right] [122148126134]
再reshape成:
[ 122 148 126 134 ] \left[\begin{array}{ll} 122 & 148 \\ 126 & 134 \end{array}\right] [122126148134]

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


反卷积的操作

由此,我们可以得出:
当我们将反卷积矩阵进行转置,那么就可以得到一个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 {, } 1410143033100000014101430331000000001410143033100000014101430331×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] 2610129292924630331617134
这样就通过转置卷积将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=(Lin1)×stride2×padding+dilation×(kernel_size1)+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=(Hin1)×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=(Win1)×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]} Couti=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=(Din1)×stride[0]2×padding[0]+dilation[0]×(kernel_size[0]1)+output_padding[0]+1Hout=(Hin1)×stride[1]2×padding[1]+dilation[1]×(kernel_size[1]1)+output_padding[1]+1Wout=(Win1)×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=Couti=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]} Couti=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] 121000121=121×[101]
在原始卷积中,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上的分解


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

  • 第一步: 在输入层上应用深度卷积。
    我们在2D-卷积中分别使用 3 个卷积核(每个filter的大小为 3×3×1),而不使用大小为 3×3×3 的单个filter。每个卷积核仅对输入层的 1 个通道做卷积,这样的卷积每次都得出大小为 5×5×1的映射,之后再将这些映射堆叠在一起创建一个 5×5×3的特征图,最终得出一个大小为 5×5×3 的输出图像。这样的话,图像的深度保持与原来的一样。
    深度学习中的卷积操作
  • 第二步是扩大深度
    我们用大小为 1×1×3卷积核做 1x1 卷积。每个 1×1×3卷积核对 5×5×3输入图像做卷积后都得出一个大小为 5×5×1的特征图。
    深度学习中的卷积操作
    这样的话,做 128 次1x1 卷积后,就可以得出一个大小为 5×5×128 的层
    深度学习中的卷积操作

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  个 通道。


分组卷积的优点:

  • 第一个优点是有效的训练。由于卷积被划分为多个路径,因此每个路径可以由不同的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://*.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