大语言模型基础:Transformer与预训练揭秘
2017年,一篇名为《Attention Is All You Need》的论文彻底改变了人工智能领域。它提出的Transformer架构,摒弃了此前主导自然语言处理的循环神经网络(RNN)和卷积神经网络(CNN),仅凭“注意力机制”这一核心思想,便奠定了当今所有大语言模型(LLM)的基石。理解Transformer,便是理解了GPT、Claude、LLaMA等模型何以能够生成流畅文本、进行逻辑推理。本章将穿透“黑箱”,从零开始构建你对大模型技术根基的认知,揭示预训练如何赋予模型“世界知识”,并阐明这一切与“Prompt对话工程”的深刻关联。
---
**1.1 Transformer:超越序列的并行革命**
在Transformer出现之前,处理文本序列的主流模型是长短期记忆网络(LSTM)等RNN变体。它们像阅读一本书一样,一个词一个词地依次处理,并将前文信息压缩到隐藏状态中。这种 sequential processing(顺序处理)存在两个根本性瓶颈:1)无法并行计算,训练速度受限于序列长度;2)长程依赖问题,信息在长序列传递中会逐渐衰减或失真。Transformer的核心突破,在于其完全基于注意力机制,实现了序列元素间的全局、并行交互。
**1.1.1 核心支柱:自注意力机制(Self-Attention)**
自注意力是Transformer的“灵魂”。它允许序列中的每个词(Token)直接与序列中的所有其他词建立联系,从而动态地计算每个词在特定上下文中的“重要性”。这个过程可以分解为三个关键向量:
- **Query(查询,Q)**: 当前词“想了解”什么?
- **Key(键,K)**: 序列中其他词“提供”什么信息?
- **Value(值,V)**: 其他词实际“包含”的语义内容。
其计算本质是:对于序列中的每个词,将其Query与所有词的Key进行相似度匹配(通常用点积),得到一组注意力分数(Attention Scores)。这些分数经过Softmax归一化后,作为权重,对所有的Value进行加权求和,最终得到该词的上下文相关表示(Contextualized Representation)。
用一个经典例子“`The animal didn't cross the street because it was too wide`”来说明。对于代词“it”,模型需要判断其指代“street”还是“animal”。自注意力机制会计算“it”与“street”、“animal”、“wide”等词的关联分数。由于“wide”通常修饰“street”,模型会给予“street”更高的注意力权重,从而在“it”的最终表示中融入更多关于“street”的语义信息。
数学表述: 对于输入序列 $X = [x_1, x_2, ..., x_n]$,其中 $x_i \in \mathbb{R}^{d_{model}}$,我们通过三个可学习的权重矩阵 $W^Q, W^K, W^V \in \mathbb{R}^{d_{model} \times d_k}$(通常 $d_k = d_v = d_{model}/h$,h为注意力头数)计算Q, K, V矩阵: $$Q = XW^Q, \quad K = XW^K, \quad V = XW^V$$ 注意力输出为: $$\text{Attention}(Q, K, V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V$$ 分母 $\sqrt{d_k}$ 用于缩放点积,防止Softmax进入梯度极小的饱和区。
实践案例: 在Hugging Face的`transformers`库中,我们可以直接提取并可视化BERT模型的注意力权重。以下代码片段展示了如何获取特定层、特定头对输入句子“The cat sat on the mat”的注意力分布: ```python from transformers import AutoTokenizer, AutoModel import torch
tokenizer = AutoTokenizer.from_pretrained("bert-base-uncased") model = AutoModel.from_pretrained("bert-base-uncased", output_attentions=True)
inputs = tokenizer("The cat sat on the mat", return_tensors="pt") outputs = model(**inputs) attentions = outputs.attentions # Tuple of (layer, batch, head, seq_len, seq_len)
提取第0层,第0个注意力头,对“cat”这个词(索引2)的注意力
cat_attention = attentions[0][0, 0, 2, :].detach().numpy() print("Attention from 'cat' to other tokens:", cat_attention) ``` 这种可视化是理解模型“思考”路径的宝贵工具,也是Prompt工程中分析模型关注点的起点。
**1.1.2 并行与深度:多头注意力与残差连接**
单一的自注意力可能倾向于学习某种单一的“关注模式”。多头注意力(Multi-Head Attention) 将Q, K, V投影到多个子空间(多个“头”),允许模型同时关注来自不同表示子空间的信息。例如,一个头可能关注语法结构(主谓关系),另一个头可能关注语义相似性(同义词)。这极大地增强了模型的表示能力。
架构全景图: ```mermaid graph TD A[输入序列 Embedding] --> B[添加位置编码]; B --> C{Transformer Encoder Block}; C --> D[多头自注意力]; D --> E[Add & Norm<br/>残差连接+层归一化]; E --> F[前馈神经网络]; F --> G[Add & Norm<br/>残差连接+层归一化]; G --> C; G --> H[输出序列];
subgraph “核心组件循环” C end
style D fill:#e1f5fe style F fill:#f3e5f5 ```
残差连接(Residual Connection) 与层归一化(Layer Normalization) 是训练极深网络(如GPT-3有96层)的关键。残差连接将输入直接加到输出上(`Output = Layer(x) + x`),缓解了梯度消失问题,使信息能够无损地流过深层网络。层归一化则对每个样本的激活进行归一化,稳定了训练过程。两者通常成对出现,称为“Pre-LN”或“Post-LN”结构。
**1.1.3 位置的注入:位置编码(Positional Encoding)**
自注意力本身是置换不变的(Permutation Invariant)。打乱输入序列的顺序,输出序列也会相应打乱,这显然不符合语言顺序的语义。因此,必须向输入嵌入(Token Embedding)中注入位置信息。原始Transformer使用正弦和余弦函数的固定位置编码: $$PE_{(pos, 2i)} = \sin(pos / 10000^{2i/d_{model}})$$ $$PE_{(pos, 2i+1)} = \cos(pos / 10000^{2i/d_{model}})$$ 其中 `pos` 是位置,`i` 是维度索引。这种设计让模型能够学习到相对位置关系,因为对于任意固定的偏移量 $k$,$PE_{pos+k}$ 可以表示为 $PE_{pos}$ 的线性函数。
后续模型(如BERT)也使用可学习的位置嵌入(Learned Position Embeddings),效果相当且更灵活。无论哪种方式,其目的都是为每个Token赋予独一无二的位置标识,使模型理解“顺序就是意义”。
---
**1.2 前馈网络与模型架构变体**
每个Transformer编码器/解码器块中,除了多头注意力,还有一个位置式前馈网络(Position-wise Feed-Forward Network, FFN)。它独立地作用于序列的每个位置,通常由两个线性变换和一个非线性激活函数(如GELU)组成: $$\text{FFN}(x) = \max(0, xW_1 + b_1)W_2 + b_2$$ 或更现代的GELU形式:$\text{FFN}(x) = xW_1 + \text{GELU}(xW_1)b_1 + b_1$。FFN充当了模型的“记忆与变换”模块,在注意力层整合了上下文信息后,对每个位置的表示进行深度非线性变换。
基于此核心模块,形成了两种主要架构:
- **编码器-解码器(Encoder-Decoder)**: 如原始Transformer、T5、BART。编码器理解输入,生成上下文表示;解码器在编码器输出和自身已生成序列的帮助下,自回归地生成输出。适用于翻译、摘要等序列到序列任务。
- **仅解码器(Decoder-Only)**: 如GPT系列、LLaMA、Claude。这是当前大语言模型的主流。它使用**掩码自注意力(Masked Self-Attention)**,确保每个位置只能看到其左侧(先前)的Token,从而进行**自回归生成**。这种架构天然适合文本生成和指令跟随任务,也是我们Prompt工程的主要交互对象。
```mermaid graph LR A[仅解码器架构<br/>如GPT] --> B[输入序列]; B --> C[掩码自注意力<br/>仅看左侧]; C --> D[前馈网络]; D --> E[输出下一个Token概率]; E --> F[采样/生成]; F --> C;
G[编码器-解码器架构<br/>如T5] --> H[编码器<br/>全注意力]; H --> I[解码器<br/>掩码注意力 + 交叉注意力]; I --> J[输出]; ```
---
**1.3 预训练:从海量文本中汲取通用智能**
Transformer架构是骨架,而预训练(Pre-training) 是为这个骨架注入“灵魂”的过程。模型在未经标注的庞大规模文本数据(数万亿Token)上,通过设计好的“自监督”目标进行训练,学习语言的统计规律、世界知识和基础推理能力。这个过程不针对任何具体下游任务,目标是获得一个通用语言表示模型。
**1.3.1 核心预训练目标**
1. 掩码语言建模(Masked Language Modeling, MLM): BERT的经典目标。随机遮盖输入序列中15%的Token(用`[MASK]`替换),训练模型预测被遮盖的原始Token。这迫使模型深入理解双向上下文,非常适合理解型任务(如分类、抽取)。关键点:预训练时的`[MASK]`在微调时不会出现,这存在“预训练-微调不一致”问题,后续工作(如RoBERTa)通过动态掩码等策略缓解。
2. 因果语言建模(Causal Language Modeling, CLM): GPT系列的目标。训练模型根据之前的所有Token,预测下一个Token。这是标准的自回归语言模型目标: $$\mathcal{L}_{\text{CLM}} = -\sum_{t=1}^{T} \log P(x_t | x_{<t})$$ 这种目标天然适用于生成任务,也是当前对话模型的基础。它让模型学会“接话茬”。
3. 下一句预测(Next Sentence Prediction, NSP): BERT的附加目标,判断两个句子是否连续。但后续研究(如RoBERTa)表明NSP在高质量数据上可能有害或无益,现代预训练大多已摒弃。
4. 前缀语言建模(Prefix Language Modeling): 一种折中方案。输入序列分为“前缀”和“后缀”,模型利用前缀(可双向)预测后缀(仅单向)。T5、UL2等模型采用类似策略,以统一编码器-解码器架构处理生成与理解任务。
数据规模与涌现能力: 预训练效果与数据量、模型参数量呈幂律关系。当模型规模(如参数>100B)和数据量(如>1T Token)突破某个阈值时,会出现涌现能力(Emergent Abilities)—— 模型在小模型上不存在,但在大模型上突然出现的能力,如复杂指令遵循、思维链推理、代码生成等。这提示我们,大模型的“智能”可能并非简单线性外推,而是系统复杂性的质变。
**1.3.2 微调范式:从通用到专用**
预训练模型是“通才”,但要高效完成特定任务(如客服对话、法律咨询),需要进行微调(Fine-tuning)。
- **有监督微调(Supervised Fine-Tuning, SFT)**: 使用高质量、结构化的(指令,输出)对数据集,对预训练模型进行标准语言模型目标或序列到序列目标的微调。这一步教会模型**遵循指令格式**和**任务特定行为**。例如,使用数万条精心编写的对话数据,让模型从“续写文本”转变为“助手式对话”。这是构建可靠对话机器人的基础步骤。
- **基于人类反馈的强化学习(Reinforcement Learning from Human Feedback, RLHF)**: 这是让模型对齐人类偏好、提升安全性和有用性的关键技术,也是ChatGPT等模型惊艳众人的核心。它是一个三阶段过程:
1. SFT模型: 如上所述,获得一个基础指令遵循模型。 2. 训练奖励模型(Reward Model, RM): 让人类标注员对SFT模型的多个回答排序(哪个更好)。基于这些排序数据,训练一个奖励模型,其输出是一个标量分数,反映人类对回答的偏好程度。 3. 强化学习微调(如PPO): 将SFT模型作为策略(Policy),奖励模型作为价值评估者。使用近端策略优化(PPO)等算法,优化策略,使其生成的回答能获得奖励模型的高分,同时惩罚其与SFT初始模型偏离过大的“KL散度”,以防模型崩溃或遗忘预训练知识。
```mermaid graph LR A[SFT模型] --> B[生成多个回复]; B --> C[人类标注员排序]; C --> D[训练奖励模型 RM]; D --> E[PPO优化]; E --> F[最终对齐模型]; B -.->|作为KL惩罚参考| E; ``` RLHF并非完美,它存在奖励黑客(Reward Hacking)、标注偏差、过度优化等风险。但它标志着从“预测文本”到“满足偏好”的范式转变。
指令微调(Instruction Tuning) 与对齐(Alignment) 是当前SFT和RLHF的核心目标,它们共同塑造了模型在Prompt交互中的行为模式:有帮助(Helpful)、诚实(Honest)、无害(Harmless)。
---
**1.4 Tokenization:模型眼中的世界**
模型无法直接理解字符,其“感官”是Token——文本的基本处理单元。Tokenization(分词)是将原始字符串分割成Token序列的过程,它决定了模型的“词汇表”和“世界观”。
**1.4.1 主流算法:BPE/WordPiece/Unigram**
- **字节对编码(Byte-Pair Encoding, BPE)**: GPT-2、ChatGPT、LLaMA使用。从字符级开始,迭代合并最高频的相邻字节对作为新Token。能有效处理未登录词(OOV)和形态丰富的语言。
- **WordPiece**: BERT使用。类似BPE,但合并标准基于最大化语言模型概率,而非单纯频率。
- **Unigram**: SentencePiece工具支持,从大词汇表开始逐步剔除低概率Token。
这些子词(Subword) 分词法的共同优点是:1)控制词汇表大小(通常50k-100k);2)减少OOV;3)共享词根词缀的语义(如“running”和“run”共享“run”的Token)。
**1.4.2 对Prompt设计的直接影响**
1. 长度与成本: 模型按Token计费(API)或受限于上下文窗口。一个英文单词可能拆成多个Token(如“tokenization” -> `["token", "ization"]`),一个中文字符常为1-2个Token。Prompt设计必须精炼,避免冗余Token。 2. 边界敏感性: 分词对空格、标点极其敏感。`“hello world”` 和 `“hello_world”` 可能产生完全不同的Token序列。在Prompt中,空格、连字符、大小写都可能改变Token化结果,从而影响模型理解。 3. 特殊Token: 模型有特殊控制Token,如`[CLS]`(BERT分类)、`[SEP]`(分隔句子)、`<|endoftext|>`(GPT结束符)、`<s>`/`</s>`(LLaMA开始/结束符)。在Prompt中正确使用这些符号至关重要。 4. 多语言与代码: 不同语言的分词效率差异巨大。拉丁语系通常效率高,中文、日文等表意文字每个字约1-2 Token。代码(如Python)由于结构重复,分词效率极高。跨语言Prompt需考虑分词成本。
实用工具: 使用Hugging Face的`tokenizer`可以精确计算Token数量并查看分词结果: ```python import transformers tokenizer = transformers.AutoTokenizer.from_pretrained("gpt2") text = "Hello, 你好! Let's explore tokenization." tokens = tokenizer.encode(text) print(f"Text: {text}") pr