Fork me on GitHub

卷积、激活函数和初始化

1. 卷积层

1.3 公式

$Y = W^TX+b$,其中$W$为卷积核参数

1.2 输出特征图大小

设$N$为输入图像的大小,$P$为填充大小,$F$为卷积核大小,$stride$为步长,则:

输出特征图大小 = $(N + 2P - F)/ stride + 1$(这里默认图像和卷积核为正方形,上下左右填充和步长相同)

1.3 卷积核参数的个数

设卷积核为$[N , C , H , W]$,那么参数个数为:$N\times(C\times H\times W + 1)$,一个卷积核有一个 $bias$。

2. 池化层

池化是一种下采样,它压缩了输入的特征图,一方面减少了特征,导致了参数减少,进而简化了卷积网络计算时的复杂度;另一方面保持了特征的某种不变性(旋转、平移、伸缩等)。

2.1 池化层的作用

  • 池化层保持了特征的某种不变性(旋转、平移、伸缩等),使得模型更关注是否存在某些特征而不是特征具体的位置。比如最大池化。
  • 保留了主要特征,减少了特征,从而导致了参数减少,进而简化了卷积网络计算时的复杂度
  • 在一定程度上能防止过拟合的发生

2.2 池化层分类

最大池化层和平均池化层

3. 激活函数

在卷积层或全连接层后的非线性函数叫激活函数。激活函数的输入是标量。

3.1 $Sigmoid$函数

数学公式:$\sigma(x)=1 /\left(1+e^{-x}\right)$

求导公式:$\frac{d \sigma(x)}{d x}=(1-\sigma(x)) \sigma(x)$

特点:把输入值“挤压”到0到1范围内

图像

1

缺点

  • Sigmoid函数饱和时会使梯度消失:当输入过大或过小时会饱和,使得梯度几乎为0,在反向传播时会阻碍梯度传递。因此,为了防止饱和,必须对于权重矩阵初始化特别留意。
  • Sigmoid函数的输出不是零中心的:这会导致Sigmoid激活函数层的下一层的输入均为正数,我们假设激活函数为sigmoid,则对于公式$f\left(\sum_{i} w_{i} x_{i}+b\right)$, 其导数为 ,那么会导致导数(是向量)的正负依赖于$\frac{\partial L}{\partial f}$,而$\frac{\partial L}{\partial f}$是标量,所以所有$w_{i}$的正负都是一样的,所以会导致梯度下降权重更新时出现z字型的下降,即使得更新速度减慢。如图所示。

1

  • 指数型计算量比较大。

3.2 $tanh$函数

数学公式:$\tanh (x)=2 \sigma(2 x)-1$

特点:将实数值压缩到[-1,1]之间。

图像:

1

缺点: 存在饱和问题

3.3 $relu$函数

数学公式:$f(x)=\max (0, x)$

特点: 一个关于0的阈值

图像:

1

优点:

  • 只有负半轴会饱和
  • 节省计算资源,不含指数运算,只对一个矩阵进行阈值计算
  • 更符合生物学观念
  • 加速随机梯度下降的收敛,Krizhevsky论文指出比sigmoid和tanh函数快6倍之多,据称这是由它的线性,非饱和的公式导致的。

缺点:

  • 仍有一半会饱和
  • 非零中心
  • relu单元比较脆弱并且可能“死掉”。当一个节点的梯度变为0时,那么从这个节点向后的节点的梯度均变为0,这使得参数没法更新。

3.3.1 造成梯度消失的原因

  • 初始化的权值太差
  • 学习率太大,导致梯度很大,那么权重的变化就很大,有可能使得输入为负数。

3.4 Leaky relu和PReLU

Leaky relu数学公式: $f(x)=\max (0.01 x, x)$

PReLU数学公式: $f(x)=\max (\alpha x, x)$

特点: 解决“ReLU死亡”问题,x<0时给出一个很小的梯度值,比如0.01。

图像:

1

3.5 指数线性单元(Exponential Linear Units,ELU)

数学公式: $f(x)=\left\{\begin{array}{ll}
x & \text { if } x>0 \\
\alpha(\exp (x)-1) & \text { otherwise }
\end{array}\right.$

特点: 介于ReLU和Leaky ReLU之间

图像:

1

4. 数据预处理

4.1 去均值

4.1.1 image mean

将训练集中所有图片的同一位置的像素求均值。

1
img = img - np.mean(train , axis = 0) # train 的shape为(n , 3 , w , h)

4.1.2 pixel mean

分别计算训练集的$RGB$三个通道的像素均值。

1
2
3
4
5
6
7
mean_rgb = np.array([0 , 0 , 0])
count = 0
for img in img_list: # [3 , w , h]
for i in range(3):
mean_rgb[i] += img[i , : , :].mean()
count += 1
mean_rgb /= count

4.1.3 图像去均值的目的

  • 消除图像的共性,突显个体差异
  • 为了进行数据特征标准化,即像机器学习中的特征预处理那样对输入特征向量各维去均值再除以标准差,但由于自然图像各点像素值的范围都在0-255之间,方差大致一样,只要做去均值处理即可。
  • 构造0中心的数据,加快下降速度
  • 标准化使得数据之间的差值缩小,而参数$w$的梯度和$x$有关,如果$x$之间相差太大,那么所求出的梯度也会相差很大,而不同梯度所对应的好的学习率不同,这样导致同一学习率不能使用不同的梯度。

4.2 标准化

多一步除标准差。

4.3 测试集和训练集进行相同的预处理

即测试集中的均值和标准差是通过训练集求得的。

  • 因为深度学习算法是建立在训练集和测试集服从同一或者类似分布的假设之上的,所以预处理训练集和测试集要保持一致。
  • 算法要求训练时不能有测试集的信息,所以不能是先预处理再分训练集和测试集。

5. 权重初始化

5.1 全0初始化

这种做法是错误的,这样会导致同一层的参数的梯度相同,进而进行相同的参数更新。

5.2 小随机数初始化

1
W = 0.01 * np.random.randn(fan_in,fan_out)

实验表明小随机数初始化在简单的网络中效果比较好,但是网络结构比较深的情况不一定会得到好的结果。比如一个10层的全连接网络,如图所示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import numpy as np
import matplotlib
matplotlib.use('TkAgg')
import matplotlib.pyplot as plt
%matplotlib notebook
# 假设一些高斯分布单元
D = np.random.randn(1000, 500)
hidden_layer_sizes = [500]*10 # 隐藏层尺寸都是500,10层
nonlinearities = ['tanh']*len(hidden_layer_sizes) # 非线性函数都是用tanh函数

act = {'relu': lambda x: np.maximum(0, x), 'tanh': lambda x: np.tanh(x)}
Hs = {}
for i in range(len(hidden_layer_sizes)):
X = D if i == 0 else Hs[i-1] # 当前隐藏层的输入
fan_in = X.shape[1]
fan_out = hidden_layer_sizes[i]
W = np.random.randn(fan_in, fan_out) * 0.01 # 权重初始化

H = np.dot(X, W) # 得到当前层输出
H = act[nonlinearities[i]](H) # 激活函数
Hs[i] = H # 保存当前层的结果并作为下层的输入

# 绘制直方图
fig , ax1 = plt.subplots(1 , 10 , figsize = (12 , 5))
for i,H in Hs.items():
ax1[i].hist(H.ravel(), bins = 30, range=(-1,1))
plt.subplots_adjust(wspace = 1)

1

可以看出,只有第一层表现好,后几层的均值和方差都接近于0,那么梯度就接近于0。

如果我们参数扩大,如下图,那么由于参数很大而且使用$tanh$函数,那么很容易达到饱和。

1
W = np.random.randn(fan_in, fan_out) * 1

1

5.3 Xavier初始化

1
W = np.random.randn(fan_in, fan_out) / np.sqrt(fan_in)

这种初始化使得输入和输出有相同的分布。

我们设输出 $Y =\sum_{i}^{n} w_{i} x_{i}$,则:

第六步我们假设输入和权重的均值都是0。所以当$\operatorname{Var}(\sqrt{1 / n} \cdot w)=1 / n \operatorname{Var}(w)=1 / n$时,$\operatorname{Var}(Y) = \operatorname{Var}(x)$

结果:

1

但是,当我们使用$relu$时,如图,又出现了问题:

1

5.4 He初始化

$relu$后的输出会有大约一半变为0,那么方差就会减半,所以我们用He初始化。

1
W = np.random.randn(fan_in, fan_out) / np.sqrt(fan_in/2)

我们使得输出$Y$的方差变为2倍来抵消。

1

6. 批量归一化

因为底层网络在训练的时候更新了参数,那么引起了输出数据分布的改变,那么我们在卷积层或全连接层后加$BN$层,就可以使得输出数据归一化。

6.1 算法前向推导公式

  1. 首先通过小批量训练集计算均值: $\mu_{\mathcal{B}} \leftarrow \frac{1}{m} \sum_{i=1}^{m} x_{i}$
  2. 计算小批量训练集的标准差:$\sigma_{\mathcal{B}}^{2} \leftarrow \frac{1}{m} \sum_{i=1}^{m}\left(x_{i}-\mu_{\mathcal{B}}\right)^{2}$
  3. 将本批次训练集归一化:$\widehat{x}_{i} \leftarrow \frac{x_{i}-\mu_{\mathcal{B}}}{\sqrt{\sigma_{\mathcal{B}}^{2}+\epsilon}}$
  4. 计算输出:$y_{i} \leftarrow \gamma \widehat{x}_{i}+\beta$,其中$\gamma$和$\beta$分别为可学习的参数,让我们的网络可以学习恢复出原始网络所要学习的特征分布。批量归一化会把输入限制在非线性函数的线性区域,有时候我们并不想没有一点饱和,所以希望能控制饱和程度,所以引入了参数。

6.2 维度

1

在卷积层后使用$BN$时,我们以一张特征图为单位计算均值和方差,主要是为了节省参数。

6.3 BN在测试集

测试集上的均值和方差:

7. 检查合理性

  1. 使用小参数进行初始化,使正则损失为0,确保得到的损失值与期望一致。例如,对于一个输入CIFAR-10的Softmax分类器,一般期望它的初始损失值是2.302,这是因为初始时预计每个类别的概率是0.1(因为有10个类别),然后Softmax损失值正确分类的负对数概率-ln(0.1) = 2.302。
  2. 提高正则化强度,损失值会变大
  3. 对小数据子集过拟合。在整个数据集进行训练之前,尝试在一个很小的数据集上进行训练(比如20个数据),然后确保能到达0的损失值(正则损失为0)。