用 RNN 训练语言模型生成文本


本文结构:

  1. 什么是 Language Model?
  2. 怎么实现?怎么应用?

cs224d Day 8: 项目2-用 RNN 建立 Language Model 生成文本
课程项目描述地址。


什么是 Language Model?

Language Model 是 NLP 的基础,是语音识别, 机器翻译等很多NLP任务的核心。

参考:

实际上是一个概率分布模型 P ,对于语言里的每一个字符串 S 给出一个概率 P(S) 。


怎么实现?怎么应用?

我们先训练一个语言模型,然后用它来生成句子。感兴趣的话可以去这里看完整代码。

1.问题识别:

我们要做的是,用 RNN 通过隐藏层的反馈信息来给历史数据 xt,xt−1,...,x1 建模。

例如,输入一个起始文本:'in palo alto',生成后面的100个单词。

其中 Palo Alto 是 California 的一个城市。

2.模型:

语言模型:给了 x1, . . . , xt, 通过计算下面的概率,预测 xt+1:

模型如下:

其中参数:

h^t 是t时刻的隐藏层,e^t 是输入层,就是 one-hot 向量 x^t 与 L 作用后得到的词向量,H 是隐藏层转换矩阵,I 是输入层词表示矩阵,U 是输出层词表示矩阵,b1,b2 是 biases,这几个是我们需要训练的参数。

我们用 cross-entropy loss 来衡量误差,使之达到最小:

我们通过评价 perplexity 也就是下面这个式子,来评价模型的表现:

当我们在最小化 mean cross-entropy 的同时,也达到了最小化 mean perplexity 的目的,因为 perplexity 就是 cross entropy 的指数形式。具体推导参考

对 J 求在 t 时刻的 各参数的偏导:

RNN 在一个时间点的 模型结构 如下:

将模型展开3步得到如下结构:

关于 t 时刻的 J 对 t-1 时刻的参数 L,H,I,b1 求导:

接下来用 Adam potimizer 来训练模型,得到 loss 最小时的参数。
再用训练好的模型去生成文本。

3.文本生成的实现

  • 一共迭代max epoch次,
  • 每一次都代入 training 数据,训练模型,并得到 perplexity 值,
  • 再选择最小的 valid perplexity 并保存相应的 weights,
  • 用模型作用在输入的初始文本,生成后面的单词。
def test_RNNLM():
  config = Config()
  gen_config = deepcopy(config)
  gen_config.batch_size = gen_config.num_steps = 1

  # We create the training model and generative model
  with tf.variable_scope('RNNLM') as scope:
    model = RNNLM_Model(config)                                                         # 要训练的model
    # This instructs gen_model to reuse the same variables as the model above
    scope.reuse_variables()
    gen_model = RNNLM_Model(gen_config)                                                 # 要reuse的model

  init = tf.initialize_all_variables()
  saver = tf.train.Saver()

  with tf.Session() as session:
    best_val_pp = float('inf')
    best_val_epoch = 0
  
    session.run(init)
    for epoch in xrange(config.max_epochs):                                     # 迭代max epoch次
      print 'Epoch {}'.format(epoch)
      start = time.time()
      ###
      train_pp = model.run_epoch(
          session, model.encoded_train,
          train_op=model.train_step)
      valid_pp = model.run_epoch(session, model.encoded_valid)                  # 代入encoded train和valid数据,训练model,得到perplexity
      print 'Training perplexity: {}'.format(train_pp)                          # training data和validation data的 perplexity
      print 'Validation perplexity: {}'.format(valid_pp)
      if valid_pp < best_val_pp:
        best_val_pp = valid_pp
        best_val_epoch = epoch
        saver.save(session, './ptb_rnnlm.weights')                              # 选择最小的 valid perplexity 并保存相应的weights
      if epoch - best_val_epoch > config.early_stopping:
        break
      print 'Total time: {}'.format(time.time() - start)
      
    saver.restore(session, 'ptb_rnnlm.weights')
    test_pp = model.run_epoch(session, model.encoded_test)                      # model.run_epoch,训练这个model
    print '=-=' * 5                                                             
    print 'Test perplexity: {}'.format(test_pp)
    print '=-=' * 5
    starting_text = 'in palo alto'
    while starting_text:
      print ' '.join(generate_sentence(
          session, gen_model, gen_config, starting_text=starting_text, temp=1.0))   # 用模型作用在输入的初始文本,生成后面的单词
      starting_text = raw_input('> ')

if __name__ == "__main__":
    test_RNNLM()

4.模型是怎么训练的呢?

  • 首先导入数据 training,validation,test:
  def load_data(self, debug=False):
    """Loads starter word-vectors and train/dev/test data."""
    self.vocab = Vocab()
    self.vocab.construct(get_ptb_dataset('train'))
    self.encoded_train = np.array(
        [self.vocab.encode(word) for word in get_ptb_dataset('train')],         # 将句子get成word,再encode成one-hot向量
        dtype=np.int32)

接下来建立神经网络:

  • 添加 embedding 层:
  def add_embedding(self):
    """Add embedding layer.
    variables you will need to create:

      L: (len(self.vocab), embed_size)

    Returns:
      inputs: List of length num_steps, each of whose elements should be
              a tensor of shape (batch_size, embed_size).
    """
    # The embedding lookup is currently only implemented for the CPU
    with tf.device('/cpu:0'):                                                       

      embedding = tf.get_variable(
          'Embedding',
          [len(self.vocab), self.config.embed_size], trainable=True)                # L: (len(self.vocab), embed_size)
      inputs = tf.nn.embedding_lookup(embedding, self.input_placeholder)            # Looks up ids in a list of embedding tensors.
      inputs = [
          tf.squeeze(x, [1]) for x in tf.split(1, self.config.num_steps, inputs)]   # remove specific dimensions of size 1 at postion=[1]

      return inputs

  • 添加 RNN 层:
  def add_model(self, inputs):

    with tf.variable_scope('InputDropout'):
      inputs = [tf.nn.dropout(x, self.dropout_placeholder) for x in inputs]         # dropout of inputs

    with tf.variable_scope('RNN') as scope:
      self.initial_state = tf.zeros(                                                # initial state of RNN
          [self.config.batch_size, self.config.hidden_size])
      state = self.initial_state
      rnn_outputs = []
      for tstep, current_input in enumerate(inputs):                                # tstep 多少个时刻,多少个单词
        if tstep > 0:
          scope.reuse_variables()
        RNN_H = tf.get_variable(
            'HMatrix', [self.config.hidden_size, self.config.hidden_size])          
        RNN_I = tf.get_variable(
            'IMatrix', [self.config.embed_size, self.config.hidden_size])
        RNN_b = tf.get_variable(
            'B', [self.config.hidden_size])
        state = tf.nn.sigmoid(
            tf.matmul(state, RNN_H) + tf.matmul(current_input, RNN_I) + RNN_b)      # 这里state是当前时刻的隐藏层
        rnn_outputs.append(state)                                                   # 不过它在下一个循环中就被用了,所以也是用来存上一时刻隐藏层的
      self.final_state = rnn_outputs[-1]

    with tf.variable_scope('RNNDropout'):
      rnn_outputs = [tf.nn.dropout(x, self.dropout_placeholder) for x in rnn_outputs]       # dropout of outputs

    return rnn_outputs

  • 建立 projection 层:
  def add_projection(self, rnn_outputs):

   with tf.variable_scope('Projection'):
     U = tf.get_variable(
         'Matrix', [self.config.hidden_size, len(self.vocab)])
     proj_b = tf.get_variable('Bias', [len(self.vocab)])                       
     outputs = [tf.matmul(o, U) + proj_b for o in rnn_outputs]                 # outputs=rnn_outputs*U+b2

   return outputs


用 cross entropy 计算 loss:

  def add_loss_op(self, output):

    all_ones = [tf.ones([self.config.batch_size * self.config.num_steps])]
    cross_entropy = sequence_loss(                                              # cross entropy
        [output], [tf.reshape(self.labels_placeholder, [-1])], all_ones, len(self.vocab))
    tf.add_to_collection('total_loss', cross_entropy)
    loss = tf.add_n(tf.get_collection('total_loss'))                            # 最终的loss

    return loss

  • 用 Adam 最小化 loss:
  def add_training_op(self, loss):

    optimizer = tf.train.AdamOptimizer(self.config.lr)                          
    train_op = optimizer.minimize(self.calculate_loss)                          # 用Adam最小化loss

    return train_op

每一次训练后,得到了最小化 loss 相应的 weights。

训练后的模型,就可以用来生成文本了:

    while starting_text:
      print ' '.join(generate_sentence(
          session, gen_model, gen_config, starting_text=starting_text, temp=1.0))   # 用模型作用在输入的初始文本,生成后面的单词
      starting_text = raw_input('> ')

[cs224d]

Day 1. 深度学习与自然语言处理 主要概念一览
Day 2. TensorFlow 入门
Day 3. word2vec 模型思想和代码实现
Day 4. 怎样做情感分析
Day 5. CS224d-Day 5: RNN快速入门
Day 6. 一文学会用 Tensorflow 搭建神经网络
Day 7. 用深度神经网络处理NER命名实体识别问题
Day 8. 用 RNN 训练语言模型生成文本
Day 9. RNN与机器翻译
Day 10. 用 Recursive Neural Networks 得到分析树
Day 11. RNN的高级应用


我是 不会停的蜗牛 Alice
85后全职主妇
喜欢人工智能,行动派
创造力,思考力,学习力提升修炼进行中
欢迎您的喜欢,关注和评论!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,924评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,902评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,716评论 0 239
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,783评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,166评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,510评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,784评论 2 311
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,476评论 0 196
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,196评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,459评论 2 243
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,978评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,321评论 2 252
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,964评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,046评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,803评论 0 193
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,530评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,420评论 2 265

推荐阅读更多精彩内容