1. 卷积基本知识

卷积的基本知识可以参考【课程学习】台大李宏毅卷积神经网络学习笔记

1.1 简述卷积的基本操作

卷积神经网络由多个卷积层构成,卷积核具备局部连接权值共享特性
具体来说,卷积层的每个卷积核对输入的多通道特征图进行扫描和运算,从而得到多个拥有更高层语义信息的输出特征图(输出通道数等于卷积核个数)。下图描述了在单通道特征图上的卷积操作:

卷积原理1卷积原理1

卷积原理2卷积原理2

卷积核的运算可以看做卷积核各权值对特征图的数值进行element-wise product之后再相加,得到输出特征图的一个位置的值,也可以理解为两个特征展平为向量进行点积

  • 局部连接:卷积核尺寸远小于输入特征图的尺寸,输出层上的每个节点都只与输入层的部分节点连。这个特征与生物视觉信号的传导机制类似,存在感受野的概念。
  • 权值共享:卷积核的滑动机制,使得输出层上的不同位置节点与输入层的连接权值都一样(卷积核参数)。
  • 输入/输出数据的结构化:局部连接和权值共享,使得卷积操作能够在输出数据中大致保持输入数据的结构信息。

1.2 为什么卷积核一般为奇数?

  1. 3×33 \times 3, 5×55 \times 5这样奇数的卷积核大小相比于2×22\times 2卷积核更能够捕捉空间信息,因为有一个中心点将周围信息进行聚合。
  2. 当我们需要保证输出特征图大小的时候,就需要用到padding,奇数大小的卷积核可以保证padding是对称的,例如:3×33\times 3卷积时,为了输入输出大小一致,设置padding=1,stride=1,padding可以是左右对称的,而偶数大小的卷积核无法对称分配padding(可能左侧插入一列,右侧插入两列)。

1.3 分析与全连接层的区别

在卷积神经网络出现之前,最常见的神经网络是多层感知机(Multi-Layer Perceptron, MLP)。这种网络的每一层是全连接层,也就是某一层的每一个神经元会与下一层的所有神经元连接,如下图:

多层感知机多层感知机

再观察卷积神经网络的两层,输入层的某几个神经元与下一层的一个神经元连接(局部连接),此外,颜色相同的权重是相同的(权值共享)。
多层感知机多层感知机

1.4 卷积神经网络中,如何计算各层的感受野大小?

由于卷积的局部连接性,输出特征图上的每个点的取值,是由卷积核在输入特征图的对应局部区域内进行卷积得到的。由于多层的结构,该层特征图的某一点会受到上上一层对应区域的影响。
感受野的定义是,对于某层输出特征图上的某个点,在卷积神经网络的原始输入数据上能影响到这个点的取值的区域。
记网络的原始输入特征图为Lw×LhL_w \times L_h,记网络第i层节点的感受野大小为Re(i)R^{(i)}_e ,其e{w,h}e\in \{w,h\}分别代表宽和高两个方向,则可按照下式计算:

  • 若第i层为卷积层或池化层,有:
    Re(i)=min(Re(i1)+(ke(i)1)j=0i1sej,Le R^{(i)}_e = min(R^{(i-1)}_e + (k^{(i)}_e -1) \prod_{j=0}^{i-1}s^{j}_e , L_e) 这个j=0i1sej\prod_{j=0}^{i-1}s^{j}_e如何理解呢?
    Layer l上前进1个元素相当于Layer l−1上前进sl个元素,转换成像素单位为
    jl=jl1×sl j_l=j_{l−1}×s_l 其中,sls_l为Conv l的kernel在Layer l−1 上滑动的步长,输入图像的s0s_0=1。
    根据递推公式可知,
    jl=j=0i1sej j_l = \prod_{j=0}^{i-1}s^{j}_e
  • 若第i层为激活层、batch norm等,则其步长为1,感受野大小为:
    Re(i)=Re(i1) R^{(i)}_e = R^{(i-1)}_e
  • 若第i层为全连接层,感受野大小为输入数据全域:
    Re(i)=Le R^{(i)}_e = L_e

1.5 卷积层的输出尺寸、参数量和计算量

假设一个卷积层输入特征图的尺寸为lw(i)×lh(i)l_w^{(i)} \times l_h^{(i)},卷积核大小为kw×khk_w \times k_h,步长为sw×shs_w \times s_h:

1.5.1 输出特征图如何计算?

假设padding分别为pwp_w列和php_h行,填充后的输入特征图尺寸为(lw(i)+2pw)×(lh(i)+2ph)(l^{(i)}_w + 2p_w) \times (l^{(i)}_h + 2p_h),则输出特征图的尺寸为:
le(o)=le(i)+2pekese+1,e{w,h} l^{(o)}_e = \frac{l^{(i)}_e + 2p_e - k_e}{s_e} + 1, e\in \{w,h\} 若步长>1时,可能会出现非整数的情况,因此,很多深度学习框架会采取向下取整的方法:
le(o)=le(i)+2pekese+1,e{w,h} l^{(o)}_e = \lfloor\frac{l^{(i)}_e + 2p_e - k_e}{s_e}\rfloor + 1, e\in \{w,h\}

1.5.2 参数量

单个卷积核的参数量为c(i)kwkhc^{(i)}k_wk_h,而卷积核的个数即输出特征图的通道个数c(o)c^{(o)},总参数量:
c(i)c(o)kwkh c^{(i)}c^{(o)}k_wk_h

1.5.3 计算量

卷积层的计算量,由每个窗口内的计算量以及整体的滑动次数决定。每个窗口内的计算量为c(i)kwkhc^{(i)}k_wk_h,而滑动次数即输出特征图的大小,整体计算量为:
c(i)c(o)lw(i)lh(i)kwkh c^{(i)}c^{(o)}l^{(i)}_wl^{(i)}_hk_wk_h 考虑到步长:
c(i)c(o)lw(i)lh(i)kwkh/(swsh) c^{(i)}c^{(o)}l^{(i)}_wl^{(i)}_hk_wk_h/(s_ws_h)

2. 卷积的变种

2.1 分组卷积

2.1.2 分组卷积与普通卷积的区别

标准卷积标准卷积

在普通卷积操作中,一个卷积核对应输出特征图的一个通道,该卷积核会作用在输入特征图的所有通道上,可以看做在通道维度的全连接。
分组卷积分组卷积

而分组卷积,就是将输入通道和输出通道都划分为同样的组数,然后仅让处于相同组的输入和输出通道互相进行“全连接”,如上图。如果g为输入和输出分组数,则分组卷积的参数量和计算量都为原来的1/g。
分组卷积最初是为了解决单个GPU无法处理含有大计算量的卷积层,采用分组卷积将计算和存储分配到多个GPU。目前,GPU性能不断升级,分组多被用来构建用于移动设别的小网络。虽然,理论上分组卷积的计算量和参数量都大大降低,但是现有GPU加速库的优化有限,实际效率并没有提升很多,反而可能降低。

2.2 转置卷积

2.2.1 转置卷积的主要思想

普通的卷积可以形式化为:
y=Ax y = Ax 转置卷积可以表示为:
y^=ATx^ \hat{y} = A^T\hat{x}

转置卷积也被成为“反卷积(deconvolution)”,可以看做普通卷积的一个“对称”操作,这种对称性体现在两个方面:

  • 转置卷积可以将普通卷积的输入到输出的尺寸变换逆反过来。这里只是恢复了形状,但是复原具体的取值。
  • 根据矩阵运算的求导,普通卷积中,输出y对于输入x的导数为y/x=AT\partial y/\partial x = A^T,对于转置卷积,输出y^\hat{y}对于输入x^\hat{x}的导数为y^/x^=A\partial \hat{y}/\partial \hat{x} = A,因此二者正反向传播过程中,所用的矩阵相同。
    转置矩阵的本质就是对输入数据进行适当变换(补零/上采样)的普通卷积操作。

在具体实现时,以二维卷积为例,一个卷积核尺寸为kw×khk_w \times k_h、滑动步长为(sw,sh)(s_w,s_h)、边界填充尺寸为(pw,ph)(p_w,p_h)的普通卷积,其对应的转置卷积可以按如下步骤执行:

  1. 对输入的特征图进行扩张(上采样):相邻数据点之间,在水平方向上填充sw1s_w-1个零,在垂直方向上填充sh1s_h-1个零。
  2. 再对输入特征图进行边界填充:左右两侧分别填充p^w=kwpw1\hat{p}_w = k_w - p_w -1个零列,上下两侧分别填充p^h=khph1\hat{p}_h = k_h - p_h -1零行。
  3. 在变换后的输入特征图上做卷积核大小为kw×khk_w \times k_h、滑动步长为(1,1)的普通卷积。

2.2.2 转置卷积的应用场景

  • 语义分隔/实例分隔:由于需要提取输入图像的高层语义信息,特征图尺寸会缩小,同时,又需要输出与原始图像大小一致的像素级分隔结果,因而需要扩张特征图到原图大小,因此需要转置卷积。
  • 物体检测、关键点检测,需要输出与原图大小一致的热图。
  • 图像的自编码器、变分自编码器、生成式对抗网络。

2.3 空洞卷积

上面提到的语义分割等任务,一般会有池化操作来扩大特征图的感受野,但是同时会降低特征图的分辨率,丢失一些信息,导致后续的上采样操作无法还原一些细节。
那么,能否不通过池化来达到扩大感受野呢?空洞卷积就是用来解决这个问题,在标准的卷积核中增加空洞,以增加卷积核的感受野。
空洞卷积引入了扩张率,来制定相邻采样点之间的间隔,扩张率为r空洞卷积,卷积核上相邻数据点之间有r-1个空洞。

扩张卷积扩张卷积

上图中,红色点为实际采样点,绿色为空洞。
若扩张率为d,新的卷积核大小可以计算为k=d(k1)+1k' = d*(k-1)+1
上图的卷积核大小分别为3×33\times 35×55\times 59×99 \times 9,感受野大小分别为3×33\times 37×77\times 715×1515 \times 15

2.4 可变形卷积

2.4.1 可变形卷积的主要思想

普通的卷积核的在固定的、规则的网格点上进行数据采样,这束缚了网络的感受野形状,限制了网络对几何形变的应对能力。

可变形卷积可变形卷积

可变形卷积让网络具有学习空间几何形变的能力,可变形卷积引入了一个平行分支来端到端地学习卷积核采样点的位置偏移量。该平行分支根据输入特征图计算出采样点的偏移量,然后再在输入特征图上采样对应的点进行卷积。
可变形卷积可变形卷积

以二维卷积为例,假设卷积核为3×33\times 3,记R={(1,1),(1,0),(1,1),(0,1),(0,0),(0,1),(1,1),(1,0),(1,1)}\mathbb{R} = \{(-1,-1),(-1,0),(-1,1),(0,-1),(0,0),(0,1),(1,-1),(1,0),(1,1)\},表示9个采样点相对于中心点的偏移。普通卷积的公式可以表示为:
y(p0)=pnRw(pn)x(p0+pn) y(p_0) = \sum_{p_n\in \mathbb{R}}w(p_n) \cdot x(p_0+p_n) p0p_0表示滑动窗口中心点的位置。对于可变形卷积。
y(p0)=pnRw(pn)x(p0+pn+Δpn) y(p_0) = \sum_{p_n\in \mathbb{R}}w(p_n) \cdot x(p_0+p_n+ \Delta p_n) 其中,Δpn\Delta p_n 是在网络中端到端地学习得到的,因此可能不是整数,会导致p0+pn+Δpnp_0+p_n+ \Delta p_n不在整数网格点上,此时需要双线性插值。

3. 卷积神经网络基本模块

3.1 batch norm

3.1.1 batch norm是为了解决什么问题?

机器学习中的经典假设是“源空间”与“目标空间”的数据分布是一致的。如果不一致就出现了新的机器学习问题,如:迁移学习/领域自适应等。而 **covarite shift(协变量偏移)**就是分布不一致假设之下的分支问题,它指的是源空间的和目标空间的条件概率是一致的,但是边缘分布是不一致的。那么对于神经网络的各层输出来说,由于他们经过了层内操作,其分布显然与各层对应的输入信号分布不同,而且这种差异会随着网络深度增大而增大,而我们的标签(label)仍然是不变的,由于是层间的问题,因此命名为Internal Covarite Shift(ICS)。

但是batch norm真正有效的原理是防止梯度弥散/梯度消失,将activation规范为均值和方差一致的手段,使得原本会减小的activation的scale变大。

batch norm计算步骤

对于一个batch,传入m个样本。
ZRdl×m Z \in \mathbb{R}^{d_l \times m} 因为batch norm计算是逐维度计算,因此我们关注当前层第j个维度,有ZjR1×mZ_j \in \mathbb{R}^{1 \times m},batch norm计算公式为:
uj=1mi=1mZj(i)σj2=1mi=1m(Zj(i)uj)2Z^j=Zjujσj2+ϵy=γZ^j+β u_j = \frac{1}{m}\sum^{m}_{i=1}Z_j^{(i)} \\ \sigma^2_j = \frac{1}{m}\sum^m_{i=1}(Z^{(i)}_j - u_j)^2 \\ \hat{Z}_j = \frac{Z_j - u_j}{\sqrt{\sigma^2_j + \epsilon}} \\ y' = \gamma\hat{Z}_j + \beta batch norm的操作可以分为两步:

  • Standardization:对batch size为m个xx进行Standardization,得到zero mean unit variance的分布x^\hat{x}
  • Scale and shift:缩放并平移到新的分布yy,具有均值β\beta和方差γ\gamma

3.1.2 Internal Convariate Shift会带来什么问题?

上层网络需要不停调整来适应输入数据分布的变化,导致网络收敛变慢

梯度下降的过程会让每层的W和b发生变化,进而使得每一层输出的分布发生变化。后层的网络就要不停地适应这种分布变化,使得整个网络学习速率变慢。

网络训练过程容易陷入梯度饱和区,减缓网络的收敛速度

所谓饱和激活函数,例如:sigmoid和tanh,存在平缓区域。
由于每层分布不稳定,网络参数会逐渐变大,Z[l]=W[l]+b[l]Z^{[l]} = W^{[l]} + b^{[l]}就会随之变大,使得Z[l]Z^{[l]}容易陷入饱和区,此时梯度会变得很小接近于0,导致网络收敛变慢。

sigmoid函数的左下角和右下角梯度平缓,称为饱和区sigmoid函数的左下角和右下角梯度平缓,称为饱和区

3.1.3 batch norm的优点?

  1. 使得网络中每层数据分布相对稳定,加速收敛;
  2. BN使得网络对网络参数不那么敏感,简化调参过程,使得网络学习更加稳定;
  3. BN允许网络使用饱和性激活函数,缓解梯度消失问题;
  4. BN具有一定正则化效果。

3.1.2 batch norm的缺点?

  1. batch小时不稳定;
  2. 训练和推理时的不一致:
    batch norm推理时,γ,β\gamma,\beta固定,均值和方差在训练时不断变化,在推理时用训练阶段的累积滑动平均结果,类似于EMA。
  3. 在RNN中效果不好。

3.1.3 没有scale and shift过程可不可以?

可以,但是网络的表达能力会下降。
浅层网络中,只需要模型适应数据分布即可。但是对于深层网络,每层的输入分布和权重更要互相协调,强制把分布限制在zero mean unit variance并不见得是最好的选择,加入可学习参数,有利于分布与权重的互相协调。

特别地,令γ=1,β=0\gamma = 1,\beta =0等价于只有标准化,令γ=σ,β=μ\gamma = \sigma, \beta = \mu等价于没有BN层。因此,在训练过程中自适应决定什么样的分布是合适的,所以scale and shift阶段增强了网络的表达能力。

3.1.4 batch norm运算细节

引用原文的话

For a layer with d-dimensional input x=(x(1)...x(d))x = (x^(1)...x^(d)), we will normalize each dimension.
也就是说batch计算一个batch里每个通道的均值和方差,而不考虑特征图大小。

def batch_norm(feature,mean,var):
    b,c,h,w = feature.shape
    for i in range(c):
        feature_i = feature[:,i:,:]
        mean_i = feature_i.mean()
        # 总体标准差
        std_t1 = feature_i.std()
        # 样本标准差
        std_t2 = feature_i.std(ddof = 1)

        feature[:,i,:,:] = (feature[:,i,:,:]-mean_t)/np.sqrt(std_t1 ** 2 + 1e-5)
        # 更新均值和方差
        mean[i] = mean[i] * 0.9 + mean[i] * 0.1
        var[i] = var[i] * 0.9 + (std_t2 ** 2) * 0.1

3.1.5 正则化这个词怎么理解

“正则化”这个词翻译的不是很好,regularization的本意是:

the act of changing a situation or system so that it follows laws or rules, or is based on reason

把regularization理解成“约束”,就方便理解了。不管是传统机器学习模型,还是深度学习模型,训练过程中都有越来越贴近训练数据的趋势,越贴近训练数据,在其他的预测数据上就越容易犯错,也就是过拟合。模型越复杂,越容易过拟合。regularization就是要限制模型的这个缺陷,有的是在模型设计阶段的regularization,有的是在模型训练阶段的regularization,目的都是防止过拟合。