引言
今天将讨论面试中最重要的部分之一:注意力机制。
如何计算注意力?
注意力机制是一种评估序列中各元素重要性的方法,相对于序列中的其他元素。
记住这句话:“通过比较查询和键,得到值的分数或权重”。
- 键(key) —— 用于计算的元素,与原文本进行比较
- 查询(query) —— 用于计算的元素,与目标文本进行比较
- 值(value) —— 进行更新的元素,与原文本有关
目标是创建这样的值向量,以便它只提取来自自身(自注意力)或目标语言(交叉注意力)的重要信息。
计算注意力的公式可能不同。重要的想法是需要一些分数来更新值。
- 点积 —— 常用的计算方法 当你在嵌入维度的平方根上应用 softmax 并进行归一化时,它就变成了缩放点积。这种方法在处理高维度数据时对训练的稳定性非常关键。
- 加性注意力 —— 一种由 Bahdanau 提出的注意力机制,它通过串联(concat)和软化(soft)操作以及全局计算来确定分数,计算过程中会用到额外的前馈神经网络。
注意力机制的复杂度是怎样的?
与循环神经网络(RNN)的复杂度相比如何?
- 注意力机制的复杂度是 O(n的n次方)
- 而RNN的复杂度是 (nXdXd)
总体而言,对于较长的序列,RNN在计算效率上更有优势。
探讨RNN与注意力机制的应用场景。在何种情况下你会选择RNN,而在何种情况下你更偏好注意力机制?
- 顺序处理:需要逐个单词处理句子。→ 不支持并行计算
- 通过先前的隐藏状态保留历史信息:序列到序列的模型遵循马尔可夫性质,即每个状态仅依赖于前一个状态 → 易出现梯度消失问题
非顺序处理:句子作为一个整体被处理,而不是逐个单词 → 支持并行计算
虽然很难断言哪种算法绝对优于另一种,但可以肯定的是,目前注意力神经网络在很多方面都超越了RNN。
用 Python 编写注意力公式。
def attention(query, keys, values):
d = keys.size(-1)
scores = torch.bmm(query, keys.transpose(1, 2) / sqrt(d)
scores = F.softmax(scores)
values_emb = torch.bmm(scores, values)
return values_emb
阐述注意力机制中的掩码作用。
简单来说,在变换器模型的编码器部分,仅利用自注意力机制,目的是为了捕捉到自身信息。在解码器部分,首先通过自注意力机制理解自身信息,然后通过交叉注意力机制引入编码器的信息。
在这个话题上,讨论两种掩码方式:随机掩码和未来标记掩码。自注意力机制中使用的是随机掩码,而交叉注意力机制中使用的是未来标记掩码。
此外,也需要注意填充问题。如果在自注意力或交叉注意力中同时使用这两种掩码,那么在掩码softmax计算时需要特别留意它们。
什么是注意力矩阵的尺寸?
注意力矩阵的尺寸基于输入元素的数目,格式为(序列长度,序列长度),这里的序列长度指的是应用注意力机制的序列的长短。
在讨论注意力计算时,BERT和GPT有何不同?
- BERT 利用自注意力机制计算每个词元的隐藏状态,通过随机掩码的方式将每个词元与序列中的其他词元进行比较。此外,还可以采用跨度掩码或不同的假设来同时掩码两个共存的词元。
- GPT 采用因果语言模型,通过掩码未来的词元来计算当前的词元。
变换器模型中嵌入层的维度是怎样的?
词汇表大小 * 嵌入维度 具体来说:
- 词汇表大小 —— 模型能理解的唯一词元的数量,这些词元是通过分词技术获得的。
- 嵌入维度 —— 用于获取每个词元隐藏表示的特征数量。这个维度在变换器模型中通常是预先设定的。
重要提示:使用分词器来更有效地分割单词,部分原因是无法更新权重(将全世界所有单词 * 嵌入维度的矩阵作为模型的输入),以包含世界上所有的单词。
为什么说嵌入是具有上下文的?它的工作原理是什么?
以单词 "mouse" 为例。根据它所处的上下文环境,它可以有不同的含义:米老鼠、真实的老鼠或电脑鼠标。 当使用 word2vec 这类模型时,可能会存储这个词的三种不同含义,或者希望在训练过程中能够覆盖所有上下文,或者希望嵌入能够同时表达所有的含义。
然而,在基于注意力的模型中,每个词元的更新都是基于它们周围的上下文。例如,当看到 "Mickey Mouse is my favourite Disney character." 这句话时,会根据句子中所有其他词元来更新 "mouse" 这个词元的嵌入。这种更新在推理过程中也会发生,但在推理时不会更新权重,以便更好地学习嵌入。此外,即使在训练时没有遇到相同的上下文,只要上下文本身训练得当,也能更好地推断出词元的含义。
此外,还需要区分不同类型的嵌入:
- 段落嵌入 —— 通常用于文本相似性任务,用于区分不同的句子。它在 BERT 预训练阶段被广泛使用。
- 标记嵌入 —— 之前讨论过的嵌入类型。它们代表了处理的每个词元的含义。
- 位置嵌入 —— 帮助确定每个词元位置的嵌入。
在变换器模型中,你会选择使用层归一化还是批量归一化,为什么?
首先,来定义为什么需要归一化。问题在于内部协变量偏移。
内部协变量偏移 —— 指的是在训练过程中由于网络参数的变化,网络激活分布发生变化。在所有随权重变化的输入上应用激活函数时,从中学习有价值的信息是很重要的。但是,如果这些值的分布过高或过低,可能会得到接近于零的导数,这最终会导致走向更长时间的收敛。
为了防止训练过程中的内部协变量偏移,可以应用批量归一化和层归一化。
批量归一化——另一层,根据小批量的均值和方差改变分布。然后它对因变量 beta 和 gamma 进行缩放和移动。
作者们引入了 beta 和 gamma 参数来保留“每层的知识”,确保每层能够继续执行其任务,尽管数据分布会相似但不会完全相同。你可以阅读这篇论文来深入了解它的工作原理、优势和劣势。
层归一化也是基于均值和方差的归一化方式,但不同之处在于,它是在整个层上计算统计数据,而不是仅在单个批次上。 有关层归一化的讲解视频。
从层归一化的概念中可以了解到,它不受批量大小的影响。此外,层归一化可以轻松地应用于RNN在不同时间步之间的处理,而批量归一化则需要进行调整。这里有一段相关讨论。
偏好使用层归一化的另一个原因是它便于进行并行计算。相比之下,批量归一化由于元素之间的依赖关系,使得并行计算更为困难。这里有一段相关讨论。
而且,在批量归一化中,训练时(基于一个批次)和推理时(使用移动平均值)的统计数据计算往往存在差异。而在自然语言处理(NLP)中,通常在推理时使用较小的批量大小。此外,层归一化能更好地处理NLP文本中可能应用的填充。
PreNorm 和 PostNorm 有什么区别?
此前,有人认为当层数增加时,Pre-LN Transformer 的性能优于 Post-LN Transformer。
最初,PreLN 和 PostLN 在设置上有所区别:
- PostLN 将层归一化(LayerNorm)放置在残差块之间,而 PreLN 则将层归一化放置在残差块内部。
- 此外,PreLN 还在预测之前立即进行最终的层归一化处理。
- 预归一化的梯度依赖于层级,而后置归一化则不受层级影响。
- 在隐藏状态的规模上,两者都面临相同的可扩展性问题:Pre-LN 依赖于层级,而 Post-LN 则不受层级影响。
- 在权重梯度的期望值上,也可以看到类似的行为。可以观察到,相对于层级,Pre-Layer 归一化提供了更多的稳定性。
值得注意的是,PostLN 中的梯度规模可能导致在 lr 调度下更难的训练。因为某些层的梯度非常大,如果没有预热就使用大的 lr 会导致不稳定。这可以在图表中看到:PostLN 预热后,其规模变小,可以承受较高的 lr。与此同时,对于 PreNorm,预热的重要性就不那么大了。此外,这使得模型能够更快地收敛。
解释软注意力和硬注意力(局部和全局)之间的区别
2017 年的经典注意力机制的主要优点在于 —— 从单一标记的视角出发,观察其他标记以更新自身。然而,对于非常长的序列,这在计算上可能代价高昂。有两种方法可以优化它:
- 软 VS 硬 —— 在图像描述中提出了这种方法,硬注意力预测一个区域,但优化是通过方差减少或强化学习来完成的。
- 全局 VS 局部 —— 全局注意力考虑所有状态进行比较和更新,而局部注意力预测即将关注的位置。在自然语言处理(NLP)中,通常更倾向于全局而非局部的方法。
阐述多头注意力机制
在注意力机制的应用中,可以把整个嵌入向量拆分成多个部分,然后让每个部分通过不同的矩阵进行处理 —— 这本质上就是多头注意力机制,其中每个“头”代表了这样的一个分割。这种方法带来的好处包括计算的并行化以及获取多样化的表征。通常认为,每个头能够学习到不同的、重要的信息。这些独立的注意力机制处理的结果会被合并起来,并通过线性变换调整到所需的维度。
增加注意力头的数量会让多头注意力机制变得多复杂?
按照标准的实现方式,增加头的数量并不会增加模型的参数总量。
如果一个模型的维度是 d,并且只有一个注意力头,那么它会将嵌入向量投影成一个 d 维的查询、键和值的三元组张量(每个投影不包括偏置项,需要 d² 参数,总共需要 3d² 参数)。如果同一个维度的模型有 k 个注意力头,那么它会将嵌入向量投影成 k 个 d/k 维的查询、键和值的三元组张量(每个投影不包括偏置项,需要 d²/k 参数,总共需要 3d² 参数)。