<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
	<channel>
		<title>首页 on Jiahao Wu</title>
		<link>https://www.jiahaowu.com/zh/</link>
		<description>Recent content in 首页 on Jiahao Wu</description>
		<generator>Hugo</generator>
		<language>zh</language>
		
		
		
		
			<lastBuildDate>Mon, 10 Aug 2026 00:00:00 +0000</lastBuildDate>
		
			<atom:link href="https://www.jiahaowu.com/zh/index.xml" rel="self" type="application/rss+xml" />
			<item>
				<title>从三个整数到排序器</title>
				<link>https://www.jiahaowu.com/zh/graph-motif-generation/</link>
				<pubDate>Mon, 10 Aug 2026 00:00:00 +0000</pubDate>
				<guid>https://www.jiahaowu.com/zh/graph-motif-generation/</guid>
				<description>&lt;p&gt;很多团队把 motif 当成又一个特征。跑一个图作业，拿到一堆三角形，像「共同好友数」那样挂到排序器上。这个做法把整个问题藏起来了。一条三角形从流水线里出来，是三个整数 node id，神经网络的排序器吃不了三个整数 id。从这三个整数到一个真正能给「人对人」打分的模型，中间隔着一层，而这层不是格式问题。真正的问题就住在这里，也就是我前面几篇一直在讲的那个：人不是 item，所以一个人的表示必须取决于谁在看。&lt;/p&gt;&#xA;&lt;p&gt;三角形不是 embedding。&lt;/p&gt;&#xA;&lt;p&gt;这篇是前面三篇的工程篇。前面我论证社交推荐之所以套不上 item 推荐那套，是因为「查看者-候选」矩阵是高秩的、是 viewer-conditioned 的，老实的修法是给一个 per-viewer 的投影 &lt;code&gt;W_v&lt;/code&gt;，作用在一个结构指纹 &lt;code&gt;E_c&lt;/code&gt; 上。这篇讲的是，在这套理论碰到线上系统之前，有两个不起眼的问题必须先解决，而它们都比理论本身更有意思。&lt;/p&gt;&#xA;&lt;p&gt;这是两个问题，大家经常混成一个。第一，图有十亿节点、有度 500 的大 V，三角形到底怎么算出来。第二，三角形算出来之后，ranker 要的是向量，一个三角形怎么变成向量，「喂给排序器」这件事排序器并不支持。我按顺序讲，然后说明第二个问题恰好就是前面那几篇里 per-viewer 投影的具体落地。&lt;/p&gt;&#xA;&lt;h2 id=&#34;把三角形从一张放不进单机的图里算出来&#34;&gt;把三角形从一张放不进单机的图里算出来&lt;/h2&gt;&#xA;&lt;p&gt;输入是边表，每一行是一条 follow。任务是找出每一个三角形（每一组互相连接的三个人），交给下游。朴素的算法是：对每个点，看它每一对邻居，查这对邻居之间有没有边。度 500 的点，单点就要 &lt;code&gt;C(500, 2) ≈ 12.5 万&lt;/code&gt;次检查，图里只要有几个这种 hub，时间几乎全花在它们身上。更糟的是每个三角形会被数 6 次。&lt;/p&gt;&#xA;&lt;p&gt;标准的修法是 degree ordering，年纪比在读这篇文章的大多数人都大。把所有点按度排序（度相同按 id），每条边重新定向，从低度端点指向高度端点。图就变成了一个有向无环图，因为每条边都严格指向「序更高」的点。然后只在每个三角形的最低度顶点上做枚举：对一个点 &lt;code&gt;v&lt;/code&gt;，看它出边邻居的两两组合，查这对之间是不是边。每个三角形只被找到一次（全序有唯一最小元），工作量从大约 &lt;code&gt;O(m·Δ)&lt;/code&gt;（&lt;code&gt;Δ&lt;/code&gt; 是最大度）掉到幂律图上的 &lt;code&gt;O(m^1.5)&lt;/code&gt;。这就是 NodeIterator++，界是 Suri 和 Vassilvitskii 给的。hub 永远不是 pivot，只有度小的点做枚举，它们便宜。把朴素算法打爆的星图，现在几乎不干活，因为中心是度最高的点，永远轮不到它当 pivot。&lt;/p&gt;&#xA;&lt;p&gt;到这里都是单机。分布式那个坑是我自己踩的，值得讲清楚，因为大部分文章里没写。&lt;/p&gt;&#xA;&lt;p&gt;Spark 上的自然做法是把定向后的边 hash 分区，在每个分区内做枚举。你会听到一个说法：「vertex-cut，复制因子降到 2」。这个说法讲的是存储，不是三角形的局部性，两者的区别很要紧。一个三角形 &lt;code&gt;{v, u, w}&lt;/code&gt;（&lt;code&gt;v&lt;/code&gt; 是最低度的，是 pivot），三条定向边是 &lt;code&gt;v→u&lt;/code&gt;、&lt;code&gt;v→w&lt;/code&gt;、&lt;code&gt;u→w&lt;/code&gt;。如果你按 source hash 每条边，&lt;code&gt;v→u&lt;/code&gt; 和 &lt;code&gt;v→w&lt;/code&gt; 都落到拥有 &lt;code&gt;v&lt;/code&gt; 的那个分区（因为它们的 source 都是 &lt;code&gt;v&lt;/code&gt;），很好。但闭合边 &lt;code&gt;u→w&lt;/code&gt; 的 source 是 &lt;code&gt;u&lt;/code&gt;，落到拥有 &lt;code&gt;u&lt;/code&gt; 的分区，几乎必然是另一台机器。pivot 的分区有两条腿，能看到 &lt;code&gt;u&lt;/code&gt; 和 &lt;code&gt;w&lt;/code&gt; 都是 &lt;code&gt;v&lt;/code&gt; 的邻居，但确认不了 &lt;code&gt;u&lt;/code&gt; 和 &lt;code&gt;w&lt;/code&gt; 之间有边，因为那条边在别处。这个三角形对唯一有权发现它的分区来说是不可见的。&lt;/p&gt;</description>
			</item>
			<item>
				<title>以查看者为条件的社交排序</title>
				<link>https://www.jiahaowu.com/zh/viewer-conditioned-social-ranking/</link>
				<pubDate>Sun, 09 Aug 2026 00:00:00 +0000</pubDate>
				<guid>https://www.jiahaowu.com/zh/viewer-conditioned-social-ranking/</guid>
				<description>&lt;p&gt;这是关于&amp;quot;社交推荐为什么在物品推荐那套打法上持续表现不佳、以及该怎么办&amp;quot;的三部曲系列的一个简短索引。整个系列归结起来就一个想法，分三篇文章。这一页就是这个想法，附上链接。&lt;/p&gt;&#xA;&lt;h2 id=&#34;核心想法&#34;&gt;核心想法&lt;/h2&gt;&#xA;&lt;p&gt;物品推荐跑在一张二分图上，用户一边、物品另一边，这个拓扑把每个节点的角色固定死了。社交推荐跑在一张单分图上，同一个人在一条边里是查看者、在另一条边里就成了候选。就这一个差异，是你把物品推荐那套机器（共享嵌入、注意力、序列模型）搬到人身上时、大部分问题的根本原因：它把&amp;quot;查看者–候选&amp;quot;的相关性矩阵推向高有效秩，而那套机器底下那些低秩算子表达不了高秩；它也把最强的信号、三元闭合，放进了一个二分拓扑根本容不下的 motif 里。这个系列给出的回应是：别再给每个候选一个固定向量，而是把比较里查看者那一侧，从查看者自身的结构生成出来，一个逐查看者的投影算子；它并不会把秩天花板抬高，而真正的问题，是怎么在十亿用户的规模上做到这件事、又不让成本爆炸。&lt;/p&gt;&#xA;&lt;h2 id=&#34;三篇文章&#34;&gt;三篇文章&lt;/h2&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&lt;strong&gt;&lt;a href=&#34;https://www.jiahaowu.com/zh/social-rec-is-not-item-rec/&#34;&gt;第一部分：物品推荐是二分图，社交推荐不是。&lt;/a&gt;&lt;/strong&gt;，诊断。拓扑差异为什么破坏了那套打法，为什么堆注意力也无济于事（注意力是一个秩受限的双线性算子），以及对那个最常见反驳的两轴防御。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;&lt;a href=&#34;https://www.jiahaowu.com/zh/beyond-low-rank/&#34;&gt;第二部分：如何判断你的推荐问题是低秩的&lt;/a&gt;&lt;/strong&gt;，严谨。怎么在不骗到自己的前提下量秩（代数秩没用；两个混淆因素是稀疏性和流行度；只有一张图能一锤定音），以及为什么就算证明了&amp;quot;高秩&amp;quot;，你仍然站在一个对这个问题来说太小的框架里。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;&lt;a href=&#34;https://www.jiahaowu.com/zh/viewer-conditioned-projection/&#34;&gt;第三部分：十亿规模下的查看者条件投影&lt;/a&gt;&lt;/strong&gt;，实现。作为结构指纹的候选嵌入、作为低秩残差的逐查看者算子、生成它的超网络、让它免费上线的查询侧技巧，外加诚实的创新定位和对双向注意力的反驳。&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h2 id=&#34;贯穿全篇的几个想法&#34;&gt;贯穿全篇的几个想法&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;候选嵌入在物品推荐里是个名词，在社交推荐里它得是个动词。&lt;/li&gt;&#xA;&lt;li&gt;同一个人，在每个人眼里都不是同一个人。&lt;/li&gt;&#xA;&lt;li&gt;注意力是混合结构，不是发现结构。你没法靠再加一个注意力头去抬一个秩天花板。&lt;/li&gt;&#xA;&lt;li&gt;社交推荐里最强的信号，三元闭合，活在一个二分图根本容不下的 motif 里。&lt;/li&gt;&#xA;&lt;li&gt;以查看者为条件的投影是一块中间楼层，不是终点。它替代不了图特征。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;止于何处&#34;&gt;止于何处&lt;/h2&gt;&#xA;&lt;p&gt;诚实的论断是窄的。逐查看者投影不是个新机制，它可追溯到 2015 年的 McAuley 等人，它也不是一条绕过秩天花板的路；单分拓扑逼出来的是不对称性，逐查看者投影是拿到它的一种办法，而查询侧的分解，是让这种办法在规模上负担得起的原因。剩下的是证据：在一张真实图上跑那条&amp;quot;指标对维度&amp;quot;的曲线，以及那个&amp;quot;在注意力上面再加 Adamic-Adar&amp;quot;的消融。那部分是一个实验，而实验，和论证不一样，是好读的。&lt;/p&gt;</description>
			</item>
			<item>
				<title>第三部分：十亿规模下的查看者条件投影</title>
				<link>https://www.jiahaowu.com/zh/viewer-conditioned-projection/</link>
				<pubDate>Sat, 08 Aug 2026 00:00:00 +0000</pubDate>
				<guid>https://www.jiahaowu.com/zh/viewer-conditioned-projection/</guid>
				<description>&lt;p&gt;头两篇给出了诊断，并检验了它。物品推荐跑在二分图上，社交推荐跑在单分图上，这个差异把&amp;quot;查看者–候选&amp;quot;的相关性矩阵推向高有效秩，这正是为什么&amp;quot;共享嵌入加注意力&amp;quot;那套打法会欠拟合、而且无论你在上面叠多少层都照样欠拟合。如果你认真对待这个诊断，修复办法就是：别再给每个候选一个固定向量，而是让它的表示依赖于谁在看，把候选嵌入从一个名词变成一个动词。&lt;/p&gt;&#xA;&lt;p&gt;这话说起来容易，乍一看却做不到。如果每个查看者都要有自己看待每个候选的方式，最直接的实现就是一个逐查看者的投影矩阵 \(W_v\)，而在十亿查看者上，这是约 64TB 的参数，而且是在你还没训练任何一个之前。所以真正的问题不是&amp;quot;要不要把嵌入做成以查看者为条件&amp;quot;，而是&amp;quot;你能不能在不让成本爆炸的前提下做到&amp;quot;。这篇就是答案，分三部分：一个刻意做成&amp;quot;与查看者无关&amp;quot;的候选嵌入、一个刻意做成低秩的逐查看者算子、还有一个把整个算子挪到查询侧的上线技巧，让候选侧永远不必知道它的存在。&lt;/p&gt;&#xA;&lt;h2 id=&#34;分工&#34;&gt;分工&lt;/h2&gt;&#xA;&lt;p&gt;把查看者 \(v\) 下、候选 \(c\) 的分数写成&lt;/p&gt;&#xA;$$\text{score}(v,c) = \langle a_v,\, W_v E_c \rangle,$$&lt;p&gt;三个部件。\(E_c\) 是候选的嵌入，它与查看者无关，只装那些对 \(c\) 稳定成立的东西。\(W_v\) 是一个 \(d \times d\) 的算子，属于查看者，所有&amp;quot;依赖查看者&amp;quot;的东西都住在这里。\(a_v\) 是一个小的、学出来的查询头。这么写的要点是分离：所有随查看者改变的东西进 \(W_v\)，所有只属于候选的东西进 \(E_c\)。这正是第一篇里&amp;quot;二分图起源&amp;quot;那个论证所要求的。在单分图里，一个节点唯一稳定的东西是它的结构；对查看者的依赖必须被分解进一个算子，而不是被平均进一个共享向量。&lt;/p&gt;&#xA;&lt;p&gt;候选嵌入是个名词，在社交推荐里它得是个动词。这个动词就是 \(W_v\)。&lt;/p&gt;&#xA;&lt;h2 id=&#34;候选嵌入到底是什么&#34;&gt;候选嵌入到底是什么&lt;/h2&gt;&#xA;&lt;p&gt;如果 \(E_c\) 要做成与查看者无关，那自然的问题是：一个人身上有什么东西，是可以不依赖于&amp;quot;谁在问&amp;quot;而稳定成立的？答案是唯一一样人身上有、而物品没有（至少没有更丰富形式）的东西：他连接的方式。一个人没有物品那种稳定的内容语义，没有类型、品牌、价格可以绑，所以嵌入里&amp;quot;与查看者无关&amp;quot;的那部分，必须是一个结构指纹。度数、三角数、他所在的那个或那些社群、他周围小 motif 的分布、个性化 PageRank 把他送到哪里的一张快照。具体地说，这才是&amp;quot;嵌入编码的是边、而不是兴趣&amp;quot;的意思。&lt;/p&gt;&#xA;&lt;p&gt;构造这个指纹有几种办法，成本和保真度从低到高。最便宜的是一个朴素的可学习 ID，每个人一个向量、随机初始化、端到端训练，它让训练信号隐式地把结构发现出来，而且因为它就是一张表，扩到十亿毫无压力。再丰富一点，是显式地算结构特征（角色发现那一脉的做法）再投影下来；再丰富，是在邻域上跑一个一两跳的浅图网络；最忠实的是一个子结构分词器，但我不会在这儿用它，因为正如第一篇所说，它在真实图的度数下会塌成直链。&lt;/p&gt;&#xA;&lt;p&gt;我真正会从之起步的，是最便宜的那个、随机初始化的 ID，理由在实验上：它把 \(W_v\) 的贡献孤立出来了。如果整个模型里唯一的、感知查看者的部件就是那个算子，那你测到的任何提升都是算子的提升、不是指纹的提升，之后你再把结构特征消融进来，看它还能加多少。&lt;/p&gt;&#xA;&lt;p&gt;有一个正确性约束值得明说，因为它是那种看起来像细节、却会悄悄毁掉整个模型的东西。候选嵌入不能是&amp;quot;随机的、且冻结的&amp;quot;，它必须是&amp;quot;随机的、但训练的&amp;quot;。原因在结构上：稍后会看到，上线技巧会把 \(W_v\) 挪到查询侧，这意味着逐查看者的查询不再依赖 \(c\)，而这意味着关于 \(c\) 的每一点信息都必须住在 \(E_c\) 里。一张冻结的随机表不带任何关于候选的信息，建在它上面的模型什么也学不到。得允许训练信号把随机初始化推成某种编码了候选稳定身份的东西。顺带，\(W_v\) 必须只是 \(v\) 的函数、绝不是 \(c\) 的，不是因为时髦，而是因为把候选信息放进查看者侧的算子，会破坏让整件事负担得起的那个上线模式。&lt;/p&gt;&#xA;&lt;p&gt;构造这个指纹的四种办法，从便宜到贵：&lt;/p&gt;&#xA;&lt;table&gt;&#xA;&#x9;&lt;thead&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;构造法&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;成本&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;保真度&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;能扩？&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&lt;/thead&gt;&#xA;&#x9;&lt;tbody&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;可学习 ID（随机初始化、训练）&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;最低&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;隐式&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;能，就是一张表&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;结构特征 → MLP（RolX 那一脉）&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;低&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;显式&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;能（可预计算）&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;浅 GNN（1–2 跳）&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;中&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;好&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;要图基础设施&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;子结构分词器（G2PM）&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;高&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;最高&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;不能，度数 500 下塌成直链&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;保持低秩的算子&#34;&gt;保持低秩的算子&lt;/h2&gt;&#xA;&lt;p&gt;贵的那个对象是 \(W_v\)，而让它变得可行的办法，是永远不去构造那个完整的 \(d \times d\) 矩阵。把它分解成一个低秩残差，&lt;/p&gt;</description>
			</item>
			<item>
				<title>第二部分：如何判断你的推荐问题是低秩的</title>
				<link>https://www.jiahaowu.com/zh/beyond-low-rank/</link>
				<pubDate>Fri, 07 Aug 2026 00:00:00 +0000</pubDate>
				<guid>https://www.jiahaowu.com/zh/beyond-low-rank/</guid>
				<description>&lt;p&gt;上一篇里我论证过：社交推荐之所以在物品推荐的那套打法上持续表现不佳，有一个结构性的原因，物品交互构成的是二分图，社交交互构成的是单分图，而这个差异会把&amp;quot;查看者–候选&amp;quot;的相关性矩阵推向高有效秩。这类论断在文章里听起来很漂亮，可一旦你想去量它，它就什么都不是了。所以在往上盖任何东西之前，我想先把它拆开：到底该怎么去检验一个推荐问题是低秩的？在这个过程中你会怎么把自己骗了？以及，一旦检验完了，这个答案能把你带多远？&lt;/p&gt;&#xA;&lt;p&gt;简短说：人们为了&amp;quot;证明自己的图是低秩/高秩&amp;quot;而做的几乎所有事情,要么没意义,要么误导人;真正能一锤定音的图只有一张;而即便定完了音,你仍然站在一个对这个问题来说太小了的框架里。我按顺序讲。&lt;/p&gt;&#xA;&lt;h2 id=&#34;代数秩帮不上忙&#34;&gt;代数秩帮不上忙&lt;/h2&gt;&#xA;&lt;p&gt;首先得把一件事赶走：交互矩阵普通的代数秩，在这里什么也告诉你不了。人们去抓它,是因为它听起来很严谨&amp;ndash;&amp;quot;\(R\) 的秩&amp;quot;&amp;ndash;但一个稀疏的 0-1 矩阵,无论它含有什么结构,都以压倒性的概率接近满代数秩。一个十亿节点、几千亿边的社交图,按代数秩是&amp;quot;满秩&amp;quot;的,把它打乱、把所有社群结构都毁掉之后,它还是&amp;quot;满秩&amp;quot;。如果满代数秩同时和&amp;quot;丰富的潜在结构&amp;quot;以及&amp;quot;纯噪声&amp;quot;都相容,那它就区分不了这二者,作为诊断工具它毫无用处。&lt;/p&gt;&#xA;&lt;p&gt;你要的是那些给奇异值加权的度量,因为整个问题就是它们掉得有多快。有几个，彼此一致远大于分歧：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;截断 SVD 重建曲线&lt;/strong&gt;，给定一个目标比例 \(x\)，能解释矩阵 \(x\%\) 的 Frobenius 范数能量的最小秩 \(r\)。这是我会第一个去抓的，原因待会讲：它问的，恰好就是模型接下来要问的那个问题。矩阵分解&lt;em&gt;本身&lt;/em&gt;就是一个秩-\(r\) 近似，这条曲线告诉你，对每一个 \(r\)，有多少信号能活下来。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;稳定秩&lt;/strong&gt;，\(\|A\|_F^2 / \|A\|_2^2\)，总谱能量与最大奇异值之比。受代数秩约束，但对稀疏矩阵总有的那一长串微小奇异值很稳健。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;有效秩&lt;/strong&gt;，\(\exp(H(p))\)，其中 \(p_i = \sigma_i / \sum_j \sigma_j\)，\(H\) 是香农熵；大白话就是谱有多摊开。[1]&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;谱衰减斜率&lt;/strong&gt;，拟合 \(\log \sigma_i \approx -\alpha \log i\)；\(\alpha\) 陡就是低秩，平就是高秩。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;这些都合理。重建曲线才是要紧的那一条，因为它用的单位，和你接下来要对这个矩阵做的事是同一个。&lt;/p&gt;&#xA;&lt;p&gt;那些真正给谱加权的度量：&lt;/p&gt;&#xA;&lt;table&gt;&#xA;&#x9;&lt;thead&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;度量&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;是什么&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;注意&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&lt;/thead&gt;&#xA;&#x9;&lt;tbody&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;代数秩&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;非零奇异值个数&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;没用，稀疏的 0-1 矩阵无论结构如何都接近满秩&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;稳定秩&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;|A|²_F / |A|²_2&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;对尾部那一长串微小奇异值很稳健&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;有效秩&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;归一化 σ 上的 exp(H(p))&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;“谱有多摊开”&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;截断 SVD 重建&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;能解释 x% 质量的秩 r&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;&lt;strong&gt;唯一要紧的&lt;/strong&gt;，就是 MF 损失多少&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;谱衰减斜率&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;拟合 log σ_i ≈ −α log i&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;α 陡 = 低秩&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;两个陷阱&#34;&gt;两个陷阱&lt;/h2&gt;&#xA;&lt;p&gt;我见过的几乎所有“看，我们的图是高秩的”这类图，都错在这里。有两个混淆因素,随便哪一个都能在物品矩阵和社交矩阵之间凭空造出一个秩差,而它和&amp;quot;以查看者为条件&amp;quot;毫无关系。&lt;/p&gt;</description>
			</item>
			<item>
				<title>第一部分：物品推荐是二分图，社交推荐不是。</title>
				<link>https://www.jiahaowu.com/zh/social-rec-is-not-item-rec/</link>
				<pubDate>Wed, 05 Aug 2026 00:00:00 +0000</pubDate>
				<guid>https://www.jiahaowu.com/zh/social-rec-is-not-item-rec/</guid>
				<description>&lt;p&gt;到现在,搭一个推荐系统已经有一套很成熟的标准打法:给物品做嵌入,给用户做嵌入,用点积打分（或者是建在同一套共享嵌入之上的、更复杂的交互架构，这不影响后面的论证），再在上面叠一层序列模型来捕捉口味随时间的漂移。这套打法在商品和广告上效果出奇地好。所以,当候选不再是物品、而是人的时候&amp;ndash;好友推荐、关注推荐、&amp;ldquo;你可能认识的人&amp;quot;那一栏&amp;ndash;很自然就会想沿用同一套打法。&lt;/p&gt;&#xA;&lt;p&gt;然后它就持续表现不佳，而且出问题的方式，和那些常被归咎的原因对不上。&lt;/p&gt;&#xA;&lt;p&gt;你最常听到的两个解释,一个是规模&amp;ndash;十亿人的候选池当然比一百万件商品难搞&amp;ndash;另一个是把人压缩成语义 ID 或聚类的代价。这两个都是真问题。尤其是规模，我在它上面花过不少时间，确实残酷;在我处理的图上,平均每个节点的度数大约是 500,这和大多数图学习论文做实验用的、度数 5 到 10 的引文图完全是两个世界。但规模也好、压缩也好,都不是这套打法表现不佳的根本原因。你把这两个都解决了,嵌入照样欠拟合,大多数团队都经历过。&lt;/p&gt;&#xA;&lt;p&gt;根本原因是结构性的，而且几乎没人点破它：物品推荐跑在一张二分图上，社交推荐不是。其他一切，高秩、序列模型失效、手工图特征那种奇怪的经久不衰，都从这一个差异里掉出来。&lt;/p&gt;&#xA;&lt;h2 id=&#34;两张看似相同的图&#34;&gt;两张看似相同的图&lt;/h2&gt;&#xA;&lt;p&gt;用户-物品交互图是二分图。它的节点分成两个不相交的集合&amp;ndash;用户和物品&amp;ndash;边只在两个集合之间连:用户连到物品,从不连到另一个用户,物品也从不连到另一个物品。这太显而易见，反而容易让人忽略它带来的好处。二分结构把每个节点的角色固定死了:用户永远是观看的那一方,物品永远是被观看的那一方。不存在某个节点&amp;quot;有时是用户、有时是物品&amp;rdquo;,于是点积两端该放谁,没有任何歧义。&lt;/p&gt;&#xA;&lt;p&gt;社交图是单分图。它只有一类节点，人，边在这一类内部连。同一个人，在一条边里是查看者，在另一条边里就成了候选。角色不是节点的属性，而是边的属性。&lt;/p&gt;&#xA;&lt;p&gt;物品推荐是二分图，社交推荐不是。&lt;/p&gt;&#xA;&lt;p&gt;这不是记号上的咬文嚼字。它正是&amp;quot;给每个人一个静态嵌入&amp;quot;这件事从根上就是错的对象的原因。&lt;/p&gt;&#xA;&lt;p&gt;对比一下，以及拓扑为什么这么要紧：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;物品推荐 — 二分图              社交推荐 — 单分图&#xA;&#xA;  u1 ---- i1                      p1 ---- p2&#xA;  |       |                       |       |&#xA;  u2 ---- i2                      p3 ---- p4&#xA;&#xA;  两类节点                        一类节点&#xA;  角色由所在侧固定                角色逐边而定&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;table&gt;&#xA;&#x9;&lt;thead&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;物品推荐&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;社交推荐&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&lt;/thead&gt;&#xA;&#x9;&lt;tbody&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;拓扑&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;二分图（两类节点）&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;单分图（一类）&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;角色&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;由划分固定&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;逐边（一个节点在一条边里是查看者，在另一条里是候选）&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;自然模型&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;两侧各一个因子&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;一个因子得同时装下口味和吸引力&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;稳定信号&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;物品内容（类型、品牌）&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;只有结构（怎么连接）&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;有效秩&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;低&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;高&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;序列模型&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;够用&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;欠拟合&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;同一个人在每个人眼里都不是同一个人&#34;&gt;同一个人，在每个人眼里都不是同一个人&lt;/h2&gt;&#xA;&lt;p&gt;在物品推荐里，给每件物品一个向量是合理的，因为物品有一个相对稳定的含义：一部手机对所有人来说都是一部手机，只是每个人想要它的程度略有不同。物品的&amp;quot;吸引力&amp;quot;近似是一个全局属性，用户的&amp;quot;口味&amp;quot;也近似是一个全局属性，分数就是这两者的内积。这种干净的分离，每一边一个因子，正是矩阵分解所假设的，而它之所以成立，是因为拓扑结构白送给你：两边本来就是不同的类型。&lt;/p&gt;&#xA;&lt;p&gt;社交图里只有一种类型。一个人同时拥有一个口味（他倾向于去联系谁）和一个吸引力（谁倾向于来联系他），而没有任何划分能把这两者分开。[1] 更糟的是，吸引力本身还依赖于查看者。对甲我是个老同学，对乙我是个前上司，对丙我是个竞争对手。这可不是某个&amp;quot;真实向量&amp;quot;周围的小扰动，它们是近乎正交的不同角色，任何单一嵌入都不得不把它们全平均掉。&lt;/p&gt;&#xA;&lt;p&gt;这才是真正破坏这套打法的地方。候选嵌入，在物品推荐里是一个名词，对物品是什么的稳定描述，在社交推荐里却必须变成一个动词：一个关于&amp;quot;谁在看&amp;quot;的函数。&lt;/p&gt;&#xA;&lt;p&gt;同一个人，在每个人眼里都不是同一个人。&lt;/p&gt;</description>
			</item>
	</channel>
</rss>
