CH 15 / SOURCE EXAMPLE
user_item_context_features.py
ch15 特征工程/code/ch01_feature_system/user_item_context_features.py
这个示例讲什么
│ ├── user_item_context_features.py §15.1.1–§15.1.4 用户/物品/上下文/行为特征清单与覆盖率
用户/物品/上下文特征的体系化组织(第15章 §15.1) 本脚本把同一批业务数据整理成"特征清单":每条特征标注来源(用户/物品/上下文/ 行为/多模态)、表示形式(Sparse/Dense/Sequence/Embedding)、基数与覆盖率, 用来演示正文的核心观点——特征体系要同时回答"特征来自哪里"和"特征以什么形式 进入模型"两个问题。 运行:python3 ch01_feature_system/user_item_context_features.py
上面来自章节 README 或源码自身的说明,不是自动验证的运行结果,也不能代替 PDF 的书中论断。讲解时请结合实际代码、输入输出与原书页码。
源码结构与阅读入口
feature_inventory· 第 23 行按正文的分类整理特征清单,并统计基数/覆盖率。main· 第 67 行
1"""2用户/物品/上下文特征的体系化组织(第15章 §15.1)34本脚本把同一批业务数据整理成"特征清单":每条特征标注来源(用户/物品/上下文/5行为/多模态)、表示形式(Sparse/Dense/Sequence/Embedding)、基数与覆盖率,6用来演示正文的核心观点——特征体系要同时回答"特征来自哪里"和"特征以什么形式7进入模型"两个问题。89运行:python3 ch01_feature_system/user_item_context_features.py10"""1112from __future__ import annotations1314import pathlib15import random16import sys1718sys.path.insert(0, str(pathlib.Path(__file__).resolve().parents[1]))1920from common import make_catalog, make_events, make_users, print_table # noqa: E402212223def feature_inventory(users: list[dict], items: list[dict], events: list[dict]) -> list[list]:24 """按正文的分类整理特征清单,并统计基数/覆盖率。"""25 n_users, n_items, n_events = len(users), len(items), len(events)2627 def ratio(pred, seq):28 if not seq:29 return 0.030 return sum(1 for x in seq if pred(x)) / len(seq)3132 # 覆盖率:Dense 特征为"非缺失/非默认值"的比例;Sparse 特征为有取值的比例33 click_events = [e for e in events if e["action"] in ("click", "watch")]34 watch_events = [e for e in events if e["action"] == "watch"]35 interacted = {e["user_id"] for e in click_events}36 popular_items = {e["item_id"] for e in click_events}3738 return [39 # 来源, 特征, 表示形式, 基数, 覆盖率, 示例40 ["用户", "user_id", "Sparse", n_users, 1.0, "u_000123"],41 ["用户", "age_band", "Sparse", 5, 0.97, "3"],42 ["用户", "gender", "Sparse", 2, 0.92, "1"],43 ["用户", "city_tier", "Sparse", 4, 0.88, "2"],44 ["用户", "intensity", "Dense", "-", 1.0, "0.7341"],45 ["用户", "ctr_7d", "Dense", "-", f"{len(interacted) / n_users:.2%}", "0.0831"],46 ["用户", "watch_time_30d", "Dense", "-", f"{len(watch_events) and len({e['user_id'] for e in watch_events}) / n_users:.2%}", "412.7s"],47 ["用户", "action_seq", "Sequence", n_users, f"{len(interacted) / n_users:.2%}", "[i12,i88,i31,...]"],48 ["物品", "item_id", "Sparse", n_items, 1.0, "v_000042"],49 ["物品", "category", "Sparse", len({it["category"] for it in items}), 1.0, "sports"],50 ["物品", "author_id", "Sparse", len({it["author"] for it in items}), 1.0, "a_011"],51 ["物品", "duration", "Dense", "-", 1.0, "37.4s"],52 ["物品", "quality", "Dense", "-", 1.0, "0.8123"],53 ["物品", "empirical_ctr", "Dense", "-", f"{len(popular_items) / n_items:.2%}", "0.0612"],54 ["物品", "cover_embedding", "Embedding", "-", 0.63, "d=64"],55 ["上下文", "hour", "Sparse", 24, 1.0, "21"],56 ["上下文", "weekday", "Sparse", 7, 1.0, "5"],57 ["上下文", "network", "Sparse", 4, 1.0, "wifi"],58 ["上下文", "device_price", "Dense", "-", 0.95, "2399"],59 ["上下文", "session_len", "Dense", "-", 1.0, "12"],60 ["上下文", "cand_size", "Dense", "-", 1.0, "200"],61 ["行为", "click_1d/7d/30d", "Dense", "-", 1.0, "3 / 21 / 88"],62 ["行为", "recency_last_click", "Dense", "-", f"{len(interacted) / n_users:.2%}", "0.42h"],63 ["行为", "watch_ratio_7d", "Dense", "-", 0.91, "0.618"],64 ]656667def main() -> None:68 rng = random.Random(42)69 users = make_users(200, 8, rng)70 items = make_catalog(400, 8, rng)71 events = make_events(200, 20_000, 400, 8, 30, random.Random(2024))7273 rows = feature_inventory(users, items, events)74 # 覆盖率统一成百分比字符串75 for r in rows:76 r[4] = f"{r[4]:.1%}" if isinstance(r[4], float) else r[4]77 print("特征清单(来源 × 表示形式)")78 print_table(["来源", "特征", "表示", "基数", "覆盖率", "示例"], rows)7980 by_source: dict[str, int] = {}81 by_repr: dict[str, int] = {}82 for r in rows:83 by_source[r[0]] = by_source.get(r[0], 0) + 184 by_repr[r[2]] = by_repr.get(r[2], 0) + 185 print("\n来源分布:", " ".join(f"{k}={v}" for k, v in by_source.items()))86 print("表示分布:", " ".join(f"{k}={v}" for k, v in by_repr.items()))8788 # ---- 同一信息的不同表示形式:以"用户近7天观看体育类内容次数"为例 ----89 print("\n同一业务信息(7 天内观看体育内容次数 = 6)的四种表示:")90 print_table(91 ["表示形式", "模型输入", "信息损失", "参数量", "备注"],92 [93 ["Sparse ID", "Embedding[6]", "完全丢失数值顺序", "词表大小 × d", "6 与 7 是两个无关类别"],94 ["Dense 原值", "6.0(可标准化)", "无", "0", "保留数值语义,最省参数"],95 ["分桶 Embedding", "Embedding[bucket(6)]", "桶内信息丢失", "桶数 × d", "兼顾非线性与语义"],96 ["Sequence", "[c,c,g,c,c,c]", "无(但更贵)", "序列编码器参数", "保留次序与具体内容"],97 ],98 )99100 # ---- 覆盖率与缺失的代价 ----101 missing = 0.38102 usable = 1 - missing103 print(f"\n示例:某商品类 Dense 特征覆盖率 {usable:.0%},"104 f"若按默认值 0 填充,模型看到的是 {missing:.0%} 的'零'样本,"105 "这部分样本会把该特征的权重向 0 拉偏(正文 §15.6.4 特征质量)。")106107108if __name__ == "__main__":109 main()如何用于学习或编写新代码
- 先看章节 README 的依赖和运行成本,定位这个例子的输入、输出及关键函数。
- 在原书 PDF 中找到对应概念的具体页;代码演示与书中论断分开标注。
- 只在依赖和耗时可控时运行单个示例,修改一个参数并记录真实输出与失败情形。
- 参考实现新代码时说明复用、改动、测试与尚未完成的生产依赖;教学示例并非生产系统。