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

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

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:拿到这些信息之后你该怎么处理

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))

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. 遇到停止条件后结束

K,V Cache

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

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

用一个完整的例子总结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的向量都包含了丰富的上下文