深度学习
SNN(Standard Neural Network):标准神经网络,房价预测可以使用这种神经网络。
CNN(Convolution Neural Network):卷积神经网络,图像处理可以使用这种神经网络。
RNN(Recurrent Neural Network):循环神经网络,语言翻译和音频处理等序列化数据可以使用这种神经网络。
非结构化(例如图片、文字、音频)的数据比结构化(例如表格)数据的学习要困难得多。
推动深度学习发展的有三大因素:数据、计算能力(GPU)和算法。
神经网络的前向传播与反向传播:
前向传播:是数据从输入层输入,经过一层层隐藏层计算得到中间变量,最后从输入层得到输出的过程。换句话说,前向传播计算出了神经网络对输入的预测输出。
反向传播:是根据前向传播计算的预测值与真实值的差距得到损失函数,并对损失函数利用梯度下降来更新参数w,b的过程。应用梯度下降的核心在于计算损失函数对各w,b的偏导数。而计算这些偏导数的过程是反向的,也就是从最后一层逐层向前去计算,去改变每一层的w和b,其原理是链式求导法则。
我们以一个有两层隐藏层的神经网络为例:
在这个例子中,我们使用前向传播依次得到x=>z[1]=>a[1]=>z[2]=>a[2]的值,使用反向传播结合链式法则依次得到:
dL/da[2]=>dL/dz[2]=>dL/dw[2]&dL/db[2]=>dL/da[1]=>dL/dz[1]=>dL/dw[1]&dL/db[1]的值。
综上,前向传播是神经网络根据输入得到输出的过程(a[l-1]=>a[l]),反向传播是神经网络根据损失函数得到参数偏导,从而利用梯度下降更新参数的过程
(dJ/da[l]=>dJ/da[l-1],dW[l],dB[l])。
一个神经元从输入到输出的过程也可以用下面这张图表示,即x->z->a:
除了线性激活函数,sigmoid激活函数和ReLU激活函数外,再介绍两个激活函数:tanh激活函数和leaky ReLU激活函数。他们分别是sigmoid激活函数和ReLU激活函数的同位替补。
其中tanh激活函数的值处于[-1,1]之间,其具有基值为0的特点,在处理非二元分类问题时通常比sigmoid激活函数更有优势,因而除了二元分类问题,适合使用sigmoid激活函数的场合都可以使用tanh激活函数替代。
而leaky ReLU激活函数在负值区域(0.01可以修改为任意小值)具有斜率,可以避免ReLU激活函数在负值区域斜率为0的缺点,而在实际使用时使用ReLU还是leaky ReLU激活函数不会产生特别大的差别,故大部分场合使用ReLU激活函数就可以了。
使用ReLU或者leaky ReLU激活函数作为隐藏层激活函数会使神经网络的效率相比于使用sigmoid和tanh激活函数高出不少。
常见激活函数的导数如下:
神经网络的矢量化:对神经网络的矢量化既涉及不同的神经元,又涉及不同的训练数据。
1.前向传播的矢量化:
对于同一训练数据而言,当其经过一个有n个神经元(换句话说就是有n个输出)和m个输入(换句话说就是上一层有m个神经元)的网络层时,基于公式可知,该层的[w][l]是一个nm的矩阵,而该层的[b][l]和输出aj[l]是一个n1的向量。
而对于一组训练数据样本而言,我们可以用矩阵X表示数据样本集,其中X的每列代表一个数据样本,而每行代表一个数据样本的不同输入。假设样本个数为m,每个样本的输入个数为n,则X的规模为n*m:
由于单个样本时的X规模为(n*1),因而[w]和[b]的规模都不需要修改,对X的矢量化会自动对Z和A矢量化,即Z和A的每列都会是不同样本的值,而横向则是不同的神经元。
2.反向传播的矢量化:假设每层神经元输入输出满足:z[l]=W[l]a[l-1]+B[l],a[l]=gl
则按照反向传播规则,对于每个样本来说,有:
①dL/dW[l]=dL/dz[l]dz[l]/dW[l]=dL/dz[l]a[l-1]T
②dL/dB[l]=dL/dz[l]
③dL/dz[l-1]=dL/dz[l]dz[l]/da[l-1]da[l-1]/dz[l-1]=W[l]TdL/dz[l]g[l-1]’(z[l-1])
以此类推得到反向传播递推式,从而可以反向传播得到各偏导值,而最后一层的dL/dz[l]值可以由dL/da[l]da[l]/dz[l]=dL/hat{y}hat{y}/dz[l]直接得到。
如果要扩展为样本总集的矢量化,由于dJ=1/m∑(dL),则有:
Z[l]=W[l]A[l-1]+B[l],A[l]=gl
①dJ/dW[l]=1/mdJ/dZ[l]dZ[l]/dW[l]=1/mdJ/dZ[l]A[l-1]T
②dJ/dB[l]=dJ/dZ[l]
③dJ/dZ[l-1]=dJ/dZ[l]dZ[l]/dA[l-1]dA[l-1]/dZ[l-1]=W[l]TdJ/dZ[l]*g[l-1]’(Z[l-1])
深度神经网络是指有很多隐藏层的神经网络,它的优势在于:它可以让前面的神经层学习一些级别较低的简单特征,而让后面更深的神经层汇聚前面所检测到的简单信息以便检测更复杂的事物。
我们把神经网络的层数、每层的神经元数量、每个神经元用到的激活函数、总迭代次数、学习率α称为神经网络的超参数,而把[W]和[B]称为参数。即超参数是可以认为设置的,而参数则是神经网络自己学习而来的。大量事实表明,神经网络超参数的可选性是很高的,且要找到一个最佳的超参数组是没有理论和系统方法的,对于不同的神经网络,超参数的最佳取值也会不同,因而在构建神经网络时应该大胆尝试超参数的选值,并利用交叉验证集以寻求一个不错的结果。
在机器学习中,我们了解了神经网络的正则化,事实上那种正则化方法被称为L2正则化,而正则化作为一个泛类,还包含了其他很多方法,但其目的都是为了解决过拟合问题,我们再介绍一个被称为随机失活的正则化方法:
随机失活(Dropout):仿照L2正则化引入正则化项以整体减小w的目的,随机失活指出,我们遍历每层网络层的每个神经元,并为每层网络层设置一个留存率(keep-prob),对该层上的每个神经元进行一次随机留弃,决定丢弃的各神经元会被切断所有的输入输出通路。随机失活的结果会带来一个比原有神经网络小得多的新神经网络,并对该神经网络进行前向和反向传播。其原理即把某些神经元的[W]和[B]参数完全置0,其效果类同于L2正则化减小w。换个角度思考,因为我们可能把任意的神经元失活,这会促使神经网络在学习参数时不过度依赖于某个输入特征,进而将w更均匀且更小的分配给每个输入特征。
对于每一层,由于随机失活消去了一部分的神经元,这会导致该层的输出期望改变为原来的输出期望乘以留存率;为了保持输出期望不变,我们最后还要对随机失活处理后的输出值除以留存率才能作为下层输入。
在新的一次迭代时,神经元的留弃会被重新确定。同时,正如L2正则化只在训练集中起作用一样,随机失活也只会针对训练集,而对于测试集数据,我们只会使用完整的神经网络而不进行随机失活。对于不同的网络层,我们可以使用不同的留存率,对于输入输出关系越复杂的网络层(上层和下层都有很多神经元),越有可能产生过拟合,就越可以采取较低的留存率,可见留存率也是一个可供选择的超参数。
随机失活的缺点在于,它让代价函数变得不够明确,因为每次的随机失活都会导致代价函数发生变化,当你想要去检验梯度下降算法表现时,你很难确认代价函数真的随着迭代减小了。
随机失活常常用于计算机视觉领域。
梯度消失与爆炸:
深层的神经网络会受制于梯度消失与爆炸的问题,具体表现为权值[W]随着层数产生指数级减小或增大的现象,这会导致训练神经网络时梯度的计算结果会过小或过大,进而影响训练效率和效果,而这个问题始终是训练深层神经网络的巨大壁垒。为了减弱梯度消失和爆炸的影响,我们可以在神经网路的权值[W]初始化中下功夫:
数学证明,对于ReLU类的激活函数和tanh类的激活函数,我们可以使用不同的权值初始化方法来达到减弱梯度消失和爆炸的效果。
对于ReLU类的激活函数,我们可以使用Xavier初始化(Xavier Initialization),具体地:
让权重[W]满足均值为0,方差为sqrt(2/n)的正态分布,其中n为该层网络层的输入个数。
而对于tanh类的激活函数,我们可以使用Kaiming初始化(Kaiming Initialization),具体地:
让权重[W]满足均值为0,方差为sqrt(1/n)的正态分布,其中n为该层网络层的输入个数。
此外,也有一些其他的权值初始化方法,例如让权重[W]满足均值为0,方差为sqrt(2/n+m)的正态分布,其中n为该层网络层的输入个数,m为该层网络层的输出个数。
如果你觉得有必要,也可以把权值初始化方法当作超参数经由交叉验证集检验。
梯度检验:
当我们算出一个函数的偏导公式时,如何检验这个公式真的准确无误又或是可能因为马虎写错了呢?此时就要使用我们的梯度检验,所谓的梯度检验事实上就是导数检验。
如果要得到一个函数对应导数的数值解,可以使用双边公式f’(x)=[f(x+ε)-f(x-ε)]/2ε,其中ε是一个很小的值,然后拿这个数值解与导数的解析解f’(x)=[f(x)]’做比较,就可以检验导数的解析解是否真的是对的了。对应到反向传播算法中,我们如何检验dJ/dW和dJ/dB的公式准确无误呢?我们只需要将[W]和[B]组合成一个大的矩阵[θ],并根据代价函数得到J([θ]),然后对每一项θi都进行一次双边公式得到数值解并与解析解J’(θi)作比较即可。
比较的方法是,两值的欧氏距离与他们各自与原点的欧氏距离之和的比值:
如果结果介于10-7-10-5之间,说明梯度检验通过,导数并没有解析问题,反之则要好好检查。值得注意的是,如果J有L2正则化项,则求导数的解析解和数值解时都应该要带上该项;且梯度检验不适用于随机失活方法中,因为此时的J不具有参考价值。
神经网络的小批量(Mini-Batch)梯度下降法(称为随机梯度下降SGD):在神经网络中同样也有小批量的概念用于处理大规模的样本集。将大规模的样本集平均分为相等的几部分,每部分分别记为X{t}和Y{t},对于每个部分进行一次梯度下降的迭代,并在下一部分继承上一部分迭代得到的参数,故n部分的小批量梯度下降总共需要做n次梯度下降。由于只采用部分数据,因此它的每一步在接近全局最小值时可能不够可靠甚至有些嘈杂,有时甚至会背离最小值移动,但它整体仍沿着趋向全局最小值的方向运动。但其优势在于当训练集很大时,它仍然能以较快的速度完成每一步迭代,这也是为什么当训练集很大时我们总是去使用小批量梯度下降法。
其他的梯度下降算法:
梯度下降算法有很多更好的变体,例如之前学过的Adam算法,这里我们从理论上解释其优化原理。在此之前,我们先介绍指数加权平均的思想。
指数加权平均曾在软更新中得到过应用,其公式即:
其中vt是本次的加权平均结果,vt-1是上次的加权平均结果,θt是本次的原始结果,β是介于(0,1)之间的权重因子,通常取β=0.9。
上式是一个递推式,如果写成一般式,即:
可以看到,指数加权平均结合了所有的过去数据来得到最终结果,且当前的原始结果是最大参照项。利用指数加权平均,我们结合了过去数据和本次数据,从而得到了一个值得信赖的结果。且由于指数加权平均的递推公式,使得其在编程中非常容易实现,其中递推终点为v0,定义v0=0。这样进行指数加权平均有一个问题,由于v0=0,故在前几次迭代中平均值vn和原始值θt会相差很大,例如v1=0.1θ1,v2=0.09θ1+0.1θ2…为了解决这个问题,我们引入偏差修正的概念:我们发现,θ的各系数是一个等比数列,其和为(1-βt),因而如果我们把最后的结果再除以(1-βt)就可以得到一个比较合理的归一化结果,且当t趋于无穷时(1-βt)趋于1,就和原来的指数加权平均公式类似了,偏差修正主要为了平和迭代次数较少时平均值和原始值的差距。例如v1=0.1/0.1θ1,v2=(0.09/0.19)θ1+(0.1/0.19)θ2
①动量梯度下降(Momentum Gradient Descent):
在运行一般梯度下降时,我们可能会遇到下面的情况:
动量梯度下降希望加快横轴方向的学习速度,减缓纵轴方向的学习速度,此时就可以用指数加权平均的思想。在每次迭代计算出原始值dW和db后,利用指数加权平均得到计算值vdW和vdb,并使用指数加权平均后的计算值来更新W和b,其公式即:
由于我们要迭代多次且只取最后的迭代结果,因而没有必要进行偏差修正,且通常仍取β=0.9。
②均方根前向传播(Root Mean Square Prop=RMSprop):
其公式与动量梯度下降类似,仍然是利用指数加权平均的思想。只是此时的原始值为dW和db的平方,记计算值为Sdw和Sdb,更新参数为dW/sqrt(Sdw)和db/sqrt(Sdb)。
通常取β=0.999。
当然,运用均方根前向传播时可能碰到的问题在于,Sdw和Sdb可能会非常小以至于dW/sqrt(Sdw)和db/sqrt(Sdb)趋于无穷,这不利于算法的稳定。解决办法是在分母加一个小量ε,通常取ε=10-8。即:
③Adam算法(Adaptive Moment Estimation):
Adam算法结合了动量梯度下降和均方根前向传播,被认为是运行梯度下降的最佳权威算法。
记动量梯度下降的相关参数为:vdW,vdb,β1。
记均方根前向传播的相关参数为:SdW,Sdb,β2。
初始时,令vdW,vdb,SdW,Sdb均为0,在每次迭代时遵循以下规则:
即利用动态梯度下降的方法计算出vdW,vdb,利用均方根前向传播的方法计算出SdW,Sdb,然后对vdW,vdb,SdW,Sdb进行偏差修正,最后的更新量为vdW/(sqrt(Sdw)+ε),vdb/(sqrt(Sdb)+ε)。
参数的选择一般直接取默认值β1=0.9,β2=0.999,ε=10-8。
学习率衰减(Learning Rate Decay):
加快学习算法的另一个方法是随时间慢慢减少学习率α,这么做的话可以让开始时有一个较大的学习率,从而加快学习速度;同时快要结束时有一个较小的学习率,从而能够更加细致地探查局部最小值,从而达成收敛,避免耗费无意义的时间在低谷附近反复摆动。
衰减学习率的方法也有很多,下面简单介绍几种:
①:其中decay-rate为衰减率,是一个超参数;epoch-num为迭代次数,α0是初始学习率。
②:其中0.95可以是任意小于1的值
③:
利用上面三种方法,可以使学习率α随着迭代次数epoch-num增加而减小。
接下来让给我们看一下局部最优的问题:
曾经我们被教导,梯度下降算法可能会陷入局部最优中而无法找到全局最优。但事实上,对于大型神经网络组成的高位空间而言,其存在局部最优的可能性是很小的,其整体图形大概率不是左边的峰谷型而是右边的马鞍形。这是因为局部最优产生的条件是,对于每个维度而言都在该处取到极小值,如果我们认为各维度下导数为零的点是极小值或极大值的概率均等,则对于n维空间而言,导数为零的点会产生局部最小值的概率仅为0.5^n,这对于高维空间而言几乎是不可能出现的,而导数为零的点更有可能出现右边的马鞍形结构,称该点为鞍点(Saddle Point)。即部分维度在鞍点取到极小值,部分维度在鞍点取到极大值。
对于马鞍形结构,梯度下降算法是有能力破局的。假设起点如下图所示,依照梯度理论,损失函数将逐渐下降到鞍点,并在鞍点附近因为随机扰动离开鞍点,往某一边继续进行梯度下降,从而完成破局。当然,存在的问题在于鞍点附近的导数接近于0,这可能会导致梯度下降算法在鞍点附近花费较长的时间,而这也是Adam等算法脱颖而出的原因。
综上,在高维度的神经网络中,我们通常不用去担忧梯度下降算法可能会陷入局部最优的问题,但鞍点的存在同样会导致梯度下降算法在鞍点附近花费较长的时间,幸运的是,Adam等算法的问世有效的提高了梯度下降在鞍点附近的效率。
在之前的学习过程中,我们学习了很多的超参数。例如学习率α,Adam算法中的β1,β2和ε,神经网络的层数,每层神经网络神经元的数量,学习率衰减参数,小批量更新的规模等。这些参数的调整也存在一定的优先级,一般来说,学习率α总是最重要的,其次是每层神经网络神经元的数量和小批量更新的规模,再其次是神经网络的层数和学习率衰减参数,而Adam算法中的β1,β2和ε通常直接取默认值就可以,无需更改。现在的问题是,对于一个完全陌生的神经网络,我们如何为这些超参数选值呢?
一个很好的方法是对所有的超参数在给定的可能最优范围内随机取值并重复多次,找到这些取值里效果最好的一组或几组超参数集合,并缩小可能最优范围到这些超参数附近,再次重复上述步骤,直到认为找到了接近全局最优的超参数。即随机选取与逐级精细。
对于可能最优范围内的随机取值,需要根据范围选择使用自然坐标轴还是对数坐标轴。例如[50,100]内的随机取值用自然坐标轴更好,而[0.0001,0.1]或[0.9,0.999]内的随机取值用对数坐标轴更好。对数随机在Python中的实现可以参考以下代码(以[0.0001,0.1]为例):
n=-3*np.random.rand()-1;
result=10^n;
return result;
补充知识:关于归一化、标准化和正则化
归一化(Normalization):把数据变为(0,1)之间的小数,比如min-max归一化。主要是为了方便数据处理,因为将数据映射到0~1范围之内,可以使处理过程更加便捷、快速。
标准化(Standardization):数据的标准化是将数据按比例缩放,使之落入一个小的特定区间。并不是为了方便与其他数据一同处理或比较,比如:z-score标准化,即零-均值标准化,数据经过零-均值标准化后,更利于使用标准正态分布的性质,进行处理;
正则化(Regularization):用一组与原不适定问题相“邻近”的适定问题的解,去逼近原问题的解,这种方法称为正则化方法。利用先验知识,在处理过程中引入正则化因子(regulator),增加引导约束的作用,比如在逻辑回归中使用正则化,可有效降低过拟合的现象。
Batch归一化(Batch Normalization):
在之前的学习过程中,我们意识到对于神经网络和一般的线性或逻辑回归而言,对输入变量进行归一化或正则化可以有效的改善程序运行效率。事实上,我们对于隐藏层的各层结果也可以进行归一化,这被证明可以加快神经网络的学习速度。其基本过程如下:
首先需要注意的是,我们要进行Batch归一化的对象是各隐藏层的[z],由z[l]=W[l]a[l-1]+B[l],我们先接受上一层的输入,再经W和B得到z[l],之后对z[l]进行Batch归一化(Batch归一化的过程在后面详述)得到tilde{z[l]},最后对tilde{z[l]}进行激活得到a[l]=gl作为下一层的输入。可见相比于一般的神经网络,Batch归一化在a=g(z)上额外加了一步算法。
现在让我们看看这步算法究竟干了什么,我们采用z-score标准化的逻辑:
首先,我们假设在逻辑层[l]上存在n组样本数据z(1)~z(n),利用这些数据我们计算出该层z的均值和方差,从而利用z-score标准化变换得到均值为0,方差为1的数据集znorm(为了防止分母为零加了小量ε),但是我们不希望要求神经网络对z有一个明确的分布,因此tilde{z}是znorm线性化的结果,其中γ和β都是由神经网络通过梯度下降等方法训练更新迭代而来的参数,其性质和更新过程等同于w和b。
Batch归一化能奏效的原因是它限制了前层的参数更新影响数值分布的程度。
值得指出的一点是,Batch归一化下每层的学习参数似乎有四个w,b,γ和β,但由于要对z进行z-score标准化为均值为0,方差为1的数据集,因此参数b的取值是无意义的,可以不进行学习而固定为0,从而减少学习参数为三个w,γ和β。此外,实际应用时Batch归一化常与小批量更新mini-batch共用,故此时对于每个mini-batch而言,其均值和方差的计算均是基于该mini-batch对应的数据集得到的,即对于不同的mini-batch来说其均值和方差是不同的,和真正全局的均值和方差之间是存在噪声的,这也是mini-batch的一个特性。
而当模型训练完毕后使用测试集测试训练效果时,如何为测试样本选择均值和方差呢?
我们通常采用指数加权平均的方法,即不选择任一mini-batch的均值和方差作为最终测试用的均值和方差,而是使用指数加权平均将每个mini-batch的均值和方差综合纳入最终的考量。
机器学习策略:
1.正交化(解耦):
所谓正交就是一个参数的调试只能影响一个结果,在调试过程中,我们应尽量避免一个参数的调试会同时改变多个结果,那么这个参数将会很难调整出一个多维都很完美的结果。就像调整电视屏幕一样,我们安排三个旋钮,其中一个只调整屏幕宽度,一个只调整屏幕高度,一个只调整屏幕角度,如此只需要对不同旋钮分别操作就可以得到一个很好的结果,反过来,如果一个旋钮可以同时控制屏幕宽度和屏幕角度,那么这个旋钮很难调出一个好结果。反映到深度学习领域,判断一个模型的好坏主要要参照四个参数:训练集的适配程度,交叉验证测试集(也叫开发集dev set)的适配程度,测试集的适配程度,在真实情况下的表现。为了分别调整这些参数,我们显然也要采用四个正交化的旋钮,保证在改变一个数据集适配程度时不影响其他数据集的适配程度。例如改变训练集的适配程度可以采取更大的神经网络或更好的梯度下降算法
2.单一数字评估指标:
有时候,我们判断一个模型的好坏可能会涉及多方面的参数,要一一比较这些参数的好坏是很困难的,最好的解决方法是给出一个由这些参数根据某个函数计算出的综合结果,也即所谓的单一数字,利用该数字做升序或降序排列便可以很好的评估不同模型的效果。典型的例子是精确率和召回率的矛盾,以及F1 Score的提出。
3.满足指标和优化指标:
有时单一数字评估指标并不是特别容易得到,应对这种多参数问题的有效方法是设置满足指标和优化指标。所谓满足指标是指一旦指标满足要求就被认为可行,而不在意该指标到底有多好;所谓优化指标是需要尽可能优化到最佳的指标,两者结合选择出最佳模型。例如下图是一个辨识猫的神经网络,我们设置满足指标为Running time<100ms,优化指标为Accuracy。则符合满足指标的为A和B,取优化指标最佳的则最佳模型为B。
4.训练、开发、测试集的划分:
要求数据集中不同类型(Distribution)的数据被按比例均分到每个集中,而不是将某个类型全分到训练集中,而把另一类型全分到开发集中。开发集和测试集的数据量对于不同体量的神经网络差别不是很大,这意味着随着数据集的增大,训练&开发&测试集的占比不是一成不变的。一般开发和测试集数据量在1w左右,对于小型神经网络而言三集比例一般在60-20-20左右,而对于大型神经网络可以扩大到98-1-1。
5.优化神经网络:
调整评估指标:当原有的评估指标认为最佳的神经网络模型经人为检验存在问题时,就有必要调整评估指标。
改变开发-测试集:开发-测试集作为检验实际运行的样本,需要确保与实际过程的匹配。例如辨别猫的神经网络,人们拍出的照片往往模糊而构图不专业,此时若以一些高清而专业的照片作为开发-测试集,会因脱离实际情况而存在误差。
用人的表现衡量神经网络:在机器学习中提到过相应的概念,为了衡量神经网络的效果,通常需要有一个基准误差,这个基准误差应当是理论最低误差,也被称为贝叶斯最优错误率,但一般而言,选取人类在该问题上的误差就可以得到一个很好的基准了。利用训练集误差与基准误差的差值,交叉验证集误差与训练集误差的差值之间的关系可以判断神经网络目前存在高偏差(欠拟合)还是高方差(过拟合)问题,从而选择相应的方法解决问题。在人类擅长解决的领域中(比如自然语言处理),人类误差接近于贝叶斯最优错误率,训练集误差几乎难以超越人类误差,使用这个方法判断神经网络出现的问题是很不错的;而在人类不擅长解决的领域中(比如大数据分析),神经网络学习得到的训练集误差是可以超越人类误差的,当训练集误差超越人类误差时,对神经网络下一步改进的思路便会变得复杂,难以预料。
误差分析:所谓误差分析,即人类在系统判断错误的样本中抽取一部分来观察出错的原因,并取其中占比较大的优先解决。我们可以为样本集添加大量由最大原因导致的同类错误样本来集中训练神经网络在这方面的缺陷。误差分析适合该任务人类擅长时,可以作为指导方向的参考,避免花了大量精力解决了一个可能不是那么重要或者占比不是那么大的出错原因。
清除标注错误的数据:有时训练集中的数据标注可能有误,例如把一张明明是狗的图片标注为猫,如果在进行误差分析时认为标注错误的数据占比较大,则可以进行重新标注。但一般而言,随机性的标注错误并不会对高鲁棒性的神经网络造成很大的影响。
6.初始化神经网络原则:
在开始搭建神经网络时,一般不考虑任何可能会导致误差和干扰的因素,要求快速构建起神经网络并迭代,而后分析其结果,根据结果来决定下一步做什么,解决哪些关键的干扰和误差因素。
不匹配数据的神经网络:
仍以辨别猫的神经网络为例,人们拍出的照片往往模糊而构图不专业,此时若以一些高清而专业的照片作为训练集训练神经网络,会导致训练数据与实际数据的不匹配而难以得到很好的效果。例如有20w个高清照片和1w个人们拍的照片,我们不希望直接对人们拍的照片进行模型训练,因为训练样本太少了;我们也不提倡按之前讲的将不同类型的数据按比例均分到每个集中,因为我们真正希望模型分辨的是人们拍的照片,假设选取2500个开发集数据的话,如此均分则人们拍的照片只有119张,而开发集的目的在于告诉我们模型在真实数据下的表现,显然均分得到的开发集将会花费大量精力处理那些高清图片上,这不是我们想要的。一个合适的选择是,选取20w高清图片和5k张模糊图片组成训练集,再选取各2500张模糊图片组成开发集和测试集,这么做的好处在于开发集中的所有数据均来自于我们真正关心的图片分布,但这么做也会带来一个问题,那就是训练集中大部分数据仍是高清图片,如此训练得到的神经网络在实际模糊图片的输入下可能达不到一个很好的效果。
要解决这个问题,我们首先要知道怎么判断不匹配数据的神经网络的效果好坏:
我们假设基准误差为0,如果训练集误差为1%,开发集误差为10%,对于一般的神经网络我们可以认为其具有高方差的问题。但对于不匹配数据的神经网络,由于开发集与训练集的数据并非来自同一类别,开发集误差高究竟是因为模型训练的差还是两者数据不匹配程度高是很难有定论的,解决方法是将训练集拆出一部分作为训练-开发集(Training-dev Set),训练-开发集不参与模型训练,而专门用于检测模型训练的结果。由于训练集和训练-开发集属于同一类型,故训练得到的模型经过训练-开发集的验证可以得到模型训练效果,也就是说训练-开发集承担了一般神经网络中开发集的作用。而训练-开发集与开发集的比较则可以看出数据的不匹配程度。
于是五者之间的关系如下:基准误差与训练集误差反映了偏差,训练集误差与训练-开发集反映了方差,训练-开发集误差与开发集误差反映了数据的不匹配程度,开发集误差与测试集误差反映了对开发集的过拟合程度。
如果出现高偏差或高方差的问题,可以参考之前的方法解决;而若发现数据的不匹配程度较高,一个很好的方法是进行人工数据合成,比如语言识别的训练集没有背景音,而实际应用时嘈杂的环境可能存在各种噪声,我们可以为原训练集增加背景噪声作为新训练集来训练神经网络,这被证明可以有效减小数据的不匹配程度,也就是提高了训练集与实际的接近程度。
端到端(end-to-end)的深度学习:
在了解端到端深度学习之前,我们先来看看一般的神经网络是怎么解决问题的。
以语音识别转文字模型为例,一般情况下它需要经过提取低层次特征,寻找音位,发现单词,最终得到文字这几个过程;以人脸识别系统为例,它需要经过提取画面中所有人脸,拿出人脸与数据库中的人脸进行比对的过程。相比于这种多阶段的流水线工程,端到端的强大之处在于它可以输入音频,并经过神经网络直接得到文字。端到端深度学习需要建立在庞大的数据库和神经网络的基础上,只有大量的数据集才能让神经网络尝试完成这种映射,真正的做到了让数据说话,减少了人类思维对神经网络过多的干预。端到端深度学习具有良好的发展前景,但在当下,让神经网络得到人类的干预会是更好的方法。
卷积神经网络:
基于深度学习的计算机视觉得到了巨大发展,可以承担诸如图片分类,目标检测等多种功能。但在应用计算机视觉时,有一个严峻的考验。对于大型高清的图片,它的输入变量规模约可以达到10001000(尺寸)3(信道)=3m,这是很大的一个输入变量,如果放到一个有1000个神经元的输入层,其参数规模将会达到3m1k=3b,这是不能承受的。而卷积神经网络为我们提供了一个解决办法。
相比于密集层可以得到上一层的所有激活作为输入,卷积层的每个神经元只能得到上一层的部分激活作为输入,换句话说,卷积层的每个神经元只能看到上一个网络层的一部分。
于是一个卷积层的基本架构包含四部分:上一层的输出(原始输入),过滤器(filter),过滤后的输入,线性化和激活后的输出。其中过滤器是卷积层的核心,过滤器是一个ff的矩阵,其中各矩阵元素可以给定,但在当下的深度学习框架下这些参数一般就是我们要利用反向传播算法学习的参数,基本原则仍然是代价函数和梯度下降,其地位等同于w和b,显然一个ff的过滤器就有ff个学习参数。它会按序遮罩输入层的各部分,并对原始输入和过滤器相同位置的元素进行标量相乘后相加,将结果放入过滤后输入矩阵的对应位置。值得注意的是,过滤器只有在完全包纳于上层输出时才会过滤元素,否则将会跳转到下一位置,跳转逻辑是从左至右,从上至下。
填充(Padding):由卷积规则,对于nn的输入矩阵和ff的过滤器而言,得到过滤后的输入矩阵规模为(n-f+1)(n-f+1)。这里要引出两个概念,分别是Valid卷积和Same卷积,其中Valid卷积是不经过填充的卷积,由上式可知对于任意f>1的过滤器而言,经过逐层卷积后的矩阵规模必然会越来越小,这有时会导致深层数据的规模过小而不足以训练参数;而Same卷积则经过填充来使输出的过滤结果与输入矩阵规模相同。而填充就是将原始输入扩大几圈的操作,其中填充元素均取0,这么做的好处是能使位于原始输入边缘的元素也能被卷积多次,从而增强边缘元素对模型训练的作用,记填充圈数为p,则填充p圈后再过滤得到的矩阵规模为(n+2p-f+1)(n+2p-f+1)。要做到Same卷积,则填充圈数应满足n+2p-f+1=n,即p=(f-1)/2。
为了方便实行Same卷积,以免p不是一个整数,故f通常取奇数;此外,f取奇数还有一个好处是是可以找到过滤器的中心点,方便在程序里表示出过滤器的位置。
步长(Stride):步长就是过滤器跳转到下一位置距离当前位置的长度。之前的讲解都是基于步长为1的情况,如果假设原始输入矩阵为nn,过滤器为ff,填充为p,步长为s,则过滤后的输入矩阵规模即为(其中符号⌊⌋为向下取整):
以上的讨论均基于二维卷积,实际应用时三维卷积更加常见,即增加了信道的维度。对于三维卷积,则过滤器也是三维的,且过滤器的信道数与原始输入的信道数保持一致,卷积计算的规则仍是过滤器遮罩部分相乘再相加,只不过此时过滤器遮罩的是一个三维立体,用包裹会比遮罩更形象。由此,三维卷积过滤后的输入仍然是一个二维矩阵。如果要使过滤后的输入是一个三维张量,可以对原始输入同时使用多个过滤器,每个过滤器分别负责不同的功能,并将每个过滤器的结果叠起来以形成三维张量,可见对于一个p=0,s=1,输入为nnnc张量,过滤器为ffnc张量的卷积层而言,其过滤结果规模为(n-f+1)(n-f+1)nc。
补充:张量,即Tensor。在数学概念中,张量是一个多维数组,它是标量、向量、矩阵的高维拓展,几维数组就是一个几维张量,我们可以将标量视为零阶张量,矢量视为一阶张量,那么矩阵就是二阶张量,一张RGB图片的像素就是三维张量(高度,宽度,信道)。
于是一个卷积层就可以被描述为:
以上图为例,输入为一663的张量,过滤器为两个333的张量,则过滤后的输入为一442的张量,为该张量增加总数为nc=2的统一偏差b1,b2得到z,再对计算偏差后的输入z进行激活得到a=g(z),本例中取g=ReLU,则最后的输出为一442的张量作为下一层的输入。该卷积层涉及的学习参数是:第一个过滤器的27个参数w1-w27,第二个过滤器的27个参数w28-w54,偏差b1和b2共计56个参数,相比一般的聚集层减少了很多。
最后对卷积层做个总结,如果一个层为卷积层,那么它包含了以下参数:
过滤器规模:f[l] 填充圈数:p[l] 步长:s[l] 过滤器数:nc[l]
输入张量:nH[l-1]nW[l-1]nC[l-1] 过滤器:f[l]f[l]nc[l-1]nc[l]
过滤后输入张量:nH[l]nW[l]nC[l] 偏差:111nC[l] 激活:nH[l]nW[l]nC[l]
输出张量:nH[l]nW[l]nC[l]
其中nH[l-1]和nH[l],nW[l-1]和nW[l]的关系如下:
卷积神经网络中除卷积层(Convolution)外,还有池化层(Pool)和全连接层(Fully Connected/FC)。
池化层只有超参数f和s(通常取f=2,s=2,如此处理池化层的输出高度和宽度便是输入的一半),没有学习参数,神经网络不从中学习来更新参数;也几乎不进行padding,即p=0。
可见对于一个nHnWnC的原始输入而言,其池化层过滤结果为*nC
池化层分为最大池化和平均池化两种,最大池化相较于平均池化更加常见。
最大池化(Max Pooling):取过滤器遮罩部分的最大值作为结果得到过滤后的输入。对于多信道原始输入的话,则是对每个信道的遮罩部分均取最大值。最大池化操作的功能在于只要在任何一次过滤内提取到某特征(比如说人脸识别中发现了人眼),它都会被保留在最大池化的输出中,如果没有提取到该特征,那么该次过滤的最大值就会比较小。
平均池化(Average Pooling):取过滤器遮罩部分的平均值作为结果得到过滤后的输入。对于多信道原始输入的话,则是对每个信道的遮罩部分均取平均值,
全连接层:也就是一般的神经网络层,上一层的输入被全部映射到本层的所有神经元。
常见的卷积神经网络的架构通常同时包含卷积层,池化层和全连接层。单独的卷积层重复叠加是得不到一个很好的卷积神经网络的。根据LeNet-5规定的卷积神经网路架构,卷积层和池化层是共生的,在一个卷积层(CONV)后需要跟进一个池化层(POOL),由于池化层不存在学习参数,故习惯上把一组卷积层和池化层的组合当做卷积神经网络的一层layer。在前期经历了若干层卷积+池化的组合后,随着卷积神经网络深度的加深,一个常见现象是a[l]的高度和宽度将会逐渐减小,而信道的数量会逐渐增大。在最后阶段,将卷积展开成为一维向量,经过若干层的全连接层(FC,即一般的神经网络层)后进入输出层得到输出。
该例对应每层的激活规模和学习参数为,可见激活规模正逐渐减小,且全连接层需要的参数数量远远大于卷积层需要的参数数量:
卷积有两个优势:参数共享和稀疏连接。参数共享表明一个特征检测器可以在输入的任何地方发挥作用,稀疏连接表明每个卷积层的输出均值取决于输入的一部分。
最后介绍两种特殊的过滤器,垂直边缘检测器与水平边缘检测器,两者分别用于检测图像的水平边缘和垂直边缘,分别定义为:
利用垂直边缘检测器与水平边缘检测器,我们会得到一个输出矩阵,矩阵各元素偏移零点的程度可以检测图像的水平边缘和垂直边缘,还可以根据值的正负来判断边缘由暗变亮或由亮变暗的趋势。假设输入为灰度图,则有:
垂直边缘检测:
水平边缘检测:
除了基本的边缘检测器之外,还有其他的边缘检测器:
当然,现今更流行的仍然是使用神经网络去自己学习边缘检测,这样子得到的边缘检测器不仅可以检测水平或垂直边缘,甚至可以检测各种角度和弧度的边缘。
对于RGB彩图而言,可以使用三维垂直边缘检测器与水平边缘检测器,即333的过滤器张量,每一层可以分别检测RGB某个信道下的垂直边缘与水平边缘,例如下例1检测红色信道边缘,下例2检测RGB各色组合的边缘。
从之前的讨论中我们可以发现,卷积神经网络中包含了极大量的超参数,这使得在自行构建卷积神经网络时往往无从下手。但好在由于卷积参数共享的优势,在计算机视觉任务中表现良好的神经网络框架往往也适用于其他任务,这使得我们可以参考前人提出的优秀网络架构来训练自己的模型,下面简单介绍几种:
LeNet-5可以识别图片中的手写数字,它是基于灰度图像训练的,输入为32321。
它经过了两层卷积+池化层,两层全连接层。LeNet-5的学习参数大约有60000个。
AlexNet可以识别图像的基本构造模块,基于RGB彩色图训练,输入为2272273。
AlexNet的学习参数大约有六千万个。
VGG-16的功能与AlexNet类似,但它的优势在于其网络架构非常清晰,各超参数之间存在明显关联,而缺点在于其学习参数高达约1.38亿个。
残差网络(ResNets):在之前我们讨论过,由于存在梯度消失和梯度爆炸的问题,非常深的神经网络是很难被实现的,而残差网络可以在一定程度上解决这个问题。在介绍残差网络之前,我们先介绍残差块(Residual Block)的概念:
上图是一个从a[l]到a[l+2]的神经网络,正常情况下a[l]会经历主路径(Main Path)的流程得到a[l+2],此时我们可以让a[l]通过捷径(Shortcut)或远跳连接(Skip Connection)将信息不经过主路径直接传递到神经网络的更深层。即此时有a[l+2]=g(z[l+2]+a[l]),值得注意的是,远跳连接的介入时刻位于线性激活后ReLU函数前,且由于z[l+2]和a[l]相加的要求,需要z[l+2]和a[l]需要具有相同维度,这在残差网络里是由多个Same卷积实现的。
由这些残差块组成的网络就是残差网络:
残差网络被证明能够有效避免梯度消失与爆炸问题,从而能使人们构建起更深层的神经网络。可以看到,它的构成是多层的Same卷积层穿插少量的池化层,并在最后进行Softmax分类。其能建立更深层神经网络的原因大致在于,前层的输入可以更加容易的进入更深层神经网络,从而可以削弱梯度消失与爆炸的影响。
11卷积(11 Convolution):
即f=1的过滤器,其主要作用是改变信道数量。之前讲到过池化层可以在保持信道不变的基础上改变张量的宽度和高度,而1*1卷积则可以在保持张量的宽度和高度不变的基础上改变信道数量。让我们来看两个例子:
在上例中,可以看到11卷积就是对原始输入在对应位置的切片相乘再相加,从而保持张量的宽度和高度不变,而由之前所学,过滤后输入的信道数量就是11卷积过滤器的个数。在下例中实现了32个1*1卷积过滤器来使原始输入的192个信道缩减为32个。
Inception网络:鉴于卷积神经网络中大量的超参数难以人工调整,Inception网络提供了一个方法来代替人工确定卷积层中的过滤器大小或是否需要创建卷积层或池化层,也就是让神经网络自己通过学习得到应该选用什么类型的过滤器,或是选择是否应用池化层。Inception网络的选择有四种,分别是应用卷积层11,应用卷积层33,应用卷积层55和应用池化层。以2828192的输入为例,选择应用卷积层11的规模是282864,选择应用卷积层33的规模是2828128(需要进行Same卷积),选择应用卷积层55的规模是282832(需要进行Same卷积),选择应用池化层的规模是282832(需要进行Same池化,这点比较特殊),最后将这些选择全部叠在一起得到2828256的合成张量,让网络自己学习它需要什么样的参数。当然,这么做会碰到的问题是大量的计算成本。
例如计算55的参数时需要进行(282832)(55192)=1.32亿次运算
为了降低计算成本,我们可以先进行一次1*1卷积计算:
此时计算次数为28281611192+2828325516=12.4m次,仅为前面的10%。
于是Inception网络的每层结构便如下图所示:
综上所述,在实际应用卷积网络时,我们有很多现成的模版可以套用,这大大较少了我们构建和训练网络的时间。也就是说当我们选定好网络架构后,便可以从github上寻找开源方案,这些网络已经被搭建完毕且经过了预训练,我们便可以使用该网络进行迁移学习。对于极小的训练集而言,可以保持非输出层参数不变而只训练输出层参数;对于稍微大一点的训练集而言,可以训练所有参数,其中非输出层参数的初值为已训练好的神经网络非输出层的参数。当训练集样本数量较少时,可以对图片进行旋转,放缩,扭曲,添加噪声等来对一个示例提出更多具有类似标签的新示例,实现在有限的样本集中添加数据。
目标定位(Object Location):之前讲过了图像分类问题,即给出一张图片判断它属于什么类别,其基本思路是将图片喂到训练好的卷积神经网络中,经过卷积+池化层,全连接层和softmax输出层得到类别。
而目标定位问题是在图片中标记出对象的位置,既适用于单对象又适用于多对象问题。要标记出对象位置,我们需要定位一个矩形(称为边界框bounding box),为此我们补充四个参数bx,by,bh,bw,定义图片左上角坐标为(0,0),右下角坐标为(1,1),则bx和by是边界框的中心坐标,bh是边界框的高度,bw是边界框的宽度。
我们假设一个模型既要判断图中物体是行人、汽车、摩托车还是背景(三者都不是),又要标记出图中物体的位置,为此输出变量应该包含以下元素:是否存在物体pc,边界框坐标参数bx,by,bh,bw,是否为行人c1、汽车c2、摩托车c3。如果采用平方误差函数作为代价函数,则其函数如下图所示,注意当pc=0时除了pc其他元素的值均无意义,不作为参考。
特征点检测(Landmark Detection):以人脸识别为例,相比于标记出人脸的边界框,我们可能更需要若干个标记人眼、鼻子、嘴巴的特征点来确认人脸,如此处理还可以做到判断人的表情等更高级的功能。为此,我们需要准备一个特征点训练集和对应的卷积网络,当输入人脸时输出一个包含各个特征点位置的高维向量。
目标检测(Object Detection):
我们先介绍基于滑动窗口的目标检测算法(Sliding Windows Detection Algorithm):
所谓的滑动窗口,就是在图片上按固定步长移动的小矩形视窗。我们每次只视察视窗内的物体,判断其中是否含有目标物体。以检测汽车为例,为此,我们先要创建一个标签训练集,其中x是视窗相同大小的图片,y是图片中是否包含汽车来训练一个卷积神经网络。而后,我们对原始图片应用滑动窗口,每滑动到一个地方,就把该窗口输入到训练好的卷积神经网络,判断其中是否含有汽车,如果有,则把此时的窗口作为边界框即可。其中步长和窗口大小可以自由确定。
这么做的问题在于计算成本,将原始图片分割成多个部分,再分别输入神经网络。更好的方法是将整张图片直接输入神经网络,而后利用过滤器来卷积出对应的窗口,最后在输出矩阵中反映每部分是否含有汽车。论文OverFeat提除了这种卷积滑动窗口的实现。
在此之前,我们先看看OverFeat怎么把全连接层转换成卷积层及其架构规定:
可以看到,一个n维的FC层可以用n个与上层输入维度相同的过滤器过滤后的结果表示。
对于任意大小的图片输入,OverFeat都会以这种卷积网络架构进行分析,其实际意义等效于进行大小为1414,步长为2的滑动窗口,而每个滑动窗口的结果都会被保存在输出矩阵的对应位置,例如输出矩阵的第三行第二列的值表示滑动窗口右移两次,下移一次对应窗口是否有汽车。使用卷积来等效滑动窗口使得输入网络只需要一整张图片,大大减少了计算成本。但两者都存在的问题是,由于此时窗口大小是固定的正方形,而实际物体的边界可能是长或高的矩形,这么做可能会导致边界框的不准确,而YOLO算法可以解决这个问题。
YOLO算法(You Only Look Once):
YOLO算法将一整张图片均分为1919个小部分,然后对某一个小部分应用图像分类和目标检测算法,每个部分都会输出一个八维向量y,YOLO算法判定一个物体是否在对应部分的方法是查看该物体的中心是否位于该部分内,例如下图中左车位于左中部分,右车位于右中部分,而虽然中间部分含有两车的部分,但由于不存在中心,故认为中间部分pc=0。可见如此处理的网络输出规模为19198,其局限性在于可能存在一个部分里存在多个物体中心的问题,此时仅凭一个八维输出无法解决,但由于19*19的细分,出现这种情况的概率很低。YOLO的优势在于相比滑动窗口以窗口和步长表示物体的模糊,YOLO的边界框可以具有任意的宽高比和更精确的坐标,且YOLO的均分也是基于卷积实现的,这意味着输入神经网络的仍然是一整张图片而不是某一部分,效率比较高,甚至可以做到实时识别。YOLO对于边界框的坐标规定如下:对于每个部分,认为左上角为(0,0),右下角为(1,1),单位长度为均分边。所以bx和by一定是小于1的,而bh和bw则可以大于1(跨部分)。
交并比(Intersection Over Union/IoU):算法给出的边界框和实际的边界框之间的交集与并集之比,实际应用目标检测算法时认为当IoU>0.5时,算法给出的边界框与实际的边界框匹配。IoU阈值设置的越高,则算法给出的边界框的匹配程度就越高。
非极大值抑制(Non-Max Suppression):在YOLO算法的实现时,可能存在很多个居于同一物体中心的均分块,它们都有可能判断令pc=1,这时候就可能给出很多个边界框。为了避免这一现象,我们可以先舍去所有pc<=0.6的低概率中心均分块,然后选取pc值最大的中心作为真正的中心,对应的边界框为真正的边界框,并舍去所有与最大可能边界框交并比IoU>=0.5的其他可能边界框,最后重复上一步骤,直到所有的待选中心均分块都被选取或舍去。这么做的好处是它根据pc的最大值确定某个物体最有可能的中心,同时对于不同的物体中心,由于IoU<=0.5,这些可能的物体中心不会被舍去,而是会在之后的循环中被找到。如果要同时判断多个不同类型的物体位置,需要对每个类型的物体判断均使用非极大值抑制。
Anchor Boxes:之前提到YOLO算法无法解决一个部分里存在多个物体中心的问题,Anchor Boxes可以解决这个问题,例如下面这个例子,车和人的中心都位于下方这个均分块,此时一个八维向量就无法表示了。需要用更高维的8*n向量,例如用16维向量,其中前八个量用于表示均分块内是否有车及其边界框,后八个量用于表示均分块内是否有人及其边界框。关键就在于怎么区分车和人,这就是Anchor Boxes的作用。Anchor Boxes规定了物体边界的样子,比如车一般是宽而矮的,而人一般是窄而高的。根据YOLO捕捉到物体边界框和规定的Anchor Boxes的交并比来判断捕捉到的物体是车还是人,然后确定向量对应位置的值。Anchor Boxes的选择既可以由人根据需要和训练集的分析预先确定,也可以由神经网络自行学习得到。
候选区域(Region Proposal):
在常规的目标检测算法中,事实上有很多地方是基本不会检测出物体的,例如下图2中的左下角和右上角,我们可以对图片运行图像分割算法得到图片的色块分布,根据色块的大致形状预测其在对应位置的真实图片里可能存在物体,并同步在对应位置运行分类器来判断是否存在物体,这么做可以减少一些不必要区域的物体判断,且同样可以被卷积神经网络实现。这种算法也被称为R-CNN。
人脸识别:人脸识别的关键在于比较当前输入的人脸和数据库中的人脸的“相似度”,定义差异函数d(img1,img2)为两张图片的差异程度,显然,比对成功即d(img1,img2)小于等于某个阈值τ,比对失败则d(img1,img2)大于τ。当然,直接拿两张图片给计算机来得到d是很难的,我们可以先把图片输入到训练的卷积网络中,并把某个全连接层作为输出层得到一个图片对应的多维矢量,如果两个图片相似,则它们在同一卷积网络的同一输出层的多维矢量也类似。这个卷积网络被称为Siamese网络架构,输入称为x,映射的多维矢量为f(x)。
于是两张图片x(i)和x(j)的差异函数便可由两个多维矢量的差异函数表示,而两个多维矢量的差异函数就是两者的欧几里得范数,也就是两矢量对应位置的元素差值的平方和的开方。也就是:‖f(x(i))-f(x(j))‖2。于是如果x(i)和x(j)相似,则‖f(x(i))-f(x(j))‖2小于等于阈值τ;如果x(i)和x(j)不同,则‖f(x(i))-f(x(j))‖2大于阈值τ。
那么怎么训练能使神经网络实现上述效果呢?反向传播算法仍然可以使用梯度下降及其变体,问题的关键在于使用什么样的损失函数。在此之前,我们先介绍三元组(Triplet)的概念:人脸识别的训练集通常由若干个三元组组成,每个三元组记为(A,P,N),其中A表示Anchor,是对应人脸的基准图片;P表示Positive,是对应人脸的匹配图片;N表示Negative,是对应人脸的不匹配图片。于是要使神经网络正常工作,差异函数应该满足d(A,P)<=d(A,N),也就是‖f(A)-f(P)‖2<=‖f(A)-f(P)‖2。当然,为了让AP与AN之间差异函数的值差的足够大,通常会增加一个间隔(Margin)α,即d(A,P)+α<=d(A,N)。
于是其损失函数可以定义为:L(A,P,N)=max{‖f(A)-f(P)‖2-‖f(A)-f(P)‖2+α,0}
如此定义的原因是当d(A,P)+α<=d(A,N)时,认为神经网络正常工作,于是其损失为0;而当d(A,P)+α>d(A,N)时,神经网络不够优秀,需要使用损失限制。
而代价函数则是训练集每个三元组的损失函数之和,即:
人脸识别的训练集通常不会太大,例如1k人只需要约1w张照片即可。但在构建三元组的时候A,P,N的选择通常不会随机,因为这通常会导致正常工作条件d(A,P)+α<=d(A,N)非常容易满足,达不到训练神经网络的效果。于是我们需要选择一些比较由训练难度的APN组成三元组,也就是说选择那些d(A,P)和d(A,N)相差无几的组成三元组,这么做可以训练神经网络参数使d(A,P)变得尽可能小而d(A,N)变得尽可能大。
此外,也可以用二分类网络来进行相似度判断:
我们同样使用Siamese网络架构,即数据库中的图片和新输入的图片经过同一个网络架构得到输出矢量f(x(i))和f(x(j)),其中数据库图片对应的输出矢量可以进行预处理提前得到。我们通过比对数据库中的图片和新输入的图片对应的输出矢量并传递到Sigmoid单元,根据Sigmoid单元输出结果是0还是1来判断两张图片是否属于同一个人。
神经风格切换:即两张图片的整合,整合图片G的内容借鉴自内容图C,风格借鉴自风格图S。要判断生成图的结果好坏,可以用下图的代价函数J(G)表示:
其中J(G)由内容代价函数Jcontent(C,G)和风格代价函数Jstyle(S,G)组成,且有α+β=1。
内容代价函数Jcontent(C,G)体现了C和G内容的相似程度,所谓内容,可以直接和前文的“相似度”画等号。其定义仍然是欧几里得范数,其中al为输入内容图C时神经网络第l层对应的多维向量,al为输入整合图G时神经网络第l层对应的多维向量。具体地,其定义为:
风格代价函数Jstyle(S,G)体现了S和G风格的相似程度,风格被定义为同一位置不同信道的多维矢量的相关性。(原文:Define style as correlation between activation across channels)
那么相关性是如何决定图片风格的呢?我们可以举个简单的例子看看,假设某个信道可以检测图片中的垂直边缘,另一个信道可以检测图片中橙色的区域,于是这两个信道的相关性决定了这个图片垂直边缘为橙色的可能性,也就是感性理解中的风格。我们可以用两矢量对应元素的乘积来表示相关性。则对于信道数量为nc的输入张量,我们定义nc*nc的风格矩阵Gkk’l为输入风格图S时神经网络第l层对应的信道k和信道k’的相关性,Gkk’l为输入整合图G时神经网络第l层对应的信道k和信道k’的相关性。其具体公式如下:
对应的代价函数为(其中前面为归一化项):
更进一步地,如果想要把每一层的代价函数整合在一起得到一个更加综合风格代价函数,有:
在得到代价函数后,我们只需对G先随机化后进行梯度下降算法就能学习到不错的结果了。
卷积的1D和3D推广:其原理和2D大体一致。
1D:对于n的输入向量和f的过滤器,假设步长为s,则过滤后的输入结果为floor((n-f)/s+1)
此外,如果输入有nc个信道,则过滤器也需要有nc个信道。如果有n个过滤器,则过滤后的输入结果规模为floor((n-f)/s+1)*n。心电图和各类信号是典型的1D数据。
3D:对于nnn的输入张量和fff的过滤器,假设步长为s,则过滤后的输入结果规模为:
(floor((n-f)/s+1),floor((n-f)/s+1),floor((n-f)/s+1))。
此外,如果输入有nc个信道,则过滤器也需要有nc个信道。如果有n个过滤器,则过滤后的输入结果规模为floor((n-f)/s+1)floor((n-f)/s+1)floor((n-f)/s+1))*n。CT图和视频是典型的3D数据。
序列模型(Sequence Model):可以用于训练语音识别,文本处理,机器翻译等问题。序列模型大多为1D模型,即一串时序数据或文本数据。序列模型利用循环神经网络在整个社会的深度学习发展中掀起了极大变革。
我们以一串文本数据为例介绍序列数据及循环神经网络的数学符号表示:
比如说有以下一段文字数据输入:Harry Potter and Hermione Granger invented a new spell.
我们要做的是判断这段文字中可能是人名的单词,即:1 1 0 1 1 0 0 0 0
各单词为一个基本输入单元,记为x
序列x的长度记为Tx,序列y的长度记为Ty,本例中Tx=Ty,但很多时候Tx不等于Ty。
如果有多组输入样本,则需要加上样本序号i,即:x(i)
对于文本这种非数字输入来说,基本思路是要把它先转换成数字输入,为此我们需要引入一张词表(Vocabulary),其中收录了按序排列的大部分常见单词(推荐选取10000个),这样子就能用单词在词表中的序号代表对应单词了,当然序号作为标量不是很适合,我们可以把它转换成之前学过的独热编码的形式,即x(i)
经过上面的讨论我们思考一个问题,为什么序列模型不适用于标准的神经网络?
可以看到,如果序列数据需要被标准神经网络处理,那么就要构建成上述形式。问题在于,序列数据的输入和输出长度并不是固定的,而且标准神经网络不能做到参数共享,而序列数据作为一种模式相似的学习,很多参数在某个位置的学习是可以反映到其他位置从而加快学习进程的。于是引入循环神经网络的架构:
一个标准的多对多单层循环神经网络的架构如下图所示。
可以看到,循环神经网络最大的特点在于它一层就具有标准神经网络多层的神经元,且每个输入仅占其中的一个标准神经网络层,而每个标准神经网络层都会进行一次输出,同时前一个标准神经网络层的激活和输入会被同时传递到后一个标准神经网络层作为激活。可见某个标准神经网络层的激活a
于是循环神经网络的前向传播关系便可以由下式表述:
其中激活函数g1通常采用tanh或ReLU,而g2是输出层函数,通常根据输出的要求选择采用二分类的sigmoid或多分类的softmax等。一般规定a<0>=0。
在实际应用中,通常将式1的Waa和Wax合并写作矩阵形式,即:
a
学习完了前向传播后,接下来看看循环神经网络的反向传播:
循环神经网络的代价函数取决于各输出层对应的损失函数,以判断单词是否为名字的循环神经网络为例,此时的输出显然需要使用二分类的sigmoid函数,故单个输出的损失函数也就是逻辑回归的损失函数,即:
而最终的代价函数就是每个输出层损失函数之和,也就是:
由于循环神经网络的反向传播是t从后往前进行的,故也被称为穿越时间的反向传播(Back Propagation Through Time)。
当然除了上面所说的标准的多对多单层循环神经网络的架构,我们还有一对一单层循环神经网络的架构,一对多单层循环神经网络的架构,多对一单层循环神经网络的架构以及多对多而数量不等的单层循环神经网络的架构,如下图所示:
比如我们输入一段评价文字,要让网络得到这段文字可能代表的评分,这就是一个多对一的网络,RNN需要在所有输入均接收完毕时才能给出一个输出判断;又或者我们输入一个主题,要让网络输出一段相关的文章,就是一个一对多的网络;又或者机器翻译,我们输入一种语言要求网络翻译成另一种语言,就是一个多对多而数量不等的网络,前半段称为编码层Encoder,后半段称为译码层Decoder。
语言模型(Language Model):语言模型的作用是,输入一组文本序列y<1>,y<2>…y
要想训练这样的语言模型RNN,需要找到对应语言的语料库(Corpus)作为训练集,语料库中包含了大量规范的语言句式,通过学习语料库,可以让语言模型具备理解句式和词汇构成的能力,从而对输入的文本序列进行检验。对于语料库中的句子,仍然是先使用词表和独热编码进行预处理,同时句尾可以用结束符
首先我们来看看语言模型RNN是怎么工作的:语言模型RNN的特殊结构是x
语言模型RNN各层的损失函数即softmax的损失函数:
对应的代价函数即各层损失函数之和:
反向传播的训练方法仍然是梯度下降及其变体。
当我们训练好一个语言模型后,如果想要检验它的效果,可以使用新序列采样的方法:
我们运行一遍训练好的模型,根据模型给出的概率进行采样(比如说模型认为第一个单词为the的概率为90%,为a的概率为10%。那么第一个单词就进行90%为the,10%为a的采样)
从第二层开始,每一层都使用上一层的采样结果hat{y
除了可以建立基于词汇的RNN模型,还可以建立基于字符的RNN模型。基于字符的RNN模型的词表由大小写字符和各类标点符号组成,其好处是不会存在
对于较深的循环神经网络,靠后的输出对靠前的输入的捕获能力将会不断减弱,梯度消失的问题仍然会出现。为了让循环神经网络应对这个问题,可以使用GRU单元或LSTM解决,GRU和LSTM可以再循环神经网络中建立起非常深的连接,让我来看看:
GRU(Gated Recurrent Unit)门控循环单元:
引入几个重要概念:
1.记忆单元c
2.相关门Γr:表示c
3.候选值tilde{c
由于c
4.更新门Γu:表示新旧参数的更新程度,也就是将候选值更新为真实值的程度,使用sigmoid函数让其介于[0,1]之间,公式为:
5.候选值tilde{c
可见更新门参数Γu越接近1,当前记忆单元c
综上,GRU单元的前向传播由以下五个公式决定,而其中的W和b均由学习得到:
于是对于给的例子,GRU要做和学习的就是在发现主语the cat时,让c
长短期记忆(LSTM):
记忆细胞c
1.更新门Γu:沿用GRU单元的概念
2.遗忘门Γf:相比于GRU用1-Γu表示对之前记忆参数的遗忘程度,在LSTM中使用专门的遗忘门参数Γf来表示对之前记忆参数的遗忘程度,即:
3.输出门Γo:相比于GRU直接令a
综上,LSTM的前向传播由以下六个公式决定,而其中的W和b均由学习得到:
有时,在学习参数Γu,Γf和Γo时,除了a
比较GRU和LSTM,由于GRU只由两个门组成,因此它在架构上更加简单,运行速度也更快;而LSTM则更加强大也更加灵活,在大型RNN中更加喜好LSTM架构。
双向循环神经网络(Bidirectional RNN/BRNN):
我们仍以判断文字是否为人名的RNN为例,我们看以下两句话:
He said:”Teddy bears are on sale.”
He said:”Teddy Roosevelt was a great president.”
如果使用单向循环神经网络,我们只能依赖之前的单词做出判断,而这两句话的前三个单词完全一致,由此判断Teddy是否为名字显然是不可能的。故我们要使用双向循环神经网络BRNN,使神经网络可以同时依赖前方和后方的单词做出判断。
一个典型的BRNN结构如上图所示,其中每层的激活都包含由左至右和由右至左两个部分。
其中由左至右的激活从a-><1>开始前向传播到a-><4>,而由右至左的激活从a<-<4>开始前向传播到a<-<1>。此时激活的递推公式不变,只是存在了两个方向而已。而同一个输出由于存在两个激活,故递推公式修改为:
利用BRNN,在预测一个输出时,我们既可以参考之前的数据,也可以参考之后的数据。例如求解hat{y<3>}时,过去的数据x<1>和x<2>可以从由左至右的激活通道进入,未来的数据x<4>可以从由右至左的激活通道进入。
在实际应用时,往往使用BRNN和LSTM、GRU的综合结构。BRNN的缺点在于它必须事先知道整条输入信息,这使得其在进行实时语音识别等任务时不是很有效。
深层循环神经网络(Deep RNN):
我们之前学习的RNN都是单层的RNN,可以看到所有的x只经过了一层神经层就得到了y。可见一层RNN在横向上由若干个标准神经网络层组成,而要得到多层的RNN,需要对单层的RNN进行纵向上的延伸。下图就是一个三层的循环神经网络:
可以看到与单层的RNN相比,所有参数都多了描述层级关系的[l]符号,比如说a[2]<3>表示第二层第三时刻的激活,且上一层的输出成为了下一层的激活,而不是直接作为整个神经网络的输出。同时,每个激活也都改为了由前一层的激活和前一时刻的激活共同影响决定,例如:
此时要学习的参数也拓展到了不同层级,例如Wa[2]和ba[2]。
由于时间尺度较长的原因,深层循环神经网络通常无法做到很深,但可以改进的点在于可以将DRNN的若干个输出单独拉出来作为输入再分别构建一个比较深的标准神经网络,这倒是很常见的。此外,DRNN的各单元也可以结合使用BRNN、LSTM、GRU等构架起一个综合的循环神经网络。
之前用词表+独热编码表示一个单词的缺点在于,它把每个单词都孤立起来看了,这使得算法对相关词的泛化能力不够强。例如算法已经学会构成语言模型I wanna a glass of orange juice,如果此时让算法学习I wanna a glass of apple juice,可能仍需要花费一定时间。但事实上,由于如果能够关注算法对相关词的泛化能力,这两句话应该能利用同一个模型快速得到。
为了表征单词之间的关联性,我们引入词嵌入(Word Embedding)向量作为表示某个单词的新方法:
词嵌入向量的每个维度都表示了对应单词的某种特征,例如下图将特征分为了型别、尊贵、年龄和食物等维度,然后得到该单词对应于每个特征维度的相关程度,词嵌入向量就是一个由多维度相关程度组成的多维向量。词嵌入向量的维度远小于独热编码的维度,一般在50-1000之间,记位于词表n位置的独热编码为on,词嵌入向量为en。于是网络判断两单词的相关性就可以参考它们的词嵌入向量,比如Apple和Orange是一对近义词,两者在大部分维度上的值相似;Man和Woman是一对反义词,两者除了性别这一维度相反外。在其他维度上的值相似,然后对相关性高的单词做出类似的学习结果。
需要注意的是,虽然我们这里用一个直观的类别概念去区分词嵌入向量的不同维度,在实际学习过程中,你无法保证词嵌入向量的各维度有明确的意义,学习算法能做的只有保持相关词之间的词嵌入向量也存在某种相关性。
我们来看词嵌入模型的一个应用,就是进行类比推理(Analogy Reasoning):
类比推理是人类思维的一个体现,例如我们给出Man对应King,那么Woman对应什么呢?显然Woman对应的是Queen,那么词嵌入模型怎么学习到这个对应关系呢?
我们可以看到,利用词嵌入向量,我们可以得到eman-ewoman≈eking-equeen≈[-2,0,0,0]。词嵌入模型就是使用词嵌入向量的相似性学习类比推理的,也就是找到一个单词w,使ew和eking-eman+ewoman的相似程度最高,写作数学表达式就是:
其中argmax函数返回数据结构取到最大值时的索引,sim函数是一种度量两个向量之间相似性的函数,在这里我们使用余弦相似度。所谓余弦相似度,就是两个向量之间的余弦夹角,可以知道,当两个向量相同时,余弦相似度=1;当两个向量正交时,余弦相似度=0;当两个向量相反时,余弦相似度=-1,这是一个比较好的判断相似度的性质。
经过这个表达式,我们发现w=Queen,便完成了一次类比推理。如果在词嵌入向量所在的多维空间里观察man和woman,king和queen之间的向量距离,可以发现两者是近似的。
那么我们怎么得到词嵌入向量的这些参数呢?一个方法是训练自己的词嵌入矩阵E,为此,我们需要一个极大量的语料库作为训练集。所谓词嵌入矩阵E,是词表中每个单词的词嵌入向量按序横向排列得到的一个矩阵,而我们训练的目标不是某个单词的词嵌入向量,而是整个词嵌入矩阵E的所有参数,假设词表有10000个单词,每个单词的词嵌入向量为300维,那么词嵌入矩阵E的规模就是30010000。从词嵌入矩阵E中得到某单词的词嵌入向量en也很简单,就是用E乘以对应n的独热编码,即en=Eon,故得到了E也就得到了所有的en。
接下来我们具体看看怎么学习E中的各参数,历史上曾经出现过很多种学习词嵌入的方法:
1.建立一个语言模型是学习词嵌入矩阵的一个好方法。首先随机生成一个词嵌入矩阵E,将语料库作为训练集,根据输入单词的独热编码得到各输入的词嵌入向量,然后把这些词嵌入编码组合成一个组合向量输入神经网络,最后在一个softmax输出层输出预测结果。这个模型要学习的模型参数为E,w[1],b[1],w[2]和b[2],把预测结果和语料库的实际结果的差值作为代价函数,由此训练出的神经网络可以得到词嵌入向量与softmax输出的关系。
这被认为是早期训练词嵌入矩阵比较成功的案例,究其原因在于语料库中存在了大量有关apple juice和orange juice的描述,于是训练好的语言模型对于I want a glass of apple 和I want a glass of orange 都有较大概率预测同一个输出juice,故如果观察包含词嵌入向量的隐藏层,apple和orange对应的词嵌入向量应该也大概率近似才会得到这样的结果。
基于这个模型,我们引入几个概念,我们把要预测的目标单词称为Target,把目标单词附近的其他已知单词称为Context。如果你的目的是得到一个语言模型,那么通常选取Context为Target的前k个单词(k是超参数,若如果不限制Context,则使用这个模型的输入长度将会无法确定,就会碰到之前在RNN中所说的问题了);事实证明,如果你的目的仅仅是学习一个词嵌入矩阵,那么Context可以选取很多其他的模型:
1>在Target前后各取k个单词
2>取Target前的一个单词
3>取Target附近的一个单词,这个方法会在之后重点讲解,被称为Skip-Gram。
2.Skip-Gram模型(Word2Vec):
抽取Context-Target对来构造一个监督学习问题:我们要做的是从语料库的各个句子中随机选取一个词作为Context,然后随机在Context一定词距以内选择另一个词作为Target构成Context-Target对,将Context-Target对作为模型的训练集。于是监督学习的目标就是给定Context,然后要求网络预测在Context一定词距以内的某个Target值。
显然,这并不是一个非常简单的学习问题,因为在Context附近可能存在各种各样的单词,但我们构造这个监督学习问题的目标并不是解决这个监督学习问题本身,而是借助这个问题去得到一个良好的词嵌入模型。
Skip-Gram的网络模型非常简单,输入的是Context的词嵌入向量,经过一层softmax输出Target为各单词的可能性。如下图所示:
此时的softmax单元我们舍弃偏移b,并把w用θ表示,则zt=wtx+bt=θtTec。
于是在输入Context为c时,输出Target为t的概率就是:
对应的损失函数为:
如果能得到一个不错的Skip-Gram模型,我们就可以得到一个不错的词嵌入矩阵E了。因为如果输入apple和orange都能得到输出juice,那么两者对应的词嵌入向量也将是类似的。
如此处理会碰到的一个问题是计算成本,指数相加是很耗费算力的。故因此,我们通常不会直接采用10000级的softmax函数,而是改为采用分级(Hierarchical)结构,也就是所谓的哈夫曼树,把出现频率更高的单词放在高层,每经过一个节点,将总集进行二分,直到找到目标单词,经过如此处理,低效的softmax就变为了多层的逻辑回归,可以加快程序运行速度。
此外,如何随机抽取c也是一个值得探讨的问题,如果按照出现概率进行随机抽取的话,网络可能会大量抽取诸如the,a等高频但无意义多关联的词汇,这会导致网络致力于训练这些不是很有用的词汇,而忽略真正想让网络训练的apple和orange等词汇。
3.负采样(Negative Sampling):
负采样的每组训练数据由一个正样本和k个负样本组成,对于一组训练数据而言,其Context对应的单词是固定的,而Target则只有一个选取自Context附近文本,而其他都由词表中随机选取,于是数据中的的x是选取的Context和Target,y是两者是否真的能成为Context-Target对,显然选取自Context附近的Target对应的y是1,而随机选取的Target对应的y是0。
负采样对应的神经网络如下图所示,输入仍然是Context对应的词嵌入向量,而输出则是10000个逻辑单元,其中每个逻辑单元代表输入为Context时对应的输出为Target的概率。
由于我们每组训练数据只包含一个正样本和k个负样本,故每次反向传播学习参数并不需要更新所有的10000个逻辑单元,而只需要更新这些样本对应单词所代表的k+1个逻辑单元即可。对于小的训练集,一般选取k=5-20;对于大的训练集,一般选取k=2-5。可见每次更新并不会更新大量参数,加快了学习时间。
同样在逻辑回归单元我们舍弃偏移b,并把w用θ表示,则zt=wtx+bt=θtTec。
于是在输入Context为c时,Target为t时,两者能组成Context-Target对的概率就是:
同样的,负采样也会碰到如何在词典中选取负样本的问题,前面提到如果随机选取会出现大量无意义的高频单词,而负采样的发明者提出了以下的经验公式。选取单词wi的概率是wi在语料库中出现的词频f(wi)的3/4方除以所有词表中单词出现词频的3/4方,即:
负采样将skip-gram中的softmax单元拆解成了若干个逻辑单元,且每次只需要更新其中的几个逻辑单元即可,这样的做法大大减轻了算法的计算成本。
4.GloVe(Global Vectors For Word Representation)词向量:
定义Xij为i出现在Context j附近的次数,这里i的地位就等同于Target,于是i和j可以用t和c代替。通过遍历训练集可以得到Xij的取值,如果将附近定义Context的左右若干个单词,则Xij和Xji的取值大致相等。可见Xij表示i和j之间的关联程度。GloVe也有对应的高频惩罚函数f(Xij):当Xij=0时,f(Xij)=0;当Xij过于高频时,限制其权重;当Xij比较低频时,提高其权重。于是其代价函数为:
其中θiT为i的权重向量,ej为j的词嵌入向量,bi和bj分别为i和j的偏移向量,这些就是神经网络的学习参数。GloVe的目标就是,学习θi,ej,bi和bj使得线性函数θiTej+bi-bj与Xij的差距尽可能小,一旦实现了这个目标的话,由于apple juice和orange juice出现的概率差不多,故j=apple或orange,i取juice时的Xij值是相似的,故此时eapple和ejuice的值也应当是相似的。此外,相比于Skip-Gram和负采样来说,GloVe下的θi和ej的地位是相等的,因为i可以变成j,j也可以变成i。所以每个单词w最终的词嵌入向量可以取θw和ew的平均值。
可以看到,GloVe方法的函数非常简单,且效果不错。
说了那么多学习词嵌入矩阵E的方法,可能你会觉得非常难。但幸运的是,网络上已经有人上传了已经经过预训练的大量单词的词嵌入向量。我们也可以直接下载这些模型进行迁移学习,将这些词嵌入向量用在自己的小规模数据集中的每个单词上即可。
seq2seq模型:
seq2seq(序列到序列)模型是机器翻译和语音识别过程中的基础模型,实际上seq2seq模型就是一个多对多而数量不等的单层循环神经网络架构的变体。
它包含了绿色部分的编码网络和紫色部分的译码网络,编码网络将输入向量训练并整合成一组综合向量输入进译码网络,然后译码网络对这个综合向量进行处理得到最终输出。seq2seq模型的特点是译码网络的每个输出hat{y}会作为RNN的下一层输入,这点与语言模型相似,语言模型是随机输出一组语句的概率,而可以把seq2seq模型看作是条件语言模型,即根据限定的输入条件输出一组语句的概率,即P(y<1>,y<2>…y
而P(y<1>,y<2>…y
所以上式也可以写作:
要实现这个效果,我们通常使用集束搜索(Beam Search)。集束搜索是一种近似搜索算法,以机器翻译和规模为10000的词表为例,假设我们要得到一个长度为10的翻译结果,如果要搜索出概率最高即效果最优的结果,需要比较1000010次,这个计算成本是不能接受的。集束搜索的思路是,对译码网络的每层输出概率进行排序,即对P(y
值得注意的是,由于集束搜索是一种近似搜索算法,因此它只能输出相对较优的结果而不保证一定是全局最优,但是低廉的时间成本和足够优秀的输出结果使它脱颖而出。
进一步优化目标函数,将概率的乘积转换为对数的相加:
如此处理可以避免一堆小于1的数相乘导致数据下溢失去精度。但它还存在的一个问题是,这个目标函数可能不自然地倾向于简短的翻译结果,因为短句子的概率是由更少数量的小于1的数字乘积或小于0的对数求和得到的,这样的结果通常比长句子要大。
为此,可以在对数目标函数前加一个简短惩罚(Brevity Penalty/BP),其中α是取值为0-1之间的柔和因子,α越趋近于0,简短惩罚就越没有效果。简短惩罚使短句承受了更低的结果。
接下来我们看看seq2seq模型的误差分析:
seq2seq模型的误差可能同时来源于编译码网络RNN和集束搜索算法,首先编译码网络RNN可能会训练的不够好以至于无法输出不错的结果,而集束搜索算法作为一种近似搜索算法,可能会因为束宽长度的选择无法搜索到比较好的输出。要想区分seq2seq模型的误差究竟是来源于RNN还是集束搜索算法,可以采取以下方法:
我们还是以机器翻译为例,我们让人和seq2seq模型同时翻译一个语句,记人翻译的结果为y,模型翻译的结果为hat{y},显然人翻译的是比较精确的,且模型相比于人翻译的不够好。于是进行误差分析,我们可以直接将y和hat{y}喂给RNN模型,让模型判断P(y|x)和P(hat{y}|x)的好坏。如果P(y|x)>P(hat{y}|x),则RNN模型本身没有问题,因为人翻译的结果确实比模型好,那就可能是集束搜索算法没有找到这个比较好的结果,于是要从集束搜索算法的优化入手;反过来,如果P(y|x)<=P(hat{y}|x),则RNN模型本身就有问题,因为它埋没了一个本应比较好的翻译。于是,当模型出现问题时,我们可以人为列出几句话对应的翻译,并让模型也翻译一遍,根据P(y|x)和P(hat{y}|x)的大小关系判断是RNN的问题还是集束搜索算法的问题,列出表格看看两者谁占主要方面,并着重解决这一主要问题。
Bleu(Bilingual Evaluation Understudy 双语评估替补)得分:
Bleu得分是seq2seq模型中一个比较不错的单一实数评估指标,可以用于评估seq2seq模型的结果。仍以机器翻译模型为例,我们会遇到的一个问题是,由于翻译结果可能同时存在多个正确答案,我们如何从这么多答案中评估模型的结果呢?我们假设机器翻译给出了两个参考结果,这两个参考结果都足够优秀。
当模型的翻译结果hat{y}输出后,我们把整个句子拆分为若干个n个连续的词组,每个词组称为n-grams,则定义pn如下,其中Count是整句n-gram的数量,Countclip是整句n-gram与参考结果中的某个n-gram相同的数量,但每个Countclip(n-gram)不能超过单个参考例句中n-gram的最大值。
例如输出为The cat the cat on the mat,则分母为6,分子为4,p2=2/3,计算过程如下:
Bleu得分中也存在简短惩罚,如果模型MT输出的长度大于参考输出长度,那么就不进行简短惩罚,否则就要进行指数级的简短惩罚。
Bleu得分的公式为:Bleu Score=BPexp(1/n∑pi),一般取n=4,即exp内计算p1-p4的均值。
注意力(Attention)模型:在翻译一个长句时,我们不希望神经网络去具备记忆一个长句子的能力,而是希望它更像人类一样,一次去翻译一个句子的一部分。这就是所谓的注意力,即在翻译某一个词语时,让神经网络只注意到句子的一部分,对应的模型就是注意力模型。
注意力模型在一般双向RNN的基础上,将多个输出hat{y}经过注意力参数整合为一个上下文变量c再次输入一个新的RNN中,新的RNN中每层会接收上个输出的词和经过注意力参数整合的语句向量作为输入,输出在新位置上的词。将新RNN中的激活命名为s
为了表示神经网络在翻译某个词语时对句中其他词语的重视程度(注意力),我们引入注意力参数α
其中e
那么怎么得到e
然后训练整个注意力模型即可。
语音识别:当下语音识别的一个有效模型就是注意力模型,通过输入一段音频来转换为文字,由于音频的输入量(取样总数)往往远大于文字的输出量,所以注意力模型显得尤为重要。
假设输入一段10s,100Hz的采样音频以得到文字”The quick brown fox”,则注意力模型为:
还有一种常见的训练方法是CTC(Connectionist Temporal Classification)损失函数,CTC损失函数对应的训练网络是一个输入和输出数目相等的双向LSTM循环神经网络架构(这里简单用单向RNN表示)。与注意力模型不同,CTC损失函数将每个采样的输入都得到一个输出字母,这意味着有很多字母都会重复,也会有部分采样不存在字母(定义这种情况输出为空blank,不要和空格space混淆),最后得到1000个输出,对于这个输出,将空白符blank之间重复的字符折叠起来作为一个输出,同时不输出blank,以此类推得到最终输出。
触发字检测:可以训练一个RNN,当某人说完触发字之后,就把输出拉高为1一段时间,而其他时候输出始终为零,以此训练一个触发字检测系统。