神经网络学习笔记(三)——长短期记忆网络LSTM

长短期记忆网络 LSTM

文章目录

  • 长短期记忆网络 LSTM
    • 一、概述
    • 二、背景
    • 三、LSTM原理
      • 3.1 模型结构
      • 3.2 前向传播
      • 3.3 反向传播
      • 3.4 LSTM的变体
        • 3.4.1 Peephole Connection
        • 3.4.2 Coupled
      • 四、LSTM的简单使用
      • 五、总结

一、概述

  长短期记忆网络——通常被称为LSTM,是一种特殊的RNN,能够学习长期依赖性。由Hochreiter和Schmidhuber(1997)提出,并且在接下来的工作中被许多人改进和推广。LSTM 在各种各样的问题上表现非常出色,现在被广泛使用。LSTM被明确设计用来避免长期依赖性问题。LSTM单元由单元,输入门,输出门和忘记门组成。该单元记住任意时间间隔内的值,并且三个门控制进出单元的信息流。

  LSTM网络非常适合基于时间序列数据进行分类,处理和预测,因为在时间序列中的重要事件之间可能存在未知持续时间的滞后。开发LSTM是为了处理在训练传统RNN时可能遇到的爆炸和消失的梯度问题。对于间隙长度的相对不敏感性是LSTM相对于RNN,隐马尔可夫模型和其他序列学习方法在许多应用中的优势。

二、背景

  传统RNN的关键点之一就是他们可以用来连接先前的信息到当前的任务上,例如使用过去的视频段来推测对当前段的理解。但是会有一些复杂的场景。上下文距离预测词较远,即相关信息和当前预测位置之间的间隔相当的大,在这个间隔不断增大时,传统RNN会丧失学习到连接如此远的信息的能力。

  循环神经网络中的LSTM可以解决这种问题,即长短期记忆网络。LSTM引入了门(gate)机制用于控制特征的流通和损失,其中输入门用来接受近期有用的信息,遗忘门用来对久远的、无用的信息选择性的遗忘,输出门的输出为根据当前状态决定的输出。可以解决RNN无法处理长距离的依赖的问题。

三、LSTM原理

3.1 模型结构

  原始RNN的隐藏层只有一个状态h,对于短期的输入非常敏感。LSTM再增加一个状态c,用来保存长期的状态,称为单元状态(cell state)。
神经网络学习笔记(三)——长短期记忆网络LSTM_第1张图片

  在 t t t时刻,LSTM的输入有三个:

  • 当前时刻网络的输入值 x t x_t xt
  • 上一时刻LSTM的输出值 h t − 1 h_{t-1} ht1
  • 上一时刻的单元状态 c t − 1 c_{t-1} ct1

  LSTM的输出有两个:

  • 当前时刻LSTM输出值 h t h_t ht
  • 当前时刻的单元状态 c t c_t ct

  在LSTM模型结构中,采用门(gate)来控制长期状态,在一层模型里有三个门,分别作用为:

  • 负责控制继续保存长期状态 c c c
  • 负责控制把即时状态输入到长期状态 c c c
  • 负责控制是否把长期状态 c c c作为当前的LSTM的输出

  gate实际上就是一层全连接层,输入是一个向量,输出是一个0到1之间的实数向量。公式为: g ( x ) = σ ( W x + b ) g(x) = \sigma(Wx+b) g(x)=σ(Wx+b)

3.2 前向传播

  LSTM每个模块中的具体结构如下:
神经网络学习笔记(三)——长短期记忆网络LSTM_第2张图片

  遗忘门(forget gate):决定了上一时刻的单元状态 c t − 1 c_{t-1} ct1如何保留到当前时刻 c t c_t ct
神经网络学习笔记(三)——长短期记忆网络LSTM_第3张图片

  遗忘阶段是对上一个节点传进来的输入进行选择性忘记。简单来说就是会 “忘记不重要的,记住重要的”。

  具体来说是通过计算得到的 f t f_t ft(f表示forget)来作为遗忘门控,来控制上一个状态的 c t − 1 c_{t-1} ct1的忘记的概率。
f t = σ ( W f ⋅ [ h t − 1 , x t ] + b f ) f_t = \sigma(W_f·[h_{t-1},x_t]+b_f) ft=σ(Wf[ht1,xt]+bf)

  输入门(input gate):决定了当前时刻网络的输入 x t x_t xt如何保存到单元状态 c t c_t ct
神经网络学习笔记(三)——长短期记忆网络LSTM_第4张图片

  这个阶段确定什么样的新信息被存放在细胞状态中。这里包含两个部分:1)sigmoid层为 “输入门层” ,主要对输入 x t x_t xt进行选择记忆。2)tanh层创建一个新的候选值向量 C ~ t \tilde{C}_t C~t,加入到状态中。
i t = σ ( W i ⋅ [ h t − 1 , x t ] + b i ) i_t = \sigma(W_i·[h_{t-1},x_t]+b_i) it=σ(Wi[ht1,xt]+bi)

C ~ t = tanh ⁡ ( W C ⋅ [ h t − 1 , x t ] + b C ) \tilde{C}_t = \tanh(W_C·[h_{t-1},x_t]+b_C) C~t=tanh(WC[ht1,xt]+bC)

  细胞更新(Update Cell):决定了如何计算当前序列下的细胞值 C t C_t Ct
神经网络学习笔记(三)——长短期记忆网络LSTM_第5张图片

  新的细胞状态由两部分组成,1)旧细胞 C t − 1 C_{t-1} Ct1 f t f_t ft相乘,丢弃掉之前序列的信息;2)新的候选值 C ~ t \tilde{C}_t C~t与比例系数 i t i_t it的积,保留当前的输入信息。
C t = f t ⊙ C t − 1 + i t ⊙ C ~ t C_t = f_t \odot C_{t-1} + i_t \odot \tilde{C}_t Ct=ftCt1+itC~t
  其中, ⊙ \odot 为Hadamard积

  输出门(output gate):控制单元状态 C t C_t Ct有多少输出到LSTM的当前输出值 h t h_t ht
神经网络学习笔记(三)——长短期记忆网络LSTM_第6张图片

  隐藏状态 h t h_t ht的更新由两部分组成:1) o t o_t ot, 它由上一序列的隐藏状态 h t − 1 h_{t−1} ht1和输入数据 x t x_t xt构成,通过激活函数sigmoid进行过滤;2)由隐藏状态 C t C_t Ct和tanh函数构成,tanh将 C t C_t Ct处理得到一个在 ( − 1 , 1 ) (-1,1) (1,1)之间的值,然后将其与sigmoid门相乘得到 h t h_t ht
o t = σ ( W o ⋅ [ h t − 1 , x t ] + b o ) o_t = \sigma(W_o·[h_{t-1},x_t]+b_o) ot=σ(Wo[ht1,xt]+bo)

h t = o t ⊙ tanh ⁡ ( C t ) h_t = o_t \odot \tanh(C_t) ht=ottanh(Ct)

3.3 反向传播

  算法框架:采用反向传播算法

  • 前向计算每个神经元的输出值,即 f t f_t ft i t i_t it c t c_t ct o t o_t ot h t h_t ht五个向量的值。
  • 反向计算每个神经元的误差项 δ \delta δ值。包括两个方向:1)沿时间的反向传播,即从当前 t t t时刻开始,计算每个时刻的误差项;2)将误差项向上一层传播。
  • 根据相应的误差项,计算每个权重的梯度。

  对于激活函数,先求出其导数:
σ ( z ) = d e f y = 1 1 + e − z ⇒ σ ′ ( z ) = y ( 1 − y ) \sigma(z) \overset{\underset{def}{}}{=} y = \frac{1}{1+e^{-z}} \Rightarrow \sigma'(z) = y(1-y) σ(z)=defy=1+ez1σ(z)=y(1y)

tanh ⁡ ( z ) = d e f y = e z − e − z e z + e − z ⇒ tanh ⁡ ′ ( z ) = 1 − y 2 \tanh(z) \overset{\underset{def}{}}{=} y = \frac{e^z-e^{-z}}{e^z+e^{-z}} \Rightarrow \tanh'(z) = 1-y^2 tanh(z)=defy=ez+ezezeztanh(z)=1y2

  在 t t t时刻,LSTM的输出值为 h t h_t ht,定义此时的误差项 δ t \delta_t δt
δ t = d e f ∂ L ∂ h t \delta_t \overset{\underset{def}{}}{=} \frac{\partial L}{\partial h_t} δt=defhtL

  误差项沿时间的反向传递

δ t − 1 T = ∂ L ∂ h t − 1 = ∂ L ∂ h t ∂ h t ∂ h t − 1 = δ t T ∂ h t ∂ h t − 1 \delta^T_{t-1} = \frac{\partial L}{\partial h_{t-1}} = \frac{\partial L}{\partial h_{t}} \frac{\partial h_t}{\partial h_{t-1}} = \delta_t^T \frac{\partial h_t}{\partial h_{t-1}} δt1T=ht1L=htLht1ht=δtTht1ht

  误差项沿时间的反向传递
  由于有 h t = o t ⊙ tanh ⁡ ( C t ) h_t = o_t \odot \tanh(C_t) ht=ottanh(Ct) C t = f t ⊙ C t − 1 + i t ⊙ C ~ t C_t = f_t \odot C_{t-1} + i_t \odot \tilde{C}_t Ct=ftCt1+itC~t,利用全导数,计算得出
δ t − 1 = δ o , j T W o h + δ f , j T W f h + δ i , j T W i h + δ C ~ t , j T W C h \delta_{t-1} = \delta^T_{o,j}W_{oh} + \delta^T_{f,j}W_{fh} + \delta^T_{i,j}W_{ih} + \delta^T_{\tilde{C}_t,j}W_{Ch} δt1=δo,jTWoh+δf,jTWfh+δi,jTWih+δC~t,jTWCh
  又因为有
{ δ o , t T = δ t T ⊙ tanh ⁡ ( C t ) ⊙ o t ⊙ ( 1 − o t ) δ f , t T = δ t T ⊙ o t ⊙ ( 1 − tanh ⁡ ( C t ) 2 ) ⊙ C t − 1 ⊙ f t ⊙ ( 1 − f t ) δ i , t T = δ t T ⊙ o t ⊙ ( 1 − tanh ⁡ ( C t ) 2 ) ⊙ C ~ t ⊙ i t ⊙ ( 1 − i t ) δ C ~ t , t T = δ t T ⊙ o t ⊙ ( 1 − tanh ⁡ ( C t ) 2 ) ⊙ i t ⊙ ( 1 − C ~ t 2 ) \begin{cases} \delta_{o,t}^T = \delta_t^T \odot \tanh(C_t) \odot o_t \odot (1-o_t) \\ \delta_{f,t}^T = \delta_t^T \odot o_t \odot (1-\tanh(C_t)^2) \odot C_{t-1}\odot f_t \odot (1-f_t) \\ \delta_{i,t}^T = \delta_t^T \odot o_t \odot (1-\tanh(C_t)^2) \odot {\tilde{C}_t} \odot i_t \odot (1-i_t) \\ \delta_{{\tilde{C}_t},t}^T = \delta_t^T \odot o_t \odot (1-\tanh(C_t)^2) \odot i_t \odot (1-{\tilde{C}_t}^2) \end{cases} δo,tT=δtTtanh(Ct)ot(1ot)δf,tT=δtTot(1tanh(Ct)2)Ct1ft(1ft)δi,tT=δtTot(1tanh(Ct)2)C~tit(1it)δC~t,tT=δtTot(1tanh(Ct)2)it(1C~t2)
  带入上式可得出
δ k T = ∏ j = k t − 1 δ o , j T W o h + δ f , j T W f h + δ i , j T W i h + δ C ~ t , j T W C h \delta_k^T = \prod_{j=k}^{t-1} \delta^T_{o,j}W_{oh} + \delta^T_{f,j}W_{fh} + \delta^T_{i,j}W_{ih} + \delta^T_{\tilde{C}_t,j}W_{Ch} δkT=j=kt1δo,jTWoh+δf,jTWfh+δi,jTWih+δC~t,jTWCh

  将误差项传递到上一层

  如果当前层为第 l l l层,设 l − 1 l-1 l1层的误差项是误差函数对 l − 1 l-1 l1层加权输入的导数:
δ t l − 1 = d e f ∂ L n e t t l − 1 \delta_t^{l-1} \overset{\underset{def}{}}{=} \frac{\partial L}{net_t^{l-1}} δtl1=defnettl1L
  则LSTM的输入可以表示为:
x t l = f l − 1 ( n e t T l − 1 ) x_t^l = f^{l-1}(net_T^{l-1}) xtl=fl1(netTl1)
  其中 f l − 1 f^{l-1} fl1表示第 l − 1 l-1 l1层的激活函数。
  将 n e t t l − 1 net_t^{l-1} nettl1展开,并利用全导数公式,得到
∂ L n e t t l − 1 = δ f , t T W f x + δ i , t T W i x + δ C ~ , t T W C x + δ o , t T W o x ⊙ f ′ ( n e t t l − 1 ) \frac{\partial L}{net_t^{l-1}} = \delta^T_{f,t}W_{fx} + \delta^T_{i,t}W_{ix} + \delta^T_{\tilde{C},t}W_{Cx} + \delta^T_{o,t}W_{ox} \odot f'(net_t^{l-1}) nettl1L=δf,tTWfx+δi,tTWix+δC~,tTWCx+δo,tTWoxf(nettl1)

  权重梯度计算

  最终的梯度值为各个时刻的梯度之和:
∂ L ∂ W o h = ∑ t ∂ L ∂ W o h , t = ∂ L ∂ n e t o , t ∂ n e t o , t ∂ W o h , t = ∑ t δ o , j h j − 1 T \frac{\partial L}{\partial W_{oh}} = \sum_t \frac{\partial L}{\partial W_{oh,t}} = \frac{\partial L}{\partial net_{o,t}} \frac{\partial net_{o,t}}{\partial W_{oh,t}} = \sum_t \delta_{o,j}h_{j-1}^T WohL=tWoh,tL=neto,tLWoh,tneto,t=tδo,jhj1T
  同理可求得其它梯度为
{ ∂ L ∂ W f h = ∑ t δ f , j h j − 1 T ∂ L ∂ W i h = ∑ t δ i , j h j − 1 T ∂ L ∂ W C h = ∑ t δ C ~ , j h j − 1 T \begin{cases} \frac{\partial L}{\partial W_{fh}} = \sum_t \delta_{f,j}h_{j-1}^T \\ \frac{\partial L}{\partial W_{ih}} = \sum_t \delta_{i,j}h_{j-1}^T \\ \frac{\partial L}{\partial W_{Ch}} = \sum_t \delta_{\tilde{C},j}h_{j-1}^T \end{cases} WfhL=tδf,jhj1TWihL=tδi,jhj1TWChL=tδC~,jhj1T

  对于偏置项 b b b的梯度,按照同样的方式可以求得
{ ∂ L ∂ b o = ∑ t δ o , j ∂ L ∂ b i = ∑ t δ i , j ∂ L ∂ b f = ∑ t δ f , j ∂ L ∂ b C = ∑ t δ C , j \begin{cases} \frac{\partial L}{\partial b_o} = \sum_t \delta_{o,j} \\ \frac{\partial L}{\partial b_i} = \sum_t \delta_{i,j} \\ \frac{\partial L}{\partial b_f} = \sum_t \delta_{f,j} \\ \frac{\partial L}{\partial b_C} = \sum_t \delta_{C,j} \\ \end{cases} boL=tδo,jbiL=tδi,jbfL=tδf,jbCL=tδC,j

  对于输入 x x x的权重矩阵的梯度,只需要根据相应的误差项直接计算:
{ ∂ L ∂ W o x = δ o , t x t T ∂ L ∂ W f x = δ f , t x t T ∂ L ∂ W i x = δ i , t x t T ∂ L ∂ W C x = δ C , t x t T \begin{cases} \frac{\partial L}{\partial W_{ox}} = \delta_{o,t} x_t^T \\ \frac{\partial L}{\partial W_{fx}} = \delta_{f,t} x_t^T \\ \frac{\partial L}{\partial W_{ix}} = \delta_{i,t} x_t^T \\ \frac{\partial L}{\partial W_{Cx}} = \delta_{C,t} x_t^T \\ \end{cases} WoxL=δo,txtTWfxL=δf,txtTWixL=δi,txtTWCxL=δC,txtT

3.4 LSTM的变体

3.4.1 Peephole Connection

  让门层也会接受细胞状态的输入。
神经网络学习笔记(三)——长短期记忆网络LSTM_第7张图片

{ f t = σ ( W f ⋅ [ C t − 1 , h t − 1 , x t ] + b f ) i t = σ ( W i ⋅ [ C t − 1 , h t − 1 , x t ] + b i ) o t = σ ( W o ⋅ [ C t − 1 , h t − 1 , x t ] + b o ) \begin{cases} f_t = \sigma(W_f·[C_{t-1},h_{t-1},x_t]+b_f) \\ i_t = \sigma(W_i·[C_{t-1},h_{t-1},x_t]+b_i) \\ o_t = \sigma(W_o·[C_{t-1},h_{t-1},x_t]+b_o) \end{cases} ft=σ(Wf[Ct1,ht1,xt]+bf)it=σ(Wi[Ct1,ht1,xt]+bi)ot=σ(Wo[Ct1,ht1,xt]+bo)

3.4.2 Coupled

  将遗忘门和输入门合二为一,一同做出决定。即仅仅会当将要输入在当前位置时遗忘;仅仅输入新值到已经遗忘旧信息的那些状态。

神经网络学习笔记(三)——长短期记忆网络LSTM_第8张图片
C t = f t ⊙ C t − 1 + ( 1 − f t ) ⊙ C ~ t C_t = f_t \odot C_{t-1} + (1-f_t) \odot \tilde{C}_t Ct=ftCt1+(1ft)C~t

四、LSTM的简单使用

  在自然语言处理中,LSTM适合用于处理与时间序列高度相关的问题。相较于传统的RNN模型,LSTM对长期记忆的表现更好。利用nn.LSTM模型对语料进行了文本分类的处理。

  首先从文件中读取相应训练集和测试集的语料,利用jieba对文本进行分词,然后利用nltk去停用词。这里nltk_data中并没有中文的停用词,我从网上找了一个中文的停用词文件放在对应的stopwords目录下,并命名为chinese

def tokenizer(text):
    sentence = jieba.lcut(text, cut_all=False)
    stopwords = stopwords.words('chinese')
    sentence = [_ for _ in sentence if _ not in stopwords]
    return sentence

  利用torchtext包处理预处理好的语料,将所提供的语料集转化为相应的词向量模型。由于每一个词均为一个向量,作为LSTM模型的输入。

train_set, validation_set = data.TabularDataset.splits(
            path='corpus_data/',
            skip_header=True,
            train='corpus_train.csv',
            validation='corpus_validation.csv',
            format='csv',
            fields=[('label', label), ('text', text)],
        )
text.build_vocab(train_set, validation_set)

  将处理好的词向量输入到lstm模型中进行处理。

self.lstm = nn.lstm(embedding_dim, self.hidden_size, batch_first=True)
self.linear = nn.Linear(self.hidden_size, label_num)
def forward(self, x):
    # 输入x的维度为(batch_size, max_len),
    x = self.embedding(x)  # 经过embedding,x的维度为(batch_size, time_step, embedding_dim)

    # 隐层初始化
    # h0,c0维度为(direction_num, batch_size, hidden_size)
    h0 = torch.rand(1, x.size(0), self.hidden_size)
    c0 = torch.zeros(1, x.size(0), self.hidden_size)
    # out维度为(batch_size, seq_length, hidden_size)
    out, (hn, cn) = self.lstm(x, (h0,c0))
    # 只需要最后一步的输出状态
    out = out[:, -1, :]
    out = self.linear(out)
    return out

  利用训练集对模型进行训练,同时评估训练效果,并利用测试集对模型的准确性进行评估。为了防止偶然性产生的不确定,每一轮迭代会产生100个模型,分别评估其效率,进行调优后再用测试集测试其效率。

  在训练初期,准确率大概在50%左右。在第4轮迭代时准确率有显著提升,第5,6轮时基本能达到85%到90%准确度,收敛效果好于普通RNN。
神经网络学习笔记(三)——长短期记忆网络LSTM_第9张图片

  多轮迭代之后,模型的准确率可以达到90%以上。
神经网络学习笔记(三)——长短期记忆网络LSTM_第10张图片

五、总结

  • 通过门控状态来控制传输状态,记住需要长时间记忆的,忘记不重要的信息,对很多需要“长期记忆”的任务来说,尤其好用。
  • 门机制极大的减轻了梯度消失问题,简化了调参复杂度;门机制提供了特征过滤,丰富了向量的表示信息。
  • 每个LSTM的cell里面都有4个全连接层,如果LSTM的时间跨度很大,并且网络又很深,则计算量会很大。
  • 在语言处理任务中,LSTM非常适合用于处理与时间序列高度相关的问题,例如机器翻译、对话生成、编码\解码等。

你可能感兴趣的:(神经网络学习笔记(三)——长短期记忆网络LSTM)