深度学习d3:过拟合、欠拟合及其解决方案;梯度消失、梯度爆炸;循环神经网络进阶

欠拟合和过拟合

  • 训练误差和泛化误差

    训练误差 :训练集上的误差
    泛化误差 :测试集上的误差
    训练误差的期望小于或等于泛化误差,因为一直在训练训练集。训练误差的降低不等于泛化误差的降低。但机器学习的真正目的是降低泛化误差

  • 模型选择

    验证集 :可以预留一部分在训练数据集和测试数据集以外的数据来进行模型选择。如从给定的训练集中选一些做验证集。
    K折交叉验证
    节省数据。把原始训练数据集分割成 𝐾个不重合的子数据集,然后我们做 𝐾次模型训练和验证。每一次,我们使用一个子数据集验证模型,并使用其他 𝐾−1个子数据集来训练模型。在这 𝐾
    次训练和验证中,每次用来验证模型的子数据集都不同。最后,我们对这 𝐾 次训练误差和验证误差分别求平均。

    #返回第i折时所需要的训练和验证数据
    def get_k_fold_data(k, i, X, y):
    assert k > 1
    fold_size = X.shape[0] // k
    X_train, y_train = None, None
    for j in range(k):
        idx = slice(j * fold_size, (j + 1) * fold_size)
        X_part, y_part = X[idx, :], y[idx]
        if j == i:
            X_valid, y_valid = X_part, y_part
        elif X_train is None:
            X_train, y_train = X_part, y_part
        else:
            X_train = nd.concat(X_train, X_part, dim=0)
            y_train = nd.concat(y_train, y_part, dim=0)
    return X_train, y_train, X_valid, y_valid
    
  • 欠拟合和过拟合

    欠拟合:模型无法得到较低的训练误差
    过拟合:模型的训练误差远小于它在测试数据集上的误差

    • 模型复杂度
      因为高阶多项式函数模型参数更多,模型函数的选择空间更大,所以高阶多项式函数比低阶多项式函数的复杂度更高。因此,高阶多项式函数比低阶多项式函数更容易在相同的训练数据集上得到更低的训练误差。
      在这里插入图片描述

    • 训练数据集大小
      若训练集过小,则过拟合更容易发生(容易被噪声影响)。
      泛化误差不会随训练数据集里样本数量增加而增大。
      故计算资源允许时,通常希望训练数据集更大。

      综上,应选择复杂度合适的模型并避免使用过少的训练样本。

  • 权重衰减

    是一种解决过拟合的方法。

    • L2范数正则化<=>权重衰减
      定义
      𝐿2范数正则化:在模型原损失函数基础上添加𝐿2范数惩罚项
      𝐿2范数惩罚项:指模型权重参数每个元素的平方和与一个正的常数乘积
      例子
      如线性回归中的损失函数为:在这里插入图片描述
      则带有L2范数惩罚项的新损失函数为:
      在这里插入图片描述
      其中,𝑤=[𝑤1,𝑤2]。 𝜆为超参数(自定的参数),表示惩罚项的权重。
      损失函数的改变,将会导致权重w1和w2的迭代方式改变
      在这里插入图片描述
      由上式可知,L2正则化后,梯度是不变的,但权重𝑤1和𝑤2自乘了小于1的数,即为权重衰减。
    • (倒置)丢弃法
      • 方法
        如多层感知机中,输入个数为4,隐藏单元个数为5,则隐藏单元hi
        ℎ𝑖=𝜙(𝑥1𝑤1𝑖+𝑥2𝑤2𝑖+𝑥3𝑤3𝑖+𝑥4𝑤4𝑖+𝑏𝑖)
        对该隐藏层使用丢弃法时,该层的隐藏单元将有一定概率被丢弃掉。设丢弃概率为 𝑝 , 那么有 𝑝 的概率 ℎ𝑖 会被清零,有 1−𝑝 的概率 ℎ𝑖 会除以 1−𝑝 做拉伸,这样让期望不变。
        由于隐藏层神经元的丢弃是随机的,故输出层的计算无法过度依赖神经元中的任一个,从而可起到正则化的作用,来应对过拟合。但为了拿到更确定的结果,一般不使用丢弃法。
        丢弃法只在训练模型时使用,测试时不用。

正向传播、反向传播和计算图

前面的实现中,只用了正向传播:即对输入计算模型输出,然后通过autograd模块来调用系统自动生成的backward函数计算梯度。反向传播算法的自动求梯度极大简化了深度学习模型训练算法的实现。

以带 𝐿2 范数正则化的含单隐藏层的多层感知机为样例:

  • 正向传播

    指对神经网络沿着从输入层到输出层的顺序,依次计算并存储模型的中间变量
    在这里插入图片描述
    其中 J 是模型在给定的数据样本上带正则化的损失,也称为有关给定数据样本的目标函数。

  • 反向传播

    反向传播依据微积分中的链式法则,沿着从输出层到输入层的顺序,依次计算并存储目标函数有关神经网络各层的中间变量以及参数的梯度。

    对输入或输出 X , Y , Z \mathsf{X}, \mathsf{Y}, \mathsf{Z} X,Y,Z为任意形状张量的函数 Y = f ( X ) \mathsf{Y}=f(\mathsf{X}) Y=f(X) Z = g ( Y ) \mathsf{Z}=g(\mathsf{Y}) Z=g(Y)
    ∂ Z ∂ X = prod ( ∂ Z ∂ Y , ∂ Y ∂ X ) , \frac{\partial \mathsf{Z}}{\partial \mathsf{X}} = \text{prod}\left(\frac{\partial \mathsf{Z}}{\partial \mathsf{Y}}, \frac{\partial \mathsf{Y}}{\partial \mathsf{X}}\right), XZ=prod(YZ,XY),
    其中 prod \text{prod} prod运算符将根据两个输入的形状,在必要的操作(如转置和互换输入位置)后对两个输入做乘法。

    回顾一下本节中样例模型,它的参数是 W ( 1 ) \boldsymbol{W}^{(1)} W(1) W ( 2 ) \boldsymbol{W}^{(2)} W(2),因此反向传播的目标是计算 ∂ J / ∂ W ( 1 ) \partial J/\partial \boldsymbol{W}^{(1)} J/W(1) ∂ J / ∂ W ( 2 ) \partial J/\partial \boldsymbol{W}^{(2)} J/W(2)。我们将应用链式法则依次计算各中间变量和参数的梯度,其计算次序与前向传播中相应中间变量的计算次序恰恰相反。首先,分别计算目标函数 J = L + s J=L+s J=L+s有关损失项 L L L和正则项 s s s的梯度

∂ J ∂ L = 1 , ∂ J ∂ s = 1. \frac{\partial J}{\partial L} = 1, \quad \frac{\partial J}{\partial s} = 1. LJ=1,sJ=1.

  • 其次,依据链式法则计算目标函数有关输出层变量的梯度 ∂ J / ∂ o ∈ R q \partial J/\partial \boldsymbol{o} \in \mathbb{R}^q J/oRq

∂ J ∂ o = prod ( ∂ J ∂ L , ∂ L ∂ o ) = ∂ L ∂ o . \frac{\partial J}{\partial \boldsymbol{o}} = \text{prod}\left(\frac{\partial J}{\partial L}, \frac{\partial L}{\partial \boldsymbol{o}}\right) = \frac{\partial L}{\partial \boldsymbol{o}}. oJ=prod(LJ,oL)=oL.

  • 接下来,计算正则项有关两个参数的梯度:

∂ s ∂ W ( 1 ) = λ W ( 1 ) , ∂ s ∂ W ( 2 ) = λ W ( 2 ) . \frac{\partial s}{\partial \boldsymbol{W}^{(1)}} = \lambda \boldsymbol{W}^{(1)},\quad\frac{\partial s}{\partial \boldsymbol{W}^{(2)}} = \lambda \boldsymbol{W}^{(2)}. W(1)s=λW(1),W(2)s=λW(2).

  • 现在,我们可以计算最靠近输出层的模型参数的梯度 ∂ J / ∂ W ( 2 ) ∈ R q × h \partial J/\partial \boldsymbol{W}^{(2)} \in \mathbb{R}^{q \times h} J/W(2)Rq×h。依据链式法则,得到 ∂ J ∂ W ( 2 ) = prod ( ∂ J ∂ o , ∂ o ∂ W ( 2 ) ) + prod ( ∂ J ∂ s , ∂ s ∂ W ( 2 ) ) = ∂ J ∂ o h ⊤ + λ W ( 2 ) . \frac{\partial J}{\partial \boldsymbol{W}^{(2)}} = \text{prod}\left(\frac{\partial J}{\partial \boldsymbol{o}}, \frac{\partial \boldsymbol{o}}{\partial \boldsymbol{W}^{(2)}}\right) + \text{prod}\left(\frac{\partial J}{\partial s}, \frac{\partial s}{\partial \boldsymbol{W}^{(2)}}\right) = \frac{\partial J}{\partial \boldsymbol{o}} \boldsymbol{h}^\top + \lambda \boldsymbol{W}^{(2)}. W(2)J=prod(oJ,W(2)o)+prod(sJ,W(2)s)=oJh+λW(2).

  • 沿着输出层向隐藏层继续反向传播,隐藏层变量的梯度 ∂ J / ∂ h ∈ R h \partial J/\partial \boldsymbol{h} \in \mathbb{R}^h J/hRh可以这样计算:

∂ J ∂ h = prod ( ∂ J ∂ o , ∂ o ∂ h ) = W ( 2 ) ⊤ ∂ J ∂ o . \frac{\partial J}{\partial \boldsymbol{h}} = \text{prod}\left(\frac{\partial J}{\partial \boldsymbol{o}}, \frac{\partial \boldsymbol{o}}{\partial \boldsymbol{h}}\right) = {\boldsymbol{W}^{(2)}}^\top \frac{\partial J}{\partial \boldsymbol{o}}. hJ=prod(oJ,ho)=W(2)oJ.

  • 由于激活函数 ϕ \phi ϕ是按元素运算的,中间变量 z \boldsymbol{z} z的梯度 ∂ J / ∂ z ∈ R h \partial J/\partial \boldsymbol{z} \in \mathbb{R}^h J/zRh的计算需要使用按元素乘法符 ⊙ \odot

∂ J ∂ z = prod ( ∂ J ∂ h , ∂ h ∂ z ) = ∂ J ∂ h ⊙ ϕ ′ ( z ) . \frac{\partial J}{\partial \boldsymbol{z}} = \text{prod}\left(\frac{\partial J}{\partial \boldsymbol{h}}, \frac{\partial \boldsymbol{h}}{\partial \boldsymbol{z}}\right) = \frac{\partial J}{\partial \boldsymbol{h}} \odot \phi'\left(\boldsymbol{z}\right). zJ=prod(hJ,zh)=hJϕ(z).

  • 最终,我们可以得到最靠近输入层的模型参数的梯度 ∂ J / ∂ W ( 1 ) ∈ R h × d \partial J/\partial \boldsymbol{W}^{(1)} \in \mathbb{R}^{h \times d} J/W(1)Rh×d。依据链式法则,得到 ∂ J ∂ W ( 1 ) = prod ( ∂ J ∂ z , ∂ z ∂ W ( 1 ) ) + prod ( ∂ J ∂ s , ∂ s ∂ W ( 1 ) ) = ∂ J ∂ z x ⊤ + λ W ( 1 ) . \frac{\partial J}{\partial \boldsymbol{W}^{(1)}} = \text{prod}\left(\frac{\partial J}{\partial \boldsymbol{z}}, \frac{\partial \boldsymbol{z}}{\partial \boldsymbol{W}^{(1)}}\right) + \text{prod}\left(\frac{\partial J}{\partial s}, \frac{\partial s}{\partial \boldsymbol{W}^{(1)}}\right) = \frac{\partial J}{\partial \boldsymbol{z}} \boldsymbol{x}^\top + \lambda \boldsymbol{W}^{(1)}. W(1)J=prod(zJ,W(1)z)+prod(sJ,W(1)s)=zJx+λW(1).

  • 两者联系

    在训练深度学习模型时,正向传播和反向传播之间相互依赖

    一方面,正向传播的计算可能依赖于模型参数的当前值,而这些模型参数是在反向传播的梯度计算后通过优化算法迭代的。

    另一方面,反向传播的梯度计算可能依赖于各变量的当前值,而这些变量的当前值是通过正向传播计算得到的

    我们交替地进行正向传播和反向传播,并根据反向传播计算的梯度迭代模型参数。两者互相使用对方的参数,导致中间变量内存不能立即释放,故训练要比预测占用更多内存

数值稳定性和模型初始化

  • 衰减和爆炸

    当层数很多时,很容易出现输出 / 梯度 的衰减或爆炸。如(0.2)^30,和 5 ^30 ,从而神经网络的数值稳定性变差。

  • 随机初始化模型参数

    当只有1个输出单元时,若每层的参数及激活函数都相同,那么正向传播时,每个隐藏单元将根据相同的输入计算出相同的值,并传递至输出层;在反向传播中,每个隐藏单元的参数梯度值相等

    因此,这些参数在使用基于梯度的优化算法迭代后值依然相等。即实质上只有1个隐藏单元在发挥作用。

    故要将神经网络的模型参数,特别是权重参数,进行随机初始化。

  1. MXNet的默认随机初始化

    net.initialize(init.Normal(sigma=0.01))
    

    使模型net的权重参数采用正态分布的随机初始化方式。

  2. Xavier随机初始化
    假设某全连接层的输入个数为 𝑎,输出个数为 𝑏,则Xavier随机初始化将使该层中权重参数的每个元素都随机采样于均匀分布
    U ( − 6 a + b U(-\sqrt{\frac{6}{a+b}} U(a+b6 , 6 a + b ) \sqrt{\frac{6}{a+b}}) a+b6 )

    每层输出的方差不该受该层输入个数影响,且每层梯度的方差也不该受该层输出个数影响。

  • 考虑环境因素
  1. 协变量偏移
    虽然输入的分布可能随时间而改变,但是标记函数,即条件分布P(y∣x)不会改变。

    这种协变量变化是因为问题的根源在于特征分布的变化。

  2. 标签偏移
    当我们认为导致偏移的是标签P(y)上的边缘分布的变化,但类条件分布是不变的P(x∣y)时,就会出现相反的问题。
    有时标签偏移和协变量移位假设可以同时成立。

  3. 概念偏移
    标签本身的定义发生变化的情况。如地区不同,相同标签的含义就不一样。

循环神经网络进阶

当时间步数较大或者时间步较小时,循环神经网络的梯度较容易出现衰减或爆炸。故循环神经网络在实际中较难捕捉时间序列中时间步距离较大的依赖关系。

  • 门控循环单元(GRU)
    可更好地捕捉时间序列中时间步距离较大的依赖关系。它通过可以学习的门来控制信息的流动,改变了隐藏状态的计算方式
  1. 重置门和更新门在这里插入图片描述
    假设隐藏单元个数为 h h h,给定时间步 t t t的小批量输入 X t ∈ R n × d \boldsymbol{X}_t \in \mathbb{R}^{n \times d} XtRn×d(样本数为 n n n,输入个数为 d d d)和上一时间步隐藏状态 H t − 1 ∈ R n × h \boldsymbol{H}_{t-1} \in \mathbb{R}^{n \times h} Ht1Rn×h。重置门 R t ∈ R n × h \boldsymbol{R}_t \in \mathbb{R}^{n \times h} RtRn×h和更新门 Z t ∈ R n × h \boldsymbol{Z}_t \in \mathbb{R}^{n \times h} ZtRn×h的计算如下:
    R t = σ ( X t W x r + H t − 1 W h r + b r ) , Z t = σ ( X t W x z + H t − 1 W h z + b z ) , \begin{aligned} \boldsymbol{R}_t = \sigma(\boldsymbol{X}_t \boldsymbol{W}_{xr} + \boldsymbol{H}_{t-1} \boldsymbol{W}_{hr} + \boldsymbol{b}_r),\\ \boldsymbol{Z}_t = \sigma(\boldsymbol{X}_t \boldsymbol{W}_{xz} + \boldsymbol{H}_{t-1} \boldsymbol{W}_{hz} + \boldsymbol{b}_z), \end{aligned} Rt=σ(XtWxr+Ht1Whr+br),Zt=σ(XtWxz+Ht1Whz+bz), 其中 W x r , W x z ∈ R d × h \boldsymbol{W}_{xr}, \boldsymbol{W}_{xz} \in \mathbb{R}^{d \times h} Wxr,WxzRd×h W h r , W h z ∈ R h × h \boldsymbol{W}_{hr}, \boldsymbol{W}_{hz} \in \mathbb{R}^{h \times h} Whr,WhzRh×h是权重参数, b r , b z ∈ R 1 × h \boldsymbol{b}_r, \boldsymbol{b}_z \in \mathbb{R}^{1 \times h} br,bzR1×h是偏差参数。

  2. 候选隐藏状态
    候选隐藏状态来辅助稍后的隐藏状态计算,将当前时间步重置门的输出上一时间步隐藏状态做按元素乘法(符号为 ⊙)。如果重置门中元素值接近0,那么意味着重置对应隐藏状态元素为0,即丢弃上一时间步的隐藏状态。如果元素值接近1,那么表示保留上一时间步的隐藏状态。然后,将按元素乘法的结果当前时间步的输入连结,再通过含激活函数tanh的全连接层计算出候选隐藏状态,其所有元素的值域为 [−1,1]。
    在这里插入图片描述
    具体来说,时间步 t t t的候选隐藏状态 H ~ t ∈ R n × h \tilde{\boldsymbol{H}}_t \in \mathbb{R}^{n \times h} H~tRn×h的计算为
    H ~ t = Z t ⊙ H t − 1 + ( 1 − Z t ) ⊙ H ~ t . \tilde\boldsymbol{H}_t = \boldsymbol{Z}_t \odot \boldsymbol{H}_{t-1} + (1 - \boldsymbol{Z}_t) \odot \tilde{\boldsymbol{H}}_t. H~t=ZtHt1+(1Zt)H~t.
    重置门控制了上一时间步的隐藏状态如何流入当前时间步的候选隐藏状态。

  3. 隐藏状态
    最后,时间步 t t t的隐藏状态 H t ∈ R n × h \boldsymbol{H}_t \in \mathbb{R}^{n \times h} HtRn×h的计算使用当前时间步的更新门 Z t \boldsymbol{Z}_t Zt来对上一时间步的隐藏状态 H t − 1 \boldsymbol{H}_{t-1} Ht1和当前时间步的候选隐藏状态 H ~ t \tilde{\boldsymbol{H}}_t H~t做组合:
    H t = Z t ⊙ H t − 1 + ( 1 − Z t ) ⊙ H ~ t . \boldsymbol{H}_t = \boldsymbol{Z}_t \odot \boldsymbol{H}_{t-1} + (1 - \boldsymbol{Z}_t) \odot \tilde{\boldsymbol{H}}_t. Ht=ZtHt1+(1Zt)H~t.在这里插入图片描述
    这个设计可以应对循环神经网络中的梯度衰减问题,并更好地捕捉时间序列中时间步距离较大的依赖关系。
    综上:

    • 重置门有助于捕捉时间序列里短期的依赖关系;
    • 更新门有助于捕捉时间序列里长期的依赖关系。
  4. 代码实现
    门控循环单元的计算

    def gru(inputs, state, params):
        W_xz, W_hz, b_z, W_xr, W_hr, b_r, W_xh, W_hh, b_h, W_hq, b_q = params
        H, = state
        outputs = []
        for X in inputs:
            Z = nd.sigmoid(nd.dot(X, W_xz) + nd.dot(H, W_hz) + b_z) #更新门
            R = nd.sigmoid(nd.dot(X, W_xr) + nd.dot(H, W_hr) + b_r) #重置门
            H_tilda = nd.tanh(nd.dot(X, W_xh) + nd.dot(R * H, W_hh) + b_h) #候选隐藏门
            H = Z * H + (1 - Z) * H_tilda #隐藏门
            Y = nd.dot(H, W_hq) + b_q #输出
            outputs.append(Y)
        return outputs, (H,)
    

    训练模型并创作

    num_epochs, num_steps, batch_size, lr, clipping_theta = 160, 35, 32, 1e2, 1e-2
    pred_period, pred_len, prefixes = 40, 50, ['分开', '不分开']
    d2l.train_and_predict_rnn(gru, get_params, init_gru_state, num_hiddens,
                          vocab_size, ctx, corpus_indices, idx_to_char,
                          char_to_idx, False, num_epochs, num_steps, lr,
                          clipping_theta, batch_size, pred_period, pred_len,
                          prefixes) 
    #传入了gru,代表用gru计算了
    

    简洁实现

     #先生成gru层
    gru_layer = rnn.GRU(num_hiddens)
    #用gru层去生成rnn模型
    model = d2l.RNNModel(gru_layer, vocab_size) 
    d2l.train_and_predict_rnn_gluon(model, num_hiddens, vocab_size, ctx,
                                corpus_indices, idx_to_char, char_to_idx,
                                num_epochs, num_steps, lr, clipping_theta,
                                batch_size, pred_period, pred_len, prefixes)
    
  • 长短期记忆(LSTM)
    另一种常用的门控循环神经网络,比门控循环单元的结构稍微复杂一点。
  1. 输入门、遗忘门和输出门
    在这里插入图片描述
    具体来说,假设隐藏单元个数为 h h h,给定时间步 t t t的小批量输入 X t ∈ R n × d \boldsymbol{X}_t \in \mathbb{R}^{n \times d} XtRn×d(样本数为 n n n,输入个数为 d d d)和上一时间步隐藏状态 H t − 1 ∈ R n × h \boldsymbol{H}_{t-1} \in \mathbb{R}^{n \times h} Ht1Rn×h
    时间步 t t t的输入门 I t ∈ R n × h \boldsymbol{I}_t \in \mathbb{R}^{n \times h} ItRn×h、遗忘门 F t ∈ R n × h \boldsymbol{F}_t \in \mathbb{R}^{n \times h} FtRn×h和输出门 O t ∈ R n × h \boldsymbol{O}_t \in \mathbb{R}^{n \times h} OtRn×h分别计算如下:
    I t = σ ( X t W x i + H t − 1 W h i + b i ) , F t = σ ( X t W x f + H t − 1 W h f + b f ) , O t = σ ( X t W x o + H t − 1 W h o + b o ) , \begin{aligned} \boldsymbol{I}_t &= \sigma(\boldsymbol{X}_t \boldsymbol{W}_{xi} + \boldsymbol{H}_{t-1} \boldsymbol{W}_{hi} + \boldsymbol{b}_i),\\ \boldsymbol{F}_t &= \sigma(\boldsymbol{X}_t \boldsymbol{W}_{xf} + \boldsymbol{H}_{t-1} \boldsymbol{W}_{hf} + \boldsymbol{b}_f),\\ \boldsymbol{O}_t &= \sigma(\boldsymbol{X}_t \boldsymbol{W}_{xo} + \boldsymbol{H}_{t-1} \boldsymbol{W}_{ho} + \boldsymbol{b}_o), \end{aligned} ItFtOt=σ(XtWxi+Ht1Whi+bi),=σ(XtWxf+Ht1Whf+bf),=σ(XtWxo+Ht1Who+bo),

  2. 候选记忆细胞
    它的计算与上面介绍的3个门类似,但使用了值域在 [−1,1] 的tanh函数 (上面是sigmoid)作为激活函数在这里插入图片描述
    具体来说,时间步 t t t的候选记忆细胞 C ~ t ∈ R n × h \tilde{\boldsymbol{C}}_t \in \mathbb{R}^{n \times h} C~tRn×h的计算为 C ~ t = tanh ( X t W x c + H t − 1 W h c + b c ) , \tilde{\boldsymbol{C}}_t = \text{tanh}(\boldsymbol{X}_t \boldsymbol{W}_{xc} + \boldsymbol{H}_{t-1} \boldsymbol{W}_{hc} + \boldsymbol{b}_c), C~t=tanh(XtWxc+Ht1Whc+bc),

  3. 记忆细胞
    当前时间步记忆细胞 C t ∈ R n × h \boldsymbol{C}_t \in \mathbb{R}^{n \times h} CtRn×h的计算组合了上一时间步记忆细胞当前时间步候选记忆细胞的信息,并通过遗忘门和输入门来控制信息的流动: C t = F t ⊙ C t − 1 + I t ⊙ C ~ t . \boldsymbol{C}_t = \boldsymbol{F}_t \odot \boldsymbol{C}_{t-1} + \boldsymbol{I}_t \odot \tilde{\boldsymbol{C}}_t. Ct=FtCt1+ItC~t.
    遗忘门控制上一时间步的记忆细胞 C t − 1 \boldsymbol{C}_{t-1} Ct1中的信息是否传递到当前时间步,而输入门则控制当前时间步的输入 X t \boldsymbol{X}_t Xt通过候选记忆细胞 C ~ t \tilde{\boldsymbol{C}}_t C~t如何流入当前时间步的记忆细胞。这个设计可以应对循环神经网络中的梯度衰减问题,并更好地捕捉时间序列中时间步距离较大的依赖关系。
    在这里插入图片描述

  4. 隐藏状态
    有了记忆细胞以后,接下来我们还可以通过输出门来控制从记忆细胞到隐藏状态 H t ∈ R n × h \boldsymbol{H}_t \in \mathbb{R}^{n \times h} HtRn×h的信息的流动:
    H t = O t ⊙ tanh ( C t ) . \boldsymbol{H}_t = \boldsymbol{O}_t \odot \text{tanh}(\boldsymbol{C}_t). Ht=Ottanh(Ct).在这里插入图片描述
    :每个循环单元中的记忆细胞和循环单元的值为LSTM模型中的隐状态,而非参数,因此不需要初始化。

  5. 代码实现
    计算实现

    def lstm(inputs, state, params):
        [W_xi, W_hi, b_i, W_xf, W_hf, b_f, W_xo, W_ho, b_o, W_xc, W_hc, b_c,
         W_hq, b_q] = params
        (H, C) = state
        outputs = []
        for X in inputs:
            I = nd.sigmoid(nd.dot(X, W_xi) + nd.dot(H, W_hi) + b_i)
            F = nd.sigmoid(nd.dot(X, W_xf) + nd.dot(H, W_hf) + b_f)
            O = nd.sigmoid(nd.dot(X, W_xo) + nd.dot(H, W_ho) + b_o)
            C_tilda = nd.tanh(nd.dot(X, W_xc) + nd.dot(H, W_hc) + b_c)
            C = F * C + I * C_tilda
            H = O * C.tanh()
            Y = nd.dot(H, W_hq) + b_q
            outputs.append(Y)
        return outputs, (H, C)
    

    调用模块简洁实现

    lstm_layer = rnn.LSTM(num_hiddens)
    model = d2l.RNNModel(lstm_layer, vocab_size)
    d2l.train_and_predict_rnn_gluon(model, num_hiddens, vocab_size, ctx,
                                corpus_indices, idx_to_char, char_to_idx,
                                num_epochs, num_steps, lr, clipping_theta,
                                batch_size, pred_period, pred_len, prefixes)
    
  • 深度循环神经网络

    即含有多个隐藏层的循环神经网络
    在这里插入图片描述
    具体来说,在时间步 t t t里,设小批量输入 X t ∈ R n × d \boldsymbol{X}_t \in \mathbb{R}^{n \times d} XtRn×d(样本数为 n n n,输入个数为 d d d),第 ℓ \ell 隐藏层( ℓ = 1 , … , L \ell=1,\ldots,L =1,,L)的隐藏状态为 H t ( ℓ ) ∈ R n × h \boldsymbol{H}_t^{(\ell)} \in \mathbb{R}^{n \times h} Ht()Rn×h(隐藏单元个数为 h h h),输出层变量为 O t ∈ R n × q \boldsymbol{O}_t \in \mathbb{R}^{n \times q} OtRn×q(输出个数为 q q q),且隐藏层的激活函数为 ϕ \phi ϕ。第1隐藏层的隐藏状态和之前的计算一样: H t ( 1 ) = ϕ ( X t W x h ( 1 ) + H t − 1 ( 1 ) W h h ( 1 ) + b h ( 1 ) ) , \boldsymbol{H}_t^{(1)} = \phi(\boldsymbol{X}_t \boldsymbol{W}_{xh}^{(1)} + \boldsymbol{H}_{t-1}^{(1)} \boldsymbol{W}_{hh}^{(1)} + \boldsymbol{b}_h^{(1)}), Ht(1)=ϕ(XtWxh(1)+Ht1(1)Whh(1)+bh(1)),
    其中权重 W x h ( 1 ) ∈ R d × h \boldsymbol{W}_{xh}^{(1)} \in \mathbb{R}^{d \times h} Wxh(1)Rd×h W h h ( 1 ) ∈ R h × h \boldsymbol{W}_{hh}^{(1)} \in \mathbb{R}^{h \times h} Whh(1)Rh×h和偏差 b h ( 1 ) ∈ R 1 × h \boldsymbol{b}_h^{(1)} \in \mathbb{R}^{1 \times h} bh(1)R1×h分别为第1隐藏层的模型参数。
    1 < ℓ ≤ L 1 < \ell \leq L 1<L时,第 ℓ \ell 隐藏层的隐藏状态的表达式为
    H t ( ℓ ) = ϕ ( H t ( ℓ − 1 ) W x h ( ℓ ) + H t − 1 ( ℓ ) W h h ( ℓ ) + b h ( ℓ ) ) , \boldsymbol{H}_t^{(\ell)} = \phi(\boldsymbol{H}_t^{(\ell-1)} \boldsymbol{W}_{xh}^{(\ell)} + \boldsymbol{H}_{t-1}^{(\ell)} \boldsymbol{W}_{hh}^{(\ell)} + \boldsymbol{b}_h^{(\ell)}), Ht()=ϕ(Ht(1)Wxh()+Ht1()Whh()+bh()),
    其中权重 W x h ( ℓ ) ∈ R h × h \boldsymbol{W}_{xh}^{(\ell)} \in \mathbb{R}^{h \times h} Wxh()Rh×h W h h ( ℓ ) ∈ R h × h \boldsymbol{W}_{hh}^{(\ell)} \in \mathbb{R}^{h \times h} Whh()Rh×h和偏差 b h ( ℓ ) ∈ R 1 × h \boldsymbol{b}_h^{(\ell)} \in \mathbb{R}^{1 \times h} bh()R1×h分别为第 ℓ \ell 隐藏层的模型参数。
    最终,输出层的输出只需基于第 L L L隐藏层的隐藏状态:
    O t = H t ( L ) W h q + b q , \boldsymbol{O}_t = \boldsymbol{H}_t^{(L)} \boldsymbol{W}_{hq} + \boldsymbol{b}_q, Ot=Ht(L)Whq+bq,
    其中权重 W h q ∈ R h × q \boldsymbol{W}_{hq} \in \mathbb{R}^{h \times q} WhqRh×q和偏差 b q ∈ R 1 × q \boldsymbol{b}_q \in \mathbb{R}^{1 \times q} bqR1×q为输出层的模型参数。
    同多层感知机一样,隐藏层个数 L L L和隐藏单元个数 h h h都是超参数。此外,如果将隐藏状态的计算换成门控循环单元或者长短期记忆的计算,我们可以得到深度门控循环神经网络。

    在深度循环神经网络中,隐藏状态的信息不断传递至当前层的下一时间步当前时间步的下一层。相当于从左往右,从下往上的二重循环。

  • 双向循环神经网络

    双向循环神经网络增加从后往前传递信息的隐藏层。下图演示了一个含单隐藏层的双向循环神经网络的架构。
    在这里插入图片描述在双向循环神经网络的架构中,
    设该时间步正向隐藏状态为 H → t ∈ R n × h \overrightarrow{\boldsymbol{H}}_t \in \mathbb{R}^{n \times h} H tRn×h(正向隐藏单元个数为 h h h),
    反向隐藏状态为 H ← t ∈ R n × h \overleftarrow{\boldsymbol{H}}_t \in \mathbb{R}^{n \times h} H tRn×h(反向隐藏单元个数为 h h h)。我们可以分别计算正向隐藏状态和反向隐藏状态:
    H → t = ϕ ( X t W x h ( f ) + H → t − 1 W h h ( f ) + b h ( f ) ) , H ← t = ϕ ( X t W x h ( b ) + H ← t + 1 W h h ( b ) + b h ( b ) ) , \begin{aligned} \overrightarrow{\boldsymbol{H}}_t &= \phi(\boldsymbol{X}_t \boldsymbol{W}_{xh}^{(f)} + \overrightarrow{\boldsymbol{H}}_{t-1} \boldsymbol{W}_{hh}^{(f)} + \boldsymbol{b}_h^{(f)}),\\ \overleftarrow{\boldsymbol{H}}_t &= \phi(\boldsymbol{X}_t \boldsymbol{W}_{xh}^{(b)} + \overleftarrow{\boldsymbol{H}}_{t+1} \boldsymbol{W}_{hh}^{(b)} + \boldsymbol{b}_h^{(b)}), \end{aligned} H tH t=ϕ(XtWxh(f)+H t1Whh(f)+bh(f)),=ϕ(XtWxh(b)+H t+1Whh(b)+bh(b)),
    然后我们连结两个方向的隐藏状态 H → t \overrightarrow{\boldsymbol{H}}_t H t H ← t \overleftarrow{\boldsymbol{H}}_t H t来得到隐藏状态 H t ∈ R n × 2 h \boldsymbol{H}_t \in \mathbb{R}^{n \times 2h} HtRn×2h,并将其输入到输出层。输出层计算输出 O t ∈ R n × q \boldsymbol{O}_t \in \mathbb{R}^{n \times q} OtRn×q(输出个数为 q q q):
    O t = H t W h q + b q , \boldsymbol{O}_t = \boldsymbol{H}_t \boldsymbol{W}_{hq} + \boldsymbol{b}_q, Ot=HtWhq+bq,
    双向循环神经网络在每个时间步的隐藏状态同时取决于该时间步之前之后的子序列(包括当前时间步的输入)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值