ResNet、Transformer、GAN、BERT
一、 ResNet
论文地址:Deep Residual Learning for Image Recognition
参考:bilibili视频《撑起计算机视觉半边天的ResNet【论文精读】》、动手深度学习《残差网络ResNet》、
1.0 摘要,论文导读
摘要主要内容:
深度神经网络很难训练,我们使用residual(残差结构)使得网络训练比之前容易很多在ImageNet上使用了152层的ResNet,比VGG多8倍,但是计算复杂度更低,最终赢下了ImageNet2015的分类任务第一名,并演示了如何在cifar-10上训练100-1000层的网络。(通常赢下ImageNet比赛且提出很不一样网络架构、方法的文章会被追捧。)
对很多任务来说,深度是非常重要的。我们仅仅是把之前的网络换成残差网络,在coco数据集上就得到了28%的改进。同样也赢下了ImageNet目标检测、coco目标检测和coco segmentation的第一名。
主要图表:

上面这张图是没有使用残差结构的网络,更深的层训练误差比浅层更高,即深层网络其实是训练不动的。下面这张图,是是否使用resnet结构的网络效果对比图。可以看到右侧使用残差结构后,34层的网络训练和测试的误差都更低。
18 layers 27.94 27.88
34 layers 28.54 25.03

后面还会有不同结构的resnet网络和VGG等其他网络的效果对比图。
1.1 导论
1.1.1 为什么提出残差结构?
深度卷积神经网络是非常有效的,因为可以堆叠很多层,不同层可以表示不同level的特征。但是学一个好的网络,就是简简单单的把所有网络堆在一起就行了吗?如果这样,网络做深就行了。
我们知道,网络很深的时候,容易出现梯度消失或者梯度爆炸,解决办法之一是一个好的网络权重初始化,使权重不能太大也不能太小;二是加入一些normalization,比如BN。这样可以校验每个词之间的输出,以及梯度的均值和方差,这样比较深的网络是可以训练的(可以收敛)。但同时有一个问题是,深层网络性能会变差,也就是精度会变差。
深层网络性能变差,不是因为网络层数多、模型变复杂而过拟合,因为训练误差也变高了。那为什么会这样呢?从理论上来说,往一个浅层网络中加入一些层,得到一个深一些的网络,后者的精度至少不应该变差。因为后者至少可以学成新加的层是identity mapping,而其它层直接从前者复制过来。但是实际上做不到,SGD优化器无法找到这个比较优的解。
identity mapping可以理解成恒等映射吧,也就是网络输入x,输出也是x。网络权重简单学成输入特征的1/n。
所以作者提出,显式地构造一个identity mapping,使得深层模型的精度至少不会变得更差。作者将其称为deep residual learning framework。
假设我们要学的是 ,在原有层上 添加一些新的层时,新的层不是直接学 ,而是学习 ,这部分用表示。(其中,是原有层的输出。)即,新加入的层不用全部重新学习,而是学习原来已经学习到的和真实的之间的残差就行 。最后模型的输出是。这种新加入的层就是residual,结构如下图所示:

在数学上就是直接相加,在神经网络中是通过shortcut connections’实现(shortcut就是跳过一个或多个层,将输入直接加到这些跳过的层的输出上)。shortcut其实做的是一个identity mapping(恒等映射),而且这个操作不需要学习任何参数,不增加模型的复杂度。就多了一个加法,也不增加计算量,网络结构基本不变,可以正常训练。
1.1.2 实验验证
接下来作者在imagenet上做了一系列实验进行验证。结果表明,加了残差的网络容易优化,而且网络堆的更深之后,精度也会提高,所以赢下了比赛。在cifar-10上,作者尝试了训练超过1000层的网络。至此,论文的核心就讲完了,下面就是ResNet网络的设计。
1.2 相关工作
其实ResNet不是第一个提出
residual的。最早的线性模型的解法,就是通过不断迭代residual来求解的,而且在机器学习中,GBDT中,就是通过残差residual来不断学习的,把弱分类器叠加起来,成为一个强的分类器。只不过GBDT是在label上做残差,ResNet是在特征上做残差。ResNet也不是第一个提出
shortcut的,比如在highway networks中就有用到shortcut,但不是直接做加法,而是更复杂一些。一篇文章之所以是经典,不一定是原创的提出很多东西,而有可能是把很多东西很巧妙的放在一起,能很好地解决问题;甚至大家都不记得之前谁还做过类似的工作。所以很多想法前人大多就想过了发表了,但是没关系, 可以是用同一个东西解决新的问题,旧的技术有新的应用新的意义。
ResNet34比起VGG19,计算复杂度更低,只有前者的18%。其它是一些训练的细节,学习率优化器等等之类,就不细讲了。
1.3 实验部分
1.3.1 不同配置的ResNet结构

- 网络输入是ImageNet图片,短边在[256,480]中随机选取,然后resize到224×224尺寸,输入网络
- conv2_x:表示第二个卷积模块,x表示模块里有很多层。
- :[]内的是一个残差块,其卷积核大小为3*3,channel=64。×3表示有两个这样的残差层。
对照下面ResNet34结构图:(3+4+6+3)=16个残差模块,每个模块两层卷积层。再加上第一个7×7卷积层和最后一个全连接层,一共是34层。

1.3.2 残差结构效果对比
从下图可以看到有残差模块,网络收敛会更快,而且精度会更好。

1.3.3 残差结构中,输入输出维度不一致如何处理
A. pad补0,使维度一致;
B. 维度不一致的时候,使其映射到统一维度,比如使用全连接或者是CNN中的1×1卷积(输出通道是输入的两倍)。
C. 不管输入输出维度是否一致,都进行投影映射。
下面作者对这三种操作进行效果验证。从下面结果可以看到,B和C效果差不多,都比A好。但是做映射会增加很多复杂度,考虑到ResNet中大部分情况输入输出维度是一样的(也就是4个模块衔接时通道数会变),作者最后采用了方案B。

1.3.4 深层ResNet引入瓶颈结构Bottleneck

在ResNet-50及以上的结构中,模型更深了,可以学习更多的参数,所以通道数也要变大。比如前面模型配置表中,ResNet-50/101/152的第一个残差模块输出都是256维,增加了4倍。
如果残差结 构还是和之前一样,计算量就增加的太多了(增加16倍),划不来。所以重新设计了Bottleneck结构,将输入从256维降为64维,然后经过一个3×3卷积,再升维回256维。这样操作之后,复杂度和左侧图是差不多的。这也是为啥ResNet-50对比ResNet-34理论计算量变化不大的原因。(实际上1×1卷积计算效率不高,所以ResNet-50计算还是要贵一些)

1.4 代码实现
resnet中残差块有两种:(use_1x1conv=True/False)
- 步幅为2 ,高宽减半,通道数增加。所以shortcut连接部分会加一个1×1卷积层改变通道数
- 步幅为1,高宽不变

残差块代码实现:
import torch
from torch import nn
from torch.nn import functional as F
from d2l import torch as d2l
class Residual(nn.Module): #@save
def __init__(self, input_channels, num_channels,
use_1x1conv=False, strides=1):
super().__init__()
self.conv1 = nn.Conv2d(input_channels, num_channels,
kernel_size=3, padding=1, stride=strides)
self.conv2 = nn.Conv2d(num_channels, num_channels,
kernel_size=3, padding=1)
if use_1x1conv:
self.conv3 = nn.Conv2d(input_channels, num_channels,
kernel_size=1, stride=strides)
else:
self.conv3 = None
self.bn1 = nn.BatchNorm2d(num_channels)
self.bn2 = nn.BatchNorm2d(num_channels)#每个bn都有自己的参数要学习,所以需要定义两个
def forward(self, X):
Y = F.relu(self.bn1(self.conv1(X)))
Y = self.bn2(self.conv2(Y))
if self.conv3:
X = self.conv3(X)
Y += X
return F.relu(Y)
resnet18:类似VGG和GoogLeNet,但是替换了resnet块。一般用resnet34或者50,很少上100,除非刷榜。

Resnet18代码:
b1 = nn.Sequential(nn.Conv2d(1, 64, kernel_size=7, stride=2, padding=3),
nn.BatchNorm2d(64), nn.ReLU(),
nn.MaxPool2d(kernel_size=3, stride=2, padding=1))
def resnet_block(input_channels, num_channels, num_residuals,
first_block=False):
blk = []
for i in range(num_residuals):
if i == 0 and not first_block:
blk.append(Residual(input_channels, num_channels,
use_1x1conv=True, strides=2))
else:
blk.append(Residual(num_channels, num_channels))
return blk
b2 = nn.Sequential(*resnet_block(64, 64, 2, first_block=True))
b3 = nn.Sequential(*resnet_block(64, 128, 2))
b4 = nn.Sequential(*resnet_block(128, 256, 2))
b5 = nn.Sequential(*resnet_block(256, 512, 2))#resnet_block是一个列表,*表示全部展开
net = nn.Sequential(b1, b2, b3, b4, b5,
nn.AdaptiveAvgPool2d((1,1)),
nn.Flatten(), nn.Linear(512, 10))
1.5 结论
ResNet就是在CNN主干上加了残差连接,这样如果新加的层训练效果不好的话,至少可以fallback变回简单模型,所以精度不会变差。
在现在来看,ResNet训练的比较快,是因为梯度保持的比较好。因为新加的层容易导致梯度消失(或者梯度爆炸),但是加了残差连接,梯度多了一部分(之前层的梯度),这样不管加了多深,梯度会保持的比较大(主要是不会出现梯度消失,学不动),不会太快收敛,SGD跑得多就训练的比较好。(SGD的精髓就是,梯度幅值稳定(下限为1)且方向更合理,使SGD能持续更新参数,收敛速度加快,同时噪声帮助逃离局部极小)

为什么在cifar-10这样一个小的数据集上(32*32图片5w张)训练1202层的网络,过拟合也不是很厉害。为何transformer那些模型几千亿的参数不会过拟合,李沐认为是加了残差连接之后,模型内在复杂度大大降低了。(理论上模型加一些层,模型也至少可以将后面的层学成恒等映射,使精度不会变差。但实际上没有引导做不到这一点。所以本文才会显示的把残差结构加进去,使模型能够更容易的训练出来。比如后面层都是0,前面一些层才学到东西,也就是更容易训练出一个简单模型来拟合数据,所以加入残差连接等于是模型复杂度降低了)
二、Attention is All You Need
论文地址《Attention is All You Need》
bilibili视频《Transformer论文逐段精读【论文精读】》
2.0 摘要
主流的序列转换模型都是基于复杂的循环或卷积神经网络,这个模型包含一个编码器和一个解码器。具有最好性能的模型在编码和解码之间通过一个注意力机制链接编解码器。我们提出了一个新的简单网络结构——Transformer,其仅仅是基于注意力机制,而完全不需要之前的循环或卷积。在两个机器翻译任务上的实验表明,该模型具有更好的性能,同时并行度更好,并且训练时间更少。(泛化到其它任务效果也不错)
在WMT 2014英语到德语翻译任务上,我们的模型达到了28.4BLEU,比之前最好的结果提高了2BLEU。 在WMT 2014英语到法语翻译任务上,我们的模型在8个GPU上训练3.5天后,所得到的单个模型获得了41.8BLEU分数。我们在大型和有限的训练数据中,通过将其成功应用于英语句法解析,表明了Transformer可以很好地适用于其他任务。
可以看到这篇文章最开始只是针对机器翻译来写的,
transformer在机器翻译上效果也很好。但是随着bert、GPT等把这种架构用在更多的NLP任务上,甚至后面CV和video等也可以使用注意力机制,整个工作就火出圈了。
2.1 结论
本文介绍了Transformer,这是第一个完全基于注意力的序列转换模型,用多头自注意力(multi-headed self-attention)代替了 encoder-decoder 架构中最常用的循环层。
对于翻译任务,Transformer可以比基于循环或卷积层的体系结构训练更快。在WMT 2014 English-to-German和and WMT 2014 English-to-French翻译任务中,我们取得了最好的结果。在前面的任务中,我们最好的模型甚至胜过以前发表过的所有整合模型。
2.2 导论
序列建模和转换问题(如机器翻译)最新方法是LSTM和GRN等。后面许多研究都围绕循环语言模型和编码器-解码器体系结构进行。
循环网络模型通常是考虑了输入和输出序列的中字符位置的计算。当前时刻隐藏状态ht,是由上一时刻隐藏状态ht−1和 t时刻输入共同决定的。(把之前的信息都放在隐藏状态里,一个个传递下去,是RNN处理时序信息的关键)。这种固有的时序模型难以并行化处理,计算性能就很差。这些年做了一些并行化改进,但是问题依然存在。
另外还存在长距离衰减问题,解码阶段,越靠后的内容,翻译效果越差。除非你把ht维度设置的很高,可以把每一个时间步的信息都存下来。但这样会造成内存开销很大。
attention在此之前,已经成功的应用在encoder-decoder 架构中,但主要是用在如何把编码器的信息有效的传递给解码器,所以是和RNN一起使用的。
本文提出的Transformer,不再使用循环神经层,而是纯基于注意力机制,来构造输入和输出之间的全局依赖关系。Transformer可以进行更多的并行化,训练时间更短但翻译效果更好。
2.3 背景
使用卷积神经网络替换循环神经网络,并行计算所有输入和输出位置的隐藏表示,是扩展神经GPU,ByteNet和ConvS2S的基础,因为这样可以减少时序计算。但是CNN对长序列难以建模(因为卷积计算时,卷积核/感受野比较小,如果序列很长,需要使用多层卷积才可以将两个比较远的位置关联起来)。但是使用Transformer的注意力机制的话,每次(一层)就能看到序列中所有的位置,就不存在这个问题。
关联来自两个任意输入或输出位置的数据所需的操作数量,随着距离增长,对于ConvS2S呈线性,对于ByteNet呈对数,而对于Transformer是常数 ,因为一次就看到了。
但是卷积的好处是,输出可以有多个通道,每个通道可以认为是识别不同的模式,作者也想得到这种多通道输出的效果,所以提出了Multi-Head Attention多头注意力机制。(模拟卷积多通道输出效果)
Self-attention,有时称为intra-attention,是一种关联单个序列的不同位置以计算序列表示的关联机制。在此之前已成功用于多种任务。但据我们所知,Transformer是第一个完全依靠self-attention,而不使用卷积或循环的的encoder-decoder 转换模型。
2.4 模型架构
大部分神经序列转换模型都使用encoder-decoder 结构 (引用)。编码器把一个输入序列映射到一个连续的表示中。解码器对z中的每个元素,生成输出序列,一个时间步生成一个元素。在每一步中,模型都是自回归的(auto-regressive)(引用),在生成下一个结果时,会将先前生成的结果加入输入序列来一起预测。(自回归模型的特点,过去时刻的输出可以作为当前时刻的输入)
编码器和解码器序列可以不一样长,且编码器可以一次看到整个序列,但是解码器是一步步输出的。
Transformer 遵循这种整体架构,对编码器和解码器使用堆叠的自注意力和逐点全连接层,分别如下图的左半部分和右半部分所示。

- 图画得好,一张图能搞定所有东西,所以画图是一个基础技能。
Outputs(shifted right):解码器在时刻其实是没有输入的,其输入是编码器的输出,所以这里写的是output,shifted right就是逐个右移的意思。Nx:模块堆叠N次
2.4.1 编码器和解码器
代码参考
编码器:编码器由N=6个相同encoder层堆栈组成。每层有两个子层。
multi-head self-attention- FFNN层(前馈神经网络层,Feed Forward Neural Network),其实就是MLP,为了fancy一点,就把名字起的很长。
- 两个子层都使用残差连接(residual connection),然后进行层归一化(layer normalization)。
- 每个子层的输出是
LayerNorm(x + Sublayer(x)),其中Sublayer(x)是当前子层的输出。 - 为了简单起见,模型中的所有子层以及嵌入层的向量维度都是(如果输入输出维度不一样,残差连接就需要做投影,将其映射到统一维度)。(这和之前的CNN或MLP做法是不一样的,之前都会进行一些下采样)
这种各层统一维度使得模型比较简单,只有N和两个参数需要调。这个也影响到后面一系列网络,比如bert和GPT等等。
解码器:解码器同样由 N=6个相同的decoder层堆栈组成,每个层有三个子层。
Masked multi-head self-attention:在解码器里,Self Attention 层只允许关注到输出序列中早于当前位置之前的单词。具体做法是:在 Self Attention 分数经过 Softmax 层之前,使用attention mask,屏蔽当前位置之后的那些位置。所以叫Masked multi-head self Attention。(对应masked位置使用一个很大的负数-inf,使得softmax之后其对应值为0)Encoder-Decoder Attention:编码器输出最终向量,将会输入到每个解码器的Encoder-Decoder Attention层,用来帮解码器把注意力集中到输入序列的合适位置。FFNN
与编码器类似,每个子层都使用残差连接,然后进行层归一化。假设一个 Transformer 是由 2 层编码器和两层解码器组成的,如下图所示:

为什么这里使用LN而不是BN?
Batch Normalization:在特征d/通道维度做归一化,即归一化不同样本的同一特征。缺点是:
- 计算变长序列时,变长序列后面会pad 0,这些pad部分是没有意义的,这样进行特征维度做归一化缺少实际意义。
- 序列长度变化大时,计算出来的均值和方差抖动很大。
- 预测时使用训练时记录下来的全局均值和方差。如果预测时新样本特别长,超过训练时的长度,那么超过部分是没有记录的均值和方差的,预测会出现问题。
Layer Normalization:在样本b维度进行归一化,即归一化一个样本所有特征。
- NLP任务中一个序列的所有token都是同一语义空间,进行LN归一化有实际意义
- 因为实是在每个样本内做的,序列变长时相比BN,计算的数值更稳定。
- 不需要存一个全局的均值和方差,预测样本长度不影响最终结果。

**为什么MLP Block先升维再降维?**神经网络中线性连接可以写成。其中三者维度分别是m×1、m×n、n×1。
- m>n:升维,将特征进行各种类型的特征组合,提高模型分辨能力
- m<n:降维,去除区分度低的组合特征。所以一般神经网络都是先做宽再做窄。
2.4.2 注意力机制
attention函数可以被描述为将query和一组key-value对映射到输出,其中query、key、value和输出都是向量。输出被计算为value的加权求和,所以输出和value的维度一致。每个value的权重由query与对应key计算所得。(不同注 意力机制有不同的算法)
2.4.2.1 缩放的点积注意力(Scaled Dot-Product Attention)
缩放的点积注意力:
- 其输入为query、key(维度是)以及values(维度是)。
- 计算query和所有key的点积,得到两个向量的相似度(结果越大相似度越高);然后对每个点积结果除以 ,
- 点积结果输入softmax函数获得value的权重。
- 最后对value进行加权求和
在实践中,我们同时计算一组query的attention函数,并将它们组合成一个矩阵。key和value也一起组成矩阵和。 我们计算的输出矩阵为:
K、V 矩阵的序列长度是一样的(加权求和),而 Q 矩阵的序列长度可以和前两者不一样;这种情况发生在:解码器部分的Encoder-Decoder Attention层中,Q 矩阵是来自解码器输出tgt,而 K、V 矩阵则是来自编码器最后的输出memory。即
tgt2 = self.multihead_attn(tgt, memory, memory, attn_mask=memory_mask,key_padding_mask=memory_key_padding_mask)[0]但是Q和K的维度必须一样,因为要计算点积。

有两个最常用的attention函数:
- 加法attention(cite):,使用具有单个隐层的前馈网络计算,q和k维度不一致也可以进行;
- 上面提到的点积(乘法)attention
除了缩放因子之外,点积Attention跟我们的算法一样(所以作者的注意力叫缩放点积注意力)。虽然理论上点积attention和加法attention复杂度相似,但在实践中,点积attention可以使用高度优化的矩阵乘法来实现,因此点积attention计算更快、更节省空间。 当dk的值比较小的时候,这两个机制的性能相差相近,当dk比较大时,加法attention比不带缩放的点积attention性能好。我们怀疑,维度dk很大时,点积结果也变得很大,将softmax函数推向具有极小梯度的区域。为了抵消这种影响,我们将点积缩小 倍。
2.4.2.2 多头注意力
其中映射由权重矩阵完成:,,我们采用个平行attention层或者叫head。对于这些head中的每一个,我们使用,总计算成本与具有全部维度的单个head attention相似。

- 输入 和8组权重矩阵, 相乘,得到 8 组 Q, K, V 矩阵。进行attention计算,得到 8 组 Z 矩阵(假设head=8)
- 把8组矩阵拼接起来,乘以权重矩阵,将其映射回 d 维向量(相当于多维特征进行汇聚),得到最终的矩阵 Z。这个矩阵包含了所有 attention heads(注意力头) 的信息。
- 矩阵Z会输入到 FFNN层。(前馈神经网络层接收的也是 1 个矩阵,而不是8个。其中每行的向量表示一个词)
使用多头自注意力的好处:
- 多语义匹配:本身缩放点积注意力是没什么参数可以学习的,就是计算点积、softmax、加权和而已。但是使用
Multi-head attention之后,投影到低维的权重矩阵, ,是可以学习的,而且有h=8次学习机会。使得模型可以在不同语义空间下学到不同的的语义表示,也扩展了模型关注不同位置的能力。类似卷积中多通道的感觉。 - 例如,“小明养了一只猫,它特别调皮可爱,他非常喜欢它”。“猫”从指代的角度看,与“它”的匹配度最高,但从属性的角度看,与“调皮”“可爱”的匹配度最高。标准的 Attention 模型无法处理这种多语义的情况。
- 注意力结果互斥:自注意力结果需要经过softmax归一化,导致自注意力结果之间是互斥的,无法同时关注多个输人。 使用多组自注意力模型产生多组不同的注意力结果,则不同组注意力模型可能关注到不同的输入,从而增强模型的表达能力。
2.4.2.3 注意力在模型中的应用
Transformer中用3种不同的方式使用multi-head attention:
multi-head self attention:标准的多头自注意力层,用在encoder的第一个多头自注意力层。所有key,value和query来自同一个地方,即encoder中前一层的输出。在这种情况下,encoder中的每个位置都可以关注到encoder上一层的所有位置。masked-self-attention:用在decoder中,序列的每个位置只允许看到当前位置之前的所有位置,这是为了保持解码器的自回归特性,防止看到未来位置的信息encoder-decoder attention:用于encoder block的第二个多头自注意力层。query来自前面的decoder层,而keys和values来自encoder的输出memory。这使得decoder中的每个位置都能关注到输入序列中的所有位置。
encoder-decoder attention层可以使解码器在每个时间步,把注意力集中到输入序列中感兴趣的位置。比如英译中‘hello world’,解码器在输出‘你’的时候,解码器的输入q对'hello'的相似度应该是最高的,这样模型就将注意力主要集中在'hello'上,即生成单词时更关注源语言序列中更相关的词。(这就是attention如何在encoder传递信息到decoder时起到作用)
2.4.3 基于位置的前馈神经网络(Position-wise Feed-Forward Networks)
编码器和解码器中的每个层都包含一个全连接的前馈网络,该前馈网络分别且相同地应用于每个位置。该前馈网络包括两个线性变换,并在两个线性变换中间有一个ReLU激活函数。
Position就是序列中每个token,Position-wise 就是把MLP对每个token作用一次,且作用的是同一个MLP。说白了就是MLP只作用于最后一个维度d=512。
因为前面的attention层以及抓取了输入序列的相关信息,并做了一次汇聚(拼接后W映射回d维)。所以attention层结果已经有了序列中我感兴趣的信息,所以后面在做MLP投影映射到想要的语义空间时,只需要对每个position(token)单独做MLP就行。
从attention抽取序列信息到MLP映射到需要的语义空间(非线性变换),就整个是transformer的处理信息的基础过程。
尽管两层都是线性变换,但它 们在层与层之间使用不同的参数。另一种描述方式是两个内核大小为1的卷积。 输入和输出的维度都是 , 内层维度是。(也就是第一层输入512维,输出2048维;第二层输入2048维,输出512维)
对比transformer和RNN,发现两者都是使用MLP来进行语义空间的转换,但区别是二者传递信息的方式不一样:(大概画了个示意图,见谅)

RNN是把上一时刻信息作为输入(和t时刻输入一起),传递给当前时刻,并用MLP做语义转换。Transformer是通过attention层直接关联到全局的序列信息,然后用MLP做语义转换。
2.4.4 词嵌入和Softmax
我们使用学习到的embedding将输入token和输出token转换为d_{\text{model}}维的向量。我们还使用普通的线性变换和softmax函数将解码器输出转换为预测的下一个token的概率。在我们的模型中,输入输出两个嵌入层,和pre-softmax线性变换共享相同的权重矩阵(这样训练起来简单一些)。最后我们将这些权重乘以(比如512)。
这是因为一般会把一个向量的L2Norm学到接近1,这样向量维度越大,这样学到的权重值就会很小。但是位置编码是不会这样学成L2Norm(L2范数)接近1的。所以把权重乘上之后,token embedding和位置编码Positional Encoding才接近统一量级。(都在-1到1之间)
2.4.5 位置编码(Positional Encoding)
Attention计算时本身是不考虑位置信息的,这样序列顺序变化结果也是一样的。所以我们必须在序列中加入关于词符相对或者绝对位置的一些信息。
为此,我们将“位置编码”添加到token embedding中。二者维度相同(例如,所以可以相加。有多种位置编码可以选择,例如通过学习得到的位置编码和固定的位置编码。
在这项工作中,我们使用不同频率的正弦和余弦函数:
其中 是位置, 是维度。也就是说,位置编码的每个维度对应于一个正弦曲线。 这些波长形成一个从 到 的集合级数。我们选择这个函数是因为我们假设它会让模型很容易学习对相对位置的关注,因为对任意确定的偏移, 可以表示为 的线性函数。最终编码向量每个元素值都是在-1到1之间。
此外,我们会将编码器和解码器堆栈中的embedding和位置编码的和再加一个dropout。对于基本模型,我们使用的dropout比例是。
2.5 为什么使用自注意力机制
本节,我们比较self-attention与循环层和卷积层的各个方面,我们使用self-attention是考虑到解决三个问题。
-
每层计算的总复杂度,越少越好
-
顺序计算量,越少代表并行度越高。(顺序计算量就是下一步需要前面多少步计算完成)
-
网络中长距离依赖之间的路径长度
影响长距离依赖性能力的一个关键因素是前向和后向信号在网络中传播的路径长度。输入和输出序列中任意位置之间的这些路径越短,学习长距离依赖性就越容易。因此,我们还比较了由不同图层类型组成的网络中任意两个输入和输出位置之间的最大路径长度。

上图n是序列长度,d是token维度。
-
Attention:
-
计算复杂度:矩阵Q*K,两个矩阵都是n行d列,所以相乘时复杂度是,其它还有一些计算量但影响不大;
-
顺序计算量:矩阵里面并行度是很高的,整个计算主要就是矩阵乘法,所以可以认为顺序计算量就是;
-
最大路径长度:也就是从一个点关联到任何一个点的路径长度。attention是一次看到整个序列,所以只需要一次操作,复杂度为
其它的暂时不写了。k就是卷积核大小,一般是3、5之类的;而n和d现在的模型都是做到几百几千,所以可以认为前三种操作,计算复杂度差不多,但是并行度是attention和卷积更好;且attention在信息的融合上更好(最大路径长度=1)。
实际上attention对模型的假设更少,导致模型需要更多的数据和更大的 模型才能训练到和RNN或CNN差不多的效果。所以现在基于transformer的模型都是很大,训练起来很贵。
2.6 实验(可以跳过)
2.6.1训练数据和批处理
我们在标准的WMT 2014 English-German dataset上进行了训练,其中包含约450万个句子对。这些句子使用byte-pair编码进行编码,源语句和目标语句共享大约37000个词符的词汇表。对于英语-法语翻译,我们使用大得多的WMT 2014 English-French dataset,它包含3600万个句子,并将词符分成32000个word-piece词汇表。序列长度相近的句子一起进行批处理。每个训练批处理的句子对包含大约25000个源词符和25000个目标词符。
bpe编码,是因为英语/德语中有很多ing之类的词根,又或者一个动词有几种形式。如果直接使用token进行表示,词表就太大了。bpe就可以把词根提取出来,这样词表会小很多,而且还能表示不同时态等等这些相关信息。
共用词表可以使编码器和解码器共用一个embedding,权重共享,模型更简单。
2.6.2 硬件和时间
我们在一台具有8个NVIDIA P100 GPU的机器上训练我们的模型。使用本文描述的超参数的基础模型,每 个训练步骤耗时约0.4秒。我们的基础模型共训练了10万步或12小时。对于我们的大型模型(在表3的底部描述),步长为1.0秒。大型模型接受了30万步(3.5天)的训练。
因为TPU非常适合做很大的矩阵乘法,所以后面Google都推荐自己的员工多使用TPU
2.6.3 优化器
我们使用Adam优化器(cite),其中 , 并且。我们根据以下公式在训练过程中改变学习率:
这对应于在第一次步中线性地增加学习速率,并且随后将其与步数的平方根成比例地减小。我们使用。
2.6.4 正则化
训练期间我们采用两种正则化:
-
Residual Dropout
我们将dropout应用到每个子层的输出,在子层输出进入残 差连接之前,和LayerNorm之前,都使用dropout。此外,在编码器和解码器中,token embedding+Positional Encoding时也使用了dropout。对于base模型,我们使用drop概率为 0.1。
-
Label Smoothing
在训练过程中,我们使用的label smoothing的值为。这让模型不易理解,因为模型学得更加不确定,但提高了准确性和BLEU得分。(softmax要逼近于1,其输入几乎要无穷大,这是很不合理的,会使模型训练困难)
2.6.5 模型配置
可以看到模型虽然比较复杂,但是没有多少超参数可以调,使得后面的人工作简单很多。

2.7 评价
Transformer(attention机制)几乎能用在所有NLP任务上,类CNN对整个CV领域的革新(不需要那么多的特征提取或者模型建模,学会CNN就行了)。Transformer也是一样,不需要那么多的文本预处理,不需要为每个任务设计不同的架构。
而且现在transformer在CV、语音、video等领域也广泛使用,等于一个架构可以适用所有领域,任何一点突破在别的领域都能被使用,减少技术的应用时间。 而且Transformer可以融合多模态的数据(文字、图片、语音等),大家都要同一个架构提取特征的话,可以都抽取到同一个语义空间,使得我们可以用文字、图片、语音等训练更大更好的模型。
虽然Transformer效果这么好,但是对它的理解还在初级阶段。
- 最新的一些结果表明,attention在里面只是起到一个聚合序列信息的作用 ,但是后面的MLP/残差连接是缺一不可的,如果去掉的话,模型是基本训练不出什么的
- Attention不会对序列的顺序建模,为何能打败RNN?RNN可以显式地建模序列信息,不是应该比attention更好。现在大家觉得attention使用了更广泛的归纳偏置,使得他能处理更一般化的信息;这也是attention没有做空间上的假设吗,但是比CNN/RNN能做到更好的效果。代价就是假设更一般,所以抓取数据信息能力变差,必须使用更大的模型和更多的数据才能训练到一个比较好的效果。
期待未来有更多新的架构。
三、GAN论文精读
论文地址:《Generative Adversarial Networks》
bilibili视频《GAN论文逐段精读【论文精读】》
3.0 前言
3.0.1 GANs生成能力展示

过去5年,在reddit的MachineLearning板块,冲上头条最多的是GAN的应用,比如生成名人小时候的照片:



- 《Stable Diffusion web ui + IMG2IMG + After Effects + artist workflow》文生图模型:

还有一个专门的网址,上面都是一些生成的高清人脸图。从以上这些例子,可见GANs图片生成之强大。
3.0.2 原理
GANs(Generative Adversarial Networks,生成对抗网络)是从对抗训练中估计一个生成模型,其由两个基础神经网络组成,即生成器神经网络G(Generator Neural Network) 和判别器神经网络D(Discriminator Neural Network)。
生成器G从给定噪声中(一般是指均匀分布或者正态分布)采样来合成数据,判别器D用于判别样本是真实样本还是G生成的样本。G的目标就是尽量生成真实的图片去欺骗判别网络D,使D犯错;而D的目标就是尽量把G生成的图片和真实的图片分别开来。二者互相博弈,共同进化,最终的结果是D(G(z)) = 0.5,此时G生成的数据逼近真实数据(图片、序列、视频等)。

GAN就是对分布进行建模,希望模型可以生成各种分布。最理想的状态下,G可以生成足以“以假乱真”的图片G(z)。对于D来说,它难以判定G生成的图片究竟是不是真实的,因此
D(G(z)) = 0.5,此时噪声分布接近真实数据分布。
GANs目前的 局限性,就是生成的内容非常逼真(GANs的目标就是以假乱真),但不够多样性。现在图片生成领域,最火的还是扩散模型。扩散模型从20年开始,从DDPM到improved DDPM、Diffusion Models Beat GANs到最近的
DALL·E2和Imagen,使用了一系列的技巧来提高扩散模型的保真度,使得扩散模型的保真度可以媲美GANs,而且多样性和原创性更强。
3.1 导论
深度学习是用来发现一些丰富的、有层次的模型,这些模型能够对AI里的各种数据做一个概率分布的表示。深度学习网络只是一种手段而已。
深度学习不仅是学习网络,更是对数据分布的一种表示。这和统计学习方法里面的观点不谋而合,后者认为机器学习模型从概率论的角度讲,就是一个概率分布 最大(即已发生的事实,其对应的概率理应最大)。
函数代表的是取参数使得数据的概率密度最大。求解最优参数的过程,我们称之为模型的训练过程( Training )
深度学习在判别模型上取得了很好的效果,但是在生成模型上比较差。难点在于最大化似然函数时,要对概率分布做很多近似,近似带来了很大的计算困难。
本文的核心观点就是, 不用再去近似似然函数了,可以用更好的办法(GAN)来计算模型。
GAN是一个框架,里面的模型都是MLP。后面是用小偷造假币警察来分辨的一个举例。最后作者说,生成器G这个MLP的输入是随机噪声,通常是高斯分布;然后将其映射到任何一个我们想去拟合的分布。判别器D也是MLP,所以可以通过误差的反向传递来训练,而不需要像使用马尔可夫链这样的算法,对一个分布进行复杂的采样。这样模型就比较简单,计算上有优势。
3.2 相关工作
这篇论文有两个版本,arxiv上是早期版本,相关工作没怎么写。李沐讲的是NeurlPS上的最终版本。
之前的生成模型总是想构造一个分布函数出来,同时这些函数提供了一些参数可以学习。这些参数通过最大化对数似然函数来求解。这样做的缺点是,采样一个分布时,求解参数算起来很难,特别是高维数据。
因为这样计算很困难,所以最近有一些Generative Machines,不再去构造分布函数,而是学习一个模型来近似这个分布。
前者真的是在数学上学习出一个分布,明明白白知道数据是什么分布,里面的均值方差等等到底是什么东西。而GAN就是通过一个模型来近似分布的结果,而不需要构造分布函数。这样计算起来简单,缺点是不知道最终的分布到底是什么样子。
我观察到,对的期望求导,等价于对自己求导。这也是为什么通过误差反向传递来对GAN求解。
3.3 对抗网络(目标函数及其求解)
3.3.1 目标函数
GAN最简单的框架就是模型都是MLP

-
生成器G是要在数据x上学习一个分布,其输入是定义在一个先验噪声z上面,z的分布为 。生成模型G的任务就是用MLP把z映射成x。
-
比如图片生成,训练图片是224*224,每个像素是一个随机变量,那么x就是一个50176维的随机变量,变量每个元素都是控制的
-
不管最终x如何生成,假设不同的生成图片其实就是那么100来个变量控制的,而MLP理论上可以拟合任何一个函数。那么我们就构造一个100维的向量,MLP强行把z映射成x。所以z可以先验的设定为一个100维向量,其均值为0,方差为1,呈高斯分布。(这样算起来简单)
-
随机设定z为100维向量,缺点就是MLP并不是真的了解背后的z是如何控制输出的,只是学出来随机选一个比较好的z来近似x,所以最终效果也就一般。
-
G的可学习参数是
-
-
判别器D是输出一个标量(概率),判断其输入是G生成的数据/图片,还是真实的数据/图片。
-
D的可学习参数是。
-
对于D,真实数据,假的数据
-
-
两个模型都会训练
-
G的目标是希望生成的图片“越接近真实越好,变大接近1,也就是最小化,所以记做。
-
D的目标是最大化,记做
-
所以最终目标函数公式如下所示,E代表期望,X → 代表分布为的真实样本,另一边同理。公式中同时有,所以是对抗训练。
-

假设x和z都是一维向量,且z是均匀分布,模型训练过程可以表示为:

如上图所示,虚线点为真实数据分布,蓝色虚线是判别器D的分布,绿色实线为生成器G的分布。初始训练出生成器网络G和判别器网络D;从a到d是我们希望的训练过程。
a. 生成器从均匀分布学成绿色实线表示的高斯分布,这时候判别器还很差;
b. 判别器学成图b所示的分布,可以把真实数据和生成数据区别开来;
c.生成器波峰靠向真实数据波峰,自然就使得判别器难以分辨了;辨别器为了更准,其分布也往真实数据靠拢;
d.最终训练的结果,生成器拟合真实分布,判别器难以分辨,输出概率都为0.5 。
3.3.2 迭代求解过程
下面是具体的算法过程:

-
k是一个超参数,不能太小也不能太大。要保证判别器D可以足够更新,但也不能更新太好。
-
如果D更新的不够好,那么G训练时在一个判别很差的模型里面更新参数,继续糊弄D意义不大;
-
如果D训练的很完美,那么趋近于0,求导结果也趋近于0,生成器难以训练
-
整体来说GAN的收敛是很不稳定的,所以之后有很多工作对其进行改进。
-
早期G非常弱,所以很容易把D训练的很好,这样就造成刚刚说的G训练不动了。所以作者建议此时,G的目标函数改为最大化(就算D训练的好,这部分也是有梯度的)。带来的问题是,的时候,是负无穷大,会带来数值上的问题。(后面的工作会对其进行改进)
3.4 理论结果
3.4.1 全局最优解
这部分需要证明目标函数有全局最优解,且这个解当且仅当 时成立,也就是生成器学到的分布等于真实数据的分布。
具体证明部分可以参考帖子《GAN论文阅读——原始GAN(基本概念及理论推导)》的3.2章节。
首先,固定生成器G,最优的辨别器应该是

- *表示最优解
- 和表示x在生成器拟合的分布里面,和真实数据的分布里面,它的概率分别 是多少。
- 当时,结果为1/2,表示两个分布完全相同,最优的判别器也无法将其分辨出来。