@ShawnNg
2017-01-04T19:23:00.000000Z
字数 8811
阅读 2722
深度学习
NLP
如今深度学习的浪潮袭来,在各个领域上都被应用。虽然这只是机器学习的一部分,但不得不赞同,依靠大数据,深度学习的表现很优秀,因此我们迎来了依靠深度学习的人工智能时代。
这是最好的时代,也是最坏的时代 --《双城记》
越来越多的人开始学习深度学习,我们需要付出更多的努力才能超越别人,加油吧,终究会有人认可我们的努力的。好了,废话就说到这里。这一章主要是通过学习 Stanford cs224d 课程做出的一些总结,在学习过程中阅读相关资料,实现模型,收获甚多。
本章首先从数学层面理解前向神经网络的基本表示形式,以及神经网络的训练过程。
在介绍神经网络时,有人会说这是一个模拟生物学神经网络的模型,很神奇。不过我认为神经网络是只是一种唬人的说法,它比起一些概率模型更加容易理解。以下是一个简单的三层前向全连接神经网络:
图中称为输入层,称为隐藏层,称为输出层。全连接网络中的上一层和下一层的每个神经元都相连。隐藏层接受输入后会进行一个非线性变换,再将输出信号传递给下一层,我们称这个非线性变换为激活函数(activation function)。
仅仅看图片并不能让我们很好地理解这个模型,在数学上,我们可以用矩阵乘法(Matrix multiplication)来解释:
接下来文中的向量都是指行向量,这里的sigmoid和softmax都是一个激活函数,是指两层之间的权重,是偏置量。我们可以看到进行了线性变换后,再做非线性变换,输出的信号将作为的输入。
激活函数的作用就是用来做非线性变换,非线性的好处是可以增加模型的复杂度,从而能够模拟更加复杂的决策边界。其缺点是如果模型比较复杂,面对样本数不大的情况时容易过拟合(overfiting)。
sigmoid函数的数学形式:
sigmoid函数有一个性质:
sigmoid函数的导数:
sigmoid函数和导数的python实现:
# python中一个好用的数值计算模块
import numpy
# 输出函数值
def sigmoid(x):
x = 1. / (1+np.exp(-x))
return x
# 输出导数,输入的f是函数值
def sigmoid_grad(f):
f = f * (1-f)
return f
softmax函数的数学形式
softmax函数求导:
softmax函数的实现:
import numpy
# 输出softmax函数值,输入向量或矩阵x
def softmax(x):
if len(x.shape)>1:
max = np.max(x,axis=1)[:,np.newaxis]
x -= max
x = np.exp(x)
softmax_deno = np.sum(x, axis=1)[:,np.newaxis]
x = x/softmax_deno
else:
max = np.max(x)
x -= max
x = np.exp(x)
softmax_deno = np.sum(x)
x = x/softmax_deno
return x
目标函数有时候又可以称为损失函数(loss fuction),代价函数(cost fuction),无论如何,我们训练模型的最终目标就是最小化或者最大化目标函数,用表示目标函数。
一种常见的目标函数形式,交叉熵:
其中代表相应的类别,是训练样本中的标签,而 是模型预测结果。
一般使用one-hot编码[1],而代表了对应类别的概率,因此,我们称 为预测函数。
对求导:
因此对向量求导:
预测函数是整个模型的最终输出结果,我们取最大的作为最终预测的类别。我们使用softmax函数对输出层的输入进行归一化,如下:
假设是输出层的输入,从softmax函数的求导可知,预测函数对求导如下:
所以根据链式法则,目标函数对求导如下:
前向传播是求目标函数值的过程,从输入层开始,样本的特征向量遍历模型,到达输出层,再将 和 进行比较,得到目标函数值,这里目标函数使用交叉熵:
我们训练模型的过程就是一个优化目标函数的过程,在这里我们需要最小化,这个优化过程可以使用梯度下降法,但是由于是一个非凸函数,因此不能使用梯度下降法求得全局最优,也就是不能获得J的最小值。
神经网络的目标函数是一个非凸函数,因此不能用简单的凸优化方法来优化目标函数。幸运的是,我们可以求得目标函数的梯度,负梯度是目标函数值每次下降最快方向,所以我们可以用迭代的方法来更新参数,使得目标函数往着最优的方向进行优化:
上式中代表着第次迭代更新,是学习率,代表着每一次迭代要走的步长,代表着我们需要更新的参数,比如对进行更新,可以看出每次迭代都要进行梯度的计算,而迭代的停止是根据目标函数值来判断的,所以每次迭代我们还要计算目标函数值。
虽然梯度下降看似简单,但是这种方法存在一些问题,假设训练样本数量为,如果每次迭代都使用所有的训练样本。这时的目标函数是:
后向传播是求梯度的过程,从输出层开始往输入层传递误差,使用链式法则可以求得每一个变量的梯度,求得的梯度可以用于梯度下降。
我们将三层模型得前向传播表示为:
后向传播我们可以得到:
可以看到就是我们的预测误差,这就是我们在后向传播的定义中往输入层方向传递的误差。上式中的代表两个矩阵的元素相乘。
得到了上面传播的误差后,我们可以对每一层的参数求梯度:
当我们对多个样本进行mini-batch时,我们的目标函数变为:。因此我们假设各个变量的矩阵维数为,然后进行后向传播。虽然这时的求梯度更为复杂,但是目标函数是一个标量值(意思就是一个数),标量对矩阵或者向量求导时是有技巧的:因为标量对矩阵求导所得的矩阵大小还是,我们可以根据这个来得到求导结果的矩阵表示方式。
import numpy as np
# 将实现的激活函数倒入
import sigmoid,sigmoid_grad
import softmax
def forward_backward_prop(data, labels, params, dimensions):
# 这是上述三层神经网络的前向后向传播实现
# data是训练样本的特征向量组成的矩阵,维度是(N,Dx),N是样本数,Dx是样本特征维数
# labels是训练样本的标签,维度是(N,Dy),Dy是类别总数
# params是参数的集合
# dimensions是网络每层的维度列表
# 将参数集合分解
ofs = 0
Dx, H, Dy = (dimensions[0], dimensions[1], dimensions[2])
W1 = np.reshape(params[ofs:ofs + Dx * H], (Dx, H))
ofs += Dx * H
b1 = np.reshape(params[ofs:ofs + H], (1, H))
ofs += H
W2 = np.reshape(params[ofs:ofs + H * Dy], (H, Dy))
ofs += H * Dy
b2 = np.reshape(params[ofs:ofs + Dy], (1, Dy))
# 前向传播求cost,cost就是目标函数值
X = data
Y = labels
H = sigmoid(X.dot(W1)+b1)
A = softmax(H.dot(W2)+b2)
cost = -np.sum(Y*np.log(A))
# 后向传播求各个参数的梯度
e1 = A-Y
e2 = e1.dot(W2.T) * sigmoid_grad(H)
gradW2 = H.T.dot(e1)
gradb2 = np.sum(e1, axis=0)
gradW1 = X.T.dot(e2)
gradb1 = np.sum(e2, axis=0)
# 合并梯度
grad = np.concatenate((gradW1.flatten(), gradb1.flatten(),
gradW2.flatten(), gradb2.flatten()))
return cost, grad
def sgd(f, x0, step, iterations):
# Inputs:
# - f: 计算目标方程值和梯度的函数,可以将forward_backward_prop进行封装后作为改值的输入
# - x0: 需要迭代的参数的初始值
# - step: 学习率
# - iterations: 迭代次数,停止迭代的标志
# Output:
# - x: 输出最后一次迭代的参数值
# 学习率衰减的迭代次数
ANNEAL_EVERY = 20000
expcost = None
start_iter = 0
for iter in xrange(start_iter + 1, iterations + 1):
cost,grad = f(x)
x -= step*grad
if iter % ANNEAL_EVERY == 0:
step *= 0.5
return x