002 大模型基础概念:从GPT到LLaMA,一文看懂Transformer架构
一个让我熬夜三天的bug
去年调一个对话机器人,模型输出总是重复“好的好的好的好的”,像卡了带的复读机。我以为是采样参数没调好,temperature从0.7试到1.2,top_k从50砍到10,没用。最后扒开tokenizer才发现——输入序列长度超过模型最大上下文窗口,位置编码直接溢出,模型把后面的token当成了前面的重复。
那一刻我意识到:不懂Transformer的细节,调参就是瞎蒙。
为什么所有大模型都长一个样?
你打开GPT-4、Claude、LLaMA、Qwen的论文,架构图几乎一模一样:左边一堆方块叠起来叫Encoder,右边一堆方块叠起来叫Decoder,中间有个Cross-Attention箭头。但实际商用模型早就把Encoder扔了,全是Decoder-only架构。
为什么?因为生成任务只需要看前面的词猜后面的词,Encoder把整个句子看完再生成,那是翻译干的活。GPT系列从1.0开始就是纯Decoder,LLaMA、ChatGLM、Qwen全是这个路子。你写代码时如果看到“Encoder-Decoder”架构,大概率是T5或者BART这种老古董,除非做翻译或者摘要,否则别选。
核心组件:Attention到底在算什么?
很多人把Attention理解成“找重点”,没错但太模糊。实际计算就三步:
- 每个token生成三个向量:Query(问问题)、Key(被检索)、Value(实际内容)
- Query和所有Key做点积,得到注意力分数(谁跟谁关系近)
- 分数归一化后加权求和Value
代码里最坑的是mask。Decoder的Self-Attention必须加因果mask——当前token只能看自己和前面的,不能偷看后面的。我第一次写的时候忘了加mask,模型训练loss降得飞快,但生成时一个字都吐不出来,因为训练时它已经看到了正确答案。
# 这里踩过坑:mask矩阵必须是上三角全为-infmask=torch.triu(torch.ones(seq_len,seq_len)*float('-inf'),diagonal=1)# 别这样写:用0填充上三角,softmax后注意力不会归零位置编码:为什么LLaMA不用sin/cos?
原始Transformer用正弦余弦位置编码,因为它是固定的,不需要学习。但有个致命问题:它假设位置之间是线性关系,实际文本中“第5个词”和“第100个词”的关系远非线性。
GPT-2和GPT-3用可学习的位置编码,每个位置分配一个向量,让模型自己学。但序列长度固定死了——GPT-3最大2048,你输入2049个token直接报错。
LLaMA用了旋转位置编码(RoPE),思路很巧妙:把位置信息编码成旋转矩阵,乘到Query和Key上。好处是位置关系通过旋转角度体现,相对位置天然被编码进去,而且可以外推到更长序列。实测LLaMA-2的上下文从4096扩展到8192,RoPE功不可没。
写代码时注意:RoPE的实现有很多版本,Meta官方代码里是每两个维度一组做旋转,千万别搞成每个维度单独旋转,否则位置信息全乱套。
前馈网络:被忽视的“记忆仓库”
Attention负责信息交互,FFN负责把交互结果映射到更高维空间存储。每个Transformer层里FFN的参数量是Attention的4倍(因为中间维度是4倍隐藏维度)。LLaMA把激活函数从ReLU换成了SwiGLU,效果提升明显,但参数量多了1/3。
这里有个工程细节:FFN的权重矩阵通常用float16存储,但激活值容易溢出。LLaMA在SwiGLU里加了门控机制,其中一个分支用sigmoid做门,数值范围控制在0~1之间,有效缓解了溢出。如果你自己训练模型,记得检查FFN层的梯度,NaN经常从这里冒出来。
层归一化:放在哪里差别巨大
原始Transformer把LayerNorm放在残差连接之后(Post-Norm),GPT-2开始改成放在之前(Pre-Norm)。别小看这个改动,Post-Norm在深层网络中梯度容易消失,Pre-Norm让训练稳定很多。
LLaMA更进一步,用了RMSNorm——去掉LayerNorm的均值归零操作,只保留方差归一化。理由是LayerNorm的均值归零在Transformer里作用不大,反而增加计算量。实测RMSNorm训练速度提升5%~10%,效果几乎不变。
从GPT到LLaMA的演进路线
GPT-1(2018):12层Transformer Decoder,1.17亿参数,开创性工作但效果一般
GPT-2(2019):48层,15亿参数,展示了规模扩展的威力
GPT-3(2020):96层,1750亿参数,few-shot能力惊艳,但训练成本高到离谱
LLaMA(2023):65亿参数版本效果接近GPT-3 175B,关键改进是RoPE、SwiGLU、RMSNorm,以及更高质量的训练数据
LLaMA的成功说明:架构创新比单纯堆参数更有效。65亿参数打平1750亿,靠的是每个组件的精心设计。
个人经验:调试Transformer的五个血泪教训
先跑小模型验证:别一上来就搞7B、13B,用几十M的模型跑通训练流程,确认loss在下降,再放大规模。我见过太多人花几万块训练一个炸掉的模型。
检查梯度范数:训练时每100步打印一次梯度范数,正常应该在0.1~10之间。如果突然变成NaN或者0,大概率是数值溢出或者梯度消失。
位置编码外推测试:训练时用512长度,测试时试试1024。如果性能断崖下跌,说明位置编码不支持外推,赶紧换RoPE。
Attention的显存占用:序列长度翻倍,Attention显存占用翻4倍(因为注意力矩阵是N×N)。长文本任务用Flash Attention或者稀疏Attention,别硬扛。
不要迷信开源代码:HuggingFace的Transformers库为了通用性做了很多抽象,性能不是最优。生产环境建议用vLLM或者TensorRT-LLM,推理速度能快3~5倍。
最后说一句:Transformer架构看起来复杂,拆开就是Attention + FFN + LayerNorm + 残差连接,四个组件反复堆叠。理解每个组件为什么存在、为什么放在那个位置,比背公式重要一万倍。下次模型出bug,你至少知道该查哪里。