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 的书中论断。讲解时请结合实际代码、输入输出与原书页码。
源码结构与阅读入口
RankItem· 第 34 行复刻正文伪代码的候选物品结构。TrafficController· 第 68 行复刻正文伪代码的级联式流量调控器(融合权重与 Boost 系数均取正文值)。make_candidates· 第 112 行[MOCK] 合成一批精排候选:PXTR 带校准噪声,真值只用于算大盘指标。group_share· 第 132 行各业务分组在榜单中的曝光占比。avg_watch· 第 141 行[MOCK] 大盘指标:榜单的人均真实观看时长(秒)。
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)如何用于学习或编写新代码
- 先看章节 README 的依赖和运行成本,定位这个例子的输入、输出及关键函数。
- 在原书 PDF 中找到对应概念的具体页;代码演示与书中论断分开标注。
- 只在依赖和耗时可控时运行单个示例,修改一个参数并记录真实输出与失败情形。
- 参考实现新代码时说明复用、改动、测试与尚未完成的生产依赖;教学示例并非生产系统。