CH 08 / SOURCE EXAMPLE
sequence_fusion_score.py
ch08 混排模块/code/ch04_sequence_ranking/sequence_fusion_score.py
这个示例讲什么
│ ├── sequence_fusion_score.py 序列融合分、多目标加权、位置衰减因子与 Top-1 选择
序列融合分与 Top-1 序列选择。 对应正文第 8 章 §8.4「序列排序」中的序列融合分计算与 argmax 选择。 本实现做了什么: 1. 按正文公式实现三层递进的序列打分口径: 模型分 + 业务分融合 → 多业务目标加权 → 引入位置衰减因子; 2. 用同一批候选序列对比三种口径选出的 Top-1,展示「位置偏差建模比排序算法本身更重要」; 3. 扫描 alpha/beta 与衰减系数 lambda,打印 Top-1 归属随权重变化的对照表。 关键公式(全部取自正文): 两项融合 Score_seq = alpha * Score_model + beta * Score_business 多目标扩展 Score_seq = sum_{k=1..N} w_k * V_k 位置衰减 Score_seq = sum_{i=1
上面来自章节 README 或源码自身的说明,不是自动验证的运行结果,也不能代替 PDF 的书中论断。讲解时请结合实际代码、输入输出与原书页码。
源码结构与阅读入口
gamma· 第 34 行位置衰减因子 gamma_i = exp(-lambda * (i - 1)),i 从 1 开始。business_score· 第 39 行Score_business = sum_k w_k * V_k:各业务目标价值的加权组合。model_score· 第 44 行[MOCK] 序列评估模型对单 Item 的收益预测(这里用合成的 item['model'] 代替)。score_fusion_flat· 第 49 行口径 A:两项融合但不做位置衰减(正文说的「简单求和往往不合理」)。score_multi_obj_flat· 第 54 行口径 B:多目标加权求和 sum_k w_k V_k,仍不做位置衰减。score_with_position· 第 59 行口径 C:引入位置衰减 Score_seq = sum_i gamma_i * Score_i(正文推荐口径)。pick_top1· 第 67 行Seq* = argmax_Seq Score_seq。make_item· 第 79 行[MOCK] 合成一个 Item:含模型分与四个业务目标价值。make_candidate_sequences· 第 96 行[MOCK] 合成若干等长候选序列(长度不等的情况见 length_normalization.py)。fmt_seq· 第 106 行demo_gamma_table· 第 112 行demo_three_scorers· 第 127 行demo_alpha_beta_sweep· 第 146 行demo_lambda_sweep· 第 164 行
1"""序列融合分与 Top-1 序列选择。23对应正文第 8 章 §8.4「序列排序」中的序列融合分计算与 argmax 选择。45本实现做了什么:61. 按正文公式实现三层递进的序列打分口径:7 模型分 + 业务分融合 → 多业务目标加权 → 引入位置衰减因子;82. 用同一批候选序列对比三种口径选出的 Top-1,展示「位置偏差建模比排序算法本身更重要」;93. 扫描 alpha/beta 与衰减系数 lambda,打印 Top-1 归属随权重变化的对照表。1011关键公式(全部取自正文):12 两项融合 Score_seq = alpha * Score_model + beta * Score_business13 多目标扩展 Score_seq = sum_{k=1..N} w_k * V_k14 位置衰减 Score_seq = sum_{i=1..L} gamma_i * Score_i15 指数衰减 gamma_i = exp(-lambda * (i - 1))16 lambda = 0.2 时 gamma = [1.00, 0.82, 0.67, 0.55]17 最终选择 Seq* = argmax_Seq Score_seq1819[MOCK] 序列评估模型的输出 Score_model 由合成函数给出(不训练真实模型),20 业务价值 V_k 也由合成数据生成,仅用于演示打分口径对 Top-1 的影响。21"""2223import math24import random2526# 业务目标权重 w_k:正文的 V_k 例子是「广告收入 / 观看时长 / 用户留存 / 直播成交额」27BIZ_OBJ_WEIGHT = {"ad_revenue": 1.00, "watch_time": 1.20, "retention": 2.00, "live_gmv": 0.60}2829LAMBDA_DECAY = 0.2 # 正文给的指数衰减系数示例30ALPHA = 0.6 # Score_model 权重31BETA = 0.4 # Score_business 权重323334def gamma(i, lam=LAMBDA_DECAY):35 """位置衰减因子 gamma_i = exp(-lambda * (i - 1)),i 从 1 开始。"""36 return math.exp(-lam * (i - 1))373839def business_score(item):40 """Score_business = sum_k w_k * V_k:各业务目标价值的加权组合。"""41 return sum(BIZ_OBJ_WEIGHT[k] * item["obj"].get(k, 0.0) for k in BIZ_OBJ_WEIGHT)424344def model_score(item):45 """[MOCK] 序列评估模型对单 Item 的收益预测(这里用合成的 item['model'] 代替)。"""46 return item["model"]474849def score_fusion_flat(seq):50 """口径 A:两项融合但不做位置衰减(正文说的「简单求和往往不合理」)。"""51 return sum(ALPHA * model_score(it) + BETA * business_score(it) for it in seq)525354def score_multi_obj_flat(seq):55 """口径 B:多目标加权求和 sum_k w_k V_k,仍不做位置衰减。"""56 return sum(business_score(it) for it in seq)575859def score_with_position(seq, lam=LAMBDA_DECAY, alpha=ALPHA, beta=BETA):60 """口径 C:引入位置衰减 Score_seq = sum_i gamma_i * Score_i(正文推荐口径)。"""61 total = 0.062 for i, it in enumerate(seq, start=1):63 total += gamma(i, lam) * (alpha * model_score(it) + beta * business_score(it))64 return total656667def pick_top1(seqs, scorer):68 """Seq* = argmax_Seq Score_seq。"""69 best_i, best_s = 0, -1e1870 for i, s in enumerate(seqs):71 v = scorer(s)72 if v > best_s:73 best_i, best_s = i, v74 return best_i, best_s757677# -------------------------------------------------------------- 合成候选序列7879def make_item(idx, biz):80 """[MOCK] 合成一个 Item:含模型分与四个业务目标价值。"""81 obj = {k: 0.0 for k in BIZ_OBJ_WEIGHT}82 if biz == "video":83 obj["watch_time"] = random.uniform(0.8, 3.0)84 obj["retention"] = random.uniform(0.02, 0.10)85 elif biz == "ad":86 obj["ad_revenue"] = random.uniform(0.6, 2.4)87 obj["retention"] = random.uniform(-0.04, 0.0) # 广告对留存是负贡献88 else: # live89 obj["live_gmv"] = random.uniform(0.5, 4.0)90 obj["watch_time"] = random.uniform(0.3, 1.5)91 obj["retention"] = random.uniform(0.0, 0.05)92 return {"id": f"{biz[0].upper()}{idx}", "biz": biz,93 "model": random.uniform(0.3, 1.1), "obj": obj}949596def make_candidate_sequences(n_seq=6, length=6):97 """[MOCK] 合成若干等长候选序列(长度不等的情况见 length_normalization.py)。"""98 pool = [make_item(i, b) for i, b in enumerate(99 ["video"] * 8 + ["ad"] * 3 + ["live"] * 3)]100 seqs = []101 for _ in range(n_seq):102 seqs.append(random.sample(pool, length))103 return seqs104105106def fmt_seq(seq):107 return "|".join(it["id"] for it in seq)108109110# ------------------------------------------------------------------ 演示111112def demo_gamma_table():113 print("=" * 78)114 print("【1】位置衰减因子 gamma_i = exp(-lambda*(i-1))(正文 lambda=0.2 的示例)")115 print("=" * 78)116 print(f"{'位置 i':>8}" + "".join(f"{i:>9}" for i in range(1, 7)))117 for lam in (0.0, 0.1, 0.2, 0.4):118 row = "".join(f"{gamma(i, lam):>9.3f}" for i in range(1, 7))119 tag = f"lambda={lam:.1f}"120 print(f"{tag:>8}{row}")121 print("-" * 78)122 g = [gamma(i, 0.2) for i in range(1, 5)]123 print("正文给出 lambda=0.2 时 gamma_1..4 = 1.0, 0.82, 0.67, 0.55;"124 f"本实现算得 {', '.join(f'{x:.2f}' for x in g)} → 一致。")125126127def demo_three_scorers(seqs):128 print()129 print("=" * 78)130 print("【2】三种打分口径下的候选序列得分(6 条等长候选,L=6)")131 print("=" * 78)132 print(f"{'候选序列':<26}{'A:模型+业务':>12}{'B:多目标和':>12}{'C:+位置衰减':>13}")133 print("-" * 78)134 for s in seqs:135 print(f"{fmt_seq(s):<26}{score_fusion_flat(s):>12.4f}"136 f"{score_multi_obj_flat(s):>12.4f}{score_with_position(s):>13.4f}")137 print("-" * 78)138 for name, fn in (("A 模型+业务融合", score_fusion_flat),139 ("B 多目标加权和", score_multi_obj_flat),140 ("C 融合+位置衰减", score_with_position)):141 i, v = pick_top1(seqs, fn)142 print(f"{name:<16} Top-1 = 候选#{i} {fmt_seq(seqs[i]):<24} score={v:.4f}")143 print("不同口径选出的 Top-1 并不相同 → 正文强调「核心挑战来自价值函数设计,而非排序算法」。")144145146def demo_alpha_beta_sweep(seqs):147 print()148 print("=" * 78)149 print("【3】alpha/beta 扫描:模型分与业务分的权重如何改变 Top-1(lambda=0.2 固定)")150 print("=" * 78)151 print(f"{'alpha':>7}{'beta':>7}{'Top-1':>10}{'最高分':>10}{'次高分':>10}{'领先幅度':>11}")152 print("-" * 78)153 for a in (0.0, 0.25, 0.5, 0.75, 1.0):154 b = 1.0 - a155 scored = sorted(((score_with_position(s, LAMBDA_DECAY, a, b), i)156 for i, s in enumerate(seqs)), reverse=True)157 lead = (scored[0][0] - scored[1][0]) / max(scored[1][0], 1e-9)158 print(f"{a:>7.2f}{b:>7.2f}{'#' + str(scored[0][1]):>10}"159 f"{scored[0][0]:>10.4f}{scored[1][0]:>10.4f}{lead:>10.2%}")160 print("-" * 78)161 print("alpha 偏向序列评估模型、beta 偏向业务价值分;两者权衡会直接改写最终曝光序列。")162163164def demo_lambda_sweep(seqs):165 print()166 print("=" * 78)167 print("【4】lambda 扫描:位置偏差建模强度对 Top-1 的影响(alpha=0.6, beta=0.4)")168 print("=" * 78)169 print(f"{'lambda':>8}{'Top-1':>8}{'得分':>10}{'首位Item':>11}{'首位业务':>10}"170 f"{'前2位分数占比':>14}")171 print("-" * 78)172 for lam in (0.0, 0.1, 0.2, 0.3, 0.5, 0.8):173 i, v = pick_top1(seqs, lambda s, l=lam: score_with_position(s, l))174 best = seqs[i]175 head = sum(gamma(j, lam) * (ALPHA * model_score(it) + BETA * business_score(it))176 for j, it in enumerate(best[:2], start=1))177 print(f"{lam:>8.2f}{'#' + str(i):>8}{v:>10.4f}{best[0]['id']:>11}"178 f"{best[0]['biz']:>10}{head / v:>13.1%}")179 print("-" * 78)180 print("lambda 越大,首屏权重越集中(前 2 位贡献占比从约 1/3 升到过半),"181 "排序越倾向把高价值内容顶到首位。")182183184if __name__ == "__main__":185 random.seed(42)186 seqs = make_candidate_sequences()187 demo_gamma_table()188 demo_three_scorers(seqs)189 demo_alpha_beta_sweep(seqs)190 demo_lambda_sweep(seqs)如何用于学习或编写新代码
- 先看章节 README 的依赖和运行成本,定位这个例子的输入、输出及关键函数。
- 在原书 PDF 中找到对应概念的具体页;代码演示与书中论断分开标注。
- 只在依赖和耗时可控时运行单个示例,修改一个参数并记录真实输出与失败情形。
- 参考实现新代码时说明复用、改动、测试与尚未完成的生产依赖;教学示例并非生产系统。