从余弦定理到 TikTok:一个用户的推荐流是怎么被算出来的
- 约 23 分钟读完 - 全文 8875 字从余弦定理到 TikTok:一个用户的推荐流是怎么被算出来的
上一篇《千人千面 + 无限瀑布流:推荐 Feed 的完整实现架构》把推荐系统的骨架拆完了:离线、近线、在线三层分工,召回、粗排、精排、重排四层漏斗。但通篇是框图和分工表,读完知道"系统长什么样",却未必有体感——我的数据,到底是怎么变成我刷到的这一屏的?
这篇换个讲法:造一个用户,给他一晚上的真实行为,跟着他走完全程。线索有三条,正好串起三十年:
- 吴军 2006 年起在 Google 黑板报连载、2012 年成书的《数学之美》——推荐算法的数学原点;
- Netflix 2013 年公开的离线/近线/在线三层实践——工程化的起点(上一篇的主线);
- TikTok 2020 年官方披露的 For You 推荐信号,和字节跳动 2022 年发表的 Monolith 在线训练系统——这套东西被推到的工程极致。
数学的原点:《数学之美》的两章
《数学之美》第 14 章"余弦定理和新闻的分类"讲了一个漂亮的化归:给新闻里每个实词算出 TF-IDF 值,按词汇表位置排成一个向量,一篇新闻就变成了一个特征向量;“新闻分类"这个看似需要理解语义的问题,变成了在向量空间里算余弦相似度的几何问题。第 15 章"矩阵运算和文本处理中的两个分类问题"接着讲:成千上万篇新闻构成的大矩阵,可以用 SVD(奇异值分解)一次拆开,分类在分解后的小矩阵上完成。
推荐系统是同一套数学,换了一张矩阵:把"新闻 × 词汇"换成"用户 × 内容”。余弦相似度回答的问题,从"两篇新闻像不像"变成"这个用户的口味和这条内容像不像";SVD 分解的那张大矩阵,就是后来协同过滤要分解的用户-物品交互矩阵。
别急着说这是类比——直接手算一遍。假设全站内容只有四个类目:宠物、科技、健身、汽车。用户的口味表示成一个 4 维向量,每个分量是他在该类目上累计的行为强度。
造一个用户:U10086 的一晚
主角 U10086,昨晚刷了一小时短视频,留下这些行为:
| 时间 | 行为 | 内容 | 类目 |
|---|---|---|---|
| 22:10 | 完播 | A123「橘猫拆家」 | 宠物 |
| 22:13 | 点赞 | B456「居家哑铃训练」 | 健身 |
| 22:15 | 1.2 秒划走 | C789「新能源车测评」 | 汽车 |
| 22:18 | 停留 8 秒 | D012「AI 编程工具实测」 | 科技 |
这些行为不会直接变成画像——中间隔着一层关键设计:内容的类目不是作者上传时选的,是平台算出来的。
内容的标签不是作者标的,是算出来的
一个常见误解:视频的类目来自作者上传时勾选的分区。在 TikTok 这个量级的平台上不是这样。作者自选标签噪声大,还挡不住"标签作弊"——挂个热门标签蹭流量。真实做法是内容理解管道:视频上传后先过一遍机器理解——抽帧做图像分类和目标检测、ASR 把语音转成文字、OCR 读画面上的字、NLP 分析标题和文案——最后由模型产出类目、标签和 embedding 向量。作者的 caption 和 hashtag 只是输入信号之一,没有最终决定权。TikTok 官方披露的三类信号里,“内容信息”(captions、sounds、hashtags)就是这条管道的产物:内容进流量池之前,“这条视频是关于什么的"已由机器定好。
甚至播放之后还在算。前面提到的 item2vec,训练数据就是全站用户的观看行为——被同一批人看完的内容,向量天然相近。也就是说,内容的向量随每一次播放被持续校准,而不是发布那一刻就定格。
用户向量是内容向量的累计
所以系统手里 U10086 的口味画像——宠物 0.55、科技 0.25、健身 0.15、汽车 0.05,即向量 $u = (0.55, 0.25, 0.15, 0.05)$——不是他填的问卷,也不是对人工类目的简单计数,而是他过去几个月每次行为对应的机器内容向量,按行为强度加权累计出来的。两边都是算出来的:内容侧机器识别,用户侧行为聚合。
这个设计值两个好处:
- 准:作者标错、乱标的类目污染不了画像,用户向量锚定在内容的真实特征上——他看完的是一只真猫,不是一个挂着"萌宠"标签的广告;
- 细:4 个类目只是教学简化,底下是几百维的 embedding,“橘猫"和"布偶猫"这种类目装不下的区分,向量装得下。
他今晚每一次播放、点赞,都会乘上那条内容的机器向量、计入自己的向量——这正是后面"短期兴趣向量"一节的微观机制。
今晚候选池里有三条内容(向量都出自内容理解管道):猫视频 $v_1 = (0.9, 0.1, 0, 0)$,哑铃教学 $v_2 = (0.05, 0.1, 0.8, 0.05)$,汽车测评 $v_3 = (0, 0.05, 0, 0.95)$。
余弦相似度的公式就一行:
$$\cos(u, v) = \frac{u \cdot v}{|u||v|}$$
给猫视频算一次:分子是点积 $0.55 \times 0.9 + 0.25 \times 0.1 = 0.52$;分母是两个向量的模长之积 $\sqrt{0.39} \times \sqrt{0.82} \approx 0.565$。结果是 $0.52 / 0.565 \approx 0.92$。三条候选全算完:
| 候选 | 余弦相似度 |
|---|---|
| 猫视频 $v_1$ | 0.92 |
| 哑铃教学 $v_2$ | 0.35 |
| 汽车测评 $v_3$ | 0.10 |
**这就是"推荐"在最原始形态下的全部:一次点积,一次归一化。**今天 TikTok 的排序模型参数量以百亿计,打分函数的内核仍然是这个动作——只是向量从 4 维人工统计变成几百维神经网络学出来的 embedding(item2vec,2016;双塔模型),从"类目"粒度细化到"每条内容"粒度。三十年里数学没有换,换的是向量的生产方式和计算规模。
行为怎么变成画像:Netflix 的三层分工
4 维向量是教学模型,真实系统里的用户向量没法靠人工统计类目得到——内容有几亿条,行为有几十种。工程上的解法就是上一篇展开讲过的 Netflix 三层分工,以及它背后的一整套大数据栈:用户的每一次曝光、点击、停留,客户端上报后先进 Kafka(事件总线);一份实时流进 Flink 作业,秒级滚动更新短期画像;同时全量落盘到 HDFS(Hadoop 生态的分布式存储),由 Hive(或 Iceberg)建成离线数仓。离线层的 Spark 批任务(天级)从数仓读数据,算物品向量、itemCF 共现矩阵、用户长期兴趣向量;在线层只做查表和轻量推理,画像和榜单都躺在 Redis 里。重活在用户到来之前全部干完。
问题是:一次"完播"和一次"秒划走”,该给短期画像各贡献多少?这个强弱排序不是工程师拍脑袋——TikTok 官方给过答案。
2020 年 6 月 18 日,TikTok 在官方 newsroom 发表 “How TikTok recommends videos #ForYou”,是平台方罕见的一次坦白。它把推荐信号分成三类:
- 用户交互:点赞、分享、关注、评论、发布的内容、看完的视频;
- 内容信息:标题文案、声音、话题标签;
- 设备与账号设置:语言、国家、设备类型——官方明说这类权重更低,因为"用户没有主动把这些表达为偏好”。
更重要的是官方对信号强度的排序:原话是"用户是否把一个较长的视频从头看到尾"这类强兴趣指标,权重远大于"观众和创作者是否在同一国家"这类弱指标。翻译成 U10086 的表格:他完播猫视频这件事的权重,压过他的手机和品牌方在同一城市。
这套强弱排序落到工程上,就是近线层滚动更新的短期兴趣向量——最近 N 次行为对应的物品向量做加权平均:
$$u_{short} = \frac{\sum_{i} w_i \cdot e^{-\Delta t_i / \tau} \cdot v_i}{\sum_{i} w_i \cdot e^{-\Delta t_i / \tau}}$$
权重 $w_i$ 沿行为强度排:完播 3.0、点赞 2.0、停留 5 秒以上 1.5、点击 1.0、秒划走 -0.5(负反馈反向拉);指数项是时间衰减,越久的行为影响越小。代入 U10086 的一晚:猫视频向量乘 3.0 主导方向,汽车视频乘 -0.5 往反方向拽。他昨晚的每一个动作,在他下次下拉之前,就已经被压缩成这个向量躺在 Redis 里了。
把公式拆开:每个符号是什么
- $i$:遍历用户最近 N 次行为的下标。N 常见取 50-200,只保留最近一小段——太久之前的行为对"今晚想看什么"没有预测力;
- $v_i$:第 i 次行为涉及的内容向量——就是上一节内容理解管道算出来的那条视频的向量。注意它是物品的向量,不是用户的;用户向量正是由一堆物品向量合成的;
- $w_i$:这次行为的强度权重(完播 3.0、点赞 2.0、停留 1.5、点击 1.0、秒划 -0.5),回答"这个动作多能代表他的兴趣";
- $\Delta t_i$:这次行为距今的时间差;
- $\tau$:时间衰减常数,控制"多久之前的行为算老"。$\tau$ 取 12 小时,则 12 小时前的行为权重衰减到 $e^{-1} \approx 0.37$,一天前只剩 0.14;
- $e^{-\Delta t_i / \tau}$:指数时间衰减因子,越久的行为对当前兴趣越没有发言权;
- 分母:归一化项。没有它,刷得多的人向量模长就大,“勤奋"会冒充"兴趣浓”;除一下只留方向,不带数量。
一句话:短期兴趣 = 最近看过的内容的向量,按"行为多重 × 时间多近"加权平均。
演算:U10086 的短期向量
把内容向量代入他昨晚的四次行为。$\tau$ 取 0.5 小时,站在 22:30 这一刻计算(D012 的内容向量是 (0.05, 0.85, 0.05, 0.05),科技向):
| 行为 | 内容向量 $v_i$ | 强度 $w_i$ | 距今 $\Delta t_i$ | 衰减 $e^{-\Delta t_i / \tau}$ | 有效权重 $w_i \cdot e^{-\Delta t_i / \tau}$ |
|---|---|---|---|---|---|
| 完播猫视频 | (0.9, 0.1, 0, 0) | 3.0 | 20 分钟 | 0.51 | 1.54 |
| 点赞哑铃 | (0.05, 0.1, 0.8, 0.05) | 2.0 | 17 分钟 | 0.57 | 1.13 |
| 秒划汽车 | (0, 0.05, 0, 0.95) | -0.5 | 15 分钟 | 0.61 | -0.30 |
| 停留 AI 工具 | (0.05, 0.85, 0.05, 0.05) | 1.5 | 12 分钟 | 0.67 | 1.01 |
分子是四个"有效权重 × 内容向量"之和,这里的算术只有两步:标量乘向量、向量相加。全部展开(保留三位小数):
第一步,标量乘向量——每条内容向量逐分量乘上自己的有效权重:
有效权重 × 内容向量 = 加权后的向量
猫视频 1.54 × (0.9, 0.1, 0, 0) = ( 1.386, 0.154, 0, 0)
哑铃 1.13 × (0.05, 0.1, 0.8, 0.05) = ( 0.057, 0.114, 0.904, 0.057)
汽车 -0.30 × (0, 0.05, 0, 0.95) = ( 0, -0.015, 0, -0.285)
AI工具 1.01 × (0.05, 0.85, 0.05, 0.05) = ( 0.050, 0.854, 0.050, 0.050)
第二步,逐分量相加——四个向量的同一列相加,每一列就是一个类目方向:
宠物分量: 1.386 + 0.057 + 0 + 0.050 = 1.49
科技分量: 0.154 + 0.114 - 0.015 + 0.854 = 1.11
健身分量: 0 + 0.904 + 0 + 0.050 = 0.96
汽车分量: 0 + 0.057 - 0.285 + 0.050 = -0.18
分子向量就是 (1.49, 1.11, 0.96, -0.18);分母是有效权重之和 1.54 + 1.13 - 0.30 + 1.01 = 3.38。最后逐分量相除:1.49 / 3.38 ≈ 0.44,1.11 / 3.38 ≈ 0.33,0.96 / 3.38 ≈ 0.28,-0.18 / 3.38 ≈ -0.05。得到:
$$u_{short} \approx (0.44, 0.33, 0.28, -0.05)$$
从第二步的列里能直接读出故事:宠物分量的九成来自那条完播的猫视频(1.49 里它独占 1.386);汽车这一列,哑铃和 AI 工具带来的两个正贡献(0.057 + 0.050)加起来也抵不过一次秒划的 -0.285——负权重的意义就在这里:他不喜欢的内容在向量里留下的不是零,而是负坐标,检索时会把相似内容往反方向推。
对比他的长期画像 $(0.55, 0.25, 0.15, 0.05)$:一小时的行为已经把科技顶到第二位、健身翻了倍、汽车打成负值。在线服务一般把长短期两个向量融合(拼接或加权和)作为最终用户向量——长期画像管"他是个什么人",短期向量管"他今晚想看什么"。下一刷召回时,猫和科技内容能同时被捞回来,靠的就是这个融合向量。
TikTok 把闭环推到极致
官方确认的,和官方从没确认的
除了信号分类,TikTok 那篇文章还有一句关键坦白:粉丝数和账号历史爆款都不是直接推荐因素(原话:neither follower count nor previous high-performing videos are direct factors)。这解释了新账号为什么也能出爆款——内容进场不看出身。
中文互联网流传的"流量池"机制要在这里说清边界:新视频先给 200-500 次曝光的小池子,完播率、互动率跑赢同期内容就推进千级、万级的更大池子,逐级叠加放量——这套具体说法出自运营圈的逆向总结,TikTok 和抖音官方从未确认过细节。但它的方向和官方披露自洽:既然粉丝数不算直接因素,新内容就必须有一个"不看出身、先看小范围真实反馈"的冷启动机制,流量池只是这个必然性的具体形态。
flowchart TD
A["新视频发布<br/>初始小流量池 数百次曝光"] --> B{"完播率 互动率<br/>跑赢同期内容?"}
B -- "达标" --> C["二级流量池<br/>千级曝光"]
B -- "不达标" --> D["停止放量<br/>转入自然长尾"]
C --> E{"数据继续跑赢?"}
E -- "达标" --> F["更大流量池<br/>万级到百万级"]
E -- "不达标" --> D
(图为第三方运营经验的通行描述,非官方口径。)
Monolith:连模型本身都在线学习
Netflix 的三层架构里,模型参数是离线训练的,近线只更新特征和画像。字节跳动 2022 年发表的 Monolith(RecSys ‘22,arXiv:2209.07663)把这条边界也打掉了:推荐模型在线训练——行为事件实时变成训练样本回流,模型参数近实时更新。
工程难点在 embedding 表。在线场景下新内容源源不断涌进来,传统哈希表会发生碰撞:两条不同的内容被映射到同一份 embedding,互相污染。Monolith 用 cuckoo hashing 实现无碰撞的 embedding 表,再配合特征过期和低频过滤控制内存,硬是把"在线深度学习"做成了生产级系统。
对比一下闭环速度:Netflix 2013 年的近线层,“行为 → 画像生效"做到秒级已经是当时的极致;Monolith 把"行为 → 模型参数更新"也压到了近实时。闭环速度,就是推荐系统的军备竞赛指标。
跟着 U10086 走一遍四层漏斗
铺垫完了,现在让 U10086 在 23:05 下拉刷新,跟他的数据走完全程。四层漏斗的原理上一篇讲过,这篇只看一件事:每一步的数据长什么样。
召回:1 亿 → 8000
输入:短期兴趣向量 $u_{short}$、最近行为物品列表 [A123, B456, D012]、长期画像。
多路并行,每一路都是查表或检索,没有任何矩阵运算:
| 召回路 | 动作 | 产出 |
|---|---|---|
| itemCF 协同过滤 | Redis 批量查 A123、B456、D012 各自的相似物品 Top50(相似度矩阵是 Spark 离线算好的共现余弦) | 约 3000 条,大量猫、健身、科技内容 |
| 向量召回 | $u_{short}$ 在 Faiss(HNSW 索引)里做近似最近邻检索,几毫秒返回 | 3000 条 |
| 热门榜 | 直接读 Redis 里 Flink 滚动窗口算好的全站热榜 | 1000 条 |
| 标签/关键词召回 | 内容元数据(标题、标签、类目)灌在 Elasticsearch 的倒排索引里,拿画像里的高权重标签当查询条件去检索——「搜推一体」里 ES 干的就是这路 | 约 500 条 |
| 新内容池 + 关注产出 + 探索流量 | 读离线、近线备好的榜单 | 各几百条 |
输出:多路合并、去重、按路配额截断——8000 条候选 ID 的列表,每条只带召回来源和粗分。注意一个细节:没有一路会拿 C789(秒划走的汽车视频)当种子去查相似内容,汽车类在召回阶段就已经天然稀疏了。
粗排:8000 → 1000
输入:8000 条候选 + 一批"便宜特征”——从特征存储(Feast 或自研 Feature Store,底层通常是 Redis)批量读出物品热度分、质量分、该用户的类目偏好分、历史 CTR。
计算:轻量模型打分(简化双塔就是一次向量点积,GBDT 就是几千次小树遍历——XGBoost / LightGBM),单条成本压到微秒级。
输出:每条一个粗排分,截断 Top 1000:
cat_video_8891 (A123 的相似内容) 0.81 ← 和短期向量点积极高
ai_tool_3302 (D012 的相似内容) 0.74
hot_funny_1102 (热门榜兜底) 0.62
car_review_5567 0.05 ← 秒划负反馈直接压死
精排:1000 → 200
输入:1000 条候选 + 全量特征拼接——用户画像(Redis KV 读出)、物品特征、近线实时特征(最近一小时的点击序列,Flink 产出)、实时上下文(23:05、手机、WiFi)。
计算:深度排序模型(DIN 系,阿里 2018 年 KDD 发表)在推理服务(TF Serving 或 NVIDIA Triton,GPU 批量矩阵乘)上跑推理。DIN 的注意力机制值得演算一步:给候选 cat_video_8891 打分时,模型给 U10086 的历史行为分配权重——同为猫视频的 A123 权重拉到最高,B456 次之,C789 趋近于零,输出高 pCTR;而给 car_review_5567 打分时,被注意机制激活的恰恰是 C789——方向是负的。同一个用户的历史,在不同候选面前讲出不同的故事,这是 DIN 相比"死板的平均历史向量"的本质进步。
输出:每条候选一对预测值 (pCTR, 预估观看时长),加权成综合分后排序,截断 Top 200。
重排:200 → 20
输入:200 条带精排分的列表。这一层几乎不算模型,全是规则:
- 已曝光过滤:查 Redis 里
user:10086:seen集合(或布隆过滤器),剔除本次 session 已刷过的 15 条; - 负反馈硬屏蔽:汽车类目整体降权——秒划走加历史"不感兴趣"记录;
- 多样性打散:同作者最多 2 条、相邻 3 条不同类目——Top 200 里连着的 8 条猫视频被打散重插;
- 混排:第 4 位插入广告(广告单独按 eCPM = 出价 × 预估点击率 排序选出),第 9 位插一条关注博主的更新;
- 业务护栏:过滤审核中内容,新内容扶持配额 5%——这就是流量池机制落在自然流量里的那一小块。
输出:
{
"items": ["...20 条:16 条自然内容 + 2 条广告 + 1 条关注更新 + 1 条新内容扶持..."],
"next_cursor": "eyJscCI6MC44MSwiYmF0Y2giOiJUYjI5..."
}游标里封着最后一条的排序分、本次批次号、过期时间,客户端不许解析。同时这 20 条写入已曝光集合,曝光和后续行为事件经 Kafka 回流——闭环合上,U10086 的下一刷已经在被这一刷改写。
沿途的三方件总表
| 位置 | 工具 | 角色 |
|---|---|---|
| 事件总线 | Kafka | 行为事件解耦、削峰、可回放 |
| 数据底座 | Hadoop(HDFS)+ Hive / Iceberg | 全量行为日志落盘,离线数仓 |
| 离线计算 | Spark | 模型训练、itemCF 共现矩阵、物品向量 |
| 近线计算 | Flink | 秒级更新短期画像、滚动热榜 |
| 在线训练 | Monolith(字节自研,论文开源) | 模型参数近实时更新 |
| 在线 KV | Redis(或 HBase) | 画像、榜单、已曝光集合、itemCF 结果 |
| 向量检索 | Faiss / HNSWlib / Milvus | 双塔向量的近似最近邻召回 |
| 倒排索引 | Elasticsearch | 标签/关键词召回路,「搜推一体」的内容检索 |
| 特征平台 | Feast 或自研 Feature Store | 离线、在线特征一致性 |
| 模型推理 | TF Serving / NVIDIA Triton | 精排批量打分 |
| 轻量模型 | XGBoost / LightGBM | 粗排 |
| 辅助 | 布隆过滤器、规则引擎 | 已曝光去重、重排策略 |
这套链路怎么扛住高并发
漏斗走完了,但还有一个工程问题没回答:几十毫秒的延迟预算,乘以几十万的并发请求,靠什么扛?先把账算出来。假设 1 亿日活、人均 50 次下拉:平均约 6 万次请求/秒,晚高峰按 3-5 倍算是 20-30 万 QPS。每次下拉背后是几十上百次 KV 读取、一次向量检索、一次千级候选的模型推理。能扛住靠的不是某台快机器,是一组选型决策:
Redis:O(1) 查表 + 批量读。 单实例简单 GET 是十万级 QPS、亚毫秒延迟;一次召回要查几十上百个 key(itemCF 种子物品、画像、热榜),用 mget 合并成一次网络往返,QPS 压力直接降一个量级;集群模式水平分片,容量不够就加分片。已曝光集合的布隆过滤器也放在这里。
向量检索:这是召回里唯一真正"算"的地方,也是选型的重点。 几个主流方案的性能画像(百万到十亿级向量、TopK 检索):
| 方案 | 形态 | 性能画像 | 适用场景 |
|---|---|---|---|
| Faiss(Meta) | 嵌入服务进程内的库 | 没有网络和序列化开销;IVF+PQ 量化后单机可扛亿级向量,检索毫秒级 | 召回服务内嵌,延迟敏感场景的主力 |
| HNSWlib | 嵌入库 | 图索引召回率高(95% 以上),代价是全量向量驻内存,内存开销大 | 中等规模、高精度 |
| ScaNN(Google) | 嵌入库 | 各向异性量化,同等召回率下 QPS 领先 | 内嵌、高 QPS |
| Milvus | 独立分布式服务 | 存算分离、支持 GPU 索引,十亿级横向扩展 | 多业务共享的向量中台 |
| Elasticsearch kNN | 搜索引擎插件 | 复用现有 ES 集群,开箱即用,但 QPS 和召回率都不及专用索引 | 小规模、搜推一体 |
工程上的关键判断:召回的向量检索优先用内嵌库(Faiss / HNSWlib),省掉一次 RPC——30 万 QPS 面前,每次网络往返都是命。Milvus 这类独立服务的价值在多团队共享、在线扩缩容和统一的向量管理,不在单点延迟。内容池上到十亿级,靠 PQ 量化把每条向量从几百字节浮点压成几十字节编码,再加分片。
Kafka:百万级消息/秒是常态负载。 曝光点击的洪峰对它来说不算事;真正要设计的是分区数(决定下游消费者的并行上限)和消费滞后监控——滞后就是"画像生效延迟",直接等于用户体感。
Flink:状态在本地的流计算。 短期画像的滚动更新是 keyed state,状态读写走本地 RocksDB,不回远程存储——这是近线层敢吞全量事件流的原因。反过来说,一旦作业挂掉从 checkpoint 恢复,RocksDB 状态的加载时间就是画像的"失明时间",所以 checkpoint 间隔要按业务能容忍的失明时长来定。
GPU 推理:批量摊薄。 精排把 1000 条候选拼成一个大 batch 一次过 GPU,摊到每条只有几微秒;Triton 的动态批处理(dynamic batching)会在毫秒级窗口里把多个请求的 batch 攒起来再推理,用一点点延迟换几倍吞吐——这是"几十毫秒预算"能成立的关键手法之一。
共同的模式只有一条:所有重活提前算完存起来,在线路径上只剩内存级查询和批量推理——30 万 QPS 里,没有一个请求需要"现场思考"。
收尾:数学没变,闭环变快
走完全程回头看,三十年里真正变化的不是数学:
- 2006 年,吴军讲余弦定理给新闻分类——打分内核从那时起就是向量点积,今天依然如此;
- 2013 年,Netflix 定下三层分工——把计算摊到用户到来之前,至今是主流形态;
- 2020 年,TikTok 官方坦白完播是强信号、粉丝数不是直接因素——行为强度加权第一次有了平台方背书;
- 2022 年,Monolith 把模型训练本身搬进在线链路——闭环的最后一段延迟也被压缩。
推荐系统真正的军备竞赛,从来不是"谁的公式更巧妙",而是谁的闭环转得更快:从用户划走一条汽车视频,到系统少推汽车内容,中间隔了几秒还是几天。U10086 今晚的每一次停留和划走都在投票,明早打开 App 时看到的那个"更懂他"的首页,没有一次是当场算出来的——全是昨晚就已开始的计算,刚好在他到来之前完成。
相关阅读:《千人千面 + 无限瀑布流:推荐 Feed 的完整实现架构》——本文的架构视角姊妹篇,讲四层漏斗的原理和无限下拉的分页协议。
参考:吴军《数学之美》(第三版,人民邮电出版社)第 14、15 章;TikTok Newsroom, “How TikTok recommends videos #ForYou”, 2020-06-18(newsroom.tiktok.com/en-us/how-tiktok-recommends-videos-for-you);Liu et al., “Monolith: Real Time Recommendation System With Collisionless Embedding Table”, RecSys 2022, arXiv:2209.07663;“流量池"分级机制为运营圈第三方经验总结,非官方披露口径。