被产品经理逼着调参的那两周:一个普通CS本科生的AI训练实战手记

半夜部署日记
2025-12-15 15:54
阅读 2000

大家好,我是一名普通一本 CS 专业的大四狗,坐标成都,刚拿了个本地二线大厂的 offer 正在等入职。最近在家闲得发慌(其实是被催着提前熟悉项目),干脆把上个月帮实习团队搞的一个小模型调优项目复盘写下来。毕竟,谁让我重度依赖 ChatGPT 和 Claude 写代码呢?不输出点东西感觉对不起每天白嫖的 token。

事情的起因特别“互联网”——我们组负责公司内容推荐系统的冷启动模块,产品经理在双11前两周突然拍脑袋说:“能不能让新用户首屏推荐点击率再提 5%?”
我:???
要知道这模块跑的是轻量级 DNN + 特征交叉,线上已经稳定跑了半年,数据 pipeline、特征工程、AB 实验都跑通了。现在要临时压榨性能,还卡 deadline,这不是拿算法工程师当炼丹童子使吗?

但没办法,PM 手里握着 KPI,我们手里只有 GPU 显存。于是,我这个即将入职的“准员工”就被拉来当壮丁,和正式同事一起卷这个项目。说是卷,其实大部分时间都在和 OOM 报错、梯度爆炸、验证集 loss 不下降斗智斗勇。


别看是小模型,照样能把你搞崩溃

我们的原始模型其实挺朴素:输入层接 embedding,中间三层全连接,最后 sigmoid 输出点击概率。训练数据是过去一个月的新用户行为日志,大约 200w 条样本,正负样本比 1:4(还算健康)。用 PyTorch Lightning 搭的,训练脚本也就 200 行出头。

但问题就出在这“朴素”上。一开始我天真地以为,调参无非就是改改 learning rate、batch size、加个 dropout。结果第一轮跑完,验证 AUC 只有 0.68,离线上基线 0.72 差了一大截。更离谱的是,训练 loss 稳稳下降,验证 loss 却在第 3 个 epoch 开始反弹——典型的过拟合。

当时真的想砸电脑。还好 Claude 提醒我:“你有没有检查特征分布漂移?” 我一查,好家伙,训练集里“用户设备类型”这个特征,iOS 占 60%,但线上新流量里 Android 占 70%。这不就相当于拿 iPhone 用户的数据去预测华为用户的行为?难怪效果差。

教训一:模型没毛病,数据先背锅。

我们赶紧和数据运营同学对齐,重新切分训练/验证集,按天滚动采样,确保分布对齐。同时加了特征重要性分析(SHAP 值),砍掉几个高 cardinality 但贡献低的 categorical 特征。这一波操作后,AUC 直接拉到 0.71,差点泪目。


调参不是玄学,但确实有点“看天吃饭”

解决了数据问题,真正的调优才开始。这里分享几个我觉得真有用的技巧,不是网上抄的那种“调大学习率”废话。

1. Learning Rate 别瞎试,用 LR Finder

以前我都是凭感觉设 0.001,后来被同事安利了 PyTorch 的 lr_finder(或者 fastai 里的版本)。原理很简单:从很小的 lr 开始,每个 batch 线性增大,记录 loss 变化。理想情况下 loss 会先快速下降,然后突然上升,拐点附近就是最佳 lr。

# 伪代码示意
lr_finder = LRFinder(model, optimizer, criterion)
lr_finder.range_test(train_loader, end_lr=1, num_iter=100)
lr_finder.plot()  # 找 loss 最陡降处

我们跑出来最佳 lr 在 3e-4 左右,比默认的 1e-3 效果好不少。而且配合 cosine annealing with warm restarts,收敛更快,还不容易卡局部最优。

2. Batch Size 不是越大越好,尤其显存紧张时

我们用的是 2080Ti(别笑,成都这边很多公司还在用这卡),batch size 超过 1024 就 OOM。但小 batch 又导致梯度噪声大。后来用了 梯度累积(gradient accumulation)

accum_steps = 4
for i, (x, y) in enumerate(dataloader):
    loss = model(x, y)
    loss = loss / accum_steps  # scale loss
    loss.backward()
    
    if (i + 1) % accum_steps == 0:
        optimizer.step()
        optimizer.zero_grad()

相当于逻辑上 batch size = 4096,物理上只占 1024 的显存。训练稳定性提升明显,AUC 又涨了 0.005。

3. 别忽略“无聊”的工程细节

比如 随机种子固定。有次我两次跑同一套参数,结果 AUC 差了 0.01,差点以为代码有 bug。后来发现是没 fix seed,PyTorch/CUDA/dataloader 的随机性叠加起来影响不小。

torch.manual_seed(42)
np.random.seed(42)
random.seed(42)
torch.backends.cudnn.deterministic = True

还有 早停(early stopping)策略。我们设了 patience=5,监控验证集 AUC 而不是 loss——因为业务目标是点击率,AUC 更贴近实际效果。有一次模型在第 8 轮就停了,省了 2 小时训练时间,运维大哥直夸我会过日子。


算法选择:有时候简单就是美

最初 PM 还提议上 GNN 或者 Transformer,说“现在不都流行 Attention 吗”。我和 mentor 对视一眼,默默打开了 FLOPs 计算器……结论很现实:在线服务延迟要求 <50ms,模型推理必须 <20ms。那些 fancy 的结构根本塞不进线上 pipeline。

最后我们回归朴实:在原始 DNN 上加了 Feature-wise Linear Modulation (FiLM) —— 一种轻量级的特征条件归一化。原理是对每个特征通道做 affine transform,参数由另一个小网络生成。计算开销几乎可忽略,但 AUC 提升了 0.008。

class FiLM(nn.Module):
    def __init__(self, feat_dim, cond_dim):
        super().__init__()
        self.gamma_net = nn.Linear(cond_dim, feat_dim)
        self.beta_net = nn.Linear(cond_dim, feat_dim)

    def forward(self, x, cond):
        gamma = self.gamma_net(cond)
        beta = self.beta_net(cond)
        return gamma * x + beta

上线后 AB 实验显示 CTR +4.2%,虽然没到 PM 要的 5%,但也够交差了。毕竟算法不是万能的,运营和产品预期管理也很关键——这话是 mentor 教我的,深以为然。


性能对比:调优前后到底差多少?

为了说服 PM 和运维接受新模型,我们做了详细对比。数据如下(测试环境:2080Ti, CPU 8核, batch=1024):

指标 原始模型 调优后模型 提升
AUC 0.720 0.741 +2.9%
训练时间 / epoch 8m 12s 9m 05s +11%
推理延迟 (p99) 18ms 21ms +17%
显存占用 5.2GB 5.8GB +12%
线上 CTR (AB实验) 基线 +4.2% 达标 ✅

虽然推理慢了点,但运维看了数据后也没多说什么——毕竟 CTR 提升直接关联 GMV,老板开心就行。


开发心得:工具链比算法更重要

这次项目让我深刻体会到,在工业界,模型只是拼图的一小块。真正决定成败的,往往是那些“非算法”环节:

  • 数据 pipeline 是否健壮:我们用 Airflow 调度,某次上游表延迟导致特征缺失,模型直接崩了。
  • 监控是否到位:接入了 Prometheus + Grafana,实时看 loss、AUC、QPS,出问题秒定位。
  • AB 实验框架是否可靠:运营同学反馈“怎么新模型反而点击少了?”,结果发现是分流逻辑写错了……

最搞笑的是上周五晚上,我刚提交代码准备下班,CI 突然报错:

AssertionError: Negative sampling ratio must be > 0

翻了半天才发现是测试用例里 mock 数据没更新。当时真想给测试同学发个“下次请用生产数据格式”。


写在最后:调参如修行

说实话,作为一个还没正式入职的菜鸟,能参与这种贴近业务的项目,真的赚到了。以前在学校总觉得“调参=调学习率+batch size”,现在才知道,模型训练是一个系统工程,需要算法、开发、运营、产品多方拉扯妥协的结果

我也终于理解为什么大厂要分 MLE(Machine Learning Engineer)和 Researcher——我们不是在追求 SOTA,而是在有限资源下找到性价比最高的解

对了,入职前 HR 问我有什么期待,我说:“希望能配一张 4090。”
HR 笑了:“我们还在用 1080Ti 呢。”
我:???成都的二线厂,原来这么 hardcore 吗……

Anyway,这篇水文就当是给自己留个笔记。如果你也在被 PM 逼着调参,不妨试试 LR Finder + 梯度累积 + 数据对齐三件套。不行的话……就祭出程序员终极奥义:
重启一下,说不定就好了。

(完)

评论 0

最热最新
暂无评论
半夜部署日记Lv.1
0
影响力
0
文章
0
粉丝