[关闭]
@ShawnNg 2017-01-04T11:23:00.000000Z 字数 8811 阅读 2643

从头学起深度学习应用于自然语言处理-前向神经网络

深度学习 NLP



1 引言

如今深度学习的浪潮袭来,在各个领域上都被应用。虽然这只是机器学习的一部分,但不得不赞同,依靠大数据,深度学习的表现很优秀,因此我们迎来了依靠深度学习的人工智能时代。

这是最好的时代,也是最坏的时代 --《双城记》

越来越多的人开始学习深度学习,我们需要付出更多的努力才能超越别人,加油吧,终究会有人认可我们的努力的。好了,废话就说到这里。这一章主要是通过学习 Stanford cs224d 课程做出的一些总结,在学习过程中阅读相关资料,实现模型,收获甚多。

2 本章介绍

本章首先从数学层面理解前向神经网络的基本表示形式,以及神经网络的训练过程。

3 前提知识

4 前向神经网络(Forward Neural Network)

在介绍神经网络时,有人会说这是一个模拟生物学神经网络的模型,很神奇。不过我认为神经网络是只是一种唬人的说法,它比起一些概率模型更加容易理解。以下是一个简单的三层前向全连接神经网络
图1 三层前向全连接神经网络
图中称为输入层,称为隐藏层,称为输出层。全连接网络中的上一层和下一层的每个神经元都相连。隐藏层接受输入后会进行一个非线性变换,再将输出信号传递给下一层,我们称这个非线性变换为激活函数(activation function)

仅仅看图片并不能让我们很好地理解这个模型,在数学上,我们可以用矩阵乘法(Matrix multiplication)来解释:

接下来文中的向量都是指行向量,这里的sigmoidsoftmax都是一个激活函数,是指两层之间的权重,是偏置量。我们可以看到进行了线性变换后,再做非线性变换,输出的信号将作为的输入。

4.1 激活函数(Activation fuction)

激活函数的作用就是用来做非线性变换,非线性的好处是可以增加模型的复杂度,从而能够模拟更加复杂的决策边界。其缺点是如果模型比较复杂,面对样本数不大的情况时容易过拟合(overfiting)

4.1.1 sigmoid函数与实现

  1. sigmoid函数的数学形式:

  2. sigmoid函数有一个性质:

  3. sigmoid函数的导数:

  4. sigmoid函数和导数的python实现:

  1. # python中一个好用的数值计算模块
  2. import numpy
  3. # 输出函数值
  4. def sigmoid(x):
  5. x = 1. / (1+np.exp(-x))
  6. return x
  7. # 输出导数,输入的f是函数值
  8. def sigmoid_grad(f):
  9. f = f * (1-f)
  10. return f

4.1.2 softmax函数与实现

  1. softmax函数的数学形式

    假设

  2. softmax函数求导:

    • 求导:
    • 求导:
  3. softmax函数的实现:

  1. import numpy
  2. # 输出softmax函数值,输入向量或矩阵x
  3. def softmax(x):
  4. if len(x.shape)>1:
  5. max = np.max(x,axis=1)[:,np.newaxis]
  6. x -= max
  7. x = np.exp(x)
  8. softmax_deno = np.sum(x, axis=1)[:,np.newaxis]
  9. x = x/softmax_deno
  10. else:
  11. max = np.max(x)
  12. x -= max
  13. x = np.exp(x)
  14. softmax_deno = np.sum(x)
  15. x = x/softmax_deno
  16. return x

4.2 目标函数(Objective fuction)

目标函数有时候又可以称为损失函数(loss fuction)代价函数(cost fuction),无论如何,我们训练模型的最终目标就是最小化或者最大化目标函数,用表示目标函数。

4.2.1 交叉熵(Cross entropy)

一种常见的目标函数形式,交叉熵

其中代表相应的类别,是训练样本中的标签,而 是模型预测结果。
一般使用one-hot编码[1],而代表了对应类别的概率,因此,我们称 为预测函数。

4.3 预测函数(Predict fuction)

预测函数是整个模型的最终输出结果,我们取最大的作为最终预测的类别。我们使用softmax函数对输出层的输入进行归一化,如下:

其中是类别个数,输出层的神经元个数即是类别个数。是输出层的输入,我们可以将softmax函数看做是输出层的激活函数。而我们常用的激活函数还有sigmoid函数tanh函数RelU函数等等。

假设是输出层的输入,从softmax函数的求导可知,预测函数求导如下:

所以根据链式法则,目标函数求导如下:


因此目标函数对向量求导为:

4.4 前向传播(Forward propagation)

前向传播是求目标函数值的过程,从输入层开始,样本的特征向量遍历模型,到达输出层,再将 进行比较,得到目标函数值,这里目标函数使用交叉熵:

我们训练模型的过程就是一个优化目标函数的过程,在这里我们需要最小化,这个优化过程可以使用梯度下降法,但是由于是一个非凸函数,因此不能使用梯度下降法求得全局最优,也就是不能获得J的最小值。

4.5 随机梯度下降(Stochastic gradient descent)

神经网络的目标函数是一个非凸函数,因此不能用简单的凸优化方法来优化目标函数。幸运的是,我们可以求得目标函数的梯度,负梯度是目标函数值每次下降最快方向,所以我们可以用迭代的方法来更新参数,使得目标函数往着最优的方向进行优化:

上式中代表着第次迭代更新,是学习率,代表着每一次迭代要走的步长,代表着我们需要更新的参数,比如对进行更新,可以看出每次迭代都要进行梯度的计算,而迭代的停止是根据目标函数值来判断的,所以每次迭代我们还要计算目标函数值。

虽然梯度下降看似简单,但是这种方法存在一些问题,假设训练样本数量为,如果每次迭代都使用所有的训练样本。这时的目标函数是:

很大的时候,每次迭代都十分耗时,因此收敛的速度会较慢。我们可以使用一种逼近方法,称为mini-batch

4.6 后向传播(Backward propagation)

后向传播是求梯度的过程,从输出层开始往输入层传递误差,使用链式法则可以求得每一个变量的梯度,求得的梯度可以用于梯度下降。
我们将三层模型得前向传播表示为:

后向传播我们可以得到:

可以看到就是我们的预测误差,这就是我们在后向传播的定义中往输入层方向传递的误差。上式中的代表两个矩阵的元素相乘。

得到了上面传播的误差后,我们可以对每一层的参数求梯度:

当我们对多个样本进行mini-batch时,我们的目标函数变为:。因此我们假设各个变量的矩阵维数为,然后进行后向传播。虽然这时的求梯度更为复杂,但是目标函数是一个标量值(意思就是一个数),标量对矩阵或者向量求导时是有技巧的:因为标量对矩阵求导所得的矩阵大小还是,我们可以根据这个来得到求导结果的矩阵表示方式。

4.7 前向传播和后向传播的实现

  1. import numpy as np
  2. # 将实现的激活函数倒入
  3. import sigmoid,sigmoid_grad
  4. import softmax
  5. def forward_backward_prop(data, labels, params, dimensions):
  6. # 这是上述三层神经网络的前向后向传播实现
  7. # data是训练样本的特征向量组成的矩阵,维度是(N,Dx),N是样本数,Dx是样本特征维数
  8. # labels是训练样本的标签,维度是(N,Dy),Dy是类别总数
  9. # params是参数的集合
  10. # dimensions是网络每层的维度列表
  11. # 将参数集合分解
  12. ofs = 0
  13. Dx, H, Dy = (dimensions[0], dimensions[1], dimensions[2])
  14. W1 = np.reshape(params[ofs:ofs + Dx * H], (Dx, H))
  15. ofs += Dx * H
  16. b1 = np.reshape(params[ofs:ofs + H], (1, H))
  17. ofs += H
  18. W2 = np.reshape(params[ofs:ofs + H * Dy], (H, Dy))
  19. ofs += H * Dy
  20. b2 = np.reshape(params[ofs:ofs + Dy], (1, Dy))
  21. # 前向传播求cost,cost就是目标函数值
  22. X = data
  23. Y = labels
  24. H = sigmoid(X.dot(W1)+b1)
  25. A = softmax(H.dot(W2)+b2)
  26. cost = -np.sum(Y*np.log(A))
  27. # 后向传播求各个参数的梯度
  28. e1 = A-Y
  29. e2 = e1.dot(W2.T) * sigmoid_grad(H)
  30. gradW2 = H.T.dot(e1)
  31. gradb2 = np.sum(e1, axis=0)
  32. gradW1 = X.T.dot(e2)
  33. gradb1 = np.sum(e2, axis=0)
  34. # 合并梯度
  35. grad = np.concatenate((gradW1.flatten(), gradb1.flatten(),
  36. gradW2.flatten(), gradb2.flatten()))
  37. return cost, grad

4.8 梯度下降的实现

  1. def sgd(f, x0, step, iterations):
  2. # Inputs:
  3. # - f: 计算目标方程值和梯度的函数,可以将forward_backward_prop进行封装后作为改值的输入
  4. # - x0: 需要迭代的参数的初始值
  5. # - step: 学习率
  6. # - iterations: 迭代次数,停止迭代的标志
  7. # Output:
  8. # - x: 输出最后一次迭代的参数值
  9. # 学习率衰减的迭代次数
  10. ANNEAL_EVERY = 20000
  11. expcost = None
  12. start_iter = 0
  13. for iter in xrange(start_iter + 1, iterations + 1):
  14. cost,grad = f(x)
  15. x -= step*grad
  16. if iter % ANNEAL_EVERY == 0:
  17. step *= 0.5
  18. return x

[1] 只有一个位是1,其他是0
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注