AI模型调优那些年,我踩过的坑和攒下的经验
上周五晚上九点半,办公室只剩我和运维小哥还在盯着训练任务的loss曲线。屏幕上那条线卡在0.42一动不动已经快两个小时了,产品那边催着周一上线新推荐策略,而我脑子里却在想:这破模型是不是又过拟合了?要不要再加点dropout?
作为在快手干了六年的老架构师,从零搭过推荐、风控、内容理解好几个核心系统,我早就习惯了这种“Deadline驱动学习”的节奏。去年双11前,我们团队临时接到任务要优化短视频打标模型的准确率,时间只有两周。那时候我才真正意识到,光会调API和跑notebook远远不够——AI模型调优是一门综合工程艺术,涉及数据、算法、计算资源、甚至团队协作。
最近参加杭州本地一个技术沙龙,有位刚毕业的小兄弟问我:“面试官总问‘你怎么调参’,除了learning rate还能说啥?” 我笑了笑,心想:要是真能一句话讲清楚,我们也不用天天加班到十点了。
今天就借这个机会,聊聊我在实际业务中总结的一些不那么教科书式的调优技巧。这些经验有些来自深夜debug的血泪史,有些是在和算法同事撕逼(划掉)讨论中悟出来的,希望能帮你在面对“面试题挑战”时多点底气。
别急着调参,先搞清楚你的数据
很多人一上来就疯狂试不同的优化器、调整batch size,但往往忽略了最根本的问题:数据质量。
我们之前有个用户兴趣建模项目,线上CTR一直上不去。团队最初怀疑是模型结构太简单,于是换了Transformer,加了各种attention机制,结果效果微乎其微。后来我拉了个数据分布对比图,发现训练集里70%的样本都是“搞笑”类视频,而线上真实流量中“知识科普”占比高达40%。典型的训练-推理分布偏移!
解决办法其实很土:重新采样 + 动态加权。我们给低频类别加了权重系数,并引入Focal Loss缓解长尾问题。代码改动不大,但CTR直接提升了8个点。
# 自定义Focal Loss,解决类别不平衡
class FocalLoss(nn.Module):
def __init__(self, alpha=1, gamma=2, reduction='mean'):
super().__init__()
self.alpha = alpha
self.gamma = gamma
self.reduction = reduction
def forward(self, inputs, targets):
ce_loss = F.cross_entropy(inputs, targets, reduction='none')
pt = torch.exp(-ce_loss)
focal_loss = self.alpha * (1 - pt) ** self.gamma * ce_loss
return focal_loss.mean() if self.reduction == 'mean' else focal_loss.sum()
记住:垃圾进,垃圾出(Garbage in, garbage out) 在AI领域尤其致命。花两天时间做EDA(探索性数据分析),可能比你调一周参数都管用。
算法选型不是越新越好,适合业务才是王道
前阵子团队来了个名校博士,特别推崇Swin Transformer,说CNN已经过时了。结果拿它去训一个轻量级的封面图分类任务,不仅训练慢,推理延迟还超标,被运维小哥骂得狗血淋头。
说实话,在快手这种高并发场景下,模型复杂度必须和业务SLA对齐。我们内部有个不成文的规矩:
| 业务类型 | 可接受延迟 | 推荐模型复杂度 |
|---|---|---|
| 实时推荐 | < 50ms | 轻量CNN / MLP |
| 离线打标 | 无严格限制 | Transformer / GNN |
| 风控拦截 | < 20ms | 极简模型 + 规则引擎 |
所以别盲目追新。有时候一个精心调优的ResNet-18,比乱配超参的ViT效果更好、更稳。
调优是个系统工程,别只盯着loss
很多工程师(包括曾经的我)有个误区:只要train loss下降,模型就在变好。大错特错!
我们有一次训练一个短视频多标签分类模型,train loss一路狂降,val loss也看着不错,结果一上线,bad case暴增。后来发现是因为用了标准交叉熵,而业务上要求“宁可漏判,不可错判”——比如把政治敏感内容标成娱乐,后果很严重。
于是我们改用带约束的损失函数,并在评估阶段引入业务指标(如precision@k、false positive rate)。这才对齐了优化目标和业务目标。
另外,早停(Early Stopping) 这招看似简单,但很多人用错了。不要只看validation loss,要结合多个指标。我们现在的训练脚本里,早停条件至少包含三项:
if val_f1 < best_f1 - 0.01 and val_precision < best_precision - 0.02 and epochs_no_improve > 5:
break # 综合判断,防止单一指标误导
关于超参数:别靠玄学,要靠实验设计
说到超参调优,很多人还在手动试 learning_rate=0.001, 0.01, 0.1... 然后截图发群里问“哪个好?”
在快手,我们早就用上了自动化调参平台(基于Optuna + Kubernetes)。但即使如此,也有讲究:
- 先调影响最大的参数:比如learning rate、batch size、weight decay。这三个定下来,其他参数才有意义。
- 用对数尺度搜索:learning rate在[1e-5, 1e-1]之间搜,别用线性。
- 固定随机种子:不然每次实验不可复现,等于白干。
下面是我们常用的一个调参优先级表:
| 参数类别 | 调整优先级 | 常见范围 | 备注 |
|---|---|---|---|
| Learning Rate | ★★★★★ | 1e-5 ~ 1e-2 | 用cosine warmup |
| Batch Size | ★★★★☆ | 64 ~ 1024 | 受GPU显存限制 |
| Weight Decay | ★★★★☆ | 1e-6 ~ 1e-3 | 防止过拟合 |
| Dropout Rate | ★★★☆☆ | 0.1 ~ 0.5 | 大模型可适当提高 |
| Optimizer | ★★★☆☆ | AdamW / SGD with momentum | AdamW更适合Transformer |
顺便吐槽一句:产品经理总觉得“调参就像调音量旋钮,扭一下就好”,殊不知背后是几十次失败实验堆出来的经验。
面试题挑战?其实考的是工程思维
现在很多大厂(阿里、网易都在招)的AI岗面试,早就不问“什么是反向传播”这种基础题了。他们更爱问:
“如果模型线上效果突然下降,你会怎么排查?”
“如何在有限GPU资源下最大化模型性能?”
这类问题没有标准答案,但能看出你是否具备综合解决问题的能力。
我在面试候选人时,特别喜欢听他们讲真实项目中的取舍。比如:
- 为了满足延迟要求,主动放弃某个SOTA模型;
- 发现数据泄露后,如何快速回滚并修复;
- 在双11流量洪峰前,如何做模型降级预案。
这些细节,比背一百个公式都更能体现水平。
最后一点真心话
写这篇文章的时候,我正在用Rust重写一个特征预处理模块(是的,最近被Rust圈粉了,内存安全+高性能太香)。这让我想到:AI工程化最终拼的不是谁的模型最 fancy,而是谁能把整个pipeline做得更稳、更快、更省。
调优不是魔法,而是一系列有依据的决策。每一次loss下降的背后,都是对数据、算法、系统的深入理解。
如果你也在杭州,欢迎来参加我们下个月的内部技术分享——虽然可能又要加班到九点半,但至少咖啡管够。
共勉。

评论 0