第一部分:物品推荐是二分图,社交推荐不是。

2026/08/05

#recommendation-systems #social-graphs #machine-learning

到现在,搭一个推荐系统已经有一套很成熟的标准打法:给物品做嵌入,给用户做嵌入,用点积打分(或者是建在同一套共享嵌入之上的、更复杂的交互架构,这不影响后面的论证),再在上面叠一层序列模型来捕捉口味随时间的漂移。这套打法在商品和广告上效果出奇地好。所以,当候选不再是物品、而是人的时候–好友推荐、关注推荐、“你可能认识的人"那一栏–很自然就会想沿用同一套打法。

然后它就持续表现不佳,而且出问题的方式,和那些常被归咎的原因对不上。

你最常听到的两个解释,一个是规模–十亿人的候选池当然比一百万件商品难搞–另一个是把人压缩成语义 ID 或聚类的代价。这两个都是真问题。尤其是规模,我在它上面花过不少时间,确实残酷;在我处理的图上,平均每个节点的度数大约是 500,这和大多数图学习论文做实验用的、度数 5 到 10 的引文图完全是两个世界。但规模也好、压缩也好,都不是这套打法表现不佳的根本原因。你把这两个都解决了,嵌入照样欠拟合,大多数团队都经历过。

根本原因是结构性的,而且几乎没人点破它:物品推荐跑在一张二分图上,社交推荐不是。其他一切,高秩、序列模型失效、手工图特征那种奇怪的经久不衰,都从这一个差异里掉出来。

两张看似相同的图

用户-物品交互图是二分图。它的节点分成两个不相交的集合–用户和物品–边只在两个集合之间连:用户连到物品,从不连到另一个用户,物品也从不连到另一个物品。这太显而易见,反而容易让人忽略它带来的好处。二分结构把每个节点的角色固定死了:用户永远是观看的那一方,物品永远是被观看的那一方。不存在某个节点"有时是用户、有时是物品”,于是点积两端该放谁,没有任何歧义。

社交图是单分图。它只有一类节点,人,边在这一类内部连。同一个人,在一条边里是查看者,在另一条边里就成了候选。角色不是节点的属性,而是边的属性。

物品推荐是二分图,社交推荐不是。

这不是记号上的咬文嚼字。它正是"给每个人一个静态嵌入"这件事从根上就是错的对象的原因。

对比一下,以及拓扑为什么这么要紧:

物品推荐 — 二分图              社交推荐 — 单分图

  u1 ---- i1                      p1 ---- p2
  |       |                       |       |
  u2 ---- i2                      p3 ---- p4

  两类节点                        一类节点
  角色由所在侧固定                角色逐边而定
物品推荐 社交推荐
拓扑 二分图(两类节点) 单分图(一类)
角色 由划分固定 逐边(一个节点在一条边里是查看者,在另一条里是候选)
自然模型 两侧各一个因子 一个因子得同时装下口味和吸引力
稳定信号 物品内容(类型、品牌) 只有结构(怎么连接)
有效秩
序列模型 够用 欠拟合

同一个人,在每个人眼里都不是同一个人

在物品推荐里,给每件物品一个向量是合理的,因为物品有一个相对稳定的含义:一部手机对所有人来说都是一部手机,只是每个人想要它的程度略有不同。物品的"吸引力"近似是一个全局属性,用户的"口味"也近似是一个全局属性,分数就是这两者的内积。这种干净的分离,每一边一个因子,正是矩阵分解所假设的,而它之所以成立,是因为拓扑结构白送给你:两边本来就是不同的类型。

社交图里只有一种类型。一个人同时拥有一个口味(他倾向于去联系谁)和一个吸引力(谁倾向于来联系他),而没有任何划分能把这两者分开。[1] 更糟的是,吸引力本身还依赖于查看者。对甲我是个老同学,对乙我是个前上司,对丙我是个竞争对手。这可不是某个"真实向量"周围的小扰动,它们是近乎正交的不同角色,任何单一嵌入都不得不把它们全平均掉。

这才是真正破坏这套打法的地方。候选嵌入,在物品推荐里是一个名词,对物品是什么的稳定描述,在社交推荐里却必须变成一个动词:一个关于"谁在看"的函数。

同一个人,在每个人眼里都不是同一个人。

不用任何数学,你也能感觉到这个问题。但它有一个精确的数学表述,而正是这个表述,能告诉你哪些修复有用、哪些只是自欺欺人。

低秩这一招为什么失灵

矩阵分解、共享嵌入、基于注意力的序列模型,共享同一个假设:交互结构是低秩的。具体来说,如果你把用户–物品交互矩阵写成 \(R\) 并看它的奇异值,它们下降得很快,于是用一个小的 \(d\) 做秩-\(d\) 近似 \(R \approx UV^\top\) 就能抓住大部分信号。这正是为什么 64 或 128 维的嵌入就够覆盖百万级物品目录,也是这一切在计算上可行的全部理由。

“低秩"不是玄学,它有具体含义:对任意一件物品的偏好,可以近似地重建为对一小撮"基物品"偏好的线性组合。这对物品成立,是因为物品有可被发现、且被许多用户共同认可的潜在维度,类型、价格、品牌。这个基很小,而且是共享的。

社交相关性没有这样一个小而共享的基。候选 \(c\) 对查看者 \(v\) 的相关性,取决于他俩之间的子图,共享了哪些共同好友、重叠在哪些社群、连上之后会闭合哪个三角,而这个子图对每个查看者都是不同的函数。把所有查看者的"查看者–候选相关性"叠起来,你得到的不是一个奇异值快速下降的矩阵,而是一个谱相对平坦的矩阵。用大家惯用的说法,它的有效秩很高。[2]

究竟高到什么程度、怎么在不被稀疏性或流行度偏斜骗到的前提下去量它,是个值得单独展开的话题,我后面会回来讲。这里只说结构层面的结论:把相关性做成"以查看者为条件”,这件事本身就把它推向高秩,这是构造使然,不是噪声。

这里有一个我一直反复回到的实际后果:如果你在不断增加嵌入维度 \(d\) 的同时训练模型,在物品数据集上,留出集指标很快饱和,你已经抓住了低秩结构,再加维度买不到任何东西;而在社交数据集上,用同一个模型,这条曲线还在爬,或者在你能负担得起的任何预算内都不饱和。信号就在那里,只是它装不进一个低秩的盒子里。

为什么堆注意力也无济于事

对“低秩不够”的自然反应是“那就上更大的模型,序列模型、Transformer”。这是物品推荐领域已经走的一步,而且在那个场景下是好棋。但它在社交推荐里逃不开秩的问题,因为序列模型在两个不同的地方都把低秩焊死了,加层都解不开。

第一处是嵌入本身。给每件物品在共享的 \(d\) 维空间里一个向量,这本身就等于断言:物品之间的相似性结构活在 \(d\) 维里,即相似性矩阵的秩不超过 \(d\)。把一个嵌入在所有查看者之间共享,只有在物品含义不依赖查看者时才是无害的,而这恰恰就是低秩的情形。

第二处更尖锐:注意力本身就是一个低秩算子。在单个注意力头里,那个 \(n \times n\) 的打分矩阵是 \(QK^\top\),其中 \(Q\)、\(K\) 被投影到头维度 \(d_h\)。所以

$$\operatorname{rank}(QK^\top) \le d_h,$$

无论模型多深多宽都成立。[3] 叠层抬不动这个天花板,只有头维度能抬,而且最多抬到 \(d_h\)。多头注意力是若干个秩-\(d_h\) 算子的并联,不是一个高秩算子。

所以,机械地说,序列模型就是一个作用在关系结构上的秩-\(d_h\) 投影器。在物品推荐里这没问题:真实结构装得进 \(d_h\) 维,投影是无损的,模型就能把它恢复出来,这正是序列模型架构在那里能赢的原因。在社交推荐里,真实结构需要的秩远高于 \(d_h\),谱又平到每个被抓住的成分都解释不了多少,于是投影器几乎返回不了任何可迁移的东西。更多的层、更多的参数,修不好一个秩天花板;它们只会让你更靠近一个你本来就在它下面的天花板。

我也怀疑,这正是生成式推荐文献在试图把异质的社交信号映射进单一共享向量空间时、反复报告不稳定的原因:模型在试图用低秩瓶颈去表达高秩结构,优化器找不到合适的地方安放那个残差。

但物品推荐不也是"超越双线性"的吗?

对上面这一切,有一个合理的反驳,我想直接摆出来,因为这是我最常收到的一个。物品推荐也是超越双线性的,毕竟,整个领域从纯矩阵分解走向序列模型,原因就在于此。矩阵分解是双线性的、不够用;HSTU 在其之上加了能力。所以"超越双线性"其实并不能把社交和物品区分开,真正的差别也许只是规模和度数。

这个反驳有一半是对的,我不想绕开它。HSTU 确实是真正超越了静态矩阵分解:它把用户状态做成了交互序列的函数,这是固定用户向量表达不了的。这是真实的能力,跑在一个真实的轴上。

错在把"超越双线性"当成一个轴。它是两个,而且彼此正交。

第一个轴是时序与序列:用户状态是一个固定向量,还是他做过的事情的序列的函数。矩阵分解在这个轴上为零,序列模型为一。HSTU 活在这个轴上,而物品推荐真正受限的也正是这个轴,所以那次的增益又大又实。

第二个轴是高阶与关系:一对节点之间的相关性,是只取决于这两个端点,还是取决于它们之间的图结构,共同好友、路径、三角。在这个轴上,注意力是用共享低秩嵌入上的内积、把候选对历史打分,这是一个秩-\(d_h\) 的双线性算子。它学到的是软的、匿名的共现(这个候选倾向于和这些历史项一起出现);它学不到"是某个具体的第三个人在居间促成这一对"。序列模型在这个轴上同样为零。它在第一个轴上超越了双线性,在第二个轴上仍是双线性。

社交推荐是在第二个轴上超越双线性的,而对它来说,重要的恰恰是这一个。好友推荐里最强的信号是三元闭合,朋友的朋友,这是某个具体的、其身份至关重要的第三方。二分图根本容不下一个三角,这就是为什么物品推荐的"超越双线性"必然被限制在时序轴上、一个序列模型在那里就够用了;单分图则是由三角构成的,这就是为什么社交推荐逼出了关系轴,而序列模型在那里不够用。

有一个经验上的迹象,能说明这两个轴确实不同。物品推荐的大幅提升,来自"矩阵分解 → 序列模型"的切换,时序轴;社交推荐的大幅提升,不是来自序列模型,而是来自手工的图特征,像 Adamic-Adar、个性化 PageRank、共同邻居数,它们直接攻击的是关系轴。生产环境的"你可能认识的人"系统至今仍在手工计算这些特征、再喂给排序器。如果"超越双线性"是同一个轴,序列模型早该让这些特征变成多余的了。它们没有,因为结构性信号不是注意力能"发现"的东西,而是你必须算出来、亲手交给它的东西。

A 轴,时序/序列 B 轴,高阶关系
问的是 用户状态是不是交互序列的函数? 相关性是否取决于这对节点之间的第三方图结构?
矩阵分解
序列模型(HSTU) 否,仍是秩 ≤ d_h 的双线性算子
社交推荐需要 有帮助 就是这个,而注意力够不到

这改变了什么

如果问题是结构性的–单分图拓扑、高有效秩、一个注意力够不到的关系轴–那么最常被尝试的那些修复就都是错的。更大的嵌入表没用,因为信号装不进一个更大的低秩盒子,它要装进的是一个形状不同的盒子;更多的注意力层没用,因为它们都顶在同一个秩天花板下面;更花哨的游走式图分词器也没用,至少在社交图的度数下没用–当我在度数 500 的图上跑 12 跳的、node2vec 风格的游走时,回来的几乎是直链,因为在一个有 500 个邻居的节点上,一次游走会回头的概率是 \((499/500)^{12} \approx 97.6\%\),每 100 条采到的结构里有 97、98 条都是同一条平凡路径。

真正有用的,我认为是把"以查看者为条件"当成一个一等公民来认真对待,而不是把它平均掉。如果同一个候选必须对每个查看者都是不同的向量,那么候选嵌入就只该在它真正稳定的部分保持与查看者无关,它的结构指纹、它连接的方式,而那个依赖于查看者的部分,应该住在一个"为每个查看者重新投影这个指纹"的算子里。在十亿人的规模上做这件事,工程问题就变成:如何在不为每个查看者存一个矩阵的前提下,物化出一个逐查看者的算子。而答案,长得很像模型适配里那个低秩残差的技巧,只是从"低成本地适配一个模型"挪到了"低成本地让候选变得依赖查看者"。

那是另一篇的事。这篇想说的更窄、但也更有用:在争论该用哪个模型之前,值得先注意到,物品推荐和社交推荐并不是同一个问题穿了不同的衣服。它们是两个不同的问题,只是恰好共享了一套词汇,而这套词汇,嵌入、注意力、序列模型,是从它管用的那个问题里借来的。再多的、借来的机器,也修不好一个你连结构都还没说清的问题。把结构说清楚,才是大部分的工作。


注释

[1] 人对人的推荐里 taste 与 attractiveness 的区分,可以追溯到 2010 年前后的对称协同过滤工作,那批工作指出:当"物品"也是用户时,协同过滤里"主动用户 / 被动物品"的标准假设是不成立的。

[2] 有效秩通常用归一化奇异值谱的熵、或稳定秩 \(\|R\|_F^2 / \|R\|_2^2\) 之类来量,而不是用代数秩,一个稀疏的 0-1 矩阵无论结构如何,代数秩都平凡地接近满秩,所以代数秩在这里给不了任何有用信息。

[3] 这是 Bhojanapalli 等人 “Low-Rank Bottleneck in Multi-Head Attention Models”(ICML 2020)的定理 1:头维度给一个注意力单元能表达的东西设了一道硬性的低秩瓶颈,深度和宽度都抬不动它。