深度CTR模型进阶:xDeepFM与FiBiNET代码实现详解

深度CTR模型进阶:xDeepFM与FiBiNET代码实现详解

在CTR预估任务中,深度学习模型通过特征交叉与高阶非线性建模显著提升了预测精度。本文将聚焦两种具有代表性的深度CTR模型——xDeepFM与FiBiNET,从理论架构、代码实现到优化策略进行系统性解析,帮助开发者掌握其核心机制与工程实践。

一、xDeepFM:显式高阶特征交互的突破

1.1 模型架构解析

xDeepFM的核心创新在于CIN(Compressed Interaction Network)模块,它通过显式方式构建高阶特征交互,解决了传统DNN隐式交叉可解释性差的问题。CIN采用向量级交叉(而非FM的位级交叉),通过逐层递归计算生成不同阶数的交互特征。

  • 输入层:将离散特征嵌入为低维稠密向量,连续特征直接拼接。
  • CIN层
    • 第k层交互向量通过前k-1层输出与原始嵌入向量交叉生成。
    • 使用压缩网络(1x1卷积)降低维度,避免参数爆炸。
  • 输出层:CIN输出与线性部分、DNN部分输出拼接,通过Sigmoid激活。

1.2 代码实现关键点

1.2.1 CIN模块实现

  1. import torch
  2. import torch.nn as nn
  3. class CIN(nn.Module):
  4. def __init__(self, field_size, embed_dim, hidden_layers=[64, 32]):
  5. super().__init__()
  6. self.field_size = field_size
  7. self.embed_dim = embed_dim
  8. self.hidden_layers = hidden_layers
  9. # 初始化CIN层参数
  10. self.cin_layers = nn.ModuleList()
  11. for i, h in enumerate(hidden_layers):
  12. self.cin_layers.append(
  13. nn.Conv1d(
  14. in_channels=field_size if i == 0 else hidden_layers[i-1],
  15. out_channels=h,
  16. kernel_size=1
  17. )
  18. )
  19. def forward(self, x):
  20. # x: [batch_size, field_size, embed_dim]
  21. cin_outputs = []
  22. for layer in self.cin_layers:
  23. # 交互计算(简化版,实际需实现外积计算)
  24. interaction = torch.bmm(x, x.transpose(1, 2)) # [batch, field, field]
  25. interaction = interaction.reshape(
  26. interaction.size(0), -1, self.embed_dim
  27. ) # [batch, field*field, embed]
  28. # 压缩网络
  29. conv_out = layer(interaction.transpose(1, 2)) # [batch, h, field*field]
  30. conv_out = conv_out.reshape(
  31. conv_out.size(0), -1, self.field_size
  32. ) # [batch, h, field]
  33. x = conv_out
  34. cin_outputs.append(torch.sum(x, dim=2)) # 聚合特征
  35. return torch.cat(cin_outputs, dim=1) # 拼接各层输出

1.2.2 完整模型集成

  1. class xDeepFM(nn.Module):
  2. def __init__(self, feature_sizes, embed_dim, hidden_layers=[64, 32]):
  3. super().__init__()
  4. # 线性部分
  5. self.linear = nn.Linear(sum(feature_sizes), 1)
  6. # 嵌入层
  7. self.embeddings = nn.ModuleList([
  8. nn.Embedding(size, embed_dim) for size in feature_sizes
  9. ])
  10. # DNN部分
  11. self.dnn = nn.Sequential(
  12. nn.Linear(len(feature_sizes) * embed_dim, 256),
  13. nn.ReLU(),
  14. nn.Linear(256, 128),
  15. nn.ReLU()
  16. )
  17. # CIN部分
  18. self.cin = CIN(len(feature_sizes), embed_dim, hidden_layers)
  19. def forward(self, x):
  20. # 嵌入处理
  21. embed_outputs = []
  22. for i, feat in enumerate(x):
  23. embed_outputs.append(self.embeddings[i](feat))
  24. x_embed = torch.cat(embed_outputs, dim=1) # [batch, field, embed]
  25. # 各部分输出
  26. linear_out = self.linear(x.flatten(1)) # 线性部分
  27. dnn_out = self.dnn(x_embed.flatten(1)) # DNN部分
  28. cin_out = self.cin(x_embed) # CIN部分
  29. # 最终输出
  30. combined = torch.cat([linear_out, dnn_out, cin_out], dim=1)
  31. return torch.sigmoid(torch.sum(combined, dim=1))

1.3 优化策略

  • 参数初始化:CIN层权重使用Xavier初始化,避免梯度消失。
  • 正则化:对CIN层输出添加Dropout(如0.3),防止过拟合。
  • 阶数控制:通过调整hidden_layers参数平衡模型复杂度与性能。

二、FiBiNET:动态特征加权与双线性交叉

2.1 模型核心机制

FiBiNET的创新在于Squeeze-and-Excitation(SE)模块双线性特征交叉

  • SE模块:动态学习特征重要性权重,增强关键特征贡献。
  • 双线性交叉:通过两种交叉方式(内积与哈达玛积)捕捉复杂交互模式。

2.2 代码实现要点

2.2.1 SE模块实现

  1. class SEBlock(nn.Module):
  2. def __init__(self, embed_dim, reduction_ratio=4):
  3. super().__init__()
  4. self.squeeze = nn.AdaptiveAvgPool1d(1) # 全局平均池化
  5. self.excitation = nn.Sequential(
  6. nn.Linear(embed_dim, embed_dim // reduction_ratio),
  7. nn.ReLU(),
  8. nn.Linear(embed_dim // reduction_ratio, embed_dim),
  9. nn.Sigmoid()
  10. )
  11. def forward(self, x):
  12. # x: [batch, field, embed]
  13. batch_size, field_num, embed_dim = x.size()
  14. y = self.squeeze(x.transpose(1, 2)).squeeze(-1) # [batch, embed]
  15. y = self.excitation(y) # [batch, embed]
  16. y = y.view(batch_size, embed_dim, 1) # [batch, embed, 1]
  17. return x * y.expand_as(x) # 特征加权

2.2.2 双线性交叉实现

  1. class BiLinearInteraction(nn.Module):
  2. def __init__(self, embed_dim, method='field_all'):
  3. super().__init__()
  4. self.method = method
  5. if method == 'field_each':
  6. # 每个字段对使用独立权重
  7. self.weight = nn.Parameter(torch.randn(embed_dim, embed_dim))
  8. def forward(self, x):
  9. # x: [batch, field, embed]
  10. if self.method == 'field_all':
  11. # 哈达玛积交叉
  12. x_t = x.transpose(1, 2) # [batch, embed, field]
  13. interaction = torch.bmm(x, x_t) # [batch, field, field]
  14. else:
  15. # 内积交叉(带权重)
  16. batch_size, field_num, embed_dim = x.size()
  17. x_flat = x.view(batch_size * field_num, embed_dim)
  18. interaction = torch.matmul(
  19. x_flat, self.weight.matmul(x_flat.t())
  20. ).view(batch_size, field_num, field_num)
  21. return interaction

2.3 完整模型集成

  1. class FiBiNET(nn.Module):
  2. def __init__(self, feature_sizes, embed_dim):
  3. super().__init__()
  4. self.embeddings = nn.ModuleList([
  5. nn.Embedding(size, embed_dim) for size in feature_sizes
  6. ])
  7. self.se_block = SEBlock(embed_dim)
  8. self.bi_linear = BiLinearInteraction(embed_dim)
  9. self.dnn = nn.Sequential(
  10. nn.Linear(len(feature_sizes) * embed_dim, 256),
  11. nn.ReLU(),
  12. nn.Linear(256, 1)
  13. )
  14. def forward(self, x):
  15. embed_outputs = []
  16. for i, feat in enumerate(x):
  17. embed_outputs.append(self.embeddings[i](feat))
  18. x_embed = torch.cat(embed_outputs, dim=1) # [batch, field, embed]
  19. # SE加权
  20. x_se = self.se_block(x_embed)
  21. # 双线性交叉
  22. interaction = self.bi_linear(x_se)
  23. interaction_flat = interaction.flatten(1) # [batch, field*field]
  24. # 拼接嵌入与交叉特征
  25. x_flat = x_embed.flatten(1) # [batch, field*embed]
  26. combined = torch.cat([x_flat, interaction_flat], dim=1)
  27. return torch.sigmoid(self.dnn(combined))

三、工程实践建议

3.1 特征处理优化

  • 嵌入维度选择:通常设为16-64,根据数据稀疏性调整。
  • 字段分组:对高频字段与低频字段分开处理,避免维度爆炸。

3.2 训练技巧

  • 学习率调度:使用余弦退火或预热学习率,稳定训练过程。
  • 损失函数:结合LogLoss与AUC优化目标,提升模型排序能力。

3.3 部署优化

  • 模型压缩:通过量化(如INT8)与剪枝减少模型体积。
  • 服务化:使用TensorRT或TorchScript加速推理,满足低延迟需求。

四、总结与对比

模型 核心创新 适用场景 复杂度
xDeepFM 显式高阶交叉(CIN) 特征交互复杂的数据集
FiBiNET 动态加权 + 双线性交叉 特征重要性差异大的场景

两种模型均通过创新机制提升了特征交互能力,实际选择需结合业务数据特点与计算资源。未来可探索将两者结合,或引入注意力机制进一步优化交互模式。