☰
深入解析 DeepFM:在 D2L 中融合因子分解机与深度神经网络实现点击率预测
2026/10/3 8:11:44 网站建设 项目流程
  • 文档
  • 教程
  • 人工智能
  • 深度学习
  • NLP
  • 计算机视觉
  • 强化学习

【免费下载链接】d2l-en

Interactive deep learning book with multi-framework code, math, and discussions. Adopted at 500 universities from 70 countries including Stanford, MIT, Harvard, and Cambridge.

项目地址:https://gitcode.com/gh_mirrors/d2/d2l-en
点击查看免费下载

深度因子分解机(Deep Factorization Machines,DeepFM)是广告点击率(CTR)预测领域最具代表性的深度学习模型之一,它通过并行融合经典二阶因子分解机与多层感知机(MLP),同时捕捉低阶与高阶特征交互。本篇指南以《动手学深度学习》(D2L)推荐系统章节中的 DeepFM 文档为核心,完整还原其模型架构推导、MXNet 源码实现与在匿名广告数据集上的完整训练流程,并深入仓库底层解析CTRDataset数据封装与train_ch13训练工具的实现细节,帮助读者从公式到代码、从数据到训练全链路掌握 DeepFM 的落地方法。

为什么需要 DeepFM:因子分解机的局限

线性交互假设与二阶瓶颈

因子分解机(FM)在 CTR 预测中被广泛使用,其核心假设是特征交互可以通过双线性范式(bilinear interactions)来建模,即任意两个特征 $i$、$j$ 的交互强度由其隐向量 $\mathbf{v}_i$ 与 $\mathbf{v}_j$ 的点积 $\langle\mathbf{v}_i, \mathbf{v}_j\rangle$ 表示(具体公式与推导见仓库中的 因子分解机文档)。

但在真实世界的广告数据中,特征交叉结构往往高度复杂且非线性,例如“用户画像 × 广告位 × 上下文”之间的高阶耦合关系难以用简单的双线性项刻画。更糟糕的是,实践中 FM 通常只使用二阶特征交互:

  • 理论上 FM 可以推广到更高阶(degree > 2)的交互,但高阶建模会带来数值不稳定与极高的计算复杂度,因此很少在实际中被采用;
  • 从复杂度看,朴素计算所有两两交互需要 $\mathcal{O}(kd^2)$,即使经过重写优化降到线性复杂度 $\mathcal{O}(kd)$,也只是解决了二阶项的计算效率,并未突破交互阶数的天花板。

深度神经网络的补位

深度神经网络擅长特征表示学习,具备捕捉精细复杂特征交互的能力。自然的思路是将深度神经网络与因子分解机集成:

  • 在 FM 上叠加非线性变换层,使其同时具备建模低阶特征组合与高阶特征组合的能力;
  • 同时,输入数据中固有的非线性结构也能被 DNN 捕获。

正是基于这一动机,Guo 等人提出了 DeepFM 模型(见文献引用Guo.Tang.Ye.ea.2017,即论文DeepFM: A Factorization-Machine based Neural Network for CTR Prediction)。

DeepFM 模型架构:FM 与 DNN 的并行融合

总体结构

DeepFM 由一个FM 组件和一个深度(deep)组件以并行结构集成而成:

  • FM 组件:与二阶因子分解机完全一致,负责建模低阶特征交互(一阶线性项 + 二阶双线性项);
  • 深度组件:一个 MLP,负责捕获高阶特征交互与非线性;
  • 共享输入:两个组件共享相同的输入/嵌入(embeddings),最终输出相加作为预测值。

值得一提的是,DeepFM 的精神与Wide & Deep架构相似——都能同时捕获记忆(memorization)与泛化(generalization)。但 DeepFM 相比 Wide & Deep 的优势在于:它通过自动识别特征组合,大幅减少了手工特征工程的工作量。

数学表达

记 FM 组件的输出为 $\hat{y}^{(FM)}$(其详细公式可参考 fm.md 中的二阶 FM 定义)。设 $\mathbf{e}_i \in \mathbb{R}^{k}$ 为第 $i$ 个字段(field)的隐特征向量,则深度组件的输入是所有字段稠密嵌入的拼接(由稀疏类别特征查表得到):

$$ \mathbf{z}^{(0)} = [\mathbf{e}_1, \mathbf{e}_2, \ldots, \mathbf{e}_f], $$

其中 $f$ 是字段(field)数量。该输入随后送入如下神经网络:

$$ \mathbf{z}^{(l)} = \alpha(\mathbf{W}^{(l)}\mathbf{z}^{(l-1)} + \mathbf{b}^{(l)}), $$

其中 $\alpha$ 为激活函数,$\mathbf{W}^{(l)}$ 与 $\mathbf{b}^{(l)}$ 分别是第 $l$ 层的权重与偏置。记深度组件的输出为 $y_{DNN}$,DeepFM 的最终预测是 FM 与 DNN 两个分支输出的求和:

$$ \hat{y} = \sigma(\hat{y}^{(FM)} + \hat{y}^{(DNN)}), $$

其中 $\sigma$ 为 sigmoid 函数,将得分压缩到 $(0,1)$ 区间以表示点击概率。整体架构如下图所示。

值得注意的是,DeepFM 并非将深度神经网络与 FM 结合的唯一方式——也可以在特征交互之上直接叠加非线性层(参见文献引用He.Chua.2017)。这一设计空间为后续的 NFM、xDeepFM 等模型留下了延伸方向。

DeepFM 的 MXNet 实现

实现思路

DeepFM 的实现与 FM 高度相似:FM 部分保持原样,额外引入一个以relu为激活函数的 MLP 块,并使用Dropout进行正则化。MLP 的神经元数量通过mlp_dims超参数灵活调节。

模型代码

仓库中 deepfm.md 给出了完整的 MXNet 实现(DeepFM继承自nn.Block):

#@tab mxnet class DeepFM(nn.Block): def __init__(self, field_dims, num_factors, mlp_dims, drop_rate=0.1): super(DeepFM, self).__init__() num_inputs = int(sum(field_dims)) self.embedding = nn.Embedding(num_inputs, num_factors) self.fc = nn.Embedding(num_inputs, 1) self.linear_layer = nn.Dense(1, use_bias=True) input_dim = self.embed_output_dim = len(field_dims) * num_factors self.mlp = nn.Sequential() for dim in mlp_dims: self.mlp.add(nn.Dense(dim, 'relu', True, in_units=input_dim)) self.mlp.add(nn.Dropout(rate=drop_rate)) input_dim = dim self.mlp.add(nn.Dense(in_units=input_dim, units=1)) def forward(self, x): embed_x = self.embedding(x) square_of_sum = np.sum(embed_x, axis=1) ** 2 sum_of_square = np.sum(embed_x ** 2, axis=1) inputs = np.reshape(embed_x, (-1, self.embed_output_dim)) x = self.linear_layer(self.fc(x).sum(1)) \ + 0.5 * (square_of_sum - sum_of_square).sum(1, keepdims=True) \ + self.mlp(inputs) x = npx.sigmoid(x) return x

逐部分解读这段代码:

  1. 嵌入层与线性层:self.embedding(维度num_inputs × num_factors)用于查表得到各字段的稠密嵌入;self.fc是每个特征的线性权重,经self.linear_layer输出一阶线性项;
  2. FM 二阶交互项:利用上一节 FM 文档中推导的等价重写公式,通过0.5 * (square_of_sum - sum_of_square)在 $\mathcal{O}(kd)$ 线性时间内计算二阶交互,避免显式枚举所有特征对;
  3. MLP 深度组件:将嵌入按embed_output_dim = len(field_dims) * num_factors展开拼接后送入 MLP,每层使用relu激活并紧跟Dropout(rate=drop_rate),最后一层输出标量 $y_{DNN}$;
  4. 输出融合:一阶项 + 二阶交互项 + 深度组件输出求和后经npx.sigmoid得到最终的点击概率。

关键超参数

参数含义文档默认值
field_dims各字段的词典大小列表(由数据集计算得到)由CTRDataset.field_dims提供
num_factors嵌入/隐因子维度 $k$10(FM 中为 20)
mlp_dimsMLP 各隐藏层神经元数[30, 20, 10]
drop_rateDropout 丢弃率0.1

数据加载与训练评估

广告数据集与 CTRDataset 封装

DeepFM 的训练沿用 FM 章节的数据加载流程,使用的同样是匿名的在线广告 CTR 数据集。数据集共34 个字段,第一列是标签(是否点击,1/0),其余为类别特征;训练集与测试集分别包含15000 与 3000 个样本(详见 ctr.md)。

数据加载的关键组件CTRDataset在仓库源码 d2l/mxnet.py 中实现,与数据集的注册条目(d2l.DATA_HUB['ctr'],包含下载地址与 SHA-1 校验码)位于同一位置。其内部逻辑要点如下:

  • 特征映射(feat_mapper):对出现次数不低于min_threshold=4的特征值建立连续索引,低于阈值的罕见特征归入默认桶,有效控制词典规模、缓解稀疏性;
  • 字段维度(field_dims):为每个字段记录len(feat_mapper) + 1(含默认桶),累计得到全局嵌入表大小;
  • 全局偏移(offsets):通过np.cumsum(field_dims)计算各字段的起始索引,__getitem__时将各字段的特征索引加上偏移量,得到拼接后的全局稀疏索引feat + self.offsets,供nn.Embedding(num_inputs, num_factors)直接查表。

训练配置

以下为 deepfm.md 中给出的完整训练流程:MLP 采用金字塔结构(30-20-10),其余超参数与 FM 保持一致:

#@tab mxnet batch_size = 2048 data_dir = d2l.download_extract('ctr') train_data = d2l.CTRDataset(os.path.join(data_dir, 'train.csv')) test_data = d2l.CTRDataset(os.path.join(data_dir, 'test.csv'), feat_mapper=train_data.feat_mapper, defaults=train_data.defaults) field_dims = train_data.field_dims train_iter = gluon.data.DataLoader( train_data, shuffle=True, last_batch='rollover', batch_size=batch_size, num_workers=d2l.get_dataloader_workers()) test_iter = gluon.data.DataLoader( test_data, shuffle=False, last_batch='rollover', batch_size=batch_size, num_workers=d2l.get_dataloader_workers()) devices = d2l.try_all_gpus() net = DeepFM(field_dims, num_factors=10, mlp_dims=[30, 20, 10]) net.initialize(init.Xavier(), ctx=devices) lr, num_epochs, optimizer = 0.01, 30, 'adam' trainer = gluon.Trainer(net.collect_params(), optimizer, {'learning_rate': lr}) loss = gluon.loss.SigmoidBinaryCrossEntropyLoss() d2l.train_ch13(net, train_iter, test_iter, loss, trainer, num_epochs, devices)

要点说明:

  • 数据加载:训练集与测试集共享feat_mapper与defaults,保证两边字段索引空间一致;num_workers由d2l.get_dataloader_workers()决定(Linux 下默认 4 进程,Windows 下为 0,见 d2l/mxnet.py);
  • 训练工具:d2l.train_ch13是 d2l/mxnet.py 中定义的多 GPU 训练函数,支持在多个设备上并行训练并记录 train loss / train acc / test acc,d2l.try_all_gpus()(d2l/mxnet.py)会自动探测可用 GPU,无 GPU 时回退到 CPU;
  • 损失与优化:采用SigmoidBinaryCrossEntropyLoss(二分类交叉熵)+ Adam 优化器,学习率 0.01,训练 30 轮,参数用 Xavier 初始化。

实验结论

与原文档结论一致:相比 FM,DeepFM 收敛更快、性能更优。这也验证了深度组件引入的高阶交互建模能力确实带来了可测量的收益。读者可以通过修改mlp_dims与num_factors复现这一对比。

小结

  • 将神经网络集成进 FM,使其能够建模复杂且高阶的特征交互,突破二阶交互的表达瓶颈;
  • 在广告 CTR 数据集上,DeepFM 的性能优于原始 FM,且收敛速度更快;
  • DeepFM 通过与 Wide & Deep 类似的“并行 + 共享嵌入 + 输出求和”结构,在避免手工特征工程的同时兼顾了记忆与泛化能力。

练习建议

  • 改变 MLP 的结构(层数与每层神经元数),观察其对模型性能的影响;
  • 更换数据集(例如 Criteo),并与原始 FM 模型进行对比实验。

延伸阅读

本部分属于 D2L 推荐系统章节(见 chapter_recommender-systems/index.md)的内容,建议按顺序阅读前序的 ctr.md(特征丰富的推荐系统与 CTR 数据集)与 fm.md(因子分解机原理与实现),以完整理解 DeepFM 两个组件的来源。D2L 项目在 d2l/ 目录下同时维护了 MXNet、PyTorch、TensorFlow 与 JAX 四种框架的配套工具实现,可将本节思路迁移到其他框架中复现。

  • 文档
  • 教程
  • 人工智能
  • 深度学习
  • NLP
  • 计算机视觉
  • 强化学习

【免费下载链接】d2l-en

Interactive deep learning book with multi-framework code, math, and discussions. Adopted at 500 universities from 70 countries including Stanford, MIT, Harvard, and Cambridge.

项目地址:https://gitcode.com/gh_mirrors/d2/d2l-en
点击查看免费下载

相关推荐

上一篇:标题:掌握鼠标:探索Python的Mouse库
下一篇:探索DOM元素的视觉边界:VisSense.js深度剖析与应用指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询