上一篇讲到,注意力机制读到要填的空时,会回头给前面每个词打一个「相关度分数」,「法国」分数最高,模型就把注意力集中在「法国」上。这一篇讲这个分数到底是怎么算出来的。按四步走:打分需要什么信息,分数怎么算,分数怎么用,最后看为什么要分成 K 和 V 两个角色。

想先看个大概,可以先播放下面的动画讲解,两分钟把四步过一遍,再往下读正文。

动画讲解Q、K、V,两分钟看一遍13 步 · 约 2 分钟 · 可以随时暂停、跳步
1 / 13

上一篇讲到,注意力读到要填的空时,回头给每个词打一个相关度分数,「法国」最高。这一篇讲这个分数到底是怎么算出来的。

朗读用的是浏览器自带的语音,不同设备声音不一样;没有语音的浏览器只显示字幕。

还是用上一篇的句子:

我在法国长大,后来去了很多地方,换过好几份工作,现在住在北京,但我仍然能说一口流利的____。

一、打分需要什么:Q、K、V

注意力的思路和每天用的搜索引擎很像。在搜索框里输入「法国 语言」,网上有几十亿个网页,搜索引擎靠关键字判断哪些网页相关:拿你输入的关键字和每个网页的关键字比,越匹配的排得越靠前,你再点进去读排在前面的网页。

这个过程里有三个角色:

  • Q(Query,查询):你输入的「法国 语言」,代表「我想要的内容」。
  • K(Key,键):每个网页的标题和标签,是可以被查找到的内容的标签,专门拿来被比对。
  • V(Value,值):网页的正文,是可以被查找到的内容的真实内容,比对完以后真正拿走的就是它。

换到句子里,空格就是发起搜索的一方,前面的每个词都是一个「网页」,各自带着 K 和 V。

Q、K、V 三个角色,和搜索引擎里的查询词、网页标题、网页正文一一对应

空格发出的 Q 写着什么?看「我能说一口流利的____」,空里该填的是一种语言,但还不知道是哪种。所以空格的 Q 用大白话说就是:「我要填一种语言,是哪国的?」

前面每个词的 K 又写着什么?「法国」和「北京」都是地名,北京在中国,中文也是一种语言,只看「国家还是城市」区分不开。关键在于它们在句子里的身份:「在法国长大」说明法国是他从小生活的地方,从小说的语言才最可能说得流利;「住在北京」只说明他现在住在哪。所以「法国」的 K 是「我是一个国家,是他从小长大的地方」,「北京」的 K 是「我是一个城市,是他现在住的地方」。空格的 Q 和「法国」的 K 对得更上。

这里有个细节:「法国」这个词本身并不知道自己是「他长大的地方」。Transformer 不只算一次注意力,它叠了很多层。前面的层里,「法国」已经回头看过「在」「长大」这些邻居,把这层意思吸收了进来;到了后面的层,「法国」贴出的 K 才带上「他从小长大的地方」。

二、分数怎么算:点积和 softmax

模型里的 Q 和 K 都是一串数字。为了好理解,假设每串只有 3 个数,分别表示三个特征:跟某种语言有关、是从小生活的地方、是一个动作,1 表示有,0 表示没有。

怎么用两串数字算出一个「匹配分」?最直接的办法是:两串数字一一对应相乘,再全部加起来。这叫点积(dot product)。

点积:空格的 Q 和每个词的 K 对应相乘再相加,「法国」得 2 分,「北京」1 分,「长大」0 分

点积的意思很直观:两串数字在同一个特征上都大,乘出来就大;一边有一边没有,乘出来就是 0。越多特征对得上,分数越高。Transformer 算注意力分数用的就是点积。

点积算出来的 2、1、0 还不能直接用。上一篇的配图里,分数是 0.62、0.08 这样的小数,所有词加起来正好是 1,相当于把注意力按百分比分给每个词。而且真实的点积可能是负数,比如句子开头的「我」和空格几乎无关,点积可能是 -3。

一个容易想到的办法是:每个都加上一个数变成正数,再除以总和,看各自的占比。但这个办法有两个问题。第一,加几没有标准:加 3 得到 42%、33%、25%、0%,加 4 又是另一组结果。第二,差距被压平了:「法国」的分数明显最高,却只分到四成左右的注意力。

Transformer 的做法是用 e 的分数次方来变正数,e 约等于 2.718,然后再除以总和。这一步叫 softmax。

把分数 2、1、0、-3 变成百分比:每个都加 3 再算占比,注意力是散的;用 softmax,「法国」拿到 66%

e 的任何次方都是正数,不用挑「加几」。而且它是指数增长,分数每多 1,结果就乘约 2.7 倍,高分和低分的差距被拉开,注意力集中到「法国」上。

实际模型在 softmax 之前还多做一件事:先把点积除以一个固定的数,这个数是 Q 长度的平方根,写作 √d。真实的 Q 和 K 有几十到上百个数,乘完加起来很容易变得很大;分数太大,softmax 会把几乎全部注意力给一个词,其他词都分不到,模型也就很难学。Q 有 64 个数时就除以 8。

三、分数怎么用:加权求和

现在空格手上有一组百分比:「法国」66%、「北京」24%、「长大」9%、「我」0.4%。接下来,用每个百分比乘上对应词的 V,再全部加起来,这叫加权求和:

空格得到的内容 = 66% ×「法国」的 V + 24% ×「北京」的 V + 9% ×「长大」的 V + 0.4% ×「我」的 V

结果里大部分是「法国」交出的信息,也混进一点「北京」和「长大」的。

为什么不只拿分数最高的那个词?有两个原因。第一,有时候需要的信息分散在好几个词里,比如理解「红色的大苹果」里的「苹果」,「红色」和「大」两个词都要看。第二,模型是靠一点点调整数字学出来的,百分比从 66% 调到 70% 是平滑的变化,模型能算出往哪个方向调更好;如果只拿第一名,第一名从「北京」换成「法国」是突然跳过去的,模型没法一点点学。

四、为什么 K 和 V 要分开

「法国」身上有两种信息,用途不一样。「我是他从小长大的国家」用来让空格找到自己,这是 K 的工作。空格找到它以后,真正要拿走的是「在法国说的语言,也就是法语」,这是 V 的工作。标签只用来被找到,内容才拿去用。

如果 K 和 V 用同一串数字,这串数字就得同时做两件事:既要长得像「长大的地方」,方便被匹配,又要装着「法语」,方便被拿走。两件事挤在一起,哪件都做不好。分开以后,K 负责被找到,V 负责被拿去用,各自承担自己的责任。搜索引擎也是这样:网页标题写得像你要搜的东西,你点进去读的却是正文。

五、Q、K、V 从哪里来

每个词在模型里本来只有一串数字。模型准备了三个矩阵,把这串数字分别乘上这三个矩阵,就得到这个词的 Q、K、V。每个词都有自己的 Q、K、V,所以每个词都既能发起提问,也能被别的词找到。

这三个矩阵一开始是随机的。模型在训练时读大量文本,不断猜下一个词,猜错了就微调这三个矩阵。调到最后,Q 学会了「该问什么」,K 学会了「该贴什么标签」,V 学会了「该交出什么」。前面写的「我要填一种语言」「我是他长大的国家」这些话,都是为了好懂用的比喻,模型里没有这些文字,只有学出来的数字。

六、合起来看

注意力的计算就是四个动作:Q 和每个 K 做点积打分;除以 √d 把分数缩小;用 softmax 变成加起来是 100% 的百分比;按百分比把各个词的 V 加起来。

注意力的完整计算:Q 和 K 做点积打分,除以根号 d 缩小,softmax 变成百分比,再按百分比把各个词的 V 加起来

《Attention Is All You Need》论文里,这四个动作写成一行公式:

Attention(Q, K, V) = softmax(Q·Kᵀ / √d) · V

Q·Kᵀ 是一次算出所有词两两之间的点积,Kᵀ 表示把 K 转过来排好方便相乘;除以 √d 是缩小;softmax 变成百分比;最后乘 V 就是加权求和。每个词的计算互不依赖,所以能并行,这正是上一篇讲的 Transformer 快的原因。

下面把四个动作串起来,你可以自己调一调。先看默认值算出的百分比是不是和前面一样;再把「分数放大」拖到最大,看注意力会怎么变;最后勾上「先除以 √d」,看分数缩小回来以后的样子。

动手试一试拖动滑块,改变空格的 Q
词K分数注意力
法国[1, 1, 0]00%
北京[1, 0, 0]00%
长大[0, 0, 1]00%
我[-2, -1, 0]00%
空格拿到的内容

分数 = Q 和 K 的点积 × 放大倍数;示意里假设除以 √d 正好抵消放大。

检验一下

空格的 Q 和「法国」的 K 点积最大,说明什么?

softmax 为什么用 e 的分数次方,不给每个分数加上同一个数?

点积算完以后,为什么要先除以 √d 再做 softmax?

K 和 V 分别负责什么?

想深入可以看

← 全部文章