CH 10 / SOURCE EXAMPLE

swing.py

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

这个示例讲什么

│ ├── swing.py Swing:共现用户重叠度惩罚

Swing 召回(第10章 §10.1.4) Swing 在 ItemCF 余弦相似度基础上增加了用户对重叠的惩罚项: 若两个用户 A、B 本身就有大量共同交互物品,则对 (i,j) 共现的贡献应打折扣, 从而避免"同一圈子"用户反复强化热门物品对之间的虚假相似度。 Swing 相似度: S(i, j) = Σ_{u,v ∈ N(i)∩N(j)} 1 / (α + |N(u) ∩ N(v)|)

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

源码结构与阅读入口

  1. build_swing_similarity · 第 15 行离线计算 Swing 物品相似度。
  2. recall_swing · 第 62 行在线召回:与 ItemCF 召回逻辑相同,相似度表换成 Swing 版本。

完整源码 · 95 行

1"""2Swing 召回(第10章 §10.1.4)34Swing 在 ItemCF 余弦相似度基础上增加了用户对重叠的惩罚项:5若两个用户 A、B 本身就有大量共同交互物品,则对 (i,j) 共现的贡献应打折扣,6从而避免"同一圈子"用户反复强化热门物品对之间的虚假相似度。78Swing 相似度:9    S(i, j) = Σ_{u,v ∈ N(i)∩N(j)} 1 / (α + |N(u) ∩ N(v)|)10"""1112from collections import defaultdict131415def build_swing_similarity(16    user_items: dict[str, list[str]],17    alpha: float = 5.0,18    top_k: int = 20,19) -> dict[str, list[tuple[str, float]]]:20    """21    离线计算 Swing 物品相似度。2223    Args:24        user_items: {user_id: [item_id, ...]}25        alpha: 平滑系数,防止分母为零26        top_k: 每个物品保留的 Top-K 邻居27    """28    # 物品→用户集合29    item_users: dict[str, set[str]] = defaultdict(set)30    # 用户→物品集合(方便计算用户间交集)31    user_item_set: dict[str, set[str]] = {u: set(items) for u, items in user_items.items()}3233    for user, items in user_items.items():34        for item in items:35            item_users[item].add(user)3637    item_sim: dict[str, dict[str, float]] = defaultdict(lambda: defaultdict(float))38    items = list(item_users.keys())3940    for idx, i in enumerate(items):41        for j in items[idx + 1 :]:42            common_users = item_users[i] & item_users[j]43            if not common_users:44                continue45            score = 0.046            common_list = list(common_users)47            for a in range(len(common_list)):48                for b in range(a + 1, len(common_list)):49                    u, v = common_list[a], common_list[b]50                    uv_common = len(user_item_set[u] & user_item_set[v])51                    score += 1.0 / (alpha + uv_common)52            item_sim[i][j] += score53            item_sim[j][i] += score5455    result: dict[str, list[tuple[str, float]]] = {}56    for item, neighbors in item_sim.items():57        ranked = sorted(neighbors.items(), key=lambda x: x[1], reverse=True)58        result[item] = ranked[:top_k]59    return result606162def recall_swing(63    user_id: str,64    user_items: dict[str, list[str]],65    item_sim: dict[str, list[tuple[str, float]]],66    top_n: int = 10,67) -> list[tuple[str, float]]:68    """在线召回:与 ItemCF 召回逻辑相同,相似度表换成 Swing 版本。"""69    history = set(user_items.get(user_id, []))70    score: dict[str, float] = defaultdict(float)71    for j in history:72        for i, sim in item_sim.get(j, []):73            if i not in history:74                score[i] += sim75    return sorted(score.items(), key=lambda x: x[1], reverse=True)[:top_n]767778# ── 快速验证 ──────────────────────────────────────────────────────────────────79if __name__ == "__main__":80    user_items = {81        "u1": ["i1", "i2", "i3"],82        "u2": ["i1", "i2", "i4"],83        "u3": ["i2", "i3", "i5"],84        "u4": ["i1", "i3", "i5"],85        "u5": ["i4", "i5"],86    }87    sim = build_swing_similarity(user_items, alpha=5.0, top_k=5)88    print("Swing 相似度(Top-3):")89    for item in ["i1", "i2"]:90        print(f"  {item}: {sim.get(item, [])[:3]}")9192    result = recall_swing("u1", user_items, sim, top_n=5)93    print("\nu1 的 Swing 召回结果:")94    for item, s in result:95        print(f"  {item}: {s:.4f}")

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

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