LSTM

参考: https://blog.csdn.net/m0_37917271/article/details/82350571

以下图示的每个黄色块都可以视为一层神经元(非一个神经元,因为一个神经元w的shape是[1, 输入维度]), 整个绿色区域也可视为一层神经元

①忘记门

决定了我们应该忘记哪些信息 
这里写图片描述

②记忆门

哪些该记住 
这里写图片描述

③更新门

把老的cell state更新为新的cell state 
这里写图片描述

④输出门

由记忆来决定输出什么门 
这里写图片描述


class LSTM_Cell(object):
    def __int__(self):
        with tf.variable_scope("input,", initializer=None):
            self.ix, self.__init__(input())

    '''
    参数num_nodes代表了该层神经元的个数,可以随意定义,与输入输出无关
    '''
    def __init__(self, train_data, train_label, num_nodes=64):
        with tf.variable_scope("input", initializer=tf.truncated_normal_initializer(-0.1, 0.1)) as input_layer:
            self.ix, self.im, self.ib = self._generate_w_b(
                x_weights_size=[config.vocabulary_size, num_nodes],
                m_weights_size=[num_nodes, num_nodes],
                biases_size=[1, num_nodes])
        with tf.variable_scope("memory", initializer=tf.truncated_normal_initializer(-0.1, 0.1)) as update_layer:
            self.cx, self.cm, self.cb = self._generate_w_b(
                x_weights_size=[config.vocabulary_size, num_nodes],
                m_weights_size=[num_nodes, num_nodes],
                biases_size=[1, num_nodes])
        with tf.variable_scope("forget", initializer=tf.truncated_normal_initializer(-0.1, 0.1)) as forget_layer:
            self.fx, self.fm, self.fb = self._generate_w_b(
                x_weights_size=[config.vocabulary_size, num_nodes],
                m_weights_size=[num_nodes, num_nodes],
                biases_size=[1, num_nodes])
        with tf.variable_scope("output", initializer=tf.truncated_normal_initializer(-0.1, 0.1)) as output_layer:
            self.ox, self.om, self.ob = self._generate_w_b(
                x_weights_size=[config.vocabulary_size, num_nodes],
                m_weights_size=[num_nodes, num_nodes],
                biases_size=[1, num_nodes])

        self.w = tf.Variable(tf.truncated_normal([num_nodes, config.vocabulary_size], -0.1, 0.1))
        self.b = tf.Variable(tf.zeros([config.vocabulary_size]))

        self.saved_output = tf.Variable(tf.zeros([config.batch_size, num_nodes]), trainable=False)
        self.saved_state = tf.Variable(tf.zeros([config.batch_size, num_nodes]), trainable=False)

        self.train_data = train_data  # train_data是个list,元素类型是tensor
        self.train_label = train_label

    def _generate_w_b(self, x_weights_size, m_weights_size, biases_size):
        x_w = tf.get_variable("x_weights", x_weights_size)
        m_w = tf.get_variable("m_weigths", m_weights_size)
        b = tf.get_variable("biases", config.batch_size, initializer=tf.constant_initializer(0.0))
        return x_w, m_w, b

    def _run(self, input, output, state):
        forget_gate = tf.sigmoid(tf.matmul(input, self.fx) + tf.matmul(output, self.fm) + self.fb)
        input_gate = tf.sigmoid(tf.matmul(input, self.ix) + tf.matmul(output, self.im) + self.ib)
        update = tf.matmul(input, self.cx) + tf.matmul(output, self.cm) + self.cb
        state = state * forget_gate + tf.tanh(update) * input_gate  # 这里的*代表矩阵乘法运算
        output_gate = tf.sigmoid(tf.matmul(input, self.ox) + tf.matmul(output, self.om) + self.ob)
        return output_gate * tf.tanh(state), state

    def loss_func(self):
        outputs = list()
        output = self.saved_output  # 保存的是上一组数据的最后一个输出,作用是作为一个新的序列到来时,首次的h值
        state = self.saved_state
        for i in self.train_data:  # train_data是个list,大小为num_unrollings,意味着把同一个神经元(上面_run方法构造了一个lstm神经元)赋值/拉伸了num_unrollings次
            output, state = self._run(i, output,
                                      state)  # output是每次预测一个字符, output是公式中的h_t(既是标记,又是下一次的输入), state即是公式中的c_t(细胞状态)
            outputs.append(output)  # 将每次预测的字符拼接到一起,预测len(self.train_data)次,即num_unrollings次
        '''
        因为不是顺序执行语言,一般模型如果不是相关的语句,其执行是没有先后顺序的,control_dependencies 的作用就是建立先后顺序,保证前面两句被执行后,才执行后面的内容
        '''
        with tf.control_dependencies([
            self.saved_output.assign(output),  # saved_output, saved_state:初始的ht和ct(ht该LSTM cell的输出向量,ct该LSTM cell的状态向量)
            self.saved_state.assign(state)
        ]):
            # 定义输出
            logits = tf.nn.xw_plus_b(tf.concat(outputs, 0), self.w, self.b)
            # the label should fix the size of ouputs
            loss = tf.reduce_mean(
                tf.nn.softmax_cross_entropy_with_logits(
                    labels=tf.concat(self.train_label, 0),
                    logits=logits))
            train_prediction = tf.nn.softmax(logits)
            '''
            这里范围outputs是自己添加的,目的是为了调试,查看数据结构
            '''
        return logits, loss, train_prediction, outputs


1. 该示例里只有一个神经网络层, 即_run方法定义的, num_unrollings的个数意味着把同一个神经元(上面_run方法构造了一个lstm神经元)赋值/拉伸了num_unrollings次

2. 该层中, 神经元的个数为num_nodes

3. 可以把每个图示中的黄色块视为一层神经层运算, 也可以把_run方法封装的运算(即lstm)视为一层神经层


关于lstm梯度消失问题:

在反向传播过程中, 如果乘积大于1,则梯度会随着反向传播层数n的增加而成指数增长,导致梯度爆炸;如果乘积小于1,经过多层传播后,小于1的数累乘后结果趋近于0,导致梯度消失. 

关于lstm中的forget gate的理解:

(1)原始的lstm是没有forget gate的,或者说相当于forget gate恒为1,所以不存在梯度消失问题

(2)现在的lstm被引入了forget gate,但是lstm的一个初始化技巧就是将forget gate的bias置为正数(例如1或5,这点可以查看各大框架代码),这样模型刚开始训练时forget gate的值接近于1,不回发生梯度消失

(3)随着训练过程的进行,forget gate就不再恒为1了。不过对于一个已经训练好的模型,需要选择性地记住或者遗忘某些信息,所以forget gate要么是1,要么是0,很少有类似0.5这样的中间值,相当于一个二元的开关。例如在某个序列里,forget gate全为1,那么梯度不会消失;否则,若某一个forget gate是0,这时候虽然会导致梯度消失,但是体现了模型的选择性,刻意遗忘某些信息。

相对于普通rnn, lstm可以缓解梯度消失问题,但不能彻底避免

posted @ 2023-04-03 12:06  車輪の唄  阅读(22)  评论(0编辑  收藏  举报  来源