How-Transformer-LLMs-Work

1.引言:LLM到底是怎么读懂一句话,并且继续写下去的?

LLM不是一次读/写一整段话,而是一个token一个token的写

什么是token

LLM无法处理文字,只能处理数据,所以想要读懂用户的input需要tokenizer
inputtext切成一小块一小块的token
比如英文:
unbelievable
可能被切成:
un + believable
中文:
人工智能
可能被切成:
人 + 工 + 智 + 能
不同的模型切法不同,具体实现取决于`tokenizer

LLM的核心工作就是,根据前文的内容,预测下一个token
例如,用户输入:
今天天气很好,我想去
模型要做的并不是真的去“思考人生”,而是去预测:
公园
散步
爬山
外面
LLM不断的重复:

1
预测一个token -> 加到上下文中 -> 预测下一个token ->再加入上下文

2.Understanding Lauguage Models

机器不能直接理解文字,所以我们必须先把文字表示成数字

Bag-of-Words

这是最早的一种简单方式

假设有三句话

1
2
3
我喜欢猫
我喜欢狗
我喜欢与

根据这三句话先做一个词表:
我, 喜欢, 猫, 狗, 鱼
然后每句话就可以表示成一个数字向量
比如:我喜欢猫 可以表示成:

喜欢
1 1 1 0 0
记录词表中词元的出现次数

但是这种方式,只能知道出现了哪些词,不知道顺序,不懂语义,不懂上下文。
只能处理一些很简单的任务。


Word Embeddings

Embedding是什么?

Embedding就是把一个词变成一个向量,比如:

1
2
3
猫 → [0.21, -0.45, 0.88, ...]
狗 → [0.19, -0.41, 0.84, ...]
汽车 → [-0.72, 0.13, 0.08, ...]

向量的目的:表达词的相似性
如果两个词的意思相近,他们的向量也会比较靠近

Word2Vec

Word2Vec这类方法的核心思想就是:
一个词的含义,可以通过它经常和哪些词一起出现来学习。
比如:

1
2
3
猫 吃 鱼
狗 吃 骨头
鸟 吃 虫子

模型慢慢就会知道:

1
2
猫、狗、鸟 都是动物
吃 后面常接食物

从上下文中学习词义。
但是传统的word embedding是静态的,也就是说一个词只有一个固定的向量
比如:
bank
在下面两句话意思不同

1
2
3
4
5
I went to the bank to deposit money.
我去银行存钱。

I sat on the bank of the river.
我坐在河岸边。

所以需要Transformer,让词的表示根据上下文的变化而变化


Encoding and Decoding Context with Attention

什么是Attention?

Attention就是,让模型在理解某个token的时候,知道重点看上下文的哪些token
如输入为:
小明把书给了小红,因为她明天要考试。
这里的“她”指的是谁?
大概率是“小红”。
Attention就是模型的这种会看上下文的机制

什么是Encoding?

把输入的text转化成有上下问含义的向量
比如原始输入的:“她”,开始只是个普通的代词
经过attention后,它变成:
她 = 小红,明天要考试的人

什么是Decoding?

Decoding是生成输出
比如你让模型翻译:
I love cats
模型生成:
我 喜欢 猫


Transformer架构

Transformer到底是啥?

Transformer是一种处理序列数据的神经网络架构,在LLM里面的任务通常是:
给定前面的tokens,预测下一个token
Transformer的强大之处在于,它可以让一句话里的所有token同时相互建立关系

现代的LLM模型

通常使用的是 Decoder-only Transformer,也就是省略了encoder部分只保留了decoder

Transformer的输入: Token -> embedding

由于模型不能直接处理文字
所以对于用户的输入,如:
我喜欢学习 Transformer
第一步是tokenizer,把我的输入切成Tokens
["我", "喜欢", "学习", "Transformer"]
然后每个token会被变成一个编号
[102, 3921, 8456, 19384]
这些编号本身没有意义,所以模型会查一张表,把每个token ID变成向量,即Token Embedding

位置编码 Positional Encoding

Transformer本身没有”顺序”的概念,所有的token是同时计算的。
为了让模型知道”小明在第一位,小红在第六位”,需要给每个token注入位置信息。

原始Transformer:正弦/余弦位置编码

给每个位置、每个维度都分配一个基于正弦/余弦函数的值,加到Embedding上。

现代LLM:RoPE (Rotary Positional Embedding)

目前主流模型(Llama、Qwen、Mistral等)都使用RoPE
RoPE不是简单地把位置向量”加”到Embedding上,而是用旋转矩阵对Q和K进行”旋转”:

1
RoPE(q_m, m) = q_m · e^(i·m·θ)

优点

  • 相对位置信息天然编码在内积中
  • 外推性好,便于处理比训练时更长的文本
  • 成为Decoder-only LLM的事实标准

Transformer Block

Transformer Block通常由很多层block堆起来
一个典型的Transformer Block如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
输入 hidden states

LayerNorm //稳定数值

Self-Attention //让每个token之间互相attention

Residual Connection

LayerNorm

Feed Forward Network //对每个token的信息进行加工

Residual Connection //保留原信息

输出 hidden states

Q,K,V:Attention的核心机制

在Self-Attention里面,每个token会生成三个向量:

1
2
3
Q = Query        //我想找什么信息
K = Key //我有什么特征,方便别人匹配到我
V = Value //如果别人关注我,我能提供什么内容

还是之前的句子:
小红把苹果给了小明,因为他饿了。
当模型处理到“他”时:

1
2
3
“他”的 Query:我指的是谁?
“小明”的 Key:我是一个男性人物,前面出现过
“小红”的 Key:我是一个女性人物,前面出现过

模型发现”他”的Query和“小明”的Key更匹配
于是“他”更多的吸收“小明”的Value

缩放因子 Scale

计算注意力分数时,公式是:

1
Attention(Q, K, V) = softmax(QK^T / sqrt(d_k))V

为什么要除以sqrt(d_k)
当向量维度d_k很大时,Q·K的点积结果会变得非常大,导致softmax的梯度极小,训练不稳定。
除以sqrt(d_k)可以把数值压回合理范围。

多头注意力 Multi-Head Attention

单组QKV只能捕捉一种关系,但语言中存在多种关系(语法、语义、指代、逻辑等)。
多头注意力的做法是:把Q、K、V分别投影到多组低维空间(如8组、16组、32组),每组独立做Attention,最后把所有头的输出拼接起来,再投影一次。

1
MultiHead(Q, K, V) = Concat(head₁, head₂, ...) · W_O

不同头会学到不同的注意力模式,比如:

  • 某些头专门关注相邻的词
  • 某些头专门关注代词和名词的指代关系
  • 某些头专门关注标点符号

Masked Self-Attention: Decoder-only LLM的关键

普通的self-attention允许每个token看一整句话
但是GPT这类生成模型不能这样
比如训练句子:
我喜欢吃苹果
当模型预测“吃”后面的内容是什么的时候,不能提前看到“苹果”
每个token只能看到自己和自己左边的token,不能看到右边未来的token

Feed Forward Network

Self-Attention负责让token之间交流
交流之后,还需要加工
这一步由FFN完成

典型形式
FFN(x) = W₂ · activation(W₁x + b₁) + b₂

Attention:你应该从哪些token哪里获得信息
FFN:拿到这些信息之后你该怎么处理

激活函数与 SwiGLU

原始的Transformer FFN使用ReLU或GELU作为激活函数。
现代LLM(如Llama、PaLM、Qwen)普遍使用 SwiGLU

1
SwiGLU(x) = (W₁x ⊗ SiLU(W₂x)) · W₃

其中SiLU(也叫Swish)是一种平滑的激活函数:

1
SiLU(x) = x · sigmoid(x)

SwiGLU引入了两个权重矩阵做门控(gating),表达能力更强,已成为当前主流配置。

Residual Connection

Transformer block里会有一个残差连接
他的形式是:
输出 = 输入 + 子层输出
如attention后:
x = x + attention(x)
FFN后:
x = x + FFN(x)
因为模型层数很深,如果每一层都彻底重写信息,原始信息可能丢掉。

LayerNorm让训练稳定

神经网络里每层都会产生一堆数字。如果这些数字变化太大,模型训练会不稳定。
LayerNorm会把每个token的向量调整到比较稳定的范围。
常见的Transformer结构有两种:

Post-LN

x = LayerNorm(x + Attention(x))

Pre-LN

现代LLM更常见,更利于训练很深的模型
x = x + Attention(LayerNorm(x))

RMSNorm

现代很多开源LLM(如Llama、Qwen)使用RMSNorm代替LayerNorm
RMSNorm不做均值中心化,只除以”均方根”(Root Mean Square):

1
RMSNorm(x) = x / sqrt(mean(x^2) + eps) * gamma

计算更简单,效果与LayerNorm相近甚至更好。

Encoder Block 和 Decoder Block

Encoder Block适合理解输入

一个Encoder Block通常包括:

1
2
3
4
Self-Attention
Feed Forward Network
Residual
LayerNorm

Encoder Block 的self-attention可以看到完整的输入
所以Encoder Block适合理解任务

Decoder Block

一个Decoder Block通常包括:

1
2
3
4
5
Masked Self-Attention
Cross-Attention
Feed Forward Network
Residual
LayerNorm

相比于Encoder Block,Decoder多了两个关键点:

  1. Self-Attention是Masked的
  2. 由Cross-Attention可以看Encoder的输出

什么是Cross-Attention

Cross-Attention出现在Encoder-Decoder Transformer里
比如翻译任务:

1
2
输入英文:I love cats
输出中文: 我喜欢猫

Encoder先读懂英文,得到一组上下文向量。
Decoder生成中文的时候,一边看已经生成了的中文(Masked Self-Attention),一边看Encoder的输出,也就是Encoder在Self-Attention后生成的一组上下文向量。

LM Head:如何输出下一个Token

经过很多层的Transformer Block后,每个Token都有一个最终的hidden state
比如输入:
中国的首都是
最后一个token”是”的hidden state进入LM Head
LM token会输出整个词表中每个token中的分数。
如:

token 分数
北京 14.2
上海 8.1
东京 4.5
苹果 -2.3
然后经过softmax变成概率
token 概率
北京 0.91
上海 0.03
东京 0.01
苹果 接近 0
于是模型生成:
北京
然后上下文变成:
中国的首都是北京
继续预测下一个token

Transformer训练

训练Decoder-only LLM时,目标是:
根据前面的tokens,预测下一个token
比如训练文本为:
我喜欢学习人工智能
模型会学习多个预测任务:

1
2
3
4
看到“我” → 预测“喜欢”
看到“我 喜欢” → 预测“学习”
看到“我 喜欢 学习” → 预测“人工”
看到“我 喜欢 学习 人工” → 预测“智能”

训练的文本越多,就能让模型预测越接近真实的下一个token

Transformer推理的流程示意

1
2
3
4
5
6
7
8
1. 用户输入 promp
2. tokenizer 转成 token IDs
3. 模型计算 hidden states
4. LM head 输出下一个 token 概率
5. 选择一个 token
6. 把新 token 加回上下文
7. 重复 3-6
8. 遇到停止条件后结束

采样策略:如何选择下一个Token

LM Head输出的是概率分布,但模型并不总是直接选概率最高的那个。
常见的采样方法:

  • Greedy Decoding:永远选概率最高的token。简单但容易生成重复、死板的内容。
  • Temperature Sampling:把概率分布除以一个temperature参数。
    • T < 1(如0.7):概率分布变得更尖锐,模型更倾向于选高分词,输出更确定。
    • T > 1(如1.2):概率分布变得更平缓,模型更愿意尝试低分词,输出更有创造性。
  • Top-K Sampling:只从概率最高的K个token里采样,避免选中极不合理的词。
  • Top-P (Nucleus) Sampling:从高到低累加概率,直到累积概率达到P(如0.9),只在这个”核心集”里采样。这是目前最常用的方法之一。
  • Beam Search:同时维护多个候选序列,选整体概率最高的完整句子。适合翻译、摘要等任务,但不适合开放式生成。

K,V Cache

解决生成token时的效率问题
为了计算新生成token的Q,K,V,需要利用到之前的tokens的K和V
如果在预测的时候,重新计算之前tokens的K和V,会浪费很多资源
KVCache的做法就是

把之前token的Key和Value存起来,下一步预测的时候直接拿来用。

FlashAttention 与 MQA/GQA

FlashAttention

标准的Attention需要计算一个很大的注意力矩阵,占用大量显存。
FlashAttention的核心思想是:把Q、K、V分块,每次只加载一小块到高速缓存(SRAM)里计算,避免存储巨大的中间结果矩阵。
这样更快、更省显存,而且数学结果完全一样。

MQA (Multi-Query Attention) 与 GQA (Grouped-Query Attention)

原始的Multi-Head Attention中,每个头都有自己独立的K和V。

  • MQA:所有头共享同一组K和V,大幅减少KV Cache的显存占用。
  • GQA:把多个头分成几组,每组共享一组K和V,在效率和质量之间取平衡。
    现代LLM如 Llama 2/3、Qwen 等都采用GQA。

用一个完整的例子总结Transformer的推理流程:

第一步:切token

用户输入:
小明把书给了小红,因为她要考试。
tokenizer把输入分割成:
小明/把/书/给/了/小红/因为/她/要/考试/。

第二步:变token ID

[101, 232, 981, 445, ....]

第三步:embedding

把每个token变成向量

第四步:加入位置

模型知道:

1
2
3
小明在前面
小红在“她”前面不远
考试在“她”后面

如果是Decoder-only,模型就不能看到当前token后面的内容

第五步:Self-Attention

处理”她”的时候,模型计算“她”和其他tokens的关系:

token 相关性
小明 较低
很低
小红 很高
因为 中等
因此“她”更多吸收”小红的信息“

第六步:FFN加工

FFN进一步把信息加工成:

1
2
她 = 小红
小红可能是要考试的人

第七步:多层重复

第一次可能处理简单的关系
中间层可能处理语法和指代
高层可能处理整体语义
最后每个token的向量都包含了丰富的上下文