CH 09 / SOURCE EXAMPLE
cascade_vs_generative.py
ch09 生成式推荐模块/code/ch02_architecture/cascade_vs_generative.py
这个示例讲什么
│ ├── cascade_vs_generative.py 级联漏斗 vs One-Model:算力 / 候选规模 / 可控性 / 异常影响半径
对应正文第 9 章 §9.2「生成式推荐架构」中生成式推荐与传统级联式架构的对比。 正文给出了两种工业部署方式: (1) 并行候选生成架构(Parallel Candidate Generation Architecture): 生成式推荐模块与传统召回并行产出候选, 最终汇合进入重排模块统一排序, 即 User -> Generative Recommendation -> Rerank; (2) 流量隔离架构(Traffic Isolation Architecture): 划分一定比例实验流量, 由生成式 模块直接产出 Item 集合再进入重排, 不再走"召回->粗排->精排"。 正文同时在 §9.8 讨论里强调: 级联架构的分层防护使单模块异常的影响可控, 而 One-Model 架构下模型异常会被放大到整条链路。 本实现用纯标准库做
上面来自章节 README 或源码自身的说明,不是自动验证的运行结果,也不能代替 PDF 的书中论断。讲解时请结合实际代码、输入输出与原书页码。
源码结构与阅读入口
cascade_cost· 第 47 行返回 (总 MFLOPs, 最终候选数, 决策点总数)。generative_cost· 第 54 行生成式模块: 算力只与 L、beam 有关, 与全库规模解耦。blast_radius· 第 62 行单模块故障的影响半径。级联有逐层兜底, One-Model 无。deploy_modes· 第 70 行正文 §9.2 给出的两种工业部署方式。main· 第 82 行
1"""对应正文第 9 章 §9.2「生成式推荐架构」中生成式推荐与传统级联式架构的对比。23正文给出了两种工业部署方式:4 (1) 并行候选生成架构(Parallel Candidate Generation Architecture):5 生成式推荐模块与传统召回并行产出候选, 最终汇合进入重排模块统一排序, 即6 User -> Generative Recommendation -> Rerank;7 (2) 流量隔离架构(Traffic Isolation Architecture): 划分一定比例实验流量, 由生成式8 模块直接产出 Item 集合再进入重排, 不再走"召回->粗排->精排"。9正文同时在 §9.8 讨论里强调: 级联架构的分层防护使单模块异常的影响可控, 而 One-Model10架构下模型异常会被放大到整条链路。1112本实现用纯标准库做一个**算力/候选规模/可控性**的解析对比模型(不做真实服务):13 * 级联漏斗: 候选规模按 N_recall -> N_coarse -> N_fine -> N_rerank 逐级收缩,14 每级算力 = 该级候选数 x 单条打分 FLOPs, 总算力为各级求和;15 * 生成式: 算力 = T 步自回归 x 每步 Decoder FLOPs(与候选规模无关), 其中16 T = L (语义 ID 层数) x beam 扩展代价, 候选规模 = beam_size 条合法路径;17 * 可控性: 用"可插入业务约束的决策点个数 / 异常影响半径"量化正文的定性结论。1819关键公式(本文件使用的解析式):20 级联总算力 C_cascade = sum_i N_i * f_i21 生成式总算力 C_gen = L * B * f_dec + B * f_retrieval22 异常影响半径 blast(架构) = 单模块故障时受影响的最终曝光比例2324[MOCK] 所有 FLOPs/延迟系数都是教学用的量级估计常数, 不代表任何真实系统的实测值。25"""2627from __future__ import annotations2829import random3031# ---------------------------------------------------------------- 架构参数32# [MOCK] 量级估计常数: 单条样本打分的 MFLOPs33CASCADE_STAGES = [34 # (阶段名, 入口候选数, 单条打分 MFLOPs, 可插入业务约束的决策点数)35 ("召回 Retrieval", 1_000_000, 0.002, 2),36 ("粗排 Coarse Rank", 6_000, 0.05, 2),37 ("精排 Fine Rank", 600, 2.0, 4),38 ("重排 Rerank", 60, 0.5, 5),39]4041GEN_L = 4 # 语义 ID 层数, 即自回归步数42GEN_BEAM = 64 # beam 宽度, 也即生成式模块输出的候选条数43GEN_FLOPS_STEP = 9.0 # [MOCK] 每个 Decoder 前向步的 MFLOPs(含 beam 批处理)44GEN_FLOPS_RETRIEVAL = 0.004 # [MOCK] 语义 ID -> 物品 ID 的查表 MFLOPs454647def cascade_cost() -> tuple[float, int, int]:48 """返回 (总 MFLOPs, 最终候选数, 决策点总数)。"""49 total = sum(n * f for _, n, f, _ in CASCADE_STAGES)50 hooks = sum(h for *_, h in CASCADE_STAGES)51 return total, CASCADE_STAGES[-1][1], hooks525354def generative_cost() -> tuple[float, int, int]:55 """生成式模块: 算力只与 L、beam 有关, 与全库规模解耦。"""56 total = GEN_L * GEN_BEAM * GEN_FLOPS_STEP + GEN_BEAM * GEN_FLOPS_RETRIEVAL57 # 正文: 生成式模块缺少显式多样性控制, 业务约束只能塞进 Beam Search 打分与重排58 hooks = 1 + 5 # Beam Search 打分 1 个 + 保留的重排模块 5 个59 return total, GEN_BEAM, hooks606162def blast_radius(arch: str) -> dict[str, float]:63 """单模块故障的影响半径。级联有逐层兜底, One-Model 无。"""64 if arch == "cascade":65 # 任一路召回挂掉, 其余召回仍能补齐候选 -> 影响被限制在该路的贡献占比66 return {"单路召回故障": 0.18, "粗排故障(降级为随机截断)": 0.35, "精排故障": 1.00}67 return {"单路召回故障": 1.00, "粗排故障(降级为随机截断)": 1.00, "精排故障": 1.00}686970def deploy_modes() -> list[tuple[str, str, str]]:71 """正文 §9.2 给出的两种工业部署方式。"""72 return [73 ("并行候选生成",74 "User -> {传统召回, 生成式模块} -> 重排",75 "生成式模块当作打通召回到粗排的统一通道, 候选汇入重排统一决策"),76 ("流量隔离",77 "User -> 生成式模块 -> 重排",78 "划分 ~5% 实验流量, 生成式模块直接决定 Item 集合, 不走传统漏斗"),79 ]808182def main() -> None:83 random.seed(42)8485 c_flops, c_cand, c_hooks = cascade_cost()86 g_flops, g_cand, g_hooks = generative_cost()8788 print("=" * 78)89 print("表 1 级联漏斗各级候选规模与算力(§9.2 传统多级漏斗)")90 print("-" * 78)91 print(f"{'阶段':<18}{'入口候选':>12}{'单条MFLOPs':>14}{'阶段MFLOPs':>14}{'占比':>9}")92 for name, n, f, _ in CASCADE_STAGES:93 stage = n * f94 print(f"{name:<18}{n:>12,}{f:>14.4f}{stage:>14.1f}{stage / c_flops:>8.1%}")95 print("-" * 78)96 print(f"{'级联合计':<18}{'':>12}{'':>14}{c_flops:>14.1f}{1.0:>8.1%}")9798 print()99 print("=" * 78)100 print("表 2 生成式推荐模块算力(§9.2, 自回归 L 步 + 语义ID检索)")101 print("-" * 78)102 print(f"{'环节':<28}{'次数':>8}{'单次MFLOPs':>14}{'MFLOPs':>12}")103 print(f"{'Decoder 自回归前向':<28}{GEN_L * GEN_BEAM:>8}{GEN_FLOPS_STEP:>14.4f}"104 f"{GEN_L * GEN_BEAM * GEN_FLOPS_STEP:>12.1f}")105 print(f"{'语义ID->物品ID 查表':<28}{GEN_BEAM:>8}{GEN_FLOPS_RETRIEVAL:>14.4f}"106 f"{GEN_BEAM * GEN_FLOPS_RETRIEVAL:>12.3f}")107 print("-" * 78)108 print(f"{'生成式合计':<28}{'':>8}{'':>14}{g_flops:>12.1f}")109110 print()111 print("=" * 78)112 print("表 3 两种架构的候选规模 / 算力 / 可控性对比")113 print("-" * 78)114 rows = [115 ("候选规模(最终进入决策)", f"{c_cand}", f"{g_cand}"),116 ("打分涉及的物品总量", f"{CASCADE_STAGES[0][1]:,}", f"{GEN_L * GEN_BEAM} 条路径"),117 ("总算力 MFLOPs", f"{c_flops:.1f}", f"{g_flops:.1f}"),118 ("算力是否随全库规模增长", "是(召回级线性)", "否(与 L、beam 有关)"),119 ("串行前向次数", f"{len(CASCADE_STAGES)}", f"{GEN_L}(逐 token 自回归)"),120 ("可插入业务约束的决策点", f"{c_hooks}", f"{g_hooks}"),121 ]122 print(f"{'维度':<26}{'级联漏斗':>22}{'生成式 One-Model':>26}")123 for k, a, b in rows:124 print(f"{k:<26}{a:>22}{b:>26}")125 print("-" * 78)126 print(f"算力比(生成式/级联) = {g_flops / c_flops:.3f}x; "127 f"候选规模比 = {g_cand / c_cand:.2f}x")128129 print()130 print("=" * 78)131 print("表 4 异常影响半径(§9.8: 级联的分层防护 vs One-Model 的集中风险)")132 print("-" * 78)133 cb, gb = blast_radius("cascade"), blast_radius("generative")134 print(f"{'故障场景':<30}{'级联受影响曝光':>18}{'One-Model 受影响曝光':>22}")135 for key in cb:136 print(f"{key:<30}{cb[key]:>17.0%}{gb[key]:>21.0%}")137 print("-" * 78)138 print(f"平均影响半径: 级联 {sum(cb.values()) / len(cb):.0%}, "139 f"One-Model {sum(gb.values()) / len(gb):.0%}")140141 print()142 print("=" * 78)143 print("表 5 §9.2 给出的两种工业部署方式")144 print("-" * 78)145 for name, chain, note in deploy_modes():146 print(f"[{name}] {chain}")147 print(f" {note}")148 print("-" * 78)149 print("结论(与正文一致): 生成式模块把算力从'候选规模线性'转成'生成长度线性',")150 print("但可控决策点显著减少、异常影响半径放大, 因此工业系统仍保留重排兜底。")151152153if __name__ == "__main__":154 main()如何用于学习或编写新代码
- 先看章节 README 的依赖和运行成本,定位这个例子的输入、输出及关键函数。
- 在原书 PDF 中找到对应概念的具体页;代码演示与书中论断分开标注。
- 只在依赖和耗时可控时运行单个示例,修改一个参数并记录真实输出与失败情形。
- 参考实现新代码时说明复用、改动、测试与尚未完成的生产依赖;教学示例并非生产系统。