CH 08 / SOURCE EXAMPLE
exchange_rate_value.py
ch08 混排模块/code/ch05_sequence_value/exchange_rate_value.py
这个示例讲什么
│ ├── exchange_rate_value.py 业务价值兑换关系(Exchange Rate)与统一价值空间
业务价值兑换关系(Exchange Rate)与统一价值空间。 对应正文第 8 章 §8.5「序列价值评估体系」中的核心机制: 不同业务的价值定义/统计口径/数值分布完全不同,因此业务分天然不可比; 工业界的做法不是简单 Min-Max / Z-Score 缩放,而是通过长期实验建立 **不同业务价值之间的兑换关系**,把一切折算到同一个价值单位(正文选「用户消费时长」)。 本实现做了什么: 1. 逐行复刻正文表「不同业务价值的兑换关系示例」,并由「平台收益 / 单位时长价值」 自动推导兑换系数,验证正文给出的 20 / 10 / 2 三个系数; 2. 复刻正文表「统一价值计算示例」,把 Video-A/Ad-B/Live-C/Ecom-D 的原始价值 折算为统一价值分 {3, 4, 5, 3}; 3. 复刻正文的序列对比:Seq1=[Live-C, Ad-B, Video
上面来自章节 README 或源码自身的说明,不是自动验证的运行结果,也不能代替 PDF 的书中论断。讲解时请结合实际代码、输入输出与原书页码。
源码结构与阅读入口
exchange_rate· 第 48 行兑换系数 = 该业务单位原始量带来的平台收益 / 单位时长价值。gamma· 第 53 行位置衰减权重 gamma_i(i 从 1 开始);超出正文给定表则线性外推到 0。score_seq· 第 60 行Score_seq = sum_i gamma_i * Value(item_i)。demo_exchange_table· 第 67 行textbook_items· 第 87 行正文表「统一价值计算示例」的四个 Item(原始价值 -> 统一价值分)。demo_unified_value· 第 101 行demo_sequence_choice· 第 122 行make_request· 第 147 行[MOCK] 合成一次混排请求的候选池,各业务的原始分尺度刻意差异巨大。minmax_scores· 第 167 行Min-Max 归一化:只做尺度对齐,不解决口径问题。zscore_scores· 第 180 行Z-Score 标准化:同样只是缩放。exchange_scores· 第 193 行兑换关系口径:折算到统一时长价值。best_seq_by· 第 199 行在候选池里枚举长度为 length 的序列,按给定打分口径取最优。demo_caliber_comparison· 第 209 行
1"""业务价值兑换关系(Exchange Rate)与统一价值空间。23对应正文第 8 章 §8.5「序列价值评估体系」中的核心机制:4不同业务的价值定义/统计口径/数值分布完全不同,因此业务分天然不可比;5工业界的做法不是简单 Min-Max / Z-Score 缩放,而是通过长期实验建立6**不同业务价值之间的兑换关系**,把一切折算到同一个价值单位(正文选「用户消费时长」)。78本实现做了什么:91. 逐行复刻正文表「不同业务价值的兑换关系示例」,并由「平台收益 / 单位时长价值」10 自动推导兑换系数,验证正文给出的 20 / 10 / 2 三个系数;112. 复刻正文表「统一价值计算示例」,把 Video-A/Ad-B/Live-C/Ecom-D 的原始价值12 折算为统一价值分 {3, 4, 5, 3};133. 复刻正文的序列对比:Seq1=[Live-C, Ad-B, Video-A] 得 10.0,14 Seq2=[Ad-B, Live-C, Video-A] 得 9.8,因此混排选 Seq1;154. 对照实验:Min-Max 归一化 / Z-Score 标准化 / 兑换关系三种口径,16 在合成请求上统计「选出的序列真实平台收益」,说明只有兑换关系口径17 优化的是平台整体价值(Platform Value)。1819关键公式(全部取自正文):20 1 分钟时长价值 = 0.05 元 => 1 元平台收益 = 20 分钟时长价值21 Value_ad = 20 x Revenue22 Value_live = 10 x Gift23 Value_ec = 2 x GMV24 Score_seq = sum_{i=1..L} gamma_i * Value(item_i)25"""2627import itertools28import random29import statistics3031# ------------------------------------------------------------ 正文给定常量3233MINUTE_VALUE_YUAN = 0.05 # 用户消费 1 分钟短视频 = 0.05 元平台收益3435# 正文表「不同业务价值的兑换关系示例」:业务 -> (单位原始量描述, 平台收益/单位)36# 「平台收益」列的单位是元,除以 MINUTE_VALUE_YUAN 即得折算时长价值(分钟)37EXCHANGE_TABLE = [38 ("短视频 video", "用户消费 1 分钟", 0.05),39 ("广告 ad", "广告收入 1 元", 1.00),40 ("直播 live", "直播打赏 1 元", 0.50),41 ("电商 ec", "电商 GMV 1 元", 0.10),42]4344# 正文表「位置衰减权重示例」45POS_WEIGHT = [1.0, 0.8, 0.6, 0.4]464748def exchange_rate(platform_revenue_per_unit, minute_value=MINUTE_VALUE_YUAN):49 """兑换系数 = 该业务单位原始量带来的平台收益 / 单位时长价值。"""50 return platform_revenue_per_unit / minute_value515253def gamma(i):54 """位置衰减权重 gamma_i(i 从 1 开始);超出正文给定表则线性外推到 0。"""55 if i <= len(POS_WEIGHT):56 return POS_WEIGHT[i - 1]57 return max(0.0, POS_WEIGHT[-1] - 0.2 * (i - len(POS_WEIGHT)))585960def score_seq(seq):61 """Score_seq = sum_i gamma_i * Value(item_i)。"""62 return sum(gamma(i + 1) * it["value"] for i, it in enumerate(seq))636465# ------------------------------------------------------------ 演示 1:兑换关系表6667def demo_exchange_table():68 print("=" * 78)69 print("【1】兑换关系推导(正文表「不同业务价值的兑换关系示例」)")70 print("=" * 78)71 print(f"统一价值单位:用户消费时长(分钟),1 分钟 = {MINUTE_VALUE_YUAN} 元平台收益")72 print(f"=> 1 元平台收益 = {1 / MINUTE_VALUE_YUAN:.0f} 分钟时长价值")73 print()74 print(f"{'业务':<14}{'用户行为':<18}{'平台收益(元)':>13}{'折算时长(分钟)':>15}"75 f"{'兑换系数':>10}")76 print("-" * 78)77 for biz, behavior, revenue in EXCHANGE_TABLE:78 r = exchange_rate(revenue)79 print(f"{biz:<14}{behavior:<18}{revenue:>13.2f}{r:>15.1f}{r:>10.1f}")80 print("-" * 78)81 print("与正文结论一致:Value_ad = 20 x Revenue,Value_live = 10 x Gift,"82 "Value_ec = 2 x GMV。")838485# ------------------------------------------------------------ 演示 2:统一价值分8687def textbook_items():88 """正文表「统一价值计算示例」的四个 Item(原始价值 -> 统一价值分)。"""89 return [90 {"id": "Video-A", "biz": "video", "raw_desc": "预计观看 3 分钟",91 "raw": 3.00, "value": 3.00 * exchange_rate(0.05)},92 {"id": "Ad-B", "biz": "ad", "raw_desc": "eCPM=0.20 元",93 "raw": 0.20, "value": 0.20 * exchange_rate(1.00)},94 {"id": "Live-C", "biz": "live", "raw_desc": "预计打赏 0.50 元",95 "raw": 0.50, "value": 0.50 * exchange_rate(0.50)},96 {"id": "Ecom-D", "biz": "ec", "raw_desc": "GMV=1.50 元",97 "raw": 1.50, "value": 1.50 * exchange_rate(0.10)},98 ]99100101def demo_unified_value():102 items = textbook_items()103 print()104 print("=" * 78)105 print("【2】统一价值映射(正文表「统一价值计算示例」)")106 print("=" * 78)107 print(f"{'Item':<10}{'业务':<8}{'原始价值':<18}{'兑换系数':>10}{'统一价值分':>12}")108 print("-" * 78)109 for it in items:110 rate = {"video": 1.0, "ad": 20.0, "live": 10.0, "ec": 2.0}[it["biz"]]111 print(f"{it['id']:<10}{it['biz']:<8}{it['raw_desc']:<18}"112 f"{rate:>10.1f}{it['value']:>12.2f}")113 print("-" * 78)114 vals = [round(it["value"], 2) for it in items]115 print(f"Value(item) = {{{', '.join(f'{v:.0f}' for v in vals)}}} "116 "-> 与正文一致,四个异质业务终于可直接比较。")117 return items118119120# ------------------------------------------------------------ 演示 3:序列对比121122def demo_sequence_choice(items):123 by_id = {it["id"]: it for it in items}124 seq1 = [by_id["Live-C"], by_id["Ad-B"], by_id["Video-A"]]125 seq2 = [by_id["Ad-B"], by_id["Live-C"], by_id["Video-A"]]126 print()127 print("=" * 78)128 print("【3】序列价值对比(正文 Seq1 vs Seq2)")129 print("=" * 78)130 print(f"{'位置':<6}{'gamma_i':>9} {'Seq1':<22}{'Seq2':<22}")131 print("-" * 78)132 for i in range(3):133 a, b = seq1[i], seq2[i]134 ca = f"{a['id']}(v={a['value']:.0f})"135 cb = f"{b['id']}(v={b['value']:.0f})"136 print(f"{i + 1:<6}{gamma(i + 1):>9.1f} {ca:<22}{cb:<22}")137 print("-" * 78)138 s1, s2 = score_seq(seq1), score_seq(seq2)139 print(f"Score(Seq1) = 1.0x5 + 0.8x4 + 0.6x3 = {s1:.1f}")140 print(f"Score(Seq2) = 1.0x4 + 0.8x5 + 0.6x3 = {s2:.1f}")141 print(f"Score(Seq1) - Score(Seq2) = {s1 - s2:.1f} > 0 -> 混排选择 Seq1,与正文一致。")142 print("直觉:高价值 Item 放前排能吃到更大的 gamma_i,位置分配本身就是价值分配。")143144145# ------------------------------------------------------------ 演示 4:口径对照实验146147def make_request(n_per_biz=2):148 """[MOCK] 合成一次混排请求的候选池,各业务的原始分尺度刻意差异巨大。149150 video: 预计观看分钟数 ~ 1-6 分钟151 ad: eCPM(元/次) ~ 0.05-0.45152 live: 预计打赏(元) ~ 0.0-1.2153 ec: GMV(元) ~ 0.0-8.0154 """155 pool = []156 for _ in range(n_per_biz):157 pool.append({"biz": "video", "raw": random.uniform(1.0, 6.0)})158 pool.append({"biz": "ad", "raw": random.uniform(0.05, 0.45)})159 pool.append({"biz": "live", "raw": random.uniform(0.0, 1.2)})160 pool.append({"biz": "ec", "raw": random.uniform(0.0, 8.0)})161 rate = {"video": 1.0, "ad": 20.0, "live": 10.0, "ec": 2.0}162 for it in pool:163 it["true_value"] = it["raw"] * rate[it["biz"]] # 真实统一价值(评估用)164 return pool165166167def minmax_scores(pool):168 """Min-Max 归一化:只做尺度对齐,不解决口径问题。"""169 out = []170 for biz in ("video", "ad", "live", "ec"):171 xs = [it["raw"] for it in pool if it["biz"] == biz]172 lo, hi = min(xs), max(xs)173 span = hi - lo if hi > lo else 1.0174 for it in pool:175 if it["biz"] == biz:176 out.append((it, (it["raw"] - lo) / span))177 return dict((id(it), s) for it, s in out)178179180def zscore_scores(pool):181 """Z-Score 标准化:同样只是缩放。"""182 out = []183 for biz in ("video", "ad", "live", "ec"):184 xs = [it["raw"] for it in pool if it["biz"] == biz]185 mu = statistics.fmean(xs)186 sd = statistics.pstdev(xs) or 1.0187 for it in pool:188 if it["biz"] == biz:189 out.append((it, (it["raw"] - mu) / sd))190 return dict((id(it), s) for it, s in out)191192193def exchange_scores(pool):194 """兑换关系口径:折算到统一时长价值。"""195 rate = {"video": 1.0, "ad": 20.0, "live": 10.0, "ec": 2.0}196 return dict((id(it), it["raw"] * rate[it["biz"]]) for it in pool)197198199def best_seq_by(pool, table, length=4):200 """在候选池里枚举长度为 length 的序列,按给定打分口径取最优。"""201 best, best_s = None, float("-inf")202 for comb in itertools.permutations(pool, length):203 s = sum(gamma(i + 1) * table[id(it)] for i, it in enumerate(comb))204 if s > best_s:205 best, best_s = comb, s206 return best207208209def demo_caliber_comparison(n_trial=60):210 print()211 print("=" * 78)212 print("【4】对照实验:三种价值口径选出的序列,真实平台收益谁更高")213 print("=" * 78)214 gains = {"minmax": [], "zscore": [], "exchange": []}215 mix = {"minmax": [], "zscore": [], "exchange": []}216 for _ in range(n_trial):217 pool = make_request()218 truth = dict((id(it), it["true_value"]) for it in pool)219 for key, fn in (("minmax", minmax_scores), ("zscore", zscore_scores),220 ("exchange", exchange_scores)):221 seq = best_seq_by(pool, fn(pool))222 gains[key].append(sum(gamma(i + 1) * truth[id(it)]223 for i, it in enumerate(seq)))224 mix[key].append(tuple(it["biz"] for it in seq))225 base = statistics.fmean(gains["exchange"])226 print(f"{'价值口径':<26}{'真实序列价值均值':>18}{'相对兑换关系':>14}")227 print("-" * 78)228 for key, name in (("minmax", "Min-Max 归一化"),229 ("zscore", "Z-Score 标准化"),230 ("exchange", "兑换关系(正文方案)")):231 m = statistics.fmean(gains[key])232 print(f"{name:<26}{m:>18.3f}{m / base:>14.4f}")233 print("-" * 78)234 print("首位业务分布(选出的 Top-1 坑位给了谁):")235 for key, name in (("minmax", "Min-Max"), ("zscore", "Z-Score"),236 ("exchange", "兑换关系")):237 cnt = {}238 for seq in mix[key]:239 cnt[seq[0]] = cnt.get(seq[0], 0) + 1240 dist = " ".join(f"{b}:{cnt.get(b, 0) / n_trial:.0%}"241 for b in ("video", "ad", "live", "ec"))242 print(f" {name:<12}{dist}")243 print("-" * 78)244 print("归一化口径把「各业务内部的相对名次」当成跨业务价值,等于默认四个业务同等重要,")245 print("于是常把低价值 Item 放到首位;兑换关系口径优化的是平台整体价值,因此最优。")246 print("这正是正文强调的:归一化只是缩放,没有解决价值口径不一致的问题。")247248249if __name__ == "__main__":250 random.seed(42)251 demo_exchange_table()252 items = demo_unified_value()253 demo_sequence_choice(items)254 demo_caliber_comparison()如何用于学习或编写新代码
- 先看章节 README 的依赖和运行成本,定位这个例子的输入、输出及关键函数。
- 在原书 PDF 中找到对应概念的具体页;代码演示与书中论断分开标注。
- 只在依赖和耗时可控时运行单个示例,修改一个参数并记录真实输出与失败情形。
- 参考实现新代码时说明复用、改动、测试与尚未完成的生产依赖;教学示例并非生产系统。