zoukankan      html  css  js  c++  java
  • LSTM实现文本生成

    在时间序列预测的例子中,数据的时间步长为1,是有问题的。
    故使用一个新的实例:用LSTM实现文本生成
    输入数据:50个单词组成一个训练样本,输出为同样长度的序列。一个多对多的模型。
    数据集:莎士比亚作品。
    整体描述:对莎士比亚的作品进行训练。为了测试我们的工作方式,我们将提供模型候选短语,例如thou art more,并查看模型是否可以找出短语后面应该包含的单词。
    代码来自:https://wizardforcel.gitbooks.io/tf-ml-cookbook-2e-zh/content/71.html

    一、模型构建

    1.模型参数

    # Set RNN Parameters
    min_word_freq = 5  # Trim the less frequent words off
    rnn_size = 128  # RNN Model size
    epochs = 10  # Number of epochs to cycle through data
    batch_size = 100  # Train on this many examples at once
    learning_rate = 0.001  # Learning rate
    training_seq_len = 50  # how long of a word group to consider
    embedding_size = rnn_size  # Word embedding size
    save_every = 500  # How often to save model checkpoints
    eval_every = 50  # How often to evaluate the test sentences
    prime_texts = ['thou art more', 'to be or not to', 'wherefore art thou']
    

    2.模型定义

    # Define LSTM RNN Model
    class LSTM_Model():
        # 这是一个多对多的模型。
        def __init__(self, embedding_size, rnn_size, batch_size, learning_rate,
                     training_seq_len, vocab_size, infer_sample=False):
            self.embedding_size = embedding_size # 词嵌入维度,每个词变为了128维的向量。
            self.rnn_size = rnn_size # 隐层大小128
            self.vocab_size = vocab_size #  单词总数
            self.infer_sample = infer_sample # 区分训练还是预测阶段
            self.learning_rate = learning_rate # 学习率
            
            if infer_sample:
                self.batch_size = 1  # 预测阶段,batch_size = 1,即输入一个样本
                self.training_seq_len = 1 # 一个样本的长度为1,即只有一个单词。
            else:
                self.batch_size = batch_size
                self.training_seq_len = training_seq_len # 训练阶段输入文本的长度为50
            
            self.lstm_cell = tf.contrib.rnn.BasicLSTMCell(self.rnn_size)
            self.initial_state = self.lstm_cell.zero_state(self.batch_size, tf.float32)
            '''
            输入的样本x:[batch_size,50] 50个单词作为一个样本。
            输入的标签y:[batch_size,50] 和单词一一对应。
            '''
            self.x_data = tf.placeholder(tf.int32, [self.batch_size, self.training_seq_len])
            self.y_output = tf.placeholder(tf.int32, [self.batch_size, self.training_seq_len])
            
            with tf.variable_scope('lstm_vars'):
                # Softmax Output Weights
                W = tf.get_variable('W', [self.rnn_size, self.vocab_size], tf.float32, tf.random_normal_initializer())
                b = tf.get_variable('b', [self.vocab_size], tf.float32, tf.constant_initializer(0.0))
            
                # Define Embedding
                embedding_mat = tf.get_variable('embedding_mat', [self.vocab_size, self.embedding_size],
                                                tf.float32, tf.random_normal_initializer())
                # 此时embedding_output的维度 [batch_size,train_sen_len,self.embedding_size]
                # [100, 50, 128]
                embedding_output = tf.nn.embedding_lookup(embedding_mat, self.x_data)
                # 把嵌入向量,需要文本长度整除 embedding_output[1]
                # 所有rnn_inputs 一共有50个 维度为 [100,1,128]的tensor,然后遍历每个tensor,把第二维去掉。
                rnn_inputs = tf.split(axis=1, num_or_size_splits=self.training_seq_len, value=embedding_output)
                # [(100,128),50个]
                rnn_inputs_trimmed = [tf.squeeze(x, [1]) for x in rnn_inputs]
            
            # If we are inferring (generating text), we add a 'loop' function
            # Define how to get the i+1 th input from the i th output
            def inferred_loop(prev):
                # Apply hidden layer
                prev_transformed = tf.matmul(prev, W) + b
                # Get the index of the output (also don't run the gradient)
                prev_symbol = tf.stop_gradient(tf.argmax(prev_transformed, 1))
                # Get embedded vector
                out = tf.nn.embedding_lookup(embedding_mat, prev_symbol)
                return out
    
    

    3. 模型的输出

    decoder = tf.contrib.legacy_seq2seq.rnn_decoder
    outputs, last_state = decoder(rnn_inputs_trimmed,
                                self.initial_state,
                                self.lstm_cell,
                                loop_function=inferred_loop if infer_sample else None)
    self.final_state = last_state
    

    本段代码使用了tf.contrib.legacy_seq2seq.rnn_decoder方法。老版本的seq2seq的实现,新版本使用 tf.contrib.seq2seq。

    tf.contrib.legacy_seq2seq.rnn_decoder(
        decoder_inputs,
        initial_state,
        cell,
        loop_function=None,
        scope=None
    )
    

    decoder_inputs:一个列表,其长度为num_steps,每个元素是[batch_size, input_size]的2-D维的tensor。
    initial_state:2-D tensor,cell的初始化状态。
    cell:使用的LSTM网络。
    loop_function:如果不为空,则将该函数应用于第i个输出以得到第i+1个输入。在预测阶段,上一个时刻的输出,经过loop_function函数,得到的值作为当前时刻解码器的输入。训练阶段设置为了None。

    两个输出:
    outputs : A list of the same length as decoder_inputs of 2D Tensors with shape [batch_size x output_size] containing generated outputs.
    state :The state of each cell at the final time-step. It is a 2D Tensor of shape [batch_size x cell.state_size].

    
    # 指定最后一个维度为128,其他维度合并在一起。
    output = tf.reshape(tf.concat(axis=1, values=outputs), [-1, self.rnn_size]) #output.shape()=[5000, 128]   
    

    outputs是一个长度为50的列表,即 50* [100,128] 。 按axis=1进行合并,结果的shape为(100,128*50=6400)
    然后reshape 为 (5000,128)。可以理解为这次训练的输出为5000个单词的embedding。

    t1 = [[1, 2, 3], [4, 5, 6]]
    t2 = [[7, 8, 9], [10, 11, 12]]
    tf.concat([t1, t2], 0)  # [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]
    tf.concat([t1, t2], 1)  # [[1, 2, 3, 7, 8, 9], [4, 5, 6, 10, 11, 12]]
    

    最后经过一个全连接层,得到每个单词的分布 (5000,词汇表大小)
    经过sotfmax,得到概率。

    self.logit_output = tf.matmul(output, W) + b  #[5000,词汇表大小]
    self.model_output = tf.nn.softmax(self.logit_output)
    

    4.损失函数

    loss_fun = tf.contrib.legacy_seq2seq.sequence_loss_by_example
    loss = loss_fun([self.logit_output], [tf.reshape(self.y_output, [-1])],
                            [tf.ones([self.batch_size * self.training_seq_len])])
    self.cost = tf.reduce_sum(loss) / (self.batch_size * self.training_seq_len)
    

    关于这里的损失函数,这个函数用于计算所有examples(假设一句话有n个单词,一个单词及单词所对应的label就是一个example,所有example就是一句话中所有单词)的加权交叉熵损失。
    sequence_loss_by_example的做法是,针对logits中的每一个num_step,即[batch_size, vocab_size], 对所有vocab_size个预测结果,得出预测值最大的那个类别,与target中的值相比较计算Loss值
    loss shape 为 (5000,) ,通过求平均得到平均的交叉熵损失值。

    tf.contrib.legacy_seq2seq.sequence_loss_by_example(
        logits,
        targets,
        weights,
        average_across_timesteps=True,
        softmax_loss_function=None,
        name=None
    )
    

    logtit:List of 2D Tensors of shape [batch_size x num_decoder_symbols].此时为 [[5000,词汇表大小]]
    targert:List of 1D batch-sized int32 Tensors of the same length as logits. 此时shape为(5000,),每个值代表一个标签的真实值。
    weights:List of 1D batch-sized float-Tensors of the same length as logits。这里每个样本的权重都为1。

    5. 优化器

    tf.gradients计算损失的梯度,进行梯度裁剪,将梯度作为参数传给优化器tf.train.AdamaOptimizer()得到优化器。
    优化器调用apply_gradients方法进行变量更新。

    gradients, _ = tf.clip_by_global_norm(tf.gradients(self.cost, tf.trainable_variables()), 4.5)
    optimizer = tf.train.AdamOptimizer(self.learning_rate)
    self.train_op = optimizer.apply_gradients(zip(gradients, tf.trainable_variables()))
    

    二.训练模型

    1。从原始数据得到输入格式的样本。

    # 一共多少个batch
    num_batches = int(len(s_text_ix)/(batch_size * training_seq_len)) + 1
    # 样本切分
    batches = np.array_split(s_text_ix, num_batches)
    # Reshape each split into [batch_size, training_seq_len]
    batches = [np.resize(x, [batch_size, training_seq_len]) for x in batches]
    targets = [np.roll(x, -1, axis=1) for x in batches] # 
    

    关于 np.roll,是数组的元素进行平移。前一个词预测后一个词。

    x = np.arange(10)
    x2 = np.reshape(x, (2,5))
    '''
    array([[0, 1, 2, 3, 4],
           [5, 6, 7, 8, 9]])
    '''
    np.roll(x2, -11, axis=1)
    '''
    array([[1, 2, 3, 4, 0],
           [6, 7, 8, 9, 5]])
    '''
    

    2 通过feed_dict传递参数,训练lstm_model.train_op

    lstm_model = LSTM_Model(rnn_size, batch_size, learning_rate, 
                         training_seq_len, vocab_size) 
    for ix, batch in enumerate(batches):
        training_dict = {lstm_model.x_data: batch, lstm_model.y_output: targets[ix]}
        c, h = lstm_model.initial_state
        training_dict[c] = state.c
        training_dict[h] = state.h
        
        temp_loss, state, _ = sess.run([lstm_model.cost, lstm_model.final_state, lstm_model.train_op],
                                       feed_dict=training_dict) 
    

    二、预测阶段

    目标:输入一个句子,得到后续10个单词作为输出
    预测阶段输入单词后如何得到输出的单词。
    使用相同的模型(具有相同的权重)来批量训练并从示例文本生成文本。如果没有采用内部抽样方法的课程,这将很难做到。

        def sample(self, sess, words=ix2vocab, vocab=vocab2ix, num=10, prime_text='thou art'):
            state = sess.run(self.lstm_cell.zero_state(1, tf.float32))
            word_list = prime_text.split()
            for word in word_list[:-1]:
                x = np.zeros((1, 1))
                x[0, 0] = vocab[word]
                feed_dict = {self.x_data: x, self.initial_state: state}
                [state] = sess.run([self.final_state], feed_dict=feed_dict)
    
            out_sentence = prime_text
            word = word_list[-1]
            for n in range(num):
                x = np.zeros((1, 1))
                x[0, 0] = vocab[word]
                feed_dict = {self.x_data: x, self.initial_state: state}
                [model_output, state] = sess.run([self.model_output, self.final_state], feed_dict=feed_dict)
                sample = np.argmax(model_output[0])
                if sample == 0:
                    break
                word = words[sample]
                out_sentence = out_sentence + ' ' + word
            return out_sentence
    

    1、此时有已经训练好的模型:lstm_cell。
    2、lstm_cell状态0初始化 state
    3、输入的单词通过单词-索引的字典转换为数值索引 x。并通过 feed_dict = {self.x_data:x,self.initial_state:state}的方式把变量传到神经网络
    4、[state] = sess.run([self.final_state],feed_dict=feed_dict),预测阶段只输出最后一个状态的值。
    5、 state传递到下一个时间步。
    6、到输入序列最后一个单词的时候,把输出和状态同时返回,传递给下一步,循环生成10个单词的文本。

  • 相关阅读:
    LAMP LNMP 和 LNMPA
    nginx版本如何选择?
    如何看apache的版本号
    CLR Via CSharp读书笔记(12):泛型
    要先怀疑外部代码的错误,再检测是不是自己代码的问题
    redis的那些事
    An Illustrated Guide to SSH Agent Forwarding
    Using sshagent with ssh
    dtach
    [原创]bind DNS IP列表的精确获取
  • 原文地址:https://www.cnblogs.com/leimu/p/13725538.html
Copyright © 2011-2022 走看看