您的位置:首页 > 理论基础 > 计算机网络

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

2019-03-20 22:31 281 查看

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

  • 实践经验
  • 神经网络训练

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

    计算图

    计算图的概念

    • 计算图是任意数学表达式的一种图表达结构
    • 计算图是一个有向无环图,其中结点对应数学变量或运算,边对应节点间的计算流。
    • 示例:(a∗b+1)∗(a∗b+2)\left( {a * b + 1} \right) * \left( {a * b + 2} \right)(a∗b+1)∗(a∗b+2)
    • 神经网络也可以表示为计算图的形式

    前向计算

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

    for i=1i=1i=1 to NNN do
    令a1,...,am=π−1(i)a_1,...,a_m=\pi^{-1}\left(i\right)a1​,...,am​=π−1(i)
    v(i)←fi(v(a1),⋯ ,v(am))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​))

    反向计算

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

    d(N)←1d\left( N \right) \leftarrow 1d(N)←1 (∂N∂N=1)(\frac{{\partial N}}{{\partial N}} = 1)(∂N∂N​=1)
    for i=N−1i=N-1i=N−1 to 111 do
    d(i)←∑j∈π(i)d(j)⋅∂fj∂id\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)⋅∂i∂fj​​ (∂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}}} })(∂i∂N​=j∈π(i)∑​∂j∂N​∂i∂j​)

    软件实现

    • 在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初始化(针对tanhtanhtanh函数)
      建议权重矩阵WWW以如下公式初始化:
      W∼U[−6din+dout,+6din+dout]W \sim U\left[ { - \frac{{\sqrt 6 }}{{\sqrt {{d_{in}} + {d_{out}}} }}, + \frac{{\sqrt 6 }}{{\sqrt {{d_{in}} + {d_{out}}} }}} \right]W∼U[−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]的一个均值采样。
    • 针对ReLUReLUReLU函数
      从均值为000,方差为2din\sqrt {\frac{2}{{{d_{in}}}}}din​2​​的高斯分布采样进行权重初始化。

    重启与集成

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

    梯度消失与梯度爆炸

    • 梯度消失(非常接近0) 网络变浅
    • 逐步训练
    • batch-normalization方法
    • 使用特定结构帮助梯度流动
  • 梯度爆炸(变得非常高)
      如果范数大于阈值,就剪掉
      g^\hat gg^​表示网络中所有参数的梯度,∥g^∥\left\| {\hat g} \right\|∥g^​∥为其L2L_2L2​范数,如果∥g^∥&gt;threshold\left\| {\hat g} \right\|&gt;threshold∥g^​∥>threshold,则令g^\hat gg^​为threshold∥g^∥\frac{{threshold}}{{\left\| {\hat g} \right\|}}∥g^​∥threshold​。

    饱和神经元与死亡神经元

    • 饱和神经元 特点 造成该层的输出都接近于111
    • 带有tanhtanhtanh和sigmoidsigmoidsigmoid激活函数的网络层往往容易饱和
    • 饱和神经元具有很小的梯度
  • 起因
      由值太大的输入层造成
  • 处理
      更改初始化
    • 缩放输入值范围
    • 改变学习速率
    • 归一激活函数后的饱和值:如使用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中均值为000,方差为111。
  • 死亡神经元
      特点 大部分甚至所有的值都为负值
    • 带有ReLUReLUReLU激活函数的网络不会饱和,但会死掉
    • 该层梯度全为000
  • 起因
      由进入网络的负值引起
  • 处理
      减少学习速率

    随机打乱

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

    学习率

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

    建议使用η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​为第ttt个训练样例的学习率,λ\lambdaλ为超参。

    minibatch

    在每训练111个训练样例(minibatch=1minibatch=1minibatch=1)或kkk个训练样例(minibatch=kminibatch=kminibatch=k)后更新参数。
    大的minibatchminibatchminibatch对训练是有益的。

    参考文献

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

  • 内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
    标签: 
    相关文章推荐