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 的书中论断。讲解时请结合实际代码、输入输出与原书页码。

源码结构与阅读入口

  1. gamma · 第 34 行位置衰减因子 gamma_i = exp(-lambda * (i - 1)),i 从 1 开始。
  2. business_score · 第 39 行Score_business = sum_k w_k * V_k:各业务目标价值的加权组合。
  3. model_score · 第 44 行[MOCK] 序列评估模型对单 Item 的收益预测(这里用合成的 item['model'] 代替)。
  4. score_fusion_flat · 第 49 行口径 A:两项融合但不做位置衰减(正文说的「简单求和往往不合理」)。
  5. score_multi_obj_flat · 第 54 行口径 B:多目标加权求和 sum_k w_k V_k,仍不做位置衰减。
  6. score_with_position · 第 59 行口径 C:引入位置衰减 Score_seq = sum_i gamma_i * Score_i(正文推荐口径)。
  7. pick_top1 · 第 67 行Seq* = argmax_Seq Score_seq。
  8. make_item · 第 79 行[MOCK] 合成一个 Item:含模型分与四个业务目标价值。
  9. make_candidate_sequences · 第 96 行[MOCK] 合成若干等长候选序列(长度不等的情况见 length_normalization.py)。
  10. fmt_seq · 第 106 行
  11. demo_gamma_table · 第 112 行
  12. demo_three_scorers · 第 127 行
  13. demo_alpha_beta_sweep · 第 146 行
  14. demo_lambda_sweep · 第 164 行

完整源码 · 190 行

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)

如何用于学习或编写新代码

  1. 先看章节 README 的依赖和运行成本,定位这个例子的输入、输出及关键函数。
  2. 在原书 PDF 中找到对应概念的具体页;代码演示与书中论断分开标注。
  3. 只在依赖和耗时可控时运行单个示例,修改一个参数并记录真实输出与失败情形。
  4. 参考实现新代码时说明复用、改动、测试与尚未完成的生产依赖;教学示例并非生产系统。