Beam search

1.简单实现

import torch

# Beam  search
samples = []
topk = 10
log_prob, v_idx = decoder_outputs.detach().topk(topk)
for k in range(topk):
    samples.append([[v_idx[0][k].item()], log_prob[0][k], decoder_state])
for i in range(max_len):
    new_samples = []
    for sample in samples:
        v_list, score, decoder_state = sample
        if v_list[-1] == de_vocab.item2index['_EOS_']:
            new_samples.append([v_list, score, decoder_state])
            continue

        decoder_inputs = torch.LongTensor([v_list[-1]])
        decoder_outputs, new_states = decoder(decoder_inputs, encoder_output, decoder_state)
        log_prob, v_idx = decoder_outputs.data.topk(topk)

        for k in range(topk):
            new_v_list = []
            new_v_list += v_list + [v_idx[0][k].item()]
            new_samples.append([new_v_list, score + log_prob[0][k], new_states])

    new_samples = sorted(new_samples, key=lambda sample: sample[1], reverse=True)
    samples = new_samples[:topk]
    
v_list, score, states = samples[0]
for v_idx in v_list:
    pred_sent.append(de_vocab.index2item[v_idx])

2.transformers实现

Seq2Seq

一.概述

  • Seq2Seq是一个Encoder-Deocder结构的模型,输入是一个序列,输出也是一个序列。
  • Encoder将一个可变长度的输入序列变为固定长度的向量,Decoder将这个固定长度的向量解码成可变长度的输出序列。
  • 使用表示输入语句, 代表输出语句,yt代表当前输出词。所有的Seq2Seq模型都是以下目标函数,都是为了优化这个函数:
为了防止数值下溢(p连乘积趋向于0),使用log代替,即

二.改进

Seq2Seq的核心部分是其解码部分,大部分改进基于此:
    • greedy search:基础解码方法
    • beam search:对greedy search的改进
    • attention:它的引入使得解码时,每一步可以有针对地关注与当前有关的编码结果,从而减小了编码器输出表示的学习难度,也更容易学到长期的依赖关系。
    • memory network:从外部获取知识。
    • 其他方法:
      • 堆叠多层RNN的Decoder
      • 增加dropout机制
      • 与Encoder建立残差连接

三.其他以及问题

  • Attention:使得解码时,能够建立长距离依赖,更好地融入encoder的信息,使解码时每一步可以有针对地关注与当前有关的编码结果。
  • 双向RNN:单向RNN中,𝑖只包含了𝑥0𝑥𝑖的信息,𝑎𝑖𝑗丢失了𝑥𝑖后面的信息;而双向RNN中,第𝑖个输入词对应的隐状态包括了𝑖h←i,前者编码了𝑥0𝑥𝑖的信息,后者编码了𝑥𝑖及之后的信息,防止信息丢失;(捕捉上下文双向语义,)
    • 否定在后(I would like to, but I have to go school.)
    • 上文缺失,无法解码当前的位置,就可以使用下文来辅导解码
  • 如何增加多样性?

四.训练

  • Teacher forcing:根据标准答案来decode的方式(减少模型发散,加快收敛速度,但是减少生成的多样性,而且会出现矫枉过正导致不通顺
  • Autoregressive:根据上一步的输出作为下一步输入的decode方式(无法并行)
  • Exposure bias:训练Teacher forcing和测试只能Autoregressive,带来的结果来自于不同的分布,并且在测试时,如果某一步出现错误,那么错误就会一直累积(因为训练时前一个单词总是正确的),正所谓“一步错,步步错”,最终导致生成不正确的文本。
  • Scheduled Sampling
    • 模型在训练过程中的每一个steps,有 [公式] 的概率选择使用 teacher-forcing,有 [公式] 的概率选择使用 Autoregressive。
    • 模型在训练前期, [公式] 应该尽可能的大,这样能够加速收敛;而在快要结束训练的时候, [公式] 尽可能的小,让模型在 Autoregressive 的方案中尽可能的修复自身生成的错误。(这个 [公式] 概率可以随着训练的Steps or Epoch 进行衰减)

五.参考

机器翻译评价指标 — BLEU

一. 简介

Bilingual Evaluation Understudy: BLEU。在自然语言处理中的机器翻译任务中, BLEU非常常见, 它是用于评估模型生成的句子(candidate)实际句子(reference)的差异的指标. 它的取值范围在0.0到1.0之间, 如果两个句子完美匹配(perfect match), 那么BLEU是1.0, 反之, 如果两个句子完美不匹配(perfect mismatch), 那么BLEU为0.0. 虽然这个指标不够完美, 但是它有5个非常引人注目的好处(compelling benefits):

    • 计算代价小,速度快
    • 容易理解
    • 与语言无关(这意味着你可以使用全世界任意的语言来测试)
    • 与人类评价结果高度相关
    • 被学术界和工业界广泛采用

二.计算

  • 最早的BLEU算法

最早的𝐵𝐿𝐸𝑈算法是直接统计candidate中的单词有多少个出现在𝑟𝑒𝑓𝑒𝑟𝑒𝑛𝑐𝑒中,具体的式子是:
𝐵𝐿𝐸𝑈=(出现在𝑟𝑒𝑓𝑒𝑟𝑒𝑛𝑐𝑒中的candidate的单词的个数) / (candidate 中单词的总数)

以下面例子为例:
candidate: the the the the the the the
𝑟𝑒𝑓𝑒𝑟𝑒𝑛𝑐𝑒: the cat is on the mat
candidate中所有的单词都在𝑟𝑒𝑓𝑒𝑟𝑒𝑛𝑐𝑒中出现过,因此:𝐵𝐿𝐸𝑈=7/7=1
对上面的结果显然是不合理的,而且主要是分子的统计不合理,因此对上面式子中的分子进行了改进

  • 改进的BLEU算法 — 分子截断计数

𝐶𝑜𝑢𝑛𝑡_𝑤𝑖 表示单词𝑤𝑖𝑐andidate中出现的次数,𝑅𝑒𝑓_𝐶𝑜𝑢𝑛𝑡_𝑤𝑖 表示单词𝑤𝑖𝑟𝑒𝑓𝑒𝑟𝑒𝑛𝑐𝑒中出现的次数;
仍然以上面的例子为例,在𝑐andidate中只有一个单词𝑡𝑒,因此只要计算一个𝐶𝑜𝑢𝑛𝑡^𝑐𝑙𝑖𝑝𝑡𝑒𝑟𝑒𝑓𝑒𝑟𝑒𝑛𝑐𝑒中只出现了两次,因此:𝐵𝐿𝐸𝑈=2/7

  • 引入𝑛𝑔𝑟𝑎𝑚

在上面我们一直谈的都是对于单个单词进行计算,单个单词可以看作时1𝑔𝑟𝑎𝑚1𝑔𝑟𝑎𝑚可以描述翻译的充分性,即逐字翻译的能力,但不能关注翻译的流畅性,因此引入了𝑛𝑔𝑟𝑎𝑚,在这里一般𝑛不大于4。引入𝑛𝑔𝑟𝑎𝑚后的表达式如下:

𝑝𝑛 中的𝑛表示𝑛𝑔𝑟𝑎𝑚𝑝𝑛表示𝑛-𝑔𝑟𝑎𝑚的精度,即1𝑔𝑟𝑎𝑚时,𝑛=1。很多时候在评价一个系统时会用多条𝑐andidate来评价,因此上面式子中引入了一个候选集合𝑐andidates

接下来简单的理解下上面的式子,首先来看分子:
1)第一个 描述的是各个𝑐andidate的总和;(可能会有多个candidates)
2)第二个 描述的是一条𝑐andidate中所有的𝑛𝑔𝑟𝑎𝑚的总和;
3)𝐶𝑜𝑢𝑛𝑡𝑐𝑙𝑖𝑝(𝑛𝑔𝑟𝑎𝑚) 表示某一个𝑛𝑔𝑟𝑎𝑚词的截断计数;
再来看分母,前两个和分子中的含义一样,𝐶𝑜𝑢𝑛𝑡(𝑛𝑔𝑟𝑎𝑚)表示𝑛𝑔𝑟𝑎𝑚𝑐andidate中的计数。
再进一步来看,实际上分母就是𝑐andidate𝑛𝑔𝑟𝑎𝑚的个数分子是出现在𝑟𝑒𝑓𝑒𝑟𝑒𝑛𝑐𝑒中的𝑐andidate𝑛𝑔𝑟𝑎𝑚的个数

举一个例子来看看实际的计算:
𝑐andidate: the cat sat on the mat
𝑟𝑒𝑓𝑒𝑟𝑒𝑛𝑐𝑒: the cat is on the mat
计算𝑛𝑔𝑟𝑎𝑚的精度:
𝑝1=56=0.83333,𝑝2=35=0.6,𝑝3=14=0.25,𝑝4=03=0

添加对句子长度的乘法因子:
在翻译时,若出现译文很短的句子时往往会有较高的𝐵𝐿𝐸𝑈值,因此引入对句子长度的乘法因子,其表达式如下:

在这里𝑐表示𝑐𝑎𝑑𝑖𝑛𝑎𝑡𝑒的长度,𝑟表示𝑟𝑒𝑓𝑒𝑟𝑒𝑛𝑐𝑒的长度。

得到最终的表达式:

三.NLTK实现

  • 句子的BLEU值
>>> from nltk.translate.bleu_score import sentence_bleu
>>> reference = [['this', 'is', 'a', 'test'], ['this', 'is' 'test']]
>>> candidate = ['this', 'is', 'a', 'test']
>>> score = sentence_bleu(reference, candidate)
>>> print(score)
1.0

这个例子的结果为1.0, 因为candidate与reference的第1个句子perfect match了。(多个references而不是多个candidates)

  • Corpus(语料库)的BLEU值

它用于来对多个句子比如一个段落甚至一篇文章进行得分评价.

# two references for one document
>>> from nltk.translate.bleu_score import corpus_bleu
>>> references = [[['this', 'is', 'a', 'test'], ['this', 'is' 'test']]]
>>> candidates = [['this', 'is', 'a', 'test']]
>>> score = corpus_bleu(references, candidates)
>>> print(score)

1.0

四.参考