每个词都在问的问题
读这句话:“奖杯没能塞进手提箱,因为它太大了。”这里的“它”指什么?你立刻就知道:奖杯。但请留意你的脑子做了什么——为了理解一个词,它伸到句子另一头,把另一个词拉了过来。这种“伸过去拉”正是[[attention-mechanism|注意力机制]]背后的全部想法。在这条阶梯更早的地方你见过循环网络,它一次处理一个词,并试图用一份记忆把含义一路往前带。注意力把这个瓶颈扔掉了:每个词都直接地、同时地看向其他每一个词。
到这一步,每个词已经是一个向量——一个[[embedding|嵌入]],把这个词安放在高维语义空间中的某处。[[self-attention|自注意力]]就是让这些向量彼此交谈、并更新自己的那道运算。“自”不过是说,词关注的是同一个序列里的别的词(而不是某个另外的输入)。一轮过后,“它”这个词的向量已悄悄吸收了一点“奖杯”,于是它现在的含义更接近“它(那只奖杯)”。这种混合正是整个 Transformer 的引擎。
查询、键、值:一座小图书馆
这套机制给每个词三个角色,最干净的类比是一次图书馆检索。查询(query)是一个词在找什么(“我是个代词——我属于哪个名词?”)。键(key)是每个词为自己挂出的标签(“我是个单数的、具体的物体”)。值(value)是一个词若被选中,真正会交出来的内容。这个三件套太核心了,因而有自己的名字:查询、键、值,通常写作 Q、K、V。
关键在于:这三个向量并非直接给模型的。每个词的嵌入会分别乘上三个可学习的权重矩阵——W_Q、W_K、W_V——来生成它的查询、键和值。于是同一个词“bank”在河流的句子里能挂出一种键,在金钱的句子里又能扮演另一种有用的角色,因为这个网络是通过在海量文本上做梯度下降,学会了什么样的提问、什么样的标签、什么样的内容才有用。Q、K、V 是被学出来的投影,而不是词的固定属性。
逐步拆解:缩放点积注意力
现在可以执行这次检索了。要决定词 A 该听词 B 多少,我们取 A 的查询和 B 的键,算它们的点积——一个数,当两个向量指向同一方向时这个数就大。所以点积实打实地度量了“B 的标签把 A 的问题答得多好?”对每一对都这么算,你就得到一张原始分数的网格:每个词对每一个其他词。
- 打分:用每个词的查询,去和每个词的键做点积,得到一行原始的匹配分数。
- 缩放:把每个分数除以键维度的平方根。这就是“缩放”那部分,它的作用比看上去重要。
- 归一化:把这一行送进 softmax,将分数变成一组为正、且加起来等于 1 的权重——一种类似概率的注意力分布。
- 混合:用这些权重对所有的值向量做加权平均。那个混合出来的向量,就是这个词新的、带上下文的表示。
这整套配方就是缩放点积注意力。其中的 softmax 步骤,让它成为一次“软”选择而非“硬”选择:一个词不会只挑中另一个词,而是把注意力摊开——也许 0.7 给“奖杯”,0.1 给“手提箱”,剩下一点点抹在别处。输出是每个位置一个全新的向量,每个都是整句话的值按相关度加权后定制混合而成。
许多个头,许多种视角
一次注意力只能形成一种混合——对“什么重要”的一种意见。但语言一次需要好几种:谁指代谁、谁是这个动词的主语、是什么时态、是什么语气。于是 Transformer 不做一次大的注意力计算,而是并排跑好几次更小的,每次都有自己的 W_Q、W_K、W_V。每一次就是一个头(head),整套安排就是多头注意力。
每个头把词投影到一个更小的子空间,在那里做自己的缩放点积注意力,产出自己那份混合输出。然后各头的输出被拼接(concatenate)在一起,再过一个可学习的矩阵,把它们融合回每个词一个向量。因为各头不共享权重,它们就能自由地各司其职——而当研究者剖析训练好的模型时,确实有些头看起来在追踪语法关系,有些追踪邻近词,有些则追踪罕见的远距离联系。
# one head, sketched (Q,K,V already projected) scores = Q @ K.T / sqrt(d_k) # every query vs every key weights = softmax(scores) # rows sum to 1 out = weights @ V # weighted blend of values # multi-head: do the above H times in parallel, then # combined = concat(out_1, ..., out_H) @ W_O
注意力看不见什么——以及如何补救
有个让人清醒的事实:纯粹的自注意力根本不知道词是按什么顺序来的。因为它对一个集合做加权平均,“狗咬人”和“人咬狗”在它看来会一模一样。这种基于集合的视角,恰恰是注意力速度快、能一次性并行的原因——可词序显然是带含义的。补救办法是位置编码:在注意力运行之前,给每个词的嵌入加进一个与位置相关的信号,于是这些向量悄悄带上了“我是第 1 个词”“我是第 2 个词”等等。
另一个该诚实承认的局限是开销。因为每个词都要和其他每个词比较,工作量随序列长度的平方增长:文本翻一倍,计算量和内存大约翻四倍。这种平方级的增长,正是长输入昂贵的原因,也是模型有一个有限上下文长度的原因;它是一整条研究路线——更快的注意力变体、更聪明的记忆方案——不断啃食的核心问题。注意力之所以强大,恰恰因为它把一切连向一切;而正是这同一个性质,让它如此“饿”。
退一步看,画面很清晰。一个词起初是不带上下文的嵌入;注意力让它从序列各处收集相关信息;多个头一次收集好几类;而把许多这样的层堆叠起来,含义就一层又一层地复合,直到“它”可靠地知道自己是那只奖杯。在下一篇导读里,我们会把这些模块——再加上归一化层和前馈层——组装成完整的 Transformer,并看清这套设计为何席卷了整个领域。