🔧 大模型微调

让通用大模型适配你的业务场景 —— 从数据准备到模型部署的完整指南

为什么需要微调?

通用大模型(如 GPT-4、Llama 3)虽然能力强大,但在特定领域和任务上往往不够精准。微调(Fine-tuning)是在预训练模型基础上,使用特定领域或任务的数据继续训练,使模型适应用户需求。

迁移学习:微调的理论基础

微调的本质是迁移学习(Transfer Learning)的核心应用。预训练阶段让模型学会了语言的通用规律(语法、语义、常识推理等),这些知识以参数权重的形式存储在模型中。微调则是将这些通用知识「迁移」到特定任务上——只需要在已有知识的基础上进行小幅度调整,而不需要从头学习。

# 迁移学习的直觉理解
预训练模型 = 一个已经学会「阅读和写作」的学生
微调 = 让这个学生学习「医学」「法律」「编程」等专业知识

# 为什么有效?
# 1. 语言理解能力已具备(语法、语义、逻辑)
# 2. 只需要学习「领域知识」和「任务格式」
# 3. 所需数据量远小于从头训练(500条 vs 数万亿 token)

微调 vs Prompt Engineering vs RAG

三种让大模型适配业务需求的方式,各有优劣:

维度 Prompt Engineering RAG 微调
原理 设计提示词引导模型 检索外部知识注入上下文 用领域数据训练模型参数
成本 零训练成本 需搭建检索系统 需 GPU 训练资源
效果稳定性 波动较大 依赖检索质量 最稳定可控
推理成本 高(长 prompt 消耗 token) 高(检索 + 长上下文) 低(无需额外 prompt)
知识更新 即时 即时(更新知识库) 需重新训练
适用场景 快速验证、简单任务 知识密集型问答 格式/风格要求严格的任务

💡 最佳实践:组合使用

实际项目中往往组合使用这三种方式:用 RAG 提供实时知识,用微调控制输出格式和风格,用 Prompt Engineering 做最后的引导。例如:微调后的模型 + RAG 检索 + System Prompt = 最佳效果。

微调的典型应用场景

以下场景特别适合通过微调来提升模型表现:

🏥 垂直领域问答

医疗、法律、金融等专业领域的问答系统。微调让模型掌握专业术语和领域知识,回答更准确。

💬 客服对话系统

让模型学习企业的客服话术、产品知识和回复风格,输出符合品牌调性的回答。

📝 特定格式输出

要求模型严格按 JSON、SQL、特定模板等格式输出。微调比 Prompt 更稳定可靠。

🌐 翻译与本地化

针对特定领域(如技术文档、医学论文)的翻译任务,微调可大幅提升术语准确性和行文流畅度。

💻 代码生成

针对特定框架、API 或内部代码规范微调,生成符合团队编码标准的代码。

🎭 角色扮演与风格

让模型扮演特定角色(如教师、顾问),或模仿特定写作风格(如学术论文、营销文案)。

微调方法分类

1. 全量微调(Full Fine-tuning)

更新模型的所有参数,效果通常最好,但需要与预训练相当的显存和计算资源。

2. LoRA(Low-Rank Adaptation)

微软提出的参数高效微调方法,是目前最主流的微调方案。核心思想:冻结原始模型权重,在旁路注入两个低秩矩阵 A 和 B,只训练这两个小矩阵。

# LoRA 核心公式
W' = W + ΔW = W + B × A

# W: 原始权重矩阵(冻结)
# A: 降维矩阵 (d × r)
# B: 升维矩阵 (r × d)
# r: 秩(rank),通常取 8-64

3. QLoRA(Quantized LoRA)

在 LoRA 基础上,将基础模型量化为 4-bit(NF4 数据类型),进一步降低显存需求。使得在单张消费级 GPU(如 24GB 的 RTX 4090)上微调 7B-13B 模型成为可能。

4. 其他 PEFT 方法

🎯 方法选择建议

\n

90% 的场景推荐 LoRA / QLoRA:性价比最高,效果接近全量微调。显存不足选 QLoRA(4-bit),显存充裕选 LoRA(BF16)。只有追求极致效果且资源充足时才考虑全量微调。

数据准备

数据质量是微调成功的最关键因素。好的数据比好的算法更重要。

数据格式

最常见的微调数据格式是指令式(Instruction),每条数据包含指令、可选输入和期望输出:

[
  {
    "instruction": "将以下英文翻译成中文",
    "input": "Artificial intelligence is transforming the world.",
    "output": "人工智能正在改变世界。"
  },
  {
    "instruction": "根据以下代码解释其功能",
    "input": "def fib(n):\n    return n if n <= 1 else fib(n-1) + fib(n-2)",
    "output": "这是一个递归实现的斐波那契数列函数..."
  },
  {
    "instruction": "总结以下文章的核心观点",
    "input": "(文章内容...)",
    "output": "本文的核心观点包括三个方面:..."
  }
]

数据质量要求

数据构建策略

实战:使用 Unsloth 微调 Llama

Unsloth 是目前最高效的开源 LLM 微调库,可将 LoRA 训练速度提升 2 倍,显存占用降低 50%。

环境安装

pip install unsloth
# 依赖:PyTorch >= 2.0, CUDA >= 11.8, transformers, peft, trl

完整训练代码

from unsloth import FastLanguageModel
from trl import SFTTrainer
from transformers import TrainingArguments
from datasets import load_dataset

# 1. 加载模型(4-bit 量化,节省显存)
model, tokenizer = FastLanguageModel.from_pretrained(
    model_name="unsloth/llama-3-8b-bnb-4bit",
    max_seq_length=2048,
    load_in_4bit=True,
)

# 2. 添加 LoRA 适配器
model = FastLanguageModel.get_peft_model(
    model,
    r=16,                    # LoRA 秩
    lora_alpha=32,           # 缩放系数
    target_modules=[         # 应用 LoRA 的模块
        "q_proj", "k_proj", "v_proj", "o_proj",
        "gate_proj", "up_proj", "down_proj",
    ],
    lora_dropout=0.05,
    bias="none",
)

# 3. 准备数据
def format_prompt(example):
    text = f"""### 指令:
{example['instruction']}

### 输入:
{example.get('input', '')}

### 回答:
{example['output']}"""
    return {"text": text}

dataset = load_dataset("json", data_files="train_data.json")["train"]
dataset = dataset.map(format_prompt)

# 4. 配置训练器
trainer = SFTTrainer(
    model=model,
    train_dataset=dataset,
    args=TrainingArguments(
        output_dir="./output",
        per_device_train_batch_size=4,
        gradient_accumulation_steps=4,
        num_train_epochs=3,
        learning_rate=2e-4,
        warmup_steps=10,
        logging_steps=10,
        save_strategy="steps",
        save_steps=200,
        fp16=True,
        seed=42,
    ),
)

# 5. 开始训练
trainer.train()

# 6. 保存模型
model.save_pretrained("./finetuned_model")
tokenizer.save_pretrained("./finetuned_model")

推理测试

# 加载微调后的模型
model, tokenizer = FastLanguageModel.from_pretrained(
    model_name="./finetuned_model",
    load_in_4bit=True,
)

# 生成回答
inputs = tokenizer(
    "### 指令:\n解释什么是量子计算\n\n### 回答:\n",
    return_tensors="pt"
).to("cuda")

outputs = model.generate(**inputs, max_new_tokens=512)
print(tokenizer.decode(outputs[0], skip_special_tokens=True))

Chat 对话格式微调

实际应用中,大多数场景需要模型以多轮对话的方式工作。Chat 微调使用 ChatML 格式,让模型学会「system / user / assistant」的角色区分。

ChatML 数据格式

# 多轮对话训练数据
{
  "messages": [
    {"role": "system", "content": "你是一个专业的Python编程导师,擅长用简单例子解释复杂概念。"},
    {"role": "user", "content": "什么是递归?"},
    {"role": "assistant", "content": "递归就是函数调用自己。就像照镜子时看到镜子里还有镜子一样..."},
    {"role": "user", "content": "能给个代码例子吗?"},
    {"role": "assistant", "content": "当然!最简单的例子是计算阶乘:\n\ndef factorial(n):\n    if n <= 1:\n        return 1\n    return n * factorial(n-1)"}
  ]
}

Chat 微调代码

from trl import SFTTrainer
from transformers import TrainingArguments

# 使用 ChatML 模板格式化数据
def apply_chat_template(example, tokenizer):
    messages = example["messages"]
    text = tokenizer.apply_chat_template(
        messages, tokenize=False, add_generation_prompt=False
    )
    return {"text": text}

dataset = dataset.map(
    lambda ex: apply_chat_template(ex, tokenizer)
)

# 训练配置
trainer = SFTTrainer(
    model=model,
    train_dataset=dataset,
    args=TrainingArguments(
        output_dir="./chat-output",
        per_device_train_batch_size=2,
        gradient_accumulation_steps=8,
        num_train_epochs=3,
        learning_rate=2e-4,
        warmup_ratio=0.03,
        logging_steps=5,
        fp16=True,
    ),
    max_seq_length=2048,
)

trainer.train()

💬 对话微调注意事项

多轮对话微调时,建议使用 apply_chat_template 而非手动拼接文本,确保与模型预训练时的格式一致。同时注意 system prompt 不要过长,保持简洁的角色设定即可。

DPO 偏好对齐微调

SFT 让模型学会「怎么回答」,而 DPO(Direct Preference Optimization)让模型学会「哪种回答更好」。这是通过人类偏好数据来对齐模型输出的关键技术。

DPO 数据格式

DPO 需要「优选回答」和「淘汰回答」的成对数据:

{
  "prompt": "解释什么是黑洞",
  "chosen": "黑洞是宇宙中引力极强的区域,连光都无法逃脱。它由大质量恒星坍缩形成...",
  "rejected": "黑洞就是一个很黑的洞,什么都出不来。"
}

DPO 训练代码

from trl import DPOTrainer, DPOConfig

# 基于 SFT 模型继续做 DPO 对齐
dpo_trainer = DPOTrainer(
    model=model,               # SFT 后的模型
    ref_model=None,            # None 表示隐式参考模型
    args=DPOConfig(
        output_dir="./dpo-output",
        per_device_train_batch_size=2,
        learning_rate=5e-5,    # DPO 学习率通常比 SFT 更小
        beta=0.1,              # KL 散度惩罚系数
        num_train_epochs=2,
        logging_steps=10,
        fp16=True,
    ),
    train_dataset=dpo_dataset,
    tokenizer=tokenizer,
)

dpo_trainer.train()

🎯 SFT → DPO 完整流程

完整的对齐流程是:预训练模型 → SFT(学会对话格式)→ DPO(学会偏好对齐)。SFT 让模型能按照指令回答,DPO 让模型的回答更符合人类期望(更有帮助、更安全、更诚实)。

训练监控与调试

训练过程中实时监控是保证质量的关键:

关键监控指标

使用 Weights & Biases 监控

import wandb

# 初始化 W&B
wandb.init(project="llm-finetune", name="lora-llama3-medical")

# 在 TrainingArguments 中启用
args = TrainingArguments(
    ...,
    report_to="wandb",       # 自动上报指标到 W&B
    logging_steps=10,
    eval_strategy="steps",   # 每隔 eval_steps 评估一次
    eval_steps=50,
)

# W&B 面板会自动生成:
# - Loss 曲线(train / eval)
# - 学习率变化图
# - 梯度范数监控
# - 超参数记录与对比

常见 Loss 曲线解读

# 正常收敛:Loss 平稳下降
t=0    t=100   t=200   t=300
2.5 ──╮
      │  ╲
1.8 ──┤    ╲
      │      ╲───
1.2 ──┤          ╲───
      │              ╲──
0.8 ──┴──────────────────

# 过拟合:Train Loss 下降,Eval Loss 上升
# 发散:Loss 突然飙升 → 降低学习率
# 震荡:Loss 上下波动 → 减小学习率或增加 batch size

模型合并与部署

LoRA 微调后,需要将适配器权重与基础模型合并,才能部署到生产环境。

合并 LoRA 权重

from peft import PeftModel
from transformers import AutoModelForCausalLM

# 加载基础模型
base_model = AutoModelForCausalLM.from_pretrained(
    "meta-llama/Llama-3-8B",
    torch_dtype="auto",
)

# 加载 LoRA 适配器
model = PeftModel.from_pretrained(
    base_model,
    "./finetuned_model",  # LoRA 权重目录
)

# 合并权重(将 LoRA 参数融入基础模型)
merged_model = model.merge_and_unload()

# 保存合并后的完整模型
merged_model.save_pretrained("./merged_model")
tokenizer.save_pretrained("./merged_model")

导出为 GGUF 格式(本地部署)

# 转换为 llama.cpp 格式,支持 CPU/Mac 本地运行
# 1. 转换为 safetensors
python convert_hf_to_gguf.py ./merged_model --outfile model-f16.gguf

# 2. 量化为 Q4_K_M(推荐,体积和质量的平衡点)
./llama-quantize model-f16.gguf model-q4_k_m.gguf Q4_K_M

# 3. 使用 Ollama 运行
# 创建 Modelfile
echo 'FROM ./model-q4_k_m.gguf' > Modelfile
ollama create my-model -f Modelfile
ollama run my-model

使用 vLLM 部署 API 服务

# 生产环境推荐使用 vLLM 提供高性能推理服务
pip install vllm

# 启动 OpenAI 兼容 API 服务
python -m vllm.entrypoints.openai.api_server \
    --model ./merged_model \
    --host 0.0.0.0 \
    --port 8000 \
    --gpu-memory-utilization 0.9

# 测试调用
curl http://localhost:8000/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{"model": "./merged_model", "messages": [{"role": "user", "content": "你好"}]}'

微调工具生态对比

工具 特点 适合场景 上手难度
Unsloth 速度 2x、显存 -50%,自定义 CUDA 内核优化 追求训练效率,消费级 GPU ⭐⭐
LLaMA-Factory Web UI + CLI,支持 100+ 模型,内置多种数据集 快速实验,不想写太多代码
Axolotl YAML 配置驱动,支持多种微调方法 喜欢配置化、可重复的实验 ⭐⭐
HF PEFT 官方库,生态最完善,灵活度最高 需要完全控制训练流程 ⭐⭐⭐
Mistral FT Mistral 官方工具,针对 Mistral 模型优化 Mistral 模型微调 ⭐⭐

关键超参数

参数 推荐值 说明
r(rank) 8 - 64 LoRA 秩,越大效果越好但参数越多。简单任务 8-16,复杂任务 32-64
lora_alpha r 的 2 倍 缩放系数,通常设为 rank 的 2 倍(如 r=16, alpha=32)
learning_rate 1e-4 ~ 3e-4 学习率。LoRA 微调通常用 2e-4,过大容易不稳定
epochs 2 - 5 训练轮次。数据量大用 2-3 轮,数据量小可用 4-5 轮
batch_size 4 - 16 结合 gradient_accumulation 使用,有效 batch = batch × accum_steps
max_seq_length 512 - 4096 最大序列长度,根据数据长度分布设置,过长浪费显存
lora_dropout 0.05 - 0.1 LoRA 层的 Dropout,防止过拟合

模型评估

微调完成后需要系统评估模型效果,确保质量达标:

自动评估指标

人工评估维度

# 使用 LLM-as-Judge 自动评估
from openai import OpenAI

judge = OpenAI()

def evaluate_response(question, response):
    result = judge.chat.completions.create(
        model="gpt-4",
        messages=[{
            "role": "user",
            "content": f"请评估以下回答的质量(1-10分):\n\n问题:{question}\n回答:{response}\n\n请从准确性、完整性、流畅性三个维度打分并说明理由。"
        }]
    )
    return result.choices[0].message.content

常见问题与解决方案

⚠️ 过拟合

模型只在训练数据上表现好,新数据表现差。解决:增加数据量、减少 epochs、增大 lora_dropout、使用早停策略。

⚠️ 灾难性遗忘

微调后丢失了预训练时的通用能力。解决:混入少量通用数据、降低学习率、使用较小的 rank。

⚠️ 训练不稳定

Loss 震荡或突然飙升。解决:降低学习率、增加 warmup steps、检查数据中的异常样本。

⚠️ 显存不足(OOM)

训练时 GPU 显存溢出。解决:使用 4-bit 量化(QLoRA)、减小 batch_size、缩短 max_seq_length。

显存需求参考

模型大小 全量微调 LoRA (BF16) QLoRA (4-bit)
1B - 3B 24GB+ 8GB 4GB
7B - 8B 8×A100 24GB 8-12GB
13B - 14B 16×A100 40GB 16GB
70B 8×A100 80GB 160GB+ 48GB

🚀 实践建议

新手推荐路线:先用 Unsloth + QLoRA 在消费级 GPU(RTX 3090/4090)上微调 7B-8B 模型,快速验证效果。确认方案可行后,再考虑更大模型或更高精度的训练。记住:数据质量 > 模型大小 > 训练技巧


📖 补充知识

以下章节对正文中的核心概念做深入展开,帮助理解微调背后的数学原理与关键细节。

补充 1:低秩矩阵与 LoRA 的数学原理

什么是「秩」(Rank)?

矩阵的是指矩阵中线性无关的行(或列)的最大数目,直觉上可以理解为矩阵所包含的「独立信息维度数」。一个 m×n 的矩阵,秩最大为 min(m, n)。秩越低,矩阵能用越少的独立成分来描述。

低秩分解的核心思想

任何一个秩为 r 的矩阵 W(m×n),都可以分解为两个更小的矩阵的乘积:

# 低秩分解
W ≈ B × A

# W: m × n 矩阵(秩为 r)
# B: m × r 矩阵(列满秩)
# A: r × n 矩阵(行满秩)
# r << min(m, n)  ← 关键!秩远小于矩阵维度

# 参数量对比
# 原始矩阵:m × n 个参数
# 分解后:  m × r + r × n = r(m + n) 个参数
# 当 r 很小时,参数量大幅减少!
低秩矩阵分解示意图
低秩矩阵分解:大矩阵拆解为两个小矩阵的乘积,大幅减少参数量

LoRA 如何应用低秩分解

在 Transformer 中,权重更新量 ΔW 被认为是低秩的——虽然权重矩阵本身维度很高(如 4096×4096),但微调时真正需要改变的「有效信息」维度很低。LoRA 利用这一点:

LoRA 架构示意图
LoRA 前向传播:原始权重 W 冻结,仅训练低秩增量 ΔW = BA
# LoRA 的核心公式
h = Wx + ΔWx = Wx + BAx

# W: 原始权重矩阵(冻结,不训练)
# ΔW = BA: 低秩分解的增量矩阵
#   B: d × r  (r 通常 8~64)
#   A: r × k
# x: 输入向量

# 参数量对比(以 d=k=4096 为例)
# 全量微调:4096 × 4096 = 16,777,216 参数
# LoRA r=8:4096×8 + 8×4096 = 65,536 参数(仅为原来的 0.39%!)
# LoRA r=16:4096×16 + 16×4096 = 131,072 参数(0.78%)

秩 r 的选择

r 值 可训练参数 适用场景 说明
r = 4~8 极少 简单任务(格式调整、风格迁移) 参数效率高,但表达能力有限
r = 16~32 适中 大多数微调任务(推荐默认值) 平衡效果与效率
r = 64~128 较多 复杂任务(知识注入、跨领域适配) 接近全量微调效果
r > 128 很多 特殊需求 优势逐渐减弱,不如直接用全量微调

💡 直觉理解

想象一个 1000×1000 的像素图片(100 万参数),但如果它只是一个渐变背景,实际上只需要几个颜色值和方向就能描述——这就是「低秩」的含义。LoRA 假设微调时模型权重的变化也是这种「简单变化」,所以用很少的参数就能捕捉到。

补充 2:量化(Quantization)原理

为什么需要量化?

大模型的参数占用了大量显存。一个 7B 参数的模型,用 FP32 存储需要约 28GB 显存(7B × 4 bytes),而大多数消费级 GPU 只有 8-24GB。量化通过降低每个参数的存储精度来大幅减少显存占用。

不同精度的数值表示

精度 位数 数值范围 7B 模型显存 质量损失
FP32 32 bit ±3.4×10³⁸ ~28 GB 无(基准)
FP16 16 bit ±6.5×10⁴ ~14 GB 极小
BF16 16 bit ±3.4×10³⁸ ~14 GB 极小(范围同 FP32)
INT8 8 bit -128 ~ 127 ~7 GB 较小
INT4 4 bit -8 ~ 7 ~3.5 GB 有一定损失

主流量化方案

💡 量化选择建议

QLoRA 微调选 bitsandbytes 4-bit(与 LoRA 原生集成);生产推理部署选 AWQ/GPTQ(质量更好);本地 CPU 运行选 GGUF。量化到 4-bit 通常只损失 1-3% 的性能,但显存节省 75%。

补充 3:灾难性遗忘(Catastrophic Forgetting)

什么是灾难性遗忘?

微调时模型在新数据上训练,可能会「忘记」原有的通用能力。例如:微调医疗问答后,模型可能不再擅长写代码或回答常识问题。这是因为新任务的梯度更新覆盖了之前学到的知识。

遗忘发生在哪里?

缓解策略

策略 原理 效果
冻结部分层 只训练少量层(如 LoRA),保留其余层的知识 ⭐⭐⭐⭐
数据混合 混入 10-20% 通用数据(如对话、指令数据) ⭐⭐⭐⭐⭐
低学习率 小步更新,减少对已有知识的破坏 ⭐⭐⭐
弹性权重合并(EWC) 限制「重要参数」的变化幅度 ⭐⭐⭐⭐
模型合并(Model Merging) 微调后与原始模型加权平均(如 DARE、TIES) ⭐⭐⭐⭐

如何检测遗忘?

# 在微调前后分别评估通用 benchmark
benchmarks = {
    "MMLU": "综合知识(57 个学科)",
    "HumanEval": "代码生成",
    "HellaSwag": "常识推理",
    "ARC": "科学问答",
    "TruthfulQA": "事实准确性",
}

# 如果微调后通用 benchmark 下降 > 5%,说明存在明显遗忘
# 解决方案:混入通用数据或降低学习率

补充 4:学习率预热(Warmup)与调度策略

为什么需要 Warmup?

训练初期,模型参数是随机初始化的(或从预训练权重开始),此时梯度方向不稳定。如果直接用大学习率,参数会被「推」到错误的方向,导致训练不稳定甚至发散。Warmup 让学习率从 0 逐步增大到目标值,给模型一个「热身」阶段。

常见调度策略

Warmup 步数设置

# 经验法则:warmup 步数 = 总步数的 3%~10%

# 示例:10000 步训练
warmup_steps = 500    # 5% warmup

# 学习率变化过程
# Step 0:    lr = 0
# Step 100:  lr = 0.4e-4   (20% warmup)
# Step 250:  lr = 1.0e-4   (50% warmup)
# Step 500:  lr = 2.0e-4   (100% warmup,到达目标值)
# Step 2500: lr = ~1.8e-4  (cosine decay 开始)
# Step 5000: lr = ~1.0e-4  (中间值)
# Step 10000: lr = 0       (衰减到 0)

💡 实用建议

微调时推荐 Linear Warmup + Cosine Decay,warmup 比例 5%。如果训练 loss 初期震荡严重,可以增大 warmup 比例到 10%。Unsloth 默认已配置好调度策略,通常不需要手动调整。

补充 5:混合精度训练(Mixed Precision)

什么是混合精度?

混合精度训练是指在训练过程中同时使用多种数值精度:前向传播和反向传播用 FP16/BF16(快、省显存),参数更新用 FP32(精确)。这样既享受低精度带来的速度优势,又保持训练的数值稳定性。

FP16 vs BF16 的关键区别

特性 FP16 BF16
指数位 5 bit(范围小) 8 bit(范围大,同 FP32)
尾数位 10 bit(精度高) 7 bit(精度略低)
数值范围 ±6.5×10⁴ ±3.4×10³⁸
溢出风险 较高(需要 Loss Scaling) 极低(无需 Loss Scaling)
硬件支持 Volta+ (2017) Ampere+ (2020)
推荐度 ⭐⭐⭐ ⭐⭐⭐⭐⭐(首选)

Loss Scaling(FP16 专用)

FP16 范围小,梯度值可能低于最小可表示值变成 0(下溢)。Loss Scaling 将 loss 乘以一个大数(如 1024),梯度相应放大,更新前再除回来:

# FP16 训练流程
loss = model(inputs)              # FP16 前向
scaled_loss = loss * 1024         # 放大 loss
scaled_loss.backward()            # 梯度也被放大 1024 倍
optimizer.step()                  # 内部自动除以 1024 恢复

# BF16 不需要这步,因为数值范围足够大,梯度不会下溢

💡 实践建议

优先使用 BF16(A100/RTX 3090+ 支持)。如果 GPU 不支持 BF16,用 FP16 + Loss Scaling。QLoRA 默认使用 BF16,无需额外配置。

补充 6:评估指标深入理解

Perplexity(困惑度)

衡量模型对文本的「预测困难程度」。值越低,模型对文本越「不意外」。

# Perplexity = exp(交叉熵损失)
# PPL = 1:完美预测(不可能达到)
# PPL = 10:模型比较确定,每步约 10 个候选词
# PPL = 100:模型很不确定,每步约 100 个候选词
# PPL > 500:模型基本无法预测

# 局限:只衡量语言建模能力,不反映指令遵循或事实准确性

文本生成评估指标

指标 衡量什么 适用场景 局限
BLEU n-gram 精确率(生成与参考的重叠) 翻译任务 不关心语义,只看词面匹配
ROUGE 召回率(参考文本被覆盖的程度) 摘要任务 同样不关心语义
BERTScore 基于 BERT 嵌入的语义相似度 通用文本生成 计算成本高
LLM-as-Judge 用 GPT-4 等强模型打分评估 对话质量、指令遵循 成本高、可能有偏见

人工评估 vs 自动评估

微调评估 Checklist

# 微调后应该评估的维度
✅ 任务准确率:在目标任务上的表现(核心指标)
✅ 通用能力保持:MMLU / HellaSwag 是否下降
✅ 格式遵循:输出是否符合要求的格式
✅ 安全性:是否产生有害/不当内容
✅ 一致性:多次采样结果是否稳定
✅ 延迟/吞吐:推理速度是否满足要求