# 9.5 基本卷积函数的变体

## 神经网络中的卷积与数学中卷积的不同

当在神经网络的上下文中讨论卷积时，我们通常不是特指数学文献中使用的那种标准的离散卷积运算。 实际应用中的函数略有不同。 这里我们详细讨论一下这些差异，并且对神经网络中用到的函数的一些重要性质进行重点说明。

### 区别一：多个并行卷积组成的运算

首先，当我们提到神经网络中的卷积时，我们通常是指由多个并行卷积组成的运算。 这是因为具有单个核的卷积只能提取一种类型的特征，尽管它作用在多个空间位置上。 我们通常希望网络的每一层能够在多个位置提取多种类型的特征。

### 区别二：输入不是实值，而是张量

另外，输入通常也不仅仅是实值的网格，而是由一系列观测数据的向量构成的网格。 例如，一幅彩色图像在每一个像素点都会有红绿蓝三种颜色的亮度。 在多层的卷积网络中，第二层的输入是第一层的输出，通常在每个位置包含多个不同卷积的输出。 当处理图像时，我们通常把卷积的输入输出都看作是3维的张量，其中一个索引用于标明不同的通道（例如红绿蓝），另外两个索引标明在每个通道上的空间坐标。 软件实现通常使用批处理模式，所以实际上会使用4维的张量，第四维索引用于标明批处理中不同的实例，但我们为简明起见这里忽略批处理索引。

> **\[success]**\
> \[样本ID]\[通道]\[空间坐标]\[空间坐标]

### 区别三：线性运算不可交换

因为卷积网络通常使用多通道的卷积，所以即使使用了核翻转， 也不一定保证网络的线性运算是可交换的。

> **\[success]** 问：不可交换会有什么影响？\
> 答：数学中的卷积需要不可交换性。DL中的卷积不需要不可交换性。核翻转可得到具有可交换性的卷积。因此DL不需要核翻转。

只有当其中的每个运算的输出和输入具有相同的通道数时，这些多通道的运算才是可交换的。

> **\[success]** 输入通道数 = 核通道数，输出通道数 = 核的个数，输入通道数和输出通道数可以不相等。通常输出通道数多于输入通道数。

假定我们有一个4维的核张量$K$，它的每一个元素是$&#x4B;_{i,j,k,l}$，表示输出中处于通道$i$的一个单元和输入中处于通道$j$中的一个单元的连接强度，并且在输出单元和输入单元之间有$k$行$l$列的偏移。 假定我们的输入由观测数据$V$组成，它的每一个元素是$V_{i,j,k}$，表示处在通道$i$中第$j$行第$k$列的值。 假定我们的输出$Z$和输入$V$具有相同的形式。 如果输出$Z$是通过对$K$和$V$进行卷积而不涉及翻转$K$得到的，那么

$$
\begin{aligned}
Z_{i,j,k} = \sum_{l,m,n} V_{l, j+m-1, k+n-1} K_{i,l,m,n}
\end{aligned}
$$

> **\[warning]** 什么是两个通道的强度连接？\
> j,k,m,n可以参考图9.1理解。\
> i,l不理解，为什么输入和输出不是同一通道？\
> 为什么K比Z、X高一维？

这里对所有的$l$，$m$和$n$进行求和是对所有（在求和式中）有效的张量索引的值进行求和。 在线性代数中，**向量的索引通常从1开始**，这就是上述公式中$-1$的由来。 但是像C或Python这类编程语言索引通常从0开始，这使得上述公式可以更加简洁。

## 步幅（stride）

我们有时会希望跳过核中的一些位置来降低计算的开销（相应的代价是提取特征没有先前那么好了）。 我们可以把这一过程看作是对全卷积函数输出的下采样(downsampling)。 如果我们只想在输出的每个方向上每间隔$s$个像素进行采样，那么我们可以定义一个下采样卷积函数$c$使得

$$
\begin{aligned}
Z_{i,j,k} = c(K, V, s)_{i,j,k} = \sum_{l,m,n} [V_{l,(j-1)\times s+m, (k-1)\times s +n,}
 K_{i,l,m,n}]
\end{aligned}
$$

我们把$s$称为下采样卷积的**步幅**(stride)。 当然也可以对每个移动方向定义不同的步幅。 图9.12演示了一个实例。

## 零填充

在任何卷积网络的实现中都有一个重要性质，那就是能够隐含地对输入$V$用零进行填充(pad)使得它加宽。 如果没有这个性质，表示的宽度在每一层就会缩减，缩减的幅度是比核少一个像素这么多。 对输入进行零填充允许我们对核的宽度和输出的大小进行独立的控制。 如果没有零填充，我们就被迫面临二选一的局面，要么选择网络空间宽度的快速缩减，要么选择一个小型的核——这两种情境都会极大得限制网络的表示能力。 图9.13给出了一个例子。

有三种零填充设定的情况值得注意。

> **\[success]**\
> 卷积的缺点：\
> （1）每做一次卷积，图像就会变小\
> 定义参数padding=p, stride=s，$n \times n \* f\times f -> \lfloor\frac{n+2p-f}{s}+1\rfloor$\
> （2）原图像边、角位置的信息在输出图像中作用小（信息丢失）。\
> 解决方法：填充padding

### 有效卷积

第一种是无论怎样都**不使用零填充**的极端情况，并且卷积核**只允许访问那些图像中能够完全包含整个核的位置**。 在MATLAB的术语中，这称为**有效卷积**。 在这种情况下，输出的所有像素都是输入中相同数量像素的函数，这使得输出像素的表示更加规范。 然而，输出的大小在每一层都会缩减。 如果输入的图像宽度是$m$，核的宽度是$k$，那么输出的宽度就会变成$m-k+1$。 如果卷积核非常大的话缩减率会非常显著。 因为缩减数大于0，这限制了网络中能够包含的卷积层的层数。 当层数增加时，网络的空间维度最终会缩减到$1\times 1$，这种情况下增加的层就不可能进行有意义的卷积了。

> **\[success]**\
> 要么网络空间宽度快速缩减，要么选择一个小型的核。\
> 极大地限制网络的表示能力\
> $n\times n \* f\times f = n-f+1 \times n-f+1$

### 相同卷积

第二种特殊的情况是**只进行足够的零填充来保持输出和输入具有相同的大小**。 在MATLAB的术语中，这称为相同卷积。 在这种情况下，只要硬件支持，网络就**能包含任意多的卷积层**，这是因为卷积运算不改变下一层的结构。。 然而，输入像素中靠近边界的部分相比于中间部分对于输出像素的影响更小。 这可能会导致**边界像素存在一定程度的欠表示**。

### 全卷积

这使得第三种极端情况产生了，在MATLAB中称为全卷积。 它进行了足够多的零填充使得每个像素在每个方向上恰好被访问了$k$次，最终输出图像的宽度为$m+k-1$。

> **\[info]**\
> 我们可以认为全卷积是在输入的两端各填充$k-1$个零，使得输入的每个像素都恰好被核访问$k$次，最终得到的输出的宽度为$\[m+2(k-1)]-k+1=m+k-1$

在这种情况下，输出像素中靠近边界的部分相比于中间部分是更少像素的函数。 这将导致**学得一个在卷积特征映射的所有位置都表现不错的单核更为困难**。

**通常零填充的最优数量（对于测试集的分类正确率）处于"有效卷积"和"相同卷积"之间的某个位置**。

> **\[success]**\
> kernel的大小f一般是奇数，这是因为：\
> 奇f的kernel有中心点，中心点有一些特征的作用。

## 局部连接

在一些情况下，我们并不是真的想使用卷积，而是想用一些局部连接的网络层。 在这种情况下，我们的多层感知机对应的邻接矩阵是相同的，但每一个连接都有它自己的权重，用一个6维的张量$W$来表示。 $W$的索引分别是：输出的通道$i$，输出的行$j$和列$k$，输入的通道$l$，输入的行偏置$m$和列偏置$n$。 局部连接层的线性部分可以表示为

$$
\begin{aligned}
Z_{i,j,k} = \sum_{l,m,n} [V_{l, j+m-1, k+n-1} w_{i, j, k, l, m, n}]. \text{这里应该这里应该是W?}
\end{aligned}
$$

这有时也被称为**非共享卷积**(unshared convolution)，因为它和具有一个小核的离散卷积运算很像，但并不横跨位置来共享参数。 图9.14比较了局部连接、卷积和全连接的区别。

> **\[success]**\
> 仅局部连接而不共享参数\
> 适用场景：特征只在一小块空间而不会出现在所有的空间上

当我们知道每一个特征都是一小块空间的函数并且相同的特征不会出现在所有的空间上时，局部连接层是很有用的。 例如，如果我们想要辨别一张图片是否是人脸图像时，我们只需要去寻找嘴是否在图像下半部分即可。

使用那些连接被更进一步限制的卷积或者局部连接层也是有用的，例如，限制每一个输出的通道$i$仅仅是输入通道$l$的一部分的函数时。 实现这种情况的一种通用方法是使输出的前$m$个通道仅仅连接到输入的前$n$个通道，输出的接下来的$m$个通道仅仅连接到输入的接下来的$n$个通道，以此类推。

> **\[success]** 这一段没看懂

图9.15给出了一个例子。 对少量通道间的连接进行建模允许网络使用更少的参数，这降低了存储的消耗以及提高了统计效率，并且减少了前向和反向传播所需要的计算量。 这些目标的实现并没有减少隐藏单元的数目。

## 平铺卷积

**平铺卷积**（tiled convoluution）对卷积层和局部连接层进行了折衷。 这里并不是对**每一个**空间位置的权重集合进行学习，我们学习一组核使得当我们在空间移动时它们可以循环利用。 这意味着在近邻的位置上拥有不同的过滤器，就像局部连接层一样，但是对于这些参数的存储需求仅仅会增长常数倍，这个常数就是核的集合的大小，而不是整个输出的特征映射的大小。 图9.16对局部连接层、平铺卷积和标准卷积进行了比较。

为了用代数的方法定义平铺卷积，令$K$是一个6维的张量，其中的两维对应着输出映射中的不同位置。 $K$在这里并没有对输出映射中的每一个位置使用单独的索引，输出的位置在每个方向上在$t$个不同的核组成的集合中进行循环。 如果$t$等于输出的宽度，这就是局部连接层了。

$$
\begin{aligned}
Z_{i, j, k} = \sum_{l, m, n} V_{l, j+m-1, k+n-1} K_{i, l, m, n, j\% t +1, k\% t+1},
\end{aligned}
$$

这里百分号是取模运算，它的性质包括$t\\% t =0, (t+1)\\% t = 1$等等。 在每一维上使用不同的$t$可以很容易对这个方程进行扩展。

> **\[warning]** 从这里开始后面的就完全看不懂了

局部连接层与平铺卷积层都和最大池化有一些有趣的关联：这些层的探测单元都是由不同的过滤器驱动的。 如果这些过滤器能够学会探测相同隐含特征的不同变换形式，那么最大池化的单元对于学得的变换就具有不变性（如\fig?所示）。 卷积层对于平移具有内置的不变性。

实现卷积网络时，通常也需要除卷积以外的其他运算。 为了实现学习，必须在给定输出的梯度时能够计算核的梯度。 在一些简单情况下，这种运算可以通过卷积来实现，但在很多我们感兴趣的情况下，包括步幅大于1的情况，并不具有这样的性质。

回忆一下卷积是一种线性运算，所以可以表示成矩阵乘法的形式（如果我们首先把输入张量变形为一个扁平的向量）。 其中包含的矩阵是关于卷积核的函数。 这个矩阵是稀疏的并且核的每个元素都复制给矩阵的多个元素。 这种观点能够帮助我们导出实现一个卷积网络所需的很多其他运算。

通过卷积定义的矩阵转置的乘法就是这样一种运算。 这种运算用于在卷积层反向传播误差的导数，所以它在训练多于一个隐藏层的卷积网络时是必要的。 如果我们想要从隐藏层单元重构可视化单元时，同样的运算也是需要的{cite?}。 重构可视化单元是本书第\\?部分的模型广泛用到的一种运算，这些模型包括自编码器、RBM和稀疏编码等等。 构建这些模型的卷积化的版本都要用到转置化卷积。 类似核梯度运算，这种输入梯度运算在某些情况下可以用卷积来实现，但在一般情况下需要用到第三种运算来实现。%这里不是很懂 必须非常小心地来使这种转置运算和前向传播过程相协调。 转置运算返回的输出的大小取决于三个方面：零填充的策略、前向传播运算的步幅以及前向传播的输出映射的大小。 在一些情况下，不同大小的输入通过前向传播过程能够得到相同大小的输出映射，所以必须明确地告知转置运算原始输入的大小。

这三种运算——卷积、从输出到权重的反向传播和从输出到输入的反向传播——对于训练任意深度的前馈卷积网络，以及训练带有（基于卷积的转置的）重建函数的卷积网络，这三种运算都足以计算它们所需的所有梯度。 对于完全一般的多维、多样例情况下的公式，完整的推导可以参考{Goodfellow-TR2010}。 为了直观说明这些公式是如何起作用的，我们这里给出一个二维单个样例的版本。

假设我们想要训练这样一个卷积网络，它包含步幅为$s$的步幅卷积，该卷积的核为$K$，作用于多通道的图像$V$，定义为$c(K, V, s)$，就像\eqn?中一样。 假设我们想要最小化某个损失函数$J(V, K)$。 在前向传播过程中，我们需要用$c$本身来输出$Z$，然后$Z$传递到网络的其余部分并且被用来计算损失函数$J$。 在反向传播过程中，我们会得到一个张量$G$满足$&#x47;_{i, j, k} = \frac{\partial}{\partial Z_{i, j, k\}} J(V, K)$。

为了训练网络，我们需要对核中的权重求导。 为了实现这个目的，我们可以使用一个函数 \begin{aligned} g(G, V, s)_{i, j, k, l} = \frac{\partial}{\partial K_{i, j, k, l\}} J(V, K) = \su&#x6D;_{m, n} G_{i, m, n} V\_{j, (m-1)\times s+k, (n-1)\times s+l}. \end{aligned}

如果这一层不是网络的底层，我们需要对$V$求梯度来使得误差进一步反向传播。 我们可以使用如下的函数 \begin{aligned} h(K, G, s)_{i, j, k} &=& \frac{\partial }{\partial V_{i, j, k\}} J(V, K)\ &=& \su&#x6D;_{\substack{l, m\ \text{s.t.}\ (l-1)\times s+m = j\}} \sum_{\substack{n, p\ \text{s.t.}\ (n-1)\times s +p = k\}} \su&#x6D;_&#x71; K_{q,i,m,p} G\_{q, l, n}. \end{aligned}

\chap?描述的自编码器网络，是一些被训练成把输入拷贝到输出的前馈网络。 一个简单的例子是\\,PCA\\,算法，将输入$x$拷贝到一个近似的重构值$r$，通过函数${W}^\top W x$来实现。 使用权重矩阵转置的乘法，就像\\,PCA\\,算法这种，在一般的自编码器中是很常见的。 为了使这些模型卷积化，我们可以用函数$h$来实现卷积运算的转置。 假定我们有和$Z$相同形式的隐藏单元$H$，并且我们定义一种重构运算 \begin{aligned} R = h(K, H, s). \end{aligned}

为了训练自编码器，我们会得到关于$R$的梯度，表示为一个张量$E$。 为了训练解码器，我们需要获得对于$K$的梯度，这通过$g(H, E, s)$来得到。 为了训练编码器，我们需要获得对于$H$的梯度，这通过$c(K, E, s)$来得到。 通过用$c$和$h$对$g$求微分也是可行的，但这些运算对于任何标准神经网络上的反向传播算法来说都是不需要的。

一般来说，在卷积层从输入到输出的变换中我们不仅仅只用线性运算。 我们一般也会在进行非线性运算前，对每个输出加入一些偏置项。 这样就产生了如何在偏置项中共享参数的问题。 对于局部连接层，很自然地对每个单元都给定它特有的偏置，对于平铺卷积，也很自然地用与核一样的平铺模式来共享参数。 对于卷积层来说，通常的做法是在输出的每一个通道上都设置一个偏置，这个偏置在每个卷积映射的所有位置上共享。 然而，如果输入是已知的固定大小，也可以在输出映射的每个位置学习一个单独的偏置。 分离这些偏置可能会稍稍降低模型的统计效率，但同时也允许模型来校正图像中不同位置的统计差异。 例如，当使用隐含的零填充时，图像边缘的探测单元接收到较少的输入，因此需要较大的偏置。
