1. 项目概述
在构建推荐系统时,负采样(Negative Sampling)是一个至关重要的技术环节。它直接影响着模型训练的效果和最终推荐的质量。简单来说,负采样就是从海量未交互物品中选取少量样本作为负例,与正例一起构成训练数据的过程。
为什么需要负采样?想象一下,一个电商平台可能有上百万商品,但用户实际交互过的(点击、购买等)通常只有几十到几百个。如果训练时使用所有未交互商品作为负例,计算量将变得极其庞大。负采样通过合理选择部分负例,既保持了训练效率,又能让模型学习到有区分度的特征。
然而,负采样并非简单的随机选择。不同的采样策略会引入不同的偏差(Bias),进而影响模型对用户真实偏好的捕捉。比如,热门商品被随机采样的概率更高,可能导致模型过度关注热门物品而忽略长尾。因此,如何设计合理的负采样策略,并校正由此带来的偏差,成为推荐系统优化中的关键问题。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 常见负采样策略解析
2.1 随机负采样
最基本的策略是均匀随机采样,即每个未交互物品被选为负例的概率相同。这种方法实现简单,但存在明显问题:热门商品由于数量庞大,实际上更容易被采样到。这会导致模型过度惩罚热门商品(因为它们作为负例出现的次数多),最终推荐结果偏向长尾。
python复制def random_negative_sampling(user_items, all_items, num_negatives):
negatives = []
for item in user_items['positives']:
sampled = random.sample(all_items - user_items['positives'], num_negatives)
negatives.extend(sampled)
return negatives
2.2 基于流行度的负采样
为了缓解随机采样的问题,可以根据物品的流行度(如点击量、购买量)进行加权采样。通常有两种方式:
- 正相关采样:流行物品被采样概率更高
- 负相关采样:流行物品被采样概率更低
实践中常用的是介于两者之间的折中方案。例如,对流行度取α次方(α∈[0,1])作为采样权重:
python复制def popularity_based_sampling(items_popularity, num_samples, alpha=0.75):
popularity = np.array([items_popularity[i]**alpha for i in items_popularity])
prob = popularity / popularity.sum()
return np.random.choice(list(items_popularity.keys()), size=num_samples, p=prob)
2.3 难例挖掘(Hard Negative Sampling)
这种方法专注于选择那些模型当前难以区分的负例(即预测得分较高的负例)。这类样本对模型提升更有帮助,但实现较为复杂:
- 首先用简单负采样训练初始模型
- 用该模型对所有候选负例评分
- 选择得分最高的部分作为难例
- 用新负例集重新训练模型
python复制def hard_negative_sampling(model, user, candidates, top_k=10):
scores = model.predict([user]*len(candidates), candidates)
hard_negatives = [x for _,x in sorted(zip(scores,candidates), reverse=True)[:top_k]]
return hard_negatives
3. 采样偏差及其影响
3.1 偏差来源分析
负采样偏差主要来自三个方面:
- 流行度偏差:热门物品被过度采样或欠采样
- 曝光偏差:未被交互的物品不一定是不喜欢的(可能只是没曝光)
- 位置偏差:用户更倾向于点击靠前的物品,与质量无关
这些偏差会导致模型学到错误的用户偏好。例如,过度采样热门负例会使模型低估热门物品的实际价值。
3.2 偏差的量化评估
可以通过以下指标评估采样偏差的影响:
- 推荐列表的流行度分布
- 长尾物品的覆盖率
- 用户历史交互物品与推荐物品的相似度
python复制def evaluate_bias(recommendations, item_popularity):
# 计算推荐物品的平均流行度
avg_pop = np.mean([item_popularity[i] for i in recommendations])
# 计算长尾覆盖率(假设长尾定义为流行度<平均值的物品)
tail_coverage = len([i for i in recommendations if item_popularity[i] < avg_pop])/len(recommendations)
return {'avg_popularity': avg_pop, 'tail_coverage': tail_coverage}
4. 偏差校正方法
4.1 重要性采样(Importance Sampling)
这是一种经典的偏差校正技术,基本思想是为每个样本赋予一个权重,抵消采样偏差的影响。权重通常定义为:
w(x) = p_true(x) / p_sample(x)
其中p_true是真实分布,p_sample是采样分布。
python复制def importance_weighting(items, true_dist, sample_dist):
weights = {}
for item in items:
weights[item] = true_dist.get(item, 1e-6) / sample_dist.get(item, 1e-6)
return weights
4.2 逆倾向评分(Inverse Propensity Scoring, IPS)
IPS是重要性采样的特例,常用于校正曝光偏差。倾向得分(propensity score)表示物品被曝光的概率:
IPS_weight = 1 / propensity_score
实践中,倾向得分可以通过单独的曝光模型估计得到。
4.3 纠偏损失函数
可以在损失函数中直接加入纠偏项。例如,对于BPR损失:
L = ∑(u,i,j) -log(σ(ŷ_ui - ŷ_uj)) + λ * bias_correction_term
其中bias_correction_term可以根据具体偏差类型设计。
python复制def debiased_bpr_loss(user_emb, item_i_emb, item_j_emb, bias_i, bias_j, lambda=0.1):
y_ui = torch.dot(user_emb, item_i_emb)
y_uj = torch.dot(user_emb, item_j_emb)
bpr_loss = -torch.log(torch.sigmoid(y_ui - y_uj))
correction = lambda * (bias_i - bias_j)**2
return bpr_loss + correction
5. 实践中的综合方案
5.1 混合负采样策略
在实际系统中,通常会组合多种采样策略:
- 70%样本使用基于流行度的采样(α=0.5)
- 20%样本使用难例挖掘
- 10%样本使用随机采样
这种混合策略能在探索与利用、效率与效果间取得平衡。
5.2 动态调整采样策略
随着模型训练,可以动态调整采样策略:
- 初期:更多随机采样,广泛探索
- 中期:增加难例比例
- 后期:侧重特定类型的难例
python复制def dynamic_sampling_ratio(epoch, total_epochs):
random_ratio = max(0.1, 0.5 - 0.4*epoch/total_epochs)
hard_ratio = min(0.5, 0.1 + 0.4*epoch/total_epochs)
pop_ratio = 1 - random_ratio - hard_ratio
return {'random': random_ratio, 'hard': hard_ratio, 'pop': pop_ratio}
5.3 多任务学习框架
将偏差校正作为辅助任务,与主推荐任务一起学习:
code复制 User Features
│
▼
┌───────────────┐
│ Shared │
│ Embedding │
│ Layers │
└───────────────┘
│
├─────────────────┐
▼ ▼
┌─────────────┐ ┌─────────────┐
│ Main │ │ Bias │
│ Task │ │ Prediction │
│ (CTR,etc) │ │ Task │
└─────────────┘ └─────────────┘
6. 评估与调优
6.1 离线评估指标
除了常规的准确率指标,应特别关注偏差相关指标:
- 流行度基尼系数:衡量推荐结果的流行度分布均衡性
- 长尾覆盖率:推荐列表中长尾物品占比
- 用户群体覆盖率:不同用户群体获得推荐的比例
python复制def gini_coefficient(popularity):
# popularity是推荐物品的流行度列表
sorted_pop = np.sort(popularity)
n = len(sorted_pop)
cum_pop = np.cumsum(sorted_pop)
gini = (n + 1 - 2 * np.sum(cum_pop) / cum_pop[-1]) / n
return gini
6.2 在线A/B测试指标
在线实验应关注:
- 不同用户群体的满意度差异
- 长尾物品的转化率变化
- 推荐多样性的用户反馈
提示:在线实验时,建议先小流量测试(如5%用户),观察偏差校正策略对系统整体生态的影响。
7. 实战经验与避坑指南
-
冷启动问题:新物品缺乏流行度数据时,可以暂时使用类别或内容相似物品的流行度作为代理。
-
采样效率:对于十亿级物品库,可以先使用近似最近邻(ANN)方法缩小候选集,再进行精确采样。
-
动态环境适应:在物品流行度变化快的场景(如新闻推荐),需要频繁更新采样分布(如每小时更新一次)。
-
多目标平衡:当推荐系统同时优化CTR、时长、多样性等多个目标时,需要设计各目标共享的采样策略。
-
系统开销监控:难例挖掘会显著增加计算开销,建议:
- 仅在训练后期启用
- 使用缓存机制存储难例
- 控制难例更新频率
-
常见误区:
- 过度校正偏差导致模型忽视真实流行度模式
- 在采样阶段和损失函数中重复校正同一偏差
- 忽略用户个体差异(如某些用户确实偏好热门内容)
在实际项目中,我们曾遇到一个典型案例:当过度校正流行度偏差后,虽然长尾覆盖率提升了,但整体点击率下降了15%。后来通过分析发现,某些品类(如电影)用户确实更关注热门内容。最终解决方案是对不同品类采用不同的采样校正强度。
