任务目标
基于Transformer实现英语翻译汉语。如有疏忽请多指教
数据
Hi. 嗨。
Hi. 你好。
Run. 你用跑的。
Wait! 等等!
Hello! 你好。
I try. 让我来。
I won! 我赢了。
Oh no! 不会吧。
Cheers! 乾杯!
Got it? 你懂了吗?
He ran. 他跑了。
Hop in. 跳进来。
I lost. 我迷失了。
I quit. 我退出。
I'm OK. 我沒事。
Listen. 听着。
No way! 不可能!
No way! 没门!
Really? 你确定?
Try it. 试试吧。
We try. 我们来试试。
Why me? 为什么是我?
…… ……
代码&数据来源
Github:transformer-simple
哈弗NLP
Transformer模型结构
Transformer的组成
1. Encoder
a. 若干个EncoderLayer(两个子层)
i. Feed Forward Neural Network
connected layer.子层间使用Add & Normalization 相连
ii. Self-Attention
2. Decoder
a. 若干个DecoderLayer(三个子层)
i. Feed Forward Neural Network
connected layer.子层间使用Add & Normalization 相连
ii. Encoder-Decoder-Attention,常规注意力机制
connected layer.子层间使用Add & Normalization 相连
iii. Self-Attention,自注意力机制
Batch and Masking
Mask策略一
一种就是普通的mask,就是自然语言处理中将某些字符(如标点符号,空格等)进行mask的操作
# 该部分与transformer实现有关
class Batch:
"""
Batches and Masking
"Object for holding a batch of data with mask during training." 在训练期间使用mask处理数据
"""
def __init__(self, src, trg=None, pad=0):
"""
构造函数
@param src: 源数据
@param trg: 目标数据
@param pad: 需要mask掉的字符,默认为0
一共有两种mask的方式:
一种就是普通的mask,就是自然语言处理中将某些字符(如标点符号,空格等)进行mask的操作
另一种就是对目标数据的mask,其原因是为了不让decoder在训练中看到后续的内容(即,我对于下一个字符的预测,只来源于前面的字符)
对于src的mask就是第一种mask,而对于tgt的mask是第一种加第二种
"""
# 将numpy.array转换为张量torch.tensor
src = torch.from_numpy(src).to(args.device).long()
trg = torch.from_numpy(trg).to(args.device).long()
self.src = src
# 此处pad=0,src向量均不为0(0表示UNK标识),src!=pad生成bool数组,且数组所有元素均为True
# 此处为第一种mask策略
self.src_mask = (src != pad).unsqueeze(-2) # unsqueeze()扩展维度,负数表示扩展的维度在倒数第n个位置
if trg is not None:
self.trg = trg[:, :-1] # 截掉trg中每个句子最后一个字符<EOS>
self.trg_y = trg[:, 1:] # 截掉trg中每个句子第一个字符<BOS>
self.trg_mask = self.make_std_mask(self.trg, pad) # 对trg掩蔽
self.ntokens = (self.trg_y != pad).data.sum()
@staticmethod
def make_std_mask(tgt, pad):
"""
mask 目标数据
"Create a mask to hide padding and future words." 翻译:创造一个mask来屏蔽补全词和字典外的词进行屏蔽
@param tgt: 即构造函数中的trg,目标数据
@param pad: 需要mask的字符,默认为0
@return: 返回mask后的目标数据
"""
# 此处为第一种mask策略
tgt_mask = (tgt != pad).unsqueeze(-2) # 由于没有0,此时布尔矩阵全为True
# 此处为第二种mask策略,调用utils中的subsequent_mask方法得到上三角布尔矩阵
# Variable()封装tensor,并存储tensor的梯度,与tgt_mask做与运算
tgt_mask = tgt_mask & Variable(subsequent_mask(tgt.size(-1)).type_as(tgt_mask.data))
return tgt_mask
Mask策略二
另一种就是对目标数据的mask,其原因是为了不让decoder在训练中看到后续的内容(即,我对于下一个字符的预测,只来源于前面的字符)
def subsequent_mask(size):
"""
第二种mask策略
"Mask out subsequent positions."
@param size: 句子长度
@return:
"""
attn_shape = (1, size, size)
# np.triu函数生成一个对角线位置上移一位的上三角矩阵(k=1代表按对角线方向上移),矩阵大小为attn_shape
subsequent_mask = np.triu(np.ones(attn_shape), k=1).astype('uint8')
return torch.from_numpy(subsequent_mask) == 0 # 返回布尔矩阵,subsequent_mask上三角矩阵中0的位置对应True
make_model
构造Transformer模型
def make_model(src_vocab, tgt_vocab, N = 6, d_model = 512, d_ff = 2048, h = 8, dropout = 0.1):
"""
定义了一个接收超参数并生成完整模型的函数。
@param src_vocab: 源数据字典长度
@param tgt_vocab: 目标数据字典长度
@param N: 层数layer
@param d_model: 表征后的维度
@param d_ff: FeedForward输出维度
@param h: attention机制,head多头个数
@param dropout:
@return:
"""
c = copy.deepcopy
attn = MultiHeadedAttention(h, d_model).to(args.device) # 多头注意力机制
ff = PositionwiseFeedForward(d_model, d_ff, dropout).to(args.device)
position = PositionalEncoding(d_model, dropout).to(args.device) # 位置信息嵌入
# model其实是Transformer的类
model = Transformer(
Encoder(EncoderLayer(d_model, c(attn), c(ff), dropout).to(args.device), N).to(args.device),
Decoder