1. 项目概述:混合推荐系统在广告投放中的实践
广告推荐系统作为数字营销的核心引擎,其精准度直接影响企业的ROI。传统基于内容的推荐容易陷入信息茧房,而协同过滤又面临冷启动难题。我们团队在实际业务中验证,结合用户画像的混合推荐方案能将点击率提升37%,这次就分享我们落地的完整技术方案。
这个系统特别适合两类人群:一是需要完成毕业设计的大数据/人工智能方向学生(我们提供了可直接运行的Python+Spark代码),二是正在搭建推荐系统的中小企业技术团队(系统日均承载2000万次推荐请求的架构经验值得参考)。下面从数据采集到模型部署的完整链路,我都会结合真实业务场景详细拆解。
2. 系统架构设计
2.1 整体技术栈选型
系统采用Lambda架构处理实时/离线数据流:
- 批处理层:Spark SQL + Hive(历史数据分析)
- 速度层:Flink + Redis(实时特征计算)
- 服务层:Spring Boot + TensorFlow Serving
选择Spark而非Flink做批处理的原因很实际:我们的用户画像需要关联近半年的行为数据(约3TB),Spark的磁盘Shuffle机制比Flink的内存模型更稳定。实测在10节点集群上,Spark完成全量特征计算耗时4.2小时,而Flink因频繁GC耗时近7小时。
2.2 混合推荐策略设计
系统融合三种推荐策略:
- 基于内容的推荐:使用BERT-wwm提取广告文本特征
- 协同过滤:改进的Item2Vec算法(解决稀疏性问题)
- 深度学习CTR预估:DeepFM模型
这里有个关键设计点:当用户行为数据少于15条时,系统自动提高内容推荐权重(70%→85%),随着行为丰富逐步过渡到协同过滤为主。这个动态权重机制使我们新用户留存率提升了23%。
3. 用户画像构建实战
3.1 数据采集与清洗
我们通过埋点SDK收集五类原始数据:
python复制# 用户行为日志示例
{
"user_id": "u_3829",
"event_type": "click", # 支持click/like/share等12种事件
"item_id": "ad_8921",
"timestamp": 1625097600,
"device_info": {
"os": "android",
"resolution": "1080x1920"
}
}
数据清洗特别注意:要处理"幽灵点击"问题(用户误触导致的无效点击)。我们的解决方案是:
- 过滤停留时间<500ms的点击
- 排除同一用户1秒内的连续点击
- 使用孤立森林检测异常流量
3.2 特征工程实现
用户画像包含三大类特征:
- 静态特征:性别、年龄等(通过手机号关联运营商数据)
- 动态特征:
- 短期兴趣(最近7天行为)
- 长期偏好(90天行为衰减加权)
- 上下文特征:地理位置、设备类型等
这里分享一个实用技巧:对于"价格敏感度"这个衍生特征,我们不是简单统计点击商品的均价,而是计算用户在不同价格区间的点击分布熵值,这样能更准确反映价格偏好。
4. 核心算法实现
4.1 改进的Item2Vec算法
传统Item2Vec面临两个问题:
- 用户行为序列中的时间衰减
- 热门物品的过度推荐
我们的解决方案:
python复制class WeightedItem2Vec:
def __init__(self):
self.time_decay = 0.95 # 时间衰减系数
self.popularity_norm = True # 热门物品归一化
def train(self, sequences):
# 带权重的skip-gram训练
for seq in sequences:
for i, target in enumerate(seq):
# 时间衰减权重
weight = self.time_decay ** (len(seq)-i-1)
# 负采样时考虑物品热度
negatives = self.sample_negatives(target)
# 更新embedding...
4.2 DeepFM模型优化
在广告推荐场景,我们发现两个关键改进点:
- 特征交叉优化:广告主行业×用户职业的交叉特征特别重要
- 动态特征处理:用户实时兴趣变化快
模型结构改进:
python复制class DynamicDeepFM(tf.keras.Model):
def __init__(self):
super().__init__()
# 实时特征通道
self.lstm = LSTM(units=64)
# 常规DeepFM结构
self.fm = FM_layer()
self.dnn = DNN_layers()
def call(self, inputs):
static_feat, dynamic_seq = inputs
# 处理实时行为序列
dynamic_emb = self.lstm(dynamic_seq)
# 特征交叉
fm_out = self.fm([static_feat, dynamic_emb])
dnn_out = self.dnn([static_feat, dynamic_emb])
return tf.sigmoid(fm_out + dnn_out)
5. 系统部署与性能优化
5.1 线上服务架构
推荐服务采用分级缓存策略:
- 本地缓存:Guava Cache(存储用户最近推荐结果)
- 分布式缓存:Redis(存储热门物品embedding)
- 持久层:HBase(全量用户画像)
压测指标(AWS c5.2xlarge实例):
- 平均响应时间:68ms
- QPS:1200(单实例)
- 推荐结果新鲜度:<5秒(从用户行为发生到影响推荐)
5.2 冷启动解决方案
对于新广告/新用户,我们采用三级降级策略:
- 内容相似推荐:用BERT计算文本相似度
- 群体偏好推荐:找到相似人群(K=20的KNN)
- 全局热门推荐:近3天CTR Top100
实测这套方案使新广告的7天留存率从12%提升到29%。
6. 效果评估与调优
6.1 离线评估指标
我们在测试集(200万样本)上对比不同算法:
| 算法 | AUC | Recall@10 | NDCG@10 |
|---|---|---|---|
| 纯CF | 0.712 | 0.183 | 0.215 |
| DeepFM | 0.781 | 0.224 | 0.258 |
| 混合推荐 | 0.823 | 0.291 | 0.327 |
6.2 线上AB测试
进行为期两周的桶测试(各5%流量):
- 对照组:原规则引擎推荐
- 实验组:混合推荐系统
关键指标对比:
- CTR提升:+37.6%
- 转化率提升:+22.3%
- 用户停留时长:+18.9秒
7. 常见问题排查
7.1 推荐多样性下降
现象:系统运行一段时间后,推荐结果趋同
排查:检查发现是Embedding维度坍塌(各物品向量聚集)
解决:
- 在损失函数中加入正交约束项
- 定期(每周)重新训练Item2Vec模型
- 人工设置多样性权重(控制相似物品出现频率)
7.2 实时特征延迟
现象:用户行为后推荐结果更新缓慢
排查:Flink作业反压导致数据处理积压
优化措施:
- 调整checkpoint间隔从10s到30s
- 增加Kafka分区数(从12到24)
- 对特征更新采用增量合并策略
这个项目给我最深的体会是:推荐系统不是算法越复杂越好,关键要建立完整的反馈闭环。我们专门开发了"推荐诊断面板",可以实时查看每个推荐结果的决策路径和特征权重,这对快速定位问题帮助巨大。比如有次发现体育类广告CTR骤降,追溯发现是用户画像中的"运动偏好"特征计算异常,因为数据源API改了字段格式。