优化器全景
| Optimizer | 优化的是 | 成本 | 适用 |
|---|---|---|---|
| LabeledFewShot | 从训练集直接取 k 条当 demo | 无 | baseline 对照 |
| BootstrapFewShot | 让学生自己生成 demo(正确的才留) | 低 | 大多数场景 |
| BootstrapFewShotWithRandomSearch | 上面 + 多次采样挑最佳组合 | 中 | 预算充足 |
| MIPROv2 | 同时优化指令文本 + demo | 高 | 精度关键场景 |
| BootstrapFinetune | 用编译过的 teacher 微调学生模型 | 很高 | 蒸馏到小/开源模型 |
准备数据
from dspy import Example trainset = [ Example(question="法国的首都?", answer="巴黎").with_inputs("question"), Example(question="H2O 是什么?", answer="水").with_inputs("question"), # ... 至少 20-50 条,多多益善 ]
with_inputs 很关键
必须告诉 DSPy 哪几个字段是"输入",其余的才会当成 gold label 用来评分。
必须告诉 DSPy 哪几个字段是"输入",其余的才会当成 gold label 用来评分。
BootstrapFewShot:入门首选
from dspy.teleprompt import BootstrapFewShot bootstrap = BootstrapFewShot( metric=exact_match, max_bootstrapped_demos=4, # 最多用 4 个由 teacher 生成的 demo max_labeled_demos=16, # 备选的人工 demo 个数 max_rounds=2, # 最多 2 轮 bootstrap ) compiled = bootstrap.compile(student=RAG(), trainset=trainset)
原理
- 遍历 trainset 的每个样本,让 student 当前版本跑一遍
- 如果预测 + trace 能通过 metric → 收集起来作为候选 demo
- 把最多 k 个候选 demo 塞进 student 的每个 Predictor 的 few-shot 区
- 这就是"自助法":学生先跑对几道,就用这些题反过来教自己
什么时候用 teacher ≠ student
bootstrap.compile(student, teacher=strong_program, trainset=...) — 让 GPT-4o 做 teacher 产出 demo,再把 demo 塞给 GPT-4o-mini 做 student。成本降一半,精度掉得少。
BootstrapFewShotWithRandomSearch
from dspy.teleprompt import BootstrapFewShotWithRandomSearch bootstrap_rs = BootstrapFewShotWithRandomSearch( metric=metric, max_bootstrapped_demos=4, max_labeled_demos=16, num_candidate_programs=10, # 采样 10 套不同 demo 组合 num_threads=8, ) compiled = bootstrap_rs.compile(student=RAG(), trainset=trainset, valset=valset)
比 BootstrapFewShot 贵一个数量级,但验证集上通常能再涨 2-8%。
MIPROv2:state-of-the-art
from dspy.teleprompt import MIPROv2 mipro = MIPROv2( metric=metric, prompt_model=dspy.LM("openai/gpt-4o"), # 专门负责生成候选指令 task_model=dspy.LM("openai/gpt-4o-mini"), # 执行任务的学生 auto="medium", # "light" | "medium" | "heavy" ) compiled = mipro.compile( student=RAG(), trainset=trainset, valset=valset, requires_permission_to_run=False, )
它在干什么
- Instruction 提议:prompt_model 看 task 描述和部分数据,生成 N 个候选指令
- Demo 引导:类似 Bootstrap,为每个 Predictor 收集候选 demo
- 贝叶斯优化:把 (指令, demo 组合) 看成超参,用 TPE 在 valset 上搜索
- 选最优:valset 分数最高的配置写回 student
auto="light"/"medium"/"heavy" 对应不同搜索步数,heavy 跑几小时很常见,但分数通常是所有优化器里最高的。
BootstrapFinetune:蒸馏到开源模型
from dspy.teleprompt import BootstrapFinetune # 1) 先用强 teacher 编译一个高分程序 teacher = mipro.compile(RAG(), trainset=trainset, valset=valset) # 2) 把 teacher 的高质量 trace 当训练数据,微调小模型 finetune = BootstrapFinetune(metric=metric, num_threads=8) student = finetune.compile( teacher, trainset=trainset, target="meta-llama/Llama-3-8B-Instruct", epochs=3, lr=1e-5, ) # student 现在是一个 LoRA 微调后的 Llama-3,调用成本 1/20
Optimizer 选择流程
你有多少预算?
│
├── 几美元 ──▶ BootstrapFewShot
│
├── 几十美元 ──▶ BootstrapFewShotWithRandomSearch
│ │
│ └── 精度还不够? ──▶ MIPROv2(auto="light")
│
├── 几百美元 ──▶ MIPROv2(auto="medium" 或 "heavy")
│
└── 几千美元 + 要上线开源模型 ──▶ MIPROv2 → BootstrapFinetune
关键超参解读
max_bootstrapped_demos
塞给 Predictor 的最大示例数。4-8 通常够,太多会挤压 context 还可能导致过拟合风格。
max_labeled_demos
从 trainset 直接取的"保底"示例数。当 bootstrap 失败时兜底。
num_threads
并发调 LLM 的线程数,直接决定你 rate-limit 撞墙的速度。gpt-4o-mini tier 1 建议 <= 8。
num_candidate_programs
RandomSearch 采样多少套组合。10-20 常见,上不封顶。
训练/验证/测试三集的划分
import random random.seed(42) random.shuffle(all_examples) n = len(all_examples) trainset = all_examples[: int(n*0.6)] valset = all_examples[int(n*0.6) : int(n*0.8)] testset = all_examples[int(n*0.8) :]
Optimizer 只看 train+val,testset 只在最后报告分数时用一次——不然你会得到一个"在 test 上调参的假象"。
成本控制 4 招
- 开缓存:默认 DSPy 的 LM 缓存就开着,relaunch 复用之前的调用
- 分层模型:prompt_model 用强模型,task_model 用便宜模型
- 小集合先试:拿 30 条跑 BootstrapFewShot 看看上限,再决定要不要 MIPRO
- dry run 先看成本:
dspy.settings.track_usage=True统计 token 花销
compile 后做什么
# 存 compiled.save("rag_v3.json") # 换机器加载 prog = RAG() prog.load("rag_v3.json") # 看看优化器选了什么 demo / 什么指令 for name, pred in prog.named_predictors(): print(name, pred.extended_signature.instructions) print(pred.demos)
本章小结
- BootstrapFewShot 入门首选,RandomSearch 稳升精度,MIPROv2 上限最高
- BootstrapFinetune 用于蒸馏到开源/自有模型,降长期成本
- trainset/valset 分开,testset 留最后一次用
- 优化器选 prompt_model 强、task_model 便宜,成本可控