Tips in DNN

仅仅是大神的搬运工,翻译官==

CNN:允许那些由多层卷积层构成的计算复杂的模型通过多层抽象来学习数据的特征表达。这些方法极大地提升了视觉目标识别、目标检测、文本检测的效果。

另外,许多CNN的教程上市,但是关于从头开始CNN实践的总结还很少,本文介绍一些搭建和训练自己的深度网络的技巧和建议。

Introduction

本文侧重介绍图像相关的CNN的任务,主要关于8个方面:

  1. 数据增强
  2. 图像预处理
  3. 网络初始化
  4. 训练的技巧
  5. 激活函数的选择
  6. 各种正则化
  7. 在图中发现的见解
  8. 集成多个深度网络的方法

Sec1:data argumentation

原始图像数据集的训练样本有限,必须做数据增广。

方式1:

  • 水平翻转,随机裁剪,色彩抖动,或者几种方式的组合。
  • 旋转,随机裁剪,提高饱和度或者提高所有像素的值(HSV色彩空间的S,V分量)到0.25-4(一个batch里面的所有图片都这样)。把这些值乘以0.7-1.4之间的一个因子,并且加上一个-0.1到0.1之间的值。也可以给一个batch中所有像素的H分量加上-0.1到0.1的值。

方式2:fancy PCA

训练Alenet时候提出的。改变了训练图像RGB通道的强度。

  • 在训练数据的所有【R,G,B】三通道的像素点上计算PCA值
  • 在每次前向传播时,依据主成分计算一些颜色偏移。
  • 将这些偏移加到每个训练数据上面:在RGB像素上加上以下的值:$i.e I_{xy}=[I_{xy}^R,I_{xy}^G,I_{xy}^B]^T:[p_1,p_2,p_3][\alpha_1\lambda_1,\alpha_2\lambda_2,\alpha_3\lambda_3]^T$ ,其中$p_i,\lambda_i$分别是RGB像素值的$3\times 3$的协方差矩阵的第i个特征向量和特征值。$\alpha_i$是从一个均值为0,标准差为0.1的高斯分布中取得一个随机值。注意:每次使用一张图片训练时只会取一个$\alpha_i$,直到这个图片再一次被取到。就是说,当模型训练时用到相同的训练图像时,它会随机产生另一个$\alpha_i$.fancy PCA可以捕获自然图像一个重要的特征,就是,目标的特性不会随着强度的改变或者色彩的亮度而改变。

Sec2:preprocessing

得到了大量训练样本之后,可以做一些预处理。

方式1:

第一个预处理步骤是去中心化,并且标准化数据:

1
2
>>> X -= np.mean(X, axis = 0) # zero-center
>>> X /= np.std(X, axis = 0) # normalize

X是输入数据。该方法只有在我们认为:不同的输入特征有不同的尺度范围时,才有意义。如果图像的像素相对尺度已经近似相等时(比如都是在0-255之间),就没有必要执行这个预处理步骤了。

方式2:PCA whitening

首先:数据被中心化,然后计算数据的协方差矩阵,来看数据的相关性结构:

1
2
>>> X -= np.mean(X, axis = 0) # zero-center
>>> cov = np.dot(X.T, X) / X.shape[0] # compute the covariance matrix

然后通过将原始(但是以0为中心处理的)数据投影到特征基上来去相关化。

1
2
>>> U,S,V = np.linalg.svd(cov) # compute the SVD factorization of the data covariance matrix
>>> Xrot = np.dot(X, U) # decorrelate the data

最后一步就是白化:把特征基上的数据在每个维度除以特征值来归一化尺度:

1
>>> Xwhite = Xrot / np.sqrt(S + 1e-5) # divide by the eigenvalues (which are square roots of the singular values)

注意:此处加了1e-5,或者其他的小的常量防止他除以0.

缺点:会放大噪声,因为它将将数据的所有维度都进行了拉伸,拉到相同的大小,包括那些方差很小几乎都是噪声的维度。实际操作中,可以使用更强的平滑器来缓解这个现象,或者用个比1e-5更大的数。

注意:实际CNN中不会用这种变换,但是zero-centered还是很重要的,每个像素标准化的操作也很常见。

Sec3:Initializations

训练网络之前,必须对参数初始化。

All Zero Initialization

理想情形,如果数据进行了合适的归一化,那么我们理所当然认为几乎一半的权重是正的,另一半是负的。因此全零初始化参数,来满足这个期望分布。但是如果所有神经元都是相同的输出,那么计算出的梯度也是一样的,参数更新也一样,就无法更新模型。

Initialization with Small Random Numbers

高斯分布:

为了打破对称(symmetry breaking),我们将参数初始化为0附近的值。
$$
weights 服从0.001\times N(0,1)
$$
其中,N(0,1)是均值为0,单位标准差的高斯分布。

均匀分布:

对性能没什么影响。

Calibrating the Variances

随机初始化的神经元的输出分布随着输入的增加会产生方差,所以我们需要将神经元的输出的方差归一化到1,通过归一化权值向量。

1
>>> w = np.random.randn(n) / sqrt(n) # calibrating the variances with 1/sqrt(n)

其中randn(n)是前面提到的高斯函数,n是输入个数。这保证了网络中所有的神经元输出都有近似相同的输出分布,经验上并且提高了收敛的速度。

Current Recommendation

如前述,校准方差时没有考虑ReLU。如果考虑ReLU时,计算公式应该是$2.0/n$:

1
>>>w=np.random.randn(n)*sqrt(2.0/n)#current recommendation

Sec4:During Training

filters and pooling size:

输入图片:size建议是2的幂次方,比如32(cifar),64,224(比如一般的ImageNet),384或者512.

滤波器: 使用小滤波器(比如$3\times 3)$和小步长(比如1)和0填充,不仅可以减少参数,还能提高准确率。同时$3\times3$的滤波器加上步长1可以保留图像或者特征图的空间尺寸。[w1,h1,d1]->[w1,h1,d2]

对于池化层,一般使用的池化尺寸是$2\times2$.

Learning Rate:

一般推荐mini-batch的随机梯度下降法。因此如果我们改变了mini-batch的大小,我们就不能随意改变学习率。 一般刚开始训练是0.1,如果在验证集上没有进展了,就把LR除以2或者5,一直这样下去。

Fine tune on pre-trained model:

预训练的模型有很棒的泛化能力,可以直接使用这些预训练模型。如果想进一步提升分类能力,一个有效的方法在自己的数据集上精修预训练模型。

有两个很重要的影响效果的因素:一是新数据集的大小,二是和原始数据集的相似度。不同的fine tune策略可以在不同的情况下使用:

  1. 比如新数据集和预训练模型的数据集很相似,如果新数据集很少,我们可以直接使用预训练模型顶层提取的特征来训练一个线性分类器。
  2. 如果数据很多,就要使用一个小的LR来fine tune预训练模型的顶层几层。
  3. 如果数据集和预训练模型的数据集差别很大,但是数目足够,就需要fine tune预训练模型的好多层。
  4. 如果数据集差别大,而且数目很少,那么就不能单纯的在网络顶层训练一个分类器 了。更好的办法是在网络更早几层的激活或者特征处训练SVM分类器。

#Reference
CNNTricks