0%

在推荐、广告和内容分发系统里,我们往往不会只优化一个目标。

以信息流场景为例,平台可能同时关心:

  • 点击率(CTR)
  • 转化率(CVR)
  • 停留时长
  • 收藏、加购、分享等互动行为

这些目标都和“用户价值”有关,但又并不完全一致。只做单目标建模会有两个明显问题:

  • 每个目标单独训练一套模型,维护成本高,特征利用也不充分
  • 不同目标之间本来存在关联,如果完全拆开训练,会损失任务间可以共享的统计规律

因此,多任务学习(Multi-task Learning, MTL)在推荐系统里非常常见。它的基本思想是:

  • 用一个模型同时学习多个目标
  • 让任务之间共享一部分表示能力
  • 又保留各自的个性化建模能力

多目标建模里最经典的一条演化路线,大致可以概括为:

  • Shared-Bottom:先做最基础的参数共享
  • MMoE:引入专家网络和门控机制,让不同任务学会“按需共享”
  • PLE:进一步缓解任务冲突,把共享信息和任务专属信息分层解耦
MMoE

基础架构:Shared-Bottom

Shared-Bottom 是多任务学习里最朴素、也最好理解的结构。

它的做法非常直接:

  1. 底部使用一套共享的特征抽取网络
  2. 共享层输出一个公共表示
  3. 每个任务在顶部再接自己的 tower
  4. 各任务分别输出自己的预测结果

如果写成形式化表达,可以理解为:

$$
h = f_{shared}(x)
$$

第 $k$ 个任务的输出为:

$$
y_k = f_k(h)
$$

其中:

  • $x$ 是输入特征
  • $f_{shared}$ 是所有任务共用的底层网络
  • $f_k$ 是第 $k$ 个任务自己的输出头

它之所以叫 Shared-Bottom,就是因为“共享”主要发生在底部。

优势

Shared-Bottom 能成为最早期、最广泛使用的多任务结构,不是没有原因的。

  1. 共享层的存在显著降低了模型总参数量。相比每个目标都单独建模,底层只训练一套网络,训练和部署成本都更低。
  2. 共享参数天然带来正则化效应。多个任务共同约束底层表示,能够减少单一任务在数据不足时的过拟合风险。
  3. 当任务之间确实存在相关性时,共享层可以学习到通用模式,从而产生知识迁移,提高泛化能力。

例如在广告场景中:

  • CTR 任务会帮助模型学到“用户是否愿意点进来”
  • CVR 任务会帮助模型学到“点进来之后是否会成交”

虽然两者不完全一样,但用户、商品、上下文之间的一部分交互规律是可以共享的。

缺陷

Shared-Bottom 的问题也同样明显:它默认所有任务都应该共享同一套底层表示

这在任务高度相关时通常还可以工作,但一旦任务相关性较弱,甚至存在冲突,就会出现典型的负迁移(negative transfer):

  • 某个任务希望强化一类特征
  • 另一个任务却希望抑制这类特征
  • 共享层被多个目标同时拉扯,最后谁都学不舒服

例如:

  • CTR 更关注“是否吸引点击”
  • 时长任务更关注“内容是否耐看”

有些标题党内容可能很容易带来点击,却未必能带来长时停留。此时多个目标对共享表示的优化方向并不一致。

所以 Shared-Bottom 的核心问题可以总结为一句话:

  • 共享是静态且强制的,无法根据任务差异动态分配表示能力

这也是后续 MMoE 出现的直接动机。

MMoE

MMoE(Multi-gate Mixture-of-Experts)是 Google 在多任务学习场景里提出的一类经典结构。它试图解决的,不是“要不要共享”,而是:

  • 哪些知识该共享
  • 共享多少
  • 不同任务该从哪些专家那里获取信息

相比 Shared-Bottom 的“一刀切共享”,MMoE 把共享改成了可学习的软选择

OMoE

在正式理解 MMoE 之前,先看一个过渡结构:OMoE(One-gate Mixture-of-Experts)。

OMoE 的核心结构是:

  1. 底部不再只有一个共享网络,而是放置多个 expert
  2. 所有 expert 并行接收同一份输入
  3. 用一个共享 gate 生成权重
  4. 将多个 expert 的输出加权汇总后,再送给不同任务头

设有 $n$ 个 expert,第 $i$ 个 expert 输出为:

$$
e_i(x)
$$

共享 gate 生成权重:

$$
g(x) = \mathrm{softmax}(W_g x)
$$

则融合表示可写成:

$$
h = \sum_{i=1}^{n} g_i(x)e_i(x)
$$

然后再由不同任务塔做预测。

OMoE 比 Shared-Bottom 更灵活,因为:

  • 不再强迫所有任务只使用同一个底层表征
  • 模型可以把不同模式分散到不同 expert 中

但它仍然有一个很大的限制:

  • 所有任务共享同一个 gate

也就是说,虽然 expert 变多了,但“如何组合这些 expert”仍然是一套统一策略。这意味着不同任务最终看到的仍是同一种专家加权结果。

所以 OMoE 只解决了“共享网络容量不足”的问题,却没有真正解决“任务对共享知识需求不同”的问题。

MMoE 架构及原理

MMoE 相比 OMoE 的关键变化只有一句话:

  • 每个任务都有自己独立的 gate

整体结构变成:

  1. 底部有多个共享 expert
  2. 每个任务都拥有一个自己的 gating network
  3. 不同任务分别对同一组 expert 进行加权组合
  4. 每个任务再接自己的 tower 输出预测结果

设共有 $n$ 个 expert,第 $k$ 个任务的 gate 输出为:

$$
g^{(k)}(x) = \mathrm{softmax}(W^{(k)}_g x)
$$

则第 $k$ 个任务获得的输入表示为:

$$
h^{(k)} = \sum_{i=1}^{n} g^{(k)}_i(x)e_i(x)
$$

最后第 $k$ 个任务输出:

$$
y_k = f_k(h^{(k)})
$$

这个变化看似不大,但本质上完成了从“统一共享”到“按任务路由”的转变。

直观理解 MMoE,可以把 expert 看成一组不同能力的老师:

  • 有的 expert 更擅长建模点击模式
  • 有的 expert 更擅长建模转化倾向
  • 有的 expert 更擅长建模长时兴趣

而每个任务自己的 gate,决定它更愿意听哪些老师的意见。

MMoE 相比 Shared-Bottom 的改进

MMoE 的优势主要体现在三个方面。

  1. 共享不再是硬性的,而是软性的。不同任务可以根据自身需要,从多个 expert 中动态组合信息。
  2. expert 之间天然形成某种功能分化。即使没有显式标注,训练过程中也可能出现“某些 expert 更偏向某类任务模式”的现象。
  3. 当任务相关性复杂、部分共享但又不完全重叠时,MMoE 往往比 Shared-Bottom 更稳健。

从经验上看,任务越多、任务差异越大,MMoE 相比 Shared-Bottom 的优势通常越明显。

MMoE 的局限

虽然 MMoE 比 Shared-Bottom 强很多,但它并没有彻底消灭负迁移。

主要原因有两个。

第一,expert 本身仍然是所有任务共享的。

也就是说,即便 gate 不同,底层 expert 参数还是被多个任务共同训练。如果任务冲突很强,expert 仍然可能学成“折中结果”。

第二,门控决策负担很重。

gate 需要同时完成两件事:

  • 判断当前样本更适合走哪些 expert
  • 间接协调不同任务之间的冲突

如果 expert 没有形成足够清晰的职责分工,gate 的选择空间就会变得混乱。最终模型可能出现:

  • 多个任务都偏向同一批 expert
  • 某些 expert 长期得不到充分训练
  • 任务冲突被转移到 gate 和 shared expert 上,但没有真正被拆开

换句话说,MMoE 让“共享”变聪明了,但共享和专属之间的边界仍然不够清楚。

这正是 PLE 想进一步解决的问题。

PLE

PLE(Progressive Layered Extraction)可以看作是对 MMoE 的进一步结构化改造。它的核心判断是:

  • MMoE 之所以仍会出现负迁移,不仅是因为共享方式不够灵活
  • 更因为“共享知识”和“任务私有知识”没有被显式拆开

因此,PLE 希望把信息流分成两类:

  • 共享信息
  • 任务专属信息

并通过逐层提取(progressive layered extraction)的方式,让二者既能交互,又不过度混杂。

MMoE 负迁移未根除,门控决策负担重,PLE 正是在这个背景下提出的。

CGC结构

PLE 的基础模块叫 CGC(Customized Gate Control)。

一个 CGC 层通常包含两类 expert:

  • shared experts:为所有任务提供公共知识
  • task-specific experts:只服务于某一个任务

然后,每个任务自己的 gate 不再对“全体 expert”做无差别选择,而是从:

  • 本任务的 task-specific experts
  • shared experts

这两部分中进行加权组合。

与此同时,共享分支自己也会有一个 gate,用来聚合:

  • 所有任务的专属 expert
  • shared experts

从而形成下一层共享表示。

这套机制的关键价值,在于它把“所有 expert 都放在一个池子里竞争”改成了“共享池 + 专属池”的显式分层。

1. 专家职责强制分离

这是 PLE 相比 MMoE 最重要的一点。

在 MMoE 中,所有 expert 理论上都服务于所有任务,最终是否形成分工,主要依赖训练过程的自发演化。

而在 PLE 中,模型结构直接规定:

  • 有些 expert 就是公共专家
  • 有些 expert 就是任务私有专家

这样做的好处是:

  • 公共模式可以沉淀在 shared experts 中
  • 任务特有模式可以沉淀在 task-specific experts 中
  • 不必把“共享多少、私有多少”都压给 gate 自己去学

也就是说,PLE 用结构先验主动帮助模型做职责划分。

2. 任务专属门控的输入限制

另一个关键点是,任务 gate 的可选范围被限制了。

对于第 $k$ 个任务,它不会像 MMoE 一样面对所有 expert,而只会从下面两类中选择:

  • 第 $k$ 个任务自己的专属 experts
  • shared experts

这意味着:

  • 任务不会直接去读取别的任务的私有 expert
  • 跨任务共享必须通过 shared experts 这一“公共通道”完成

这种限制非常重要,因为它减少了无序的信息串扰。

可以把它理解为:

  • MMoE 像一个开放式大群,所有任务都能直接找所有 expert
  • PLE 更像有公共会议室,也有每个任务自己的办公室

共享问题去公共会议室讨论,私有问题留在各自办公室解决。

PLE架构及原理

PLE 往往不是只堆一层 CGC,而是堆叠多层,形成 progressive layered extraction。

它的基本流程可以理解为:

  1. 输入特征进入第一层 CGC
  2. 每个任务分支和共享分支分别得到自己的表示
  3. 这些表示继续送入下一层 CGC
  4. 随着层数加深,共享信息与任务专属信息被逐步提炼
  5. 最后每个任务分支接自己的 tower 输出结果

如果从建模逻辑上理解,PLE 做的是一种“逐层去混叠”:

  • 底层允许较多共享,因为原始特征里确实有大量共性
  • 越往上走,任务差异越明显,专属分支承担更多个性化建模

这比只在单层上做一次专家选择更细致,也更符合多任务学习中“底层偏通用、高层偏任务化”的经验规律。

PLE 的优势

PLE 在工业界受欢迎,主要是因为它比 MMoE 更稳定。

  1. 显式区分共享专家和任务专家,能更有效缓解负迁移。
  2. gate 的选择空间被约束后,决策难度下降,训练更容易稳定。
  3. 多层 CGC 让共享信息和私有信息可以逐步分化,而不是一次性硬切。
  4. 在任务相关但不完全一致的场景中,通常比 Shared-Bottom 和 MMoE 更鲁棒。

特别是在推荐和广告系统中,像:

  • CTR
  • CVR
  • 完播率
  • 停留时长
  • 互动率

这些目标往往既有共性,又各自强调不同的行为结果,PLE 通常会比简单共享结构更合适。

PLE 的代价

当然,PLE 也不是没有成本。

  • 结构更复杂,参数量和训练成本通常高于 Shared-Bottom。
  • 超参数更多,例如 shared experts 数量、task-specific experts 数量、CGC 层数等,调参复杂度更高。
  • 当任务非常少、而且相关性很强时,PLE 的收益未必足以覆盖其复杂度。

因此是否使用 PLE,本质上还是一个工程权衡:

  • 任务越多
  • 目标差异越大
  • 负迁移越明显

PLE 的价值通常越大。

总结

如果把这几种结构放在同一条演进线上,可以这样理解:

1. Shared-Bottom

  • 假设所有任务共享同一套底层表示
  • 优点是简单、高效、易落地
  • 缺点是容易受到负迁移影响

2. MMoE

  • 通过多个 expert 和任务独立 gate,实现按任务动态共享
  • 比 Shared-Bottom 更灵活
  • 但 expert 仍是全共享的,任务冲突没有被彻底拆解

3. PLE

  • 在 MMoE 基础上进一步显式区分共享专家和任务专家
  • 通过分层提取逐步解耦共享信息与私有信息
  • 通常在复杂多目标场景下表现更稳

所以从本质上说,这三者是在回答同一个问题:

  • 多个任务到底应该如何共享表示能力

它们给出的答案分别是:

  • Shared-Bottom:大家先共用一套底层再说
  • MMoE:共享可以,但要让不同任务自己决定怎么用
  • PLE:共享和私有都要有,而且要在结构上明确分开

在真实业务里,没有哪个结构对所有问题都最优。更合理的做法通常是根据场景判断:

  • 如果任务少且高度相关,Shared-Bottom 可能已经足够
  • 如果任务关系复杂,MMoE 往往是一个很好的起点
  • 如果负迁移明显、目标差异较大,PLE 通常更值得尝试

理解这条演化链条,比死记某个结构图更重要。因为多目标建模的核心从来不是“哪篇论文最火”,而是:

  • 如何在共享收益和任务冲突之间找到合适的平衡

DIN

在推荐系统里,用户兴趣并不是一个固定不变的向量,而是会随着候选物品的不同而表现出不同侧面。

例如同一个用户既买过母婴用品,也看过数码产品。当我们要预测他是否会点击一款婴儿车时,历史里的母婴行为显然更重要;而在预测他是否会购买耳机时,数码相关行为又更有参考价值。

传统的 Embedding + MLP 排序模型,通常会把用户历史行为池化成一个固定长度向量,例如平均池化或求和池化:

$$
v_u = \frac{1}{T}\sum_{i=1}^{T} e_i
$$

其中:

  • $e_i$ 表示第 $i$ 个历史行为 item 的 embedding
  • $T$ 表示行为序列长度
  • $v_u$ 表示汇总后的用户兴趣表示

这种做法虽然简单,但问题也很明显:

  • 所有历史行为被一视同仁
  • 用户兴趣被压缩成单一静态向量
  • 无法根据当前候选广告或商品动态调整关注重点

DIN(Deep Interest Network)提出的核心思想就是:

  • 不预先生成一个固定的用户兴趣向量
  • 而是针对当前候选 item,自适应地从历史行为里挑选更相关的兴趣信号
DIN

1. 核心动机

DIN 最重要的观察是:用户兴趣是局部激活的

也就是说,用户并不是始终对所有兴趣都保持同样强度,而是在面对某个候选 item 时,只会激活与它相关的那部分历史兴趣。

因此,DIN 不再简单做平均池化,而是引入注意力机制,让候选 item 作为 query,历史行为作为 key/value,计算不同历史行为对当前目标的相关程度。

2. 兴趣激活单元

设当前候选 item 的 embedding 为 $e_a$,用户历史行为序列为:

$$
{e_1,e_2,\ldots,e_T}
$$

DIN 会先计算每个历史行为和候选 item 之间的匹配分数:

$$
w_i = f(e_i, e_a)
$$

这里的 $f(\cdot)$ 通常是一个前馈网络,输入可能包括:

  • $e_i$
  • $e_a$
  • $e_i - e_a$
  • $e_i \odot e_a$

其中 $\odot$ 表示逐元素乘积。

得到权重后,对历史行为做加权汇总:

$$
v_u(e_a) = \sum_{i=1}^{T} w_i e_i
$$

注意这里的 $v_u(e_a)$ 不再是一个固定的用户向量,而是和候选 item 相关的兴趣表示

这就是 DIN 相比传统 pooling 的本质区别。

3. 模型结构

DIN 的整体流程可以理解为下面几步:

  1. 对用户画像、上下文特征、候选 item、历史行为序列分别做 embedding
  2. 用候选 item 对历史行为序列做 attention,生成与当前目标相关的兴趣表示
  3. 将兴趣表示与其他特征拼接
  4. 输入多层感知机(MLP)输出点击率或转化率

因此,从结构上看,DIN 仍然属于 CTR 预估中的深度排序模型,只不过它在“用户兴趣建模”这一层引入了目标感知的注意力机制。

4. DIN 解决了什么问题

DIN 相比早期的 Wide & Deep、DeepFM 一类模型,主要改进在于:

  • 从静态兴趣建模转向动态兴趣激活
  • 让历史行为不再等权参与预测
  • 显式建模“候选 item 和历史行为是否相关”

对于电商场景尤其有效,因为用户的浏览、点击、加购、购买行为天然带有强烈的多兴趣特征。

5. 感知 mini-batch 的正则化

除了兴趣激活单元本身,DIN 论文里还提到了一个比较工程化、但很实用的训练技巧:mini-batch aware regularization。

CTR 模型里大量参数来自 embedding,尤其是用户、item、类目等高维稀疏特征。若直接对全部 embedding 参数做普通 $L_2$ 正则:

$$
\lambda \lVert \theta \rVert_2^2
$$

虽然形式上很简单,但在工业场景里会遇到一个问题:

  • 参数规模极大
  • 每个 mini-batch 实际只访问其中很小一部分 embedding
  • 如果每一步都对全量参数做正则,计算和存储代价都很高

DIN 的做法是:只对当前 mini-batch 里真正出现过的稀疏特征参数施加正则约束。

直观理解就是:

  • 这一批样本没有出现的 embedding,就不在这一轮额外计算正则
  • 这一批样本访问到的 embedding,才参与当前 batch 的惩罚项

这样做的好处是:

  • 更适合超大规模稀疏参数训练
  • 降低无意义的全量正则计算开销
  • 在工程上更容易和稀疏更新框架结合

6. 数据自适应激活函数

DICE

DIN 里另一个常被提到的技巧,是数据自适应激活函数 DICE(Data Adaptive Activation Function)。

在普通 MLP 里,常见激活函数是 ReLU:

$$
\mathrm{ReLU}(x) = \max(0, x)
$$

但 CTR 场景有一个特点:

  • 不同层、不同特征维度的输入分布差异很大
  • 同一层神经元在不同 batch 下的激活分布也可能不断变化

固定形状的激活函数,有时不能很好适应这种分布漂移。

DICE 的核心思想是:

  • 不再用固定阈值把输入简单切成正半轴和负半轴
  • 而是根据当前数据分布,自适应地决定激活函数在不同区域的响应强度

可以把它理解成一种带数据感知能力的 PReLU。它会先结合输入的均值、方差做归一化判断,再用一个可学习参数控制负半区保留多少信息。

从直觉上说:

  • 如果某个神经元当前输入大多偏大,激活边界就应随之调整
  • 如果输入大多偏小,激活函数也不应死板地直接截断

这个技巧对 CTR 模型很有帮助,因为推荐场景里的特征分布往往高度非平稳,激活函数如果能随数据分布自适应变化,通常会比固定 ReLU 更灵活。

所以从工程角度看,DIN 的贡献不只是提出了兴趣激活机制,还包括:

  • 在稀疏大规模参数场景下,引入更实用的 mini-batch aware regularization
  • 在深层非线性建模里,引入更适合 CTR 分布特点的 DICE 激活函数

7. DIN 的局限

DIN 虽然把“相关兴趣激活”做出来了,但它仍然有两个重要限制:

  • 只关注当前候选 item 与历史行为的相关性,没有显式建模兴趣随时间的演化过程
  • 历史行为之间彼此独立参与 attention,缺少对序列依赖关系的建模

换句话说,DIN 更像是在回答:

  • 哪些历史行为与当前候选 item 更相关

但还没有很好回答:

  • 用户兴趣是如何一步步演变到现在的

这也是后续 DIEN 出现的原因。

DIEN

DIEN(Deep Interest Evolution Network)是在 DIN 基础上的进一步发展。它认为,仅仅做“兴趣激活”还不够,因为用户行为本身是一个时间序列,兴趣会不断形成、强化、衰减和转移。

因此,DIEN 想解决的是两个问题:

  • 如何从原始行为序列中提取更合理的“兴趣状态”
  • 如何建模这些兴趣状态随时间的演化过程

1. 为什么 DIN 还不够

在 DIN 中,历史行为 embedding 基本直接拿来和目标 item 做匹配。这样做默认了一个假设:

  • 单个行为 embedding 就足以代表用户在那个时刻的兴趣

但实际并不总是如此。

例如用户连续看了很多手机壳、充电头、数据线,这种连续行为共同表达的可能是“正在为新手机配件做选购”,单独看某一次点击并不能完整体现这一意图。

所以,DIEN 先用序列模型对行为进行编码,把“行为”提升成“兴趣状态”。

2. Interest Extractor Layer

DIEN 的第一层关键结构是 Interest Extractor Layer,通常使用 GRU 来编码用户行为序列:

$$
h_t = \mathrm{GRU}(e_t, h_{t-1})
$$

其中:

  • $e_t$ 是第 $t$ 个行为 item 的 embedding
  • $h_t$ 是第 $t$ 时刻提取出的兴趣状态

这样一来,$h_t$ 不再只是单个行为的表示,而是融合了前序上下文的信息,更接近“用户此刻的兴趣”。

DIEN 还引入了辅助损失(auxiliary loss)来增强兴趣提取效果。直观理解是:

  • 用当前兴趣状态去预测下一个真实行为
  • 同时区分真实下一个行为和负采样行为

辅助损失让中间层的兴趣状态拥有更明确的监督信号,而不是只靠最终 CTR 标签反向传播。

3. Interest Evolving Layer

提取出兴趣状态后,DIEN 还需要进一步回答:

  • 哪些兴趣状态会持续演化到当前目标相关的兴趣

因此它在第二层又使用了一次基于 GRU 的演化建模,并把目标 item 的注意力信息融入其中。

设从第一层得到兴趣状态序列:

$$
{\mathbf{h}_1,\mathbf{h}_2,\ldots,\mathbf{h}_T}
$$

先计算每个兴趣状态和候选 item 的相关性:

$$
\alpha_t = \mathrm{Att}(\mathbf{h}_t, \mathbf{e}_a)
$$

然后再把这些注意力分数注入到兴趣演化过程里,从而让演化网络更关注与目标相关的兴趣轨迹。

论文中提出了几种 attention 与 GRU 结合的方式,其中最常被提到的是 AUGRU(Attentional Update Gate GRU)。它的直观含义是:

  • 如果某个时刻的兴趣状态和目标 item 更相关,就让 GRU 更新得更明显
  • 如果相关性较弱,就减小该时刻对最终兴趣表示的影响

4. DIEN 相比 DIN 的提升

DIEN 相比 DIN,实质上多做了两件事:

  • 把“历史行为”升级成“兴趣状态序列”
  • 把“静态相关性加权”升级成“目标感知的兴趣演化建模”

因此,DIN 更像是:

  • 从历史里挑出和当前候选相关的行为

而 DIEN 更像是:

  • 先理解用户兴趣是如何形成的
  • 再挑出那条与当前目标最相关的兴趣演化路径

5. DIEN 的价值与不足

DIEN 很适合行为序列较长、兴趣变化较快的场景,因为它比 DIN 更强调时间顺序和状态转移。

但它依然有一个局限:

  • 它主要把用户行为看成一条线性的时间序列

在很多场景中,用户会在一天内反复切换兴趣,比如上午看零食,中午看耳机,晚上又回来看零食。单纯按时间顺序压成一条长序列,有时并不能很好刻画这种多会话、多兴趣跳转行为。

这就引出了 DSIN。

DSIN

DSIN(Deep Session Interest Network)进一步强调:用户的行为并不只是一个长序列,还天然带有会话(session)结构。

例如在一次连续浏览中,用户可能集中关注某一类商品;而过了一段时间后再打开 App,又进入了另一段新的兴趣会话。把这些行为全都平铺到一条长序列里,可能会稀释局部兴趣模式。

1. 核心动机

DSIN 的出发点是:

  • 用户在短时间内的一组连续行为,往往反映一个较稳定的局部兴趣
  • 不同 session 之间,兴趣可能发生切换

因此,DSIN 会先把用户行为划分为多个 session,再分别建模 session 内部兴趣与 session 之间的关系。

2. Session 划分

DSIN 通常会根据时间间隔把用户行为切成多个 session。例如:

  • 如果两次行为间隔较短,则认为属于同一次浏览会话
  • 如果间隔较长,则切到新的 session

于是原始行为序列:

$$
{\mathbf{e}_1,\mathbf{e}_2,\ldots,\mathbf{e}_T}
$$

会被划分为:

$$
\mathcal{S}_1,\mathcal{S}_2,\ldots,\mathcal{S}_K
$$

其中每个 $\mathcal{S}_k$ 表示一个局部会话。

3. Session 内兴趣建模

在每个 session 内,DSIN 会利用 self-attention 或类似机制,对该 session 中的多个行为进行建模,抽取局部兴趣表示。

直观理解是:

  • 同一个 session 内的行为通常主题更集中
  • 因此更适合在局部范围内做兴趣聚合

如果第 $k$ 个 session 的输出表示记为 $\mathbf{s}_k$,那么最终会得到一个 session 兴趣序列:

$$
{\mathbf{s}_1,\mathbf{s}_2,\ldots,\mathbf{s}_K}
$$

这一步的意义是:先把原始的 item 级行为压缩成 session 级兴趣单元。

4. Session 间演化

得到多个 session 表示后,DSIN 再使用序列模型(如 Bi-LSTM)去建模 session 之间的依赖关系,从而捕捉用户兴趣在多个会话之间的变化。

随后,再结合当前候选 item 做 attention,挑出和目标最相关的 session 兴趣:

$$
\beta_k = \mathrm{Att}(\mathbf{s}_k, \mathbf{e}_a)
$$

再对 session 兴趣加权汇总,得到最终用户表示。

与 DIN 相比,DSIN 不再直接在 item 级别做一次全局 attention;与 DIEN 相比,它也不只是沿单一行为链路建模,而是显式利用了 session 结构。

5. DSIN 适合什么场景

DSIN 特别适合:

  • 用户访问具有明显会话特征的场景
  • 用户兴趣切换频繁的电商或信息流场景
  • 长行为序列下需要降低噪声、突出局部兴趣模式的任务

因为把行为先分 session,可以减少“不同兴趣片段互相干扰”的问题。

6. 从 DIN 到 DIEN 再到 DSIN

这三类模型的演进逻辑可以概括成下面一条主线:

  • DIN:解决“历史行为不是等权的”,引入目标感知注意力
  • DIEN:解决“兴趣不是静态的”,引入兴趣提取与兴趣演化
  • DSIN:解决“行为不是单纯长序列的”,引入会话级兴趣结构

如果从建模粒度来看:

  • DIN 主要在 item 行为级别做目标相关建模
  • DIEN 在 item 行为级别上进一步建模时间演化
  • DSIN 则提升到 session 级别建模局部兴趣和跨会话迁移

7. 总结

DIN 及其后续演化,本质上都围绕一个核心问题展开:

  • 如何更真实地表示用户兴趣

早期做法往往把用户兴趣压缩成一个固定向量,而从 DIN 开始,推荐模型逐步意识到:

  • 兴趣是和目标相关的
  • 兴趣是动态变化的
  • 兴趣还可能具有分段、分会话的结构

因此,这条演进路线并不是简单地把模型做深,而是在一步步逼近真实用户行为的生成机制。

在实际工程里如何选择,也可以粗略理解为:

  • 如果重点是快速引入目标感知兴趣建模,DIN 是一个经典起点
  • 如果更关心兴趣随时间的连续演化,DIEN 更合适
  • 如果场景里 session 结构明显,DSIN 往往更有表达力

从这个角度看,DIN 系列模型的价值,不只是提升离线指标,更在于它们把“用户兴趣”从一个静态特征,逐步建模成了一个与目标、时间和会话结构都相关的动态对象。

Deng J, Wang S, Cai K, et al. Onerec: Unifying retrieve and rank with generative recommender and iterative preference alignment[J]. arXiv preprint arXiv:2502.18965, 2025.

方法介绍

本文提出 OneRec,一个端到端的单阶段生成式推荐框架,其目标是直接根据用户历史行为生成目标 session,而不再采用传统的多阶段级联排序流程。设用户历史行为序列为

$$ H_u={v_1^h,v_2^h,\dots,v_n^h} $$

其中,$v_i^h$ 表示用户产生过有效正反馈的视频,$n$ 表示历史行为长度。模型输出为一个 session 级推荐列表

$$ S={v_1,v_2,\dots,v_m} $$

其中,$m$ 表示该 session 中的视频个数。

从整体上看,OneRec 可以表示为一个从用户历史序列到目标 session 的映射函数:

$$ S := M(H_u) $$

OneRec 的方法主要由三部分构成:语义表示构建、session 级生成建模,以及基于奖励模型的迭代偏好对齐。

1. 语义表示构建

对于每个视频 $v_i$,首先利用多模态表示学习得到其连续向量表示

$$ e_i \in \mathbb{R}^d $$

为了让生成模型能够像生成文本 token 一样生成推荐结果,OneRec 不直接生成原始 item id,而是将每个视频 embedding 离散化为多层语义 ID。具体地,采用多层 residual quantization。第 1 层的初始残差定义为

$$ r_i^1 = e_i $$

在第 $l$ 层,设码本为

$$ C_l={c_1^l,c_2^l,\dots,c_K^l} $$

其中,$K$ 为该层 codebook 大小。则第 $l$ 层所选取的语义 token 索引为

$$ s_i^l=\arg\min_k \lVert r_i^l-c_k^l \rVert_2^2 $$

随后,将该层选中的聚类中心从残差中扣除,得到下一层残差:

$$ r_i^{l+1}=r_i^l-c_{s_i^l}^l $$

因此,多层语义 ID 的生成过程可以写为

$$ s_i^1 = \arg\min_k \lVert r_i^1-c_k^1 \rVert_2^2,\qquad r_i^2 = r_i^1-c_{s_i^1}^1 $$

$$ s_i^2 = \arg\min_k \lVert r_i^2-c_k^2 \rVert_2^2,\qquad r_i^3 = r_i^2-c_{s_i^2}^2 $$

$$ \cdots $$

$$ s_i^L = \arg\min_k \lVert r_i^L-c_k^L \rVert_2^2 $$

最终,每个视频被表示为一个由 $L$ 层 token 构成的语义 ID:

$$ v_i \longrightarrow (s_i^1,s_i^2,\dots,s_i^L) $$

为了避免语义 token 分布极不均衡,OneRec 采用 Balanced K-Means 构造更平衡的码本。给定物品集合 $V$ 和簇数 $K$,每个簇分配的样本数为

$$ w=\frac{\lvert V \rvert}{K} $$

Balanced K-Means 的核心约束是:每个 cluster 都尽量包含相同数量的 item,从而缓解语义编码中的分布偏斜问题。

2. Session 级列表生成

不同于传统 point-wise 推荐方法只预测下一个 item,OneRec 直接建模整个 session 的生成。用户历史序列和目标 session 都用语义 ID 序列表示:

$$ H_u={(s_1^1,s_1^2,\dots,s_1^L),(s_2^1,s_2^2,\dots,s_2^L),\dots,(s_n^1,s_n^2,\dots,s_n^L)} $$

$$ S={(s_1^1,s_1^2,\dots,s_1^L),(s_2^1,s_2^2,\dots,s_2^L),\dots,(s_m^1,s_m^2,\dots,s_m^L)} $$

模型结构采用 Encoder-Decoder Transformer。首先,Encoder 对用户历史行为进行编码,得到用户兴趣表示:

$$ H = Encoder(H_u) $$

然后,Decoder 基于编码后的用户兴趣表示,以自回归方式生成目标 session 的语义 token 序列。

为了增强模型容量,OneRec 在 Decoder 的前馈层中引入了 **Mixture of Experts (MoE)**。对于第 $l$ 层 decoder 表示 $H_t^l$,MoE 输出为

$$ H_t^{l+1} = \sum_{i=1}^{N_{\mathrm{MoE}}} g_{i,t},FFN_i(H_t^l) + H_t^l $$

其中,$N_{\mathrm{MoE}}$ 表示 expert 总数,$FFN_i(\cdot)$ 表示第 $i$ 个 expert。门控值 $g_{i,t}$ 定义为

$$ g_{i,t} = s_{i,t},\mathbb{I}!\left[s_{i,t}\in \mathrm{Topk}\big({s_{j,t}\mid 1\le j\le N},K_{\mathrm{MoE}}\big)\right] $$

并且

$$ s_{i,t}=\mathrm{Softmax}_i\big((H_t^l)^\top e_i^l\big) $$

这意味着每个 token 只会被路由到少数几个 expert 上,从而实现“参数规模大但计算稀疏”。

在训练时,在每个 item 的语义 token 前加入起始符号 $s^{[\mathrm{BOS}]}$,得到 decoder 输入序列

$$ \bar S={s^{[\mathrm{BOS}]},s_1^1,s_1^2,\dots,s_1^L,s^{[\mathrm{BOS}]},s_2^1,s_2^2,\dots,s_2^L,\dots,s^{[\mathrm{BOS}]},s_m^1,s_m^2,\dots,s_m^L} $$

模型采用 next-token prediction 训练目标,对目标 session 的语义 token 使用交叉熵损失:

$$ L_{\mathrm{NTP}} = -\sum_{i=1}^{m}\sum_{j=1}^{L} \log P\Big(s_i^{j+1}\mid[s^{[\mathrm{BOS}]},s_1^1,\dots,s_1^L,\dots,s^{[\mathrm{BOS}]},s_i^1,\dots,s_i^j];\Theta\Big) $$

经过这一阶段训练后,可以得到初始生成模型 $M_t$。

3. 奖励模型

为了进一步刻画用户对整个 session 的偏好,OneRec 引入奖励模型 $R(u,S)$。其中,$u$ 表示用户,$S$ 表示一个候选 session,奖励模型输出

$$ r = R(u,S) $$

表示该用户对该 session 的偏好程度。

对于 session 中的每个 item $v_i$,首先构造其面向用户的 target-aware 表示:

$$ e_i = v_i \odot u $$

其中,$\odot$ 表示目标感知操作。于是,整个 session 的表示可以写为

$$ h={e_1,e_2,\dots,e_m} $$

为了建模 session 内部 item 之间的关系,采用 self-attention 融合上下文信息:

$$ h_f=\mathrm{SelfAttention}(hW_s^Q,hW_s^K,hW_s^V) $$

接着,使用多个 tower 对不同目标进行预测:

$$ \hat r_{\mathrm{swt}} = Tower_{\mathrm{swt}}(Sum(h_f)) $$

$$ \hat r_{\mathrm{vtr}} = Tower_{\mathrm{vtr}}(Sum(h_f)) $$

$$ \hat r_{\mathrm{wtr}} = Tower_{\mathrm{wtr}}(Sum(h_f)) $$

$$ \hat r_{\mathrm{ltr}} = Tower_{\mathrm{ltr}}(Sum(h_f)) $$

其中

$$ Tower(\cdot)=\mathrm{Sigmoid}(\mathrm{MLP}(\cdot)) $$

奖励模型采用多目标二元交叉熵进行训练:

$$ L_{\mathrm{RM}} = -\sum_{xtr}\left[y_{xtr}\log(\hat r_{xtr}) + (1-y_{xtr})\log(1-\hat r_{xtr})\right] $$

4. 迭代偏好对齐(IPA)

在得到初始生成模型 $M_t$ 和奖励模型 $R(u,S)$ 后,OneRec 进一步采用 Direct Preference Optimization (DPO) 做偏好对齐。

对于每个用户,当前模型通过 beam search 生成 $N$ 个候选 session:

$$ S_u^n \sim M_t(H_u),\qquad \forall u\in U,; n\in[N] $$

随后,利用奖励模型为每个候选 session 打分:

$$ r_u^n = R(u,S_u^n) $$

根据得分,选出奖励最高的候选作为 winner,奖励最低的候选作为 loser:

$$ S_u^w = S_u^{\arg\max_i r_u^i},\qquad S_u^l = S_u^{\arg\min_i r_u^i} $$

从而构造偏好对

$$ D_t^{\mathrm{pairs}}=(S_u^w,S_u^l,H_u) $$

给定偏好对后,训练下一个模型 $M_{t+1}$,其 DPO 损失定义为

$$ L_{\mathrm{DPO}} = -\log \sigma\left(\beta \log \frac{M_{t+1}(S_u^w\mid H_u)}{M_t(S_u^w\mid H_u)} - \beta \log \frac{M_{t+1}(S_u^l\mid H_u)}{M_t(S_u^l\mid H_u)}\right) $$

因此,OneRec 的总训练目标可以写为

$$ L = L_{\mathrm{NTP}} + \lambda L_{\mathrm{DPO}} $$

也就是说,模型一方面通过 $L_{\mathrm{NTP}}$ 学习基本的 session 生成能力,另一方面通过 $L_{\mathrm{DPO}}$ 将生成分布进一步推向更高奖励的候选结果。

由于偏好对构造需要 beam search,计算代价较高,论文仅对一小部分样本执行 DPO 对齐。设采样比例为 $r_{\mathrm{DPO}}$,则训练时仅在

$$ \mathrm{rand()} < r_{\mathrm{DPO}} $$

时执行偏好对齐,否则仅使用 $L_{\mathrm{NTP}}$ 更新参数。

5. 方法总结

总体而言,OneRec 的建模流程可以概括为:

  1. 利用多模态 embedding 和多层残差量化,将 item 表示为语义 ID;
  2. 使用 Encoder-Decoder Transformer 对整个 session 进行自回归生成;
  3. 借助 MoE 扩展模型容量;
  4. 使用奖励模型估计 session 质量;
  5. 通过 IPA + DPO,在迭代训练中不断提升模型对高价值 session 的偏好。

因此,OneRec 可以视为一个统一了 检索、列表生成和偏好对齐 的端到端生成式推荐框架。

FNN

FNN(Factorization-machine supported Neural Network)可以看作是 FM 和 MLP 结合的一个早期代表模型。它的核心想法是:

  • FM 很擅长建模二阶特征交互
  • 但 FM 本身是浅层模型,表达能力有限
  • 所以可以先用 FM 学出一层有意义的 embedding,再把它交给深层网络

设输入向量为 $x$,FM 的预测形式为:

$$
\hat y_{FM} = w_0 + \sum_{i=1}^{n} w_i x_i + \sum_{i=1}^{n}\sum_{j=i+1}^{n}\langle v_i, v_j\rangle x_i x_j
$$

其中 $v_i$ 是第 $i$ 个特征对应的隐向量。

FNN 的关键不是直接使用 FM 的预测分数,而是使用 FM 学到的一阶参数和隐向量来初始化神经网络输入层。假设一个样本激活的 field 对应特征索引为 $i_1, i_2, \ldots, i_m$,那么 FNN 的输入表示可以写成:

$$
z_0 = [w_{i_1}, v_{i_1}, w_{i_2}, v_{i_2}, \ldots, w_{i_m}, v_{i_m}]
$$

随后再送入标准 MLP:

$$
z_1 = \sigma(W_1 z_0 + b_1)
$$

$$
z_2 = \sigma(W_2 z_1 + b_2)
$$

$$
\hat y = \mathrm{sigmoid}(W_L z_{L-1} + b_L)
$$

所以 FNN 的本质是:

  • 用 FM 解决稀疏特征 embedding 初始化问题
  • 用 DNN 继续学习更高阶的非线性交互

它的优点是思路自然,但缺点也很明显:

  • FM 和 DNN 往往是分阶段训练的
  • 不是一个完全端到端的结构

DeepFM

DeepFM 是 FNN 之后非常重要的一个模型。它解决的核心问题是:

  • 能不能不再先训练 FM、再训练 DNN
  • 能不能让低阶交叉和高阶交叉共享同一套 embedding,并联合训练

DeepFM 的做法是把 FM 部分和 Deep 部分并联起来。

1. FM 部分

给定输入向量 $x$,FM 部分包含一阶项和二阶项:

$$
y_{FM} = \sum_{i=1}^{n} w_i x_i + \sum_{i=1}^{n}\sum_{j=i+1}^{n}\langle v_i, v_j\rangle x_i x_j
$$

2. Deep 部分

Deep 部分把各个 field 的 embedding 拼接起来,作为 DNN 输入:

$$
a^{(0)} = [e_1, e_2, \ldots, e_m]
$$

然后通过多层感知机:

$$
a^{(l+1)} = \sigma(W^{(l)} a^{(l)} + b^{(l)})
$$

最终得到:

$$
y_{Deep} = W^{(L)} a^{(L)} + b^{(L)}
$$

3. 最终输出

DeepFM 把两部分相加后做 sigmoid:

$$
\hat y = \mathrm{sigmoid}(y_{FM} + y_{Deep})
$$

DeepFM 最关键的一点在于:

  • FM 部分和 Deep 部分共享同一套 feature embedding

因此模型可以同时学习:

  • 低阶显式特征交叉
  • 高阶隐式非线性交叉

而且是端到端训练的。

所以和 FNN 相比,DeepFM 更自然,也更实用。可以把它理解成:

  • FM 负责记住低阶组合模式
  • DNN 负责做更强的泛化

NFM

NFM(Neural Factorization Machine)可以理解成:先保留 FM 的二阶交互结构,再对这些交互做非线性建模。

它和 DeepFM 的区别在于:

  • DeepFM 的 Deep 部分直接对 embedding 拼接后建模
  • NFM 则先显式构造二阶交互表示,再把这个交互表示送入神经网络

1. Bi-Interaction Pooling

设样本激活的特征 embedding 为:

$$
e_1, e_2, \ldots, e_m
$$

NFM 的核心是 Bi-Interaction Pooling 层,其输出为:

$$
z_{BI} = \sum_{i=1}^{m}\sum_{j=i+1}^{m} x_i x_j (e_i \odot e_j)
$$

其中 $\odot$ 表示逐元素乘法。

这个式子也可以更高效地写成:

$$
z_{BI} = \frac{1}{2}\left[\left(\sum_{i=1}^{m} x_i e_i\right)^2 - \sum_{i=1}^{m}(x_i e_i)^2\right]
$$

这里平方表示按元素平方。

2. 神经网络部分

得到二阶交互向量后,再送入 MLP:

$$
h_1 = \sigma(W_1 z_{BI} + b_1)
$$

$$
h_2 = \sigma(W_2 h_1 + b_2)
$$

$$
\hat y = \mathrm{sigmoid}(w^{\top} h_L + b)
$$

NFM 的关键点在于:

  • FM 只能线性叠加各个二阶交互
  • NFM 则让“交互本身”再经过一层非线性组合

所以它比标准 FM 更强的地方在于:

  • 不只是学习哪些特征对有交互
  • 还学习这些交互之间更复杂的组合关系

AFM

AFM(Attentional Factorization Machine)是在 FM 上加入注意力机制的模型。它想解决的问题是:

  • FM 默认所有二阶特征交互的重要性相同
  • 但真实任务里,不同交互对预测结果的贡献显然不同

所以 AFM 的核心思想是:

  • 不是把所有交互简单相加
  • 而是给每个交互分配一个可学习的重要性权重

1. 二阶交互表示

先构造所有特征对的交互向量:

$$
z_{ij} = (e_i \odot e_j)x_i x_j,\quad i < j
$$

设全部交互集合为:

$$
\mathcal{P} = {z_{ij}\ |\ i < j}
$$

2. 注意力网络

对于每个交互向量 $z_{ij}$,AFM 先通过一个小的 attention network 计算打分:

$$
a’{ij} = h^{\top}\mathrm{ReLU}(W z{ij} + b)
$$

再通过 softmax 得到注意力权重:

$$
\alpha_{ij} = \frac{\exp(a’{ij})}{\sum{(p,q)\in \mathcal{P}} \exp(a’_{pq})}
$$

3. 加权汇聚与输出

所有交互向量按权重加权求和:

$$
z_{AFM} = \sum_{(i,j)\in \mathcal{P}} \alpha_{ij} z_{ij}
$$

最后得到预测:

$$
\hat y = \mathrm{sigmoid}\left(w_0 + \sum_{i=1}^{n} w_i x_i + p^{\top} z_{AFM}\right)
$$

4. AFM 的意义

AFM 相比 FM 的最大区别是:

  • FM 假设所有交互一视同仁
  • AFM 显式学习“哪一对特征交互更重要”

这带来两个直接好处:

  1. 表达能力更强,因为不同交互可以有不同权重
  2. 可解释性更好,因为可以直接观察注意力分布

如果把这四个模型放在一起看,可以得到一条很清晰的演化路线:

  • FNN:先用 FM 学 embedding,再送入 DNN
  • DeepFM:FM 和 DNN 并联,共享 embedding,端到端训练
  • NFM:先构造二阶交互向量,再用 MLP 学非线性
  • AFM:在二阶交互上加入 attention,学习不同交互的重要性

Vaswani A, Shazeer N, Parmar N, et al. Attention is all you need[J]. Advances in neural information processing systems, 2017, 30.

Transformer 是现代大模型最核心的基础架构之一。从 BERT、GPT 到如今的大语言模型,几乎都可以看作是在 Transformer 基础上的扩展和变体。

如果只用一句话概括它的核心思想,那就是:

  • 不再像 RNN 那样按时间步顺序处理序列
  • 而是用 self-attention 让每个 token 直接和序列中其他 token 建立联系

这种设计带来了两个非常重要的优点:

  1. 可以并行计算,训练效率更高
  2. 更容易建模长距离依赖关系

这篇文章会先把 Transformer 的整体架构讲清楚,再基于练习题中的推导,分析多头自注意力的参数量和计算复杂度。

一、为什么 Transformer 会出现

在 Transformer 之前,序列建模主要依赖两类方法:

  • RNN / LSTM / GRU
  • CNN 式序列模型

RNN 的问题在于,它天然是串行计算的。即使一个句子里后面的词和前面的词关系很远,信息也要一层层沿时间步传递。这会带来两个经典问题:

  • 长距离依赖难学
  • 训练和推理难以充分并行

CNN 虽然可以并行,但感受野需要通过堆叠层数逐渐扩大,对于全局依赖的建模仍然不够直接。

Transformer 的突破在于:让序列中每个位置都能直接“看见”其他所有位置,并根据相关性动态分配注意力权重。

因此,Transformer 的关键不是“记忆前一个状态”,而是“计算当前位置和其他位置之间的关系”。

二、整体架构

原始 Transformer 由两部分组成:

  • Encoder
  • Decoder

整体上是一个典型的 seq2seq 结构。

1. Encoder

Encoder 由多个相同的编码层堆叠而成。每一层通常包含两个子层:

  1. Multi-Head Self-Attention
  2. Position-wise Feed Forward Network

并且每个子层外面都有:

  • Residual Connection
  • Layer Normalization

因此一层 Encoder Block 的计算流程可以简化为:

$$
X \rightarrow \text{Multi-Head Attention} \rightarrow \text{Add & Norm} \rightarrow \text{FFN} \rightarrow \text{Add & Norm}
$$

2. Decoder

Decoder 同样由多个相同的解码层堆叠而成,但每层会多一个子层:

  1. Masked Multi-Head Self-Attention
  2. Cross-Attention
  3. Position-wise Feed Forward Network

其中:

  • 第一层 masked self-attention 保证当前位置只能看到自己和前面的 token,不能偷看未来信息
  • 第二层 cross-attention 让 decoder 去关注 encoder 输出的表示

因此 Decoder 比 Encoder 多了一步“从输入序列表示中取信息”的过程。

如果只讨论 GPT 这类自回归语言模型,那么通常只保留 Decoder 风格的 masked self-attention 结构;如果讨论 BERT,则更接近只用 Encoder。

三、输入表示

Transformer 的输入并不是单纯的 one-hot token,而是由两个部分相加得到:

  1. Token Embedding
  2. Positional Encoding

写成公式就是:

$$
X = E + P
$$

其中:

  • $E$ 表示 token embedding
  • $P$ 表示位置编码

这么做的原因很简单:attention 本身只关心“元素之间的关系”,它并不知道序列顺序。如果不加入位置信息,那么词序就会丢失。

位置编码为什么重要

例如下面两个句子:

  • dog bites man
  • man bites dog

如果模型完全不知道词的位置,这两个句子在 bag-of-words 意义上可能几乎一样,但语义完全不同。

因此 Transformer 必须额外注入顺序信息。

在原始论文中,位置编码使用的是正弦余弦函数:

$$
PE_{(pos, 2i)} = \sin\left(\frac{pos}{10000^{2i / d_{\text{model}}}}\right)
$$

$$
PE_{(pos, 2i+1)} = \cos\left(\frac{pos}{10000^{2i / d_{\text{model}}}}\right)
$$

它的直觉是:用不同频率的波形来编码位置,使得模型既能区分绝对位置,也能较容易推断相对位置信息。

四、自注意力机制

Transformer 的核心就是 attention,尤其是 self-attention。

1. Q、K、V 的含义

对于输入表示矩阵 $X \in \mathbb{R}^{n \times d_{\text{model}}}$,模型会分别通过三个线性映射得到:

$$
Q = XW^Q,\quad K = XW^K,\quad V = XW^V
$$

其中:

  • $Q$ 是 Query
  • $K$ 是 Key
  • $V$ 是 Value

直观地说:

  • Query 可以理解为“我现在想找什么信息”
  • Key 可以理解为“我这里提供什么信息”
  • Value 可以理解为“真正要取出的内容”

当某个位置的 Query 和另一个位置的 Key 越匹配时,它就应该更多地吸收对方的 Value。

2. Scaled Dot-Product Attention

单头注意力的公式是:

$$
\text{Attention}(Q, K, V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V
$$

这个公式可以分成三步理解。

第一步,计算相关性分数:

$$
S = QK^T
$$

这里的 $S_{ij}$ 表示第 $i$ 个 token 对第 $j$ 个 token 的关注程度。

第二步,进行缩放:

$$
\frac{QK^T}{\sqrt{d_k}}
$$

除以 $\sqrt{d_k}$ 的原因是,当向量维度变大时,点积的数值方差会变大,容易让 softmax 进入非常陡峭的区域,从而导致梯度不稳定。

第三步,做 softmax 并加权求和:

$$
A = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)
$$

$$
O = AV
$$

其中 $A$ 是注意力权重矩阵,$O$ 是当前注意力层的输出。

3. Self-Attention 的含义

当 $Q,K,V$ 都来自同一个输入序列时,这种 attention 就叫 self-attention。

它的意义是:序列中的每个 token 都可以根据当前任务需要,从整个序列的其他位置动态收集信息。

例如在句子:

  • The animal didn’t cross the street because it was tired.

中,词 it 的含义更接近 animal,而不是 street。self-attention 就允许模型直接给 animal 更高权重,而不需要像 RNN 那样跨很多步去传递状态。

五、多头注意力

单头注意力只能在一个表示子空间中建模关系,而多头注意力(Multi-Head Attention)希望让模型从多个不同角度同时观察序列。

设一共有 $h$ 个头,每个头的维度为:

$$
d_k = d_v = \frac{d_{\text{model}}}{h}
$$

第 $j$ 个头的计算为:

$$
\text{head}_j = \text{Attention}(Q_j, K_j, V_j)
$$

最后把所有头拼接起来:

$$
\text{MultiHead}(Q,K,V) = \text{Concat}(\text{head}_1, \ldots, \text{head}_h)W^O
$$

这样做的直觉是:

  • 有的头可能关注语法关系
  • 有的头可能关注实体对齐
  • 有的头可能关注局部上下文
  • 有的头可能关注长距离依赖

虽然每个头的维度更小,但多个头并行后,整体表达能力反而更强。

六、前馈网络、残差连接与 LayerNorm

Transformer 不只有 attention。每个 block 中还有一个位置独立的前馈网络:

$$
\text{FFN}(x) = W_2 \sigma(W_1 x + b_1) + b_2
$$

这个网络对每个位置分别作用,但参数共享。它的作用可以理解为:

  • attention 负责“信息交换”
  • FFN 负责“对每个位置的表示做非线性变换”

此外,每个子层后面都会配合残差连接和 LayerNorm:

$$
\text{LayerNorm}(x + \text{Sublayer}(x))
$$

残差连接有助于深层网络训练,LayerNorm 有助于稳定表示分布。

七、为什么 Transformer 训练得更快

Transformer 相比 RNN 的一个重大优势是并行性。

RNN 的状态更新是:

  • 第 $t$ 步依赖第 $t-1$ 步

所以天然串行。

而 Transformer 在训练时可以一次性拿到整个序列,直接并行计算:

  • 所有位置的 $Q,K,V$
  • 所有位置之间的相关性矩阵
  • 所有位置的注意力输出

因此在 GPU 上训练效率会高很多。

不过,这种全局两两交互也带来了新的问题:attention 的复杂度会随着序列长度平方增长。

这正是练习题里重点分析的部分。

八、多头自注意力的参数量与计算复杂度

下面基于练习题中的设定,分析一个 encoder 中单个 multi-head self-attention block 的参数量与 FLOPs。

设:

  • 序列长度为 $n$
  • 模型维度为 $d_{\text{model}}$
  • 头数为 $h$
  • 每个头的维度为 $d_k = d_v = d_{\text{model}} / h$

输入张量形状为:

$$
(1, n, d_{\text{model}})
$$

这里默认 batch size 为 1。

1. 参数量

一个多头注意力块中,主要有四个线性投影矩阵:

  • $W^Q \in \mathbb{R}^{d_{\text{model}} \times d_{\text{model}}}$
  • $W^K \in \mathbb{R}^{d_{\text{model}} \times d_{\text{model}}}$
  • $W^V \in \mathbb{R}^{d_{\text{model}} \times d_{\text{model}}}$
  • $W^O \in \mathbb{R}^{d_{\text{model}} \times d_{\text{model}}}$

因此:

$$
\mathrm{Params} = 4 d_{\text{model}}^2
$$

这个结果和头数 $h$ 无关。原因在于,多头拆分本质上只是 reshape,参数仍然集中存在于这四个总投影矩阵中。

2. FLOPs 推导

下面只看前向传播的主要计算量,并采用题目中的近似规则:

  • 一个 $m \times k$ 与 $k \times p$ 的矩阵乘法,记为 $2mkp$ FLOPs
  • softmax、缩放等逐元素操作近似记为低阶项

第一步:计算 Q、K、V 投影

输入 $X$ 的形状是 $(n, d_{\text{model}})$,乘上投影矩阵 $(d_{\text{model}}, d_{\text{model}})$。

每次投影代价为:

$$
2 n d_{\text{model}}^2
$$

三次投影合计:

$$
6 n d_{\text{model}}^2
$$

第二步:计算注意力分数 $QK^T$

对于每个头,$Q_{\text{head}}$ 的形状为 $(n, d_k)$,$K_{\text{head}}^T$ 的形状为 $(d_k, n)$。

每个头的代价为:

$$
2 n^2 d_k
$$

一共有 $h$ 个头,因此总代价为:

$$
h \cdot 2 n^2 d_k = h \cdot 2 n^2 \frac{d_{\text{model}}}{h} = 2 n^2 d_{\text{model}}
$$

第三步:缩放和 softmax

这一部分作用在 $(n,n,h)$ 的注意力分数张量上,近似记作:

$$
O(n^2 h)
$$

第四步:注意力权重与 V 相乘

对于每个头,注意力矩阵形状为 $(n,n)$,$V_{\text{head}}$ 形状为 $(n,d_k)$。

每个头的代价为:

$$
2 n^2 d_k
$$

所有头加起来:

$$
2 n^2 d_{\text{model}}
$$

第五步:输出投影

拼接所有头之后,输出形状回到 $(n,d_{\text{model}})$,再乘上 $W^O$,代价为:

$$
2 n d_{\text{model}}^2
$$

3. 总 FLOPs

把各部分加起来:

$$
6 n d_{\text{model}}^2 + 2 n^2 d_{\text{model}} + O(n^2 h) + 2 n^2 d_{\text{model}} + 2 n d_{\text{model}}^2
$$

整理得到:

$$
8 n d_{\text{model}}^2 + 4 n^2 d_{\text{model}} + O(n^2 h)
$$

如果忽略低阶项,那么主要复杂度就是:

$$
8 n d_{\text{model}}^2 + 4 n^2 d_{\text{model}}
$$

4. 哪一部分是瓶颈

从上式可以看出,多头自注意力中主要有两类大头开销:

  1. 线性投影相关的 $n d_{\text{model}}^2$
  2. 注意力相关的 $n^2 d_{\text{model}}$

因此瓶颈取决于:

  • 是 $d_{\text{model}}$ 更大
  • 还是序列长度 $n$ 更大

当 $d_{\text{model}} \gg n$ 时

线性投影更贵,因为:

$$
n d_{\text{model}}^2
$$

会显著大于:

$$
n^2 d_{\text{model}}
$$

例如中等长度序列、但模型宽度很大时,Q/K/V 和输出投影往往是主要成本。

当 $n \gg d_{\text{model}}$ 时

注意力分数计算和 attention 与 V 的矩阵乘法会成为瓶颈,因为它们都含有:

$$
n^2 d_{\text{model}}
$$

这也是为什么超长上下文会让 Transformer 的计算和显存开销迅速爆炸。

所以 Transformer 的核心效率问题,本质上就在于:

  • 模型宽度决定了投影层成本
  • 序列长度决定了 attention 的平方复杂度成本

九、Transformer 的优点与局限

优点

  • 可以高效并行训练
  • 更容易建模长距离依赖
  • 架构统一,扩展性非常强
  • self-attention 的交互方式非常灵活

局限

  • attention 对长序列是平方复杂度
  • 显存消耗大
  • 对训练数据和算力依赖较强
  • 原始位置编码方式并不一定最适合所有任务

也正因为这些问题,后续出现了大量改进方向,例如:

  • 稀疏注意力
  • 线性注意力
  • 相对位置编码
  • FlashAttention
  • MoE 结构

但无论具体怎么改,很多工作依然是在 Transformer 主干上演化。

十、小结

Transformer 的核心不是“把 RNN 换成更深的网络”,而是重新定义了序列建模的基本方式:

  • 用 self-attention 代替递归状态传递
  • 用多头机制在不同子空间中并行建模关系
  • 用残差连接、LayerNorm 和 FFN 组成稳定的堆叠结构

从这次练习题里的复杂度推导也可以看到,Transformer 的成功并不意味着它没有代价。它一方面通过并行计算大幅提升了训练效率,另一方面也因为 attention 的全局两两交互,引入了明显的平方复杂度瓶颈。

所以理解 Transformer,不能只停留在“attention 很强”这句话上,而要真正理解三件事:

  1. 它为什么能替代 RNN
  2. 它的每个模块到底在做什么
  3. 它的计算瓶颈到底来自哪里

这三点想明白之后,再去看 BERT、GPT、ViT 或各种高效注意力变体,就会容易很多。

提升树

提升树(Boosting Tree)本质上是一类加法模型。它不是只训练一棵树,而是按轮次训练很多棵弱学习器,并把它们逐步叠加起来。

如果把第 $m$ 轮训练得到的树记作 $h_m(x)$,那么最终模型可以写成:

$$
F_M(x) = \sum_{m=1}^{M} \alpha_m h_m(x)
$$

其中:

  • $h_m(x)$ 表示第 $m$ 棵树
  • $\alpha_m$ 表示该树的权重
  • $F_M(x)$ 表示最终集成后的预测函数

提升方法的核心不是“一次把问题学完”,而是:

  • 先训练一个较弱的模型
  • 观察当前模型还没学好的部分
  • 再让下一轮模型去修正前一轮的误差

如果是平方损失回归问题,当前模型 $F_{m-1}(x)$ 在样本 $x_i$ 上的残差为:

$$
r_{im} = y_i - F_{m-1}(x_i)
$$

于是第 $m$ 轮训练的树,就是去逼近这些残差:

$$
h_m(x) \approx r_{im}
$$

模型更新为:

$$
F_m(x) = F_{m-1}(x) + \alpha_m h_m(x)
$$

所以提升树可以理解成一种“逐轮纠错”的方法。前面的树先把大体结构学出来,后面的树不断补充前面没拟合好的部分。

梯度提升树

GBDT(Gradient Boosting Decision Tree)是在提升树上的进一步推广。它最关键的地方在于:

  • 不再局限于直接拟合残差
  • 而是统一地去拟合损失函数对当前模型输出的负梯度

这样一来,GBDT 就不只适用于平方损失回归,也能自然用于分类、排序等任务。

1. 加法模型

GBDT 仍然采用加法模型。设第 $m$ 轮新增的树为 $h_m(x)$,则:

$$
F_M(x) = F_0(x) + \sum_{m=1}^{M} \nu \gamma_m h_m(x)
$$

其中:

  • $F_0(x)$ 是初始模型
  • $h_m(x)$ 是第 $m$ 轮训练得到的回归树
  • $\gamma_m$ 是该轮步长
  • $\nu \in (0,1]$ 是学习率,也叫 shrinkage

2. 优化目标

给定训练集 ${(x_i, y_i)}_{i=1}^{N}$,GBDT 希望最小化经验风险:

$$
\mathcal{L} = \sum_{i=1}^{N} L(y_i, F(x_i))
$$

这里 $L(y_i, F(x_i))$ 是单样本损失。

由于模型是逐步加出来的,所以第 $m$ 轮就是在已有模型 $F_{m-1}(x)$ 的基础上,再增加一个新函数,使总损失下降:

$$
F_m(x) = F_{m-1}(x) + \nu \gamma_m h_m(x)
$$

3. 负梯度思想

在函数空间里做梯度下降时,当前样本的负梯度定义为:

$$
g_{im} = - \frac{\partial L(y_i, F(x_i))}{\partial F(x_i)}, \quad \text{其中 } F(x)=F_{m-1}(x)
$$

GBDT 每一轮的关键动作就是:

  • 先计算所有样本在当前模型下的负梯度 $g_{im}$
  • 再用一棵回归树去拟合这些负梯度

也就是说,第 $m$ 轮不是直接去拟合标签 $y_i$,而是拟合:

$$
h_m(x) \approx g_{im}
$$

这就是“梯度提升”的本质。

4. 为什么平方损失下等价于拟合残差

如果损失函数使用平方误差:

$$
L(y_i, F(x_i)) = \frac{1}{2}(y_i - F(x_i))^2
$$

那么对 $F(x_i)$ 求导可得:

$$
\frac{\partial L(y_i, F(x_i))}{\partial F(x_i)} = F(x_i) - y_i
$$

于是负梯度就是:

$$
g_{im} = y_i - F_{m-1}(x_i)
$$

这正好就是残差。所以普通的“拟合残差的提升树”,其实可以看成是 GBDT 在平方损失下的一个特例。

5. 每轮训练流程

GBDT 一轮训练可以拆成下面几步:

  1. 给定当前模型 $F_{m-1}(x)$
  2. 计算每个样本的伪残差,也就是负梯度

$$
g_{im} = - \frac{\partial L(y_i, F(x_i))}{\partial F(x_i)}, \quad \text{其中 } F(x)=F_{m-1}(x)
$$

  1. 用样本对 $(x_i, g_{im})$,$i=1,2,\ldots,N$ 拟合一棵回归树,得到若干叶子区域 $R_{jm}, j=1,\dots,J$
  2. 对每个叶子区域求最优输出值

$$
\gamma_{jm} = \arg\min_{\gamma} \sum_{x_i \in R_{jm}} L\big(y_i, F_{m-1}(x_i) + \gamma\big)
$$

  1. 更新模型

$$
F_m(x) = F_{m-1}(x) + \nu \sum_{j=1}^{J} \gamma_{jm} I(x \in R_{jm})
$$

其中 $I(\cdot)$ 是示性函数,表示样本是否落在第 $j$ 个叶子区域。

6. 初始模型

GBDT 通常先用一个常数作为初始模型:

$$
F_0(x) = \arg\min_{\gamma} \sum_{i=1}^{N} L(y_i, \gamma)
$$

它的含义是:在还没有任何树之前,先找一个能让整体损失最小的常数起点。

常见情况下:

  • 平方损失回归时,$F_0$ 一般是样本均值
  • 二分类对数损失时,$F_0$ 一般对应正样本比例的 logit

7. 二分类中的 GBDT

在二分类中,常见损失是 logistic loss。若标签 $y_i \in {0,1}$,设:

$$
p_i = \sigma(F(x_i)) = \frac{1}{1 + e^{-F(x_i)}}
$$

则交叉熵损失可写为:

$$
L(y_i, F(x_i)) = -\big[y_i \log p_i + (1-y_i)\log(1-p_i)\big]
$$

其对模型输出的导数为:

$$
\frac{\partial L}{\partial F(x_i)} = p_i - y_i
$$

所以负梯度为:

$$
g_{im} = y_i - p_i
$$

这里的形式仍然很像残差,但它表示的是“真实标签与当前预测概率之间的差距”。

8. GBDT 的特点

GBDT 在表格数据上长期非常强,原因主要有:

  • 能处理非线性关系
  • 能自动学习特征组合
  • 对特征尺度不太敏感
  • 对数值特征和离散特征都比较友好

它的缺点也很明显:

  • Boosting 是串行训练,训练速度通常不如 bagging 系模型容易并行
  • 参数较多,比如树深、树数、叶子数、学习率
  • 树太深或迭代轮数太多时容易过拟合

一个常见经验是:

  • 学习率调小一些
  • 树的数量加多一些
  • 再配合样本采样和特征采样

通常会比“大步快跑”的配置更稳。

用GBDT+LR进行粗排

GBDT + LR 是推荐和广告系统里非常经典的一种粗排方案。它的核心思路是:

  • 用 GBDT 自动挖掘非线性特征组合
  • 再把这些组合特征交给 LR 做最终打分

1. 为什么要结合 LR

逻辑回归的形式很简单:

$$
\hat y = \sigma(w^\top x + b)
$$

它非常擅长处理高维稀疏特征,但天然是线性的。如果要让 LR 学到复杂交叉,通常只能依赖人工做特征工程。

而 GBDT 很擅长把特征空间切成很多局部区域。一条从根节点到叶子节点的路径,本质上就是一组规则的组合,例如:

  • 年龄 < 25
  • 近7天点击次数 > 3
  • 品类 = 数码

当样本落入某个叶子节点时,就说明它满足了一组非线性条件。所以“叶子节点”本身就可以看成自动学习得到的组合特征。

2. 特征转换方式

假设训练好的 GBDT 一共有 $M$ 棵树,第 $m$ 棵树有 $J_m$ 个叶子节点。对于一个样本 $x$,它在第 $m$ 棵树中只会落入一个叶子。

于是可以把第 $m$ 棵树的输出编码成 one-hot 向量:

$$
\phi_m(x) \in {0,1}^{J_m}
$$

并且满足:

$$
\sum_{j=1}^{J_m} \phi_{mj}(x) = 1
$$

把所有树的叶子 one-hot 拼接起来,得到新的稀疏特征:

$$
\phi(x) = [\phi_1(x), \phi_2(x), \ldots, \phi_M(x)]
$$

然后再把它送给 LR:

$$
\hat y = \sigma(w^\top \phi(x) + b)
$$

3. 它为什么有效

因为每一个叶子节点都代表了一种局部模式,所以 GBDT + LR 相当于:

  • 先用 GBDT 做自动分桶和高阶交叉
  • 再用 LR 对这些交叉模式做线性组合

这比人工枚举交叉特征要省很多工作量,而且在工业场景里通常更稳定。

4. 训练流程

工程上常见的流程如下:

  1. 用原始特征训练一个 GBDT
  2. 对每个样本,取它在每棵树上的叶子编号
  3. 把所有叶子编号展开成 one-hot 稀疏向量
  4. 用这个新的稀疏向量训练 LR

如果总叶子数为 $\sum_{m=1}^{M} J_m$,那么 LR 的参数维度就是:

$$
w \in \mathbb{R}^{\sum_{m=1}^{M} J_m}
$$

5. 为什么适合粗排

粗排阶段通常希望:

  • 比纯 LR 有更强的非线性能力
  • 在线推断又不能太重

GBDT + LR 很符合这个诉求。因为:

  • GBDT 可以离线完成复杂模式学习
  • 在线阶段只需要走树拿叶子,再做一次稀疏 LR 打分

虽然现在很多系统会进一步升级到 FM / DeepFM / DCN 这类模型,但 GBDT + LR 依然是理解工业排序模型演化路径时非常重要的一环。

用 LightGBM 的 LambdaRank 方法进行粗排

如果任务目标不是点估计 CTR,而是对一个候选列表做排序,那么普通的 pointwise 分类目标往往不够。因为排序任务真正关心的是:

  • 哪些样本应该排在前面
  • 把两个样本交换位置之后,排序指标会变化多少

LambdaRank 就是为这种目标设计的一类排序方法,而 LightGBM 提供了非常成熟的 lambdarank 实现。

1. 排序问题的本质

对于同一个 query 或同一个用户请求下的一组候选样本,设模型对第 $i$ 个样本输出分数 $s_i$。最终排序就是按这些分数从大到小排列。

排序指标如 NDCG@K 更关心前几位是否排对,因此它和普通分类损失的关注点并不一致。

LambdaRank 的核心思想是:

  • 不直接只看单个样本
  • 而是看样本对 $(i, j)$ 的相对顺序
  • 如果样本 $i$ 应该排在 $j$ 前面,就推动 $s_i > s_j$
  • 并且这种推动力度要和排序指标收益相关

2. pairwise 概率形式

若在同一个 group 中,样本 $i$ 的相关性高于样本 $j$,则希望:

$$
s_i > s_j
$$

RankNet 中常见的 pairwise 概率写法为:

$$
P_{ij} = \sigma(s_i - s_j) = \frac{1}{1 + e^{-(s_i - s_j)}}
$$

对应的 pairwise loss 可以写成:

$$
L_{ij} = \log\big(1 + e^{-(s_i - s_j)}\big)
$$

如果只优化这个损失,本质上只是做成对排序,还没有显式考虑 NDCG

3. Lambda 的核心思想

LambdaRank 的关键是:如果交换样本 $i$ 和 $j$ 的位置会带来更大的指标变化,那么这对样本在训练时就应该被赋予更大的梯度权重。

设交换它们后带来的指标变化为:

$$
\Delta \mathrm{NDCG}_{ij}
$$

那么常见的 lambda 形式可以写成:

$$
\lambda_{ij} = - \frac{\partial L_{ij}}{\partial s_i} \cdot |\Delta \mathrm{NDCG}_{ij}|
$$

先看导数本身:

$$
\frac{\partial L_{ij}}{\partial s_i} = -\frac{1}{1 + e^{s_i - s_j}}
$$

因此负导数为:

$$
-\frac{\partial L_{ij}}{\partial s_i} = \frac{1}{1 + e^{s_i - s_j}}
$$

所以可以写成:

$$
\lambda_{ij} = \frac{|\Delta \mathrm{NDCG}_{ij}|}{1 + e^{s_i - s_j}}
$$

对于样本 $i$,它最终接收到的梯度信号可以理解成相关样本对贡献的加总:

$$
\lambda_i = \sum_{j:(i,j)} \lambda_{ij} - \sum_{j:(j,i)} \lambda_{ji}
$$

LightGBM 在 lambdarank 训练时,本质上就是利用这些 lambda 值来构造树的分裂和更新方向。

4. 为什么适合粗排

推荐和搜索里的粗排,常常更关心:

  • top-k 的结果是否尽量靠前
  • 强相关内容有没有被压到后面

lambdarank 的优势就在于:

  • 它优化目标更接近实际线上排序指标
  • 它关注列表内部的相对顺序,而不是单点分类误差
  • 它会更重视那些影响前排结果的样本对

5. LightGBM 中的数据组织

使用 LightGBMlambdarank 时,训练数据一般按 group 组织:

  • 每个 group 对应一个 query、一次搜索请求,或一次推荐曝光集合
  • group 内部样本一起参与排序损失计算

如果共有 $Q$ 个 group,第 $q$ 个 group 的样本集合为 $\mathcal{G}_q$,那么整体目标可以理解为:

$$
\mathcal{L} = \sum_{q=1}^{Q} \mathcal{L}_{rank}(\mathcal{G}_q)
$$

其中每个 group 内部再基于样本对和 NDCG 增益计算 lambda。

MIND

Li C, Liu Z, Wu M, et al. Multi-interest network with dynamic routing for recommendation at Tmall[C]//Proceedings of the 28th ACM international conference on information and knowledge management. 2019: 2615-2623.

传统召回模型通常把用户压成一个向量,但单向量很难表达用户的多兴趣结构。MIND 的核心做法是:把用户历史行为映射成多个兴趣向量,再让目标 item 选择最相关的那个兴趣参与训练。

MIND

胶囊网络介绍

capsule network0

MIND 借鉴了胶囊网络里的动态路由思想。这里可以把:

  • 用户历史行为向量看成低层 capsule
  • 用户兴趣向量看成高层 capsule

动态路由的目标不是简单 pooling,而是把行为向量自适应地分配到不同兴趣上。

capsule network

多兴趣提取层

设用户行为序列对应的 item embedding 为:

$$
E = {e_1, e_2, \ldots, e_n}
$$

MIND 希望从中提取出 $K$ 个兴趣向量:

$$
V = {v_1, v_2, \ldots, v_K}
$$

其中 $v_j \in \mathbb{R}^d$ 表示第 $j$ 个兴趣 capsule。

先把行为向量做线性映射:

$$
\hat{e}_{ij} = W_j e_i
$$

其中 $W_j$ 是第 $j$ 个兴趣 capsule 对应的变换矩阵。

然后通过 routing logit $b_{ij}$ 计算行为 $i$ 分配给兴趣 $j$ 的权重:

$$
c_{ij} = \frac{\exp(b_{ij})}{\sum_{k=1}^{K}\exp(b_{ik})}
$$

接着聚合得到第 $j$ 个兴趣 capsule 的输入:

$$
s_j = \sum_{i=1}^{n} c_{ij}\hat{e}_{ij}
$$

再经过 squash 函数得到兴趣向量:

$$
v_j = \mathrm{squash}(s_j)
= \frac{|s_j|^2}{1+|s_j|^2}\frac{s_j}{|s_j|}
$$

最后根据 agreement 更新 routing logit:

$$
b_{ij} \leftarrow b_{ij} + \hat{e}_{ij}^{\top} v_j
$$

重复若干轮后,相似行为会被分到同一个兴趣 capsule 中。

Label-Aware 注意力层

设目标 item embedding 为 $e_t$。MIND 用目标 item 和多个兴趣向量计算注意力权重,从而选出和当前目标最相关的兴趣。

先计算相似度分数:

$$
g_j = v_j^{\top} e_t
$$

再通过幂指数放大差异:

$$
a_j = \frac{\exp(g_j^p)}{\sum_{k=1}^{K}\exp(g_k^p)}
$$

其中 $p$ 是可调超参数。$p$ 越大,注意力越接近 hard attention。

最后得到与目标 item 对齐的用户表示:

$$
u = \sum_{j=1}^{K} a_j v_j
$$

这一步的作用是:训练时不再让一个目标 item 同时对齐所有兴趣,而是重点更新最相关的那个兴趣表示。

训练流程

训练时,先用动态路由得到多个兴趣向量,再用目标 item 通过 Label-Aware Attention 得到用户表示 $u$。随后计算用户和目标 item 的匹配分数:

$$
z = u^{\top} e_t
$$

如果采用 sampled softmax,训练目标可以写成:

$$
P(i_t \mid u) = \frac{\exp(u^{\top} e_t)}{\sum_{i \in \mathcal{I}}\exp(u^{\top} e_i)}
$$

$$
\mathcal{L} = - \log P(i_t \mid u)
$$

在线召回时,用户最终不再对应一个向量,而是对应多个兴趣向量 $v_1, \ldots, v_K$。可以让每个兴趣向量分别去检索候选 item,再把结果合并。这样比单向量用户表示更容易覆盖用户的多样化兴趣。

SDM

Lv F, Jin T, Yu C, et al. SDM: Sequential deep matching model for online large-scale recommender system[C]//Proceedings of the 28th ACM international conference on information and knowledge management. 2019: 2635-2643.

MIND解决了兴趣“广度”的问题,但新的问题随之而来:时间。 用户兴趣不仅是多元的,还是动态演化的。一个用户在一次购物会话中的行为,往往比他一个月前的行为更能预示他下一刻的需求。MIND虽然能捕捉多个兴趣,但并未在结构上显式地区分它们的时效性。序列深度匹配模型(SDM) 正是为了解决这一问题而提出的。SDM模型的核心思想是分别建模用户的短期即时兴趣和长期稳定偏好,然后智能地融合它们。

SDM

捕捉短期兴趣

SDM 用用户最近的一段行为序列来建模短期兴趣。根据图中的结构,短期路径由 LSTM + Multi-head Self-Attention + AttnNet 组成。设最近 $T$ 个行为对应的 item embedding 为:

$$
S = {e_1, e_2, \ldots, e_T}
$$

先用 LSTM 编码顺序信息,第 $t$ 步隐状态为:

$$
h_t = \mathrm{LSTM}(e_t, h_{t-1})
$$

然后用多头自注意力进一步建模行为之间的依赖关系。若记

$$
H = [h_1, h_2, \ldots, h_T]
$$

则自注意力输出可以写成:

$$
\tilde{H} = \mathrm{MultiHead}(H, H, H)
$$

其中 $\tilde{H} = [\tilde{h}_1, \tilde{h}_2, \ldots, \tilde{h}_T]$。

接下来,图中的 AttnNet 会使用查询向量 $e_u$ 对这些时序表示做注意力汇聚。其打分形式可以写成:

$$
r_t = \mathrm{score}(\tilde{h}_t, e_u)
$$

$$
\alpha_t = \frac{\exp(r_t)}{\sum_{j=1}^{T}\exp(r_j)}
$$

最终得到短期兴趣表示:

$$
s_t^u = \sum_{t=1}^{T}\alpha_t \tilde{h}_t
$$

其中 $e_u$ 可以理解为当前匹配任务对应的查询向量,AttnNet 会让和当前目标更相关的近期行为获得更大的权重。

捕捉长期兴趣

短期兴趣描述“此刻想买什么”,长期兴趣描述“这个人稳定偏好什么”。从图里可以看到,SDM 的长期兴趣并不是直接从长序列中提,而是从多路用户画像特征中提取,包括:

  • user id
  • leaf
  • cate
  • brand
  • shop

记这些特征集合分别为 L_id^uL_leaf^uL_cate^uL_brand^uL_shop^u

每一路特征都会经过一个 AttnNet,并共享查询向量 $e_u$。以第 $m$ 路特征集合 $L_m^u = {x_1^m, x_2^m, \ldots, x_{N_m}^m}$ 为例,其注意力权重为:

$$
\beta_i^m = \frac{\exp(\mathrm{score}(x_i^m, e_u))}{\sum_{j=1}^{N_m}\exp(\mathrm{score}(x_j^m, e_u))}
$$

对应的第 $m$ 路长期偏好表示为:

$$
p_m^u = \sum_{i=1}^{N_m} \beta_i^m x_i^m
$$

最后把多路画像表示拼接后经过一层全连接,得到长期兴趣表示:

$$
p^u = \phi\left(W_p [p_{id}^u; p_{leaf}^u; p_{cate}^u; p_{brand}^u; p_{shop}^u] + b_p\right)
$$

其中 $\phi(\cdot)$ 表示图中的 Dense 层变换。

长短期兴趣融合

有了短期兴趣 $s_t^u$ 和长期兴趣 $p^u$ 之后,SDM 使用 fusion gate 做自适应融合,而不是固定加权。

从图里可以看出,门控值 $G_t^u$ 同时依赖:

  • 长期兴趣 $p^u$
  • 短期兴趣 $s_t^u$
  • 查询向量 $e_u$

因此可以写成:

$$
G_t^u = \sigma\left(W_g [p^u; s_t^u; e_u] + b_g\right)
$$

其中 $G_t^u \in (0,1)^d$ 表示逐维门控权重。根据图中的 1-、逐元素乘法和逐元素加法,最终输出表示为:

$$
o_t^u = G_t^u \odot s_t^u + (1 - G_t^u) \odot p^u
$$

其中 $\odot$ 表示逐元素乘法。

得到最终用户表示 $o_t^u$ 后,就可以和目标 item 做匹配。若目标 item 向量记作 $v_i$,则打分函数可以写成:

$$
z_i = {o_t^u}^{\top} v_i
$$

如果采用 softmax 形式训练,则目标概率为:

$$
P(i_t \mid o_t^u) = \frac{\exp({o_t^u}^{\top} v_{i_t})}{\sum_{i \in \mathcal{I}} \exp({o_t^u}^{\top} v_i)}
$$

对应损失函数为:

$$
\mathcal{L} = -\log P(i_t \mid o_t^u)
$$

所以 SDM 的核心可以概括成:

  • 短期路径刻画最近行为的时序依赖
  • 长期路径从多路画像特征中提取稳定偏好
  • fusion gate 根据当前查询向量动态平衡短期和长期兴趣

Word2vec

如果只看方法本身,word2vec 其实不复杂。它最核心的内容只有两种训练方式:

  1. CBOW
  2. Skip-gram

任务目标是生成一个嵌入词表,每一个词对应词表中的一行嵌入向量。

word2vec

CBOW:用上下文预测中心词

CBOW 的全称是 Continuous Bag of Words。

它的训练目标是:

  • 已知上下文词
  • 预测中间的中心词

1. 一个具体样本怎么构造

还是用句子:

  • I like deep learning models

如果窗口大小取 2,并把 deep 当作中心词,那么一个训练样本可以写成:

  • 输入:I, like, learning, models
  • 输出:deep

也就是说,CBOW 是把上下文聚合起来,去预测被遮住的中心词。

2. 训练流程

cbow

CBOW 的训练流程可以概括成四步:

  1. 把上下文词都表示成 one-hot
  2. 通过 embedding 矩阵查出它们对应的向量
  3. 对这些上下文向量做平均或求和
  4. 用这个聚合向量去预测中心词

假设词表大小是 V,embedding 维度是 d,embedding 矩阵记为:

$$
W \in \mathbb{R}^{V \times d}
$$

对于上下文词 w1, w2, ..., wk,先查出各自的词向量:

  • v1, v2, ..., vk

然后把它们聚合成一个上下文表示:

$$
h = \frac{v_1 + v_2 + \cdots + v_k}{k}
$$

再用这个 h 去预测中心词 wc 的概率分布:

$$
P(w_c \mid \text{context})
$$

如果采用 softmax,那么训练目标可以写成最小化负对数似然:

$$
L = -\log P(w_c \mid \text{context})
$$

3. 直观理解

CBOW 可以理解成一种“完形填空”:

  • 给你周围几个词
  • 让你猜中间最可能是什么词

如果模型经常看到:

  • I like _ learning models

它就会逐渐学到:

  • 在这种上下文里,deep 出现的概率应该比较高

于是 deep 的词向量就会被训练成更适合这个上下文的位置。

4. CBOW 的特点

CBOW 的特点是:

  • 训练速度通常更快
  • 对高频词更稳定
  • 因为把多个上下文词做平均,所以目标比较平滑

它更像是:

  • 用上下文的整体语义去猜中心词

Skip-gram:用中心词预测上下文

Skip-gram 和 CBOW 的方向正好相反。

它的训练目标是:

  • 已知中心词
  • 预测它周围的上下文词

1. 一个具体样本怎么构造

还是句子:

  • I like deep learning models

如果把 deep 当作中心词,窗口大小还是 2,那么它会被拆成多个训练对子:

  • (deep -> I)
  • (deep -> like)
  • (deep -> learning)
  • (deep -> models)

也就是说:

  • CBOW 是“多个上下文 -> 一个中心词”
  • Skip-gram 是“一个中心词 -> 多个上下文词”

2. 训练流程

skipgram

Skip-gram 的训练过程也可以概括成四步:

  1. 输入中心词的 one-hot
  2. 从 embedding 矩阵中取出中心词向量
  3. 用这个向量分别去预测窗口中的每个上下文词
  4. 对多个上下文词的损失求和

假设中心词是 wi,它对应的输入向量记为 vi

对于一个目标上下文词 wo,模型要最大化:

$$
P(w_o \mid w_i)
$$

如果使用 softmax,这个概率写成:

$$
P(w_o \mid w_i) = \frac{\exp(v_o^T v_i)}{\sum_{w=1}^{V} \exp(v_w^T v_i)}
$$

其中:

  • vi 是中心词的输入向量
  • vo 是目标上下文词的输出向量

这个公式的意思其实很简单:

  • 如果中心词和上下文词真的经常一起出现
  • 那么它们的向量点积就应该更大
  • 点积越大,被 softmax 分到的概率就越高

如果窗口内的上下文词集合记为 C(wi),那么 Skip-gram 的训练目标可以写成:

$$
L = - \sum_{w_o \in C(w_i)} \log P(w_o \mid w_i)
$$

3. 直观理解

Skip-gram 可以理解成一种“扩散预测”:

  • 给你一个词
  • 让你去猜它周围最可能出现哪些词

例如给定 deep,模型会学习:

  • 它附近更可能出现 learning
  • 也可能出现 models
  • 不太可能出现完全无关的词

这样训练多了之后,词向量就会逐渐把共现关系编码进去。

4. Skip-gram 的特点

Skip-gram 的特点是:

  • 对低频词往往更友好
  • 更容易学到细粒度的共现关系
  • 一个中心词会拆成多个训练样本,因此训练信号更细

所以在很多语料上,Skip-gram 学出的词向量会更有表现力。

负采样:用更便宜的损失替代完整 softmax

原始 Skip-gram 的 softmax 最大问题是:

  • 分母要对整个词表求和

词表一大,训练代价就非常高。

负采样的做法是把问题改写成二分类任务。对于一个真实共现的词对 (wi, wo),把它当成正样本;再随机采样 K 个负样本 w1^- , ..., wK^-

这时单个样本的损失函数可以写成:

$$
L = -\log \sigma(v_o^T v_i) - \sum_{j=1}^{K} \log \sigma\left(-{v_j^-}^T v_i\right)
$$

其中:

  • σ 是 sigmoid 函数
  • vi 是中心词向量
  • vo 是正样本上下文词向量
  • v_j^- 是第 j 个负样本词向量

这个损失的含义很直接:

  • 对正样本,希望 vo^T vi 越大越好
  • 对负样本,希望 v_j^- ^T vi 越小越好

也就是说,训练目标就是:

  • 把真实共现的词拉近
  • 把随机采样的无关词推远

这样一来,每次更新只需要处理:

  • 1 个正样本
  • K 个负样本

而不需要遍历整个词表,所以训练会快很多。

实际中大家常说的:

  • Skip-gram with Negative Sampling

本质上就是:

  • Skip-gram 定义预测任务

  • Negative Sampling 定义高效可训练的损失函数

  • (deep, learning)

当作正样本。

同时再随机采一些负样本,比如:

  • (deep, banana)
  • (deep, railway)
  • (deep, window)

训练时模型会被推动去学习:

  • deeplearning 的点积应该更大
  • deepbananarailwaywindow 的点积应该更小

这样训练多了以后,“经常共现的词彼此更接近”这个结构就会在 embedding 空间中逐渐形成。

Item2vec

其实就是Word2vec在Item上的实现,可以用于构建物品塔

  • 词语 → 物品

  • 句子 → 用户交互序列

  • 词语共现 → 物品共同被用户交互

Item2vec和Word2vec唯一的不同在于,Item2vec摒弃了时间的概念,认为序列中任意两个物品都相关。

Graph Embedding

DeepWalk

eges_item_graph
  1. 图(a)是原始的用户行为序列。

  2. 图(b)基于这些用户行为序列构建了物品关系图。可以看出,物品A和B之间的边产生的原因是用户U1先后购买了物品A和物品B。如果后续产生了多条相同的有向边,则有向边的权重被加强。在 将所有用户行为序列都转换成物品关系图中的边之后,全局的物品关系图就建立起来了。

  3. 图(c)采用随机游走的方式随机选择起始点,重新产生物品序列。

最后将这些物品序列输入Word2vec 模型中, 生成最终的物品Embedding向量。

Node2vec

node2vec1

DeepWalk 的核心做法是:

  • 在图上做随机游走
  • 把游走得到的节点序列当作“句子”
  • 再用 Word2vec 学节点向量

Node2vec 可以看作是 DeepWalk 的增强版。它最关键的改进在于:

  • 它不是做完全均匀的随机游走
  • 而是通过两个参数控制游走更偏向 BFS 还是 DFS

也就是说,Node2vec 的重点不只是“在图上走”,而是:

  • 如何走,决定了最终 embedding 更偏向学习哪类图结构信息

1. 为什么要引入有偏随机游走

在图结构中,我们往往希望 embedding 能表达两类信息:

  • 局部邻居关系
  • 结构角色关系

例如在推荐系统里:

  • 如果两个商品经常和同一批商品一起出现,它们可能属于同一兴趣簇
  • 如果两个商品虽然不直接相邻,但在图中扮演类似的桥接角色,它们也可能结构相似

DeepWalk 的随机游走比较平均,无法显式控制更强调哪一类关系。Node2vec 则通过有偏采样补上了这一点。

2. pq 两个参数

设随机游走中前一个节点是 t,当前节点是 v,下一步候选节点是 x

Node2vec 通过两个超参数控制转移概率:

  • p:return parameter
  • q:in-out parameter

更具体地说,在当前节点 v 上,从上一个节点 t 出发,走向候选节点 x 的未归一化转移概率可以写成:

$$
\pi_{vx} = \alpha_{pq}(t, x)\cdot w_{vx}
$$

其中:

  • $w_{vx}$ 表示边 $(v,x)$ 的权重
  • $\alpha_{pq}(t, x)$ 是由 pq 控制的偏置项

偏置项定义为:

$$
\alpha_{pq}(t, x)=\frac{1}{p}\ \text{if } d_{tx}=0,\quad
\alpha_{pq}(t, x)=1\ \text{if } d_{tx}=1,\quad
\alpha_{pq}(t, x)=\frac{1}{q}\ \text{if } d_{tx}=2
$$

这里 $d_{tx}$ 表示节点 $t$ 和节点 $x$ 之间的最短路径距离。

这个公式的含义是:

  • 如果 x = t,也就是走回上一个节点,那么转移权重是 $\frac{1}{p}$
  • 如果 x 是当前节点 v 的邻居,且和 t 也相邻,那么权重是 $1$
  • 如果 x 更远一些,那么权重是 $\frac{1}{q}$
node2vec2

它们的直观作用可以这样理解。

p 控制是否容易“走回头路”:

  • p 大,回到上一个节点的概率更低
  • p 小,更容易回到刚访问过的节点

q 控制游走更像 BFS 还是 DFS:

  • q 大,更倾向停留在当前节点附近,更像 BFS
  • q 小,更倾向往远处探索,更像 DFS

所以:

  • BFS 风格更容易捕捉同一局部社区中的相似节点
  • DFS 风格更容易捕捉结构角色相似的节点

EGES

EGES 全称是 Enhanced Graph Embedding with Side Information,是推荐系统场景下很有代表性的一种图 embedding 方法。

它要解决的问题非常实际:

  • 如果只靠图结构来学 item embedding,热门商品通常能学得很好
  • 但冷启动商品或者交互很少的商品,向量往往不稳定

因为在推荐系统里,商品不只有行为数据,还通常有很多 side information,例如:

  • 类目
  • 品牌
  • 店铺
  • 标题关键词

这些信息即使在行为很少时,也能提供很强的先验。

1. EGES 的核心思想

EGES 的核心思想可以概括成一句话:

  • item 的表示不应该只来自图结构,还应该融合它本身的属性信息

所以它的流程通常可以理解为:

  1. 根据用户行为构建 item 图
  2. 在图上做随机游走,生成 item 序列
  3. 在训练 embedding 时,把 item ID 和 side information 一起编码

前两步和 DeepWalk / Node2vec 很像,真正的区别出现在第三步。

2. 为什么 side information 很重要

如果只用 item ID 学 embedding,那么只有交互充分的商品才能学到比较稳定的向量。

而对于冷启动商品:

  • 行为序列短
  • 图中连接少
  • 采样得到的上下文不充分

这时单靠图结构往往不够。

但如果我们知道这个商品还属于:

  • 某个类目
  • 某个品牌
  • 某个店铺

那么这些属性就可以帮助模型在数据不足时仍然学到更合理的表示。

3. EGES 怎么融合 side information

EGES 的做法不是简单平均所有 side information,而是:

  • 为 item ID 和每一种 side information 都学习一个 embedding
  • 再学习一组权重,决定不同信息源的重要性

也就是说,一个商品最终的表示不只是:

  • e_item

而更像是:

  • e = α0 e_item + α1 e_category + α2 e_brand + ...

其中这些权重 α 不是手工指定的,而是通过训练学出来的。

这个设计很关键,因为不同商品对 side information 的依赖程度并不一样。

例如:

  • 有些商品行为很多,item ID 本身就足够
  • 有些商品很新,这时类目、品牌等 side information 会更重要

EGES 通过学习权重,让模型自动决定该更依赖哪一类信息。

4. 和 DeepWalk / Node2vec 的区别

可以简单这样理解:

  • DeepWalk:只利用图结构
  • Node2vec:利用图结构,并改进随机游走策略
  • EGES:在图结构基础上,进一步融合 side information

所以 EGES 的意义不在于完全推翻前面的图 embedding 方法,而在于:

  • 把更符合推荐系统实际的数据条件融了进来

5. 在推荐场景中的价值

EGES 特别适合:

  • 电商推荐
  • 内容推荐
  • 冷启动比较明显的 item 场景

因为在这些场景里,单靠用户行为往往是不够的。

它抓住了推荐系统里一个非常真实的问题:

  • 商品不只有“在图中的位置”
  • 还有“它本身的属性信息”

而一个更好的 embedding,通常应该同时利用这两部分信息。

Rendle S. Factorization machines[C]//2010 IEEE International conference on data mining. IEEE, 2010: 995-1000.

因子分解机(Factorization Machines,FM)是推荐系统中非常经典的一类模型。它在高维稀疏特征场景下,给出了一个非常优雅的二阶特征交叉建模方法。很多后续模型,例如 FFM、DeepFM、NFM,本质上都和 FM 有直接关系。

一、问题背景

在推荐系统、广告点击率预估、搜索排序等任务中,输入特征通常具有两个非常典型的特点:

  1. 维度很高
  2. 非常稀疏

例如一个样本可能包含如下信息:

  • 用户 ID
  • 性别
  • 年龄段
  • 城市
  • 商品 ID
  • 商品类别
  • 广告位
  • 时间段

这些离散特征通常会经过 one-hot 或 multi-hot 编码。编码之后,样本向量 $x$ 的维度可能非常大,但每个样本真正非零的位置却很少,因此形成了典型的高维稀疏输入。

在这种场景下,线性模型虽然训练稳定、实现简单,但表达能力有限;而复杂的高阶特征交叉如果直接显式建模,又会带来参数爆炸和严重的数据稀疏问题。

FM 就是在这个背景下提出的。

二、从线性模型说起

设输入特征向量为:

$$
x = (x_1, x_2, \ldots, x_n)
$$

最基本的线性模型可以写成:

$$
\hat y = w_0 + \sum_{i=1}^{n} w_i x_i
$$

其中:

  • $w_0$ 是偏置项
  • $w_i$ 是第 $i$ 个特征的线性权重

这个模型的含义很直接:每个特征独立地对预测结果产生影响。

例如:

  • 男性用户对点击率有一个固定提升
  • 晚上时段对点击率有一个固定提升
  • 某个商品类别对点击率有一个固定提升

但问题在于,现实中的很多信号并不是“单个特征本身”决定的,而是“特征之间的组合关系”决定的。例如:

  • 男性用户是否更偏好数码类商品
  • 年轻用户是否更容易在夜间点击短视频内容
  • 某个用户与某个商品之间是否存在强匹配

这些关系都不是一阶线性项能表达的。

三、显式二阶交叉的问题

为了引入特征交互,一个自然的想法是在线性模型上直接加入二阶项:

$$
\hat y = w_0 + \sum_{i=1}^{n} w_i x_i + \sum_{i=1}^{n} \sum_{j=i+1}^{n} w_{ij} x_i x_j
$$

这里 $w_{ij}$ 表示第 $i$ 个特征和第 $j$ 个特征之间的交叉权重。

这个公式本身没有问题,甚至可以说非常自然。但它有两个致命缺点。

1. 参数量太大

如果有 n 个特征,那么二阶交叉参数 wij 的数量大约是:

$$
\frac{n(n-1)}{2}
$$

也就是 O(n^2) 级别。

在推荐系统中,特征维度常常以十万、百万计,显式学习所有特征对之间的参数几乎不可行。

2. 参数学习很困难

高维稀疏输入意味着大部分特征组合在训练集中出现次数极少,甚至一次都不出现。

例如:

  • 用户 A 和商品 X 可能从未同时出现
  • 城市 C 和品类 Y 的联合样本可能非常少

如果为每一对特征单独学习一个 $w_{ij}$,那么大量交叉参数根本没有足够样本支撑,估计会非常不稳定。

所以问题不只是“参数多”,更是“参数学不准”。

四、从显式二阶模型到 FM 的数学推导

如果我们把所有二阶交叉参数组织成一个矩阵 $W$,其中:

$$
W(i, j) = w_{ij}
$$

那么显式二阶模型可以写成更紧凑的形式:

$$
\hat y = w_0 + w^T x + \sum_{i=1}^{n} \sum_{j=i+1}^{n} w_{ij} x_i x_j
$$

如果进一步把 $W$ 视为一个对称矩阵,并约定对角线元素 $W(i, i) = 0$,那么上式也可以写成:

$$
\hat y = w_0 + w^T x + \frac{1}{2} x^T W x
$$

这里乘上 $\frac{1}{2}$ 的原因是:在 $x^T W x$ 中,$w_{ij} x_i x_j$ 和 $w_{ji} x_j x_i$ 会被重复计算两次。

写成矩阵形式之后,问题就更清楚了。FM 真正要解决的,其实不是“怎么随便造一个交叉项公式”,而是下面这个更本质的问题:

  • 我们想学习一个二阶交互矩阵 $W$
  • 但这个矩阵维度巨大,而且在稀疏数据下很难直接估计
  • 所以需要给 $W$ 加一个强结构约束,让它既能表达交互,又能共享参数

FM 采用的约束就是:假设这个二阶交互矩阵是一个低秩矩阵,或者至少可以被一个低秩矩阵很好地近似。

具体来说,设:

$$
V \in \mathbb{R}^{n \times k}
$$

其中第 i 行记作特征 i 的隐向量:

$$
v_i = (v_{i1}, v_{i2}, \ldots, v_{ik})
$$

并且 $k \ll n$。如果令:

$$
W \approx V V^T
$$

那么矩阵元素就满足:

$$
w_{ij} \approx \langle v_i, v_j \rangle = \sum_{f=1}^{k} v_{if} v_{jf}
$$

这里有一个容易忽略的细节:$V V^T$ 的对角线元素 $W(i, i) = \langle v_i, v_i \rangle$ 通常不为 0。但 FM 在定义二阶项时只保留 $i < j$ 的交叉,因此真正参与预测的是非对角元素;自交叉项不会被算进去。

于是原来需要单独学习的每一个交叉权重 $w_{ij}$,都被改写成两个低维向量的内积。

这样一来,显式二阶模型:

$$
\hat y = w_0 + \sum_{i=1}^{n} w_i x_i + \sum_{i=1}^{n} \sum_{j=i+1}^{n} w_{ij} x_i x_j
$$

就自然变成了 FM 的二阶形式:

$$
\hat y = w_0 + \sum_{i=1}^{n} w_i x_i + \sum_{i=1}^{n} \sum_{j=i+1}^{n} \langle v_i, v_j \rangle x_i x_j
$$

所以从数学上看,FM 并不是凭空定义了一个内积形式,而是对“二阶交互矩阵 $W$ 做低秩分解”后的结果。

这个视角非常重要,因为它解释了“因子分解机”这个名字的由来:

  • “因子”指的是每个特征对应的隐向量,也就是潜在因子
  • “分解”指的是把原本庞大的交互矩阵 $W$ 分解成低秩形式 $V V^T$

五、低秩分解为什么成立

接下来的关键问题是:为什么可以假设 $W$ 是低秩的?

严格来说,这并不是说真实世界中的交互矩阵一定严格低秩,而是说在高维稀疏场景下,低秩约束是一种很有效的归纳偏置。

它隐含的假设是:

  • 特征之间的交互并不是彼此完全独立的
  • 很多交互关系可以由少数几个潜在语义维度解释

比如在推荐系统里,一个物品和一个用户是否匹配,往往并不需要为这对组合单独记一个参数,而是可能由几个更底层的因素决定,例如:

  • 是否偏好某类内容
  • 是否匹配某种价格带
  • 是否符合某种时间场景

如果这些潜在因素的维度是 $k$,那么每个特征只需要学习一个 $k$ 维表示,很多交互关系就可以共享统计强度。

从参数化角度看,这相当于把原本自由度约为 $O(n^2)$ 的矩阵 $W$,限制在一个自由度约为 $O(nk)$ 的低秩空间内。这个限制虽然降低了模型表达能力,但换来了两个非常关键的好处:

  1. 参数量大幅下降
  2. 不同交互之间可以共享参数,从而提升稀疏场景下的泛化能力

六、这个公式到底在表达什么

这个公式可以分成三部分理解。

1. 偏置项

表示全局平均效应。

2. 一阶项

表示每个特征单独带来的线性贡献。

3. 二阶项

表示任意两个特征同时出现时,它们之间的交互影响。

其中最关键的是内积 $\langle v_i, v_j \rangle$。它可以理解为:

  • 如果两个特征在隐空间中方向相近,内积较大,说明它们之间有较强正相关交互
  • 如果两个特征关系弱,内积接近 0
  • 如果方向相反,交互作用甚至可能为负

因此,FM 不再把“特征交叉”看作互相独立的离散事件,而是把每个特征映射到一个隐空间里,用隐空间中的相似性来表达交互强度。

七、为什么这种做法能缓解稀疏问题

这是 FM 最值得理解的地方。

在显式二阶模型里,$w_{ij}$ 只属于 $(i, j)$ 这一对特征。一旦 $(i, j)$ 在训练集中出现得很少,那么这个参数就学不好。

而在 FM 中,交互项变成:

$$
w_{ij} = \langle v_i, v_j \rangle
$$

此时:

  • $v_i$ 不仅参与 $(i, j)$ 的交互
  • 还参与 $(i, p)$、$(i, q)$、$(i, r)$ 等所有与特征 $i$ 相关的交互

因此,一个特征的隐向量会从许多样本和许多交互关系中共同学习。

举个直观的例子。

假设:

  • 用户 A 看过电影 1、电影 2
  • 用户 B 看过电影 1

那么即使“用户 B 和电影 2”这个组合在训练集中很少出现,模型仍然可能通过:

  • $v_{\text{用户B}}$
  • $v_{\text{电影2}}$

这两个向量各自在其他样本中的学习结果,推断出它们之间可能存在怎样的交互关系。

这就是 FM 能在稀疏场景下泛化的根本原因。

八、FM 的参数规模

如果直接学习显式二阶参数,那么总参数量大约为:

$$
O(n^2)
$$

而在 FM 中,需要学习的参数包括:

  1. 一个偏置项 w0
  2. n 个一阶权重 wi
  3. $n$ 个 $k$ 维隐向量 $v_i$

因此总参数规模约为:

$$
O(nk)
$$

只要 k << n,这个规模就远小于显式二阶模型。

例如:

  • 若 $n = 100000$
  • $k = 10$

则显式二阶参数规模约为 10^10 量级,而 FM 的隐向量参数规模只有 10^6 量级,两者完全不是一个数量级。

九、二阶项的高效计算

虽然 FM 用隐向量取代了显式交叉参数,但如果仍然直接根据定义计算:

$$
\sum_{i=1}^{n} \sum_{j=i+1}^{n} \langle v_i, v_j \rangle x_i x_j
$$

那么时间复杂度依旧会达到 O(n^2 k)

FM 之所以能真正落地,还因为它有一个非常重要的代数化简。这个化简本质上是在计算:

$$
\frac{1}{2} x^T (V V^T) x
$$

但如果直接先算出完整的 V V^T,代价仍然会非常高。FM 的技巧在于:不显式构造交互矩阵,而是直接在代数层面对求和式进行变形。

先把内积展开:

$$\begin{aligned}
\sum_{i=1}^{n} \sum_{j=i+1}^{n} \langle v_i, v_j \rangle x_i x_j
&= \sum_{i=1}^{n} \sum_{j=i+1}^{n} \sum_{f=1}^{k} v_{if} v_{jf} x_i x_j
\end{aligned}$$

交换求和顺序:

$$\begin{aligned}
\sum_{i=1}^{n} \sum_{j=i+1}^{n} \sum_{f=1}^{k} v_{if} v_{jf} x_i x_j
&= \sum_{f=1}^{k} \sum_{i=1}^{n} \sum_{j=i+1}^{n} v_{if} v_{jf} x_i x_j
\end{aligned}$$

对于固定的第 f 个隐因子维度,有一个经典恒等式:

$$\begin{aligned}
\sum_{i=1}^{n} \sum_{j=i+1}^{n} a_i a_j
&= \frac{1}{2}\left[\left( \sum_{i=1}^{n} a_i \right)^2 - \sum_{i=1}^{n} a_i^2\right]
\end{aligned}$$

令:

$$
a_i = v_{if} x_i
$$

则可得:

$$\begin{aligned}
\sum_{i=1}^{n} \sum_{j=i+1}^{n} v_{if} v_{jf} x_i x_j
&= \frac{1}{2}\left[\left( \sum_{i=1}^{n} v_{if} x_i \right)^2 - \sum_{i=1}^{n} v_{if}^2 x_i^2\right]
\end{aligned}$$

因此二阶项可整体写成:

$$\begin{aligned}
\frac{1}{2}\sum_{f=1}^{k}\left[\left( \sum_{i=1}^{n} v_{if} x_i \right)^2 - \sum_{i=1}^{n} v_{if}^2 x_i^2\right]
\end{aligned}$$

这样每个隐因子维度只需要对所有特征扫一遍,计算复杂度就从:

$$
O(n^2 k)
$$

降到了:

$$
O(nk)
$$

这也是 FM 在工业界非常实用的关键原因。

十、直观理解这个化简

这个化简的本质是:原本我们想逐对枚举所有特征交互,但实际上可以先在每个隐因子维度上把所有特征贡献加总,再扣掉自身和自身相乘的部分。

可以把它想象成:

  1. 先把所有特征在第 $f$ 维上的“投影贡献”加起来
  2. 对总和平方,就等于把所有两两组合都算进去了
  3. 但平方中包含了 i = j 的自交叉项
  4. 所以再减去这些自交叉项
  5. 最后由于 (i, j)(j, i) 被重复计算一次,所以再乘 1/2

这个推导很像很多线性代数和概率论里常见的“先整体求和,再扣除重复项”的技巧。

十一、一个简单样例

假设只有三个特征非零:

  • $x_1 = 1$
  • $x_2 = 1$
  • $x_3 = 1$

并且设隐向量维度 $k = 2$,三个特征对应的隐向量分别为:

$$
v_1 = (1, 2)
$$

$$
v_2 = (2, 1)
$$

$$
v_3 = (1, 1)
$$

则交互项为:

$$
\langle v_1, v_2 \rangle x_1 x_2 + \langle v_1, v_3 \rangle x_1 x_3 + \langle v_2, v_3 \rangle x_2 x_3
$$

分别计算内积:

$$
\langle v_1, v_2 \rangle = 1 \cdot 2 + 2 \cdot 1 = 4
$$

$$
\langle v_1, v_3 \rangle = 1 \cdot 1 + 2 \cdot 1 = 3
$$

$$
\langle v_2, v_3 \rangle = 2 \cdot 1 + 1 \cdot 1 = 3
$$

所以二阶项总和为:

$$
4 + 3 + 3 = 10
$$

这个例子说明,FM 的每一对交互强度本质上都是通过隐向量相似性给出的,而不是一个单独存储的查表参数。

十二、FM 与矩阵分解的关系

FM 这个名字里有“分解”两个字,并不是偶然。它和矩阵分解(Matrix Factorization)有非常紧密的关系。

在推荐系统中,如果输入特征只包含:

  1. 用户 ID
  2. 物品 ID

并都用 one-hot 编码表示,那么每个样本通常只有一个用户特征和一个物品特征取值为 1。此时 FM 的二阶交互项就退化为:

$$
\langle v_{\text{user}}, v_{\text{item}} \rangle
$$

这和矩阵分解中的用户向量与物品向量内积是同一个思想。

因此可以认为:

  • 矩阵分解是 FM 在特定输入形式下的特例
  • FM 是把矩阵分解中的隐向量交互思想推广到了一般特征空间

十三、小结

FM 的核心公式是:

$$
\hat y = w_0 + \sum_{i=1}^{n} w_i x_i + \sum_{i=1}^{n} \sum_{j=i+1}^{n} \langle v_i, v_j \rangle x_i x_j
$$

它最重要的思想可以概括成两句话:

  1. 用隐向量内积替代显式二阶交叉参数
  2. 用低秩分解解决高维稀疏场景下的交互建模问题

如果从建模角度看,FM 介于“线性模型”和“深度推荐模型”之间。它保留了线性模型对稀疏特征友好的优点,同时又引入了隐向量来建模特征交互,因此成为推荐系统历史上非常关键的一步。

Deep Crossing 模型

Shan Y, Hoens T R, Jiao J, et al. Deep crossing: Web-scale modeling without manually crafted combinatorial features[C]//Proceedings of the 22nd ACM SIGKDD international conference on knowledge discovery and data mining. 2016: 255-262.

微软在2016年提出的Deep Crossing模型是第一次深度学习架构在推荐系统中的完整应用。下图是模型的架构。

deep cross

输入特征分为类别特征,数值型特征。类别型特征可以通过onehot或multihot编码生成特征向量,数值型特征可以直接拼接进特征向量中。在输入所有特征向量后,deep crossing模型进行CTR预估。

解决的问题

  • 离散型特征向量编码后过于稀疏,该模型解决了稀疏特征向量稠密话的问题。
  • 解决特征自动交叉组合的问题。
  • 在输出层中达成问题设定的优化目标。

架构介绍

架构包括embedding, stacking, multiple residual unit, scoring四层。

  1. Embedding层:
    将输入的特征向量转化成稠密的嵌入向量。经典结构以全连接层为主,但现在有word2vec,graph embedding等多种不同的embedding方式。(只有离散型特征向量需要embedding, 数值型特征直接进入stacking层)。
  2. Stacking层:
    把所有特征拼接在一起。
  3. Multiple Residual Unit层:
    主要结构是MLP。Deep Crossing使用多层残差网络作为MLP的具体实现。下图是残差单元的具体实现。 residual
  4. Scoring层:
    逻辑回归(sigmoid)或softmax模型。

NeuralCF 模型

He X, Liao L, Zhang H, et al. Neural collaborative filtering[C]//Proceedings of the 26th international conference on world wide web. 2017: 173-182.

NeuralCF

Neural Collaborative Filtering,通常简称为 NCF,是 2017 年非常有代表性的一篇推荐模型工作。它最核心的出发点是:传统矩阵分解通常使用用户向量和物品向量的内积来建模交互关系,但“内积”本身是一种比较强的结构假设,它虽然简单高效,却未必足够灵活。既然深度学习已经在视觉、语音、NLP 中证明了强大的非线性表达能力,那么在推荐问题里,我们是否也可以用神经网络来学习用户和物品之间更复杂的交互函数?

NCF 就是在这个思路下提出的。

解决的问题

  • 传统矩阵分解把用户和物品的匹配关系限制为向量内积,表达形式较单一。
  • 用户兴趣和物品属性之间往往存在复杂的非线性交互,单纯内积不容易充分刻画。
  • 希望在协同过滤框架下,引入深度神经网络自动学习更灵活的用户-物品交互函数。

一、为什么要从矩阵分解走向 NCF

在最经典的隐语义模型里,用户 u 和物品 i 分别对应一个隐向量:

  • p_u
  • q_i

它们的匹配分数通常写成:

ŷ_ui = p_u^T q_i

这个形式非常经典,也非常有效,因为它把推荐问题转化成了“两个低维向量是否相似”。

但它也有明显限制:用户和物品之间的交互形式被固定成了双线性内积。换句话说,无论用户偏好有多复杂,模型最终都只能通过“对应维度相乘再求和”的方式表达。

NCF 的作者认为,这种假设可能过于刚性。真实场景里,用户和物品之间的匹配关系很可能不是简单线性结构,而是某种更复杂的非线性函数:

ŷ_ui = f(u, i | Θ)

其中 f 由神经网络来学习,Θ 是模型参数。

所以如果用一句话概括 NCF 的思想,就是:

  • 用神经网络替代固定的内积函数
  • 让模型自己学习“用户向量和物品向量应该如何交互”

二、整体思路

NCF 的基本流程并不复杂,可以概括为四步:

  1. 为用户和物品分别学习 embedding
  2. 将用户 embedding 和物品 embedding 送入交互层
  3. 用神经网络学习高阶非线性交互
  4. 输出用户对物品的偏好分数

和很多后来的推荐模型相比,NCF 的结构其实非常干净。它并不直接处理特别复杂的多域特征,而是聚焦在最基本的协同过滤设定:

  • 输入只有用户 ID 和物品 ID
  • 每个用户、物品都映射成一个稠密向量
  • 通过不同交互方式预测用户是否会点击、购买或评分

NCF 论文中最重要的并不是一个单独模型,而是一组逐步递进的结构:

  • GMF
  • MLP
  • NeuMF

可以把它们理解为从“矩阵分解的神经化版本”一步步走向“融合线性匹配和非线性匹配”的过程。

三、GMF:Generalized Matrix Factorization

GMF 可以看作是对传统矩阵分解的一种神经网络化改写。

在传统矩阵分解中,用户 u 和物品 i 的匹配分数为:

ŷ_ui = p_u^T q_i = Σ_k p_uk q_ik

而在 GMF 中,作者先保留“按元素乘积”这个交互思想:

φ_GMF = p_u ⊙ q_i

其中 表示逐元素乘法。也就是说,第 k 个维度的交互结果为:

p_uk q_ik

然后不再直接把这些维度简单求和,而是再接一个输出层:

ŷ_ui = σ(h^T (p_u ⊙ q_i))

其中:

  • h 是输出层参数
  • σ 是 sigmoid 函数

这样看起来只是“小改”,但意义很重要。传统矩阵分解相当于默认每个维度的权重都一样,而 GMF 允许模型进一步学习不同隐维度在最终预测中的重要性。

因此可以把 GMF 理解成:

  • 保留矩阵分解中“逐维匹配”的思想
  • 但把最终的固定求和替换成可学习的加权输出

它仍然偏向“线性式交互”,但已经开始摆脱经典矩阵分解那种完全固定的匹配方式。

四、MLP:用多层感知机建模非线性交互

如果说 GMF 还是在矩阵分解附近做改造,那么 MLP 分支就更激进一些。它不再强调“逐元素乘积”,而是直接把用户和物品 embedding 拼接起来,再交给多层感知机学习:

z_1 = [p_u ; q_i]

z_2 = a_2(W_2^T z_1 + b_2)

z_3 = a_3(W_3^T z_2 + b_3)

...

z_L = a_L(W_L^T z_{L-1} + b_L)

最后输出预测分数:

ŷ_ui = σ(h^T z_L)

这里最关键的变化在于:

  • GMF 的交互方式是显式设计好的,即逐元素乘法
  • MLP 不再预设交互形式,而是让网络在多层非线性变换中自动学习

从表示学习角度看,MLP 给了模型更强的函数逼近能力。理论上,只要网络足够深、参数足够充分,它就可以拟合比内积复杂得多的用户-物品交互模式。

不过,MLP 也带来一个代价:模型虽然更灵活,但可解释性更弱,而且训练难度通常也会高于 GMF。

五、NeuMF:把 GMF 和 MLP 融合起来

作者进一步发现,GMF 和 MLP 其实各有优势:

  • GMF 擅长保留矩阵分解式的线性匹配信息
  • MLP 擅长学习复杂的非线性交互

所以最自然的想法就是把它们融合起来,这就是 NeuMF(Neural Matrix Factorization)。

NeuMF 的结构是:

  1. 用户和物品分别进入 GMF 分支
  2. 用户和物品也分别进入 MLP 分支
  3. 两个分支各自产生一个高层表示
  4. 把两个表示拼接,再送入最终输出层

形式上可以写成:

φ_GMF = p_u^G ⊙ q_i^G

φ_MLP = MLP([p_u^M ; q_i^M])

φ_NeuMF = [φ_GMF ; φ_MLP]

ŷ_ui = σ(h^T φ_NeuMF)

这里要注意,GMF 分支和 MLP 分支通常使用不同的 embedding 参数,也就是:

  • p_u^G, q_i^G
  • p_u^M, q_i^M

这样做的原因是,两条分支建模的交互模式不同,如果强行共享 embedding,反而可能限制它们各自的表达能力。

NeuMF 可以理解成一种“混合专家”的雏形:

  • 一部分参数负责学习类似矩阵分解的结构化交互
  • 另一部分参数负责学习更自由的非线性关系
  • 最终再把二者融合起来做预测

这也是 NCF 论文里效果最好的主模型。

六、训练方式

NCF 主要考虑的是隐式反馈场景,例如:

  • 点击
  • 购买
  • 收藏
  • 观看

在这类任务里,我们通常只知道用户“做了什么”,而不知道用户“明确不喜欢什么”。因此训练时往往要做负采样,也就是:

  • 用户交互过的物品作为正样本
  • 用户未交互的物品中随机采样一部分作为负样本

在损失函数上,论文主要采用的是二分类学习目标。输出经过 sigmoid 后,可以用交叉熵来优化用户对物品的点击或偏好概率。

直观上就是让模型学会:

  • 正样本的预测分数更高
  • 负样本的预测分数更低

此外,作者还提出了一个很实用的训练技巧:可以先分别预训练 GMF 和 MLP,再把它们的参数初始化到 NeuMF 中继续联合训练。这样通常会比从随机初始化直接训练 NeuMF 更稳定。

七、NCF 和矩阵分解的关系

理解 NCF 时,一个特别重要的问题是:它和矩阵分解到底是什么关系?

可以这样看:

  • 矩阵分解是最经典的协同过滤隐向量模型
  • GMF 是对矩阵分解的神经网络化扩展
  • MLP 是完全用深度网络重新定义用户-物品交互函数
  • NeuMF 则把这两种思路融合起来

所以 NCF 并不是“推翻矩阵分解”,而是在矩阵分解的基础上继续往前走了一步。

如果只保留最简单的交互形式,那么 NCF 可以退化到类似矩阵分解的结构;但如果给它更强的非线性网络,它又可以学习比矩阵分解更复杂的模式。

八、优点与局限

NCF 的优点很明显:

  • 模型结构清晰,容易理解
  • 直接把深度学习引入协同过滤交互建模
  • 比固定内积更灵活,表达能力更强
  • NeuMF 同时结合了线性匹配与非线性匹配信息

但它也有一些局限:

  • 输入仍然主要是用户 ID 和物品 ID,侧信息利用不充分
  • 如果数据量不够大,复杂的 MLP 分支容易过拟合
  • 相比传统矩阵分解,训练成本和调参复杂度更高
  • 对工业级推荐系统来说,它更像是重要起点,而不是最终形态

后续很多模型,例如 Wide & Deep、DeepFM、DIN、DCN 等,都在更丰富的特征输入和更复杂的交互建模上继续发展。但从历史脉络上看,NCF 非常关键,因为它清楚地提出了一个问题:

  • 用户和物品之间的交互函数,为什么一定只能是内积?

九、小结

NeuralCF 的核心思想可以概括成三句话:

  1. 传统协同过滤中的内积函数不一定足够表达复杂偏好关系。
  2. 可以用神经网络来学习用户和物品之间的非线性交互函数。
  3. 通过融合 GMF 和 MLP,NeuMF 同时保留了矩阵分解式交互与深度非线性表达能力。

如果说 Deep Crossing 解决的是“多特征输入如何通过深层网络自动组合”的问题,那么 NCF 关注的则是另一个更基础的问题:

  • 在最纯粹的用户-物品协同过滤场景里,交互函数本身应当如何设计?

这也是它在推荐系统发展史上非常有代表性的原因。

PNN 模型

Qu Y, Cai H, Ren K, et al. Product-based neural networks for user response prediction[C]//2016 IEEE 16th international conference on data mining (ICDM). IEEE, 2016: 1149-1154.

PNN架构

PNN 全称是 Product-based Neural Network。它是 CTR 预估领域很有代表性的一个模型,因为它抓住了一个非常关键的问题:

  • embedding 之后直接把所有特征拼接起来,再交给 MLP,当然也能学习交互
  • 但这种交互是“隐式”学习出来的,不一定高效,也不一定足够稳定

PNN 的核心想法是:

  • 既然点击率预估里最重要的信息之一就是不同 field 之间的组合关系
  • 那么不如在 embedding 层之后,先显式做一次乘积交互
  • 再把这些交互结果送进 DNN

这就是 PNN 相比 Deep Crossing 最关键的区别。

解决的问题

  • 仅仅做 embedding 拼接,模型需要靠后续 MLP 自己去摸索特征交叉关系。
  • CTR 场景中的很多有效信号都来自 field 与 field 之间的组合,而不是单一特征。
  • 希望在进入深层网络之前,先显式构造二阶交互,让后续网络学习更容易。

一、PNN 的整体结构

如果从结构上看,PNN 和 Deep Crossing 很像,主要也包括下面几层:

  1. 输入层
  2. Embedding 层
  3. Product Layer
  4. 全连接神经网络层
  5. 输出层

它和 Deep Crossing 最大的不同在于第三层。

在 Deep Crossing 中,embedding 之后通常直接做拼接,也就是 stacking;而在 PNN 中,embedding 之后不会立刻简单拼接,而是先进入 Product Layer,通过乘积操作显式建模特征交互。

所以可以把 PNN 理解成:

  • 前半部分负责把稀疏离散特征映射成稠密向量
  • 中间的 Product Layer 负责显式做特征交叉
  • 后半部分的 MLP 负责在交叉结果基础上继续学习更高阶模式

二、为什么需要 Product Layer

在推荐系统或广告点击率预估中,输入通常不是一个单一特征,而是很多 field 的组合,例如:

  • 用户性别
  • 年龄段
  • 城市
  • 广告 ID
  • 广告类别
  • 设备类型
  • 时间段

这些 field 经过 embedding 之后,会变成多个低维向量:

  • e1
  • e2
  • e3
  • ...
  • em

其中 m 是 field 数量。

如果像普通 DNN 一样,直接把它们拼接:

z = [e1 ; e2 ; ... ; em]

那么模型确实可以学习交叉,但这种交叉需要在后续多层非线性变换中间接形成。

PNN 认为,这样做太“绕”了。因为很多关键模式本来就是显式的特征组合,例如:

  • 某类用户是否更偏好某类广告
  • 某个广告在某个时段是否更容易被点击
  • 某种设备环境下某类素材是否更有效

这些关系天然就是 field 与 field 的交互。因此更合理的方式是:

  • 先明确构造交叉信号
  • 再把这些信号交给 DNN

三、Embedding Layer

PNN 的 embedding 层和其他深度推荐模型差别不大。

假设第 i 个 field 经过 embedding 后得到向量:

ei ∈ R^d

其中 d 是 embedding 维度。

这样,一个样本最终就会得到 m 个 embedding 向量。到这里为止,PNN 和普通的 embedding + MLP 结构还没有本质区别。真正的创新在于下一层的 Product Layer。

四、Product Layer

Product Layer 是 PNN 的核心。

它的目标不是简单把 embedding 拼起来,而是要同时保留两类信息:

  • 线性信息
  • 乘积交互信息

因此从结构上看,Product Layer 通常可以理解为由两部分组成:

  1. z:线性部分,保留原始 embedding 的信息
  2. p:乘积部分,显式表示 field 与 field 之间的交互

最终,这两部分会一起输入到后面的全连接层。

你可以把这一层想象成:

  • z 告诉模型“每个 field 本身是什么”
  • p 告诉模型“这些 field 之间是怎么相互作用的”

这也是 PNN 和单纯 DNN 相比最重要的建模增强。

五、Product Layer 的两种主要形式

PNN 论文中主要讨论了两种 product 形式:

  • IPNN:Inner Product-based Neural Network
  • OPNN:Outer Product-based Neural Network

两者的区别在于,它们对两个 embedding 向量之间的交互保留了多少信息。

六、IPNN:内积式交互

在 IPNN 中,两个 field 的交互由内积表示:

pij = <ei, ej>

也就是说,第 i 个 field 和第 j 个 field 的关系,被压缩成一个标量。

如果一共有 m 个 field,那么就可以得到所有两两组合的交互:

  • (e1, e2)
  • (e1, e3)
  • ...
  • (em-1, em)

直观上,IPNN 的含义是:

  • 如果两个 field 的 embedding 在隐空间中更接近,那么它们的内积更大
  • 内积越大,说明模型认为这两个 field 的组合关系越重要

这种方式的优点是很轻量,而且和 FM 的思想很接近,因为 FM 也是通过隐向量内积来表达特征交叉。

所以从直觉上看,IPNN 其实是在做一件事:

  • 用向量相似性来表达不同 field 之间的交互强度

七、OPNN:外积式交互

相比 IPNN,OPNN 更进一步。它不是把两个 embedding 的关系压缩成一个标量,而是计算外积:

pij = ei ej^T

如果 ei ∈ R^dej ∈ R^d,那么外积结果是一个 d × d 的矩阵。

这个矩阵会保留更丰富的交互信息,因为:

  • 内积只能得到一个整体匹配分数
  • 外积则保留了“第 a 维和第 b 维如何相互作用”的细粒度结构

因此,OPNN 的表达能力通常比 IPNN 更强,但代价也很明显:

  • 计算量更大
  • 参数量更大
  • 实现更复杂

所以可以简单理解为:

  • IPNN 更轻量,更容易训练
  • OPNN 更强,但更重

这也是 PNN 中很典型的表达能力与效率之间的权衡。

八、PNN 为什么比普通 DNN 更适合 CTR 预估

CTR 场景有一个非常重要的特点:很多有效信号天然来自组合关系,而不是单个特征本身。

例如:

  • 某类用户对某类广告更敏感
  • 某广告在某时间段点击率更高
  • 某设备环境下某类素材表现更好

如果只用普通 DNN:

  • 模型必须通过后续多层网络慢慢学出这些交叉关系

而 PNN 则是:

  • 在进入 DNN 之前,就先把这些乘积关系显式做出来

这样一来,后面的 MLP 不需要从零开始摸索“谁和谁要交互”,而是直接在已有交叉特征的基础上继续建模。因此在 CTR 任务中,PNN 往往比简单的 embedding + MLP 更有针对性。

Wide & Deep 模型

Cheng H T, Koc L, Harmsen J, et al. Wide & deep learning for recommender systems[C]//Proceedings of the 1st workshop on deep learning for recommender systems. 2016: 7-10.

Deep & Wide Deep & Wide

Wide & Deep 是 Google 在 2016 年提出的经典推荐模型。它之所以重要,不是因为结构特别复杂,而是因为它非常准确地抓住了推荐系统里的一个核心矛盾:

  • 我们希望模型能够“记住”历史上出现过的高价值规则
  • 也希望模型能够对没见过的新组合进行“泛化”

传统的线性模型在“记忆”方面很强,因为它能够非常直接地学习人工设计好的交叉特征;但它的泛化能力有限。纯深度模型则恰好相反,能通过 embedding 学到一定泛化能力,但对一些明确的、稀疏的规则模式不一定学得足够稳定。

Wide & Deep 的核心思想就是把这两类能力放进同一个模型里:

  • Wide 部分负责 memorization,也就是记忆能力
  • Deep 部分负责 generalization,也就是泛化能力

解决的问题

  • 线性模型擅长记住显式规则,但难以对未见组合泛化。
  • 深度模型有更强的表示学习能力,但不一定能稳定捕捉那些稀疏而明确的组合规则。
  • 推荐系统既需要记忆历史经验,也需要对新样本、新组合做合理推断。

一、为什么“记忆”和“泛化”要分开看

在推荐和广告任务里,很多有效信号其实可以分成两类。

第一类是“记忆型”信号,例如:

  • 安装过某类 App 的用户更容易点击某类广告
  • 某城市、某设备、某时间段与某个广告位组合时点击率特别高

这种模式往往是明确的、稀疏的,而且很像规则。一旦数据中真的出现过多次,线性模型就能把它们记得很好。

第二类是“泛化型”信号,例如:

  • 虽然用户没看过这个广告,但他看过相似类别广告
  • 虽然某个组合没在训练中高频出现,但相关特征在 embedding 空间里很接近

这种模式就更适合交给深度模型去学习。

Wide & Deep 的出发点正是:

  • 单独依赖线性模型不够
  • 单独依赖 DNN 也不够
  • 更合理的方式是把两者结合起来

二、整体结构

Wide & Deep 的结构很直观,由两部分并联组成:

  1. Wide 侧
  2. Deep 侧

最后把两部分的输出加到一起,再经过 sigmoid 或 softmax 得到最终预测结果。

从结构上理解,它像是:

  • 一条“规则记忆通路”
  • 一条“表示学习通路”

这两条通路共享同一个任务目标,但分别承担不同职责。

三、Wide 部分:负责记忆

Wide 部分本质上是一个广义线性模型,可以写成:

y_wide = w^T x + b

这里的 x 不只是原始稀疏特征,还通常包括人工构造的交叉特征,例如:

  • AND(user_installed_app = TikTok, impression_app = YouTube)
  • AND(gender = male, category = sports)

这种交叉特征通常可以通过笛卡尔积、哈希交叉等方式构造出来。

Wide 部分的优势在于:

  • 对显式交叉非常敏感
  • 对训练中高频出现的规则能学得很准
  • 结果相对更可解释

但它的问题也很明显:

  • 人工交叉特征设计成本高
  • 对没见过的组合泛化较弱

所以它很适合做“记忆”,但不适合单独承担整个推荐任务。

四、Deep 部分:负责泛化

Deep 部分则是一条典型的 embedding + MLP 结构。

首先,离散特征会被映射成 embedding:

  • 用户特征 embedding
  • 物品特征 embedding
  • 上下文特征 embedding

然后这些 embedding 被拼接起来,送入多层全连接网络:

a^(l+1) = f(W^(l) a^(l) + b^(l))

通过这种多层非线性变换,模型可以学习到:

  • 特征之间更抽象的组合关系
  • 相似特征之间的共享表示
  • 训练集中未充分出现过的模式

Deep 部分最重要的能力就是泛化。

它不需要显式写出“某用户属性和某广告属性应该如何交叉”,而是通过 embedding 空间中的相似性,把不同组合联系起来。

五、Wide 与 Deep 如何融合

Wide & Deep 的最终输出可以理解为:

y = σ(y_wide + y_deep)

也就是说:

  • Wide 分支给出一部分线性/规则型判断
  • Deep 分支给出一部分非线性/泛化型判断
  • 两者共同决定最终结果

这种融合方式非常简单,但很有效。它的关键不是复杂的融合技巧,而是明确地让两种不同归纳偏置同时存在。

换句话说,Wide & Deep 不是要让一个模型同时“既像线性模型又像 DNN”,而是干脆保留两个分支,让它们各司其职。

六、为什么这个模型在推荐里非常重要

Wide & Deep 的价值在于,它把很多工业推荐系统早就知道的一个经验显式写进了模型结构里:

  • 某些模式非常像规则,应该被记住
  • 某些模式更依赖表示学习,应该被泛化

对于推荐系统来说,这特别重要,因为真实数据往往同时具有:

  • 稀疏性
  • 长尾性
  • 新组合不断出现

如果只有 wide:

  • 模型会过于依赖已见规则

如果只有 deep:

  • 模型可能对明确的稀疏规则记忆不够稳

Wide & Deep 刚好在这两者之间找到一个平衡点。

七、和前面几个模型的关系

把 Wide & Deep 和前面几个模型放在一起看,会更容易理解它的定位。

1. 和 Deep Crossing 的关系

Deep Crossing 更强调:

  • embedding 后直接接深层网络
  • 依靠 DNN 自动学习特征组合

Wide & Deep 则认为:

  • 只靠深层网络还不够
  • 一些显式规则值得单独保留一条 wide 分支

所以:

  • Deep Crossing 更偏“纯深度”
  • Wide & Deep 更偏“线性规则 + 深度学习混合”

2. 和 PNN 的关系

PNN 是在 deep 分支内部显式加入 Product Layer,强调 embedding 之间的交互。

Wide & Deep 的关注点则不同:

  • 它不是在 DNN 内部细化交互方式
  • 而是在模型顶层直接并联一条线性记忆分支

所以两者虽然都在解决“特征交互”问题,但切入点不同:

  • PNN 强调在 deep 网络内部做显式交叉
  • Wide & Deep 强调把“记忆”和“泛化”两种能力拆开建模

八、优点与局限

Wide & Deep 的优点主要有:

  • 结构简单清晰,工程上容易落地
  • 同时兼顾显式规则记忆和深度表示学习
  • 在工业推荐场景中非常实用
  • 为很多后续模型提供了重要设计思路

它的局限也比较明显:

  • Wide 部分仍然依赖人工交叉特征设计
  • 特征工程成本较高
  • wide 和 deep 的交互方式比较简单,通常只是最后相加
  • 对更复杂的自动特征交叉能力仍有限

这也解释了为什么后续会出现 DeepFM、DCN、xDeepFM 等模型。它们本质上都在尝试进一步回答一个问题:

  • 能不能把“记忆”和“泛化”结合得更自然,同时减少人工交叉特征设计?

九、小结

Wide & Deep 的核心思想可以概括成三句话:

  1. 线性模型擅长记住显式规则,DNN 擅长学习泛化表示。
  2. 推荐系统同时需要记忆能力和泛化能力,因此不能只依赖其中一类模型。
  3. 通过把 wide 分支和 deep 分支联合训练,模型可以同时吸收两类优势。

如果说 Deep Crossing 强调“让深层网络自动学习特征组合”,那么 Wide & Deep 更强调另一件事:

  • 有些知识应该被模型记住,有些知识则应该被模型泛化。

这也是它在推荐系统工业实践中极具代表性的原因。

十、进一步:Deep & Cross 模型

Wang R, Fu B, Fu G, et al. Deep & cross network for ad click predictions[M]//Proceedings of the ADKDD’17. 2017: 1-7.

Deep & Cross Network,通常简称为 DCN,可以看作是对 Wide & Deep 的一个很自然的延伸。

Wide & Deep 的问题在于:

  • wide 分支确实擅长记忆显式规则
  • 但它通常依赖人工构造交叉特征

这在工业上非常有效,但也有明显成本:

  • 需要做大量特征工程
  • 交叉方式依赖经验
  • 很难系统性地覆盖更高阶组合

DCN 的核心思路就是:

  • 既然 wide 部分的本质是在做显式交叉
  • 那么能不能设计一个神经网络结构,自动完成这种交叉构造

所以可以把 DCN 理解成:

  • 用一个可学习的 Cross Network,替代 Wide & Deep 中依赖人工设计的 wide 部分
Deep & Cross

解决的问题

  • Wide & Deep 中的 wide 分支依赖人工交叉特征设计,工程成本较高。
  • 普通 DNN 虽然可以隐式学习交叉,但对一些低阶或明确的组合关系不一定学习得高效。
  • 希望模型能够自动、显式地构造有限阶特征交叉,同时保留 deep 分支的泛化能力。

Cross网络介绍

DCN 的整体结构仍然保留了“并联两路”的思想:

  1. Cross Network
  2. Deep Network

其中:

  • Cross Network 负责显式构造特征交叉
  • Deep Network 负责学习更一般的非线性表示

最后再把两路输出拼接或融合,送入最终输出层。

这意味着 DCN 并不是否定 Deep & Wide,而是对它进行了结构上的升级:

  • Wide & Deep:wide 负责人工规则记忆
  • DCN:cross 负责自动化特征交叉

Cross Network 的核心公式

设 embedding 和数值特征拼接后的输入向量为:

x0 ∈ R^d

Cross Network 的第 l 层输出记为:

xl

那么它的核心递推公式是:

x_{l+1} = x0 (xl^T wl) + bl + xl

其中:

  • wl ∈ R^d 是第 l 层的参数向量
  • bl ∈ R^d 是偏置项
  • xl^T wl 是一个标量

这个公式第一次看会有点怪,但它非常重要,因为它直接体现了 DCN 的建模方式。

可以把它拆开理解:

  1. xl^T wl 先把当前层表示压缩成一个标量
  2. 再用这个标量去缩放原始输入 x0
  3. 最后加上残差项 xl

于是每一层都在做一件事:

  • 让原始输入 x0 和当前表示 xl 发生一次显式交互

这就是为什么它叫 Cross Network。

为什么这个公式能产生特征交叉

DCN 最值得理解的地方就在这里。

先看第一层:

x1 = x0 (x0^T w0) + b0 + x0

其中 x0^T w0 是对输入各维度的线性组合。再乘回 x0 之后,就会出现不同维度之间的乘积项。

如果继续往后堆层,比如到第二层、第三层,那么这种“原始输入”和当前表示”的交互会不断叠加,于是模型可以显式构造更高阶特征交叉。

一个非常关键的结论是:

  • Cross Network 堆叠 L 层时,理论上可以显式建模到 L+1 阶特征交叉

这和普通 MLP 不一样。

普通 MLP 当然也可能拟合高阶交叉,但那是“隐式”学出来的;而 DCN 的 Cross Layer 是把交叉结构直接写进了网络形式里。