
【NLP】第十四章Transformer论文解读¶Transformer的论文名称是《Attention Is All You Need》,这篇论文是深度学习史上最重要的论文之一因为Transformer是继MLP、CNN、RNN之后的第四大深度学习基础架构所以原始论文非常有必要认真读一读。本篇整理了我精读过程的关注点当然核心的内容都在前面5个篇章展开详细写过了。本篇展示的是前面没有涉及到的边角知识点算是对前面篇章的一个补充。我本人是不擅长翻译的尤其是这种专业论文背后有大量的背景知识所以看不懂的同学还是建议从MLP开始一点点每个细节都扎实学透后再来理解这篇文章。此时你再看即使有些单词或者语法不是很明白你也可以猜的七七八八了。transformer是google于2017年提出的模型架构论文地址[1706.03762] Attention Is All You Need 大家可自行下载阅读。1、论文作者及作者的贡献的描述2、摘要部分3、结论部分4、引言部分5、背景部分扩展神经GPUExtended Neural GPU, ENGPU是一种基于卷积神经网络的神经网络模型专门设计用于高效地处理长序列的任务。它旨在减少传统循环神经网络在处理长序列时的计算开销通过并行化计算来加速训练和推理过程。 GPU 表示模型的设计灵感来源于 GPU 的并行计算能力但它并不指代实际的硬件。模型本身仍然是一种神经网络模型。ByteNet是一种用于序列建模的神经网络架构主要用于NLP任务。它是基于卷积神经网络CNN设计的它结合了卷积操作和残差连接使用卷积层来处理序列数据并且通过残差连接帮助在训练时保持梯度稳定从而捕捉长距离的依赖。卷积网络中有一个重要的概念就是感受野在我专门讲卷积层时画了几个非常通俗的图给大家展示了感受野的概念。所以清楚感受野的同学自然也就知道感受野再大也不可能覆盖全部序列。因为感受野就像一个滑窗的效果有的窗口大点有的窗口小点但是不管大小它的本质都是窗口。那既然是窗口你就别想通过窗口看到序列的全部了因为上一个窗口的信息是不传递到小一个窗口的。也所以卷积网络是很难处理长序列数据的。但是注意力机制是一个注意力层就计算了一个序列中的全部样本所以注意力层天然就适合处理长序列数据。但同时这也是注意力模型的代价所在代价就是计算量啊假如你一个序列是10000个样本你的注意力层是不是得学一个10000x10000的注意力矩阵啊这是一个理解维度。另外一个理解维度是卷积网络虽然是独立的小滑窗看序列的但它是可以有一系列的卷积核组成就是卷积是多通道看这个序列那不同通道就会看到不同的数据模式这是卷积的强大之处。所以Transformer为了也能有卷积的这种效果Transformer就加入了多头的概念就是多头注意力机制。所以多头其实就是为了模拟卷积层的多通道的效果。6、模型架构上面的架构图基本就是Transformer的全部内容了。但是你只看图只看3.1的两段英文你是完全理解不了Transformer的因为这张图背后后非常多的知识点但凡你有思维断点你就串不起来。所以建议回看我前面写的七、八万字的Transfomer解读再回看论文你就可以踏实体会论文的内容了。所以本文并不是再重复写前面已经写过的内容。本部分是结合论文补齐前面没有提到的点。所以后面我就自己挑点内容写了算是对之前写的一个点对点的补齐:7、两个embedding层和最后的linear层共享参数意思就是数据集WMT2014这个英德数据集中的源文本source(英文语句)和目标文本target(德文语句)经过bpe编码后最小的单元是subword,而这些subword中有很多是可以共享语义的。因为英语和德语同属日耳曼语族所以这些subword很多是可以共享语义的。也就是使用bpe生成的词表是一个大的共享词表做词嵌入时只有对应语言的embeeding被激活。这样做的意义是当目标语言和源语言有相同subword的时候可以共享语义信息。所以Transformer论文中的两个embedding层是共享参数的。但对于中英翻译来说的话可能意义不大因为没有相同的subword权重共享会在做softmax时加大计算量所以实际中是否使用还要权衡。至于softmax前面的linear层因为这个linear层的输出是tgt字典的长度。或者说这个linear层是把embedding后的词向量再映射会到标签编码所以这个linear层和权重结构和embedding层的权重结构是一样的而且功能是互逆的所以这两个层也是可以共享参数的。当然linear的bias就无法共享了。共享参数的好处就是减少参数量了模型会好训练一点。8、Transformer的计算效率所以架构的设计是综合你的数据、硬件、效果来综合评估和抉择的。9、训练部分10、模型超参数的设置补充代码相关的知识点1、PyTorch中Tensor和tensor的区别1torch.Tensor()是python类更明确地说是默认张量类型torch.FloatTensor()的别名torch.Tensor([1,2])会调用Tensor类的构造函数__init__生成单精度浮点类型的张量。2torch.tensor()则是python函数函数原型是torch.tensor(data, dtypeNone, deviceNone, requires_gradFalse), 其中data可以是list, tuple, NumPy ndarray, scalar和其他类型。 torch.tensor会从data中的数据部分做拷贝,而不是直接引用根据原始数据类型生成相应的torch.LongTensor、torch.FloatTensor和torch.DoubleTensor。所以二者底层实现是不一样的。因为函数调用要拷贝参考而类属性则可以直接引用所以使用类比使用函数性能会好一点。其他就没必要深究了建议使用torch.Tensor()。2、torch.autograd.Variable的用法将pytorch中的张量封装成Variable对象。Variable对象将张量作为其内部状态主要作用就是保存张量数据。此外还提供一系列有用的方法和属性来简化神经网络模型的构建和训练过程使得神经网络编程更加简单、直观和高效。比如1Variable有requires_grad()属性可以自动计算梯度autograd使得在反向传播过程中能够自动计算损失函数对模型参数的梯度而volatileTrue的节点不会求导即使requires_gradTrue也不会进行反向传播对于不需要反向传播的情景该参数可以实现一定速度的提升并节省一半的显存因为其不需要保存梯度。2Variable有save()方法和loadstate_dict()方法用来保存和恢复模型的状态使得模型的训练和预测过程可以方便地进行断点续传3Variable 可以进行数据增强data augmentation使得模型能够在训练过程中更好地泛化。3、pytorch中backward计算梯度的过程如果Tensor是非标量(non-scalar)的(即是说Y中有不止一个y即Y[y1,y2,…])且requires_gradTrue。那么backward函数需要指定gradient它的形状应该和Variable的长度匹配。因为gradient的长度体与Y的长度一直才能保存每一个yi的梯度值啊。关于梯度、关于正向传播、反向传播、计算图等这些概念不是特别清楚的同学建议参考【深度学习】第四章反向传播-梯度计算-更新参数-CSDN博客4、unsqueeze和squeeze的升维降维5、nn.Module中register_buffer用法register_buffer是Module类中的一个方法用于记录不需要计算梯度但要跟随模型参数一起保存、加载或者移动(cuda)的变量。和BatchNorm中的均值running_mean和方差running_var类似都是不需要被计算梯度但是需要保存在模型中。register_buffer(name, tensor, persistentTrue)name(str)字符串指定被调用的名字。tensor(Tensor or None)初始化该注册缓冲器张量如果为None则不会保存在模型中只是暂存。persistent(default: True)True则可以跟随模型被保存model.save_state_dict()和加载model.load_state_dict()6、一些计算函数7、画图技巧8、制作掩码矩阵当调用tensor.masked_fill()时PyTorch会遍历掩码中的每个元素进行保留或替换。但是masked_fill操作是基于C/C的实现因此在处理大规模数据时性能较高。9、手动构建一个Embedding类来实现文本嵌入层10、字典11、一些细节