1. 精排模型概述:从理论到实践
在推荐系统领域,精排(精细排序)阶段承担着至关重要的角色。如果说召回阶段是海选,粗排是初筛,那么精排就是最终的"面试官",决定了用户最终看到的推荐内容排序。今天我要分享的是基于Torch-RecHub框架实现的两种经典精排模型:DeepFM和DIN(Deep Interest Network)。
为什么选择这两个模型?DeepFM作为结合了因子分解机(FM)和深度神经网络的混合模型,能够同时捕捉低阶和高阶特征交互,特别适合处理稀疏特征场景。而DIN则创新性地引入了注意力机制,能够根据当前候选物品动态调整用户兴趣表示,更精准地刻画用户偏好。
在实际业务中,我们经常遇到这样的场景:用户昨天浏览了游戏机,今天又在看洗衣机。传统模型可能会将这两种兴趣等权重处理,但DIN能够根据当前候选商品(比如正在展示的是游戏手柄)动态调整,更关注用户与游戏相关的历史行为。这种"因地制宜"的能力使其在电商推荐中表现尤为突出。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. DeepFM模型深度解析
2.1 模型架构与核心思想
DeepFM的创新之处在于它优雅地结合了FM和深度网络的优势。想象一下,FM就像是一位擅长处理简单关系的专家,能够快速识别"买了手机的用户也会买手机壳"这样的二阶组合;而深度网络则像是一位擅长发现复杂模式的大师,能挖掘"用户年龄+收入水平+浏览时段"这样的高阶交叉特征。
模型结构上,DeepFM包含三个关键组件:
- FM组件:负责一阶和二阶特征交互
- Deep组件:多层感知机(MLP)处理高阶特征交互
- 共享的Embedding层:统一处理稀疏特征输入
这种设计带来的直接好处是:
- 无需人工特征工程(Wide&Deep需要)
- 端到端训练,两部分共享Embedding相互促进
- 计算效率高,FM部分的复杂度是线性的
2.2 代码实现详解
在Torch-RecHub中实现DeepFM异常简洁,这得益于框架良好的封装。以下是关键代码解析:
python复制from torch_rechub.models.ranking import DeepFM
from torch_rechub.trainers import CTRTrainer
# 模型配置
model = DeepFM(
deep_features=sparse_feas + dense_feas, # 深度网络处理的特征
fm_features=sparse_feas, # FM处理的特征(只能是稀疏特征)
mlp_params={"dims": [256, 128, 64], "dropout": 0.2, "activation": "relu"}
)
# 训练配置
trainer = CTRTrainer(
model=model,
optimizer_params={"lr": 0.001, "weight_decay": 1e-5},
n_epoch=10,
device="cuda:0", # GPU加速
earlystop_patience=3 # 早停策略
)
参数说明:
mlp_params中的dims定义了神经网络的三层结构,从256→128→64逐渐降维dropout=0.2意味着每层有20%的神经元会被随机丢弃,防止过拟合activation="relu"使用ReLU激活函数,平衡训练效率和效果
实践技巧:对于稀疏特征占主导的场景(如用户ID、商品ID),可以适当增大FM特征的权重;而对于密集特征(如用户统计信息)较多的场景,则应加强深度网络部分的容量。
2.3 从零实现DeepFM
理解框架封装背后的原理很重要,下面我们看看如何用PyTorch原生实现DeepFM:
python复制class MyDeepFM(torch.nn.Module):
def __init__(self, deep_features, fm_features, mlp_params):
super().__init__()
self.deep_features = deep_features
self.fm_features = fm_features
self.deep_dims = sum([fea.embed_dim for fea in deep_features])
self.fm_dims = sum([fea.embed_dim for fea in fm_features])
# 三大核心组件
self.linear = LR(self.fm_dims) # 一阶特征
self.fm = FM(reduce_sum=True) # 二阶交叉
self.embedding = EmbeddingLayer(deep_features + fm_features)
self.mlp = MLP(self.deep_dims, **mlp_params) # 高阶交叉
def forward(self, x):
input_deep = self.embedding(x, self.deep_features, squeeze_dim=True)
input_fm = self.embedding(x, self.fm_features, squeeze_dim=False)
# 三部分得分相加
y = self.linear(input_fm.flatten(1)) + \
self.fm(input_fm) + \
self.mlp(input_deep)
return torch.sigmoid(y.squeeze(1))
关键点说明:
squeeze_dim参数控制Embedding输出的维度结构,适应不同组件的需求- FM部分的计算复杂度是O(kn),其中k是Embedding维度,n是特征数
- 最终通过sigmoid将预测值压缩到[0,1]区间,符合CTR预测需求
3. DIN模型原理与实现
3.1 注意力机制在推荐中的应用
DIN模型的核心创新是提出了"局部激活"概念。举个生活中的例子:当你在电商平台浏览游戏机时,系统应该更关注你过去购买游戏相关的历史行为,而不是你买洗衣机的记录。传统模型对所有历史行为一视同仁,而DIN通过注意力机制实现了这种动态聚焦。
模型的关键计算过程:
python复制# query: 候选广告 [batch_size, 1, embedding_dim]
# keys: 历史行为序列 [batch_size, seq_len, embedding_dim]
attention_weights = ActivationUnit(query, keys) # 计算注意力权重
output = weighted_sum(keys, attention_weights) # 加权求和
特别值得注意的是,DIN中的注意力权重没有经过Softmax归一化。这种设计保留了用户兴趣的绝对强度信息——有些用户就是比其他人对某类商品更感兴趣,这种差异应该被保留。
3.2 特征处理的艺术
DIN中的特征处理比DeepFM更复杂,因为它要处理序列特征。Torch-RecHub提供了三种特征类型:
python复制from torch_rechub.basic.features import DenseFeature, SparseFeature, SequenceFeature
# 稀疏特征(类别型)
features = [
SparseFeature("user_id", vocab_size=10000, embed_dim=64),
SparseFeature("target_item", vocab_size=50000, embed_dim=64)
]
# 序列特征(历史行为)
history_features = [
SequenceFeature("hist_item", vocab_size=50000, embed_dim=64,
pooling="concat", shared_with="target_item")
]
参数解析:
shared_with允许历史物品和候选物品共享Embedding,这符合直觉且减少参数量pooling="concat"指定序列特征的聚合方式,其他选项包括"mean"/"max"vocab_size需要包含特殊token(如padding),所以通常取最大值+2
3.3 DIN模型实战
完整的DIN模型构建示例如下:
python复制from torch_rechub.models.ranking import DIN
model = DIN(
features=sparse_feas, # 用户画像等静态特征
history_features=history_feas, # 历史行为序列
target_features=target_feas, # 候选物品特征
mlp_params={"dims": [256, 128, 64], "dropout": 0.2, "activation": "dice"},
attention_mlp_params={"dims": [64, 32], "activation": "dice"}
)
trainer = CTRTrainer(
model=model,
optimizer_params={"lr": 0.001, "weight_decay": 1e-5},
n_epoch=15,
device="cuda:0"
)
几个关键设计选择:
- 使用Dice激活函数而非ReLU,它能根据数据分布自适应调整,在推荐场景表现更好
- 注意力网络的维度(64→32)小于主网络,符合"先粗筛后精排"的直觉
- 历史序列长度通常取最近50-100个行为,平衡信息量和计算开销
4. 实战经验与调优技巧
4.1 数据准备与特征工程
虽然深度学习模型可以减少特征工程的工作量,但合理的特征设计仍然至关重要:
-
数值特征:
- 连续值进行分桶处理(如年龄分段)
- 统计类特征(如用户过去7天的点击率)需要做平滑处理
-
类别特征:
- 低频类别需要合并(如将出现次数<100的商品ID归为"其他")
- 层次化编码(商品ID→类目ID→品牌ID)
-
序列特征:
- 按时间倒排,最近的行为放在前面
- 过滤异常行为(如秒退的点击)
- 可以混合多种行为类型(点击、加购、收藏)
踩坑记录:曾经遇到模型效果突然下降的情况,排查发现是某个特征的数据分布发生了漂移。解决方案是:
- 添加特征监控(统计均值/方差/缺失率)
- 在线学习逐步适应分布变化
- 定期全量retrain模型
4.2 模型训练技巧
-
学习率设置:
- 使用学习率warmup:前5%的step从0线性增长到初始lr
- 配合余弦衰减:$lr_t = \frac{1}{2}(1 + \cos(\frac{t\pi}{T})) \cdot lr_{init}$
-
正则化策略:
python复制regularization_params={ "embedding_l1": 1e-6, # 稀疏化Embedding "embedding_l2": 1e-6, # 防止过拟合 "dense_l2": 1e-5 # MLP参数正则 } -
早停策略:
- 监控验证集AUC,连续3轮不提升则停止
- 保存最佳checkpoint而非最后一个
4.3 线上部署考量
-
性能优化:
- 使用TensorRT加速推理
- 对Embedding进行量化(FP16→INT8)
- 批量预测提高吞吐量
-
特征实时化:
- 用户最近10次行为实时更新
- 上下文特征(如地理位置、时间)实时获取
-
AB测试设计:
- 分桶策略:用户ID哈希分桶
- 核心指标:CTR、转化率、人均PV
- 观察周期:至少7天覆盖工作日和周末
5. 效果对比与延伸思考
在实际AB测试中,我们观察到:
- DeepFM相比纯FM模型,AUC提升约2-3%
- DIN相比DeepFM,在电商场景下进一步带来1.5%的GMV提升
- 两种模型融合(DIN作为精排,DeepFM作为召回)效果最佳
未来可能的改进方向:
- 结合图神经网络(GNN)挖掘用户-商品二部图信息
- 引入多任务学习,同时优化CTR和CVR
- 探索强化学习实现更长期的用户体验优化
最后分享一个实用技巧:当模型效果达到平台期时,可以尝试:
- 增加用户长短期兴趣分离(如DIN的变体DIEN)
- 引入对抗训练增强鲁棒性
- 使用课程学习(Curriculum Learning)逐步增加数据难度
