文章目录
神经翻译笔记6. 卷积神经网络及其在机器翻译中的应用
本章主要记录卷积神经网络(及其常见的附加操作,例如池化操作)的原理,及其在机器翻译中的使用方法。此外,在附录中,会填补前面在写RNN相关文章时的埋的坑QuasiRNN和SRU。卷积神经网络(CNN)在机器翻译上的应用比较尴尬,其提出时模型效果与RNN相比没有太过明显的优势(经典测试集WMT2014英德比SOTA高0.5个点),然后不到半年时间Transformer横空出世,带走了所有研究学者的目光,因此其在机器翻译领域地位稍显尴尬。此外笔者加速了计划的写作进度,所以对CNN这种在整个深度学习领域比较重要的网络结构本笔记不会写得特别深入,相比前面各章也省去了一些内容(例如TF的实现)。有需要的读者还需要参考其它资料
本文以CS224n 2019Winter的讲义、2020Winter的幻灯片和CS231讲义为主,涉及具体工作的内容来源于原始论文
CNN的基本原理
卷积
卷积的定义
本小节额外参考如何通俗易懂地解释卷积? - 马同学的回答 - 知乎
卷积本来是一个在泛函中的一个抽象概念。对函数 f f f和 g g g,两者卷积的连续定义为
( f ∗ g ) ( n ) = ∫ − ∞ ∞ f ( τ ) g ( n − τ ) d τ (f \ast g)(n) = \int_{-\infty}^{\infty}f(\tau)g(n-\tau)d\tau (f∗g)(n)=∫−∞∞f(τ)g(n−τ)dτ
离散卷积定义为
( f ∗ g ) ( n ) = ∑ τ = − ∞ ∞ f ( τ ) g ( n − τ ) (f \ast g)(n) = \sum_{\tau = -\infty}^\infty f(\tau)g(n-\tau) (f∗g)(n)=τ=−∞∑∞f(τ)g(n−τ)
在比较简单的离散卷积定义下,可以看一个比较直观的例子:假设有两枚骰子,掷出以后,两者点数加起来为4的概率是多少?记 f ( x ) f(x) f(x)为第一枚骰子投出 x x x的概率, g ( x ) g(x) g(x)为第二枚骰子掷出 x x x的概率,则两枚骰子点数加起来为4的概率为
f ( 1 ) g ( 3 ) + f ( 2 ) g ( 2 ) + f ( 3 ) g ( 1 ) f(1)g(3) + f(2)g(2) + f(3)g(1) f(1)g(3)+f(2)g(2)+f(3)g(1)
这就符合了离散卷积的定义,写成标准的形式就是
( f ∗ g ) ( 4 ) = ∑ m = 1 3 f ( 4 − m ) g ( m ) (f \ast g)(4) = \sum_{m=1}^3 f(4-m)g(m) (f∗g)(4)=m=1∑3f(4−m)g(m)
CNN中的基本二维卷积操作
从前述的例子中可以看出,卷积操作蕴含了一些滑动窗口的思想在里面,而具体应用于CNN时,这种原理体现得更加明显。在CNN中,最简单的情况是使用一个小的二维矩阵(称之为卷积核kernel,通常大小为 3 × 3 3 \times 3 3×3或 5 × 5 5 \times 5 5×5)作用在一个输入上。在经典的CV领域中,输入一般也是一个二维矩阵。在这种情况下,记卷积核矩阵为 K ∈ R m × n \boldsymbol{K} \in \mathbb{R}^{m \times n} K∈Rm×n(通常 m = n m = n m=n),输入矩阵为 X ∈ R M × N \boldsymbol{X} \in \mathbb{R}^{M \times N} X∈RM×N,结果矩阵为 Y \boldsymbol{Y} Y,则计算二维卷积的过程可以描述为
- 计算 S u m ( K ⊙ X 0 , 0 m × n ) {\rm Sum}(\boldsymbol{K} \odot \boldsymbol{X}_{0,0}^{m \times n}) Sum(K⊙X0,0m×n)。其中 X i , j m × n \boldsymbol{X}_{i,j}^{m \times n} Xi,jm×n是 X \boldsymbol{X} X的一个大小为 m × n m\times n m×n的子矩阵,以 X i , j X_{i,j} Xi,j为子矩阵左上角的元素。 ⊙ \odot ⊙为两个矩阵的Hadamard乘积(逐元素乘积), S u m \rm Sum Sum函数求矩阵中所有元素的和。该操作得到一个标量,对应 Y 0 , 0 Y_{0,0} Y0,0(实际上,也可以看做是两个向量的内积操作)
- 将 K \boldsymbol{K} K“向右滑”一个步长 s s s,计算 S u m ( K ⊙ X 0 , s m × n ) {\rm Sum}(\boldsymbol{K} \odot \boldsymbol{X}_{0,s}^{m \times n}) Sum(K⊙X0,sm×n),对应 Y 0 , 1 Y_{0, 1} Y0,1……直至超出范围为止。最简单情况下, s = 1 s=1 s=1
- 将 K \boldsymbol{K} K拨回行起始处并“向下滑”一个步长 s s s,计算 S u m ( K ⊙ X s , 0 m × n ) {\rm Sum}(\boldsymbol{K} \odot \boldsymbol{X}_{s,0}^{m \times n}) Sum(K⊙Xs,0m×n),对应 Y 1 , 0 Y_{1, 0} Y1,0,然后重复第2步
- 重复第3步,直至超出范围为止
不难得到,当 s = 1 s=1 s=1时,结果矩阵 Y ∈ R ( M − m + 1 ) × ( N − n + 1 ) \boldsymbol{Y} \in \mathbb{R}^{(M - m + 1) \times (N - n + 1)} Y∈R(M−m+1)×(N−n+1)
对二维卷积操作的扩展
前述二维卷积操作是最基础的卷积操作。实际应用时,通常会在以下方面做扩展
- 信道(channel)。在CV领域中,输入一般都是包含三个信道的图像,三个信道分别对应R、G和B。对图像做二维卷积操作时,实际上卷积核在一个时刻会看到该区域在所有信道的值。一般地,假设输入的信道数为 C C C,卷积核的真实尺寸应该为 m × n × C m \times n \times C m×n×C(即对三维输入,二维卷积核仍然是三维的,只不过第三个维度永远和输入的第三个维度相同)。对输入应用该卷积核得到的结果可以看做是:先对每个信道 i i i做前述卷积操作得到结果 c i c_i ci,然后将所有的 c i c_i ci再相加。更泛化地说,每个卷积核实际上都是一个尺寸为 m × n × C m \times n \times C m×n×C的全连接网络
- 滤波器(filter)。一般情况下,在使用CNN时通常不止使用一个卷积核,而是使用 K K K个独立的卷积核。称这些卷积核组成一个大小为 K K K的滤波器(在闻名遐迩的斯坦福CS231n讲义中,并未做如此区分,而是直接将卷积核称为滤波器)。由于对三维输入做二维卷积得到的结果是一个二维矩阵,因此 K K K各卷积核卷积的结果通常再沿原来的第三维(信道维)展开,即某一个卷积层若输入有 C C C个信道,使用大小为 K K K的滤波器处理,则下一层的输入就有 K K K个信道
- 补齐(padding)。由前面的分析可以看到,每经过一次卷积操作,得到的结果尺寸一般都会变小,如果不做任何额外处理,经过若干层计算,更高层的输入就会越来越小。此外,在当前的计算方法中,四角元素只能被用到1次,其它四边元素只能被用到3次,其它元素会被用到9次,周边元素会隐含地被降采样。基于以上原因,通常会对输入在周边使用某个元素做补齐,其中最常见的补齐就是加0补齐
二维卷积总结
本部分完全来自于CS231n讲义
通常来讲,二维卷积一般都是
- 输入维度 W 1 × H 1 × D 1 W_1 \times H_1 \times D_1 W1×H1×D1
- 接收四个参数
- 滤波器大小 K K K
- 卷积核大小 F F F(即卷积核尺寸为 F × F F\times F F×F)
- 步长 S S S
- 单边补0个数 P P P。 P = 1 P=1 P=1时,每行最左补一个0,最右补一个0,加一个全0的首行和末行
- 产生的结果维度为 W 2 × H 2 × D 2 W_2 \times H_2 \times D_2 W2×H2×D2,其中
- W 2 = ( W 1 − F + 2 P ) / S + 1 W_2 = (W_1 - F + 2P) / S + 1 W2=(W1−F+2P)/S+1
- H 2 = ( H 1 − F + 2 P ) / S + 1 H_2 = (H_1 - F + 2P) / S + 1 H2=(H1−F+2P)/S+1
- D 2 = K D_2 = K D2=K
每个卷积核共有 F × F × D 1 F \times F \times D_1 F×F×D1个参数,该滤波器共有 F × F × D 1 × K F \times F \times D_1 \times K F×F×D