0基础跟德姆(dom)一起学AI 自然语言处理18-解码器部分实现
1 解码器介绍
解码器部分:
- 由N个解码器层堆叠而成
- 每个解码器层由三个子层连接结构组成
- 第一个子层连接结构包括一个多头自注意力子层和规范化层以及一个残差连接
- 第二个子层连接结构包括一个多头注意力子层和规范化层以及一个残差连接
- 第三个子层连接结构包括一个前馈全连接子层和规范化层以及一个残差连接
- 说明:
- 解码器层中的各个部分,如,多头注意力机制,规范化层,前馈全连接网络,子层连接结构都与编码器中的实现相同. 因此这里可以直接拿来构建解码器层.
2 解码器层
2.1 解码器层的作用
- 作为解码器的组成单元, 每个解码器层根据给定的输入向目标方向进行特征提取操作,即解码过程.
2.2 解码器层的代码实现
# 解码器层类 DecoderLayer 实现思路分析
# init函数 (self, size, self_attn, src_attn, feed_forward, dropout)
# 词嵌入维度尺寸大小size 自注意力机制层对象self_attn 一般注意力机制层对象src_attn 前馈全连接层对象feed_forward
# clones3子层连接结构 self.sublayer = clones(SublayerConnection(size,dropout),3)
# forward函数 (self, x, memory, source_mask, target_mask)
# 数据经过子层连接结构1 self.sublayer[0](x, lambda x:self.self_attn(x, x, x, target_mask))
# 数据经过子层连接结构2 self.sublayer[1](x, lambda x:self.src_attn(x, m, m, source_mask))
# 数据经过子层连接结构3 self.sublayer[2](x, self.feed_forward)
class DecoderLayer(nn.Module):
def __init__(self, size, self_attn, src_attn, feed_forward, dropout):
super(DecoderLayer, self).__init__()
# 词嵌入维度尺寸大小
self.size = size
# 自注意力机制层对象 q=k=v
self.self_attn = self_attn
# 一遍注意力机制对象 q!=k=v
self.src_attn = src_attn
# 前馈全连接层对象
self.feed_forward = feed_forward
# clones3子层连接结构
self.sublayer = clones(SublayerConnection(size, dropout), 3)
def forward(self, x, memory, source_mask, target_mask):
m = memory
# 数据经过子层连接结构1
x = self.sublayer[0](x, lambda x:self.self_attn(x, x, x, target_mask))
# 数据经过子层连接结构2
x = self.sublayer[1](x, lambda x:self.src_attn (x, m, m, source_mask))
# 数据经过子层连接结构3
x = self.sublayer[2](x, self.feed_forward)
return x
- 函数调用
def dm_test_DecoderLayer():
d_model = 512
vocab = 1000 # 词表大小是1000
# 输入x 是一个使用Variable封装的长整型张量, 形状是2 x 4
x = Variable(torch.LongTensor([[100, 2, 421, 508], [491, 998, 1, 221]]))
emb = Embeddings(d_model, vocab)
embr = emb(x)
dropout = 0.2
max_len = 60 # 句子最大长度
x = embr # [2, 4, 512]
pe = PositionalEncoding(d_model, dropout, max_len)
pe_result = pe(x)
x = pe_result # 获取位置编码器层 编码以后的结果
# 类的实例化参数与解码器层类似, 相比多出了src_attn, 但是和self_attn是同一个类.
head = 8
d_ff = 64
size = 512
self_attn = src_attn = MultiHeadedAttention(head, d_model, dropout)
# 前馈全连接层也和之前相同
ff = PositionwiseFeedForward(d_model, d_ff, dropout)
x = pe_result
# 产生编码器结果 # 注意此函数返回编码以后的结果 要有返回值
en_result = dm_test_Encoder()
memory = en_result
mask = Variable(torch.zeros(8, 4, 4))
source_mask = target_mask = mask
# 实例化解码器层 对象
dl = DecoderLayer(size, self_attn, src_attn, ff, dropout)
# 对象调用
dl_result = dl(x, memory, source_mask, target_mask)
print(dl_result.shape)
print(dl_result)
- 输出效果
torch.Size([2, 4, 512])
tensor([[[-27.4382, 0.6516, 6.6735, ..., -42.2930, -44.9728, 0.1264],
[-28.7835, 26.4919, -0.5608, ..., 0.5652, -2.9634, 9.7438],
[-19.6998, 13.5164, 45.8216, ..., 23.9127, 22.0259, 34.0195],
[ -0.1647, 0.2331, -36.4173, ..., -20.0557, 29.4576, 2.5048]],
[[ 29.1466, 50.7677, 26.4624, ..., -39.1015, -27.9200, 19.6819],
[-10.7069, 28.0897, -0.4107, ..., -35.7795, 9.6881, 0.3228],
[ -6.9027, -16.0590, -0.8897, ..., 4.0253, 2.5961, 37.4659],
[ 9.8892, 32.7008, -6.6772, ..., -11.4273, -21.4676, 32.5692]]],
grad_fn=<AddBackward0>)
2.3 解码器层总结¶
-
学习了解码器层的作用:
- 作为解码器的组成单元, 每个解码器层根据给定的输入向目标方向进行特征提取操作,即解码过程.
-
学习并实现了解码器层的类: DecoderLayer
- 类的初始化函数的参数有5个, 分别是size,代表词嵌入的维度大小, 同时也代表解码器层的尺寸,第二个是self_attn,多头自注意力对象,也就是说这个注意力机制需要Q=K=V,第三个是src_attn,多头注意力对象,这里Q!=K=V, 第四个是前馈全连接层对象,最后就是droupout置0比率.
- forward函数的参数有4个,分别是来自上一层的输入x,来自编码器层的语义存储变量mermory, 以及源数据掩码张量和目标数据掩码张量.
- 最终输出了由编码器输入和目标数据一同作用的特征提取结果.
3 解码器
3.1 解码器的作用
- 根据编码器的结果以及上一次预测的结果, 对下一次可能出现的'值'进行特征表示.
3.2 解码器的代码分析
# 解码器类 Decoder 实现思路分析
# init函数 (self, layer, N):
# self.layers clones N个解码器层clones(layer, N)
# self.norm 定义规范化层 LayerNorm(layer.size)
# forward函数 (self, x, memory, source_mask, target_mask)
# 数据以此经过各个子层 x = layer(x, memory, source_mask, target_mask)
# 数据最后经过规范化层 return self.norm(x)
# 返回处理好的数据
class Decoder(nn.Module):
def __init__(self, layer, N):
# 参数layer 解码器层对象
# 参数N 解码器层对象的个数
super(Decoder, self).__init__()
# clones N个解码器层
self.layers = clones(layer, N)
# 定义规范化层
self.norm = LayerNorm(layer.size)
def forward(self, x, memory, source_mask, target_mask):
# 数据以此经过各个子层
for layer in self.layers:
x = layer(x, memory, source_mask, target_mask)
# 数据最后经过规范化层
return self.norm(x)
- 函数调用
# 测试 解码器
def dm_test_Decoder():
d_model = 512
vocab = 1000 # 词表大小是1000
# 输入x 是一个使用Variable封装的长整型张量, 形状是2 x 4
x = Variable(torch.LongTensor([[100, 2, 421, 508], [491, 998, 1, 221]]))
emb = Embeddings(d_model, vocab)
embr = emb(x)
dropout = 0.2
max_len = 60 # 句子最大长度
x = embr # [2, 4, 512]
pe = PositionalEncoding(d_model, dropout, max_len)
pe_result = pe(x)
x = pe_result # 获取位置编码器层 编码以后的结果
# 分别是解码器层layer和解码器层的个数N
size = 512
d_model = 512
head = 8
d_ff = 64
dropout = 0.2
c = copy.deepcopy
# 多头注意力对象
attn = MultiHeadedAttention(head, d_model)
# 前馈全连接层
ff = PositionwiseFeedForward(d_model, d_ff, dropout)
# 解码器层
layer = DecoderLayer(d_model, c(attn), c(attn), c(ff), dropout)
N = 6
# 输入参数与解码器层的输入参数相同
x = pe_result
# 产生编码器结果
en_result = demo238_test_Encoder()
memory = en_result
# 掩码对象
mask = Variable(torch.zeros(8, 4, 4))
# sorce掩码 target掩码
source_mask = target_mask = mask
# 创建 解码器 对象
de = Decoder(layer, N)
# 解码器对象 解码
de_result = de(x, memory, source_mask, target_mask)
print(de_result)
print(de_result.shape)
- 输出结果
tensor([[[ 0.1853, -0.8858, -0.0393, ..., -1.4989, -1.4008, 0.8456],
[-1.0841, -0.0777, 0.0836, ..., -1.5568, 1.4074, -0.0848],
[-0.4107, -0.1306, -0.0069, ..., -0.2370, -0.1259, 0.7591],
[ 1.2895, 0.2655, 1.1799, ..., -0.2413, 0.9087, 0.4055]],
[[ 0.3645, -0.3991, -1.2862, ..., -0.7078, -0.1457, -1.0457],
[ 0.0146, -0.0639, -1.2143, ..., -0.7865, -0.1270, 0.5623],
[ 0.0685, -0.1465, -0.1354, ..., 0.0738, -0.9769, -1.4295],
[ 0.3168, 0.6305, -0.1549, ..., 1.0969, 1.8775, -0.5154]]],
grad_fn=<AddBackward0>)
torch.Size([2, 4, 512])