一开始接手这个推荐系统的时候,我脑子里全是深度学习。 那会儿刚看完几篇论文,感觉什么数据扔进去都能学出花来。 于是直接上了个三层的DNN,embedding加两个全连接层,训练batch size设128,learning rate 0.001,跑了两小时,AUC比之前的逻辑回归高了0.03。 当时还挺得意,业务方看指标也高兴。
结果上线第三天,点击率就开始掉。 一开始以为是数据回流晚了,后来排查发现,线上请求里的用户行为序列长度和我训练时假设的完全不一样。 我训练时统一padding到20,但线上有些用户的历史行为就两条,填充进去的0把模型搞懵了。 更糟的是,模型对缺失特征特别敏感,只要某个实时特征没传上来,输出就剧烈波动。 那阵子凌晨三点被报警电话叫醒的日子,真是难忘。
后来我赌气,把特征工程认真做了一遍,换成了XGBoost。 没有用任何花哨的模型结构,就是十几个手工特征组合,缺失值处理用-999填充,然后调了一下max_depth和learning_rate。 结果你猜怎么着?线下AUC比DNN还低了0.005,但线上效果稳住了,点击率甚至比之前最好记录的还高了一点。 而且推理延迟从GPU上的5ms直接降到了CPU上的0.5ms,成本省了一大半,也不用一直占着GPU资源。 这让我反思了很久。
深度学习的优势在于建模复杂关系,但前提是数据量够大、分布稳定、特征完整。 我的场景每天才几十万请求,样本量十万级别,根本不够DNN霍霍。 而且特征体系经常变,线上和训练特征不一致是常态,深度学习对这种不一致太敏感。 传统GBM反而因为每棵树只负责一部分样本,加上天然支持缺失值,抗扰动能力更强。 别迷信端到端,端到端的前提是干净、稳定的输入流。
所以我现在的原则是:先做扎实的特征工程,用LightGBM跑一个baseline,评估特征重要性和稳定性。 只有当数据量达到百万级,或者确实需要从原始文本、图像、序列中自动提取信息时,才考虑引入深度学习网络。 而且就算用深度学习,也一定要加手工特征作为兜底,千万不要把整个系统的预测能力寄托在一个黑盒上。 这是我用无数个凌晨四点换来的教训。 希望看到这篇文章的朋友,能少走我这条弯路。