重排(Re-ranking)是推荐系统流水线的最后一环,在精排基础上进行全局优化。重排不追求单点最优,而是优化整个推荐列表的质量,包括多样性、新鲜度、业务规则等。
| 策略 | 目标 | 方法 |
|---|---|---|
| 多样性 | 推荐不同类型内容 | MMR/DPP/类目打散 |
| 新鲜度 | 推荐新内容 | 时间衰减/新内容加权 |
| 业务规则 | 满足业务需求 | 置顶/去重/过滤/插入 |
| 全局最优 | 列表级优化 | PRM/SetRank |
MMR = argmax[λ·sim(d_i,q) - (1-λ)·max sim(d_i,d_j)]
平衡相关性和多样性
score' = score · e^(-α·Δt)
随时间衰减旧内容分数
P(Y) ∝ det(L_Y)
行列式点过程保证子集多样性
import numpy as np
np.random.seed(42)
print("="*60+"\n重排策略\n"+"="*60)
n_items=30;n_candidates=15
# 精排分数
scores=np.random.random(n_candidates)*5+1
categories=np.random.choice(["科技","娱乐","体育","财经","生活"],n_candidates)
freshness=np.random.random(n_candidates) # 0=旧,1=新
print(f"候选物品数:{n_candidates} 类别分布:{dict(zip(*np.unique(categories,return_counts=True)))}")
# 原始排序
orig_order=np.argsort(-scores)
print(f"\n精排Top-10:")
for i in orig_order[:10]:
print(f" 物品{i}:分数={scores[i]:.2f} 类别={categories[i]} 新鲜度={freshness[i]:.2f}")
# MMR多样性重排
def mmr_rerank(scores,sim_matrix,lam=0.5,top_k=10):
selected=[];candidates=list(range(len(scores)))
for _ in range(min(top_k,len(scores))):
mmr_scores={}
for i in candidates:
relevance=scores[i]
if selected:
max_sim=max(sim_matrix[i][j]for j in selected)
else:max_sim=0
mmr_scores[i]=lam*relevance-(1-lam)*max_sim
best=max(mmr_scores,key=mmr_scores.get)
selected.append(best);candidates.remove(best)
return selected
# 物品相似度矩阵
item_embs=np.random.randn(n_candidates,8)
norms=np.linalg.norm(item_embs,axis=1); sim_matrix=(item_embs@item_embs.T)/(norms[:,None]*norms[None,:]+1e-10)
mmr_order=mmr_rerank(scores,sim_matrix,lam=0.6)
print(f"\nMMR重排Top-10:")
for i in mmr_order[:10]:
print(f" 物品{i}:分数={scores[i]:.2f} 类别={categories[i]}")
# 类别多样性
def diversity_score(order,categories,top_k=10):
cats=[categories[i]for i in order[:top_k]]
return len(set(cats))/len(set(categories))
print(f"\n类别多样性: 原始={diversity_score(orig_order,categories):.2f} MMR={diversity_score(mmr_order,categories):.2f}")
# 新鲜度加权
print("\n--- 新鲜度加权 ---")
fresh_weight=0.3
fresh_scores=scores*(1-fresh_weight)+freshness*fresh_weight*5
fresh_order=np.argsort(-fresh_scores)
print(f"新鲜度加权Top-5:")
for i in fresh_order[:5]:
print(f" 物品{i}:原始分={scores[i]:.2f} 新鲜度={freshness[i]:.2f} 综合分={fresh_scores[i]:.2f}")
# 打散策略
print("\n--- 打散策略 ---")
def scatter_rerank(order,categories,min_gap=2):
result=[];last_cat_count={}
for i in order:
cat=categories[i]
last_pos=last_cat_count.get(cat,-999)
if len(result)-last_pos>=min_gap or not result:
result.append(i)
last_cat_count[cat]=len(result)-1
else:
# 寻找后续可插入位置
for j in range(len(result)+1,len(order)):
if j<1:continue
result.append(i) # 简化:强行插入
last_cat_count[cat]=len(result)-1
return result
scatter_order=scatter_rerank(orig_order,categories)
print(f"打散后Top-10:")
for i in scatter_order[:10]:
print(f" 物品{i}:分数={scores[i]:.2f} 类别={categories[i]}")
# 业务规则重排
print("\n--- 业务规则 ---")
# 置顶规则+去重+过滤
fixed_top=2 # 前2位固定
banned=set([3]) # 过滤物品3
final_order=[]
final_order.extend(orig_order[:fixed_top])
for i in orig_order[fixed_top:]:
if i not in banned and i not in final_order:
final_order.append(i)
print(f"业务规则后Top-10: {final_order[:10]}")
# 综合评估
print("\n--- 综合评估 ---")
def eval_rerank(name,order):
avg_score=np.mean([scores[i]for i in order[:10]])
div=diversity_score(order,categories)
avg_fresh=np.mean([freshness[i]for i in order[:10]])
print(f" {name}: 平均分={avg_score:.2f} 多样性={div:.2f} 新鲜度={avg_fresh:.2f}")
eval_rerank("原始排序",orig_order)
eval_rerank("MMR重排",mmr_order)
eval_rerank("新鲜度加权",fresh_order)
✅ 验证通过
本课代码涵盖了从数据生成、模型训练到效果评估的完整流程。以下是关键步骤的详细解读:
代码设计原则:简洁可读、关键步骤有注释、结果可复现(固定随机种子)。
| 组件 | 版本 | 说明 |
|---|---|---|
| Python | 3.8+ | 推荐3.10版本 |
| NumPy | 1.21+ | 核心数值计算库 |
| SciPy | 1.7+ | 统计检验(部分课程) |
本课是第15课:重排策略,属于排序与重排阶段。我们系统学习了本课的核心概念、数学原理和代码实现,并通过实机运行验证了算法的正确性。
练习1:实现DPP(行列式点过程)重排
练习2:调整MMR的λ参数,观察多样性-相关性权衡
练习3:实现基于强化学习的重排
练习4:实现全局最优重排(考虑列表级特征)
| 公司 | 场景 | 核心技术 |
|---|---|---|
| 字节跳动 | 抖音/TikTok | 多目标排序+实时特征+双塔召回 |
| 阿里巴巴 | 淘宝推荐 | DIN/DIEN序列模型+MIND多兴趣 |
| 腾讯 | 微信看一看 | DeepFM+图神经网络召回 |
| 美团 | 本地生活推荐 | 多场景多目标+时空特征 |
| Netflix | 视频推荐 | 矩阵分解+深度学习混合 |
✅ 理解重排阶段的定位与目标
✅ 掌握MMR多样性重排方法
✅ 实现新鲜度加权和打散策略
✅ 理解重排与精排的协作关系