一些与Transformer模型相关的问题总结,有不对的欢迎指出。
💡 残差网络为何可以解决梯度消失
对比1和2可以发现,对于普通网络,当有几个偏导很小的时候,梯度会迅速趋近于0;而对于残差网络,要趋近于0,条件比较苛刻,要么前面一部分趋近0,要么后一部分趋近-1。总的来说,残差网络并不是解决了梯度消失问题,而是在一定程度上规避了问题,让其很难梯度消失。
注意:
1.神经网络的权重更新是沿着梯度的负方向,当没有梯度的时候,更新就会停滞,或者当梯度很大的时候,有可能会跳过最优解,这就是梯度消失和梯度爆炸对神经网络的“危害”
2.残差网络使得网络更深,而更深的网络拥有更大的感受野,感受野越大,越能捕获输入中的细节信息。残差网络是一个双支路,他可以融合两个支路的特征,使得融合后的特征图有更强的表达能力,说白了就是使得融合后的特征图有更大的感受野!
💡 Transformer解决的是什么问题,相较传统seq2seq有什么优势?
RNN、LSTM 和 GRU 网络已在序列模型、语言建模、机器翻译等应用中取得不错的效果。循环结构 (recurrent) 的语言模型和编码器 - 解码器体系结构取得了不错的进展。但是,RNN 固有的顺序属性阻碍了训练样本间的并行化,对于长序列,内存限制将阻碍对训练样本的批量处理。
注意力机制 (attention) 已经成为各类任务中序列建模 (sequencem modeling) 和转导模型 (transduction model) 中的组成部分,允许对输入输出序列的依赖项进行建模,而无需考虑它们在序列中的距离。但之前的注意力机制都与 RNN 结合使用。而Transformer,是一种避免循环 (recurrent) 的模型结构,完全依赖于注意力机制对输入输出的全局依赖关系进行建模。因为对依赖的建模完全依赖于注意力机制,Transformer 使用的注意力机制被称为自注意力(self-attention)。
Transforme的优点和缺点
(1) 每层计算复杂度更优:Total computational complexity per layer,时间复杂度优于RNN、CNN等。
(2) 一步计算解决长时依赖问题:这里Path length指的是要计算一个序列长度为n的信息要经过的路径长度。CNN需要增加卷积层数来扩大视野,RNN需要从1到n逐个进行计算,而self-attention只需要一步矩阵计算就可以。所以也可以看出,self-attention可以比RNN更好地解决长时依赖问题。当然如果计算量太大,比如序列长度n>序列维度d这种情况,也可以用窗口限制self-attention的计算数量。
(3) 模型更可解释:self-attention模型更可解释,attention结果的分布表明了该模型学习到了一些语法和语义信息。
实践上:有些RNN轻易可以解决的问题,transformer没做到,比如复制string,或者推理时碰到的sequence长度比训练时更长(因为碰到了没见过的position embedding)。
💡 Transformer的encoder和decoder有什么区别?
(1)decoder包含两个 Multi-Head Attention 层。
(2)decoder第一个 Multi-Head Attention 层采用了 Masked 操作。
(3)decoder第二个 Multi-Head Attention 层的K, V矩阵使用 Encoder 的编码信息矩阵C进行计算,而Q使用上一个 Decoder block 的输出计算。
(4)decoder最后有一个 Softmax 层计算下一个翻译单词的概率
💡 Transformer 的 Positional Encoding 是如何表达相对位置关系的?
在任何一门语言中,词语的位置和顺序对句子意思表达都是至关重要的。传统的RNN模型在处理句子时,以序列的模式逐个处理句子中的词语,这使得词语的顺序信息在处理过程中被天然的保存下来了,并不需要额外的处理。而对于Transformer来说,由于句子中的词语都是同时进入网络进行处理,顺序信息在输入网络时就已丢失。因此,Transformer是需要额外的处理来告知每个词语的相对位置的。其中的一个解决方案,就是论文中提到的Positional Encoding,将能表示位置信息的编码添加到输入中,让网络知道每个词的位置和顺序。一句话概括,Positional Encoding就是句子中词语相对位置的编码,让Transformer保留词语的位置信息。
理想状态下,编码方式应该要满足以下几个条件:
· 对于每个位置的词语,它都能提供一个独一无二的编码
· 词语之间的间隔对于不同长度的句子来说,含义应该是一致的
· 能够随意延伸到任意长度的句子
一种思路是使用有界的周期性函数。在前面的两种方法中,我们为了体现某个字在句子中的绝对位置,使用了一个单调的函数,使得任意后续的字符的位置编码都大于前面的字,如果我们放弃对绝对位置的追求,转而要求位置编码仅仅关注一定范围内的相对次序关系,那么使用一个sin/cos函数就是很好的选择,因为sin/cos函数的周期变化规律非常稳定,所以编码具有一定的平移不变性。transformer采用的位置编码公式如下:
选择这个函数是因为作者假设它将允许模型容易地学习通过相对位置参与,因为对于任何固定的偏移k,PEpos+k可以表示为PEpos的线性函数。
证明如下:
class PositionalEncoding(nn.Module):
def __init__(self, d_model, max_len=5000):
super(PositionalEncoding, self).__init__()
pe = torch.zeros(max_len, d_model)
position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(1)
# 大数之间相除很容易导致计算结果误差很大。因此这里需要使用下面的小技巧
div_term = torch.exp(torch.arange(0, d_model, 2).float() * (-math.log(10000.0) / d_model))
pe[:, 0::2] = torch.sin(position * div_term)
pe[:, 1::2] = torch.cos(position * div_term)
pe = pe.unsqueeze(0).transpose(0, 1)
#pe.requires_grad = False
self.register_buffer('pe', pe)
def forward(self, x):
return x + self.pe[:x.size(0), :]
💡 Transformer 中为何使用Layer Norm 而不是 Batch Norm?
从原理操作上来讲,BN针对的是同一个batch内的所有数据,而LN则是针对单个样本。另外,从特征维度来说,BN是对同一batch内的数据的同一纬度做归一化,因此有多少维度就有多少个均值和方差;而LN则是对单个样本的所有维度来做归一化,因此一个batch中就有batch_size个均值和方差。
BN归一化会抹去同一样本所有位置特征的原有大小关系,保留的是不同样本之间的大小关系;而LN则刚好相反,使用LN可以保留同一样本内部特征的大小关系,因此更有利于Transformer模型(注意力机制)捕捉统一样本间词与词的全局信息。另外,NLP的文本本质上可以看成一个时间序列,而时间序列是不定长的,长度不同的序列原则上属于不同的统计对象,所以很难得到稳定的统计量,而得不到稳定的统计量,BN就无法成立。
💡 Transformer的 Layer Normalization是怎么样做的?
transformer采用的LN的公式如下:
N(x)即为在layer中进行归一化操作的函数(即减去均值,除以方差),同时再额外地学习g和b对数值进行rescale。代码实现如下:
class LayerNorm(nn.Module):
"层归一化"
def __init__(self, features, eps=1e-6):
super(LayerNorm, self).__init__()
self.a_2 = nn.Parameter(torch.ones(features))
self.b_2 = nn.Parameter(torch.zeros(features))
self.eps = eps
def forward(self, x):
mean = x.mean(-1, keepdim=True)
std = x.std(-1, keepdim=True)
return self.a_2 * (x - mean) / (std + self.eps) + self.b_2
💡 什么是Transformer的多头注意力机制?
在注意力机制计算过程中,我们就是用qi去找相关的ki,但是相关可能会存在很多种,所以我们应该需要有不同的q,然后不同的q去负责捕获不同的相关性,多头注意力机制实现的就是这个功能。相比于Self-Attention,其只是将计算得到的qi和ki进行拆分。先从a乘上矩阵Wq变成q,然后再将q乘上两个不同的矩阵W(q,1),W(q,2),分别变成两个不同的向量q1和q2。k和v也是同样的进行操作。然后的话用q1,k1,v1去按照之前的方法计算出一个b1出来,再用q2,k2,v2去计算出一个b2出来。
使用多头注意力机制,扩展了模型关注不同位置的能力。此外,他给了自注意力层多个“表示子空间”。对于多头自注意力机制,我们不止有一组Q/K/V权重矩阵,而是有多组(论文中使用8组),所以每个编码器/解码器使用8个“头”(可以理解为8个互不干扰自的注意力机制运算),每一组的Q/K/V都不相同。然后,得到8个不同的权重矩阵Z,每个权重矩阵被用来将输入向量投射到不同的表示子空间。
💡 不考虑多头的原因,self-attention中词向量不乘QKV参数矩阵,会有什么问题?
Self-Attention的核心是用文本中的其它词来增强目标词的语义表示,从而更好的利用上下文的信息。
self-attention中,sequence中的每个词都会和sequence中的每个词做点积去计算相似度,也包括这个词本身。
对于 self-attention,一般会说它的 q=k=v,这里的相等实际上是指它们来自同一个基础向量,而在实际计算时,它们是不一样的,因为这三者都是乘了QKV参数矩阵的。那如果不乘,每个词对应的q,k,v就是完全一样的。
在相同量级的情况下,qi与ki点积的值会是最大的(可以从“两数和相同的情况下,两数相等对应的积最大”类比过来)。
那在softmax后的加权平均中,该词本身所占的比重将会是最大的,使得其他词的比重很少,无法有效利用上下文信息来增强当前词的语义表示。
而乘以QKV参数矩阵,会使得每个词的q,k,v都不一样,能很大程度上减轻上述的影响。
当然,QKV参数矩阵也使得多头,类似于CNN中的多核,去捕捉更丰富的特征/信息成为可能。
💡 多头注意力机制在进行softmax之前需要对attention进行scaled(为什么除以 d k d_k dk的平方根)?
缩放的作用是为了防止点积的值过大或过小,导致 softmax 函数的梯度消失或爆炸。这是因为点积的结果会随着向量维度的增加而增大,而 softmax 函数对输入的大小非常敏感,如果点积的值过大或过小,softmax 函数的输出将非常接近于 0 或 1,导致梯度消失或爆炸。
基本的注意力机制有以下两种形式,add和mul:
在Transformer中,作者使用的是第二种即mul形式的注意力。之所以用这个,作者解释是mul计算快。但是,当维度升高使得mul性注意力的整体方差变大,进而出现极大值使softmax梯度消失,所以通过scale控制方差,进而稳定梯度流,防止block堆叠时这一情况的恶化。MUL在dk比较大的时候,必须要做scaled。我们知道,对于两个矩阵在做乘法的时候,会在相乘时引入一次对所有位置的求和,整体分布就会扩大到[0,dk]。而对于add方式,右侧被tanh限制,分布在[-1,1],整体分布和dk没有关系。之所以要除以
d
k
d_k
dk的平方根,论文里其实有简单解释到:
即假设Q和K的是独立的随机变量,满足均值为0,方差为1,那么他们点积的结果为Q·K的均值为0,方差为dk。那么除以
d
k
d_k
dk的平方根,点积的方差将拉回1,也就有效地控制了前面提到的梯度消失的问题。
那为什么在分类层(最后一层),使用非scaled的softmax? 事实上,分类层的softmax也没有两个随机变量相乘的情况。此外,这一层的softmax通常和交叉熵联合求导,在某个目标类别i上的整体梯度变为 y i ′ − y i y'_i-y_i yi′−yi,即预测值和真值的差。当出现某个极大的元素值,softmax的输出概率会集中在该类别上。如果是预测正确,整体梯度接近于0,抑制参数更新;如果是错误类别,则整体梯度接近于1,给出最大程度的负反馈。也就是说,这个时候的梯度形式改变,不会出现极大值导致梯度消失的情况了.
💡 在计算attention score的时候如何对padding做mask操作?
在 Transformer 中,为了避免对填充符号进行注意力计算,通常会使用掩码(mask)来遮蔽填充符号。具体来说,假设我们要对一个序列进行注意力计算,其中包含一些填充符号。首先,我们需要将填充符号对应的位置标记出来,然后将这些位置的注意力分数设置为一个非常小的值,比如负无穷。
在 Transformer 中,padding mask 可以通过创建一个大小为 [batch_size, seq_length] 的矩阵来实现,其中元素为 0 的位置表示输入中的填充符号,元素为 1 的位置表示输入中的实际标记。然后,这个 mask 矩阵可以与注意力分数矩阵相乘,以将填充符号的注意力分数设置为负无穷。
在代码实现中,可以使用 TensorFlow 或 PyTorch 中的函数来创建 mask 矩阵,然后在计算注意力分数时使用它来进行掩码操作。
💡 Bert是基于Transformer结构的,做了什么针对性的改动?
结构上,BERT采用堆叠Transformer编码器而形成。主要有两种变体:
BERT Base: 12层(指transformer blocks), 12个attention head, 以及1.1亿个参数 BERT
BERT Large: 24层(指transformer blocks), 16个attention head,以及3.4亿个参数
token embedding: 在BERT中,每个词会被转换成768维的向量表示。在实际代码实现中,输入文本在送入token embeddings 层之前要先进行tokenization处理。此外,两个特殊的token会被插入到tokenization的结果的开头 ([CLS])和结尾 ([SEP])
Positional Embeddings: BERT 在输入层引入了位置嵌入(Positional Embeddings),以便模型能够正确处理输入文本的顺序信息。
Segment Embeddings: BERT还可以将句子对作为任务(问答)的输入。这就是为什么它学习了第一个和第二个句子的嵌入,以帮助模型区分二者。在上面的例子中,所有标记为EA的标记都属于句A(EB同理)。
Tokenization: BERT 采用一种基于 WordPiece 的子词划分方法,将单词划分成子词,并对每个子词进行嵌入表示。这个方法可以更好地处理未登录词和单词拼写错误的情况,并且可以更好地利用上下文信息。
💡 Bert训练任务是什么?
预训练方式: BERT 使用了一种新的预训练方式,即使用大规模无标注文本数据进行预训练,然后通过微调将模型应用到下游任务中。这种方式相对于传统的基于监督学习的方式,能够更好地捕捉文本数据中的语言规律和模式,并且具有更好的泛化性能。
Masked Language Model(MLM): BERT 在训练过程中采用了一种新的预测任务,即 Masked Language Model(MLM),这个任务要求模型在输入文本中随机掩盖一些单词,并预测被掩盖的单词。这个任务的目的是让模型学会双向上下文,即不仅能够根据单词的左侧上下文进行预测,还能够根据单词的右侧上下文进行预测。
Sentence Pair Tasks: BERT 还引入了一些针对性的文本对任务,如 Next Sentence Prediction(NSP),该任务要求模型判断两个输入句子是否是连续的。这些任务的目的是让模型学习处理文本对的能力,从而更好地支持下游自然语言处理任务,如问答、文本分类和命名实体识别等。
💡 Bert和GPT有什么区别与联系
联系: 都是基于Transformer的二阶段训练模型,都分为Pre-Training与Fine-Tuning两个阶段,都在Pre-Training阶段无监督地训练出一个可通用的Transformer模型,然后在Fine-Tuning阶段对这个模型中的参数进行微调,使之能够适应不同的下游任务。
区别: 它们的训练目标和模型结构和使用上还是有着些许的不同
GPT采用的是单向的Transformer,而BERT采用的是双向的Transformer,也就是不用进行Mask操作;
使用的结构的不同,直接导致了它们在Pre-Training阶段训练目标的不同;
💡 持续收集ing