transformer 是怎么工作的?
当前每个语言模型背后的架构,从输入 token 一路追到输出概率——一层一层地看。
token → embedding
文本切成 token 后,每个 token 变成一个向量。此时整个序列就是一片数字网格。
你用过的语言模型几乎都是 transformer。这个架构 2017 年发表,把之前的东西全都取代了,原因是一个结构上的理由——在深入细节之前,它值得先弄懂。
它解决了什么问题
早先的语言模型按顺序处理文本——第一个词、第二个、第三个——一边把滚动更新的摘要带下去。这个设计有两个致命特性。
它没法并行。 第 500 个词要求第 1 到第 499 个词先处理完。在巨大语料上训练会慢到不可能。
它会忘。 长序列靠前的信息要撑过几百步更新才能走到末尾。实践中衰减得很厉害。
transformer 的回答是:同时处理每一个位置,并让每个位置直接看向其他所有位置。 没有顺序瓶颈,没有随距离衰减的信息。第 500 个词访问第 3 个词,和访问第 499 个一样容易。
整个想法就这一句。其余的全是为它服务的机械结构。
穿过模型的路径
跟一个序列从输入走到输出。
1 · token 变成向量
文本被切成 token,每个映射到一个 embedding——几百到几千个数字组成的向量。序列现在成了一张网格:一行一个 token,一列一个维度。
2 · 加上位置
因为所有位置是同时处理的,眼下还没有任何东西编码顺序。the cat sat 和 sat the cat 到这里还是同一批向量。
所以要注入位置信息。参见模型怎么知道词序?——这是个真正不显然的问题,有好几种互相竞争的解。
3 · 层的堆叠
同一个由两部分组成的块重复几十次。这是模型的心脏。
A 部分——注意力。 每个 token 检视其他所有 token,把相关的拉进来。it 找到它指代的东西。形容词找到它的名词。右括号找到它的左括号。细节见自注意力;至于为什么要有许多并行的副本,见为什么要多个注意力头?。
B 部分——前馈。 每个 token 的向量此时已被上下文充实,各自独立地穿过一个小网络。注意力在位置之间搬运信息;前馈层处理单个位置内部的信息。模型的大部分参数住在这里,大量事实性知识似乎也存在这里。
两部分外面都裹着残差连接和归一化,正是它们让堆几十层还能训得动——见层、残差与深度。
每一层都在精炼表示。靠前的层解决句法和词义;中间的层追踪实体和关系;靠后的层把预测下一个 token 所需的东西拼装起来。
4 · 输出
最后一层之后,最后一个位置的向量被投影到词表上,为每个可能的 token 产出一个分数。softmax 把它们变成概率,采样挑中一个,生成循环把它追加到末尾,然后重来一遍。
编码器、解码器,还是都要
原论文描述了两个堆叠——一个编码器读输入,一个解码器产输出——因为它的目标是翻译。
语言模型只留下了解码器。只有解码器的模型是因果的:每个位置只能看向更早的位置和自己,绝不看后面的。正是这条限制让下一 token 预测站得住脚,因为模型不能看到它正在预测的那个答案。
像 BERT 这样只有编码器的模型走的是另一条路,让每个位置双向地看到其他所有位置。做分类和 embedding 极好,做生成不合适。人们说「一个 LLM」时,几乎总是指只有解码器的。
代价是什么
这个设计换来了并行和长距离访问,付出的代价是算力:每个 token 都要看向每个 token,意味着计算量随序列长度的平方增长。参见长上下文为什么这么贵。
就这一个伸缩特性,决定了上下文窗口的上限、定价,以及当前研究的很大一部分。KV cache 是生成过程中的主要缓解手段。
要点回顾
- transformer 一次处理所有位置,并让每个位置直接看向其他任意位置——去掉了早先设计的顺序瓶颈和长距离衰减。
- 路径是:token → embedding → 位置信息 → 许多个(注意力 + 前馈)块 → 词表上的概率。
- 注意力在位置之间搬运信息;前馈层在单个位置内部处理信息,并承载大部分参数。
- 语言模型只有解码器且是因果的:每个位置只看得到它前面的东西。
- 代价随序列长度呈平方增长。
下一篇: 自注意力图解 —— 处在正中央的机制。