NLP基础学习9(本次结伴学习完结篇,撒花)--TextRNN

终于来到最后一期,也是目前NLP最常用的架构RNN,及其各种变种。具体来看,本次总结的主要内容:

  1. RNN的结构。循环神经网络的提出背景、优缺点。着重学习RNN的反向传播、RNN出现的问题(梯度问题、长期依赖问题)、BPTT算法。
  2. 双向RNN
  3. LSTM、GRU的结构、提出背景、优缺点。
  4. 针对梯度消失(LSTM等其他门控RNN)、梯度爆炸(梯度截断)的解决方案。
  5. Text-RNN的原理。
  6. 利用Text-RNN模型来进行文本分类

RNN

循环神经网络(Recurrent Neural Network,RNN)是用来建模序列化数据的一
种主流深度学习模型 [1]。传统的前馈神经网络一般的输入都是一个定长的向量,无法处理变长的序列信息,即使通过一些方法把序列处理成定长的向量,模型也很难捕捉序列中的长距离依赖关系。RNN则通过将神经元串行起来处理序列化的数据。由于每个神经元能用它的内部变量保存之前输入的序列信息,因此整个序列被浓缩成抽象的表示,并可以据此进行分类或生成新的序列。近年来,得益于计算能力的大幅提升和模型的改进,RNN在很多领域取得了突破性的进展——机器翻译、序列标注、图像描述、推荐系统、智能聊天机器人、自动作词作曲等。
下图展示一个典型的RNN的结构:
RNN
由图可见,一个长度为T的序列用循环神经网络建模,展开之后可以看作是一
个T层的前馈神经网络。其中,第t层的隐含状态 h t h_t ht编码了序列中前t个输入的信息,可以通过当前的输入 x t x_t xt和上一层神经网络的状态 h t − 1 h_{t−1} ht1计算得到;最后一层的状态 h T h_T hT编码了整个序列的信息。以此为基础的结构可以应用于多种具体任务。例如,在 h T h_T hT后面直接接一个Softmax层,输出文本所属类别的预测概率y,就可以实现文本分类。 h t h_t ht和y的计算公式为:
n e t t = U x t + W h t − 1 net_t=Ux_t+Wh_{t−1} nett=Uxt+Wht1
h t = f ( n e t t ) h_t=f(net_t) ht=f(nett)
y = g ( V h T ) y=g(Vh_T) y=g(VhT)

其中f和g为激活函数,U为输入层到隐含层的权重矩阵,W为隐含层从上一时刻到
下一时刻状态转移的权重矩阵。在文本分类任务中,f可以选取Tanh函数或者ReLU函数,g可以采用Softmax函数。
通过最小化损失误差(即输出的y与真实类别之间的距离),我们可以不断训
练网络,使得得到的循环神经网络可以准确地预测文本所属的类别,达到分类目
的。相比于卷积神经网络等前馈神经网络,循环神经网络由于具备对序列顺序信
息的刻画能力,往往能得到更准确的结果。

RNN 的变种有很多,从架构上来看,主要可以总结为一下4种:
RNN变种
循环神经网络的参数可以通过梯度下降方法来进行学习。循环神经网络中存在一个递归调用的函数f(·),因此其计算参数梯度的方式和前馈神经网络不太相同。在循环神经网络中主要有两种计算梯度的方式:随时间反向传播(BPTT)和实时循环学习(RTRL)算法。
随时间反向传播(Backpropagation Through Time,BPTT)算法的主要
思想是通过类似前馈神经网络的错误反向传播算法[2,3] 来进行计算
梯度。BPTT算法将循环神经网络看作是一个展开的多层前馈网络,其中“每一
层”对应循环网络中的“每个时刻”。这样,循环神经网络就可以按
按照前馈网络中的反向传播算法进行计算参数梯度。在“展开”的前馈网络中,
所有层的参数是共享的,因此参数的真实梯度是将所有“展开层”的参数梯度
之和。
因为参数U 和隐藏层在每个时刻k(1 ≤ k ≤ t) 的净输入 z k = U h k − 1 + W x k + b z_k = Uh_{k−1} + Wx_k + b zk=Uhk1+Wxk+b有关,因此第t 时刻损失的损失函数 L t L_t Lt 关于参数 U i j U_{ij} Uij 的梯度为:
梯度求导
其中 ∂ + z k ∂ U i j \frac{\partial^+z_k}{\partial U_{ij}} Uij+zk表示“直接”偏导数,即公式 z k = U h k − 1 + W x k + b z_k = Uh_{k−1} +Wx_k + b zk=Uhk1+Wxk+b中保持 h k − 1 h_{k−1} hk1 不变,对 U i j U_{ij} Uij 进行求偏导数,得到
直接偏导
其中 [ h k − 1 ] j [h_{k−1}]j [hk1]j为第k −1 时刻隐状态的第j 维; I I i ( x ) II_i(x) IIi(x)除了第i 行值为x外,其余都为0 的向量。定义 δ t , k = ∂ L t z k \delta_{t,k} = \frac{\partial L_t}{z_k} δt,k=zkLt为第t 时刻的损失对第k 时刻隐藏神经层的净输入 z k z_k zk 的导数,则:
delatatk
公式合并,并写出矩阵形式得到:
∂ L t ∂ U = ∑ k = 1 t δ t , k h k − 1 T \frac{\partial L_t}{\partial U}=\sum_{k=1}^t \delta_{t,k}h_{k-1}^T ULt=k=1tδt,khk1T
BPTT由图表示可得:
BPTT
与反向传播的BPTT算法不同的是,实时循环学习(Real-Time Recurrent
Learning,RTRL)是通过前向传播的方式来计算梯度,详细可参考文献[3]

双向RNN和LSTM

Bidirectional RNN(双向RNN)假设当前t的输出不仅仅和之前的序列有关,并且 还与之后的序列有关,例如:预测一个语句中缺失的词语那么需要根据上下文进 行预测;Bidirectional RNN是一个相对简单的RNNs,由两个RNNs上下叠加在 一起组成。输出由这两个RNNs的隐藏层的状态决定 [4]。 具体结构如图所示:
BiRNN
Long Short Term 网络—— 一般就叫做 LSTM ——是一种 RNN 特殊的类型,可以学习长期依赖信息。LSTM 由Hochreiter & Schmidhuber (1997)提出,并在近期被Alex Graves进行了改良和推广。在很多问题,LSTM 都取得相当巨大的成功,并得到了广泛的使用。
LSTM 通过刻意的设计来避免长期依赖问题。记住长期的信息在实践中是 LSTM 的默认行为,而非需要付出很大代价才能获得的能力!所有 RNN 都具有一种重复神经网络模块的链式的形式。在标准的 RNN 中,这个重复的模块只有一个非常简单的结构,例如一个 tanh 层。
LSTM
LSTM 的关键就是细胞状态,水平线在图上方贯穿运行。
细胞状态类似于传送带。直接在整个链上运行,只有一些少量的线性交互。信息在上面流传保持不变会很容易。LSTM 有通过精心设计的称作为“门”的结构来去除或者增加信息到细胞状态的能力。门是一种让信息选择式通过的方法。他们包含一个 sigmoid 神经网络层和一个 pointwise 乘法操作。
lstm公式
门控循环单元(Gated Recurrent Unit,GRU)网络是一种比LSTM网络更加简单的循环神经网络。
在LSTM网络中,输入门和遗忘门是互补关系,用两个门比较冗余。GRU将输
入门与和遗忘门合并成一个门:更新门。同时,GRU也不引入额外的记忆单元,
直接在当前状态 h t h_t ht 和历史状态 h t − 1 h_{t−1} ht1 之间引入线性依赖关系。具体结构如下:
GRU

梯度消失与梯度爆炸

循环神经网络在学习过程中的主要问题是长期依赖问题,
在BPTT算法中,将 δ t , k \delta_{t,k} δt,k 展开得到:
δ t , k = ∏ i = k t − 1 ( d i a g ( f ′ ( z i ) ) U T ) δ t , t \delta_{t,k} = \prod_{i= k}^{t-1} (diag(f'(z_i))U^T)\delta_{t,t} δt,k=i=kt1(diag(f(zi))UT)δt,t
如果定义 γ ≅ ∣ ∣ d i a g ( f ′ ( z i ) ) U T ∣ ∣ \gamma \cong || diag(f'(z_i))U^T|| γdiag(f(zi))UT,则:
δ t , k = γ t − k δ t , t \delta_{t,k} = \gamma^{t-k}\delta_{t,t} δt,k=γtkδt,t
γ > 1 \gamma > 1 γ>1,当 t − k → ∞ t−k → \infty tk时, γ t − k → ∞ \gamma^{t−k} → \infty γtk,会造成系统不稳定,称为梯度爆炸问
题(Gradient Exploding Problem);相反,若 γ &lt; 1 \gamma &lt; 1 γ<1,当 t − k → ∞ t−k → \infty tk时, γ t − k → 0 \gamma^{t−k} → 0 γtk0,会出现和深度前馈神经网络类似的梯度消失问题(gradient vanishing problem)。

梯度爆炸的问题可以通过梯度裁剪来缓解,即当梯度的范式大于某个给定值
时,对梯度进行等比收缩。而梯度消失问题相对比较棘手,需要对模型本身进行
改进。深度残差网络是对前馈神经网络的改进,通过残差学习的方式缓解了梯度
消失的现象,从而使得我们能够学习到更深层的网络表示;而对于循环神经网络
来说,长短时记忆模型 LSTM及其变种门控循环单元(Gated recurrent unit,GRU)等模型通过加入门控机制,很大程度上弥补了梯度消失所带来的损失。

TextRNN

TextRNN的一般流程是1. embeddding layer, 2.Bi-LSTM layer, 3.concat output, 4.FC layer, 5.softmax:
其基本结构如图所示:
TextRNN
参考代码如下:

# 构建模型
class BiLSTM(object):
    """
    Bi-LSTM 用于文本分类
    """
    def __init__(self, config, wordEmbedding):

        # 定义模型的输入
        self.inputX = tf.placeholder(tf.int32, [None, config.sequenceLength], name="inputX")
        self.inputY = tf.placeholder(tf.float32, [None, 1], name="inputY")
        
        self.dropoutKeepProb = tf.placeholder(tf.float32, name="dropoutKeepProb")
        
        # 定义l2损失
        l2Loss = tf.constant(0.0)
        
        # 词嵌入层
        with tf.name_scope("embedding"):

            # 利用预训练的词向量初始化词嵌入矩阵
            self.W = tf.Variable(tf.cast(wordEmbedding, dtype=tf.float32, name="word2vec") ,name="W")
            # 利用词嵌入矩阵将输入的数据中的词转换成词向量,维度[batch_size, sequence_length, embedding_size]
            self.embeddedWords = tf.nn.embedding_lookup(self.W, self.inputX)
            
        # 定义两层双向LSTM的模型结构
        with tf.name_scope("Bi-LSTM"):

            for idx, hiddenSize in enumerate(config.model.hiddenSizes):
                with tf.name_scope("Bi-LSTM" + str(idx)):
                    # 定义前向LSTM结构
                    lstmFwCell = tf.nn.rnn_cell.DropoutWrapper(tf.nn.rnn_cell.LSTMCell(num_units=hiddenSize, state_is_tuple=True),
                                                                 output_keep_prob=self.dropoutKeepProb)
                    # 定义反向LSTM结构
                    lstmBwCell = tf.nn.rnn_cell.DropoutWrapper(tf.nn.rnn_cell.LSTMCell(num_units=hiddenSize, state_is_tuple=True),
                                                                 output_keep_prob=self.dropoutKeepProb)


                    # 采用动态rnn,可以动态的输入序列的长度,若没有输入,则取序列的全长
                    # outputs是一个元祖(output_fw, output_bw),其中两个元素的维度都是[batch_size, max_time, hidden_size],fw和bw的hidden_size一样
                    # self.current_state 是最终的状态,二元组(state_fw, state_bw),state_fw=[batch_size, s],s是一个元祖(h, c)
                    outputs, self.current_state = tf.nn.bidirectional_dynamic_rnn(lstmFwCell, lstmBwCell, 
                                                                                  self.embeddedWords, dtype=tf.float32,
                                                                                  scope="bi-lstm" + str(idx))
        
                    # 对outputs中的fw和bw的结果拼接 [batch_size, time_step, hidden_size * 2]
                    self.embeddedWords = tf.concat(outputs, 2)
        
        # 去除最后时间步的输出作为全连接的输入
        finalOutput = self.embeddedWords[:, -1, :]
        
        outputSize = config.model.hiddenSizes[-1] * 2  # 因为是双向LSTM,最终的输出值是fw和bw的拼接,因此要乘以2
        output = tf.reshape(finalOutput, [-1, outputSize])  # reshape成全连接层的输入维度
        
        # 全连接层的输出
        with tf.name_scope("output"):
            outputW = tf.get_variable(
                "outputW",
                shape=[outputSize, 1],
                initializer=tf.contrib.layers.xavier_initializer())
            
            outputB= tf.Variable(tf.constant(0.1, shape=[1]), name="outputB")
            l2Loss += tf.nn.l2_loss(outputW)
            l2Loss += tf.nn.l2_loss(outputB)
            self.predictions = tf.nn.xw_plus_b(output, outputW, outputB, name="predictions")
            self.binaryPreds = tf.cast(tf.greater_equal(self.predictions, 0.5), tf.float32, name="binaryPreds")
        
        # 计算二元交叉熵损失
        with tf.name_scope("loss"):
            
            losses = tf.nn.sigmoid_cross_entropy_with_logits(logits=self.predictions, labels=self.inputY)
            self.loss = tf.reduce_mean(losses) + config.model.l2RegLambda * l2Loss

参考文献

  1. 诸葛越,《百面机器学习》,人民邮电出版社
  2. Paul J Werbos. Backpropagation through time: what it does and how to do it.
    Proceedings of the IEEE, 78(10):1550–1560,1990.
  3. 邱锡鹏, 《神经网络和深度学习》
  4. DC童生,深度学习——RNN(2)双向RNN深度RNN几种变种,腾讯云(https://cloud.tencent.com/developer/article/1144238)
  5. wangduo, 理解 LSTM(Long Short-Term Memory, LSTM) 网络,博客园(https://www.cnblogs.com/wangduo/p/6773601.html?utm_source=itdadao&utm_medium=referral)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值