2.1 LLM 基本原理
大语言模型(LLM)是基于 Transformer 架构、在海量文本上预训练的超大规模神经网络。理解其底层原理,能帮助你写出更好的 Prompt、更高效地微调模型、在应用开发中做出正确的架构决策。
一、LLM 概述
1. 什么是大语言模型
定义:Large Language Model(LLM)是通过海量文本数据预训练的 Transformer 神经网络,参数量从数十亿到数千亿,能够理解和生成自然语言。
核心能力:
| 能力 | 说明 | 示例 |
|---|---|---|
| 语言理解 | 阅读理解、情感分析、实体识别 | "这段话表达的是正面还是负面情绪?" |
| 语言生成 | 续写、创作、翻译、摘要 | "帮我写一段产品介绍" |
| 推理能力 | 逻辑推理、数学计算、代码生成 | "这个 bug 的原因是什么?" |
| 上下文学习 | Few-shot Learning,无需微调适应新任务 | 给几个例子就能模仿格式 |
| 通用性 | 一个模型处理多种任务 | 同时能写代码、翻译、问答 |
2. 本质:预测下一个 Token
LLM 的核心任务是:给定前文,预测下一个 Token 的概率分布。
$$P(w_t \mid w_1, w_2, ..., w_{t-1})$$
这种机制叫做**自回归(Auto-Regressive)**生成——每次产出一个 Token,然后将其加入输入继续生成,直到结束。
输入: "今天天气"
↓ 预测
Step 1: "今天天气" → "很"
Step 2: "今天天气很" → "好"
Step 3: "今天天气很好" → "。"
Step 4: "今天天气很好。" → [END]关键洞察:
- LLM 是概率生成器,不是知识数据库——它生成的是"最可能的下一个词",不保证事实正确
- 自回归意味着模型无法回溯修改已生成的内容,所以 Chain of Thought(让模型先推理再回答)非常有效
- 训练目标(预测下一个词)看似简单,但当模型规模足够大、数据足够多时,会涌现出推理、翻译、编程等复杂能力
3. 发展历程
2017 Transformer 架构提出(Attention is All You Need)
│
2018 BERT(双向编码)+ GPT-1(自回归生成)
│
2019 GPT-2(1.5B 参数)—— 文本生成引起关注
│
2020 GPT-3(175B 参数)—— 展现涌现能力(Emergent Abilities)
│
2022 ChatGPT 发布 —— 引爆大模型应用热潮
│
2023 GPT-4 + 开源爆发(LLaMA、Qwen、Mistral)+ 多模态融合
│
2024 Claude 3.5 + GPT-4o + 开源追赶(LLaMA 3、Qwen2、DeepSeek-V2)
│
2025 推理模型兴起(o1、DeepSeek-R1)—— 强化推理成为新范式二、Transformer 在 LLM 中的应用
Transformer 基础已在 1.3 节介绍,本节聚焦于 LLM 中的架构选择和关键改进。
1. 三种架构范式
Decoder-Only(GPT 系列)—— 当前主流
只使用 Transformer 的 Decoder 部分,自回归生成(从左到右预测下一个 Token)。
输入: [Token1, Token2, Token3, ...]
↓ Masked Self-Attention(每个位置只能看到自己和之前的位置)
↓ × N 层
输出: 预测每个位置的下一个 Token 概率- 训练目标:语言建模(预测下一个词)
- 优势:生成能力强,适合对话、创作、代码
- 代表:GPT-3/4、LLaMA、Qwen、DeepSeek、Mistral、Claude
# GPT 风格的 Decoder-Only 架构(简化版)
class GPTBlock(nn.Module):
def __init__(self, d_model, num_heads, d_ff, dropout=0.1):
super().__init__()
self.ln1 = nn.LayerNorm(d_model) # Pre-Norm(LLaMA 风格)
self.attn = MultiHeadAttention(d_model, num_heads)
self.ln2 = nn.LayerNorm(d_model)
self.ffn = nn.Sequential(
nn.Linear(d_model, d_ff),
nn.GELU(),
nn.Linear(d_ff, d_model),
)
self.dropout = nn.Dropout(dropout)
def forward(self, x, mask):
# Pre-Norm + Masked Self-Attention + 残差
h = self.ln1(x)
x = x + self.dropout(self.attn(h, h, h, mask))
# Pre-Norm + FFN + 残差
h = self.ln2(x)
x = x + self.dropout(self.ffn(h))
return xEncoder-Only(BERT 系列)
只使用 Encoder 部分,双向编码(同时看到前后文)。
- 训练目标:Masked Language Model(随机遮掩 15% 的 Token 让模型预测)
- 优势:理解能力强,适合分类、NER、问答
- 代表:BERT、RoBERTa、DeBERTa
- 局限:不擅长生成任务
Encoder-Decoder(T5 系列)
完整 Transformer 架构,Encoder 理解输入,Decoder 生成输出。
- 训练目标:序列到序列(Seq2Seq)
- 优势:适合翻译、摘要等"输入→输出"转换任务
- 代表:T5、BART、mT5
架构选择指南
生成式应用(对话/创作/代码) → Decoder-Only(GPT 系列) ← 当前主流
分类/抽取任务 → Encoder-Only(BERT 系列)
翻译/摘要 → Encoder-Decoder(T5 系列)2. LLM 中的关键改进
现代大模型在原始 Transformer 基础上做了很多工程优化:
位置编码优化
| 方法 | 说明 | 使用模型 |
|---|---|---|
| 绝对位置编码 | 原始 Transformer 三角函数编码 | GPT-2 |
| 可学习位置编码 | 位置 Embedding 作为参数学习 | BERT、GPT-3 |
| RoPE(旋转位置编码) | 在注意力计算中注入相对位置,支持长上下文外推 | LLaMA、Qwen、Mistral |
| ALiBi | 通过注意力偏置实现位置编码 | MPT、BLOOM |
# RoPE 旋转位置编码(核心思想)
def apply_rotary_pos_emb(q, k, cos, sin):
"""对 Q 和 K 应用旋转位置编码"""
# 将 q, k 拆分为实部和虚部
q_real, q_imag = q[..., ::2], q[..., 1::2]
k_real, k_imag = k[..., ::2], k[..., 1::2]
# 旋转变换
q_out_real = q_real * cos - q_imag * sin
q_out_imag = q_real * sin + q_imag * cos
k_out_real = k_real * cos - k_imag * sin
k_out_imag = k_real * sin + k_imag * cos
# 交织合并
q_out = torch.stack([q_out_real, q_out_imag], dim=-1).flatten(-2)
k_out = torch.stack([k_out_real, k_out_imag], dim=-1).flatten(-2)
return q_out, k_out注意力机制优化
| 方法 | 说明 | 效果 |
|---|---|---|
| MHA(Multi-Head Attention) | 原始多头注意力,每个头有独立的 K、V | 标准实现 |
| MQA(Multi-Query Attention) | 所有头共享一组 K 和 V | 推理加速 2-5x,KV Cache 减少 |
| GQA(Grouped-Query Attention) | 头分组,每组共享 K、V | MHA 和 MQA 的折中,LLaMA 2/3 使用 |
| Flash Attention | 利用 GPU 内存层级优化注意力计算 | 速度提升 2-4x,显存减少 |
# GQA:Grouped-Query Attention 示意
# 假设 num_heads=32, num_kv_heads=8
# 每 4 个 Q head 共享 1 个 KV head
class GroupedQueryAttention(nn.Module):
def __init__(self, d_model, num_heads, num_kv_heads):
super().__init__()
self.num_heads = num_heads # Q 的头数:32
self.num_kv_heads = num_kv_heads # KV 的头数:8
self.head_dim = d_model // num_heads
self.q_proj = nn.Linear(d_model, num_heads * self.head_dim)
self.k_proj = nn.Linear(d_model, num_kv_heads * self.head_dim)
self.v_proj = nn.Linear(d_model, num_kv_heads * self.head_dim)
self.o_proj = nn.Linear(d_model, d_model)其他改进
| 改进 | 原始 Transformer | 现代 LLM |
|---|---|---|
| 激活函数 | ReLU | SwiGLU(LLaMA)、GELU(GPT) |
| 归一化位置 | Post-Norm(层后) | Pre-Norm(层前)—— 训练更稳定 |
| 归一化方法 | LayerNorm | RMSNorm(LLaMA)—— 更快 |
| FFN 结构 | 2 层全连接 | 3 层 + 门控(SwiGLU) |
三、Tokenization 与向量化
1. 为什么需要 Tokenization
神经网络只能处理数字,Tokenization 是文本与数字之间的桥梁:
"Hello, world!" → Tokenizer → [15496, 11, 995, 0] → Embedding → 向量矩阵2. Token 的概念
- Token:文本的基本处理单位(可以是字、词、子词、甚至字节)
- Vocabulary(词表):所有可能 Token 的集合(通常 32K-150K 个)
- Token ID:Token 在词表中的索引编号
3. 主流 Tokenization 方法
字符级(Character-level)
每个字符是一个 Token。词表小(几百),但序列过长、难以捕捉语义。
词级(Word-level)
每个词是一个 Token。语义清晰,但词表巨大且无法处理未登录词(OOV)。
子词级(Subword-level)—— 主流方法
在字符和词之间取平衡,高频词保持完整,低频词拆分为子词片段。
BPE(Byte Pair Encoding)
GPT 系列使用。算法从字符开始,迭代合并最频繁的字符对:
初始: l o w e r → 词表: {l, o, w, e, r}
合并1: lo w e r → 词表加入: {lo}
合并2: low e r → 词表加入: {low}
合并3: lowe r → 词表加入: {lowe}
合并4: lower → 词表加入: {lower}WordPiece — BERT 使用,用 ## 标记子词:playing → play + ##ing
SentencePiece — Google 开源,LLaMA、Qwen 使用,与语言无关,直接处理 Unicode
4. Tokenization 实践
from transformers import AutoTokenizer
# --- 对比不同 Tokenizer ---
# GPT-2 Tokenizer(BPE)
gpt2_tok = AutoTokenizer.from_pretrained("gpt2")
text = "Hello, world! 你好世界"
tokens = gpt2_tok.tokenize(text)
print(f"GPT-2 Tokens: {tokens}")
# ['Hello', ',', 'Ġworld', '!', 'Ġ', 'ä½', 'ł', 'å¥', '½', 'ä', '¸', 'ĸ', 'ç', 'ķ', 'Į']
ids = gpt2_tok.encode(text)
print(f"GPT-2 Token IDs: {ids}")
print(f"GPT-2 Token 数量: {len(ids)}") # 中文分得很碎
# 使用 tiktoken(OpenAI 官方 Tokenizer)
import tiktoken
enc = tiktoken.encoding_for_model("gpt-4")
tokens = enc.encode("Hello, world! 你好世界")
print(f"GPT-4 Token 数量: {len(tokens)}")
# 中英文 Token 效率对比
texts = {
"英文": "The quick brown fox jumps over the lazy dog",
"中文": "敏捷的棕色狐狸跳过了那只懒狗",
}
for lang, text in texts.items():
n = len(enc.encode(text))
print(f"{lang}: {len(text)} 字符 → {n} tokens(比率: {n/len(text):.2f})")5. 特殊 Token
不同模型使用不同的特殊 Token 来标记结构信息:
| Token | 用途 | 使用模型 |
|---|---|---|
[CLS] [SEP] | 分类标记、句子分隔 | BERT |
<s> </s> | 序列开始、结束 | LLaMA |
<|endoftext|> | 文档结束 | GPT-2 |
<|im_start|> <|im_end|> | 指令开始、结束 | ChatML 格式 |
6. Token Embedding(词嵌入)
将离散的 Token ID 映射为连续的高维向量:
import torch.nn as nn
# Token ID → 向量
vocab_size = 32000 # 词表大小
d_model = 4096 # 嵌入维度
embedding = nn.Embedding(vocab_size, d_model)
token_ids = torch.tensor([15496, 11, 995, 0]) # "Hello, world!"
vectors = embedding(token_ids)
print(f"输入: {token_ids.shape}") # (4,)
print(f"输出: {vectors.shape}") # (4, 4096)
# 最终输入 = Token Embedding + Position Encoding7. Tokenization 对开发的影响
上下文长度限制
- 模型上下文窗口以 Token 计数(如 4K、128K、200K)
- 中文 1 字 ≈ 2-3 tokens(GPT)或 ≈ 1 token(Qwen 等国产模型)
- 英文 1 词 ≈ 1-1.5 tokens
API 成本计算
import tiktoken
def estimate_cost(text, model="gpt-4", input_price_per_1m=30):
"""估算 API 调用成本"""
enc = tiktoken.encoding_for_model(model)
num_tokens = len(enc.encode(text))
cost = num_tokens / 1_000_000 * input_price_per_1m
return num_tokens, cost
text = open("document.txt").read()
tokens, cost = estimate_cost(text)
print(f"Token 数: {tokens}, 预估输入成本: ${cost:.4f}")模型选择考量
- 中文应用优先选择中文优化模型(Qwen、DeepSeek),Token 效率更高,成本更低
- 多语言场景选择多语言 Tokenizer 的模型
四、预训练与微调范式
1. 预训练(Pre-training)—— "通识教育"
在海量无标注文本上训练模型,学习语言的通用表示和世界知识。
预训练数据:
- 规模:数万亿 tokens(LLaMA 2: 2T tokens,LLaMA 3: 15T tokens)
- 来源:网页(Common Crawl)、书籍、代码(GitHub)、论文、Wikipedia、对话
- 质量控制:去重、过滤低质内容、有害内容过滤、多样性平衡
- 成本:数百万到数千万美元级别
预训练任务:
自回归语言建模(Causal LM)— GPT 系列
给定前文,预测下一个 Token:
输入: "The cat sat on the"
标签: "cat sat on the mat"
损失: CrossEntropy(预测分布, 真实Token)# 自回归训练的核心逻辑
def causal_lm_loss(model, input_ids):
# input_ids: [BOS, w1, w2, w3, w4]
# 标签右移: [w1, w2, w3, w4, EOS]
logits = model(input_ids[:, :-1]) # 输入: [BOS, w1, w2, w3]
labels = input_ids[:, 1:] # 标签: [w1, w2, w3, w4]
loss = F.cross_entropy(logits.view(-1, vocab_size), labels.view(-1))
return loss掩码语言建模(Masked LM)— BERT 系列
随机遮掩 15% 的 Token,让模型预测被遮掩的内容:
输入: "The [MASK] sat on the [MASK]"
标签: "cat" "mat"预训练产出:Base Model(基座模型)
- 知识渊博,但不懂遵循指令
- 你问它 "如何写代码",它可能续写为 "如何写代码的教程第一章..."
普通开发者无需从头预训练,直接使用开源模型或调用 API。
2. 微调(Fine-tuning)—— "职业培训"
将通用基座模型适配为特定任务或领域的专家。
指令微调(Instruction Tuning / SFT)
让模型学会遵循人类指令,从"续写机器"变为"助手"。
训练数据格式:
{
"instruction": "请将以下句子翻译成英文",
"input": "今天天气很好",
"output": "The weather is nice today."
}# 指令微调的数据构造
def format_instruction(example):
"""构造 ChatML 格式的训练数据"""
return f"""<|im_start|>system
你是一个有帮助的助手。<|im_end|>
<|im_start|>user
{example['instruction']}
{example.get('input', '')}<|im_end|>
<|im_start|>assistant
{example['output']}<|im_end|>"""典型数据集:Alpaca(52K)、ShareGPT、UltraChat
RLHF(人类反馈强化学习)
让模型更符合人类偏好(有用、诚实、无害)。
Step 1: 收集偏好数据
用户提问 → 模型生成多个回复 → 人类标注哪个更好
回复 A: ★★★★☆ (更好)
回复 B: ★★☆☆☆
Step 2: 训练奖励模型(Reward Model)
学习人类偏好,给回复打分
Step 3: 强化学习优化(PPO 算法)
用奖励模型的反馈优化语言模型代表:ChatGPT、Claude
DPO(直接偏好优化)
RLHF 的简化版本,直接从偏好数据学习,无需单独训练奖励模型。更简单、更稳定,是最新趋势。
# DPO 的核心思想(简化)
# 直接优化:让模型更倾向于生成"好回复",远离"坏回复"
def dpo_loss(model, ref_model, chosen, rejected, beta=0.1):
"""
chosen: 人类偏好的回复
rejected: 人类不偏好的回复
ref_model: 参考模型(冻结的 SFT 模型)
"""
# 计算当前模型和参考模型的对数概率
log_prob_chosen = model.log_prob(chosen)
log_prob_rejected = model.log_prob(rejected)
ref_log_prob_chosen = ref_model.log_prob(chosen)
ref_log_prob_rejected = ref_model.log_prob(rejected)
# DPO 损失
logits = beta * ((log_prob_chosen - ref_log_prob_chosen) -
(log_prob_rejected - ref_log_prob_rejected))
loss = -F.logsigmoid(logits).mean()
return loss3. 参数高效微调(PEFT)
全量微调更新所有参数,显存需求大。PEFT 只训练少量参数,效果接近全量微调。
LoRA(Low-Rank Adaptation)—— AI 应用开发首选
核心思想:冻结原始权重 W,添加低秩分解矩阵 A·B。
$$W' = W + \Delta W = W + A \cdot B$$
其中 A 的形状是 (d, r),B 的形状是 (r, d),r << d(通常 r=8, 16, 32, 64)。
import torch.nn as nn
class LoRALinear(nn.Module):
"""LoRA 改造的线性层"""
def __init__(self, original_linear, rank=16, alpha=32):
super().__init__()
self.original = original_linear
self.original.weight.requires_grad = False # 冻结原始权重
d_out, d_in = original_linear.weight.shape
self.lora_A = nn.Parameter(torch.randn(d_out, rank) * 0.01)
self.lora_B = nn.Parameter(torch.zeros(rank, d_in))
self.scaling = alpha / rank
def forward(self, x):
# 原始输出 + LoRA 增量
original_output = self.original(x)
lora_output = (x @ self.lora_B.T @ self.lora_A.T) * self.scaling
return original_output + lora_output
# LoRA 的优势:
# - 仅训练 1-2% 的参数
# - 显存占用大幅降低
# - 训练速度快
# - 可插拔:LoRA 权重可保存为独立文件,方便切换不同任务QLoRA
LoRA + 4-bit 量化,进一步降低显存需求。消费级 GPU(如 RTX 4090 24GB)可微调 70B 参数模型。
# 使用 bitsandbytes + peft 进行 QLoRA 微调
from transformers import AutoModelForCausalLM, BitsAndBytesConfig
from peft import LoraConfig, get_peft_model
# 4-bit 量化配置
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4", # NormalFloat4 量化
bnb_4bit_compute_dtype=torch.float16,
bnb_4bit_use_double_quant=True, # 双重量化
)
# 加载量化模型
model = AutoModelForCausalLM.from_pretrained(
"meta-llama/Llama-2-7b-hf",
quantization_config=bnb_config,
device_map="auto",
)
# LoRA 配置
lora_config = LoraConfig(
r=16, # 秩
lora_alpha=32, # 缩放系数
target_modules=["q_proj", "k_proj", "v_proj", "o_proj"], # 应用 LoRA 的层
lora_dropout=0.05,
task_type="CAUSAL_LM",
)
# 应用 LoRA
model = get_peft_model(model, lora_config)
model.print_trainable_parameters()
# trainable params: 4,194,304 || all params: 3,504,607,232 || trainable%: 0.12%其他 PEFT 方法
| 方法 | 原理 | 特点 |
|---|---|---|
| Prefix Tuning | 在每层输入前添加可学习前缀向量 | 不改模型结构 |
| Adapter | 在 Transformer 层间插入小型全连接模块 | 灵活,可组合 |
| P-Tuning v2 | 优化每层的输入 Embedding | 效果好,但实现复杂 |
微调最佳实践
# 微调超参数参考
training_config = {
"learning_rate": 2e-5, # 小学习率(1e-5 到 5e-5)
"warmup_ratio": 0.03, # 预热比例
"num_epochs": 3, # 通常 1-5 个 epoch
"batch_size": 4, # 受显存限制
"gradient_accumulation": 8, # 梯度累积模拟大 batch
"max_seq_length": 2048, # 最大序列长度
"weight_decay": 0.01, # 权重衰减
"lora_rank": 16, # LoRA 秩
"lora_alpha": 32, # LoRA 缩放
}
# 关键原则:
# 1. 数据质量 > 数据数量(1000 条高质量数据 > 10000 条低质量数据)
# 2. 监控验证集 loss,防止过拟合
# 3. 多样化训练数据,避免模型能力退化
# 4. 从小模型开始实验,验证后再上大模型五、上下文窗口
1. 定义
上下文窗口(Context Window)是模型一次能处理的最大 Token 数量,包括输入和输出。
2. 主流模型上下文长度
| 模型 | 上下文 | 备注 |
|---|---|---|
| GPT-3.5 Turbo | 16K | 性价比高 |
| GPT-4 Turbo / GPT-4o | 128K | 长文档分析 |
| Claude 3.5 Sonnet | 200K | 可处理整本书 |
| Gemini 1.5 Pro | 1M+ | 超长上下文 |
| LLaMA 2 | 4K | 基础版 |
| LLaMA 3 | 8K-128K | 不同版本 |
| Qwen 2 | 32K-128K | 中文优化 |
| DeepSeek V2 | 128K | 开源长上下文 |
3. "Lost in the Middle" 问题
虽然窗口越来越大,但模型并非均匀关注所有位置:
关注度高 ████████░░░░░░░░░░░░░░░░░░░░░░░░████████ 关注度高
开头 中间(容易被忽略) 结尾开发启示:在 RAG 或长文档分析中,关键信息尽量放在 Prompt 的开头或结尾。
4. 长上下文技术
| 技术 | 方法 | 说明 |
|---|---|---|
| 位置插值 | 压缩位置索引到原始范围 | 扩展已有模型的上下文 |
| NTK-Aware Scaling | 改进位置插值 | 更好的长文本外推 |
| YaRN | 自适应位置缩放 | 最新方法 |
| Sliding Window Attention | 每个位置只关注附近窗口 | Mistral 使用 |
| Flash Attention 2 | 高效 GPU 实现 | 速度和显存均优化 |
| RAG | 不增大窗口,检索外部知识 | 最实用方案(第四章详解) |
六、推理机制
1. 自回归生成流程
输入: "What is AI?"
↓ Tokenize
Token IDs: [2, 1724, 374, 15592, 30]
↓ Embedding + Position
向量矩阵: (5, d_model)
↓ Transformer × N 层
输出 Logits: (5, vocab_size)
↓ 取最后位置的 Logits
下一个 Token 概率分布: (vocab_size,)
↓ 采样策略
生成 Token: "AI"
↓ 添加到输入,重复
...直到生成结束符或达到 max_tokens2. 采样策略
Temperature(温度)
温度控制概率分布的"锐度"。原始 logits 经过 $\text{softmax}(z_i / T)$ 变换:
Token 概率分布
"好" "不错" "棒" "差" 其他
Temperature=0.1 95% 3% 1.9% 0.1% ... (几乎确定选"好")
Temperature=0.7 50% 25% 20% 3% ... (有一定随机性)
Temperature=1.5 25% 22% 20% 15% ... (很随机)# Temperature 的作用
import torch
import torch.nn.functional as F
logits = torch.tensor([5.0, 3.0, 2.0, 0.5, -1.0])
for temp in [0.1, 0.5, 1.0, 1.5]:
probs = F.softmax(logits / temp, dim=-1)
print(f"T={temp:.1f}: {probs.numpy().round(3)}")
# T=0.1: [1. 0. 0. 0. 0. ] → 确定性
# T=0.5: [0.878 0.108 0.013 0.001 0. ]
# T=1.0: [0.583 0.158 0.058 0.013 0.003] → 默认
# T=1.5: [0.425 0.173 0.095 0.036 0.014] → 随机性使用建议:
- T = 0 ~ 0.3:代码生成、数学计算、事实问答(确定性高)
- T = 0.5 ~ 0.8:通用对话、文档摘要(平衡)
- T = 0.8 ~ 1.2:创意写作、头脑风暴(多样性高)
Top-k 采样
只从概率最高的 k 个 Token 中采样,过滤低概率选项:
def top_k_sampling(logits, k=50):
values, indices = logits.topk(k)
probs = F.softmax(values, dim=-1)
sampled_index = torch.multinomial(probs, 1)
return indices[sampled_index]Top-p 采样(Nucleus Sampling)—— 最常用
选择累积概率达到 p 的最小 Token 集合,动态调整候选集大小:
def top_p_sampling(logits, p=0.9):
sorted_logits, sorted_indices = torch.sort(logits, descending=True)
probs = F.softmax(sorted_logits, dim=-1)
cumsum = torch.cumsum(probs, dim=-1)
# 移除累积概率超过 p 的 Token
mask = cumsum - probs > p
sorted_logits[mask] = float('-inf')
probs = F.softmax(sorted_logits, dim=-1)
sampled_index = torch.multinomial(probs, 1)
return sorted_indices[sampled_index]Beam Search
保留多个候选序列,选择总体概率最高的。适合翻译/摘要,对话较少使用(容易平淡)。
3. 推理优化
KV Cache
自回归生成时,每生成一个新 Token 都需要对所有已有 Token 做注意力计算。KV Cache 缓存已计算的 Key 和 Value,避免重复计算。
# 无 KV Cache:生成第 N 个 token 需要重新计算前 N-1 个 token 的 K、V
# 有 KV Cache:只计算新 token 的 K、V,拼接到缓存中
# 效果:生成速度提升数十倍
# 代价:显存占用随序列长度线性增长量化(Quantization)
降低权重精度,减少显存、加速推理:
| 精度 | 每参数占用 | 7B 模型显存 | 精度损失 |
|---|---|---|---|
| FP32 | 4 bytes | 28 GB | 无 |
| FP16 | 2 bytes | 14 GB | 极小 |
| INT8 | 1 byte | 7 GB | 小 |
| INT4 | 0.5 bytes | 3.5 GB | 可接受 |
工具:GPTQ、AWQ(GPU)、GGUF(CPU,llama.cpp 格式)
推理参数控制
from transformers import AutoModelForCausalLM, AutoTokenizer
model = AutoModelForCausalLM.from_pretrained("meta-llama/Llama-2-7b-chat-hf")
tokenizer = AutoTokenizer.from_pretrained("meta-llama/Llama-2-7b-chat-hf")
inputs = tokenizer("What is machine learning?", return_tensors="pt")
output = model.generate(
**inputs,
max_new_tokens=512, # 最大生成长度
temperature=0.7, # 温度
top_p=0.9, # Top-p 采样
top_k=50, # Top-k 采样
do_sample=True, # 启用随机采样
repetition_penalty=1.1, # 重复惩罚(>1 减少重复)
num_beams=1, # Beam search(1=不使用)
)
result = tokenizer.decode(output[0], skip_special_tokens=True)
print(result)七、幻觉(Hallucination)
LLM 本质是概率生成器,不是知识库,会一本正经地编造事实。
产生原因:
- 预训练数据中不包含相关知识
- 模型为了"接龙"顺畅,强行编造
- 对低频知识的记忆不牢固
解决方案:
| 方案 | 方法 | 效果 |
|---|---|---|
| RAG | 检索外部知识作为上下文 | 最有效(开卷考试) |
| Prompt 约束 | "如果不确定,请说不知道" | 简单有效 |
| 降低 Temperature | 减少随机性 | 减少编造倾向 |
| 微调 | 用高质量数据微调 | 提升领域准确性 |
| 多模型验证 | 用另一个模型检查事实 | 成本高但可靠 |
八、实战练习
练习1:对比不同 Tokenizer
from transformers import AutoTokenizer
models = ["gpt2", "bert-base-uncased", "meta-llama/Llama-2-7b-hf"]
texts = [
"Hello, how are you?",
"你好,今天天气怎么样?",
"def fibonacci(n): return n if n <= 1 else fibonacci(n-1) + fibonacci(n-2)",
]
for model_name in models:
tokenizer = AutoTokenizer.from_pretrained(model_name)
print(f"\n=== {model_name} ===")
for text in texts:
tokens = tokenizer.tokenize(text)
ids = tokenizer.encode(text)
print(f" 文本: {text[:40]}...")
print(f" Tokens ({len(tokens)}): {tokens[:8]}...")练习2:调试生成参数
from transformers import AutoModelForCausalLM, AutoTokenizer
model_name = "gpt2" # 本地可以跑的小模型
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForCausalLM.from_pretrained(model_name)
prompt = "The future of artificial intelligence is"
inputs = tokenizer(prompt, return_tensors="pt")
# 对比不同参数的输出
configs = [
{"temperature": 0.1, "top_p": 1.0, "label": "低温度(确定性)"},
{"temperature": 1.0, "top_p": 0.9, "label": "中等温度 + Top-p"},
{"temperature": 1.5, "top_p": 1.0, "label": "高温度(随机)"},
]
for config in configs:
output = model.generate(
**inputs,
max_new_tokens=50,
temperature=config["temperature"],
top_p=config["top_p"],
do_sample=True,
)
text = tokenizer.decode(output[0], skip_special_tokens=True)
print(f"\n--- {config['label']} ---")
print(text)练习3:LoRA 微调入门
# 使用 LLaMA-Factory 进行快速微调(推荐方式)
# 安装:pip install llmtuner
# 或使用 PEFT + Transformers 手动微调
from transformers import AutoModelForCausalLM, AutoTokenizer, TrainingArguments, Trainer
from peft import LoraConfig, get_peft_model
from datasets import load_dataset
# 1. 加载模型
model = AutoModelForCausalLM.from_pretrained("gpt2")
tokenizer = AutoTokenizer.from_pretrained("gpt2")
tokenizer.pad_token = tokenizer.eos_token
# 2. 应用 LoRA
lora_config = LoraConfig(
r=8, lora_alpha=16,
target_modules=["c_attn"], # GPT-2 的注意力层
lora_dropout=0.05,
task_type="CAUSAL_LM",
)
model = get_peft_model(model, lora_config)
model.print_trainable_parameters()
# 3. 准备数据并训练
# ... (数据处理和 Trainer 配置)九、学习资源
| 类型 | 资源 | 说明 |
|---|---|---|
| 论文 | Attention is All You Need | Transformer 原始论文 |
| 论文 | Language Models are Few-Shot Learners (GPT-3) | 大模型能力展示 |
| 论文 | LLaMA 系列论文 | 开源模型代表 |
| 论文 | LoRA: Low-Rank Adaptation | PEFT 核心方法 |
| 课程 | Stanford CS324 - LLM | 大模型系统课程 |
| 课程 | 李沐《动手学大模型》 | 中文讲解,实战导向 |
| 工具 | HuggingFace Transformers | LLM 开发必备库 |
| 工具 | LLaMA-Factory | 微调工具集 |
| 工具 | tiktoken / tokenizers | Tokenizer 工具 |
| 文档 | OpenAI API 文档 | API 调用与参数说明 |
十、AI 应用开发要点
开发者无需从头预训练
直接使用开源模型(LLaMA、Qwen、Mistral、DeepSeek)或调用 API(OpenAI、Claude、Gemini)。
重点掌握
- 模型选择:根据任务、成本、部署环境选择合适模型
- Prompt 工程:高效利用模型能力(下一章详解)
- 微调技术:LoRA/QLoRA 适配特定领域
- 推理优化:参数调优、KV Cache、量化部署
常见应用场景决策
通用对话 → 直接调用 API(GPT-4、Claude)
领域专家 → 收集领域数据 + LoRA 微调开源模型
知识密集型任务 → RAG 检索增强(第四章)
复杂多步任务 → Agent 系统(第五章)
低成本部署 → 量化 + 小模型(7B/14B)学习建议
- 理解原理,但不要纠结数学细节
- 重点实践:调用 API → 使用开源模型 → LoRA 微调
- Transformer 和 Attention 是基础,必须理解
- 关注开源社区最新进展:模型更新快,保持学习