[code] Transformer For Summarization Source Code Reading [3]

1. Label Smoothing

对于分类问题,我们希望模型输出的标签上概率分布,逼近于真实标签的one-hot representation。带来的问题是:

  1. 无法保证泛化
  2. one-hot表示鼓励将真实类别和其他类别之间的差距尽可能拉大,造成模型过分相信预测的类别

论文 When Does Label Smoothing Help? 中对label smoothing的数学描述:

  1. (多标签)交叉熵损失公式不变,源码中也有利用KL散度作为损失函数的,实际上最小化两种损失函数是等价的(多了一个常量,熵)
  2. 通常是利用真实y的one-hot representation预测y的概率分布进行交叉熵计算;label smoothing用smooth过的y,与预测y的概率分布进行交叉熵计算
  3. 常引入uniform distribution作为u(k),进行label smoothing

引入一个与分布无关的u(k),可由先验概率充当,可以是均匀分布:

其中有两步操作:

  1. 确定真实标签的Dirac分布,以及分布u(k)
  2. 以(1-epsilon),epsilon分别作为两者的权重进行加权求和

对应代码实现:


def label_smoothing(inputs, epsilon=0.1):
    K = inputs.get_shape().as_list()[-1]    # number of labels
    return ((1-epsilon) * inputs) + (epsilon / K)

效果就是,将原本one-hot的表示变得不那么绝对化,分出了一些概率给其他标签,留个模型泛化的空间。由于其主要的作用是防止过拟合,在过拟合严重的训练情况下,可以考虑加入label smoothing

2. Copy Mechanism

本实现中用到的copy mechanism与类似论文 Get To The Point: Summarization with Pointer-Generator Networks 一致。

Copy mechanism的流程:

  1. copy-mode:利用当前时间步的decoder-encoder attention weight来计算input article word list上概率分布的

  2. generate-mode:利用decoder hidden states、decoder input、decoder-encoder context三者,来计算fixed vocabulary上的概率分布

  3. gate:用于选择上述两种模式,利用decoder hidden states、decoder input、decoder-encoder context三者,来计算当前选择generate-mode的概率

  4. 计算联合概率(如下图):

单词的属性有如下几种:

  1. fixed vocabulary V
  2. input word list X
  3. OOV (X - V)
  4. extended vocabulary X + V

从上图可以看出如何计算单词的概率分布:

X中单词的概率由copy mode计算(乘上权重),V中单词的概率由generate-mode计算(乘上权重),X∩V中的单词的概率是两种模式下的概率的加权和。其中权重由另外一个神经网络计算得到gate。

原本我们只能得到fixed vocabulary中单词的概率分布,现在得到extend vocabulary上的概率分布(并且由于输入不同,X是动态的)。我们就可以解码输出在固定词表中没有出现,但是在输入此表中出现过的单词。

如果在解码的时候,从X中拷贝了一个单词y,但是是属于X-V集合中,即OOV单词。因为只有V中的单词有embedding,所以y单词并没有embedding,那么下一时刻的解码输入应该如何处理?参考李丕绩前辈的实现:

next_y = []
for e in last_traces:
    eid = e[-1].item()
    if eid in modules["i2w"]:
        next_y.append(eid)
        else:
            next_y.append(modules["lfw_emb"]) # unk for copy mechanism

如果当前解码时刻的输出是从input word list中拷贝出来的OOV word的数,那么下一时刻的解码输入,就是 对应的embedding ,但是当前输出就不是UNK了,而是copy的词。

3. Recurrent Decoder?

在没有看Transformer在sequence generation任务上的具体实现的时候,一直好奇Decoder如果也是auto-regressive的话,那么是不是也是recurrent的?

在进行解码的时候(training 阶段使用teacher forcing,inference逐词输出),每次解码一个单词。在copy(pointer-generator network)机制下,当前decoder的输入如下:

y_pred, attn_dist = model.decode(ys, tile_x_mask, None, tile_word_emb, tile_padding_mask, tile_x, max_ext_len)
def decode(self, inp, mask_x, mask_y, src, src_padding_mask, xids=None, max_ext_len=None):
        pass
    

可以看出decoder的输入对应关系:

  1. 解码输入就是[seq_len, beam_width],其中seq_len是beam search已经得到的部分解码序列的长度

  2. 不对input_y进行mask,计算attention的时候,考虑全部的input_y序列中的元素;故mask_y=None

  3. src 是 tile_word_emb,输入序列的embedding,[max_x_len, beam_width, embedding_dim]

  4. src_padding_mask 是 tile_padding_mask,[max_x_len, beam_width]

  5. xid 是 tile_x,[max_x_len, beam_width]

    x = torch.tensor([1, 2, 3])
    
    x.shape
    Out[7]: torch.Size([3])
    x.repeat(4,2).shape
    Out[8]: torch.Size([4, 6])
    x.repeat(4,2,1).shape
    Out[9]: torch.Size([4, 2, 3])

    torch.Tensor.repeat函数,可以将指定维度进行复制。x的维度为[dx_0],x.repeat(dy_3, dy_2, dy_1, dy_0),需要先进行unsequeeze操作,将x的维度数目拓展到与repeat函数中的参数数目相同。再对应每个维度进行repeat。

decoder内部需要进行基于解码输入的self-attention,以及基于编码输出的external attention,输出有二:

  1. y_dec 解码器的解码输出,形状为 [seq_y_len, beam_width, vocab_size]。
    通过y_pred = y_pred[-1, :, :],取最后一个隐含状态作为当前的解码输出。
  2. attn_dist 是 word_prob_layer 计算出的,external attention的注意力权重,形状为 [seq_y_len, beam_width, max_x_len]

* Beam Search的Inference阶段,按照batch读取数据,进行编码,但是按照每个样本分别进行解码

In a nutshell,这不算recurrent,但还是auto-regressive的解码模式。recurrent指的是,当前时刻的解码,接受来自上一时刻的隐含状态。而Transformer decoder在解码每一个词的时候,不仅考虑了前一个词,还考虑了之前已经生成了的所有单词,并且利用其进行self-attention计算。

4. 参考资料

  1. 【Network】优化问题——Label Smoothing
  2. 【机器学习基础】熵、KL散度、交叉熵
  3. github repository: lipiji/TranSummar

猜你喜欢

转载自www.cnblogs.com/lauspectrum/p/11237421.html