CH 15 / SOURCE EXAMPLE
feature_governance.py
ch15 特征工程/code/ch05_feature_selection/feature_governance.py
这个示例讲什么
│ └── feature_governance.py §15.6.7 注册表、生命周期与下线决策
特征治理:注册表、生命周期与下线决策(第15章 §15.6.7) 正文列出的治理要素:定义、负责人、来源、新鲜度、覆盖率、成本、质量、漂移、 使用情况、生命周期。单个模型开发者记不住这些,所以需要平台化的注册表。 脚本用 [MOCK] 数据模拟一个 12 条记录的特征注册表,把前几节算出来的指标 (重要性、成本、覆盖率、漂移)汇到一张表里,然后: 1) 按生命周期状态分组:上线 → 观察 → 待下线 → 实验(已下线的记录会移出注册表) 2) 用决策规则自动给出处置建议(保留 / 观察 / 下线 / 补文档) 3) 计算"僵尸特征"比例:长期无人使用、指标又不好的特征 4) 打印一次下线评审需要收集的证据清单 所有数据均为 [MOCK],用于演示治理流程,不对应任何真实系统。 运行:python3 ch05_feature_selection/feature_governance
上面来自章节 README 或源码自身的说明,不是自动验证的运行结果,也不能代替 PDF 的书中论断。讲解时请结合实际代码、输入输出与原书页码。
源码结构与阅读入口
decide· 第 61 行治理规则([MOCK]):综合边际收益、成本、覆盖率、使用方数量。main· 第 84 行
1"""2特征治理:注册表、生命周期与下线决策(第15章 §15.6.7)34正文列出的治理要素:定义、负责人、来源、新鲜度、覆盖率、成本、质量、漂移、5使用情况、生命周期。单个模型开发者记不住这些,所以需要平台化的注册表。67脚本用 [MOCK] 数据模拟一个 12 条记录的特征注册表,把前几节算出来的指标8(重要性、成本、覆盖率、漂移)汇到一张表里,然后:9101) 按生命周期状态分组:上线 → 观察 → 待下线 → 实验(已下线的记录会移出注册表)112) 用决策规则自动给出处置建议(保留 / 观察 / 下线 / 补文档)123) 计算"僵尸特征"比例:长期无人使用、指标又不好的特征134) 打印一次下线评审需要收集的证据清单1415所有数据均为 [MOCK],用于演示治理流程,不对应任何真实系统。1617运行:python3 ch05_feature_selection/feature_governance.py18"""1920from __future__ import annotations2122import pathlib23import sys2425sys.path.insert(0, str(pathlib.Path(__file__).resolve().parent))2627# 前几节实测/估算的指标,作为注册表的一部分28DELTA_AUC = {29 "user_activity": 0.2173, "item_ctr": 0.0432, "cat_match": 0.0186,30 "clicks_7d": 0.0062, "clicks_30d": 0.0000, "hour_bucket": -0.0001,31 "is_new_user": -0.0004, "hist_len": -0.0002, "device": 0.0004,32 "price_band": -0.0001, "item_popularity": 0.0000, "follow_ratio": 0.0001,33}34COST = {35 "user_activity": 15.70, "clicks_7d": 7.90, "clicks_30d": 6.65,36 "item_ctr": 4.25, "follow_ratio": 4.10, "item_popularity": 2.35,37 "hist_len": 1.94, "price_band": 1.30, "is_new_user": 0.24,38 "cat_match": 0.33, "hour_bucket": 0.0, "device": 0.0,39}4041# [MOCK] 注册表:owner / source / 更新频率 / 覆盖率 / 使用方 / 状态42REGISTRY = [43 # name, owner, source, freshness, coverage, users, state44 ("user_activity", "推荐特征组", "实时流 + 离线聚合", "小时级", 0.98, 6, "上线"),45 ("clicks_7d", "推荐特征组", "离线宽表", "天级", 0.97, 6, "上线"),46 ("clicks_30d", "推荐特征组", "离线宽表", "天级", 0.97, 6, "待下线"),47 ("item_ctr", "内容理解组", "离线统计", "天级", 0.95, 5, "上线"),48 ("item_popularity", "内容理解组", "离线统计", "天级", 0.99, 1, "观察"),49 ("cat_match", "推荐特征组", "在线实时计算", "请求级", 1.00, 5, "上线"),50 ("hist_len", "推荐特征组", "离线宽表", "天级", 0.93, 2, "观察"),51 ("follow_ratio", "社交组", "跨服务 RPC", "小时级", 0.88, 1, "待下线"),52 ("is_new_user", "用户画像组", "用户属性", "天级", 0.60, 4, "观察"),53 ("hour_bucket", "推荐特征组", "请求自带", "请求级", 1.00, 6, "上线"),54 ("device", "推荐特征组", "请求自带", "请求级", 1.00, 6, "上线"),55 ("price_band", "电商组", "离线统计", "周级", 0.72, 1, "实验"),56]5758STATE_ORDER = ["上线", "观察", "待下线", "实验"]596061def decide(name: str, coverage: float, users: int, state: str) -> tuple[str, str]:62 """63 治理规则([MOCK]):综合边际收益、成本、覆盖率、使用方数量。6465 已经进入"待下线"评审的特征优先按评审结论走;其余特征按指标判断。66 """67 delta = DELTA_AUC[name]68 cost = COST[name]69 if state == "待下线":70 if delta > 0.001:71 return "复核", "已列入下线但仍有边际收益,需确认是否误判"72 return "下线", "无增量,沿用既有下线排期"73 if delta > 0.001 and coverage >= 0.9:74 return "保留", "有边际收益且覆盖稳定"75 if delta > 0.001:76 return "观察", f"有收益但覆盖率仅 {coverage:.0%},先补数据源"77 if users >= 4:78 return "保留", "无增量但被多个模型依赖,删之前需评估下游"79 if cost > 3.0:80 return "下线", "无增量、成本高、使用方少"81 return "待讨论", "无增量、低成本,可留在观察区"828384def main() -> None:85 print("特征治理:注册表、生命周期与处置建议(§15.6.7)\n")8687 print("① 按状态分组:")88 for state in STATE_ORDER:89 names = [r[0] for r in REGISTRY if r[6] == state]90 print(f" {state:<6}({len(names):>2}): {', '.join(names) if names else '(无)'}")9192 print("\n② 注册表全貌(按状态 + 边际收益排序):")93 rows = sorted(REGISTRY, key=lambda r: (STATE_ORDER.index(r[6]), -DELTA_AUC[r[0]]))94 print(f"{'特征':<17}{'状态':<7}{'负责人':<12}{'来源':<18}{'更新':<8}"95 f"{'覆盖':>6}{'成本':>7}{'ΔAUC':>8}{'使用方':>7}")96 print("-" * 96)97 for name, owner, source, fresh, cov, users, state in rows:98 print(f"{name:<17}{state:<7}{owner:<12}{source:<18}{fresh:<8}"99 f"{cov:>6.0%}{COST[name]:>7.2f}{DELTA_AUC[name]:>+8.4f}{users:>7}")100101 print("\n③ 自动处置建议:")102 print(f"{'特征':<17}{'建议':<8}{'理由':<44}")103 print("-" * 71)104 suggestions = {}105 for name, owner, source, fresh, cov, users, state in REGISTRY:106 action, why = decide(name, cov, users, state)107 suggestions[name] = action108 print(f"{name:<17}{action:<8}{why:<44}")109110 counts = {}111 for a in suggestions.values():112 counts[a] = counts.get(a, 0) + 1113 print("\n 处置分布:" + " ".join(f"{k}={v}" for k, v in sorted(counts.items())))114115 # 僵尸特征:无增量 + 使用方 <= 1116 zombies = [r[0] for r in REGISTRY117 if DELTA_AUC[r[0]] <= 0.001 and r[5] <= 1]118 print(f"\n④ '僵尸特征'(长期低收益 + 使用方 ≤ 1):{len(zombies)} 个")119 print(f" {', '.join(zombies) if zombies else '(无)'}")120 print(" 这些特征是特征负债的主要来源:没人敢删,但也说不清价值。")121122 # 低覆盖特征:即使当前边际 ΔAUC 小,也可能因为只覆盖少数用户而"看起来没用"123 low_cov = [r[0] for r in REGISTRY if r[4] < 0.9]124 print(f"\n⑤ 覆盖率不足 90% 的特征:{', '.join(low_cov) if low_cov else '(无)'}")125 print(" 这类特征不建议直接下线:低覆盖会让它在离线评估里天然吃亏,")126 print(" 优先级是补数据源、加兜底、做降级策略,拿回覆盖后再评一次。")127128 print("\n⑥ 下线评审证据清单(一个特征走到'下线'需要准备):")129 checklist = [130 "离线消融:整组删除后的 ΔAUC / LogLoss 变化(本目录 feature_redundancy.py)",131 "成本核算:计算 / 存储 / 网络 / 延迟四项的现值(feature_cost_roi.py)",132 "质量与漂移:近 30 天覆盖率、新鲜度、PSI 记录(feature_quality / data_drift)",133 "使用方确认:所有依赖该特征的模型与规则已排期改造",134 "灰度计划:小流量关闭 → 观测核心指标 → 全量 → 保留回滚开关",135 "血缘归档:数据源、计算任务、下游模型记录留档,便于未来复盘",136 ]137 for i, item in enumerate(checklist, 1):138 print(f" {i}. {item}")139140 print("\n治理的本质是让'加特征'和'删特征'都变成有据可查的流程,")141 print("而不是靠某个人的记忆。平台承担监控与告警,评审会承担决策(§15.6.7)。")142143144if __name__ == "__main__":145 main()如何用于学习或编写新代码
- 先看章节 README 的依赖和运行成本,定位这个例子的输入、输出及关键函数。
- 在原书 PDF 中找到对应概念的具体页;代码演示与书中论断分开标注。
- 只在依赖和耗时可控时运行单个示例,修改一个参数并记录真实输出与失败情形。
- 参考实现新代码时说明复用、改动、测试与尚未完成的生产依赖;教学示例并非生产系统。