2.4 解码器部分实现


学习目标

  • 了解解码器中各个组成部分的作用.
  • 掌握解码器中各个组成部分的实现过程.

  • 解码器部分:

    • 由N个解码器层堆叠而成
    • 每个解码器层由三个子层连接结构组成
    • 第一个子层连接结构包括一个多头自注意力子层和规范化层以及一个残差连接
    • 第二个子层连接结构包括一个多头注意力子层和规范化层以及一个残差连接
    • 第三个子层连接结构包括一个前馈全连接子层和规范化层以及一个残差连接

  • 说明:

    • 解码器层中的各个部分,如,多头注意力机制,规范化层,前馈全连接网络,子层连接结构都与编码器中的实现相同. 因此这里可以直接拿来构建解码器层.


2.4.1 解码器层


  • 学习目标:

    • 了解解码器层的作用.
    • 掌握解码器层的实现过程.

  • 解码器层的作用:

    • 作为解码器的组成单元, 每个解码器层根据给定的输入向目标方向进行特征提取操作,即解码过程.

  • 解码器层的代码实现:
# 使用DecoderLayer的类实现解码器层
class DecoderLayer(nn.Module):def __init__(self, size, self_attn, src_attn, feed_forward, dropout):"""初始化函数的参数有5个, 分别是size,代表词嵌入的维度大小, 同时也代表解码器层的尺寸,第二个是self_attn,多头自注意力对象,也就是说这个注意力机制需要Q=K=V, 第三个是src_attn,多头注意力对象,这里Q!=K=V, 第四个是前馈全连接层对象,最后就是droupout置0比率."""super(DecoderLayer, self).__init__()# 在初始化函数中, 主要就是将这些输入传到类中self.size = sizeself.self_attn = self_attnself.src_attn = src_attnself.feed_forward = feed_forward# 按照结构图使用clones函数克隆三个子层连接对象.self.sublayer = clones(SublayerConnection(size, dropout), 3)def forward(self, x, memory, source_mask, target_mask):"""forward函数中的参数有4个,分别是来自上一层的输入x,来自编码器层的语义存储变量mermory, 以及源数据掩码张量和目标数据掩码张量."""# 将memory表示成m方便之后使用m = memory# 将x传入第一个子层结构,第一个子层结构的输入分别是x和self-attn函数,因为是自注意力机制,所以Q,K,V都是x,# 最后一个参数是目标数据掩码张量,这时要对目标数据进行遮掩,因为此时模型可能还没有生成任何目标数据,# 比如在解码器准备生成第一个字符或词汇时,我们其实已经传入了第一个字符以便计算损失,# 但是我们不希望在生成第一个字符时模型能利用这个信息,因此我们会将其遮掩,同样生成第二个字符或词汇时,# 模型只能使用第一个字符或词汇信息,第二个字符以及之后的信息都不允许被模型使用.x = self.sublayer[0](x, lambda x: self.self_attn(x, x, x, target_mask))# 接着进入第二个子层,这个子层中常规的注意力机制,q是输入x; k,v是编码层输出memory, # 同样也传入source_mask,但是进行源数据遮掩的原因并非是抑制信息泄漏,而是遮蔽掉对结果没有意义的字符而产生的注意力值,# 以此提升模型效果和训练速度. 这样就完成了第二个子层的处理.x = self.sublayer[1](x, lambda x: self.src_attn(x, m, m, source_mask))# 最后一个子层就是前馈全连接子层,经过它的处理后就可以返回结果.这就是我们的解码器层结构.return self.sublayer[2](x, self.feed_forward)
  • 实例化参数:
# 类的实例化参数与解码器层类似, 相比多出了src_attn, 但是和self_attn是同一个类.
head = 8
size = 512
d_model = 512
d_ff = 64
dropout = 0.2
self_attn = src_attn = MultiHeadedAttention(head, d_model, dropout)# 前馈全连接层也和之前相同
ff = PositionwiseFeedForward(d_model, d_ff, dropout)

  • 输入参数:
# x是来自目标数据的词嵌入表示, 但形式和源数据的词嵌入表示相同, 这里使用per充当.
x = pe_result# memory是来自编码器的输出
memory = en_result# 实际中source_mask和target_mask并不相同, 这里为了方便计算使他们都为mask
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)
print(dl_result.shape)

  • 输出效果:
tensor([[[ 1.9604e+00,  3.9288e+01, -5.2422e+01,  ...,  2.1041e-01,-5.5063e+01,  1.5233e-01],[ 1.0135e-01, -3.7779e-01,  6.5491e+01,  ...,  2.8062e+01,-3.7780e+01, -3.9577e+01],[ 1.9526e+01, -2.5741e+01,  2.6926e-01,  ..., -1.5316e+01,1.4543e+00,  2.7714e+00],[-2.1528e+01,  2.0141e+01,  2.1999e+01,  ...,  2.2099e+00,-1.7267e+01, -1.6687e+01]],[[ 6.7259e+00, -2.6918e+01,  1.1807e+01,  ..., -3.6453e+01,-2.9231e+01,  1.1288e+01],[ 7.7484e+01, -5.0572e-01, -1.3096e+01,  ...,  3.6302e-01,1.9907e+01, -1.2160e+00],[ 2.6703e+01,  4.4737e+01, -3.1590e+01,  ...,  4.1540e-03,5.2587e+00,  5.2382e+00],[ 4.7435e+01, -3.7599e-01,  5.0898e+01,  ...,  5.6361e+00,3.5891e+01,  1.5697e+01]]], grad_fn=<AddBackward0>)
torch.Size([2, 4, 512])

  • 2.4.1 解码器层总结:

    • 学习了解码器层的作用:

      • 作为解码器的组成单元, 每个解码器层根据给定的输入向目标方向进行特征提取操作,即解码过程.

    • 学习并实现了解码器层的类: DecoderLayer

      • 类的初始化函数的参数有5个, 分别是size,代表词嵌入的维度大小, 同时也代表解码器层的尺寸,第二个是self_attn,多头自注意力对象,也就是说这个注意力机制需要Q=K=V,第三个是src_attn,多头注意力对象,这里Q!=K=V, 第四个是前馈全连接层对象,最后就是droupout置0比率.
      • forward函数的参数有4个,分别是来自上一层的输入x,来自编码器层的语义存储变量mermory, 以及源数据掩码张量和目标数据掩码张量.
      • 最终输出了由编码器输入和目标数据一同作用的特征提取结果.


2.4.2 解码器


  • 学习目标:

    • 了解解码器的作用.
    • 掌握解码器的实现过程.

  • 解码器的作用:

    • 根据编码器的结果以及上一次预测的结果, 对下一次可能出现的'值'进行特征表示.

  • 解码器的代码分析:
# 使用类Decoder来实现解码器
class Decoder(nn.Module):def __init__(self, layer, N):"""初始化函数的参数有两个,第一个就是解码器层layer,第二个是解码器层的个数N."""super(Decoder, self).__init__()# 首先使用clones方法克隆了N个layer,然后实例化了一个规范化层. # 因为数据走过了所有的解码器层后最后要做规范化处理. self.layers = clones(layer, N)self.norm = LayerNorm(layer.size)def forward(self, x, memory, source_mask, target_mask):"""forward函数中的参数有4个,x代表目标数据的嵌入表示,memory是编码器层的输出,source_mask, target_mask代表源数据和目标数据的掩码张量"""# 然后就是对每个层进行循环,当然这个循环就是变量x通过每一个层的处理,# 得出最后的结果,再进行一次规范化返回即可. for layer in self.layers:x = layer(x, memory, source_mask, target_mask)return self.norm(x)
  • 实例化参数:
# 分别是解码器层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 = 8

  • 输入参数:
# 输入参数与解码器层的输入参数相同
x = pe_result
memory = en_result
mask = Variable(torch.zeros(8, 4, 4))
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.9898, -0.3216, -1.2439,  ...,  0.7427, -0.0717, -0.0814],[-0.7432,  0.6985,  1.5551,  ...,  0.5232, -0.5685,  1.3387],[ 0.2149,  0.5274, -1.6414,  ...,  0.7476,  0.5082, -3.0132],[ 0.4408,  0.9416,  0.4522,  ..., -0.1506,  1.5591, -0.6453]],[[-0.9027,  0.5874,  0.6981,  ...,  2.2899,  0.2933, -0.7508],[ 1.2246, -1.0856, -0.2497,  ..., -1.2377,  0.0847, -0.0221],[ 3.4012, -0.4181, -2.0968,  ..., -1.5427,  0.1090, -0.3882],[-0.1050, -0.5140, -0.6494,  ..., -0.4358, -1.2173,  0.4161]]],grad_fn=<AddBackward0>)
torch.Size([2, 4, 512])

  • 2.4.2 解码器总结:

    • 学习了解码器的作用:

      • 根据编码器的结果以及上一次预测的结果, 对下一次可能出现的'值'进行特征表示.

    • 学习并实现了解码器的类: Decoder

      • 类的初始化函数的参数有两个,第一个就是解码器层layer,第二个是解码器层的个数N.
      • forward函数中的参数有4个,x代表目标数据的嵌入表示,memory是编码器层的输出,src_mask, tgt_mask代表源数据和目标数据的掩码张量.
      • 输出解码过程的最终特征表示.



2.5 输出部分实现


学习目标

  • 了解线性层和softmax的作用.
  • 掌握线性层和softmax的实现过程.

  • 输出部分包含:

    • 线性层
    • softmax层

线性层的作用

  • 通过对上一步的线性变化得到指定维度的输出, 也就是转换维度的作用.

softmax层的作用

  • 使最后一维的向量中的数字缩放到0-1的概率值域内, 并满足他们的和为1.

  • 线性层和softmax层的代码分析:
# nn.functional工具包装载了网络层中那些只进行计算, 而没有参数的层
import torch.nn.functional as F# 将线性层和softmax计算层一起实现, 因为二者的共同目标是生成最后的结构
# 因此把类的名字叫做Generator, 生成器类
class Generator(nn.Module):def __init__(self, d_model, vocab_size):"""初始化函数的输入参数有两个, d_model代表词嵌入维度, vocab_size代表词表大小."""super(Generator, self).__init__()# 首先就是使用nn中的预定义线性层进行实例化, 得到一个对象self.project等待使用, # 这个线性层的参数有两个, 就是初始化函数传进来的两个参数: d_model, vocab_sizeself.project = nn.Linear(d_model, vocab_size)def forward(self, x):"""前向逻辑函数中输入是上一层的输出张量x"""# 在函数中, 首先使用上一步得到的self.project对x进行线性变化, # 然后使用F中已经实现的log_softmax进行的softmax处理.# 在这里之所以使用log_softmax是因为和我们这个pytorch版本的损失函数实现有关, 在其他版本中将修复.# log_softmax就是对softmax的结果又取了对数, 因为对数函数是单调递增函数, # 因此对最终我们取最大的概率值没有影响. 最后返回结果即可.return F.log_softmax(self.project(x), dim=-1)

  • nn.Linear演示:
>>> m = nn.Linear(20, 30)
>>> input = torch.randn(128, 20)
>>> output = m(input)
>>> print(output.size())
torch.Size([128, 30])

  • 实例化参数:
# 词嵌入维度是512维
d_model = 512# 词表大小是1000
vocab_size = 1000

  • 输入参数:
# 输入x是上一层网络的输出, 我们使用来自解码器层的输出
x = de_result

  • 调用:
gen = Generator(d_model, vocab_size)
gen_result = gen(x)
print(gen_result)
print(gen_result.shape)

  • 输出效果:
tensor([[[-7.8098, -7.5260, -6.9244,  ..., -7.6340, -6.9026, -7.5232],[-6.9093, -7.3295, -7.2972,  ..., -6.6221, -7.2268, -7.0772],[-7.0263, -7.2229, -7.8533,  ..., -6.7307, -6.9294, -7.3042],[-6.5045, -6.0504, -6.6241,  ..., -5.9063, -6.5361, -7.1484]],[[-7.1651, -6.0224, -7.4931,  ..., -7.9565, -8.0460, -6.6490],[-6.3779, -7.6133, -8.3572,  ..., -6.6565, -7.1867, -6.5112],[-6.4914, -6.9289, -6.2634,  ..., -6.2471, -7.5348, -6.8541],[-6.8651, -7.0460, -7.6239,  ..., -7.1411, -6.5496, -7.3749]]],grad_fn=<LogSoftmaxBackward>)
torch.Size([2, 4, 1000])

小节总结

  • 学习了输出部分包含:

    • 线性层
    • softmax层

  • 线性层的作用:

    • 通过对上一步的线性变化得到指定维度的输出, 也就是转换维度的作用.

  • softmax层的作用:

    • 使最后一维的向量中的数字缩放到0-1的概率值域内, 并满足他们的和为1.

  • 学习并实现了线性层和softmax层的类: Generator

    • 初始化函数的输入参数有两个, d_model代表词嵌入维度, vocab_size代表词表大小.
    • forward函数接受上一层的输出.
    • 最终获得经过线性层和softmax层处理的结果.



Transformer解码器部分实现相关推荐

  1. 这么多年,终于有人讲清楚Transformer了

    作者 | Jay Alammar 译者 | 香槟超新星,责编 | 夕颜 来源 | CSDN(ID:CSDNnews) 注意力机制是一种在现代深度学习模型中无处不在的方法,它有助于提高神经机器翻译应用程 ...

  2. 复旦邱锡鹏团队:Transformer最新综述!

    Datawhale干货 编辑:Liyuan.杜伟,来源:机器之心 自提出至今,Transformer 模型已经在自然语言处理.计算机视觉以及其他更多领域「大展拳脚」,学界也提出了各种各样基于原始模型的 ...

  3. 华为诺亚最新视觉Transformer综述

    点击上方"视学算法",选择加"星标"或"置顶" 重磅干货,第一时间送达 来源丨机器之心 编辑丨极市平台 导读 华为诺亚方舟实验室联合北大和悉 ...

  4. 完全基于Transformer的目标检测器,ICLR匿名论文实现视觉、检测统一

    点击上方"视学算法",选择加"星标"或"置顶" 重磅干货,第一时间送达 来源丨机器之心 编辑丨极市平台 导读 一种新的集成视觉和检测 Tra ...

  5. Transformer模型有多少种变体?复旦邱锡鹏教授团队做了全面综述

    视学算法报道 转载自:机器之心 编辑:Liyuan.杜伟 自提出至今,Transformer 模型已经在自然语言处理.计算机视觉以及其他更多领域「大展拳脚」,学界也提出了各种各样基于原始模型的变体.但 ...

  6. Facebook AI的多任务多模态的统一Transformer

    点击上方"视学算法",选择加"星标"或"置顶" 重磅干货,第一时间送达 仅作学术分享,不代表本公众号立场,侵权联系删除 转载于:作者:Syn ...

  7. 视觉Transformer最新综述

    点击上方"视学算法",选择加"星标"或"置顶" 重磅干货,第一时间送达 ©PaperWeekly 原创 · 作者|张一帆 学校|华南理工大学 ...

  8. 最新视觉Transformer综述(2017-2020年)

    点击上方"视学算法",选择加"星标"或"置顶" 重磅干货,第一时间送达 作者丨坐化@知乎(已授权) 来源丨https://zhuanlan. ...

  9. 万能 Transformer,你应该知道的一切

    一.Transformer 概  述 Transformer模型由Google在2017年在 Attention Is All You Need[1] 中提出.该文使用 Attention 替换了原先 ...

最新文章

  1. 我是如何在天猫、蚂蚁金服、百度等大厂面试中被拒的 | 掘金技术征文
  2. 俄研发新无线传电系统 隔20cm保持80%传输效率
  3. WinAPI: GetLocalTime、SetLocalTime、SetSystemTime - 获取与设置系统时间
  4. 国二C语言文字选择程序选择,全国计算机等级考试二级C语言题型总结(二)——选择循环结构程序设计部分.doc...
  5. 2021天翼杯 密码官方wp
  6. linux 函数 增加用户,linux常用c函数用户组有哪些?
  7. 浅析C语言中assert的用法(转)
  8. 代码传奇丨美女黑客张婉桥的“爱丽丝奇遇记”
  9. 千万别强制停机!我嘴都气歪了!
  10. @ResponseStatus
  11. 流程图符号以及绘制流程图方法
  12. 高斯伪谱法 matlab,Gauss 高斯伪谱法求解的 ,希望对大家有用的!代码比较复杂,但是可以运行。 matlab 263万源代码下载- www.pudn.com...
  13. 2-Linux C语言指针与内存-学习笔记
  14. 【Unit 1】Python以及Pycharm的安装与设置
  15. Android自定义权限
  16. 微星主板节能模式怎么关闭_技嘉小雕、微星迫击炮、华硕电竞特工三款主板对比...
  17. CSS 颜色代码大全
  18. 免费下载卫星地图 高清卫星地图软件
  19. 程序员,对自己好一点
  20. android 夏令时,android 时间处理(夏令时)

热门文章

  1. 九龙证券|避险情绪升温,黄金上破2000大关,概念股逆市大涨
  2. Windows XP SP2 服务详解
  3. plusready html5,Android平台5+ API提前生效,支持在plusready事件前调用
  4. 注册交管12123服务器异常,交管12123服务器异常是怎么回事
  5. DNS部署和安全(图文解析)~千锋
  6. Win7系统x64正在准备再循环
  7. c++ 彻底搞懂 wchar_t WCHAR LPCSTR PCSTR TCHAR
  8. SSH初探:通过查看GPU的PID获取USER详细信息
  9. python中文件读取_如何从Python中的文件读取字节
  10. 移动信息化:移动运营商的新跨越