边喂奶边调参:一个全职妈妈的AI模型训练实战手记

开源搬砖工
2025-12-19 05:33
阅读 2640

上周五晚上十一点半,娃终于睡了。我蹑手蹑脚地打开MacBook Pro,屏幕亮起的那一刻,仿佛回到了“职场人”的身份。其实现在我已经算是“自由职业+带娃”双线作战选手了——白天陪娃搭积木、读绘本,晚上(如果娃肯睡)就撸代码、调模型。坐标成都,生活节奏是舒服的,但技术债可不会因为你在泡奶粉就自动消失。

最近接手了一个老项目重构任务:用AI模型预测某个区块链交易网络中的异常行为。说白了,就是判断一笔链上转账是不是洗钱、套利或者机器人刷量。听起来高大上,实则天天在和数据噪声、模型过拟合、还有产品经理那句“能不能再准一点?”搏斗。

这篇文章,就聊聊我在训练这个模型过程中踩过的坑、学到的技巧,顺便穿插点面试题级别的思考——毕竟,谁还没个跳槽梦呢?


问题背景:不是所有数据都配叫“训练集”

这个项目最初是外包团队做的,模型准确率只有78%,召回率更是惨不忍睹。领导拍桌子:“这玩意儿上线了会被用户骂死!” 于是锅就甩到了我头上。接手时,我心里其实有点虚:毕竟半年没碰正经的深度学习项目了,上一次跑训练还是在娃还没出生前。

数据来自某公链的历史交易记录,字段包括:发送方地址、接收方地址、金额、时间戳、Gas费、合约交互标识等。标签是人工打标的“正常/异常”。乍一看挺标准,但实际一探查,问题一堆:

  • 标签噪声严重:有些“异常”其实是新DeFi协议的正常交互;
  • 极度不平衡:异常样本只占0.3%;
  • 地址匿名性:没法直接用地址ID做特征,得靠图嵌入或行为聚类。

更离谱的是,测试环境用Windows跑出来的结果和我Mac本地完全对不上——后来发现是PyTorch版本不一致导致的随机种子失效。从此我立下规矩:开发用Mac,Windows只用来验证兼容性,其他时候请远离我的键盘。


调优实战:从“能跑”到“靠谱”

第一步:别急着上Transformer,先搞定数据

很多新人(包括曾经的我)一上来就想着用最新最炫的模型。但现实是:垃圾进,垃圾出。我花了一周时间做数据清洗和增强:

  • 把高频正常地址聚类,生成合成异常样本(SMOTE不太适用,改用GAN-based oversampling);
  • 对时间戳做周期性编码(hour-of-day, day-of-week);
  • 地址用Node2Vec做图嵌入,再拼接交易行为统计特征(如7天内交易频次、平均金额波动等)。
# 地址图嵌入示例(简化版)
from node2vec import Node2Vec

graph = build_transaction_graph(df)  # 构建交易关系图
node2vec = Node2Vec(graph, dimensions=64, walk_length=30, num_walks=200)
model = node2vec.fit(window=10, min_count=1)
address_embeddings = {node: model.wv[node] for node in graph.nodes()}

这一步让AUC直接从0.82提升到0.89。有时候,特征工程比换模型更有效——这话我面试时也常被问到:“你认为模型调优中最重要的环节是什么?”

第二步:模型选型:XGBoost vs GNN vs TabNet?

项目初期用了XGBoost,快、稳、可解释。但面对图结构信息时力不从心。后来尝试GraphSAGE,效果不错,但训练慢得像蜗牛——尤其在我这台2019款MacBook上,风扇狂转,娃都被吵醒了两次。

最后折中方案:用TabNet做主干,融合图嵌入特征。TabNet既能处理表格数据,又有注意力机制,还能输出特征重要性,方便和风控团队对齐逻辑。

from pytorch_tabnet.tab_model import TabNetClassifier

clf = TabNetClassifier(
    n_d=32, n_a=32,
    n_steps=5,
    gamma=1.3,
    optimizer_params={"lr": 0.02},
    scheduler_params={"step_size":10, "gamma":0.9},
    seed=42  # 别忘了固定随机种子!
)
clf.fit(X_train, y_train, eval_set=[(X_valid, y_valid)], max_epochs=100)

关键参数调优心得:

  • n_steps 控制决策步数,太大容易过拟合;
  • 学习率配合StepLR调度器,比AdamW更稳;
  • 一定要设早停(early stopping),不然半夜训练完发现过拟合了,真的会哭。

第三步:处理不平衡:Focal Loss + 动态采样

0.3%的正样本,普通交叉熵根本学不到东西。我试过加class_weight,效果一般。后来换成Focal Loss,聚焦难分类样本:

import torch.nn.functional as F

def focal_loss(pred, target, alpha=0.75, gamma=2):
    ce_loss = F.binary_cross_entropy_with_logits(pred, target, reduction='none')
    pt = torch.exp(-ce_loss)
    loss = alpha * (1 - pt)**gamma * ce_loss
    return loss.mean()

同时,在DataLoader里加入动态负采样:每个batch里保证至少20%是正样本。虽然训练速度慢了30%,但召回率从52%飙到81%——风控团队终于不再追着我问“为什么漏报这么多”。


面试题视角:这些坑,面试官最爱问

干这行久了你会发现,工作中踩的坑,往往就是面试题的原型。比如:

“你们怎么处理类别极度不平衡的问题?”

别只答“用SMOTE”或“调class_weight”,得结合场景:
→ 我们用GAN生成少数类 + Focal Loss + batch-level动态采样,三管齐下。

“如何评估异常检测模型的效果?”

别只说“看准确率”——在不平衡场景下,准确率是骗人的!我们主要看:

  • 召回率(Recall):不能漏掉真异常;
  • Precision@K:前K个预警中多少是真的;
  • AUC-PR曲线(不是ROC!),因为负样本太多,ROC会虚高。

顺便吐槽一句:有次面试官问我“区块链和AI怎么结合”,我差点笑出声——这不就是我天天在干的事吗?不过也提醒我:技术要落地,得懂业务。光会调参,不懂Gas费、MEV、闪电贷,模型根本抓不住关键模式。


效果对比:数字不说谎

调优前后关键指标对比如下:

指标 初始模型(XGBoost) 最终模型(TabNet + 图嵌入 + Focal Loss)
AUC-ROC 0.82 0.94
AUC-PR 0.31 0.68
Recall@0.9 Precision 48% 81%
推理延迟(单条) 8ms 15ms

虽然延迟翻了近一倍,但在风控场景下,宁可慢一点,也不能漏报。而且通过ONNX导出+TensorRT优化,线上服务延迟压回了10ms内——感谢运维小哥没把我拉黑。


写在最后:当妈后,代码更干净了

很多人觉得带娃会影响技术成长。但说实话,当了妈反而让我写代码更谨慎了。以前可能随便改个配置就push,现在会多想三遍:“这会不会半夜崩?会不会让测试小姐姐加班?”

而且,碎片化时间逼我学会了高效学习:娃午睡一小时,我就啃一篇论文;喂奶时听AI播客;甚至在游乐场排队时用手机看GitHub issue。这种“见缝插针”的能力,比什么算法都珍贵。

所以啊,别信什么“35岁危机”、“妈妈程序员不行”的鬼话。只要键盘还在响,脑子还在转,我们就永远在线。

对了,下周又要和产品开会讨论“能不能把召回率提到90%以上”……算了,先把这篇博客发了,然后去热奶瓶。毕竟,AI可以重启,娃哭了可不能等


P.S. 如果你也是一边带娃一边coding的战友,欢迎留言交流。咱们组个“深夜码农妈妈联盟”,互相打气,顺便交换防秃头秘方(狗头保命)

评论 0

最热最新
暂无评论
开源搬砖工Lv.1
0
影响力
0
文章
0
粉丝