首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

CS224n笔记——NLP与神经网络

基本的神经网络结构略去不谈,让我们从一个NLP实例开始。

设想一个判断上述语句中心词'Paris'是否是命名实体的任务。在此任务中,我们不仅要捕获词向量表示的窗口中单词的存在,而且想要获取单词之间的相互影响。例如,可能"只有在'in'是第二个单词的情况下第一个单词会是'museums' "这一点会有用。这样的非线性决策常常不能由输入直接传给softmax函数来解决,而需要一个中间层的处理。随后我们还可以使用另一个矩阵U来生成非标准化的分数,也就是:

其中F为激活函数。

如果我们使用4维的词向量,窗口大小为5维,一层8个Units,那么一层的参数维度:

我们使用最大间隔作为优化目标,也就是让正确的样本的分数比错误的样本的分数尽可能高。设正例分数为S,负例分数为Sc,那么目标函数就要最大化(S-Sc)或者最小化(Sc-S)。不过,这里我们只关注Sc>S的部分,也就是分类错误的部分,得到优化目标:

不过,我们这个优化目标不太“安全”,为了达到比较好的泛化能力,我们不仅要让正例分数比负例高,还需要在正负例之间设定一个间隔,假定这个间隔为1(感觉就是SVM里面的思路):

然后就是利用反向传播算法去更新参数了,老生常谈,略去。

让我们来看看官方笔记中给出的一些神经网络的技巧。

Gradient Check

梯度检查是一种数值逼近的梯度计算技术,虽然计算效率低下以至于不能直接用于神经网络的训练,但是它可以让我们对导数的正确性做有用而完整的检查。

centered difference formula:

e是一个很小的数,常常只有10^-5数量级,为在theta上添加一个微小的干扰项e后的输出,为添加干扰项-e后的输出。利用此公式可估计某个参数状态下的梯度。在实际应用中,这个公式比利用导数公式进行估计更准确:

直觉上来说,考虑点前后两方面的变化量自然要比只考虑前向变化要来的精确些。

根据泰勒定理,这个梯度估计的误差为e^2数量级,通常已经足够精确。

不过,由于利用此公式计算每次要进行两次前向传递,加上很多神经网络的参数非常多,所以计算效率很低,只能用来做检查。官方笔记中的梯度检查代码:

def eval _ numerical _ gradient(f, x):

"""

a naive implementation of numerical gradient of f at x

- f should be a function that takes a single argument

- x is the point (numpy array) to evaluate the gradient

at

"""

fx = f(x) # evaluate function value at original point

grad = np.zeros(x.shape)

h = 0.00001

# iterate over all indexes in x

it = np.nditer(x, flags=[’multi _ index’],

op _ flags=[’readwrite’])

while not it.finished:

# evaluate function at x+h

ix = it.multi _ index

old _ value = x[ix]

x[ix] = old _ value + h # increment by h

fxh _ left = f(x) # evaluate f(x + h)

x[ix] = old _ value - h # decrement by h

fxh _ right = f(x) # evaluate f(x - h)

x[ix] = old _ value # restore to previous value(very important!)

# compute the partial derivative

grad[ix] = (fxh _ left - fxh _ right) / (2 * h) # the slope

it.iternext() # step to next dimension

return grad

正则化:

为解决过拟合,也就是高方差问题,常常会在损失函数中添加正则化项:

官方笔记中说我们都用L2正则化,因为L1正则化容易得到稀疏解。。。。嗯?能得到稀疏解不是优势么。。

总之L1正则化倾向于选择较少较大的参数,L2倾向于选择较多较小的参数。还有就是L2更利于凸优化。

应用中主要问题i是正则化权重的选取,太大容易欠拟合,太小还是会过拟合。

Dropout

在一次迭代中随机选择一部分神经元进行计算,可以学到更多有意义的信息,防止过拟合,改善性能。直觉上的原因就是每次迭代只更新了一个小的神经网络,这样参数能够捕捉到更大更有效的变化趋势,最后将这些小神经网络的预测结果进行平均。

官方笔记中提了一个问题,为了使训练过程和测试过程的输出一致,需要在测试阶段的每个神经元上乘一个因数,那么这个因数是什么?

假设丢弃率为p,每次迭代中只有(1-p)的神经元在工作,这(1-p)的神经元的输出期望要与标注相适应,也就是说,每个神经元的输出实际上是正常情况下输出的(1/(1-p))倍,那么测试时只要乘(1-p)就好了。

激活函数:

官方笔记中并没有说各种激活函数的优劣,只是介绍了一下各个函数长什么样。

首先是之前一直在用的sigmoid函数:

这函数由于算梯度方便且易于理解,所以一直以来都用这个入门,但是导致了一个很严重的问题——梯度消失,直觉理解就是这个函数在[-1,1]之外的增长率太低了,导致多层神经网络中梯度传到前面几层时就太小了,根本train不动。

Tanh函数:

sigmoid函数的变体,拓展了值域。

还有一些变体,要么是为了计算更快,要么是为了增大Z比较大时的导数,只到ReLU的出现,用之前的函数的情况就比较少了。

ReLU:

现在最流行的激活函数,计算快,Z很大时梯度保持地很好。还有一种变体:

这种变体就是为了使Z为负时也能有一定的梯度。

数据预处理:

均值移除:

给定一个输出数据集X,首先通过减去平均值使X变为以0为中心的分布。这种方法的优点在于一般只在训练集中计算平均值,而训练之间做0分布利用了全部数据的信息。

Normalization:

将所有输出映射到相同的范围中去,使得输入数据得以平等化。其实之前用softmax计算概率也是一中normalization的思想。

Whitening;

是特征变得不相关且有值为1的方差。具体方法就是先做均值移除再做奇异值分解。

参数初始化:

一般神经网络中的参数初始化就是选取0附近的比较小的随机数,这种方法一般也工作的比较好。但是,有人提出了一种初始化方案:

n(l)即为fan-in,n(l+1)即为fan-out。这种初始化方式在反向传播过程中有保持激活梯度的作用,如果要使用sigmoid类函数时有用。

学习率:

就是

中的梯度权重。

学习率高,每次迭代的更新幅度大,但是有可能出现震荡或者错过最优解的情况。学习率小,虽然通常能达到更好的性能,但是很容易陷入局部最小值,且会受到计算资源的限制。为此,有人提出了一些动态更新学习率的方法,例如:

当迭代次数超过时,学习率就会越来越小。

惯性更新:

带惯性的参数更新策略,目的主要是能越过一些局部极小值尽量找到全局最优解,实现策略也简单:

v = mu * v - alpha * grad _ x

x += v

Adaptive Optimization Methods

自适应优化,基本思想是根据参数变化的情况来选择学习率,每个参数的学习率取决于该参数的梯度更新的历史,使得具有稀少更新历史的参数使用更大的学习速率更快地更新。 换句话说,过去未被更新的参数现在可能具有更高的学习速率。然后再看一下现在很流行的Adam:

m = beta1 * m + (1-beta1) * dx

v = beta2 * v + (1-beta2) * (dx ** 2)

x += - learning _ rate * m / (np.sqrt(v) + eps)

Adam不仅用了自适应优化策略,而且还融入了惯性更新的思想。

理论上来说Adam看起来很美好,但是Adam也有自身的问题:

可能不收敛——由于参数是时间窗口内的累计,学习率的变化并不是单调的,遇到一些波动较大的数据时,学习后期也可能会发生震荡。

可能错过全局最优解——Adam的最大优势也是它的劣势,那就是收敛非常快,所以收敛过程中理所当然地可能会错过最优解。

目前,有很多人会使用Adam做初期,SGD做后期的方法。

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20180125G0EKVH00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券