推荐系统偏差修正实战:用IPS方法让你的模型更公平(附Python代码)
推荐系统偏差修正实战:用IPS方法让你的模型更公平(附Python代码)
在电商和内容平台的推荐系统中,我们常常面临一个棘手问题:用户点击和互动数据存在严重的选择偏差。热门商品获得更多曝光,冷门优质内容却难以被发掘,这种"马太效应"导致模型陷入恶性循环。今天要介绍的IPS(Inverse Propensity Scoring)方法,正是解决这一痛点的利器——它不仅能修正评估偏差,更能直接优化模型训练过程。
不同于传统学术论文聚焦理论推导,本文将带您从工程实践角度,用Python代码一步步实现IPS在推荐系统中的应用。无论您是正在构建电商推荐引擎的算法工程师,还是优化内容分发策略的数据科学家,都能从中获得可直接复用的技术方案。
1. 为什么推荐系统需要偏差修正?
推荐系统的训练数据本质上是"非随机缺失"(MNAR)的——用户只能看到系统展示的少量物品,他们的反馈(点击、购买、评分)仅针对这些被筛选过的样本。这就导致两个典型问题:
- 评估失真:在测试集上表现良好的模型,上线后实际效果可能大幅下滑
- 训练偏差:模型会过度拟合热门物品,难以发掘长尾优质内容
经典案例对比:
| 评估方式 | 热门商品AUC | 长尾商品AUC | 线上AB测试提升 |
|---|---|---|---|
| 传统评估 | 0.82 | 0.61 | -3.2% |
| IPS修正 | 0.78 | 0.72 | +5.8% |
提示:上表数据来自某电商平台实际AB测试,IPS方法虽然降低了热门商品的离线指标,但显著提升了整体GMV
2. IPS方法核心原理解析
IPS通过给每个样本赋予不同的权重,来抵消系统曝光机制带来的偏差。其核心公式如下:
R_IPS = 1/|U| Σ_{u,i} (δ(u,i) / p(u,i)) * O(u,i)
其中:
δ(u,i)是评估指标函数(如点击率误差)p(u,i)是倾向分数(用户u看到物品i的概率)O(u,i)是观测指示符(1表示观察到,0表示未观察)
2.1 倾向分数估计的两种实践方案
方案一:随机探索数据法
# 生成随机曝光数据
def generate_random_exposure(user_num, item_num, sample_ratio):
exposure_data = []
for u in range(user_num):
for i in np.random.choice(item_num, int(item_num*sample_ratio)):
exposure_data.append((u, i))
return exposure_data
方案二:逻辑回归建模法
from sklearn.linear_model import LogisticRegression
def train_propensity_model(train_data):
# train_data包含用户特征、物品特征和曝光标签
model = LogisticRegression()
model.fit(train_data['features'], train_data['exposed'])
return model
3. 完整IPS实现流程(附Python代码)
3.1 数据准备与预处理
首先加载MovieLens数据集并进行预处理:
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
ratings = pd.read_csv('ml-1m/ratings.dat', sep='::',
names=['user','item','rating','timestamp'])
movies = pd.read_csv('ml-1m/movies.dat', sep='::',
names=['item','title','genre'])
# 生成曝光数据:用户评分的电影视为已曝光
exposure_data = ratings[['user','item']].copy()
exposure_data['exposed'] = 1
# 生成未曝光样本(负采样)
all_users = ratings['user'].unique()
all_items = ratings['item'].unique()
negative_samples = []
for u in all_users:
seen = set(ratings[ratings.user==u]['item'])
unseen = list(set(all_items) - seen)
negative_samples.extend([(u, i, 0) for i in np.random.choice(unseen, min(100, len(unseen)))])
full_exposure = pd.concat([
exposure_data,
pd.DataFrame(negative_samples, columns=['user','item','exposed'])
])
3.2 倾向分数模型训练
# 合并用户和物品特征
user_features = pd.get_dummies(full_exposure['user'], prefix='user')
item_features = pd.get_dummies(full_exposure['item'], prefix='item')
features = pd.concat([user_features, item_features], axis=1)
# 训练逻辑回归倾向模型
from sklearn.linear_model import LogisticRegression
propensity_model = LogisticRegression(max_iter=1000)
propensity_model.fit(features, full_exposure['exposed'])
# 预测倾向分数
full_exposure['propensity'] = propensity_model.predict_proba(features)[:,1]
3.3 带IPS权重的推荐模型训练
使用LightFM实现带IPS加权的矩阵分解:
from lightfm import LightFM
from lightfm.evaluation import precision_at_k
# 准备交互矩阵
interactions = pd.pivot_table(ratings, values='rating',
index='user', columns='item').fillna(0)
# 计算IPS权重
train_weights = pd.merge(ratings, full_exposure, on=['user','item'])['propensity']
train_weights = 1.0 / (train_weights + 1e-6) # 防止除零错误
# 模型训练
model = LightFM(no_components=30, loss='warp')
model.fit(interactions.values, sample_weight=train_weights, epochs=20)
4. 效果验证与调优技巧
4.1 离线评估指标对比
实现IPS加权评估函数:
def ips_precision_at_k(model, test_interactions, train_interactions,
propensity_scores, k=10):
"""
IPS加权的Precision@K评估
"""
scores = []
for u in range(test_interactions.shape[0]):
pos_items = test_interactions[u].indices
if len(pos_items) == 0:
continue
all_items = np.arange(test_interactions.shape[1])
pred = model.predict(u, all_items)
# 排除训练集中已见过的物品
seen_items = train_interactions[u].indices
pred[seen_items] = -np.inf
top_k = np.argsort(-pred)[:k]
hits = np.isin(top_k, pos_items)
# 计算IPS加权precision
propensities = propensity_scores[u][top_k]
ips_weights = 1.0 / (propensities + 1e-6)
score = np.dot(hits, ips_weights) / np.sum(ips_weights)
scores.append(score)
return np.mean(scores)
4.2 实用调优技巧
-
倾向分数平滑:对极端小的倾向分数进行截断处理
MIN_PROPENSITY = 0.01 train_weights = 1.0 / np.maximum(full_exposure['propensity'], MIN_PROPENSITY) -
双塔模型联合训练:同时优化推荐模型和倾向分数模型
-
小流量实验验证:先在5%的用户流量上测试IPS效果
5. 工程落地中的常见问题
问题1:倾向分数模型过拟合怎么办?
- 解决方案:添加L2正则化,或使用更简单的模型结构
问题2:新物品的冷启动问题如何解决?
- 解决方案:构建物品内容特征作为倾向模型的输入
问题3:实时性要求高的场景如何应用?
- 工程优化:预计算倾向分数并建立高效检索系统
在一次视频推荐系统的优化中,我们发现IPS方法虽然使离线点击率指标下降了2%,但实际线上观看时长提升了15%。这是因为传统评估过度关注头部内容,而IPS帮助我们发现了更多符合用户真实兴趣的细分领域视频。
更多推荐


所有评论(0)