卷积神经网络CNN是目前应用最广泛的模型之一,具有局部连接、权值共享等特点,是一种深层前馈神经网络。
卷积与池化是CNN中的两个核心操作。
题外话:因为这部分的核心知识应该是属于《信号与系统》这门课程的,但…我没学过,所以学起来应该会痛苦面具吧,摘录原书公式的同时我也尽量给出一些自己的理解
卷积的一个典型应用:给定输入信号 f ( τ ) f(\tau) f(τ)和系统响应 g ( τ ) g(\tau) g(τ),求系统的输出。
卷积的数学定义为: ( f ∗ g ) ( t ) = ∫ ∞ ∞ f ( τ ) g ( t − τ ) d τ (f*g)(t)=\int^{\infty}_{\infty}f(\tau)g(t-\tau)d\tau (f∗g)(t)=∫∞∞f(τ)g(t−τ)dτ,这个过程就是 g g g经过翻转和平移后,与 f f f乘再求积分,如下图展示:
之后给出了一个卷积在图像处理中的例子,与GNN关系不大所以不多说,只是注意一下卷积的过程:卷积核和图像按对应位置进行卷积(矩阵求内积)。
卷积定理:将时域中复杂的卷积运算转换为频域中简单的相乘运算: ( f ∗ g ) ( t ) ⇔ F ( w ) G ( w ) (f*g)(t)\Leftrightarrow F(w)G(w) (f∗g)(t)⇔F(w)G(w)
想到GCN论文里前半部分就有相当一部分章节在说这个事儿,但我当时就没太看懂,再次痛苦面具。
卷积定理的关键预备知识就是傅里叶变换,其将时域中的数据转换到频域,将函数分解为一些列不同频率的三角函数的叠加,可以将其理解为从另一个维度去看数据。
回到图像处理的例子,卷积实际上就是将图像和卷积核都变换到频域,卷积核作为一个滤波器进行处理,如果是一个低通滤波器,那么就会过滤掉较高频率的部分,过滤后的图像就会丢失一些细节的部分,因为高频对应着剧烈变化的区域,比如图像的边缘和细节。
当然,深度学习中实际的卷积操作和前面提到的信号处理的卷积还是有所不同的。
单通道输入下的深度学习中的卷积定义为: H m , n = Σ i = − ⌊ 2 k ⌋ ⌊ 2 k ⌋ Σ j = − ⌊ 2 k ⌋ ⌊ 2 k ⌋ X m + i , n + j G i , j H_{m,n}=\Sigma^{\lfloor\frac{2}{k}\rfloor}_{i=-\lfloor\frac{2}{k}\rfloor}\Sigma^{\lfloor\frac{2}{k}\rfloor}_{j=-\lfloor\frac{2}{k}\rfloor}X_{m+i,n+j}G_{i,j} Hm,n=Σi=−⌊k2⌋⌊k2⌋Σj=−⌊k2⌋⌊k2⌋Xm+i,n+jGi,j,这里卷积核是一个 k × k k\times k k×k的矩阵。这个卷积的过程形象地理解一下就是带着卷积核在输入矩阵里溜达一圈,每到一个地方就卷积一次(矩阵求内积),然后把结果存一下,全卷一遍就得到了结果,以下图为例辅助理解:
卷积核就是贴着输入矩阵走了一圈算了一圈,每一次算都是矩阵的内积运算,得到一个数,最后得到的所有数再组成一个新的矩阵。
实际的计算中可能会使用多个卷积核,每个卷积核运算后再拼接起来,将结果变成了一个三维的张量。
可以看到,输入在卷积过后维度减小了,这会导致两个问题:
为解决这一问题,通常会填充边缘的值为0使得原始的输入矩阵维度增大(这一过程称作padding)。
为了保证卷积后的输出和输入的维度一致,填充多少0与卷积核的形状是相关的,对于 k × k k\times k k×k的卷积核,需要取 P = ⌊ k 2 ⌋ P=\lfloor\frac{k}{2}\rfloor P=⌊2k⌋,在输入矩阵填充 P P P圈0。
另外,有时候卷积不需要每个位置都卷一次,可以设置不长来调整卷积核的移动,使得卷积核每隔几步卷积一次。
多通道实际指的就是输入不是一个二维矩阵而是多维的: X ∈ R H × W × C X\in R^{H\times W\times C} X∈RH×W×C,其中 C C C表示输入的通道数。假设单通道每个卷积核仍然是 k × k k\times k k×k的,那么多通道的卷积核就是 G c ′ ∈ R k × k × C G^{c'}\in R^{k\times k\times C} Gc′∈Rk×k×C。
具体的卷积过程和单通道的一致,只不过是多了一个维度做了更多的卷积:
同样也可以有多个卷积核,即卷积核再升一个维度即可,得到的输出也会多一个维度。
卷积完成后,一般还会加入一个偏置。
回顾一下深度学习中的卷积过程,对于输入 H ∈ R H × W × C H\in R^{H\times W\times C} H∈RH×W×C,卷积核 G ∈ R k × k × C × C ′ G\in R^{k\times k\times C\times C'} G∈Rk×k×C×C′(C‘个不同卷积核),最后得到的输出 H ∈ R H ′ × W ′ × C ′ H\in R^{H'\times W' \times C'} H∈RH′×W′×C′,引入的参数总量为 k 2 × C × C ′ + C ′ k^2\times C\times C'+C' k2×C×C′+C′(C’个偏置)。
卷积核大小、填充和步长都会影响输出的维度,假设输入为 H × W × C H\times W\times C H×W×C,卷积核大小 k × k × C k\times k\times C k×k×C,有 C ′ C' C′个不同的卷积核,填充值 p p p,步长为 s s s,输出的维度 H ′ × W ′ × C ′ H'\times W' \times C' H′×W′×C′。可以计算: H ′ = ⌊ H + 2 p − k s ⌋ + 1 , W ′ = ⌊ W + 2 p − k s ⌋ + 1 H'=\lfloor\frac{H+2p-k}{s}\rfloor+1,W'=\lfloor\frac{W+2p-k}{s}\rfloor+1 H′=⌊sH+2p−k⌋+1,W′=⌊sW+2p−k⌋+1。
池化的主要目的是降维,以此降低计算量,并在训练初期提供平移不变性(后续会解释)。
常用的池化操作有平均池化和最大值池化。
具体操作就是用一个固定大小的滑动窗口在输入上滑动,每次将窗口内的元素聚合为一个元素(取平均、取最大),如下图所示:
一般来说,滑动窗口的大小为 k × k k\times k k×k,一般取 2 × 2 2\times 2 2×2,滑动的步长会取与窗口大小相等的值,就也会取 2 2 2。对于多通道,池化会逐窗口进行,不影响通道数。最后池化后输入就会在长和宽上减半。
卷积神经网络就是卷积层和池化层堆叠得到的。
以用于图像识别的AlexNet为例来看CNN的结构。AlexNet有5个卷积层,2个池化层,3个全连接层组成。
AlexNet的结构为:
AlexNet除了结构上给出了深层的CNN网络,还提出了两个改进:使用ReLU避免梯度消失;使用Dropout避免过拟合(Dropout是在训练时随机的将部分位置置0,相当于放弃某些信息,强迫模型在信息更少的情况下进行推断,好让其学习到更具有鲁棒性和判别性更好的特征)。
我们可以看到CNN网络结构的两个部分:卷积层+池化层;还有最后的全连接层。券链接层就是将卷积得到的特征展平,放弃空间信息,聚合全局信息并映射到输出空间。
卷积每次计算都只计算卷积核的部分,所以输入和输出是局部连接的。比如1个 5 × 5 5\times5 5×5的输入矩阵被一个 3 × 3 3\times3 3×3的卷积核卷积两次后就只剩下一个元素了,这一个元素也只与这个 5 × 5 5\times5 5×5的输入有关,被称作感知野。
输入中不同的区域使用相同的卷积核,这就带来了平移不变性:不管输入如何平移,输出总是相同的。
池化也带来了平移不变性:比如最大值池化,那么即便卷积后的结果有变化,只要最大值不变,池化结果也不变。
这个就比较抽象了,简单来说,就是因为卷积网络是通过卷积层堆叠得到的,底层的卷积可能还和原始输入关联较大,但更高层的卷积对应的特征就更抽象了。一般来说低层次的特征具有通用性,而高层次的特征则和任务关联性更强(比如图像分类决定图像属于哪一类的特征)。
如图所示:
两个功能:
主要用于语义分割,应该是CV的内容,不太了解…
书里解释的也比较乱,但大体上来说就是转置卷积反而可以增加输入的长款维度,如图所示:
图a中,一个 2 × 2 2\times2 2×2的输入, 3 × 3 3\times3 3×3的卷积核,输入填充了两圈0,最后的输出是 4 × 4 4\times4 4×4的。
图b给出了更一般的过程,步长 s s s,填充 p p p, k × k k\times k k×k的卷积核,最后会在外围填充 k − 1 − p k-1-p k−1−p圈0,输入之间会被插入 s − 1 s-1 s−1个0,再进行卷积。
和转置卷积扩张输入相反,空洞卷积会扩张卷积核填入0值,如下图所示:
但卷积核的参数仍然保持不变,所以空洞卷积是不增加参数而扩大感知野的方法。
空洞率 r r r用于控制扩张程度,其会在卷积核中间插入 r − 1 r-1 r−1个0,之后卷积核会变为 k + ( k − 1 ) ( r − 1 ) k+(k-1)(r-1) k+(k−1)(r−1)维。
就是输入分组,每组分别卷积,最后的输出再结合起来。因为输入分组了,所以卷积核实际上也会变小,如下图:
好处是卷积可以并行进行了,比如每组在不同的GPU上跑,可以加快卷积计算的速度,同时使用参数的数量也减少了。
书里解释的不多,直接看书上写的会更清楚一点,我就不自己总结了:
深度可分离卷积由两部分组成,一部分是沿着深度的逐层卷积;另一部分是1×1卷积。沿着深度的逐层卷积是分组卷积的一种特殊情况,当g=C1=C2时,它相当于为每一个输入通道设定了一个卷积核分别进行卷积。由于这种卷积只利用了单个输入通道的信息,即只使用了空间位置上的信息,而没有使用通道间的信息,因此,其后通常使用1×1卷积来增加通道间的信息。
还是基本都是CV中的内容,这里就特别记录一个用到了GNN里的内容:ResNet。
这里提一点我之前学习GCN的时候的认识,GCN一个比较致命的缺点就是深层GCN的过度平滑,即所有节点的表达会趋于统一,因为GCN每卷一层就会收集更远一跳邻居的信息,邻居的邻居这样的展开很容易遍布全图,这就让最后每个节点都在学全图的表达,导致节点之间没有了区分度,这让GCN不好做深层。我最早接触GNN的课程项目就是尝试更深层的GCN,是个组队任务。我和队友的朴素的想法就是给每层加权让更深层的权值更低好让节点更多学到离自己更近的邻居的特征,更远的邻居只是稍微知道一下就好。为了让这个权值是可训练的所以就改成了在每层GCN中间加一个线性层来训练权值。当然最后效果…并不好。只是比裸的深层GCN准确率高了一点点,但比2层的GCN准确率低了好多。我们后来读到了JK-Net这篇论文,大概的思路也是如此,但论文作者考虑的更多,实现上也更好,最后准确率也很高。另外一种可能的做法就是引入ResNet,但当时啥也不懂所以也没尝试过。实际上,将CV的内容引入GNN的尝试有很多,比如另一个非常有名的GNN模型GAT,就是将CV中的Attention机制加入到了GNN中。
不多扯了,ResNet的模型就如下图所示:
可以看到ResNet的输出是输入与卷积后的结果共同决定的,而不是单纯的只有卷积的结果。
这样的好处有:前向传播的时候输入和原本的输出信息会融合,能够更好地利用特征(就相当于我卷积可能卷过头了,就像前面提到的GCN卷了太多层邻居的信息导致周围邻居的信息反而学不到了,再把输入丢回来好找回卷积过程中可能丢掉的信息);反向传播的时候有一部分梯度通过输入和输出的直接连接传回输入,可以缓解梯度消失的问题。
ResNet之后也带来了很多的改进后的模型,比如DenseNet,这个模型之后也被用到过GCN中。