CH 10 / SOURCE EXAMPLE

item_cf.py

ch10 召回模型/code/ch01_collaborative_filtering/item_cf.py

这个示例讲什么

│ ├── item_cf.py ItemCF:基于物品的协同过滤

ItemCF 召回 —— 基于物品的协同过滤(第10章 §10.1.1) 核心思想:统计物品间共现用户数,用余弦相似度衡量物品相似性; 召回时查询用户交互物品的相似物品列表并累加得分。

上面来自章节 README 或源码自身的说明,不是自动验证的运行结果,也不能代替 PDF 的书中论断。讲解时请结合实际代码、输入输出与原书页码。

源码结构与阅读入口

  1. build_item_similarity · 第 12 行离线计算物品间相似度。
  2. recall_itemcf · 第 48 行在线召回:根据用户历史行为和物品相似度表打分并排序。

完整源码 · 87 行

1"""2ItemCF 召回 —— 基于物品的协同过滤(第10章 §10.1.1)34核心思想:统计物品间共现用户数,用余弦相似度衡量物品相似性;5召回时查询用户交互物品的相似物品列表并累加得分。6"""78import math9from collections import defaultdict101112def build_item_similarity(user_items: dict[str, list[str]], top_k: int = 20) -> dict[str, list[tuple[str, float]]]:13    """14    离线计算物品间相似度。1516    Args:17        user_items: {user_id: [item_id, ...]},每个用户的交互物品列表18        top_k: 每个物品保留的最相似邻居数1920    Returns:21        {item_i: [(item_j, sim), ...]} 按相似度降序排列22    """23    # 统计物品交互用户数 N(i) 以及共现次数 |N(i) ∩ N(j)|24    item_user_count: dict[str, int] = defaultdict(int)   # N(i)25    co_occur: dict[str, dict[str, int]] = defaultdict(lambda: defaultdict(int))2627    for _, items in user_items.items():28        for i in items:29            item_user_count[i] += 130        # 枚举用户行为物品对31        for i in items:32            for j in items:33                if i != j:34                    co_occur[i][j] += 13536    # 余弦相似度:|N(i) ∩ N(j)| / sqrt(N(i) * N(j))37    item_sim: dict[str, list[tuple[str, float]]] = {}38    for i, neighbors in co_occur.items():39        sims = []40        for j, cnt in neighbors.items():41            sim = cnt / math.sqrt(item_user_count[i] * item_user_count[j])42            sims.append((j, sim))43        sims.sort(key=lambda x: x[1], reverse=True)44        item_sim[i] = sims[:top_k]45    return item_sim464748def recall_itemcf(49    user_id: str,50    user_items: dict[str, list[str]],51    item_sim: dict[str, list[tuple[str, float]]],52    top_n: int = 10,53) -> list[tuple[str, float]]:54    """55    在线召回:根据用户历史行为和物品相似度表打分并排序。5657    得分:p(u, i) = Σ_{j ∈ N(u)} S(i, j)58    """59    history = set(user_items.get(user_id, []))60    score: dict[str, float] = defaultdict(float)61    for j in history:62        for i, sim in item_sim.get(j, []):63            if i not in history:64                score[i] += sim65    ranked = sorted(score.items(), key=lambda x: x[1], reverse=True)66    return ranked[:top_n]676869# ── 快速验证 ──────────────────────────────────────────────────────────────────70if __name__ == "__main__":71    user_items = {72        "u1": ["i1", "i2", "i3"],73        "u2": ["i1", "i2", "i4"],74        "u3": ["i2", "i3", "i5"],75        "u4": ["i1", "i3", "i5"],76        "u5": ["i4", "i5"],77    }7879    item_sim = build_item_similarity(user_items, top_k=5)80    print("物品相似度(Top-3):")81    for item in ["i1", "i2", "i3"]:82        print(f"  {item}: {item_sim.get(item, [])[:3]}")8384    result = recall_itemcf("u1", user_items, item_sim, top_n=5)85    print("\nu1 的 ItemCF 召回结果:")86    for item, score in result:87        print(f"  {item}: {score:.4f}")

如何用于学习或编写新代码

  1. 先看章节 README 的依赖和运行成本,定位这个例子的输入、输出及关键函数。
  2. 在原书 PDF 中找到对应概念的具体页;代码演示与书中论断分开标注。
  3. 只在依赖和耗时可控时运行单个示例,修改一个参数并记录真实输出与失败情形。
  4. 参考实现新代码时说明复用、改动、测试与尚未完成的生产依赖;教学示例并非生产系统。