把一个大模型调得更懂你的业务领域,听起来简单——喂数据、跑训练、看指标上涨就完事了。现实远比这残酷:领域指标确实涨了,但模型突然不会做通用任务了;或者训练跑完才发现学习率设错,几十小时的 GPU 时间打了水漂。Amazon Nova Forge 的超参优化实践,本质上就是在"领域提升"和"通用保持"之间走钢丝,同时尽量别在计算成本上摔跟头。
先选策略,再调参数
很多人拿到数据就急着填学习率和 batch size,但更前面的决策是:你的任务和数据到底该用哪种定制化策略?
Nova Forge 上常见的三种路径:
- 全量微调(Full Fine-tuning):数据量大、任务与原模型差异大时考虑。代价是计算成本高,通用能力最容易受损。
- 参数高效微调(PEFT / LoRA):数据量中等、只想调整部分行为。训练快、显存省,通用能力退化风险低,但领域提升上限也相对有限。
- 持续预训练 + 微调:需要注入大量领域知识(比如行业术语、内部文档风格),先让模型"读完领域书",再针对任务微调。
选错策略的后果比选错学习率更严重。一个只需要 LoRA 就能搞定的小数据任务,你用了全量微调,不仅浪费算力,还大概率把通用能力搅乱。
粗略判断依据:如果你的训练数据不到原模型预训练数据的 1%,优先考虑 PEFT;如果任务只是换了个输出格式或风格,LoRA 几乎总是更安全的选择。
三个最值得盯的超参
Nova Forge 的训练配置项很多,但真正决定成败的集中在三个参数上。
学习率:最危险的旋钮
学习率太大,训练震荡甚至发散;太小,模型学不动,白烧算力。领域微调还有一个更隐蔽的风险:学习率稍高就会把通用权重冲掉。
Nova Forge 上的经验值:
- 全量微调:
1e-5到5e-5,起步建议2e-5 - LoRA 微调:
1e-4到5e-4,起步建议2e-4 - 持续预训练阶段:
5e-5到1e-4
关键做法:不要只跑一个学习率。用 Nova Forge 的超参搜索功能跑 3–5 个候选值的小规模试验(少量数据、几个 epoch),看验证集loss曲线再决定正式跑的值。
Batch Size:不只是显存问题
batch size 影响梯度估计的稳定性和训练速度,但在微调场景里还有一个容易被忽略的作用:大 batch 会削弱模型对稀有样本的学习。
如果你的领域数据里有少量但关键的边缘案例(比如罕见的安全违规模式),大 batch 会把这些样本的梯度信号淹没。此时适当缩小 batch size、配合梯度累积(gradient accumulation),既保显存又保信号。
# Nova Forge 训练配置示例 — 小 batch + 梯度累积
TrainingConfig:
batchSize: 4 # 单步小 batch,保留稀有样本信号
gradientAccumulationSteps: 8 # 等效 batch size = 32
learningRate: 2e-5
maxSteps: 2000
optimizer: adamw
weightDecay: 0.01
warmupSteps: 100 # 前 100 步线性升温,防止初期震荡
把 batchSize 和 gradientAccumulationSteps 的乘积当作你的"有效 batch size"。显存不够时,缩小前者、放大后者,效果基本等价,但稀有样本的梯度不会被平滑掉。
Checkpointing:最便宜的安全网
很多人不重视 checkpoint 配置,结果训练跑到第 18 小时出了问题,只能从头再来。Nova Forge 支持按步数或按 epoch 保存 checkpoint,合理的配置能让你:
- 在训练中途回滚到通用能力尚未退化的版本
- 对比不同阶段的模型,判断是否出现过拟合
CheckpointConfig:
saveStrategy: steps # 按步数保存,比按 epoch 更灵活
saveSteps: 200 # 每 200 步一个 checkpoint
saveTotalLimit: 5 # 只保留最近 5 个,控制存储成本
evaluationStrategy: steps
evalSteps: 200 # 评估频率与保存频率对齐,方便对比
saveTotalLimit 别设太大。每个 checkpoint 可能占几 GB,保留 5 个足够回滚和对比,多了只是浪费 S3 存储。
常见的翻车模式与早期信号
Nova Forge 文章里点出了几种典型的浪费算力的错误,这里拆开说清楚。
通用能力退化:最隐蔽的失败
领域指标在涨,通用指标在跌,但你只盯了领域指标——这是最常见的"假成功"。
早期信号:训练到中期,拿 checkpoint 在一个通用基准集(比如 MMLU 子集或你自己的通用测试集)上跑一下。如果通用指标开始持续下滑,立刻停。继续跑只会更糟。
# 通用能力监控脚本 — 每 N 步在通用测试集上评估
import json, subprocess, pathlib
CHECKPOINTS_DIR = pathlib.Path("/mnt/checkpoints")
GENERAL_EVAL_SET = "general_benchmark_config.json" # 你的通用测试集配置
DOMAIN_EVAL_SET = "domain_benchmark_config.json"
def evaluate_checkpoint(ckpt_path: str, eval_config: str) -> dict:
"""调用 Nova Forge 评估 API 对单个 checkpoint 打分"""
result = subprocess.run(
[
"aws", "nova-forge", "evaluate",
"--checkpoint", ckpt_path,
"--config", eval_config,
"--output", "json"
],
capture_output=True, text=True
)
return json.loads(result.stdout)
def monitor_training():
checkpoints = sorted(CHECKPOINTS_DIR.glob("checkpoint-*"))
for ckpt in checkpoints:
domain_score = evaluate_checkpoint(str(ckpt), DOMAIN_EVAL_SET)
general_score = evaluate_checkpoint(str(ckpt), GENERAL_EVAL_SET)
print(f"{ckpt.name}: domain={domain_score['accuracy']:.3f} "
f"general={general_score['accuracy']:.3f}")
# 通用能力跌破基线 2% 就该警觉
if general_score["accuracy"] < baseline_general - 0.02:
print(f"⚠ 通用能力退化显著,建议回滚到更早 checkpoint")
break
monitor_training()
这段脚本的核心思路:把通用评估和领域评估放在同等频率上。如果你的通用测试集太大跑不动,挑一个 200–500 条的子集,覆盖常见任务类型就行,不需要全集。
学习率预热不足或过度
warmup 步数太少,前几步梯度震荡,可能直接把权重搞乱;warmup 太长,有效训练步数被浪费。
经验范围:warmup 步数占总步数的 5%–10%。2000 步的训练,warmup 设 100–200 步。
过早停止或过晚停止
早停(early stopping)是双刃剑。领域指标还在涨你就停了,模型没学够;涨了很久你才停,通用能力已经废了。
更靠谱的做法:用领域指标 + 通用指标的综合分数做早停判据,而不是只看一个。
# 综合早停判据示例
def should_stop(domain_acc, general_acc, baseline_general, step, patience=3):
"""
综合分数 = 领域提升量 - 通用退化量 * 惩罚系数
连续 patience 个 checkpoint 综合分数不涨就停
"""
penalty = 2.0 # 通用退化的惩罚权重,根据业务调整
domain_gain = domain_acc - baseline_domain
general_loss = max(0, baseline_general - general_acc)
composite = domain_gain - general_loss * penalty
return composite # 外部逻辑跟踪历史值判断是否连续不涨
penalty 系数是关键:如果你的业务对通用能力要求高,设成 3.0 甚至 5.0;如果通用能力只是锦上添花,1.0 就够。
正式训练前的低成本验证清单
在提交长时间训练任务之前,跑一遍这个清单,能避免大部分"跑完才发现问题"的惨剧:
- 数据质量抽查:随机看 20 条训练样本,确认格式正确、标签无误、没有重复或乱码。
- 小规模试跑:用 10% 数据跑 3–5 个 epoch,确认 loss 在下降、没有 NaN 或爆炸。
- 学习率扫描:同上小规模数据,跑 3 个不同学习率(比如
1e-5、2e-5、5e-5),选 loss 曲线最平稳的。 - 通用基线记录:正式训练前,用原模型在你的通用测试集上跑一次,记下分数作为 baseline。
- checkpoint 配置确认:
saveTotalLimit ≥ 3,evalSteps和saveSteps对齐。
# Nova Forge 快速试跑配置 — 用于正式训练前的验证
QuickValidationConfig:
dataFraction: 0.1 # 只用 10% 数据
maxSteps: 300 # 少量步数
batchSize: 4
gradientAccumulationSteps: 4
learningRateCandidates: # 多学习率并行试跑
- 1e-5
- 2e-5
- 5e-5
checkpointConfig:
saveSteps: 50
saveTotalLimit: 3
evalSteps: 50
earlyStopping:
patienceSteps: 3
metric: composite_score # 领域 + 通用综合指标
这轮试跑的总计算量大概只有正式训练的 3%–5%,但能帮你排除 80% 的配置错误。
写在最后
领域微调的核心矛盾从来不是"怎么让领域指标涨上去"——多跑几轮、调大学习率,指标总能涨。真正的难题是涨上去的同时别把其他东西弄坏,以及别用十倍的算力去换一倍的指标。
三个带走的原则:
- 策略优先于参数:数据量和任务差异决定了该用全量微调还是 LoRA,选错策略后面所有参数调整都是在补救。
- 通用指标和领域指标同等频率监控:只看一边必然翻车,综合分数做判据比单指标早停更安全。
- 小规模验证不亏:3% 的算力投入换来 80% 的配置错误排除,这笔账怎么算都值。