基于深度学习的自然语言处理——神经网络训练

基于深度学习的自然语言处理——神经网络训练

  • 神经网络训练
    • 计算图
        • 计算图的概念
        • 前向计算
        • 反向计算
        • 软件实现
        • 实现流程
        • 网络构成
    • 实践经验
        • 优化算法选择
        • 初始化
        • 重启与集成
        • 梯度消失与梯度爆炸
        • 饱和神经元与死亡神经元
        • 随机打乱
        • 学习率
        • minibatch
            • 参考文献

神经网络训练

神经网络也是可微分的参数化函数,常用的训练方法就是基于梯度的优化方法。

计算图

计算图的概念

  • 计算图是任意数学表达式的一种图表达结构
  • 计算图是一个有向无环图,其中结点对应数学变量或运算,边对应节点间的计算流。
  • 示例: ( a ∗ b + 1 ) ∗ ( a ∗ b + 2 ) \left( {a * b + 1} \right) * \left( {a * b + 2} \right) (ab+1)(ab+2)
    基于深度学习的自然语言处理——神经网络训练_第1张图片
  • 神经网络也可以表示为计算图的形式
    基于深度学习的自然语言处理——神经网络训练_第2张图片

前向计算

前向计算就是计算图中每个节点的输出,
假设 f i f_i fi为结点 i i i的计算函数, π ( i ) \pi\left(i\right) π(i)为结点 i i i的父节点, π − 1 ( i ) \pi^{-1}\left(i\right) π1(i)为结点 i i i的子节点, v i ( i ) v_i\left(i\right) vi(i)为结点 i i i的输出,则前向计算可以表示为:


for i = 1 i=1 i=1 to N N N do
a 1 , . . . , a m = π − 1 ( i ) a_1,...,a_m=\pi^{-1}\left(i\right) a1,...,am=π1(i)
v ( i ) ← f i ( v ( a 1 ) , ⋯   , v ( a m ) ) v\left( i \right) \leftarrow {f_i}\left( {v\left( {{a_1}} \right), \cdots ,v\left( {{a_m}} \right)} \right) v(i)fi(v(a1),,v(am))


反向计算

反向传播过程开始于损失结点 N N N,向前传播,指定 d ( i ) d\left(i\right) d(i) ∂ N ∂ i \frac{{\partial N}}{{\partial i}} iN,可以表示为:


d ( N ) ← 1 d\left( N \right) \leftarrow 1 d(N)1 ( ∂ N ∂ N = 1 ) (\frac{{\partial N}}{{\partial N}} = 1) (NN=1)
for i = N − 1 i=N-1 i=N1 to 1 1 1 do
d ( i ) ← ∑ j ∈ π ( i ) d ( j ) ⋅ ∂ f j ∂ i d\left( i \right) \leftarrow \sum\nolimits_{j \in \pi \left( i \right)} {d\left( j \right) \cdot \frac{{\partial {f_j}}}{{\partial i}}} d(i)jπ(i)d(j)ifj ( ∂ N ∂ i = ∑ j ∈ π ( i ) ∂ N ∂ j ∂ j ∂ i ) ( {\frac{{\partial N}}{{\partial i}} = \sum\limits_{j \in \pi \left( i \right)} {\frac{{\partial N}}{{\partial j}}\frac{{\partial j}}{{\partial i}}} }) (iN=jπ(i)jNij)


软件实现

  • 在Python中使用DyNet架构创建图
import dynet as dy

#模型初始化
model=dy.Model()
mW1=model.add_parameters((20,150))   #向模型添加权重参数
mb1=model.add_parameters(20)
mW2=model.add_parameters((17,20))
mb2=model.add_parameters(17)
lookup=model.add_lookup_parameters((100,50))   #向模型添加查找参数
trainer=dy.SimpleSGDTrainer(model)   #定义训练器

def get_index(x):
    pass 
#将词映射为索引值

#构建图结构并执行
#更新模型参数
#只显示一个数据点,实践中应该运行一个数据填充循环

#建立计算图
dy.renew_cg()  #创建一个新图
#将模型参数创建
W1=dy.parameter(mW1)
b1=dy.parameter(mb1)
W2=dy.parameter(mW2)
b2=dy.parameter(mb2)

#生成embeddings层
vthe=dy.lookup[get_index("the")]
vblack=dy.lookup[get_index("black")]
vdog=dy.lookup[get_index("dog")]

#将叶子结点连接成完整的图
x=dy.concatenate([vthe,vblack,vdog])
output=dy.softmax(W2*(dy.tanh(W1*x+b1))+b2)
loss=-dy.log(dy.pick(output,5))
loss_value=loss.forward()
loss.backward()   #计算参数并存储
trainer.update()   #通过梯度进行参数更新
  • 在Python中通过Tensorflow实现
#TensorFlow

import tensorflow as tf

W1=tf.get_variable("W1",[20,150])
b1=tf.get_variable("b1",[20])
W2=tf.get_variable("W2",[17,20])
b2=tf.get_variable("b2",[17])

def get_index(x):
    pass

p1=tf.placeholder(tf.int32,[])
p2=tf.placeholder(tf.int32,[])
p3=tf.placeholder(tf.int32,[])
target=tf.placeholder(tf.int32,[])

v_w1=tf.nn.embedding_lookup(lookup,p1)
v_w2=tf.nn.embedding_lookup(lookup,p2)
v_w3=tf.nn.embedding_lookup(lookup,p3)

x=tf.concat([v_w1,v_w2.v_w3],0)
output=tf.nn.softmax(tf.einsum("ij,j->i",W2,tf.tanh(tf.einsum("ij,j->i",W1,x)+b1))+b2)
loss=-tf.log(output[target])
trainer=tf.train.GradientDescentOptimizer(0.1).minimize(losss)

#完成图的初始化工作,编译并赋予具体数据
#只显示一个数据点,实践中我们将使用一个数据输入环
with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    feed_dict={
        p1:get_index("the"),
        p2:get_index("black"),
        p3:get_index("dog"),
        
        target:5
    }
    loss_value=sess.run(loss,feed_dict)
    sess.run(trainer,feed_dict)
  • 两者的区别
    • DyNet使用动态图结构,为每个训练样本创建不同的计算图进行前传和反传
    • TensorFlow使用静态图结构,每一个训练样本都输入到同一张图中。

实现流程

具有计算图概念的神经网络训练

for iteration=1 to T do
     for 数据集中训练样本(x_i,y_i) do
     	loss_node<-build_computation_graph(x_i,y_i,parameters)   #用户自定义函数,给定输入、输出和网络结构可自动生成计算图
     	loss_node.forward()
     	gradients<-loss_node().backward()
     	parameters<-update_parameters(parameters,gradients)  #优化器特定更新规则
return parameters     

网络构成

实践经验

优化算法选择

虽然SGD算法效果很好,但是收敛速度慢,在训练大型网络时Adam算法非常有效。

初始化

  • xavier初始化(针对 t a n h tanh tanh函数)
    建议权重矩阵 W W W以如下公式初始化:
    W ∼ U [ − 6 d i n + d o u t , + 6 d i n + d o u t ] W \sim U\left[ { - \frac{{\sqrt 6 }}{{\sqrt {{d_{in}} + {d_{out}}} }}, + \frac{{\sqrt 6 }}{{\sqrt {{d_{in}} + {d_{out}}} }}} \right] WU[din+dout 6 ,+din+dout 6 ]
    其中 U [ a , b ] U\left[ {a,b} \right] U[a,b]是范围 [ a , b ] \left[ {a,b} \right] [a,b]的一个均值采样。
  • 针对 R e L U ReLU ReLU函数
    从均值为 0 0 0,方差为 2 d i n \sqrt {\frac{2}{{{d_{in}}}}} din2 的高斯分布采样进行权重初始化。

重启与集成

  • 随即重启:多次进行训练过程,每次都进行随机初始化,并选择最好的一个。
  • 模式集成:一旦有了多个模型,可以根据模型的集成进行预测。

梯度消失与梯度爆炸

  • 梯度消失(非常接近0)
    • 网络变浅
    • 逐步训练
    • batch-normalization方法
    • 使用特定结构帮助梯度流动
  • 梯度爆炸(变得非常高)
    • 如果范数大于阈值,就剪掉
      g ^ \hat g g^表示网络中所有参数的梯度, ∥ g ^ ∥ \left\| {\hat g} \right\| g^为其 L 2 L_2 L2范数,如果 ∥ g ^ ∥ > t h r e s h o l d \left\| {\hat g} \right\|>threshold g^>threshold,则令 g ^ \hat g g^ t h r e s h o l d ∥ g ^ ∥ \frac{{threshold}}{{\left\| {\hat g} \right\|}} g^threshold

饱和神经元与死亡神经元

  • 饱和神经元
    • 特点
      • 造成该层的输出都接近于 1 1 1
      • 带有 t a n h tanh tanh s i g m o i d sigmoid sigmoid激活函数的网络层往往容易饱和
      • 饱和神经元具有很小的梯度
    • 起因
      • 由值太大的输入层造成
    • 处理
      • 更改初始化
      • 缩放输入值范围
      • 改变学习速率
      • 归一激活函数后的饱和值:如使用 g ( h ) = tanh ⁡ ( h ) ∥ tanh ⁡ ( h ) ∥ g\left( h \right) = \frac{{\tanh \left( h \right)}}{{\left\| {\tanh \left( h \right)} \right\|}} g(h)=tanh(h)tanh(h)
      • batch normalization:对每一层激活函数后的值均进行归一化,每个mini-batch中均值为 0 0 0,方差为 1 1 1
  • 死亡神经元
    • 特点
      • 大部分甚至所有的值都为负值
      • 带有 R e L U ReLU ReLU激活函数的网络不会饱和,但会死掉
      • 该层梯度全为 0 0 0
    • 起因
      • 由进入网络的负值引起
    • 处理
      • 减少学习速率

随机打乱

网络读入训练样本的顺序是很重要的。

学习率

实验应该从 [ 0 , 1 ] \left[ {0,1} \right] [0,1]内尝试初始学习率,观察网络 l o s s loss loss值,一旦 l o s s loss loss值停止改进则降低学习率。

建议使用 η t = η 0 ( 1 + η 0 λ t ) − 1 {\eta _t} = {\eta _0}{\left( {1 + {\eta _0}\lambda t} \right)^{ - 1}} ηt=η0(1+η0λt)1作为学习率的表达式, η 0 \eta_0 η0为初始学习率, η t \eta_t ηt为第 t t t个训练样例的学习率, λ \lambda λ为超参。

minibatch

在每训练 1 1 1个训练样例( m i n i b a t c h = 1 minibatch=1 minibatch=1)或 k k k个训练样例( m i n i b a t c h = k minibatch=k minibatch=k)后更新参数。
大的 m i n i b a t c h minibatch minibatch对训练是有益的。

参考文献

《基于深度学习的自然语言处理》

你可能感兴趣的:(自然语言处理)