作者:李丕绩(腾讯AI Lab)
模型:Transformer + copy mechanism for abstractive summarization
数据集:CNN/Daily Mail
指定变量existing_model_name,载入之前训练保存的模型参数
进入run
利用函数init_modules
,初始化模型参数modules, constants, options。其中具体的超参数,在config.py中的class DeepmindConfigs
中已有定义;将已经处理好的字典载入modules
打印模型信息:函数print_basic_info
,主要是modules, constants, options等参数,或者操作(初始化model之后,也可以将model打印一下)
载入数据集
函数datar.batched
返回batch list(每个子列表中是batch size个样本的索引),总的样本数,以及batch的总数目
实例化模型:model = Model(modules, consts, options)
定义优化器:optimizer = torch.optim.Adagrad(model.parameters(), lr=consts["lr"], initial_accumulator_value=0.1)
如果需要载入之前训练的模型,使用函数load_model
载入model和optimizer的参数
进入epoch循环:每个epoch都会对训练集重新进行打乱,重新生成batch list
进入batch循环:按照当前batch list中子序列中的索引id,索引出对应的原始数据
利用函数datar.get_data
得到一个批量的数据——返回一个类class BatchData
的实例(包含进行了word2id的输入x,目标y,考虑拓展词表的x_ext,y_ext,以及记录补零位的x_mask,y_mask;此时所有的数据都是numpy array)
拓展词表x_ext_word,拓展词表是一个batch中所有样本共享的,拼接到fixed vocab的后面;y_ex区别于y之处:后者的OOV被设置为<unk>,但是前者给出了OOV在extended vocabulary中的索引;y_input是对y进行了shift,头部添加了标志位<bos>,作为解码输入
根据一个batch的数据进行训练,惯常操作:
# 梯度置零
model.zero_grad()
# 前向传播
y_pred, cost = model() # 输出y_pred是为了打印预测结果
# 反向传播
cost.backward() # 保证反向传播的路径通畅
# 梯度裁剪
torch.nn.utils.clip_grad_norm_(model.parameters(), consts["norm_clip"])
# 优化参数
optimizer.step()
datar.batched
划分batch,函数datar.get_data
获取一个batch的数据model.encode
基本流程:
forward()
│
└─── encode()
│
└─── decode()
│ └───decoding()
│ └───word_prob_layer()
│
└─── label_smoothing_loss() or nll_loss()
传入model的数据(全部来自于类BatchData
),该pytorch代码的实现中,batch_first全部设置为False:
torch.LongTensor(batch.x).to(options["device"])
# shape: [max_x_len, batch_size]
# 不考虑OOV的word2id处理后的source article
torch.LongTensor(batch.y_inp).to(options["device"])
# shape: [max_y_len, batch_size]
# 是y的基础上向后shift了一位,作为解码输入
torch.LongTensor(batch.y).to(options["device"])
# shape: [max_y_len, batch_size]
# 不考虑OOV的word2id处理后的target summary
torch.LongTensor(batch.x_ext).to(options["device"])
# shape:
# 考虑了OOV的word2id处理后的source article
torch.LongTensor(batch.y_ext).to(options["device"])
# shape:
# 考虑了OOV的word2id处理后的target summary
torch.FloatTensor(batch.x_mask).to(options["device"])
# shape: [max_x_len, batch_size, 1]
# 欲将一个batch中的所有序列对其,需要将每个序列补足到最大长度,mask掉padding位
torch.FloatTensor(batch.y_mask).to(options["device"])
# shape: [max_y_len, batch_size, 1]
batch.max_ext_len
# scalar
# 每个sample对应一个OOV list,一个batch中OOV的最大容量
Args:
x: source article经过word2id的表示,尚未进行embedding
Returns:
encoding hidden state:经过Transformer encoder编码的结果
source padding mask:padding位置1
进入N encoder stack逐层进行编码。每一层的输出是隐含状态,每一层输入是上一层的输出
xs = []
for layer_id, layer in enumerate(self.enc_layers):
x, _ ,_ = layer(x, self_padding_mask=padding_mask) # 进行N个blocks的编码
xs.append(x)
列表 xs 保存了每一层的隐含状态。
encoder layer的输入输出:
输入的shape:[max_x_len, batch_size, embedding_dim];
输出的shape:[max_x_len, batch_size, embedding_dim]
每一层的输入有二:1. 上一层的输出,2. padding mask;每一层encoder是一个Transformer单元,执行的操作有:
其中,attention normalization和 feed forward normalization使用的使用一个class LayerNorm
。所做的操作都是减去均值,除以标准差,并且经过一个全连接层的映射。
final encoder layer output 作为 输入编码返回
训练时用的是Teacher Forcing的decoder,需要输入shifted ground truth作为decoder input。
Args:
y_inp :shifted y
[max_y_len, batch_size]
mask_y :非padding的位置置为1
[max_y_len, batch_size, 1]
mask_x :非padding的位置置为1
[max_x_len, batch_size, 1]
hs :source article经过编码的隐含向量
[max_x_len, batch_size, embed_dim]
src_padding_mask :padding的位置置为1
[max_x_len, batch_size]
x_ext :考虑OOV的source article对应的ids(numpy array)
[max_x_len, batch_size]
max_ext_len :最大的OOV容量
Returns:
y_dec
attn_dist
class SelfAttentionMask
。此处返回了一个上三角矩阵(非零元素全为1),作为self_attn_mask进入N层解码。与encoder不同的时,decoder输入了encoder hidden states作为external memory,及其对应的padding(均为encoder的输出),以及self-attention mask。
Transformer内部的具体操作流程为:
记录transformer中的形状变化:
x, residual [sequence_length, batch_size, embedding_dim]
self-attention [sequence_length, batch_size, embedding_dim]
dropout, add & norm 操作不改变形状
residual [sequence_length, batch_size, embedding_dim]
external-attention [sequence_length, batch_size, embedding_dim]
dropout, add & norm 操作不改变形状
residual [sequence_length, batch_size, embedding_dim]
fully connected layer [sequence_length, batch_size, ff_dim]
gelu, dropout 操作不改变形状
fully connected layer [sequence_length, batch_size, embedding_dim]
dropout, add & norm 操作不改变形状
最终Transformer的输入输出形状相同
利用解码结果,映射到单词表维度上进行vocabulary probability distribution的计算
Args:
h: decoder output (hidden states)
y_emb: decoder input(shifted)
memory: encoder output (hidden states)
mask_x: encoder output (padding_mask)
xids: source article in the id representation, considering OOVs
max_ext_len: maximum length of OOV lists
Returns:
pred
dists decoder-encoder attention [max_y_len, batch_size, max_x_len]
计算单词表上概率分布的流程:
query = h,key = value = memory 进行一次external attention的计算,返回contexts,以及对应的attention weights,记作dists,shape:[num_query, batch_size, num_kv]
pred的计算:
基于decoder states h,decoder input y_emb,以及encoder-decoder context(external attention的输出)
将三者进行concatenation;shape:[max_y_len, batch_size, embed_dim * 3]
再将concatenation的hidden state维度映射到vocabulary size(fixed vocabulary);shape:[max_y_len, batch_size, vocab_size]
再在单词表的维度上进行softmax表示成概率分布
给pred拼接上全零Tensor,使得单词表的维度大小变为vocabulary size + max_ext_len(extended vocabulary),shape: [max_y_len, batch_size, vocab_ext_size]
利用上述的concatenation,经过全连接层,和sigmoid激活函数,得到一个gate,[max_y_len, batch_size, 1]
xids 的shape原本是 [max_x_len, batch_size], 经过一次转置,在第一个维度上复制max_y_len次,形状变为 [max_y_len, batch_size, max_x_size]
用下述函数得到最终的概率分布:
pred = (g * pred).scatter_add(2, xids, (1 - g) * dists)
# selfTensor.scatter_add_(dim, indexTensor, otherTensor)
# 该函数将otherTensor的所有值加到selfTensor中,加入位置由indexTensor指明
# 理论上indexTensor和otherTensor的形状应该相同,index_Tensor中的元素取值范围应该小于selfTensor的指定维度dim
# For a 3-D tensor, :attr:`self` is updated as::
# self[index[i][j][k]][j][k] += other[i][j][k] # if dim == 0
# self[i][index[i][j][k]][k] += other[i][j][k] # if dim == 1
# self[i][j][index[i][j][k]] += other[i][j][k] # if dim == 2
(g * pred) shape: [max_y_len, batch_size, vocab_ext_size]
xids shape: [max_y_len, batch_size, max_x_len]
(1 - g) * dists shape: [max_y_len, batch_size, max_x_len]
xids 中的元素(id)对应着(g * pred)中的每一个单词,也对应着(1 - g) * dists中的每一个(出现在source article中的)单词的注意力权重;
将这些单词对应的(decoder-encoder)attention weights,与利用decoder输出计算得到的pred 进行加权求和。
Args:
y_inp: 解码输入(shifted)
y_tgt: 目标序列(not shifted)
mask_y: 非padding位置1,padding位被置0
Returns:
avg_loss: 返回该样本中的平均loss:总loss / 总输出词数(而非 总loss / batch size)
预处理
y_pred = T.log(y_pred.clamp(min=1e-8))
将y_pred中的概率值小于1e-8的数据,全部设置为1e-8
label smoothing
def forward(self, output, target):
# 计算real_size,此处相当与之前的max_ext_len
if output.size(1) > self.size:
real_size = output.size(1) - self.size
else:
real_size = 0
model_prob = self.one_hot.repeat(target.size(0), 1)
if real_size > 0:
ext_zeros = torch.full((model_prob.size(0), real_size), self.smoothing_value).to(self.device)
model_prob = torch.cat((model_prob, ext_zeros), -1)
model_prob.scatter_(1, target, self.confidence)
model_prob.masked_fill_((target == self.padding_idx), 0.)
return F.kl_div(output, model_prob, reduction='sum')
流程:
将y_pred(shape: [max_y_len, batch_size, vocab_ext_size])展开成 [max_y_len * batch_size, vocab_ext_size]
将y_tgt(shape: [max_y_len, batch_size])展开成 [max_y_len * batch_size, 1]
model_prob由one_hot向量repeat而来,shape: [max_y_len * batch_size, vocab_size]
ext_zeros的shape [max_y_len * batch_size, max_ext_len],其中的元素的值均为smoothing value = 2e-6
将两者进行concatenation,得到model_prob,shape:[max_y_len * batch_size, vocab_ext_size]
在model_prob中target对应元素的位置,加上confidence = 0.9
model_prob.scatter_(1, target, self.confidence)
# torch.Tensor.scatter_(dim, index, src) -> Tensor
# 该函数将src加到self中,加入位置由index指明
# For a 3-D tensor, :attr:`self` is updated as::
# self[index[i][j][k]][j][k] += src # if dim == 0
# self[i][index[i][j][k]][k] += src # if dim == 1
# self[i][j][index[i][j][k]] += src # if dim == 2
对结果进行mask
model_prob.masked_fill_((target == self.padding_idx), 0.)
# torch.Tensor.masked_fill_(mask, value)
# Args:
# mask (ByteTensor): the binary mask
# value (float): the value to fill in with
# mask中元素为1的位置,被替换为value
# selfTensor应该与maskTensor形状一样
返回model_prob与output之间的KL散度作为 label smoothing loss
[code] Transformer For Summarization Source Code Reading [2]
原文:https://www.cnblogs.com/lauspectrum/p/11234831.html