CH 06 / SOURCE EXAMPLE

cascading_traffic_control.py

ch06 精排模块/code/ch05_traffic_control/cascading_traffic_control.py

这个示例讲什么

cascading_traffic_control.py § 6.5.1 级联式:融合→初排→调控→重排→TopK 与 Boost 强度权衡

级联式多目标融合与流量调控(第 6 章 §6.5.1 级联式多目标融合与流量调控) 对应正文概念: 正文指出工业界精排链路的主流实现是把「多目标融合」与「流量调控」解耦为两个 串行独立阶段:系统先用精排/LTR 输出的各类 PXTR 做多目标融合得到统一排序分, 按融合分初排,再把结果送入流量调控模块,由后者处理模型难以直接学习的业务规则 (新内容冷启动扶持、新作者成长保护、电商活动流量倾斜、商业化保量等),常见手段 是动态 Boost、局部 Rank 调整、Quota 控制与保量策略。其优点是排序逻辑与业务逻辑 相互独立、实现简单、维护成本低、业务扩展灵活。 本实现做了什么: 1. 完整复刻正文伪代码 `RankItem` / `TrafficController` 的五步流程: Step1 多目标融合 -> Step2 初始排序 -> Step3 流量调控 -> Step

上面来自章节 README 或源码自身的说明,不是自动验证的运行结果,也不能代替 PDF 的书中论断。讲解时请结合实际代码、输入输出与原书页码。

源码结构与阅读入口

  1. RankItem · 第 34 行复刻正文伪代码的候选物品结构。
  2. TrafficController · 第 68 行复刻正文伪代码的级联式流量调控器(融合权重与 Boost 系数均取正文值)。
  3. make_candidates · 第 112 行[MOCK] 合成一批精排候选:PXTR 带校准噪声,真值只用于算大盘指标。
  4. group_share · 第 132 行各业务分组在榜单中的曝光占比。
  5. avg_watch · 第 141 行[MOCK] 大盘指标:榜单的人均真实观看时长(秒)。

完整源码 · 256 行

1"""2级联式多目标融合与流量调控(第 6 章 §6.5.1 级联式多目标融合与流量调控)34对应正文概念:5正文指出工业界精排链路的主流实现是把「多目标融合」与「流量调控」解耦为两个6串行独立阶段:系统先用精排/LTR 输出的各类 PXTR 做多目标融合得到统一排序分,7按融合分初排,再把结果送入流量调控模块,由后者处理模型难以直接学习的业务规则8(新内容冷启动扶持、新作者成长保护、电商活动流量倾斜、商业化保量等),常见手段9是动态 Boost、局部 Rank 调整、Quota 控制与保量策略。其优点是排序逻辑与业务逻辑10相互独立、实现简单、维护成本低、业务扩展灵活。1112本实现做了什么:131. 完整复刻正文伪代码 `RankItem` / `TrafficController` 的五步流程:14     Step1 多目标融合 -> Step2 初始排序 -> Step3 流量调控 -> Step4 再排序 -> Step5 输出 TopK15   包括正文给的加法融合权重与三条乘性 Boost 规则(1.10 / 1.05 / 1.15)。162. [MOCK] 一个进程内合成候选集 + 「大盘指标」评估器:每个候选物品带隐藏的真实17   后验(真实点击/时长/转化),用于量化流量调控对大盘指标的代价。183. 打印三张数值表:19   (a) 级联流水线的逐步骤 Top-10 榜单变化(谁被 Boost 挤上来、谁被挤下去);20   (b) 各业务分组曝光占比:融合分初排 vs 流量调控后,以及 Boost 系数的边际效应;21   (c) Boost 强度扫描:业务目标达成度 vs 大盘指标(人均时长)损失的权衡曲线。224. 复现正文「Step3 在排序后对 score 做乘性修正、再 Step4 重排」这一细节带来的23   一个工程事实:乘性 Boost 的效果依赖融合分自身的量纲与分布 —— 同样的 1.10 倍,24   在分数稠密区能换来多个名次,在分数稀疏区几乎不动。2526关键公式(正文原文):27  Score = w_ctr * pctr + w_wt * pwatch_time + w_like * plike       (多目标融合)28  Score' = Score * 1.10^[新内容] * 1.05^[作者粉丝<1000] * 1.15^[电商活动]   (流量调控)29"""3031import random323334class RankItem:35    """复刻正文伪代码的候选物品结构。3637    正文字段:pctr / pwatch_time / plike + 三个业务标记 is_new_video /38    author_fans / in_campaign + 可变的 score。39    这里额外挂了 [MOCK] 的隐藏真值 (true_watch, iid),仅供大盘指标评估使用,40    排序与调控逻辑完全不读取它们。41    """4243    def __init__(self, pctr, pwatch_time, plike, is_new_video=False,44                 author_fans=0, in_campaign=False, iid=-1, true_watch=0.0):45        self.pctr = pctr46        self.pwatch_time = pwatch_time47        self.plike = plike4849        self.is_new_video = is_new_video50        self.author_fans = author_fans51        self.in_campaign = in_campaign5253        self.score = 0.054        self.iid = iid55        self.true_watch = true_watch5657    def group(self):58        """按正文 Boost 规则的优先级给物品打一个展示用的业务分组标签。"""59        if self.is_new_video:60            return "新内容"61        if self.in_campaign:62            return "电商活动"63        if self.author_fans < 1000:64            return "新作者"65        return "普通内容"666768class TrafficController:69    """复刻正文伪代码的级联式流量调控器(融合权重与 Boost 系数均取正文值)。"""7071    def __init__(self, ctr_weight=0.5, wt_weight=0.3, like_weight=0.2,72                 boost_new=1.10, boost_creator=1.05, boost_campaign=1.15):73        self.ctr_weight = ctr_weight74        self.wt_weight = wt_weight75        self.like_weight = like_weight76        self.boost_new = boost_new77        self.boost_creator = boost_creator78        self.boost_campaign = boost_campaign7980    def multi_objective_fusion(self, item):81        """Step1:正文的加法融合公式。"""82        return (self.ctr_weight * item.pctr83                + self.wt_weight * item.pwatch_time84                + self.like_weight * item.plike)8586    def traffic_control(self, item):87        """Step3:正文的三条乘性 Boost 业务规则。"""88        score = item.score89        if item.is_new_video:            # 新内容扶持90            score *= self.boost_new91        if item.author_fans < 1000:      # 新作者扶持92            score *= self.boost_creator93        if item.in_campaign:             # 电商活动扶持94            score *= self.boost_campaign95        return score9697    def rank(self, candidates, topk=100, return_stages=False):98        """正文五步流程。return_stages=True 时额外返回初排榜单用于对比。"""99        for item in candidates:                               # Step1 多目标融合100            item.score = self.multi_objective_fusion(item)101        rank_list = sorted(candidates, key=lambda x: x.score,  # Step2 初始排序102                           reverse=True)103        stage2 = list(rank_list)104        for item in rank_list:                                # Step3 流量调控105            item.score = self.traffic_control(item)106        rank_list = sorted(rank_list, key=lambda x: x.score,   # Step4 再排序107                           reverse=True)108        out = rank_list[:topk]                                 # Step5 输出 TopK109        return (out, stage2[:topk]) if return_stages else out110111112def make_candidates(rng, n=200):113    """[MOCK] 合成一批精排候选:PXTR 带校准噪声,真值只用于算大盘指标。"""114    items = []115    for i in range(n):116        q = rng.random()                              # 内容质量(潜变量)117        is_new = rng.random() < 0.18                  # 新内容占比 18%118        fans = 0 if rng.random() < 0.22 else int(1000 + 90000 * rng.random())119        camp = rng.random() < 0.12                    # 电商活动内容占比 12%120        # 新内容/新作者的后验天然偏低(样本不足、模型低估)-> 纯模型排序会压制它们121        cold = 0.80 if (is_new or fans < 1000) else 1.0122        ctr = (0.03 + 0.17 * q) * cold123        wt = (0.10 + 0.70 * q ** 1.3) * cold124        like = (0.004 + 0.030 * q ** 2) * cold125        noisy = [max(1e-6, v * (0.9 + 0.2 * rng.random()))126                 for v in (ctr, wt, like)]127        items.append(RankItem(noisy[0], noisy[1], noisy[2], is_new, fans, camp,128                              iid=i, true_watch=ctr * (20.0 + 80.0 * wt)))129    return items130131132def group_share(lst):133    """各业务分组在榜单中的曝光占比。"""134    n = len(lst)135    out = {}136    for g in ("新内容", "电商活动", "新作者", "普通内容"):137        out[g] = sum(1 for it in lst if it.group() == g) / n138    return out139140141def avg_watch(lst):142    """[MOCK] 大盘指标:榜单的人均真实观看时长(秒)。"""143    return sum(it.true_watch for it in lst) / len(lst)144145146if __name__ == "__main__":147    random.seed(42)148    rng = random.Random(42)149    cands = make_candidates(rng, n=200)150    TOPK = 50151    GROUPS = ("新内容", "电商活动", "新作者", "普通内容")152153    print("=" * 104)154    print("级联式多目标融合与流量调控(正文 §6.5.1 伪代码复刻):"155          "Step1 融合 -> Step2 初排 -> Step3 调控 -> Step4 重排 -> Step5 TopK")156    print(f"候选集 {len(cands)} 个,输出 Top-{TOPK};"157          "融合权重 w_ctr=0.5, w_wt=0.3, w_like=0.2;"158          "Boost 新内容 1.10 / 新作者 1.05 / 电商 1.15")159    print("=" * 104)160161    tc = TrafficController()162    final, initial = tc.rank(list(cands), topk=TOPK, return_stages=True)163164    # ---------------- 表 (a):Top-10 榜单在 Step2 与 Step4 之间的变化165    pos_init = {it.iid: r for r, it in enumerate(initial, 1)}166    print("\n【表 a】Top-10 榜单:融合分初排(Step2) vs 流量调控后(Step4)")167    print(f"{'名次':>4}{'初排物品':>10}{'初排分组':>10}"168          f"{'  |':>4}{'调控后物品':>12}{'调控后分组':>12}{'初排名次':>10}"169          f"{'名次变化':>10}{'Boost系数':>12}")170    print("-" * 104)171    for r in range(10):172        a, b = initial[r], final[r]173        boost = (tc.boost_new if b.is_new_video else 1.0) \174            * (tc.boost_creator if b.author_fans < 1000 else 1.0) \175            * (tc.boost_campaign if b.in_campaign else 1.0)176        old = pos_init.get(b.iid)177        delta = f"{old - (r + 1):+d}" if old else ">Top50"178        print(f"{r + 1:>4}{a.iid:>10}{a.group():>10}{'  |':>4}{b.iid:>12}"179              f"{b.group():>12}{(old if old else 0):>10}{delta:>10}"180              f"{boost:>12.4f}")181    print("-" * 104)182    moved = sum(1 for r, it in enumerate(final, 1) if pos_init.get(it.iid) != r)183    pushed_out = len({it.iid for it in initial} - {it.iid for it in final})184    print(f"观察:Top-{TOPK} 里有 {moved} 个物品名次被 Step3/Step4 改写,"185          f"{pushed_out} 个原本入榜的普通内容被挤出 —— 这就是「后置修正」的直接代价。")186187    # ---------------- 表 (b):分组曝光占比的变化188    print("\n【表 b】各业务分组在 Top-50 中的曝光占比:初排 vs 调控后")189    s_init, s_fin = group_share(initial), group_share(final)190    pool = group_share(cands)191    print(f"{'业务分组':<12}{'候选池占比':>14}{'初排占比':>14}{'调控后占比':>14}"192          f"{'绝对变化':>12}{'相对变化':>12}")193    print("-" * 104)194    for g in GROUPS:195        d = s_fin[g] - s_init[g]196        rel = (d / s_init[g]) if s_init[g] > 0 else float("inf")197        rel_s = f"{rel:+.1%}" if s_init[g] > 0 else "  新增(0->)"198        print(f"{g:<12}{pool[g]:>14.2%}{s_init[g]:>14.2%}{s_fin[g]:>14.2%}"199              f"{d:>+12.2%}{rel_s:>12}")200    print("-" * 104)201    print(f"大盘指标(人均真实观看时长):初排 {avg_watch(initial):.4f} 秒 -> "202          f"调控后 {avg_watch(final):.4f} 秒,"203          f"变化 {avg_watch(final) / avg_watch(initial) - 1:+.2%}")204    print("观察:新内容占比从 16% 抬到 26%(+62.5%),但「新作者」组反而从 12% 掉到 8%:")205    print("      它只拿到最弱的 1.05 倍,被 1.10 / 1.15 的强扶持组一起挤了出去;电商组")206    print("      占比甚至一动不动(10%->10%),只是内部名次变了。代价是普通内容 -6pp 与")207    print("      大盘人均时长 -1.13% —— 乘性 Boost 的「实际分配结果」并不等于系数大小,")208    print("      这正是正文所说「调和模型最优与业务最优的矛盾」里最难的一部分。")209210    # ---------------- 表 (c):Boost 强度扫描(权衡曲线)211    print("\n【表 c】Boost 强度扫描:业务目标达成度与大盘指标的权衡")212    print(f"{'Boost(新/创/电商)':<22}{'新内容占比':>12}{'电商占比':>12}"213          f"{'新作者占比':>12}{'普通内容占比':>14}{'人均时长':>12}{'时长相对损失':>14}")214    print("-" * 104)215    base_wt = None216    for mult in [1.00, 1.05, 1.10, 1.20, 1.50, 2.00]:217        t = TrafficController(boost_new=mult,218                              boost_creator=1.0 + (mult - 1.0) * 0.5,219                              boost_campaign=1.0 + (mult - 1.0) * 1.5)220        lst = t.rank(list(cands), topk=TOPK)221        sh = group_share(lst)222        wt = avg_watch(lst)223        if base_wt is None:224            base_wt = wt225        tag = (f"{t.boost_new:.2f}/{t.boost_creator:.2f}/{t.boost_campaign:.2f}")226        print(f"{tag:<22}{sh['新内容']:>12.2%}{sh['电商活动']:>12.2%}"227              f"{sh['新作者']:>12.2%}{sh['普通内容']:>14.2%}{wt:>12.4f}"228              f"{wt / base_wt - 1:>+14.2%}")229    print("-" * 104)230    print("观察:新内容与电商两个强扶持组的占比随系数单调上升,普通内容从 62% 一路被")231    print("      挤到 10%,人均时长从 12.04 掉到 9.11(-24.36%)。注意新作者组是非单调的")232    print("      (12%->10%->8%->10%->16%->18%):弱扶持组会先被强扶持组挤掉、只有系数")233    print("      整体放大到普通内容塌陷后才回升。该表就是上线前运营与算法要谈的「兑换")234    print("      汇率」:每多给扶持池 1 个点曝光,大盘时长要付出多少。")235236    # ---------------- 乘性 Boost 的量纲敏感性237    print("\n【表 d】同样 1.10 倍 Boost,在不同分数密度区段换来的名次差异")238    print(f"{'初排名次段':>12}{'该段分数跨度':>16}{'1.10 倍可跨越名次(均值)':>26}")239    print("-" * 104)240    full = sorted(cands, key=lambda x: -tc.multi_objective_fusion(x))241    sc = [tc.multi_objective_fusion(it) for it in full]242    for lo, hi in [(0, 10), (10, 30), (30, 60), (60, 120)]:243        span = sc[lo] - sc[hi - 1]244        gains = []245        for r in range(lo, hi):246            boosted = sc[r] * 1.10247            new_r = sum(1 for s in sc if s > boosted)248            gains.append(r - new_r)249        print(f"{f'{lo + 1}-{hi}':>12}{span:>16.6f}"250              f"{sum(gains) / len(gains):>26.2f}")251    print("-" * 104)252    print("观察:同一个 1.10 倍系数,在头部稀疏段(1-10,分数跨度 0.0551)平均只能跨越")253    print("      3.00 个名次,而在中段(31-60,跨度 0.0445)能跨越 15.17 个。可见乘性")254    print("      Boost 的「实际力度」取决于融合分在该名次段的局部密度,而不是系数本身,")255    print("      这也是工业界更偏好直接用 Quota / 坑位(§6.5.2、§6.5.5)的原因之一。")256    print("=" * 104)

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

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