CNN系列总结(二)---数据预处理、激活函数、梯度、损失函数、优化方法等

数据预处理

零中心化、归一化

关于数据预处理我们有3个常用的符号,数据矩阵X,假设其尺寸是[N x D]N是数据样本的数量,D是数据的维度)。
均值减法(Mean subtraction)是预处理最常用的形式。它对数据中每个独立特征减去平均值,从几何上可以理解为在每个维度上都将数据云的中心都迁移到原点。在numpy中,该操作可以通过代码
X -= np.mean(X, axis=0)实现。而对于图像,更常用的是对所有像素都减去一个值,可以用
X -= np.mean(X)实现,也可以在3个颜色通道上分别操作。
归一化(Normalization)是指将数据的所有维度都归一化,使其数值范围都近似相等。有两种常用方法可以实现归一化。
第一种是先对数据做零中心化(zero-centered)处理,然后每个维度都除以其标准差,实现代码为X /= np.std(X, axis=0)
第二种方法是对每个维度都做归一化,使得每个维度的最大和最小值是1和-1。这个预处理操作只有在确信不同的输入特征有不同的数值范围(或计量单位)时才有意义,但要注意预处理操作的重要性几乎等同于学习算法本身。在图像处理中,由于像素的数值范围几乎是一致的(都在0-255之间),所以进行这个额外的预处理步骤并不是很必要。
一般数据预处理流程:如下图,左边:原始的2维输入数据。中间:在每个维度上都减去平均值后得到零中心化数据,现在数据云是以原点为中心的。右边:每个维度都除以其标准差来调整其数值范围。红色的线指出了数据各维度的数值范围,在中间的零中心化数据的数值范围不同,但在右边归一化数据中数值范围相同。

PCA和白化

PCA和白化(Whitening)是另一种预处理形式。在这种处理中,先对数据进行零中心化处理,然后计算协方差矩阵,它展示了数据中的相关性结构。
PCA
数据协方差矩阵的第$ (i, j) $个元素是数据第$i$个和第$j$个维度的协方差。具体来说,该矩阵的对角线上的元素是方差。还有,协方差矩阵是对称和半正定的。我们可以对数据协方差矩阵进行SVD(奇异值分解)运算。

1
2
3
4
5
# 假设输入数据矩阵X的尺寸为[N x D]
X -= np.mean(X, axis = 0) # 对数据进行零中心化(重要)
cov = np.dot(X.T, X) / X.shape[0] # 得到数据的协方差矩阵
U,S,V = np.linalg.svd(cov)
Xrot = np.dot(X,U) # 对数据去相关性

U的列是特征向量,S是装有奇异值的1维数组(因为cov是对称且半正定的,所以S中元素是特征值的平方)。为了去除数据相关性,将已经零中心化处理过的原始数据投影到特征基准上。
注意U的列是标准正交向量的集合(范式为1,列之间标准正交),所以可以把它们看做标准正交基向量。因此,投影对应x中的数据的一个旋转,旋转产生的结果就是新的特征向量。如果计算Xrot的协方差矩阵,将会看到它是对角对称的。np.linalg.svd的一个良好性质是在它的返回值U中,特征向量是按照特征值的大小排列的。我们可以利用这个性质来对数据降维,只要使用前面的小部分特征向量,丢弃掉那些包含的数据没有方差的维度。 这个操作也被称为主成分分析( Principal Component Analysis 简称PCA)降维:

1
Xrot_reduced = np.dot(X, U[:,:100]) # Xrot_reduced 变成 [N x 100]

经过上面的操作,将原始的数据集的大小由[N x D]降到了[N x 100],留下了数据中包含最大方差的100个维度。通常使用PCA降维过的数据训练线性分类器和神经网络会达到非常好的性能效果,同时还能节省时间和存储器空间。
白化
白化操作的输入是特征基准上的数据,然后对每个维度除以其特征值来对数值范围进行归一化。该变换的几何解释是:如果数据服从多变量的高斯分布,那么经过白化后,数据的分布将会是一个均值为零,且协方差相等的矩阵。该操作的代码如下:

1
2
3
# 对数据进行白化操作:
# 除以特征值
Xwhite = Xrot / np.sqrt(S + 1e-5)

下图中左边是二维的原始数据。中间:经过PCA操作的数据。可以看出数据首先是零中心的,然后变换到了数据协方差矩阵的基准轴上。这样就对数据进行了解相关(协方差矩阵变成对角阵)。右边:每个维度都被特征值调整数值范围,将数据协方差矩阵变为单位矩阵。从几何上看,就是对数据在各个方向上拉伸压缩,使之变成服从高斯分布的一个数据点分布。

PCA和白化主要是为了介绍的完整性,实际上在卷积神经网络中并不会采用这些变换。然而对数据进行零中心化操作还是非常重要的,对每个像素进行归一化也很常见。
常见错误:进行预处理很重要的一点是:任何预处理策略(比如数据均值)都只能在训练集数据上进行计算,算法训练完毕后再应用到验证集或者测试集上。例如,如果先计算整个数据集图像的平均值然后每张图片都减去平均值,最后将整个数据集分成训练/验证/测试集,那么这个做法是错误的。应该怎么做呢?应该先分成训练/验证/测试集,只是从训练集中求图片平均值,然后各个集(训练/验证/测试集)中的图像再减去这个平均值。

权重初始化

在开始训练网络之前,还需要初始化网络的参数。
错误:全零初始化。让我们从应该避免的错误开始。在训练完毕后,虽然不知道网络中每个权重的最终值应该是多少,但如果数据经过了恰当的归一化的话,就可以假设所有权重数值中大约一半为正数,一半为负数。这样,一个听起来蛮合理的想法就是把这些权重的初始值都设为0吧,因为在期望上来说0是最合理的猜测。这个做法错误的!因为如果网络中的每个神经元都计算出同样的输出,然后它们就会在反向传播中计算出同样的梯度,从而进行同样的参数更新。换句话说,如果权重被初始化为同样的值,神经元之间就失去了不对称性的源头。
小随机数初始化。因此,权重初始值要非常接近0又不能等于0。解决方法就是将权重初始化为很小的数值,以此来打破对称性。其思路是:如果神经元刚开始的时候是随机且不相等的,那么它们将计算出不同的更新,并将自身变成整个网络的不同部分。小随机数权重初始化的实现方法是:W = 0.01 * np.random.randn(D,H)。其中randn函数是基于零均值和标准差的一个高斯分布(译者注:国内教程一般习惯称均值参数为期望$\mu$)来生成随机数的。根据这个式子,每个神经元的权重向量都被初始化为一个随机向量,而这些随机向量又服从一个多变量高斯分布,这样在输入空间中,所有的神经元的指向是随机的。也可以使用均匀分布生成的随机数,但是从实践结果来看,对于算法的结果影响极小。
警告。并不是小数值一定会得到好的结果。例如,一个神经网络的层中的权重值很小,那么在反向传播的时候就会计算出非常小的梯度(因为梯度与权重值是成比例的)。这就会很大程度上减小反向传播中的“梯度信号”,在深度网络中,就会出现问题。
使用1/sqrt(n)校准方差。上面做法存在一个问题,随着输入数据量的增长,随机初始化的神经元的输出数据的分布中的方差也在增大。我们可以除以输入数据量的平方根来调整其数值范围,这样神经元输出的方差就归一化到1了。也就是说,建议将神经元的权重向量初始化为:w = np.random.randn(n) / sqrt(n)。其中n是输入数据的数量。这样就保证了网络中所有神经元起始时有近似同样的输出分布。实践经验证明,这样做可以提高收敛的速度。


实践:当前的推荐是使用ReLU激活函数,并且使用w = np.random.randn(n) * sqrt(2.0/n)来进行权重初始化

激活函数

激活函数的作用

CNN网络中激活函数一般在卷积层之后,激活feature map中相关特征区域。激活函数的主要作用是提供网络的非线性建模能力。激活函数是必不可少的,因为没有激活函数,网络仅能够表示线性映射。

激活函数的性质

  • 可微性:当优化方法是基于梯度的时候,要保证可微性;
  • 单调性:当激活函数是单调的时候,单层网络能保证是凸函数;
  • 输出值范围:当激活函数输出值是 有限 的时候,基于梯度的优化方法会更加 稳定,因为特征的表示受有限权值的影响更显著;当激活函数的输出是 无限 的时候,模型的训练会更加高效,不过在这种情况小,一般需要更小的learning rate;
  • 函数形式:目前来看,常见的激活函数多是分段线性和具有指数形状的非线性函数

sigmoid激活函数

  • 函数:
  • 一阶导数:
  • 推导过程:
  • 曲线图像:
  • 特点:
    $sigmoid$函数连续,光滑,严格单调,以(0,0.5)中心对称,是一个非常良好的阈值函数;
  • 优点:域范围限制在(0,1)之间,可以表示概率,或用于输入的归一化,代表性如Sigmoid交叉熵损失函数。
  • 缺点:饱和性导致梯度消失:从$sigmod$曲线图像可以看到,其两侧导数逐渐趋近于0,$ \lim_{x \to \infty}{f^{\prime}(x)} = 0 $ 具有这种性质的称为软饱和激活函数。饱和又可分为左饱和与右饱和。与软饱和对应的是硬饱和, 即 $f^{\prime}(x) = 0$,当$|x|>c$,其中$c$为常数。$Sigmoid$的软饱和性,使得深度神经网络在二三十年里一直难以有效的训练,是阻碍神经网络发展的重要原因。具体来说,由于在后向传递过程中,$sigmoid$向下传导的梯度一旦输入落入饱和区,$f^{\prime}(x)$ 就会变得接近于0,导致了向底层传递的梯度也变得非常小。此时,网络参数很难得到有效训练。这种现象被称为梯度消失。一般来说, $sigmoid$网络在 5 层之内就会产生梯度消失现象.
    均值非0:$sigmoid$函数的输出均大于0,使得输出不是0均值,这称为偏移现象,这会导致后一层的神经元将得到上一层输出的非0均值的信号作为输入。

tanh激活函数

  • 函数:
  • 曲线图像:
    • 特点:
      tanh与sigmoid相比,它的输出均值是0,使得其收敛速度要比sigmoid快,减少迭代次数。但是tanh一样具有软饱和性,从而造成梯度消失。

ReLU,P-ReLU, Leaky-ReLU

ReLU函数

  • 函数:$f(x) = \max{(0, x)}$
  • 曲线图像:
  • 优点:ReLU的全称是Rectified Linear Units,是一种后来才出现的激活函数。 当x<0时,ReLU硬饱和,而当x>0时,则不存在饱和问题。所以,ReLU 能够在x>0时保持梯度不衰减,从而缓解梯度消失问题。这让我们能够直接以监督的方式训练深度神经网络,而无需依赖无监督的逐层预训练。
  • 缺点:随着训练的推进,部分输入会落入硬饱和区,导致对应权重无法更新。这种现象被称为“神经元死亡”。与sigmoid类似,ReLU的输出均值也大于0,偏移现象和神经元死亡会共同影响网络的收敛性

P-ReLU, Leaky-ReLU函数

针对在$x<0$的硬饱和问题,我们对ReLU做出相应的改进,使得

这就是Leaky-ReLU, 而P-ReLU认为,α也可以作为一个参数来学习。

ELU激活函数

融合了sigmoid和ReLU,左侧具有软饱和性,右侧无饱和性。右侧线性部分使得ELU能够缓解梯度消失,而左侧软饱能够让ELU对输入变化或噪声更鲁棒。ELU的输出均值接近于零,所以收敛速度更快。在 ImageNet上,不加 Batch Normalization 30 层以上的 ReLU 网络会无法收敛,PReLU网络在MSRA的Fan-in (caffe )初始化下会发散,而 ELU 网络在Fan-in/Fan-out下都能收敛

Maxout激活函数

优点:与常规激活函数不同的是,它是一个可学习的分段线性函数,任何一个凸函数,都可以由线性分段函数进行逼近近似,因此Maxout可以拟合任意的凸函数;能够缓解梯度消失,同时又规避了ReLU神经元死亡的缺点;
缺点:从上面的激活函数公式中可以看出,每个神经元中有两组(w,b)参数,那么参数量就增加了一倍,这就导致了整体参数的数量激增。

使用方法

如果使用 ReLU,那么一定要小心设置 learning rate,而且要注意不要让你的网络出现很多 “dead” 神经元,如果这个问题不好解决,那么可以试试 Leaky ReLU、PReLU 或者 Maxout.
最好不要用 sigmoid,可以试试 tanh,不过可以预期它的效果会比不上 ReLU 和 Maxout.

##

梯度更新

目前优化神经网络的方法都是基于反向传播的思想,即根据损失函数计算的误差通过梯度反向传播的方式,指导深度网络权值的更新优化。

为什么要使用梯度更新

深层网络由许多非线性层堆叠而来,每一层非线性层都可以视为是一个非线性函数$f(x)$(非线性来自于非线性激活函数),因此整个深度网络可以视为是一个复合的非线性多元函数$$ F(x)=f_n(…f_3(f_2(f_1​(x)∗θ_1+b)∗θ_2+b)…) $$

最终的目的是希望这个多元函数可以很好的完成输入到输出之间的映射。假设不同的输入,输出的最优解是$g(x)$,那么,优化深度网络就是为了寻找到合适的权值,满足$Loss=L(g(x),F(x))$取得极小值点,比如最简单的损失函数 $$Loss=∣∣g(x)−f(x)∣∣^{2}_{2}$$

假设损失函数的数据空间是下图这样的,最优的权值就是为了寻找下图中的最小值点,对于这种数学寻找最小值问题,采用梯度下降的方法再适合不过了。

梯度更新方法

梯度消失、爆炸

梯度消失与梯度爆炸其实是一种情况。两种情况下梯度消失经常出现,一是在深层网络中,二是采用了不合适的激活函数,比如sigmoid。梯度爆炸一般出现在深层网络和权值初始化值太大的情况下,下面分别从这两个角度分析梯度消失和爆炸的原因。
原因
深层网络角度

总结:从深层网络角度来讲,不同的层学习的速度差异很大,表现为网络中靠近输出的层学习的情况很好,靠近输入的层学习的很慢,有时甚至训练了很久,前几层的权值和刚开始随机初始化的值差不多。因此,梯度消失、爆炸,其根本原因在于反向传播训练法则,属于先天不足,另外多说一句,Hinton提出capsule的原因就是为了彻底抛弃反向传播,如果真能大范围普及,那真是一个革命。
激活函数角度:
如果激活函数选择不合适,比如使用sigmoid,梯度消失就会很明显了。
解决办法

  • 1.预训练加微调:此方法来自Hinton在2006年发表的一篇论文,Hinton为了解决梯度的问题,提出采取无监督逐层训练方法,其基本思想是每次训练一层隐节点,训练时将上一层隐节点的输出作为输入,而本层隐节点的输出作为下一层隐节点的输入,此过程就是逐层“预训练”(pre-training);在预训练完成后,再对整个网络进行“微调”(fine-tunning)。Hinton在训练深度信念网络(Deep Belief Networks中,使用了这个方法,在各层预训练完成后,再利用BP算法对整个网络进行训练。此思想相当于是先寻找局部最优,然后整合起来寻找全局最优,此方法有一定的好处,但是目前应用的不是很多了。
  • 2.梯度剪切、正则:梯度剪切这个方案主要是针对梯度爆炸提出的,其思想是设置一个梯度剪切阈值,然后更新梯度的时候,如果梯度超过这个阈值,那么就将其强制限制在这个范围之内。这可以防止梯度爆炸。另外一种解决梯度爆炸的手段是采用权重正则化(weithts regularization)比较常见的是l1
    $l1$正则和$l2$正则。
  • 3.relu、leakrelu、elu等激活函数
  • 4.batch normalization,批规范化:通过规范化操作将输出信号x规范化保证网络的稳定性。
  • 5.残差结构。
  • 6.LSTM。

损失函数

损失函数的主要部分是数据损失,它是一个有监督学习问题,用于衡量分类算法的预测结果(即分类评分)和真实标签结果之间的一致性。数据损失是对所有样本的数据损失求平均。也就是说,$L=\frac{1}{N}\sum_iL_i$中,N是训练集数据的样本数。让我们把神经网络中输出层的激活函数简写为$f=f(x_i;W)$。另一部分是正则化损失部分,它可以看做是对模型复杂程度的某种惩罚。在实际中你可能需要解决以下几类问题:

分类问题

一般不同的分类器有不同的损失函数。
svm分类去
常见的损失函数是SVM,$$\displaystyle L_i=\sum_{j\not=y_i}max(0,f_j-f_{y_i}+1)$$,有些学者的论文中指出平方折叶损失(即使用$max(0,f_j-f_{y_i}+1)^2)$算法的结果会更好。
Softmax分类器
第二个常用的损失函数是Softmax分类器,它使用交叉熵损失:
$$\displaystyle L_i=-log(\frac{e^{f_{y_i}}}{\sum_je^{f_j}})$$。
问题:当标签集非常庞大(例如字典中的所有英语单词,或者ImageNet中的22000种分类),就需要使用分层Softmax(Hierarchical Softmax)了(参考文献)。分层softmax将标签分解成一个树。每个标签都表示成这个树上的一个路径,这个树的每个节点处都训练一个Softmax分类器来在左和右分枝之间做决策。树的结构对于算法的最终结果影响很大,而且一般需要具体问题具体分析。
二分类器

回归问题

  • 回归问题

批量归一化

批量归一化(Batch Normalization)

正则化

正则化(Regularization)是机器学习中一种常用的技术,其主要目的是控制模型复杂度,减小过拟合。最基本的正则化方法是在原目标(代价)函数 中添加惩罚项,对复杂度高的模型进行“惩罚”。其数学表达形式为:

l1和l2的正则化数学推导来源见深入理解L1、L2正则化

$l_{2}$ 正则化的直观理解

图中椭圆为原目标函数$ J\left( w \right) $的一条等高线,圆为半径 $\sqrt{C} $的$l_{2} $范数球。由于约束条件的限制, $w $必须位于$ l_{2} $范数球内。考虑边界上的一点 $w $,图中蓝色箭头为$ J\left( w \right) $在该处的梯度方向$ \nabla J\left( w \right) $,红色箭头为$ l_{2} $范数球在该处的法线方向。由于$ w $不能离开边界(否则违反约束条件),因而在使用梯度下降法更新$ w$ 时,只能朝 $\nabla J\left( w \right) $在 范数球上 $w $处的切线方向更新,即图中绿色箭头的方向。如此$ w $将沿着边界移动,当 $\nabla J\left( w \right) $与范数球上 $w $处的法线平行时,此时$ \nabla J\left( w \right) $在切线方向的分量为0,$ w $将无法继续移动,从而达到最优解$ w^{*}$(图中红色点所示)。

$l_{1}$ 正则化的直观理解

$l_{1}$ 范数会使w产生稀疏性的原因:
其主要差别在于$ l_{1} 、 l_{2} $范数球的形状差异。由于此时每条边界上$ w $的切线和法线方向保持不变,在图中$ w $将一直朝着$ \nabla J\left( w \right) $在切线方向的分量沿着边界向左上移动。当$ w $跨过顶点到达$ w’ $时, $\nabla J\left( w \right) $在切线方向的分量变为右上方,因而$ w $将朝右上方移动。最终,$ w $将稳定在顶点处,达到最优解 $w^{*}$ 。此时,可以看到 $w_{1}=0$ ,这也就是采用 $l_{1}$ 范数会使w产生稀疏性的原因。

其他正则化方法

Droupt out:是一个简单又极其有效的正则化方法.在训练的时候,随机失活的实现方法是让神经元以超参数p的概率被激活或者被设置为0。
最大范式约束(Max norm constraints):另一种形式的正则化是给每个神经元中权重向量的量级设定上限,并使用投影梯度下降来确保这一约束。在实践中,与之对应的是参数更新方式不变,然后要求神经元中的权重向量$\overrightarrow{w}$必须满足$||\overrightarrow{w}||_2<c$这一条件,一般c值为3或者4。有研究者发文称在使用这种正则化方法时效果更好。这种正则化还有一个良好的性质,即使在学习率设置过高的时候,网络中也不会出现数值“爆炸”,这是因为它的参数更新始终是被限制着的。

优化方法

训练技巧

  • 推荐的预处理操作是对数据的每个特征都进行零中心化,然后将其数值范围都归一化到[-1,1]范围之内。
  • 使用标准差为$\sqrt{2/n}$的高斯分布来初始化权重,其中n是输入的神经元数。例如用numpy可以写作:w = np.random.randn(n) * sqrt(2.0/n)。
  • 使用L2正则化和随机失活的倒置版本。
  • 使用批量归一化。

参考资料

0%