目录
前言
3Blue1Brown 视频笔记,仅供自己参考
这几个章节主要解析 GPT 背后的 Transformer,本章主要是解释 Transformer 中的注意力机制
1. 前情提要:词嵌入
上一章,我们为逐步了解 Transformer 内部运作机理开了个头,这是大语言模型的核心技术之一,也广泛应用于现代 AI 热潮中的其它工具
它首次出现于 2017 年一篇著名论文《Attention Is All You Need》,本章我们将深入探讨文章标题里的注意力机制是什么,并可视化其数据处理的过程
快速回顾一下重要的背景知识,我们正在研究的模型,其目标是接收一段文本预测下一个词
输入文本被切分为小块即 token,通常是单词或单词片段,但为了方便我们理解例子,我们简化一下将 token 视为单词
Transformer 的第一步是将每个 token 关联到一个称为嵌入向量的高维向量,我希望你能明白的关键一点是在所有可能的嵌入向量构成的高维空间中,方向可以对应语义
在上一章中,我们举了个方向能够对应性别的例子,也就是在该空间中加上一个方向就能将男性名词的嵌入转到对应女性名词的嵌入
这只是一个例子,可想而知在高维空间中还有多少个方向可以对应多少种语义
Transformer 的目标是逐渐调整这些嵌入使它们不单单编码单个词,还能融入丰富的上下文含义
2. 注意力是什么?Mole是什么?Tower又是什么?
我得事先说明,很多人觉得注意力机制这一 Transformer 中的关键部分非常令人困惑,所以一开始不理解要慢慢消化是很正常的,在我们深入计算细节和各种矩阵乘法之前,不妨举几个例子说明注意力机制要实现的目标
考虑以下短语 American shrew mole、One mole of carbon dioxide 以及 Take a biopsy of the mole,我们都知道 mole 一词在不同语境有不同的含义
但在 Transformer 的第一步之后即切分文本并将每个 token 对应到一个向量后,三个 mole 对应的都是同一个向量,因为初始的 token 嵌入本质上是没有上下文参照的查找表
要等到 Transformer 的这一步周围的信息才能传递到该嵌入向量
你可以这样子想象,嵌入空间中有多个方向编码了 mole 一词的不同含义,而训练得好的注意力模块能计算出需要给初始的泛型嵌入加个什么向量,才能把它移动到上下文对应的具体方向上
再举一个例子,考虑 Tower 的词嵌入,这也许会是空间中某个很泛型,不具体的方向,与许多高大物体的名词相关联
如果 Tower 的前面是 Eiffel,就应该有一个机制来更新这个向量使其更具体地指向 Eiffel tower 的方向,新的向量也许还会关联上巴黎、法国与钢铁制品
如果在前面还有 Miniature 一词,那么向量就应进一步更新,使其不再与高大的事物相关联
注意力模块不仅精细化了一个词的含义,还允许模型相互传递这些嵌入向量所蕴含的信息,甚至可以传递得很远,而且新嵌入向量的信息可以比单个词丰富得多
正如上一章所提到得,所有向量流经网络后经过了许多层的注意力模块,此时预测下一个 token 的计算过程完全基于序列中最后一个向量
举例来说,你输入了几乎整本推理小说,从头输入到快结尾的 “因此,凶手是…” 这里结束
如果模型要准确预测下一个词,该序列中的最后一个向量即 是 的嵌入向量必须经过所有注意力模块的更新以包含远超单个词的信息量
也就是要设法编码整个上下文窗口中与预测下一个词相关的所有信息
3. 注意力模式:“一个毛茸茸的蓝色生物漫步于葱郁的森林”,名词与形容词,查询与键
为了逐步讲解计算过程,我们来看一个很简单的例子
假设输入包含以下短语 一个毛茸茸的蓝色生物漫步于葱郁的森林,并且暂时假设我们只关心形容词调整对应名词的含义这种类型的更新
我们先来了解 单头注意力机制,之后再介绍注意力模块中多个注意力头如何并行运算
要强调的是,每一个词的初始嵌入是一个高维向量,只编码了该单词的含义且与上下文没有关联
这样说其实也不太对,向量还编码了词的位置信息,关于编码位置的方式能说的就多了,但现在你只需要知道该向量内的元素足以说明这个词是什么以及它在文中的位置
我们用 E \mathrm{E} E 表示这些嵌入向量,最后的目标是通过一系列计算产生一组新的更为精准的嵌入向量,比如那些经过形容词修饰的名词所对应的向量
在深度学习中,我们希望多数计算都是矩阵向量乘法,其中矩阵填满了可调的权重,需要由模型学习数据来调整
说明一下,我编造这个 形容词更新名词 的例子只是为了方便理解注意力头可能会做的事情
在深度学习中,模型的实际行为往往是很难解释的,因为它只是通过调整大量参数来最小化某个代价函数
不过当我们逐一分析这些带有参数的矩阵时,我还是认为设想出一个具体的行为能更有助于理解
这个过程的第一步,你可以想象成是每个名词比如 生物 在问 喂,我前面有形容词吗?,而 毛茸茸的 和 蓝色 两词想回答 有啊,我就是形容词,我就在这里
这样的提问被编码为了另一个向量,也就是另一组数字,我们称它为这个词的 查询(Query),查询向量的维度比嵌入向量小得多,例如只有 128 维
要计算查询向量,先取一个矩阵记为 W Q \color{gold}W_{Q} WQ 再乘以嵌入向量就可以得到
稍微简化一下表述,把这个查询向量记为 Q \color{gold}Q Q,额外提一句,每当我把矩阵放在箭头旁边就表示该矩阵乘以箭头起点处的向量得到箭头末端的向量,在这里就是把查询矩阵分别与文章的所有嵌入向量相乘,给每个 token 算出一个查询向量
W Q \color{gold}W_{Q} WQ 这个矩阵内部的数值都是模型的参数,具体的行为模式是从数据中学得的,实际来看,这个矩阵在某个注意力头中的作用非常难以解释
但为了方便起见,我们希望它能学习的一个方向是,这个查询矩阵 W Q \color{gold}W_{Q} WQ 将 嵌入空间 中的名词映射到较小的 查询空间 中的某个方向,用向量来编码 寻找前置形容词 的概念
至于非名词的嵌入会怎样,谁知道呢?也许它同时想要实现其它目标吧,现在我们只关注名词
同时,我们还需要第二个矩阵叫做 键矩阵,它也会与每个嵌入向量相乘,产生第二个向量序列称为 键(Key)
从概念上讲,可以把 键 视为想要回答 查询,这个键矩阵也填满了可调参数,和查询矩阵一样,它也会将嵌入向量映射到相同的低维空间
当键与查询的方式对齐时就能认为它们相匹配,就本例而言,键矩阵会将形容词 毛茸茸的 和 蓝色 映射到与名词 生物 对应的查询向量高度对齐的方向上
为了衡量每个键与每个查询的匹配程度,我们要计算所有可能的 键-查询对 之间的点积,我喜欢将其想象为充满圆点的网格,圆点越大点积越大,键与查询就越对齐
回到形容词—名词的例子,看起来就像上面这样,如果由 毛茸茸的 和 蓝色的 生成的键确实与 生物 所产生的查询高度对齐,那么这两个位置的点积就会是较大的正数
用机器学习的术语来说,毛茸茸的 和 蓝色 的嵌入注意到了 生物 的嵌入,反过来 the 等词的键与 生物 的查询之间的点积将是较小值或负值,意思是这两个词互不相关
网格中的值可以是负无穷到正无穷的任何实数,这个分数代表的是 每个词与更新其它词含义有多相关
而这些分数的用法就是对每一列进行加权求和,权重为相关性,这样一来数值就不能是负无穷到正无穷,而是要介于 0 和 1 之间,并且每列总和为 1,就像概率分布一样
如果你看过上一章就知道接下来要做什么
对每列应用 softmax 函数进行归一化,如上图所示,每列经过 softmax 计算之后,用归一化后的值填进网格
此时就能将每一列看作权重,表示左侧的键与顶部的查询的相关度,我们称这个网格为 注意力模式(Attention Pattern)
如果你看一下原版的 Transformer 论文,他们用了一种非常简洁的写法
Attention
(
Q
,
K
,
V
)
=
softmax
(
Q
K
T
d
k
)
V
\text{\color{black}Attention}\color{black}(\color{gold}Q\color{black},\color{aqua}K\color{black},\color{red}V\color{black})=\text{softmax}(\frac{\color{gold}Q\color{aqua}K\color{black}^T}{\sqrt{d_k}})\color{red}V
Attention(Q,K,V)=softmax(dkQKT)V
这里的
Q
\color{gold}Q
Q 和
K
\color{aqua}K
K 分别包含了所有的查询向量和键向量,即通过将嵌入向量与查询矩阵
W
Q
\color{gold}W_Q
WQ 和 键矩阵
W
K
\color{aqua}W_K
WK 相乘得到的小向量
分子中的表达式 Q K T \color{gold}Q\color{aqua}K\color{black}^T QKT 非常简洁地表示了所有可能 键—查询对 之间点积的网格
有个技术细节我没有提到,为了数值稳定性建议将所有点积除以 键—查询 空间维度的平方根即 d k \sqrt{d_k} dk
然后,整个表达式外层的 softmax 函数应理解为 逐列的计算,至于 V \color{red}V V 项,我们稍后再谈
4. 掩码:看前不看后
首先要补充一下我跳过的另一个技术细节,在训练过程中,对给定示例文本跑模型时,模型会根据正确预测出下一词的概率高低来进行奖惩,并稍微调整各个权重
而效率更高的一种方法是,让它 同时 预测每个初始 token 子序列之后所有可能的下一个 token
比如之前这个短语,它可以同时预测 生物 的下一个词以及 the 的下一个词,这很好啊,因为这样一个训练样本就能提供多次训练机会
就注意力模式而言,这意味着不能让后词影响前词,不然就会泄露接下来的 答案
也就是说,我们希望左下方这些代表着后方 token 影响前方的位置能被强制变为 0
你可能会想直接将它们设为 0,但这样一来每列总和就不再是 1 了,不再是归一化的了
所以常见方法是在应用 softmax 之前,先将它们设为负无穷,这样应用 softmax 之后,它们就都会变为 0,但列仍保持归一化
这一过程称为 掩码(masking),也有的注意力机制不应用掩码,但在我们的 GPT 示例中,尽管掩码在训练阶段比在运行阶段更重要,例如当成聊天机器人之类的运行的时候。但两个阶段里都会采用掩码,以防止后方 token 影响到前方 token
5. 上下文窗口大小
关于注意力模式,值得一提的是,其大小等于上下文长度的平方,这就是为何上下文长度会成为大语言模型的巨大瓶颈,而扩大上下文长度绝非易事
可想而知,出于对更大上下文窗口的渴求,近年来,注意力机制出现了一些变体,旨在使上下文更具扩展性,但就本章我们只关注基础知识
6. 值矩阵
回到主线,根据注意力模式就能让模型推断出每个词与其它哪些词有关
然后要做的就是去更新嵌入向量,把各个词的信息传递给与之相关的其它词
例如,想办法让 毛茸茸的 的嵌入改变 生物 的嵌入,使其移动到这个 12000 多维嵌入空间的另一个方向,从而更具体地代表 毛茸茸的生物
我首先向你展示最直接的方法,不过对于多头注意力,方法会稍有调整
这个方法会用到第三个矩阵,我们称之为 值矩阵(Value Matrix),将它乘以前面那个词的嵌入比如 毛茸茸的,得到的结果就是 值向量(Value)。这也就是你要给后词的嵌入所加的向量,在本例中,加到的就是 生物 的嵌入式上,因此这个值向量与嵌入向量处于同一个高维空间
值矩阵乘以一个词的嵌入向量可以理解为,如果这个词需要调整目标词的含义,那么要反映这一点得对目标词的嵌入加上什么向量呢?
回过头来看网格图,这时候就不用管键和查询了,毕竟算出注意力模式后这些都用不着了,这时用值矩阵分别乘以嵌入向量,就可以得到一系列值向量,这些值向量可以想成是与对应的键向量相关联
对于网格中的每一列,你需要给每个值向量乘上该列的对应权重,例如这里对 生物 一词的嵌入要加上 毛茸茸的 和 蓝色 的值向量的大部分贡献,而其他词的值向量贡献不是归零就是在归零的路上
最后,为了更新该列对应的嵌入向量,也就是一上来没有上下文含义的 生物 的词嵌入,要做的就是将该列中所有带权值向量加和,得到想要引入的变化量,记为 Δ E \Delta{\mathrm{E}} ΔE,然后把它加入到原始嵌入向量上,预期是能得到一个更精准的向量,编码了更丰富的上下文信息,比如一个 毛茸茸的蓝色生物
当然,不能只对一个嵌入进行处理,而是对所有列分别进行加权求和,得到一系列变化量,将所有变化量加到对应的嵌入向量上,从而通过注意力模块得到一系列更精准的嵌入向量
7. 参数有多少
放眼来看,整个过程就是 单头注意力 机制
正如我之前所说的,这个过程由三种填满了可调参数的矩阵实现即查询矩阵、键矩阵、值矩阵,我们花点时间继续上一章做的事,来清点一下 GPT-3 模型本身的总参数量
键矩阵和查询矩阵各有 12288 列对应了嵌入维度,以及 128 行对应了较小的键—查询空间维度,这样就各有约 150 万个参数
而对于值矩阵,根据我之前所说的,它是一个 12288 列 12288 行的方阵,因为它的输入和输出都存在于高维的嵌入空间,如果真这样做,那就是要增加约 1.5 亿个参数
虽然也不是不可以,设计上值矩阵的参数量可以比键矩阵和查询矩阵多几个数量级
但实际上更为高效的做法是,让值矩阵所需要的参数量等于键矩阵和查询矩阵的参数量之和,对于并行运行多个注意力头来说,这点尤为重要
具体做法是将值矩阵分解为两个小矩阵相乘,从概念上讲,我还是建议你把它整体视为一个线性映射,输入和输出都在这个高维的嵌入空间。例如把 蓝色 的嵌入向量映射到相加后 把名词变蓝 的向量,只不过实践上这分为两个步骤
右边的第一个矩阵的行数较少,通常等于键—查询空间的维度,可以看作是将较大的嵌入向量降维到较小的空间,这不是通用命名,但我称之为 “值↓矩阵”( V a l u e ↓ \color{red}\mathrm{Value}_{\downarrow} Value↓)
左侧的第二个矩阵则是从小空间映射回嵌入空间,得到的是用于实际更新的向量,同样不是通用命名,我称之为 “值↑矩阵”( V a l u e ↑ \color{red}\mathrm{Value}_{\uparrow} Value↑),多数论文中的写法和这不一样,我稍后再讲,我个人认为那种写法容易让人混淆概念,借用线性代数的术语来说,这种操作的实质就是对大矩阵进行 低秩分解
我们继续清点参数量,这四个矩阵的大小相同,全加起来一个注意力头包含约为 630 万个参数
8. 交叉注意力
顺带一提,为了更准确,目前讨论的都是 自注意力 头,与其它模型中的变体 交叉注意力 头有所不同
这与我们的 GPT 示例无关,但如果你感兴趣的话,交叉注意力涉及的模型会处理两种不同类型的数据,比如原文与正在被翻译出来的译文,亦或是语音音频与正在被转录出来的文字
交叉注意力和自注意力几乎相同,唯一的区别是 键和查询矩阵作用于不同的数据集,例如,文本翻译模型中,键可能来自一种语言而查询则来自另一种语言,这个注意力模式就可以描述一种语言中的哪些词对应另一种语言中的哪些词,在这种情况下通常不会用到掩码,因为不存在后面 token 影响前面 token 的问题
9. 多头注意力
回到自注意力,若是之前的你都理解了,哪怕就此停下,你也已经领悟了注意力机制的核心
剩下要讲的就是解释如何多次重复这一过程
我们的例子里关注的是形容词更新名词含义,但实际上上下文影响语义的方式多种多样
如果 他们撞毁了 出现在 车 之前,这意味着对车的形状和结构发生了改变,而很多联系也不是语法层面上的
如果 巫师 与 Harry 出现在同一段话,就表明它很可能是指 哈利·波特,而如果这段话中出现了 女王、萨塞克斯、威廉 等词,那么 Harry 的词嵌入也许该更新到 哈里王子 的方向
对于你能想象到的每一种不同的上下文更新方式,键矩阵和查询矩阵的参数都会有所改变以捕捉不同的注意力模式,而值矩阵的参数也会因嵌入的更新值不同而改变。但和之前说的一样,这些矩阵实际情况下的真实行为难以解释,无论如何,权重已被训练完成能让模型高效预测下一个 token
正如我之前所说,我们目前只描述了单头注意力,而 Transformer 内完整的注意力模块由多头注意力组成,大量并行地执行这些操作,而每个头都有不同的键、查询、值矩阵,例如 GPT-3 每个模块内使用 96 个注意力头
只说一个就让人云里雾里了,96 个肯定得花点精力消化
先来明确一下,这意味着有 96 个不同的键和查询矩阵产生 96 种不同的注意力模式,然后,每个注意力头都有独特的值矩阵,用来产生 96 个值向量序列,全部都将以对应注意力模式作为权重分别进行加权求和
对于上下文中的每个位置,也就是每个 token,每个头都会给出一个要加入到该位置的嵌入中的变化量,而你要做的就是把各个头给出的变化量加起来,然后给该位置的初始嵌入加上这个加和,这个总和就是多头注意力模块输出的一列,也就是通过这个模块能得到一个更精准的嵌入
是的,这里有点费脑,要花点时间理解完全没关系
总的来说,通过并行多头计算,模型能学习到根据上下文来改变语义的多种方式
继续统计参数量,一共 96 个头,每个头都包括四个矩阵,每个多头注意力模块最终会有约 6 亿个参数
10. 输出矩阵
想要深入了解 Transformer 的话,还有个小插曲我不得不提,还记得我说过,值矩阵被分解成两个小矩阵相乘,我们称之为 值↓矩阵 和 值↑矩阵
我这样命名意思就是说,每个注意力头都会有这对矩阵,当然也完全可以这样实现,这种设计是可行的
但你在论文中看到的写法以及实际的实现方式会有些不同,这些 值↑矩阵 会合在一起被称作 输出矩阵,与整个多头注意力模块相关联,而单个注意力头的值矩阵则单指第一步的矩阵,即我所说的将嵌入向量投影到低维度空间的 值↓矩阵
感兴趣的话,上面有相关说明,这种细节就属于那种可能会偏离主题的内容,但我还是提一下,以免你看到其它版本时摸不着头脑
11. 加深网络
抛开所有技术上的细微差别,在上一章的总览中,我们了解到流经 Transformer 的数据并不只是经过单个注意力模块,首先,它之后还会经过 多层感知器 模块,这将在下一章详细介绍,其次,它还会多次重复这两种操作
这意味着,某个词吸收了一些上下文信息后,含义细致的嵌入向量还有更多的机会被周围含义细致的词语所影响,越是接近网络的深处,每个嵌入就会从其他嵌入中吸收越多的含义,使得嵌入本身也越来越细致丰富
理想的话,对于输入的内容,这样能提炼出更高级、更抽象的概念,而不仅仅是修饰和语法结构,也许还包括情感、语气、诗意以及话题涉及的科学原理等等
再次回到计数表,GPT-3 包含 96 个不同的层,因此键、查询、值的参数总数还得再乘以 96,使得所有注意力头参数总数将近 580 亿个,这确实很多了,但只占网络总参数量 1750 亿的三分之一
因此,尽管注意力机制吸引了所有的注意力,但实际上更多的参数来自于其间的模块,下一章,我们将详细讨论这些模块以及训练过程的细节
12. 结语
注意力机制成功的主要原因,并不在于它能实现什么特定的行为,而是它的可行性,这样就能用 GPU 在短时间内运行大量计算
在过去的一二十年里,深度学习的一个重要经验就是,仅靠扩大模型规模就能为模型性能带来质的飞跃,而可并行架构的巨大优势就在于能轻易扩大规模
相关资料
- 从零构建 GPT | Andrej Karpathy
- 从概念上了解语言模型 | Vcubingx
- 理解 LLM 可能实际在做什么 | Antropic
- 与 ML 编程和 GPT 相关的练习网站
- 语言模型的历史 | Brit Cruise
- embedding 空间中的方向如何具有意义的论文
结语
这章我们主要学习了注意力机制,词嵌入只编码了该单词的含义和位置信息并没有与上下文关联,通过注意力模块可以将上下文信息融入到这个嵌入向量中,融入的方式主要是通过查询矩阵、键矩阵以及值矩阵来完成的
以文中举例而言,查询矩阵与嵌入向量相乘得到的查询向量像是在提问题,而相应的键矩阵与嵌入向量相乘得到的键向量像是在回答查询提出的问题,为了衡量每个键与每个查询的匹配程度,要计算所有可能的键—查询对之间的点积,之后对每列做 softmax 后得到的网格就是注意力模式,网格中的每个数表示的是每个词与更新其它词含义有多相关。最后值矩阵与每个词的嵌入向量相乘得到值向量,值向量与注意力模式网格中的每个分数相乘相加得到变化量,将该变化量加到原始的嵌入向量上得到一个更精细的包含了上下文信息的嵌入向量,这就是 Transformer 中的注意力机制做的事情
OK,以上就是本章的全部内容了,下个章节我们来学习多层感知器,敬请期待😄