Neural Network 2 课程笔记翻译

一、设置数据和模型

在前面的章节中我们介绍了神经网络的模型。从结构上,神经网络把神经元组织成层次结构。从数学上,它进行了这样一个数学运算——这个点积之后再进行非线性化。把这些特点综合起来,我们得到了一个新形式的评分系统。相较于之前我们介绍的模型来说,它在简单的线性映射基础上做了扩展。神经网络构造了一种线性和非线性交织在一起的映射序列。在这一节中,我们将会讨论一些更多设计神经网络的细节,比如数据预处理、权重初始化,损失函数。

1.1 数据预处理

数据预处理本质上就是一个数学trick。在不改变原有性质的基础上,但是变得更容易操作了。
有三种形式的数据预处理。我们假设数据为 X X X X X X是一个N X D的矩阵,N是数据集中样本个数,D是每个样本的维度。

1.1.1 减去均值

再讲之前首先我们说说啥是“中心化”。从几何意义来进行解释,中心化是把原有的数据在分布不变的情况下平移到以原点为中心的位置上。这样数据就以原点为中心呈现一个对称的形态。“减去均值”的目的就是把数据进行“中心化”。数据的每个维度都会以原点为中心。操作的方式对整个X取平均值,之后的在每个数据点的每个维度上都减去这个平均值。对于图形来说,这个平均值既可以是以整个图片为单位,也可以分别以不同的色彩通道为单位。

1.1.2 规范化(归一化)

规范化的意思是把数据的各个维度调整到都差不多大小的程度。实现规范化有两种常见方法:

  • 一种是在实现0中心化之后,对每一个维度,用这个维度的数据除以这个维度的标准差 X /= np.std(X,axis =0)
  • 还有一种方式是,经过调整,每个维度上的最大值和最小值是1和-1
    规范化有意义的前提是:每个维度上的数据在尺度上相差较大,同时对于学习来说每个维度的上数据同等重要。
    在图片学习的领域,像素的相对尺度差别不大(都是从0到255),所以没有必要执行这个预处理步骤。
    在这里插入图片描述

1.1.3 PCA和白化

1.1.3.1 PCA

PCA就是主成份分析(Principal Component Analysis),是一种降维技术,说白了就是计算资源有限的情况下,排列优先级的一种方式。

第一步 计算协方差矩阵

我们首先计算协方差矩阵。这个协方差矩阵能够告诉我们数据中的一些相关结构信息。【参考求协方差矩阵.md】

# Assume input data matrix X of size [N x D]
X -= np.mean(X, axis = 0) # zero-center the data (important)
cov = np.dot(X.T, X) / X.shape[0] # get the data covariance matrix

协方差矩阵中的第(i,j)号元素代表了第i维和第j维之间的相关性,这个是用相关系数表示出来的。
协方差矩阵的一个特点是其对角线就是方差。
协方差矩阵是对称的而且是半正定的。【其实按照英语的语序来说是正的、半定的】

第二步计算协方差矩阵的特征向量

在协方差矩阵上计算SVD因子【注:参考PCA的一些零散笔记.md】

U,S,V = np.linalg.svd(cov)

矩阵U的列向量是协方差矩阵的特征向量。【其实我们利用SVD的主要目的是方便的求出特征向量】
S是一个一行D列的奇异值向量。【因为S是一个只有在对角线上才存在元素的矩阵,所以S用一个向量来存储。这个奇异值其实就是特征向量对应的特征值】

第三步 投影与筛选

我们将原数据(已经做了0中心对称了)投影到特征向量构成的特征空间中。

Xrot = np.dot(X, U) # decorrelate the data

【注:从几何的角度去理解,其实矩阵乘法就是一种线性变换,这个在《人工智能基础课》里说的还比较清楚。是把一个线性空间里的点映射到另一个线性空间里。】
U的列向量是一组正交而且模为1的向量,所以他们是一组规范正交基。投影意味着把原始数据X旋转到以特征向量为新坐标轴的空间里去。如果我们再投影过后的矩阵“Xrot”再计算协方差矩阵,我们可以得到一个对角矩阵。np.linalg.svd的一个优良性质是,构成返回值U的特征向量是按照其特征值的大小进行排列的。通过只保留最靠前的若干列特征向量,我们可以消掉很多维度,并且将方差为0的维度丢弃掉。这种方法也称为“PCA”降维:

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

经过了一通操作,我们可以原始数据的大小从[N X D]削减到[N X 100],这100个维度包含最多的方差。
通过PCA进行降维,你的训练效果一般不会打折扣同时也能节省很多开销。

1.1.3.2 白化

白化做了两件事:1. 把原始数据映射到特征空间里(用特征空间的基来表达)【这个其实不是白化做的而是上面PCA做的】。2. 用特征值去除每个维度以达到规范尺度的目的。这个操作的几何解释是:如果输入数据是一个多变量的高斯分布,数据白化就是把数据变成一个0均值的高斯分布,并且获得一个单位协方差矩阵。

关于“identity covariance matrix”我之前的理解是有误的

并且在这个过程中协方差矩阵是保持不变的。【初始数据的协方差矩阵和白化之后数据的协方差矩阵是一样的,说明啥,相关性没有发生变化,不会因为白化而发生变化】

但是这句解释的还挺想会事的。

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

注意:噪声放大。 通过在分布上增加一个小项10的负5次方,可以避免除以0的问题。白化的一个缺点是它放大了的噪声,它把所有的维度都等量的放大了,也包括那些方差特别小的不相关的维度,这些一般都视作噪声。在实践中,这个缺点可以通过更强的平滑(比如把10的负5次方加到一个更大的数)来缓解。
在这里插入图片描述

左边,原始输入数据是2维的。中间,经过PCA,数据变成中心对称的并且旋转到协方差矩阵的特征基构成的坐标系中。这样把数据去相关了(协相关矩阵是对角的)。右边:每一个维度被特征值缩放,把协方差矩阵转换成单位矩阵。几何上,这相当与把数据通过延展和压缩变成了各项同性的符合高斯分布的数据集合体。

我们通过可视化在CIFAR-10上的图片进行操作演示这种操作来直观的感受这些操作带来的效果。CIFAR-10的训练集大小是50000 X 3072(一个五万张图片,每张图片都被展成一个1 X 3072的向量)。之后我们计算 [3072 X 3072]这个矩阵的协方差矩阵。之后计算协方差矩阵的SVD分解值。特征向量看起来什么样呢?
在这里插入图片描述

**左边:**49张图片的样本集。**左数第二个:**3072个特征向量的前144维。头几个特征向量构成了数据绝大部分方差【感觉像人类中的20%人口掌握着人类80%的财富一样】,它们构成了图片的低频部分。**右数第二个:**经过PCA后由144个特征向量构成的图片。在PCA之前图片的每个向量包含3027个维度,而每一个维度代表图片中某一个区域或者某一个通道上的像素的明度。在PCA之后,构成的图片的3072维向量的被替换成了144维的向量,而向量的每一个维度衡量了这个维度在构成图片方面的贡献度。为了查看144个数字中保留了多少图片信息,我们必须把它旋转回含有3072个数字的“像素基”(pixel basis)。因为U(左奇异矩阵)是一个旋转,这个操作可以由乘以一个U.transpose()[:144,:]来实现【当初是 XU 既实现旋转有实现了裁剪,那么要旋转回去的话乘以 U T U^T UT,但是这个时候X已经少了很多列了,所以 U T U^T UT也要砍掉一些行才行,先旋转过来在砍掉一些行,“:144”这个表示保留前144行,“:”表示保留全部的列】。你可以看到图片有点模糊,说明最前面的特征向量主要捕获了图片中的低频部分。但是图片大部分的信息始终被保持。右边: 白化以后的可视化展现,144个维度中的每个维度都被压缩到相同的长度。这里白化的144个数字通过乘以U.transpose()[:144,:]实现了旋转回“像素基”中。现在图像中的低频部分(贡献了绝大多数方差)可以忽略不计了,而高频部分(贡献了原始图片的少量方差)变得更显著了。

1.1.4 实践做法

在卷积神经网络中这种变换操作其实不需要,我们这里提到它只是保证数据预处理的完整性。但是0中心对称化还是很重要的,对每个像素进行归一化也是很常见。

1.1.5 常犯的错误

关于数据预处理的非常重要的一点是——数据的预处理中用到的各种统计量(比如均值)只在训练数据上进行计算并获得,之后在应用到验证集和训练集中。以均值为例,利用全部的数据集计算均值之后在所有的数据上减去均值之后再把数据分成训练集、验证集、训练集的方法是错误的。正确的做法是,先把数据分成训练集、验证集、测试集。在训练集上计算均值,之后再在各个集中减去均值。

1.2 权重初始化

1.2.1 易犯错误:全零初始化

让我从“不该怎么做”开始这个话题。当训练完成时,我们并不知道每个权重会是怎么样的,但是想象大约一半的权重是正的一半是负的,这样一种假设应该是合理的【why】。一个听上去合理的想法是把所有的权重初始化为0,这样我们可能期望获得预期之中的“最佳猜测”。但是这种想法是错的。因为如果所有的神经元计算相同的输出,那么在反向传播的时候他们会得到同样的梯度,并且得到同样的参数更新。换句话说,如果所有的权重初始化成一样的值,那么在神经元之间就没有任何力量打破这种平衡【原话是没有“不对称源”】

1.2.2 随机小数字初始化

因此,我们仍然希望权重非常接近0,但不能是0。一种常见的方法是用很小的数初始化权重,这种方法称为“对称破坏”。这个方法的理念是:每个神经元一开始都是随机并且唯一的,因此他们会有不同的梯度,虽然它们一起构成了一个网络中但是又各有不同。【统一而又丰富多彩是吗】。对权重矩阵进行初始化的操作一般像这样:W = 0.01* np.random.randn(D,H)。其中randn是标准正态分布( μ = 0 , σ = 1 \mu = 0 , \sigma =1 μ=0,σ=1)。利用这个公式,每个神经元的权重向量被初始化为一个随机向量,而这个随机向量又服从多维的高斯分布,所以神经元在输入空间的指向是随机的。利用均匀分布也是可以的,但是实践中似乎效果不如用正态分布好。

注意: 并不是数字越小越好。权重越小,在反向传播中计算出来的梯度也越小。(因为梯度与权重的值是正比关系)。过小数字会导致梯度在反向传播的时候被很大的削弱,这在深度神经网络中是非常令人担忧的问题。

1.2.3 利用1/sqrt(n) 调整方差

上面提到的方法(利用随机小数对参数进行初始化)有一个问题,那就是输出分布的方差会输入数量的增多而增多。如果每个神经元有n个输入,我们把这个神经元的权重向量除以sqrt(n),通过这样的方法可以把方差归一化为1。我们推荐这样一种初始化参数的方法(这种方法也是一种启发式方法):w = np.random.randn(n) / sqrt(n),其中n是输入到神经元的输入的个数。
在这里插入图片描述

通过这种方法,所有的神经元在一开始的时候有类似的输出分布。而且在实践中确实能提高收敛率。
大概的推导过程如下:设权重w和输入x的内积为s,也就是$s = \sum_i^n w_i x_i$ 。这个被激活前的一个值。s的方差可以进行如下的转化:

KaTeX parse error: No such environment: align at position 8: \begin{̲a̲l̲i̲g̲n̲}̲ \text{Var}(s) …

证明的一些细节还是有点跳跃参考注释【Xavier initialization.md 这个笔记其中的一部分解释了这个证明过程】
头两步利用了方差的性质。第三步我假设输入和权重都是0均值的,也就是 E [ x i ] = E [ w i ] = 0 E[x_i] = E[w_i] = 0 E[xi]=E[wi]=0。但并不是所有的模型都适用这个情况:比如ReLU单元均值就是非负的。最后一步我们假设 w i , x i w_i,x_i wi,xi服从同一个分布。从这个推导过程中我们能了解到这样一个事实:如果我们希望s在所有的输入x下都有相同的方差,那么在初始化的时候我们就要保证每个权重的方差是1/n。因为$\text{Var}(aX) = a^2\text{Var}(X)$,其中x是随机变量,a是标量。所以我们可以先用标准正态分布得到一组数,之后再用 a = 1 / n a = \sqrt{1/n} a=1/n 进行缩放,使这一组数的方差为1/n。所以初始化变为:w = np.random.randn(n) / sqrt(n)。

相关的分析参考Glorot等人所著的Understanding the difficulty of training deep feedforward neural networks。这篇文章最后,作者推荐这样的初始化方法: Var ( w ) = 2 / ( n i n + n o u t ) \text{Var}(w) = 2/(n_{in} + n_{out}) Var(w)=2/(nin+nout),其中 n i n , n o u t n_{in}, n_{out} nin,nout分别代表前一层的输入的神经单元的个数以及下一层输入的神经元个数。这个结论是在对反向传播梯度的等效分析之后,综合各方面折中的结论。最近的一篇论文Delving Deep into Rectifiers: Surpassing Human-Level Performance on ImageNet Classification,针对如何对ReLU神经单元进行初始化,得出了这样的结论——神经网络的方差应该是2.0/n。因此初始化方式为:w = np.random.randn(n) * sqrt(2.0/n)。在实践中如果使用ReLU,那么推荐这种初始化方法。

1.2.4 稀疏初始化

还有一种解决方差问题的方法是,让权重矩阵大部分都为0,少数部分不为0。当然选择谁为0谁不为0是有讲究的,我们知道权重矩阵提现了神经元之间的连接,矩阵的每一列代表从上一层的某一个神经元看这个元素到下一层每个神经元之间的连接情况,如果这一列都是0的话,说明这个上一层的这个神经元是和下一层是没有连接的,这个是不行的,至少要保证每个神经元和下层的连接有10个。
在这里插入图片描述

而且不为零部分的数值是从高斯分布采样而来的。

这跟dropout很像啊,就是通过砍掉一些连接来让函数的功能弱化。

1.2.5 偏置初始化

由于上面的一通操作都是针对权重的,而且已经打破的对称性了。所以很多人基本上就不怎么在乎偏置了,很多时候都把偏置初始化为0。对ReLU的情况,有些人喜欢用0.01这样的小数来初始化偏置。这是因为这样可以保证ReLU在一开始就会激发,获得并传播梯度。但是是否能在后面的进程中一直保持这样的效果是不确定,事实上有些实验结果证明这样做的效果会更糟。所以更加常见的就是0来进行初始化。

1.2.6 实践

如果是ReLU,推荐使用这样的初始方法:w = np.random.randn(n) * sqrt(2.0/n)

1.3 批量归一化

显式的让整个网络的输出强制变成标准正态分布。在训练一开始的时候通过这种初始化的方法,可以解决很多问题。一个核心的洞见是:归一化(或者叫规范化)这种操作是一个简单数学表达式,而且这个表达式可微。因此上述的方法(显式的让整个网络的输出强制变成标准正态分布)是可行的。实现的方法是直接在全连接层(或者卷积层)后面直接添加BN层,并且BN层要置于非线性层。在这里我们不打算对原理进行展开说明因为在链接论文里说的非常清楚。因为BN能够显著的提高网络的健壮性,所以现在BN的使用已经非常普遍。BN也可以从这样一个角度理解:它相当于对网络中的每一层进行预处理,并且以一种可微的方式集成到网络中。实在是太妙了。

1.4 正则化(L2/L1/Maxnorm/Dropout)

控制神经网络的能力以避免过拟合有以下这几种方法:

1.4.1 L2正则

L2方法的操作方法是直接在目标函数中加入每个参数的平方项来实现惩罚的目的。
L2方法的背后的理念是,希望能够尽可能将所有的输入都利用起来,而不是特别依赖某一些输入。
L2方法起作用的机制是——权重和输入是一个乘法关系【我理解吧,所谓的乘法关系就是乘积固定的两个数,当一个变大的时候另一个就变小,目标函数的目标是尽可的小嘛,假设存在一个最小点,当我增加 λ \lambda λ的时候,w要变小(相对于没有正则化项的时候)才能保证最小点不变,而wx是不变的,x也不会变大,因为输入值是固定的。但是可以实现在x不变的时候,w变小。真是简单但具有深刻的意义啊】。
L2方法倾向于惩罚那些存在极端数值的向量,而倾向于选择分布均匀的向量。
利用了L2方法,实际上意味着所有的权重是线性衰减的:W += -lambda * W

【感觉有必要对L2进行一下专题研究】

1.4.2 L1正则

如果L2引入的是一种“均匀性”,那么L1引入的是一种“稀疏性”。
L1的做法是:在目标函数中加入了这样的项,对于每一个权重,都引入一个对应的项 λ ∣ w ∣ \lambda |w| λw
可以把L1和L2结合起来, λ 1 ∣ w ∣ + λ 2 w 2 \lambda_1 \mid w \mid + \lambda_2 w^2 λ1w+λ2w2,这个叫做弹性网络正则化。
L1有一个有趣的性质:在优化的过程中,让权重向量变得稀疏(比如非常接近0)。
L1结束的时候,因为很多的权重最后基本上就是0了,与这些权重对应的输入部分等于说基本上没有发挥作用。权重没有变成0的对应的输入部分可能是比较重要的输入部分。而且这个部分相对于整个输入来说比例很小可以说非常稀疏。
L1相对于输入中噪声是恒等不变的。【这句啥意思?】
相对于L1来说,经过L2正则化的权重向量经常是分散,小的数。
实践中,如果你并不特别的关注某些特定的特征,L2的效果会比L1好一些。

1.4.3 最大范式约束

还有一种正则化的形式是——在权重向量上增加一个上界,利用受投影梯度下降来加强这种限制。
操作的方法是:跟正常一样进行参数更新,但是用一个不等式 ∥ w ⃗ ∥ 2 < c \Vert \vec{w} \Vert_2 < c w 2<c将每个神经元的权重向量限制在一个范围内。c的取值一般在3或者4.
有人做过实验,说这种方法效果还不错。
这个方法的一个性质是,即使学习率设置的很大,loss值也不会爆掉,因为更新的上限被限制了嘛。

1.4.4 随机失活

这是一个极其有效、简单正则化技术,对传统的方法(L1、L2、maxnorm)是一个很好的补充。
这种方法的思想是,为神经元增加“活跃状态”这样一个属性。这样,在训练的任何一个时刻,网络中任何一个神经元要么是活跃的(active)要么是不活跃(zero)的。其活跃的概率可以用P来表示(这样又多了一个新的超参数)。
在这里插入图片描述

上面这个图可真是把这个方法的实质表现出来了。让某个神经元随机失活相当于弱化整个网络的表达能力,相当于被敌人施加了能力下降的魔法,能力肯定下降,至于下降多少是随机的但是也有一个限度。
我们可以把随机失活理解为在原有的神经网络中采样之后生成一个新的神经网络。之后的训练,输入数据还是用原有的输入数据,但是值更新这采样后生成的网络里面的参数。
通过采样生成的网络可能性有指数多个,但是他们之间并不是相互独立的,因为他们还共享这参数。
最后测试的时候用的不是随机失活的模型而是整体模型,整体模型的能力相当于指数个子网络模型集成起来的能力。
【我这里还想到一些问题:

  1. 每次训练完之后生成的参数是否会用到随后(不一定是下一个有可能是后几个)随机生成的网络里(如果随后生成的网络有部分参数和当前的这个网络有重合)?
  2. 这样的训练要进行多少轮?
    我的一个猜测是,如果当前生成的网络用到的参数在之前所有训练过的网络里有重合的,那么就把当时训练的结果拿过来用,也就是在原有的效果上进行叠加。因为文中说到共享
    如果不这样,每次每个网络训练出来了一堆参数,最后这些参数怎么整合到一起呢?我想象不出来。

是不是觉得这样的训练方法有一些集成方法的特点,集成真的是一个非常重要的思想。我觉得蜜蜂寻找新的巢穴的表决方法也是一种集成方法。集成有两种方法一种是独立的生成若干的模型,再把若干个模型加起来平均。还有一种方法是模型会不断的优化,每次优化完毕会形成一个记录点,进化到最后会形成若干的记录点,一般人可能只会保留最后一个,但是一种更聪明的思想是把每个记录点都当成一个不同的模型,把他们加起来形成一个新的模型。这两个方法好像,一个是在同一时间找很多不同的人来进行表决。第二个方法是同一个人,但是不同的时间阶段,比如当进行一个表决的时候一个人把小时候的自己,少年时候的自己,青年时候的自己,中年时候的自己,老年时候的自己都叫过来进行表决。后一种方法非常超现实,对生活在线性空间的我们真的非常难想象。我们没有这种思维方式,这让我想到那篇科幻小说“你一生的故事”,其实这种决策方法也有叫做3个10法则,10分钟后的自己、10天以后的自己、10个月后的自己、10年后的自己。
集成就是一种民主表决的方法。

在一个3层【从下面的代码看,输入层应该没算在这三层里】神经网络里,串行随机失活的实现方式如下:

""" Vanilla Dropout: Not recommended implementation (see notes below) """
p = 0.5 # probability of keeping a unit active. higher = less dropout
def train_step(X):
    """ X contains the data """
    # forward pass for example 3-layer neural network
    H1 = np.maximum(0, np.dot(W1, X) + b1)
    U1 = np.random.rand(*H1.shape) < p # first dropout mask
    H1 *= U1 # drop!
    H2 = np.maximum(0, np.dot(W2, H1) + b2)
    U2 = np.random.rand(*H2.shape) < p # second dropout mask
    H2 *= U2 # drop!
    out = np.dot(W3, H2) + b3
    # backward pass: compute gradients... (not shown)
    # perform parameter update... (not shown)

def predict(X):
    # ensembled forward pass
    H1 = np.maximum(0, np.dot(W1, X) + b1) * p # NOTE: scale the activations
    H2 = np.maximum(0, np.dot(W2, H1) + b2) * p # NOTE: scale the activations
    out = np.dot(W3, H2) + b3

在上面的代码中,随机失活的操作只在向前传播里进行了两次操作,分别是第一个隐藏层和第二个隐藏层。其实也是可以在输入层进行的,如果要这样就给输入也产生一个随机的二机制掩码即可。同时,随机失活在反向传播的时候也是可以使用的。在反向传播的时候使用随机失活的话还要在产生新的掩码。
总的来说就是,你可以在向前传播和反向传播的过程中的每个一阶段都进行失活,只要在这一层上施加一个掩码就可以了。

以下的内容至关重要:在预测阶段我们不再进行失活操作,而是在每一个隐藏层的输出上乘以一个P。【这一部分的注释可以看:注释:dropout中的一些理解难点.md 这里有一些详细的数学证明,虽然是我自己琢磨的】
在测试阶段,所有的神经元能看到所有的输入,而我们想让神经元的输出和他们在训练阶段的输出保持一致。【这里有一个翻译问题,我觉得原文写的逻辑就又问题,原文的逻辑是一个因果关系,而我觉得应该是一个转折关系。特别是在你搞清了所有的机制以后。我觉得作者的逻辑是有问题的。】
比如说:P=0.5的情况,在测试阶段的神经元必须要把他们输出二等分才能和他们在测试阶段的输出期望一致。为了搞明白这点,假设某一个神经元的输出为x(失活之前的)。失活之后,这个神经元输出的数学期望变成了 px + (1-p)0,这个很简单把,就是一个简单的01分布【到这里,终于明白了,这一段从一开始说的“输出”是一种含有概率的输出,也就是输出的数学期望】。
在测试阶段,当我把神经元保持全活的时候,我们必须把输出x调整成px来保证相同的数学期望。这样一种缩小操作其实就相当于遍历了全部可能的二进制掩码(或者说遍历了全部可能子网络,而且这种子网络是指数级别的)并且还计算出了这些子网络的集成预测值。【原话直译过来是这样的——这种“打薄/减量/稀释/衰减”的操作与遍历所有的可能的二进制掩码有关(因此是指数级子网络的个数)而且计算了他们的集成预测。其实我理解这句话表达的意思是:这个 releated to 虽然是有关,但是我认为这里可以翻译“相当于”。也就是“这种衰减的操作就相当于遍历所有可能的二进制代码”,言外之意就是这种直接成倍数的方法比起遍历所有的子网络的可能组合简直高效太多了】。

一个不太好的性质是我们必须在测试的时候在激活后面乘以P。因为测试性能非常关键,因此我们经常使用“反向失活”,也就是在训练的时候进行放缩,而不动前向传播在测试时的代码。这有一个好处是,你可以保持预测代码不动。如果你想进行任何改进,你只需要在训练的代码里动就行了。“反向失活”的代码如下:

"""
Inverted Dropout: Recommended implementation example.
We drop and scale at train time and don't do anything at test time.
"""
p = 0.5 # probability of keeping a unit active. higher = less dropout

def train_step(X):
    # forward pass for example 3-layer neural network
    H1 = np.maximum(0, np.dot(W1, X) + b1)
    U1 = (np.random.rand(*H1.shape) < p) / p # first dropout mask. Notice /p!
    H1 *= U1 # drop!
    H2 = np.maximum(0, np.dot(W2, H1) + b2)
    U2 = (np.random.rand(*H2.shape) < p) / p # second dropout mask. Notice /p!
    H2 *= U2 # drop!
    out = np.dot(W3, H2) + b3
    # backward pass: compute gradients... (not shown)
    # perform parameter update... (not shown)

def predict(X):
    # ensembled forward pass
    H1 = np.maximum(0, np.dot(W1, X) + b1) # no scaling necessary
    H2 = np.maximum(0, np.dot(W2, H1) + b2)
    out = np.dot(W3, H2) + b3

当“随机失活”这种方法被发明出来之后,就有很多人开始研究它为什么能够在实践中起那么大作用,以及它跟其它正则化方法的关系是什么。

推荐下面的阅读:
Dropout paper
Dropout Training as Adaptive Regularization
文章里证明了:随机失活正则化和利用“逆对角fisher信息矩阵”计算结果对特征进行缩放之后使用L2正则化的效果一阶等价。

1.4.5 与向前传播中的噪音有关的主题

“随机失活”属于一种更加更高层的方法门类,这个方法门类主要研究在前向传播中引入随机行为。
在测试阶段,通过使用“解析的方式”(比如乘以一个倍数P)或者“数值的方式”(比如采样:让前向转播通过几个不同的随机网络并且对这几种网络进行平均)噪声得到了抑制。在这个领域里还有一个方法叫做“随机失联”,相对于随机失活中随机的把某些权重置0,它采取的方法是随机选取权重的一部分。先做一个预告,卷积神经网络也利用了类似的技术,比如:随机汇总,片段汇总,数据增强。这里先按下不表,我们将来会展开讨论。

1.4.6 偏置正则化

在线性分类那一部分我们已经提到过:其实并不经常对偏置参数进行正则化。这个还得从目标函数的数学表达式说起,在目标函数的表达式中,偏置是一个常数项,由于求导运算的特点这一项经常就没了,不像权重是和数据之间是有乘法关系,对权重进行求导之后还会留下一些系数和降了一阶的权重。所以它几乎不会对数据的内在结构产生什么影响。【注:这里原话几乎不是这么说的,所以我完全按照我自己的理解对这一段进行描述,感觉就像是把原来的语言素材进行融化之后形态重塑的感觉,也可以成为“化”】
但是事实上,你对偏置进行正则化也不会导致非常明显的性能下降。这很可能是由于相对于权重来说,偏置的数量太少了,即使是导致一些问题,分类器也完全扛得住。

1.4.7 逐层正则化

对不同的层进行不同强度的正则化非常少见(除非是对输出层)。【其实这点从数学上就能明白,我们从结构上看是一层一层的,其实这个都是一个数学表达式的某一个嵌套部分,如果我们对同一个数学表达式的不同部分进行不同处理,那还是同一个表达式吗,要处理就要对全部项进行处理。对输出层进行处理相当于就是在全体上进行缩放了。这里我提出一个观点,就是神经网络的本质就是数学的,我们用网络这个形象的工具只是方便思考,但是根上都是数学的,所以所有的知识吃不准的时候,或者像寻找内在联系的时候最好在回到数学的本源上】
关于这个思路的相关文献非常少。

1.4.8 实践

使用单一的、全局的,经过交叉验证L2正则化强度是比较常见的【这里我不太明白经过交叉验证的正则化是什么意思】。
也可以加入随机失活,一般是在每层使用完随机失活再用L2。如果用随机失活,P可以设为0.5,不过这个值在验证集上可以进行调整。【在验证集进行调整是啥意思】

这么几句话写的,感觉意思七零八落的。刨去那些不理解的成分。从新梳理如下:

  1. 用L2正则
  2. 如果要用dropout,P设为0.5,而且先用dropout,再用L2

二、损失函数

目标函数中的正则化项,提现了对模型复杂程度的惩罚或者说抑制。另一项是数据损失,在监督学习问题中,它提现了预测和真值之间的一种匹配程度。数据损失是利用将每一个样本的损失值加起来求平均的方式来表达的。也就是说 L = 1 N ∑ i L i L = \frac{1}{N} \sum_i L_i L=N1iLi中的N是训练样本的个数。让我们把神经网络输出的激活简写为 f = f ( x i ; W ) f = f(x_i; W) f=f(xi;W)。在实践中可能会遇到这样几类问题

2.1 分类

分类问题使我们迄今为止一直在讨论的问题。我们假设有一个样本的数据集,对于每一个样本还有一个唯一正确的标签集合。有两种非常常见的损失函数。SVM的Weston Watkins表达式是:
L i = ∑ j ≠ y i max ⁡ ( 0 , f j − f y i + 1 ) L_i = \sum_{j\neq y_i} \max(0, f_j - f_{y_i} + 1) Li=j=yimax(0,fjfyi+1)
我们之前简要暗示过,有人认为用平方折页损失会有更好的性能(也就是用 m a x ( 0 , f j − f y i + 1 ) 2 max(0, f_j - f_{y_i} + 1)^2 max(0,fjfyi+1)2)。【关于这个多分类SVM的损失函数形式参考:注释:SVM损失函数 Weston Watkins 形式 如何理解.md】
第二种形式是用softmax分类器中的交叉熵损失:
L i = − log ⁡ ( e f y i ∑ j e f j ) L_i = -\log\left(\frac{e^{f_{y_i}}}{ \sum_j e^{f_j} }\right) Li=log(jefjefyi)

2.2 问题:类别数特别大的时候

当标签集合非常大的时候(英语字典,ImageNet里包含22000个分类)可以使用层次softmax分类(解释在这)。层次softmax 把标签分解成一棵树。每一个标签对应于树上的一条路径(这个不就是香侬编码么)。在树上的每一个节点训练softmax分类器来走左枝还是右枝【原文是消除左枝或者右枝之前的歧义】。树的结构在很大程度上会影响性能,而且树的结构基本上是有问题决定的。

2.3 属性分类

【理解这部分可以看我写的注释:注释:Attribute classfication.md】
上面所讨论的损失都假设只有一个正确答案 y i y_i yi。但是我们考虑这样一种情况, y i y_i yi是一个向量,每个分量对应一种属性,它的取值只有两种取值比如说1,或者-1。当样本具有某个属性的时候它的值是1,当没有这个属性的时候它的值是-1。并且一个样本可以具有多个属性。举个例子,Instagram上的图片可以被打上不同的标签,这些标签可能是热门标签的子集,而热门标签是全部标签的子集,每张图可以有多个标签。一个合理的方法是为每一个属性单独的建立一个二分类器。当我们为每一个属性单独的建一个二分类器的时候,它的代价函数是这样的形式:
L i = ∑ j max ⁡ ( 0 , 1 − y i j f j ) L_i = \sum_j \max(0, 1 - y_{ij} f_j) Li=jmax(0,1yijfj)
每个样本上的损失是把全部类别上的损失值相加而得, y i j y_{ij} yij的值是+1或者-1,它代表第i个样本上的第j个属性存在或者不存在。每个属性都有一个分类器的话,每个属性就会对应一个计分函数。当第j个属性的分类器判断这个属性存在的时候,所对应的计分函数 f j f_j fj是正数,反之则是负数。当判断样本具有第j个属性(意味着 y i j = + 1 y_{ij}= +1 yij=+1)并且f的得分小于1, y i j f j y_{ij}f_j yijfj是一个0到1之间的数,那么 m a x ( 0 , 1 − y i j f j ) = 1 − y i j f j max(0,1-y_{ij}f_j) = 1-y_{ij}f_j max(0,1yijfj)=1yijfj。这样就产生了损失值并且被累加起来。同理,当判断样本不具有第j个属性(意味着 y i j = − 1 y_{ij}= -1 yij=1)并且f的得分大于-1, y i j f j y_{ij}f_j yijfj是一个0到-1之间的数,那么 1 − y i j f j 1-y_{ij}f_j 1yijfj就是一个大于0小于1的数,也就是 m a x ( 0 , 1 − y i j f j ) 还 是 等 于 1 − y i j f j max(0,1-y_{ij}f_j) 还是等于1-y_{ij}f_j max(0,1yijfj)1yijfj,这种情况下也产生了损失。

损失函数还有另一个替代方案,就是利用逻辑回归分类器。对于二分类逻辑回顾来说,类别是{0,1}。类别是1的概率为:
P ( y = 1 ∣ x ; w , b ) = 1 1 + e − ( w T x + b ) = σ ( w T x + b ) P(y = 1 \mid x; w, b) = \frac{1}{1 + e^{-(w^Tx +b)}} = \sigma (w^Tx + b) P(y=1x;w,b)=1+e(wTx+b)1=σ(wTx+b)
由于类别1上的概率和类别0上的概率加起来为1,因此在类别0上的概率为: ( P ( y = 0 ∣ x ; w , b ) = 1 − P ( y = 1 ∣ x ; w , b ) (P(y = 0 \mid x; w, b) = 1 - P(y = 1 \mid x; w,b) (P(y=0x;w,b)=1P(y=1x;w,b)。所以当 σ ( w T x + b ) > 0.5 \sigma (w^Tx + b) > 0.5 σ(wTx+b)>0.5时,样本被判断为正例。又因为 σ ( w T x + b ) > 0.5 \sigma (w^Tx + b) > 0.5 σ(wTx+b)>0.5等价于 w T x + b > 0 w^Tx + b> 0 wTx+b>0。因此也可以说当 w T x + b > 0 w^Tx + b> 0 wTx+b>0时,样本被判断为正例。逻辑回归的的损失函数是将概率的似然函数取对数并且最大化,代价函数形式如下:
L i = ∑ j y i j log ⁡ ( σ ( f j ) ) + ( 1 − y i j ) log ⁡ ( 1 − σ ( f j ) ) L_i = \sum_j y_{ij} \log(\sigma(f_j)) + (1 - y_{ij}) \log(1 - \sigma(f_j)) Li=jyijlog(σ(fj))+(1yij)log(1σ(fj))
其中 y i j y_{ij} yij要么是1要么是0,并且 σ ( ⋅ ) \sigma(\cdot) σ()是sigmoid函数。上面的表达式虽然看起来很吓人,但是其实它的梯度求起来极简单。梯度为:
∂ L i / ∂ f j = y i j − σ ( f j ) \partial{L_i} / \partial{f_j} = y_{ij} - \sigma(f_j) Li/fj=yijσ(fj)

2.4 回归

回归的目的是为了预测一个实数值,比如房价或者图片中某个东西的长度。对于这种任务,计算损失的一种常见方法是计算预测值与真实值之间的L2距离的平方【原文写的是计算预测值与真实值之间的差(loss)之后在计算L2模的平方,其实就是这两个数的L2距离的平方】或者L1距离。对于单个样本,损失的L2模平方的公式如下:
L i = ∥ f − y i ∥ 2 2 L_i = \Vert f - y_i \Vert_2^2 Li=fyi22
L2模之所以取平方是因为这样计算梯度更容易点,由于平方是一个单调操作,因此不会改变最优参数。而L1模的计算则是将每个纬度上的差的绝对值加起来:
L i = ∥ f − y i ∥ 1 = ∑ j ∣ f j − ( y i ) j ∣ L_i = \Vert f - y_i \Vert_1 = \sum_j \mid f_j - (y_i)_j \mid Li=fyi1=jfj(yi)j
由于想被预测的属性不止一个,所以用 ∑ j \sum_j j将所有关心的维度上的L1值加起来。如果只看第i个样本的第j个维度并且将真值和预测值之间的差记为 δ i j \delta_{ij} δij,这个维度的梯度可以被很容易的推导出来,要么是 δ i j \delta_{ij} δij(L2模的情况),要么是 s i g n ( δ i j ) sign(\delta_{ij}) sign(δij)。也就是,计分函数的梯度或者正比于差值,或者是一个固定值而且仅由差值的符号决定。

*警告:*相较于其它更为稳定的损失计算方法(比如softmax)来说,L2损失优化起来更难一点。直观上来讲,输出值越是精确越需要这个网络具有比较精巧的结构或者特定的属性。然而这点对于softmax却无关紧要,比起具体值是多少,我们更关心量级是否正确。另外L2并不是非常健壮,因为异常点会导致巨大的梯度。当我们面对一个回归问题时,首先考虑是否能把输出离散化映射到不同类别或者层次中【注意这里bin原意是桶的意思,把原本混在一起的东西分开到不同的桶里,其实就是分类分层的意思】。举个例子,如果你想对一个产品进行星级预测,用5个独立的分类器进行1到5分的平方比你用回归损失效果好得多。不像回归只输出一个值而没有任何对应的置信度,分类相对于回归还有一个好处就是能够给输出的分布情况。如果你确定分类用在你的问题里不合适,那么就小心的使用L2。L2事多一点(原文是L2更fragile,本身比较fragile也就是使用者用的时候要考虑的事情多一点,所以我翻译成事多一点)。在L2损失中使用dropout不是好主意(特别是紧挨着L2的前一层上使用dropout)

当我们面对一个回归任务时,首先考虑它是否一定要用回归。相应的,只要有可能,我们强烈建议把输出离散化到不同的层次中把它变成一个分类问题。

2.5 结构预测

结构化的损失对应于这样的情况——标签可以是任意的结构:图、树或者其它复杂的结构。一般来说,我们一般假设结构空间很大而且无法枚举。解决这类问题主要还是用结构化的SVM,它基本思想是,在正确的结构 y i y_i yi与得分最高的不正确的结构之间寻找一个最大边界。把这个问题看成“以梯度为主要手段来解决非受限的优化问题”是不太合适的。对于这种问题往往需要设计专门的解决办法来简化结构空间。这里我们只是简单的提一下这个问题,但是具体的细节超出这门课的范围。

三、总结

总结起来:

  • 推荐这样的数据预处理过程:先将数据中心化使其均值为0,之后对每个特征的尺度进行归一化使其尺度在[-1,1]
  • 利用标准差为 s q r t 2 / n sqrt{2/n} sqrt2/n的高斯分布来初始化权重,其实其中n为神经元的输入数。例如在numpy中:w = np.random.randn(n) * sqrt(2.0/n)
  • 利用L2正则以及随机失活(反向随机失活)
  • 利用批量规范化
  • 我们讨论了现实中你可能会碰到的各种问题,以及针对每一种问题最常见的损失函数是什么。
    我们现在已经完成了数据的预处理以及初始化好了模型。在下一个章节中我们会关注学习过程以及相应的机制。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值