📑 本页目录(点开跳转)
18 · 进阶专题:GNN、蒸馏、去偏与强化学习
⏱ 35 分钟 | 🎁 进阶选修 | 建议至少学完第 8、12 节再来
这一节是五个独立的小专题,可以只挑感兴趣的看,互相不依赖。
🎯 一句话
这五个专题是「从会用到精通」的分水岭:图神经网络(换个视角看数据)、知识蒸馏(让大模型的能力装进小模型)、因果去偏(对抗数据里的谎言)、强化学习(优化长期而非单次)、LLM 特征工厂(2026 年性价比之王)。
专题一:图神经网络召回(LightGCN)
换个视角:交互矩阵其实是一张图
用户-物品交互,除了看成矩阵,还可以看成二部图:
u1 ─────── i1
u1 ─────── i2 用户和物品是两类节点
u2 ─────── i2 交互是边
u2 ─────── i3
u3 ─────── i3
图视角的红利:可以看「多跳」关系
u1 → i2 → u2 → i3
(u1 没看过 i3,但通过 2 跳连到了它 → 潜在兴趣!)
协同过滤只用了 1 跳信息(直接交互)。GNN 把 2 跳、3 跳的「间接协同信号」也显式地用上了。
LightGCN:把 GCN 里没用的东西全砍掉
何向南团队 2020 年的经典工作。做了一个大胆的实验:把标准 GCN 的特征变换矩阵 W 和非线性激活都删掉,效果反而更好。
为什么? 推荐场景的输入只有 ID Embedding(没有丰富的节点特征),复杂的变换只会过拟合。
剩下的唯一操作:邻居平均(消息传递)
$$e_u^{(k+1)} = \sum_{i \in N(u)} \frac{1}{\sqrt{|N(u)|}\sqrt{|N(i)|}} e_i^{(k)}$$
💡 人话翻译:
第 k+1 层的用户向量 = 他交互过的所有物品的第 k 层向量的加权平均。 物品同理。做 K 层,就吸收了 K 跳以内的邻居信息。 最终表示 = 各层的平均(既有自己,也有 1 跳、2 跳邻居的信息)。
import torch, torch.nn as nn
class LightGCN(nn.Module):
def __init__(self, n_users, n_items, k=64, n_layers=3):
super().__init__()
self.n_users, self.n_items, self.n_layers = n_users, n_items, n_layers
self.emb = nn.Embedding(n_users + n_items, k)
nn.init.normal_(self.emb.weight, std=0.1)
def propagate(self, norm_adj):
"""norm_adj: 归一化的邻接稀疏矩阵 (U+I, U+I)"""
x = self.emb.weight
outs = [x]
for _ in range(self.n_layers):
x = torch.sparse.mm(norm_adj, x) # 一层 = 一次邻居平均
outs.append(x)
final = torch.stack(outs, dim=0).mean(0) # 各层取平均
return final[:self.n_users], final[self.n_users:]
# 训练:用第 5 节的 BPR loss,users/items 向量点积打分
什么时候值得用 GNN
| 情况 | 建议 |
|---|---|
| 数据极稀疏(每用户交互 < 10 次) | ✅ 多跳信息收益大 |
| 有社交关系(好友、关注) | ✅ 图天然表达这种结构 |
| 数据充足、行为丰富 | ⚠️ 收益不明显,SASRec/双塔通常更划算 |
| 物品每天大量更新 | ❌ 图要频繁重建,工程代价大 |
📌 现实定位:LightGCN 是学术界最强的 CF baseline 之一;工业界通常把它当召回的其中一路,而不是主力。
专题二:Swing —— 工业界最强的 i2i 算法之一
第 4 节的 ItemCF 有个弱点:共现统计容易被「巧合」污染。 两个物品被同一批人点过,可能只是因为它们都在同一个活动页上。
阿里 Swing 的洞察:如果多对用户都同时点了物品 i 和 j,且这些用户之间的重合度低(来自不同圈子),那 i 和 j 才是真的相关。
$$\text{sim}(i,j) = \sum_{u \in U_i \cap U_j} \sum_{v \in U_i \cap U_j} \frac{1}{\alpha + |I_u \cap I_v|}$$
💡 人话翻译:
对每一对同时点过 i、j 的用户 (u, v): 如果 u 和 v 的整体行为很像($|I_u \cap I_v|$ 大)→ 他们可能只是同一个小圈子,证据打折; 如果 u 和 v 八竿子打不着却都点了 i 和 j → 强证据,i 和 j 是真相关。
名字的由来:u、i、v、j 四个节点构成一个「秋千(Swing)」形状的结构。
from itertools import combinations
from collections import defaultdict
def swing(user_items, alpha=1.0, top_k=20):
# 反向索引:物品 → 点过它的用户集合
item_users = defaultdict(set)
for u, items in user_items.items():
for i in items:
item_users[i].add(u)
user_set = {u: set(items) for u, items in user_items.items()}
sim = defaultdict(lambda: defaultdict(float))
for i, j in combinations(item_users, 2):
common = item_users[i] & item_users[j]
if len(common) < 2:
continue
for u, v in combinations(common, 2):
overlap = len(user_set[u] & user_set[v])
w = 1.0 / (alpha + overlap)
sim[i][j] += w
sim[j][i] += w
return {i: dict(sorted(s.items(), key=lambda x: -x[1])[:top_k])
for i, s in sim.items()}
⚠️ 复杂度是 O(物品对 × 用户对),全量算不动。工业实现要做用户采样(每个物品对最多采 N 对用户)+ 分布式计算。
📌 Swing 在阿里系产品长期是「买了又买」「看了又看」的主力算法,鲁棒性显著好于朴素 ItemCF。
专题三:知识蒸馏 —— 大模型的能力装进小模型
为什么推荐系统特别需要蒸馏
第 6 节说过:粗排必须比精排快 10 倍,但要「尽可能像精排」。 第 15 节说过:推理延迟是生命线。
蒸馏就是同时满足这两者的标准答案。
为什么学「教师的分数」比学「真实标签」好?
真实标签只有 0 和 1,信息量很少。教师的打分是连续的、带排序信息的:
真实标签: A=1 B=0 C=0 ← B 和 C 看起来一样差
教师打分: A=0.9 B=0.4 C=0.05 ← 其实 B 远比 C 好!
学生从教师那里学到了"B 和 C 的差别"——这是标签里没有的暗知识 (Dark Knowledge)
推荐系统蒸馏的三种姿势
| 姿势 | 做法 | 用在哪 |
|---|---|---|
| 分数蒸馏 (pointwise) | 学生拟合教师的 logit(MSE) | 最简单,粗排蒸馏的起点 |
| 排序蒸馏 (listwise) ⭐ | 学生学教师对候选列表的排序,而不是绝对分 | 更符合粗排职责(一致性) |
| 特征/表示蒸馏 | 学生的中间层向学教师的中间层 | Embedding 压缩 |
import torch
import torch.nn.functional as F
def ranking_distill_loss(student_logits, teacher_logits, temperature=2.0):
"""
Listwise 蒸馏:把两边的打分都 softmax 成"列表上的分布",
然后让学生的分布逼近教师的分布(KL 散度)
student_logits/teacher_logits: (B, N) 同一批候选在两个模型下的分
"""
t = F.softmax(teacher_logits / temperature, dim=-1)
s = F.log_softmax(student_logits / temperature, dim=-1)
return F.kl_div(s, t, reduction="batchmean") * temperature ** 2
# 总损失 = 真实标签损失 + λ × 蒸馏损失
# loss = bce(student_logits, labels) + 0.5 * ranking_distill_loss(student_logits, teacher_logits)
🔮 2026 视角:蒸馏正在变得更重要——生成式大模型(第 14 节)效果好但太贵, 「大模型离线打分 → 蒸馏成轻量模型上线」是当前最现实的落地路径之一(如 MLP 蒸馏生成式推荐的研究方向)。
专题四:因果与去偏 —— 对抗数据里的谎言
第 12 节讲了曝光偏差的存在,这里讲怎么定量地纠正它。
核心工具:IPS(逆倾向得分加权)
问题:热门物品被曝光的机会大 → 训练数据里它们的样本多 → 模型进一步偏向它们。
IPS 的思路:每条样本除以「它被观测到的概率」。
$$\mathcal{L}_{IPS} = \frac{1}{N}\sum_{(u,i)\in D} \frac{\delta(u,i)}{P(o_{ui}=1)}$$
💡 人话翻译:
一个冷门物品的样本能进训练集,是小概率事件——所以它代表了一大批没被观测到的同类,要放大它的权重。 一个热门物品的样本随处可见——权重缩小。 就像民意调查:如果你的受访者里城市人口占 90%(实际只占 60%),就要给农村受访者加权。
# 最简单的实现:用物品的曝光频率估计倾向得分
propensity = item_exposure_count / item_exposure_count.sum()
propensity = np.clip(propensity, 1e-4, None) # ⭐ 必须截断!
sample_weight = 1.0 / propensity[item_ids]
loss = (sample_weight * bce_per_sample).mean()
⚠️ IPS 的致命弱点:方差爆炸。倾向得分接近 0 的样本权重会大到离谱,一条样本能掀翻整个模型。 ✅ 缓解:截断(clipping)、自归一化(SNIPS)、或升级到 Doubly Robust(IPS + 一个预估模型互为保险,其中一个准就行)。
无偏数据:最贵但最干净的解法
第 13 节说的「1-5% 随机流量」,在去偏语境下的正式用法:
📌 实用主义总结:中小团队优先做「位置偏差建模」(第 12 节,收益大成本低), IPS 类方法在评估(Off-Policy Evaluation)里比在训练里更常用。
专题五:强化学习 —— 优化长期价值
为什么点击率模型天然短视
精排优化的是:这一次曝光的点击概率
产品要的是: 用户三个月后还在不在
两者可能冲突:
标题党 → 这次点击率高 ✅ → 用户长期反感流失 ❌
深度长文 → 这次点击率低 ❌ → 用户长期信任留存 ✅
强化学习的视角:推荐是序列决策——每次推荐是一个动作,用户的长期活跃是累计奖励。
状态 s = 用户当前的兴趣状态(历史行为、疲劳度、会话上下文)
动作 a = 推荐哪批内容
奖励 r = 即时反馈(点击/时长)+ 折扣后的未来反馈
目标 = max E[ Σ γᵗ·rₜ ] ← 不是 max 单次 r!
现实中的三大拦路虎
| 难题 | 说明 |
|---|---|
| 不能在线试错 | 游戏可以死一万次重来,推荐每次坏动作都伤害真实用户 → 只能离线 RL(Off-Policy),从历史日志学 |
| 动作空间巨大 | 从 1 亿物品里选 10 个 = 天文数字的组合动作 |
| 奖励稀疏且延迟 | 「三个月后还留存」这个奖励信号要等三个月 |
工业界实际在用的形态(务实版)
- YouTube 的 Top-K REINFORCE(2019):策略梯度 + 重要性采样纠偏,用于召回。是最著名的成功案例
- 奖励塑形:不等三个月,用「会话深度」「次日回访」这类中期代理指标当奖励
- 保守离线 RL:限制新策略不能离旧策略(日志策略)太远(类似 PPO 的约束思想)
- RLHF/DPO 进推荐(2025+):生成式推荐(第 14 节的 OneRec)直接借用 LLM 的偏好对齐技术——把「用户真实选了 A 没选 B」当偏好对,做 DPO
🔑 给初学者的定位:RL 推荐是「效果上限最高、落地难度也最高」的方向。 面试中能讲清「为什么点击率模型短视 + 离线 RL 为什么难」就已经超过大多数人。
专题六(彩蛋):LLM 特征工厂 —— 2026 年性价比之王
第 14 节提过「LLM 四种用法里最实用的是当特征工厂」。这里给出能直接抄的操作流程:
为什么它性价比高: - 离线计算,不影响线上延迟;每个物品只算一次 - 直接命中推荐系统最大的痛点之一:物品冷启动(第 13 节) - 不用改任何架构,标签当普通类别特征喂进现有模型 - 比人工运营打标签便宜几个数量级,且覆盖率 100%
三个实操注意: 1. 让 LLM 输出受控词表(在 prompt 里给出候选标签列表让它选),否则标签会发散到无法当特征用 2. 抽样人工审核(1%),LLM 会有系统性的偏差(如把所有东西都标「高性价比」) 3. 用 Embedding 时注意降维(768 维太大,PCA/SVD 到 64 维再入模型)
🔗 这一章连到哪里
| 去哪 | 为什么 |
|---|---|
| 强化学习 14 | 专题五的展开版:离线 RL 为什么难、动作空间怎么压、什么时候真的该上 RL |
| 上线之后 13 | 专题四的完整版:倾向得分怎么估、SNIPS / Doubly Robust 为什么能压住方差 |
| 上线之后 18 | 专题三的落地账:蒸馏在降本清单里排最后一位——先 profile、缓存、请求分级,那三步就能拿 70% 收益 |
✅ 检查点
- LightGCN 相比标准 GCN 删掉了什么?为什么删掉反而更好?
- Swing 比 ItemCF 强在哪?「秋千结构」指什么?
- 为什么蒸馏时学「教师的分数」比学「真实标签」信息量大?
- IPS 的直觉是什么?它的致命弱点和缓解方法?
- 为什么推荐里的强化学习只能用离线 RL?
- LLM 特征工厂为什么是「性价比之王」?(说出三个理由)
👀 答案
1. 删掉了特征变换矩阵 W 和非线性激活,只留邻居平均。因为推荐输入只有 ID Embedding,没有丰富节点特征,复杂变换只会过拟合。 2. Swing 检查「同时点过 i、j 的用户对」之间的重合度:来自不同圈子的用户对是强证据,同一小圈子的证据打折。u-i-v-j 四节点构成秋千形状。它对「碰巧被同一批人点过」的噪声更鲁棒。 3. 真实标签只有 0/1;教师的连续打分包含「负样本之间谁更好」的排序信息(暗知识)。 4. 每条样本按「被观测到的概率」的倒数加权,让冷门样本代表它背后没被观测的同类。弱点是倾向得分接近 0 时权重爆炸(方差大);缓解:截断、自归一化 SNIPS、Doubly Robust。 5. 在线试错会伤害真实用户,不能像游戏一样死一万次重来,只能从历史日志(旧策略产生的数据)学习。 6. 离线计算不碰线上延迟、直接解决物品冷启动、不改架构即可接入现有系统(外加:比人工标注便宜且全覆盖)。🛑 可以停在这里
⚡ 走神救援
五个进阶专题:LightGCN(图视角,多跳协同信号,只做邻居平均)、Swing(用户对重合度打折,鲁棒 i2i)、蒸馏(学教师的连续打分=暗知识,listwise 蒸馏最适合粗排)、去偏(IPS 逆倾向加权,弱点方差爆炸,要截断)、RL(优化长期价值,只能离线 RL,YouTube Top-K REINFORCE 是标杆)+ 彩蛋 LLM 特征工厂(离线打标签,治冷启动,性价比之王)。⭐ 每个专题一句话定位,记这个就够:LightGCN —— 学术界最强 CF baseline,工业界只当召回的其中一路,数据极稀疏或有社交关系才值得上;行为丰富时 SASRec/双塔更划算,物品天天大换血就别碰(图要频繁重建)。Swing —— 「买了又买 / 看了又看」的主力 i2i,⚠️ 复杂度 O(物品对 × 用户对) 全量算不动,工业实现必须对用户采样 + 分布式。蒸馏 —— 教师的连续打分带的是暗知识(「这个负样本其实有点像正的」),真实标签只有 0/1;粗排用 listwise 蒸馏对齐精排的序,比对齐分数更管用。IPS —— 评估(Off-Policy Evaluation)里比训练里更常用;中小团队优先做位置偏差建模,收益大成本低。RL —— 效果上限最高、落地难度也最高,⚠️ 三座大山是不能在线试错(只能离线 RL)、动作空间天文数字、奖励稀疏且延迟;务实解法是用「会话深度、次日回访」这类中期代理指标当奖励。⚠️ LLM 特征工厂有三个实操细节别漏:让 LLM 在给定候选词表里选标签(不然标签发散到没法当特征)、抽 1% 人工审核(LLM 有系统性偏差,比如什么都标「高性价比」)、Embedding 先 PCA/SVD 降到 64 维再入模型。
下一步 👉 挑战项目:19-挑战项目A-新闻推荐-时效性生死时速.md