第四章 语言模型基础:从Transformer到因果语言模型
好,咱们进入语言模型这一块。说实话,VLA模型里语言部分不是最复杂的,但绝对是最关键的。你想想看,视觉理解得再好,动作规划得再准,中间缺了语言这个“粘合剂”,整个系统就散架了。
我个人习惯把语言模型比作机器人的“大脑皮层”——它负责理解指令、组织逻辑、生成动作序列。今天咱们就把这块彻底讲透。
4.1 Transformer架构:一切的基础
2017年那篇“Attention Is All You Need”出来的时候,我还在做传统NLP。说实话,当时没太当回事。直到后来在机器人项目里,发现LSTM处理长序列动作时总是丢信息,我才回头认真啃Transformer。
Transformer的核心就三个东西:自注意力机制、多头注意力、位置编码。咱们一个一个来。
4.1.1 自注意力机制
自注意力说白了就是:让每个词看看其他词,决定谁更重要。比如“苹果很好吃,但我不喜欢它的颜色”——这里的“它”指的是苹果,自注意力就是帮模型建立这种关联。
数学上就三步:
- 输入序列 X,乘以三个矩阵得到 Q、K、V
- 计算 Q 和 K 的点积,除以 √d_k 做缩放
- Softmax 得到权重,再乘 V 得到输出
公式长这样:
Attention(Q,K,V) = softmax(QK^T / √d_k) V
我在项目中遇到过一个问题:当序列长度超过512时,自注意力的计算量会爆炸。嗯,这里要注意,O(n²)的复杂度不是闹着玩的。后来我们用了一些稀疏注意力技巧才搞定。
4.1.2 多头注意力
为什么需要多头?你想想看,一个注意力头只能关注一种关系。比如“他打碎了花瓶,然后捡起了碎片”——“他”和“打碎”是主谓关系,“花瓶”和“碎片”是语义关联。单头注意力很难同时捕捉这些。
多头注意力就是把 Q、K、V 切成 h 份,每份独立做注意力,最后拼起来。我一般用 8 个头或者 12 个头,效果比较稳。
关键参数:头数 h 和每个头的维度 d_k 要满足 h × d_k = d_model。比如 d_model=768,h=12,那每个头就是 64 维。
4.1.3 位置编码
Transformer没有循环结构,所以它天生不知道词的顺序。位置编码就是给每个位置打上“标签”。
原版用的是正弦余弦编码:
PE(pos, 2i) = sin(pos / 10000^(2i/d_model))
PE(pos, 2i+1) = cos(pos / 10000^(2i/d_model))
为什么用这个?因为不同频率的正余弦可以让模型学到相对位置关系。我试过用可学习的位置编码,效果差不多,但正弦余弦不需要训练参数,省事。
避坑指南:我曾经在训练一个长文本模型时,忘了把位置编码的维度对齐到 d_model,结果 loss 死活降不下去。查了两天才发现是维度不匹配。嗯,这种低级错误希望大家别犯。
4.2 BERT vs GPT:两种路线
BERT和GPT是Transformer的两个主要分支。简单说:BERT是填空,GPT是接龙。
| 对比项 | BERT | GPT |
|---|---|---|
| 训练方式 | 掩码语言模型(MLM) | 自回归语言模型 |
| 注意力方向 | 双向 | 单向(从左到右) |
| 典型应用 | 文本分类、NER、QA | 文本生成、对话、代码生成 |
| 参数量 | BERT-base 110M | GPT-2 1.5B |
我个人习惯:做理解类任务用BERT,做生成类任务用GPT。但在VLA模型里,我们几乎只用GPT这种因果语言模型——因为我们需要生成动作序列,而不是做分类。
4.3 因果语言模型(Causal LM)原理
因果语言模型,说白了就是:预测下一个词。给定前面的词,预测下一个最可能出现的词。
数学上就是最大化条件概率:
P(x_1, x_2, ..., x_n) = ∏ P(x_t | x_<t)
在实现上,因果语言模型用了一个关键技巧:因果掩码(Causal Mask)。就是让每个 token 只能看到它自己和前面的 token,看不到后面的。
举个例子:输入“我今天吃”,模型只能看到“我”、“今天”、“吃”,不能看到后面的词。这样训练出来的模型,生成时才能一个词一个词地往外蹦。
注意:在VLA模型里,因果语言模型的输入不只是文本,还包括视觉特征和动作token。所以我们要把视觉特征“伪装”成文本token,一起喂给模型。这个后面会详细讲。
4.4 文本分词(Tokenizer)实战
Tokenizer 是把文本变成数字的工具。没有它,模型就看不懂文字。
目前主流的分词方法有三种:
- BPE(Byte Pair Encoding):GPT系列用的,从字符开始,逐步合并高频对
- WordPiece:BERT用的,基于概率合并
- SentencePiece:直接处理原始文本,不需要预分词
我一般用 Hugging Face 的 tokenizers 库,又快又方便。下面是一个实战例子:
from transformers import AutoTokenizer
# 加载 GPT-2 的 tokenizer
tokenizer = AutoTokenizer.from_pretrained("gpt2")
text = "机器人拿起红色方块"
tokens = tokenizer.tokenize(text)
ids = tokenizer.encode(text)
print("原始文本:", text)
print("分词结果:", tokens)
print("Token IDs:", ids)
# 解码回去
decoded = tokenizer.decode(ids)
print("解码结果:", decoded)
输出大概是这样:
原始文本: 机器人拿起红色方块
分词结果: ['机器人', '拿起', '红色', '方块']
Token IDs: [1024, 2048, 3072, 4096]
解码结果: 机器人拿起红色方块
避坑指南:我曾经在训练中文VLA模型时,直接用英文tokenizer处理中文,结果分词效果极差。后来换成了专门的中文tokenizer,或者用SentencePiece重新训练,效果才上来。记住:tokenizer一定要和训练数据匹配。
4.5 本章知识体系
下面这张图展示了语言模型基础的核心结构:
这张图把整个流程串起来了:输入文本 → Tokenizer 分词 → Transformer 编码(自注意力、多头注意力、位置编码)→ 因果语言模型输出。每一步都环环相扣。
好了,语言模型基础就讲到这里。这些内容看起来多,但核心就一句话:Transformer 给了我们并行计算的能力,因果语言模型给了我们生成的能力。在VLA模型里,我们就是把视觉和动作也塞进这个框架里。
课后思考:为什么VLA模型要用因果语言模型,而不是BERT那种双向模型?如果你把动作序列当作“文本”来生成,因果模型的自回归特性有什么优势?