【CTR预估技术演进】从FM到DeepFM:因子分解机家族的原理、演进与实战
1. 从逻辑回归到FM:为什么我们需要特征交叉?
十年前我刚入行推荐系统时,整个行业还在用逻辑回归(LR)打天下。记得第一次看到LR模型在稀疏特征上的表现时,简直怀疑人生——明明特征工程做得那么辛苦,AUC却死活上不去0.7。直到遇到FM(Factorization Machines),我才真正理解特征交叉的魔力。
逻辑回归就像个固执的老教授,只会盯着单个特征的重要性。比如判断用户是否会点击游戏广告,它看到"男性"特征权重高就给高分,看到"周末"特征权重高也给高分,但就是不懂"男性+周末"这个组合会产生化学反应。这导致在实际业务场景中,LR模型总是欠拟合。
而FM模型像是开了天眼,它通过向量内积的方式自动学习特征间的关系。具体来说,每个特征都会对应一个隐向量,当两个特征经常共同出现时,它们的向量方向就会越来越接近。比如"啤酒"和"尿布"这两个看似不相关的商品,在FM的向量空间里会越靠越近,这就是著名的"啤酒与尿布"效应。
举个例子,我们在电商推荐中常用这些特征组合:
- 用户年龄段 + 商品品类
- 用户性别 + 商品价格区间
- 访问时段 + 商品促销力度
FM的数学表达看似简单,却暗藏玄机:
# FM模型预测公式 def fm_predict(w0, w, v, x): # 一阶项 linear = w0 + np.sum(w * x) # 二阶交互项 interactions = 0 for i in range(len(x)): for j in range(i+1, len(x)): interactions += np.dot(v[i], v[j]) * x[i] * x[j] return linear + interactions这个公式的巧妙之处在于,它用向量内积⟨v_i,v_j⟩代替了传统的权重系数w_ij,使得即使某些特征组合在训练集中从未出现过,只要它们的隐向量被充分训练过,模型也能给出合理预测。
2. FM的工程实现与调优实战
第一次实现FM模型时,我掉进了不少坑。最深刻的一个教训是:直接按照论文公式实现,训练速度慢得让人崩溃。后来才发现,FM的二次项其实可以优化计算:
# 优化后的二阶项计算 sum_square = np.sum(np.dot(x, v) ** 2) square_sum = np.sum(np.dot(x ** 2, v ** 2)) interaction = 0.5 * (sum_square - square_sum)这个trick把复杂度从O(kn²)降到O(kn),当特征维度n很大时,速度提升能达到上百倍。在我们的广告系统中,单机就能支持每秒上万次的预测请求。
另一个实战经验是关于隐向量维度k的选择。早期我总认为k越大越好,直到有次把k设到256,模型效果反而下降。后来通过实验发现,对于大多数业务场景,k在8-32之间就足够:
| 隐向量维度k | 训练AUC | 验证AUC | 训练时间 |
|---|---|---|---|
| 4 | 0.782 | 0.768 | 23min |
| 8 | 0.791 | 0.776 | 31min |
| 16 | 0.793 | 0.777 | 45min |
| 32 | 0.794 | 0.775 | 68min |
| 64 | 0.794 | 0.773 | 112min |
从表格可以看出,k=16时模型已经达到最佳平衡。这也印证了FM论文的观点:在数据稀疏的场景下,低维隐向量反而能更好地泛化。
在特征处理方面,我有三个实用建议:
- 对数值特征进行分桶处理,比如将年龄离散化为10-20、20-30等区间
- 对类别特征采用均值编码而非one-hot,特别是高基类别
- 添加重要的业务特征组合作为显式特征
3. 从FM到FFM:当特征遇到场域
当我在某电商平台尝试用FM优化推荐系统时,发现一个奇怪现象:同样的"用户性别+商品品类"组合,在不同商品类目下表现差异很大。比如"女性+美妆"在服饰类目效果很好,但在数码类目却不起作用。这就是FFM(Field-aware Factorization Machine)要解决的问题。
FFM引入了field(场域)的概念,比如我们可以定义这些field:
- 用户基础属性(性别、年龄等)
- 商品基础属性(品类、价格等)
- 上下文信息(时间、位置等)
关键突破在于,FFM让每个特征在不同field下有不同的隐向量表示。比如"女性"这个特征:
- 与商品品类交互时,使用v_女性_商品field
- 与时间交互时,使用v_女性_时间field
这相当于给模型装上了"场景感知"能力。在我们的实验中,FFM相比FM在CTR预估任务上平均提升了2-3%的AUC。
但FFM的实现要复杂得多,这里分享一个工程上的经验:由于FFM的参数数量是FM的field_num倍,必须特别注意正则化和早停:
# FFM参数初始化示例 self.v = np.random.normal( scale=1/np.sqrt(k), size=(n_features, n_fields, k) ) # 使用场域敏感的特征交互 def ffm_interaction(): for i in features: for j in features: if j > i: field_j = get_field(j) v_i = v[i][field_j] # 特征i对特征j所在field的向量 field_i = get_field(i) v_j = v[j][field_i] # 特征j对特征i所在field的向量 interaction += np.dot(v_i, v_j) * x[i] * x[j]在实际业务中,是否选择FFM需要权衡:
- 当不同field的特征交互模式差异明显时,FFM优势大
- 当特征field划分不明确或数据量较小时,可能FM更合适
- FFM的训练成本通常是FM的3-5倍
4. DeepFM:当传统模型遇上深度学习
2017年第一次看到DeepFM论文时,我正为如何融合深度模型发愁。传统的Wide&Deep需要精心设计wide部分的特征交叉,而DeepFM完美解决了这个问题——它让FM自动处理二阶特征交互,DNN处理高阶交互,两者共享输入。
实现一个基础的DeepFM并不复杂:
class DeepFM(nn.Module): def __init__(self, num_features, embedding_size): super().__init__() # FM部分 self.fm_linear = nn.Linear(num_features, 1) self.embeddings = nn.ModuleList([ nn.Embedding(num_features, embedding_size) for _ in range(num_features) ]) # Deep部分 self.mlp = nn.Sequential( nn.Linear(num_features*embedding_size, 256), nn.ReLU(), nn.Linear(256, 128), nn.ReLU(), nn.Linear(128, 1) ) def forward(self, x): # FM一阶项 fm_1st = self.fm_linear(x) # FM二阶项 embeds = [e(x[:,i].long()) for i,e in enumerate(self.embeddings)] sum_square = torch.sum(torch.stack(embeds), dim=0) ** 2 square_sum = torch.sum(torch.stack([e**2 for e in embeds]), dim=0) fm_2nd = 0.5 * (sum_square - square_sum) # Deep部分 deep_input = torch.cat(embeds, dim=1) deep_out = self.mlp(deep_input) return torch.sigmoid(fm_1st + fm_2nd + deep_out)在实际业务中,DeepFM有几个调优重点:
- 调整FM和DNN部分的权重比例
- 对稀疏特征和稠密特征采用不同的embedding策略
- 使用残差连接防止梯度消失
去年我们AB测试显示,DeepFM相比纯FM在电商推荐场景下CTR提升12.6%,这主要得益于它捕捉到了更多高阶特征组合,比如"用户历史行为序列+当前上下文+商品属性"这样的复杂模式。
5. 因子分解机家族选型指南
面对这么多算法变种,如何选择适合自己业务的模型?根据我在多个行业的实战经验,总结出这个决策框架:
场景一:中小规模数据+强业务特征
- 选择:基础FM
- 原因:训练快,解释性强
- 案例:某垂直电商的初版推荐系统,特征维度<1k
场景二:多源异构特征+明显场域划分
- 选择:FFM
- 原因:能建模场域特定交互
- 案例:旅游平台的跨业务线推荐(酒店+机票+景点)
场景三:海量数据+复杂用户行为
- 选择:DeepFM
- 原因:自动学习高阶特征组合
- 案例:头部内容平台的个性化信息流
在模型部署阶段,有几个工程细节特别重要:
- 在线服务时对embedding查表做缓存
- 对数值特征进行动态分桶
- 实现模型的热更新机制
最近我们还在探索FM与其他技术的结合,比如:
- FM+图神经网络:用于社交关系增强的推荐
- FM+强化学习:动态调整推荐策略
- FM+多任务学习:同时优化点击率和停留时长
这些尝试都取得了不错的效果,但核心思想始终不变:如何更好地建模特征之间的交互关系。这也是因子分解机家族经久不衰的根本原因——它抓住了机器学习最本质的问题之一。
