自然语言处理 第十一期

循环神经网络基础

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

一、RNN的结构

将神经网络模型训练好之后,在输入层给定一个x,通过网络之后就能够在输出层得到特定的y,那么既然有了这么强大的模型,为什么还需要RNN(循环神经网络)呢?

为什么需要RNN(循环神经网络):
他们都只能单独的取处理一个个的输入,前一个输入和后一个输入是完全没有关系的。但是,某些任务需要能够更好的处理序列的信息,即前面的输入和后面的输入是有关系的。

比如,当我们在理解一句话意思时,孤立的理解这句话的每个词是不够的,我们需要处理这些词连接起来的整个序列; 当我们处理视频的时候,我们也不能只单独的去分析每一帧,而要分析这些帧连接起来的整个序列。

详细解释:https://zhuanlan.zhihu.com/p/27485750
在这里插入图片描述

二、循环神经网络的提出背景、优缺点

  1. 背景:
    RNNs的目的使用来处理序列数据。在传统的神经网络模型中,是从输入层到隐含层再到输出层,层与层之间是全连接的,每层之间的节点是无连接的。但是这种普通的神经网络对于很多问题却无能无力。
  2. 优缺点:
    RNN的优点之一是他们能够将先前的信息连接到当前的任务,例如使用先前的视频帧可以推导对当前帧的理解。
    RNN利用内部的记忆来处理任意时序的输入序列,并且在其处理单元之间既有内部的反馈连接又有前馈连接,这使得RNN可以更加容易处理不分段的文本等。但是由于RNN只能对部分序列进行记忆,所以在长序列上表现远不如短序列,造成了一旦序列过长便使得准确率下降的结果。

三、RNN的反向传播

详细推导过程:https://blog.csdn.net/wjc1182511338/article/details/79191099
代码:

def bptt(self, x, y):
    T = len(y)
    # Perform forward propagation
    o, s = self.forward_propagation(x)
    # We accumulate the gradients in these variables
    dLdU = np.zeros(self.U.shape)
    dLdV = np.zeros(self.V.shape)
    dLdW = np.zeros(self.W.shape)
    delta_o = o
    delta_o[np.arange(len(y)), y] -= 1.
    # For each output backwards...
    for t in np.arange(T)[::-1]: # t:(T-1)->0
        dLdV += np.outer(delta_o[t], s[t].T)
        # Initial delta calculation: dL/dz
        delta_t = self.V.T.dot(delta_o[t]) * (1 - (s[t] ** 2))
        # Backpropagation through time (for at most self.bptt_truncate steps)
        for bptt_step in np.arange(max(0, t-self.bptt_truncate), t+1)[::-1]: # bptt_step:t->...
            # print "Backpropagation step t=%d bptt step=%d " % (t, bptt_step)
            # Add to gradients at each previous step
            dLdW += np.outer(delta_t, s[bptt_step-1])              
            dLdU[:,x[bptt_step]] += delta_t
            # Update delta for next step dL/dz at t-1
            delta_t = self.W.T.dot(delta_t) * (1 - s[bptt_step-1] ** 2)
    return [dLdU, dLdV, dLdW]

四、RNN出现的问题(梯度问题、长期依赖问题)

  1. 梯度消失问题:https://blog.csdn.net/weixin_39773661/article/details/80973956
    解决办法:
    ReLU函数在定义域大于0部分的导数恒等于1,这样可以解决梯度消失的问题,(虽然恒等于1很容易发生梯度爆炸的情况,但可通过设置适当的阈值可解决)。

    另外计算方便,计算速度快,可以加速网络训练。但是,定义域负数部分恒等于零,这样会造成神经元无法激活(可通过合理设置学习率,降低发生的概率)。

    ReLU有优点也有缺点,其中的缺点可以通过其他操作取避免或者减低发生的概率,是目前使用最多的激活函数。

    还可以通过更改内部结构来解决梯度消失和梯度爆炸问题,那就是LSTM。

  2. 长期依赖问题:https://blog.csdn.net/YZXnuaa/article/details/80009769
    解决办法:
    渗透单元及其它多时间尺度的策略;
    长短期记忆和其它门控RNN;
    优化长期依赖(截断梯度、引导信息流的正则化);

五、BPTT算法

循环神网络的训练算法是Backpropagation Through Time,BPTT算法,其基本原理和反向传播算法是一样的,只不过反向传播算法是按照层进行反向传播,BPTT是按照时间t进行反向传播。
具体推导过程:https://zhuanlan.zhihu.com/p/26892413

六、双向RNN

在经典的循环神经网络中,状态的传输是从前往后单向的。然而,在有些问题中,当前时刻的输出不仅和之前的状态有关系,也和之后的状态相关。这时就需要双向RNN(BiRNN)来解决这类问题。例如预测一个语句中缺失的单词不仅需要根据前文来判断,也需要根据后面的内容,这时双向RNN就可以发挥它的作用。

双向RNN是由两个RNN上下叠加在一起组成的。输出由这两个RNN的状态共同决定。
在这里插入图片描述
从上图可以看出,双向RNN的主题结构就是两个单向RNN的结合。在每一个时刻t,输入会同时提供给这两个方向相反的RNN,而输出则是由这两个单向RNN共同决定(可以拼接或者求和等)。

七、LSTM、GRU的结构、提出背景、优缺点

参考:https://zhuanlan.zhihu.com/p/46836364

八、Text-RNN的原理

模型结构:embedding—>bi-drectional lstm —> concat output –>average—–> softmax layer
通过利用双向LSTM建模,然后输出最后一个词的结果直接接全连接层softmax输出了。

九、利用Text-RNN模型来进行文本分类

import inspect
import tensorflow as tf
 
class RNN_Model(object):
 
    def __init__(self, config, num_classes, is_training=True):
 
        keep_prob = config.keep_prob
        batch_size = config.batch_size
 
        num_step = config.num_step
        embed_dim = config.embed_dim
        self.embedded_x = tf.placeholder(tf.float32, [None, num_step, embed_dim], name="embedded_chars")
        self.target = tf.placeholder(tf.int64, [None, num_classes], name='target')
        self.mask_x = tf.placeholder(tf.float32, [num_step, None], name="mask_x")
 
        hidden_neural_size=config.hidden_neural_size
        hidden_layer_num=config.hidden_layer_num
 
        # build LSTM network
        def lstm_cell():
            if 'reuse' in inspect.signature(tf.contrib.rnn.BasicLSTMCell.__init__).parameters:
                return tf.contrib.rnn.BasicLSTMCell(hidden_neural_size, forget_bias=0.0,
                                                    state_is_tuple=True,
                                                    reuse=tf.get_variable_scope().reuse)
            else:
                return tf.contrib.rnn.BasicLSTMCell(
                    hidden_neural_size, forget_bias=0.0, state_is_tuple=True)
 
        attn_cell = lstm_cell
 
        if is_training and keep_prob < 1:
            def attn_cell():
                return tf.contrib.rnn.DropoutWrapper(
                    lstm_cell(), output_keep_prob=config.keep_prob)
 
        cell = tf.contrib.rnn.MultiRNNCell(
            [attn_cell() for _ in range(hidden_layer_num)], state_is_tuple=True)
 
        self._initial_state = cell.zero_state(batch_size, dtype=tf.float32)
 
        inputs = self.embedded_x
 
        if keep_prob < 1:
            inputs = tf.nn.dropout(inputs, keep_prob)
 
        out_put = []
        state = self._initial_state
        with tf.variable_scope("LSTM_layer"):
            for time_step in range(num_step):
                if time_step > 0: tf.get_variable_scope().reuse_variables()
                (cell_output, state) = cell(inputs[:, time_step,:],state)
                out_put.append(cell_output)
 
        out_put=out_put*self.mask_x[:,:,None]
 
        with tf.name_scope("mean_pooling_layer"):
            out_put = tf.reduce_sum(out_put,0)/(tf.reduce_sum(self.mask_x,0)[:,None])
 
        with tf.name_scope("Softmax_layer_and_output"):
            softmax_w = tf.get_variable("softmax_w",[hidden_neural_size,num_classes],dtype=tf.float32)
            softmax_b = tf.get_variable("softmax_b",[num_classes],dtype=tf.float32)
            # self.logits = tf.matmul(out_put,softmax_w)
            # self.scores = tf.add(self.logits, softmax_b, name='scores')
            self.scores = tf.nn.xw_plus_b(out_put, softmax_w, softmax_b, name="scores")
 
        with tf.name_scope("loss"):
            self.loss = tf.nn.softmax_cross_entropy_with_logits(labels=self.target, logits=self.scores + 1e-10)
            self.cost = tf.reduce_mean(self.loss)
 
        with tf.name_scope("accuracy"):
            self.prediction = tf.argmax(self.scores, 1, name="prediction")
            correct_prediction = tf.equal(self.prediction, tf.argmax(self.target, 1))
            self.correct_num = tf.reduce_sum(tf.cast(correct_prediction, tf.float32))
            self.accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32), name="accuracy")
            self.probability = tf.nn.softmax(self.scores, name="probability")
 
        # add summary
        loss_summary = tf.summary.scalar("loss", self.cost)
        # add summary
        accuracy_summary = tf.summary.scalar("accuracy_summary", self.accuracy)
 
        if not is_training:
            return
 
        self.global_step = tf.Variable(0, name="global_step", trainable=False)
        self.lr = tf.Variable(0.0, trainable=False)
 
        tvars = tf.trainable_variables()
        grads, _ = tf.clip_by_global_norm(tf.gradients(self.cost, tvars), config.max_grad_norm)
 
        # Keep track of gradient values and sparsity (optional)
        grad_summaries = []
        for g, v in zip(grads, tvars):
            if g is not None:
                grad_hist_summary = tf.summary.histogram("{}/grad/hist".format(v.name), g)
                sparsity_summary = tf.summary.scalar("{}/grad/sparsity".format(v.name), tf.nn.zero_fraction(g))
                grad_summaries.append(grad_hist_summary)
                grad_summaries.append(sparsity_summary)
        self.grad_summaries_merged = tf.summary.merge(grad_summaries)
 
        self.summary = tf.summary.merge([loss_summary,accuracy_summary,self.grad_summaries_merged])
 
        optimizer = tf.train.GradientDescentOptimizer(self.lr)
        optimizer.apply_gradients(zip(grads, tvars))
        self.train_op=optimizer.apply_gradients(zip(grads, tvars))
 
        self.new_lr = tf.placeholder(tf.float32,shape=[],name="new_learning_rate")
        self._lr_update = tf.assign(self.lr,self.new_lr)
 
    def assign_new_lr(self,session,lr_value):
        session.run(self._lr_update,feed_dict={self.new_lr:lr_value})
 

参考资料

  1. 一文搞懂RNN(循环神经网络)基础篇 https://zhuanlan.zhihu.com/p/30844905
  2. 长短期记忆网络(LSTMs)介绍1:传统RNN的缺点,LSTM结构解析,LSTM变体简介 https://zhuanlan.zhihu.com/p/33113729
  3. LSTM与RNN的优势 https://blog.csdn.net/qq_32113189/article/details/79462696
  4. 循环神经网络RNN详解 反向传播公式推导+代码(十分详细) https://blog.csdn.net/wjc1182511338/article/details/79191099
  5. 数学 · RNN(二)· BPTT 算法 https://zhuanlan.zhihu.com/p/26892413
  6. 循环神经网络RNN 梯度推导(BPTT)
    https://ilewseu.github.io/2017/12/30/RNN简单推导/
  7. 深度学习笔记——RNN(LSTM、GRU、双向RNN)学习总结 https://blog.csdn.net/mpk_no1/article/details/72875185
  8. LSTM与GRU的一些比较–论文笔记 https://blog.csdn.net/meanme/article/details/48845793
  9. 2018.06.06论文:12个NLP分类模型 https://www.xuqingtang.top/2018/06/06/2018.06.06论文:12个NLP分类模型/
    10.基于RNN的文本分类模型(Tensorflow) https://blog.csdn.net/tcx1992/article/details/78194384
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值