End-To-End之于推荐: Meta GRs & HSTU 生成式推荐革命之作

Meta 24年release的工作,属于LLM对于推荐的颠覆性创新工作,End-To-End推荐的第一篇工作,效果也是惊人,在Meta业务下有12%的业务指标提升!

下面从几个方面进行该工作的记录:

一 创新缘起

先说DLRMs的缺点,特征异质结构(features in recommendation systems lack explicit structures),没有 NLP/CV 那种自然结构,使得深度模型无法从结构中自动学习规律,导致 scaling 受限,不符合Scaling Law规律。

推荐系统的输入长这样:用户年龄、用户星级、设备型号、过去 7 天点击次数、过去 30 天浏览 CTR、用户行为序列(但很短)

这些特征特点是:
a 异构的(heterogeneous)

每个特征类型完全不同:有些是 float,有些是类别 embedding,有些是计数统计,有些是序列,有些是罕见高维 id(用户 id/商品 id)

不像 NLP:所有 token 都是同一类型
b 没有固定的顺序位置

语言模型的 token 是:x1, x2, x3, ... 按句子顺序排列;推荐模型的特征却是:[性别, 年龄, 星级, 城市, 设备型号, 7天CTR, 30天ratio, 商品品类, 标签...]

这些特征之间没有天然顺序,也没有上下文关系。
c 没有空间邻接关系

在图像中:(x,y) 像素和旁边像素关系固定,卷积核可以移动;而推荐中的特征:年龄和城市之间没有空间关系,CTR 和 用户 id 之间也没有"邻居",模型不能像 CNN 那样共享 pattern。
d 没有统一 token 表示

NLP → 全是 tokens

CV → 全是 pixels

推荐 → 每个特征意义完全独立,没有 "word-like token" 可以堆叠大模型去捕捉模式

二 实现细节

作为一个全新的生成式推荐架构,架构图如下:

2.1 Input

先梳理下DLRMs的输入,一版有三类,类别特征(Sparse)/数值特征(Dense or Numberical)/序列特征(varlen sparse or sequence)。

这里的输入把类别特征和序列特征融合到了一起,至于数值特征则直接摒弃。GR 把所有特征变成一个长序列,让模型直接"自己学"这些统计特征。当序列足够长时,GR 能表达的特征空间 ≈ DLRM 全部特征,甚至更强。(We sequentialize and unify the heterogeneous feature space in DLRMs, with the new approach approximating the full DLRM feature space as sequence length tends to infinity

分为主序列和辅助序列(We first select the longest time series ... as the main time series/
The remaining features are generally time series that slowly change over time

2.1.1 主时间序列(main time series)

首先出场的是一个统一的主时间序列(main time series),把所有 "用户 × item 的交互行为(及 item 的属性)",按时间合并起来形成的最长行为序列,是 GR 序列化的骨架,对应架构图里的E/F,形成 ϕ i \phi_i ϕi 序列。
a 用户原始行为日志

假设这是一个短视频+权益+套餐混合推荐场景,某个用户过去的行为日志如下(按时间):

时间 行为类型 item_id item类型 主题/标签
t1 展示曝光 990027088 流量包 「省内流量」「5G」
t2 点击 990027088 流量包 「省内流量」「5G」
t3 展示曝光 3700066543 宽带升级 「千兆宽带」「家庭」
t4 浏览详情 3700066543 宽带升级 「千兆宽带」「家庭」
t5 点击 37100001862 短期流量包 「叠加包」「短期」
t6 订购成功 37100001862 短期流量包 「叠加包」「短期」
t7 展示曝光 990024443 视频会员权益 「视频」「会员」「权益」
t8 点击 990024443 视频会员权益 「视频」「会员」「权益」

这是典型的:

"items user engaged with" = 用户与具体产品(item)的交互行为序列


b 把行为序列当作 main time series

在 GR 里,这些会先被视为一条主时间线

复制代码
t1: 展示 item 990027088
t2: 点击 item 990027088
t3: 展示 item 3700066543
t4: 浏览 item 3700066543
t5: 点击 item 37100001862
t6: 订购 item 37100001862
t7: 展示 item 990024443
t8: 点击 item 990024443

此时:

  • 只关心"用户 × item × 行为 × 时间"
  • 还没合并城市/套餐/星级等慢特征
  • 这条就是 GR 所说的 "最长的时间序列",用来当作 main series

c main series 在 GR 里的 token 形式

模型不会直接看到 "点击 流量包" 这种中文,而是拆成一串 token;

一个时间步(比如 t5)可能被编码成:

复制代码
[TS=t5]
[ACTION=click]
[ITEM_ID=37100001862]
[ITEM_TYPE=short_data_pack]
[ITEM_TAG=叠加包]
[ITEM_TAG=短期]
[SURFACE=homepage]      # 来自哪个入口

那么整条 main time series 就会变成类似:

复制代码
# t1
[TS=t1] [ACTION=impression] [ITEM_ID=990027088] [ITEM_TYPE=data_pack] [TAG=省内流量] [TAG=5G]

# t2
[TS=t2] [ACTION=click] [ITEM_ID=990027088] [ITEM_TYPE=data_pack] [TAG=省内流量] [TAG=5G]

# t3
[TS=t3] [ACTION=impression] [ITEM_ID=3700066543] [ITEM_TYPE=broadband] [TAG=千兆宽带] [TAG=家庭]

# t4
[TS=t4] [ACTION=view_detail] [ITEM_ID=3700066543] [ITEM_TYPE=broadband] [TAG=千兆宽带] [TAG=家庭]

# t5
[TS=t5] [ACTION=click] [ITEM_ID=37100001862] [ITEM_TYPE=short_data_pack] [TAG=叠加包] [TAG=短期]

# t6
[TS=t6] [ACTION=purchase] [ITEM_ID=37100001862] [ITEM_TYPE=short_data_pack] [TAG=叠加包] [TAG=短期]

# t7
[TS=t7] [ACTION=impression] [ITEM_ID=990024443] [ITEM_TYPE=video_privilege] [TAG=视频] [TAG=会员] [TAG=权益]

# t8
[TS=t8] [ACTION=click] [ITEM_ID=990024443] [ITEM_TYPE=video_privilege] [TAG=视频] [TAG=会员] [TAG=权益]

这串就是 纯粹的 main time series

  • 每个 step 都是 "用户对某个 item 的一次 engagement + item 自身属性"
  • 全部按时间排序
  • 不含城市、套餐、星级等慢特征(那些之后再 merge)
2.1.2 辅助时间序列(auxiliary time series)

定义:那些不是行为,但也带时间属性、且变化速度比行为慢很多的特征序列。

换句话说:"所有除了主行为序列之外的、随时间缓慢变化的特征,都叫 auxiliary time series。"

一般有用户人口属性(demographics)、用户户长期偏好特征(interests)、用户设备/系统属性(device)等内容。对应架构图里,G 和 H 都是 auxiliary time series,但它们代表的是不同的"缓变特征类型"

进一步的举个例子

Auxiliary 1:套餐(main_plan_in_use)

真实时间序列例子:

复制代码
t1:   套餐 = 99元全国通用
t300: 套餐 = 99元全国通用
t500: 套餐 = 129元5G融合
t900: 套餐 = 129元5G融合

→ Segment 压缩:

复制代码
t1:   plan=99元
t500: plan=129元

Auxiliary 2:城市(u_city)

真实城市变化日志:

复制代码
t1:   city=济南
t200: city=济南
t400: city=青岛
t800: city=青岛

→ Segment 压缩后:

复制代码
t1:   city=济南
t400: city=青岛
b 创建 auxiliary feature tokens

转换成 token 形式:

套餐 tokens(G 系列):

复制代码
[TS=t1]   [FEAT:plan=99]
[TS=t500] [FEAT:plan=129]

城市 tokens(H 系列)

复制代码
[TS=t1]   [FEAT:city=济南]
[TS=t400] [FEAT:city=青岛]

c 按时间戳 merge 到 main time series**

合并规则:

按真实时间顺序,将所有 auxiliary tokens 插入 main sequence。
(同时间戳可并列插入)


最终 Unified GR 序列(合并后的完整序列)
🔹 t1 时刻发生了 3 件事:城市、套餐变化、行为
复制代码
[TS=t1] [FEAT:city=济南]
[TS=t1] [FEAT:plan=99]

[TS=t1] [ACTION=impression] [ITEM_ID=990027088] [ITEM_TYPE=data_pack] [TAG=省内流量] [TAG=5G]

🔹 t2~t6:正常行为事件(没有辅特征变化)
复制代码
[TS=t2] [ACTION=click]        [ITEM_ID=990027088]    [ITEM_TYPE=data_pack]        [TAG=省内流量] [TAG=5G]

[TS=t3] [ACTION=impression]   [ITEM_ID=3700066543]   [ITEM_TYPE=broadband]        [TAG=千兆宽带] [TAG=家庭]
[TS=t4] [ACTION=view_detail]  [ITEM_ID=3700066543]   [ITEM_TYPE=broadband]        [TAG=千兆宽带] [TAG=家庭]

[TS=t5] [ACTION=click]        [ITEM_ID=37100001862]  [ITEM_TYPE=short_data_pack]  [TAG=叠加包] [TAG=短期]
[TS=t6] [ACTION=purchase]     [ITEM_ID=37100001862]  [ITEM_TYPE=short_data_pack]  [TAG=叠加包] [TAG=短期]

🔹 t7~t8:继续行为事件
复制代码
[TS=t7] [ACTION=impression]  [ITEM_ID=990024443] [ITEM_TYPE=video_privilege] [TAG=视频][TAG=会员][TAG=权益]
[TS=t8] [ACTION=click]       [ITEM_ID=990024443] [ITEM_TYPE=video_privilege] [TAG=视频][TAG=会员][TAG=权益]

🔹 t400:城市变成青岛 → 插入 auxiliary token
复制代码
[TS=t400] [FEAT:city=青岛]

(此时 main series 可能没有行为,但 auxiliary token 一样会出现在序列里)


🔹 t500:套餐升级 → 插入 auxiliary token
复制代码
[TS=t500] [FEAT:plan=129]

⭐ 最终 unified sequence(整合后序列完整版)
复制代码
# 序列开始
[TS=t1] [FEAT:city=济南]
[TS=t1] [FEAT:plan=99]
[TS=t1] [ACTION=impression] [ITEM_ID=990027088] [ITEM_TYPE=data_pack] [TAG=省内流量] [TAG=5G]

[TS=t2] [ACTION=click] [ITEM_ID=990027088] [ITEM_TYPE=data_pack] [TAG=省内流量] [TAG=5G]

[TS=t3] [ACTION=impression]  [ITEM_ID=3700066543] [ITEM_TYPE=broadband] [TAG=千兆宽带] [TAG=家庭]
[TS=t4] [ACTION=view_detail] [ITEM_ID=3700066543] [ITEM_TYPE=broadband] [TAG=千兆宽带] [TAG=家庭]

[TS=t5] [ACTION=click]    [ITEM_ID=37100001862]  [ITEM_TYPE=short_data_pack]  [TAG=叠加包] [TAG=短期]
[TS=t6] [ACTION=purchase] [ITEM_ID=37100001862]  [ITEM_TYPE=short_data_pack]  [TAG=叠加包] [TAG=短期]

[TS=t7] [ACTION=impression] [ITEM_ID=990024443] [ITEM_TYPE=video_privilege] [TAG=视频] [TAG=会员] [TAG=权益]
[TS=t8] [ACTION=click]      [ITEM_ID=990024443] [ITEM_TYPE=video_privilege] [TAG=视频] [TAG=会员] [TAG=权益]

# 缓变特征变化点
[TS=t400] [FEAT:city=青岛]
[TS=t500] [FEAT:plan=129]

🎯 这就是 GR 中 "序列化 + auxiliary merge" 的完整过程

2. Output

传统推荐系统中,召回和排序通常是两个独立阶段;而 GR 通过将 item 与用户 action 交错编码进同一序列,使"推荐什么"和"这个推荐是否会被点击"都成为生成式模型的一部分,从而在统一的序列建模框架下同时支持召回和排序。

召回(Retrieval) 阶段输出
In recommendation system's retrieval stage, we learn a distribution
p ( Φ i + 1 ∣ u i ) p(\Phi_{i+1}\mid u_i) p(Φi+1∣ui) over Φ i + 1 ∈ X c \Phi_{i+1}\in \mathcal{X}_c Φi+1∈Xc, where u i u_i ui is the user's representation at token i.
学习的是给定当前用户状态,对所有候选 item 打分/建模偏好分布
Φi+1 表示"下一个候选 item 的特征表示"(不是 token id,而是 item 的语义特征)

与语言模型不同的是,推荐序列中的下一个 token 并不一定是正样本,且序列中还包含大量没有 engagement 语义的特征 token,因此 retrieval 的监督是稀疏且不连续的。

排序(Ranking) 阶段输出
ranking task to be formulated as p ( a i + 1 ∣ Φ 0 , a 0 , Φ 1 , a 1 , ... , Φ i + 1 ) p(a_{i+1} \mid Φ_0, a_0, Φ_1, a_1, \ldots, Φ_{i+1}) p(ai+1∣Φ0,a0,Φ1,a1,...,Φi+1)

简单来说,就是在看到目标 item Φᵢ₊₁ 的前提下,预测用户会采取什么行为

二. 模型细节:

整体是一个Encoder结构,如下图所示,多层堆叠,每一层为定制化的单元结构,叫Hierarchical Sequential Transduction Unit (HSTU) ,在图里是用一种颜色表示(蓝色/紫色/黄色都是一个Unit). 每个单元结构一共有三个子层:1)Pointwise Projection 2)Spatial Aggregation 3)Pointwise Transformation。

1 Pointwise Projection

投影层,如下公式所示

X ∈ ℝ^{L × d}, L为token数量,d是hidden dim, f i ( X ) f_i(X) fi(X) 是个MLP, ϕ 1 \phi_1 ϕ1 是SiLU非线性变换,这里和经典的Transformer的结构有两点明显的差异:

1) 首先是引入了一个U,在标准 Transformer 中,attention 的输出是通过
Attention ( Q , K , V ) + X \text{Attention}(Q,K,V) + X Attention(Q,K,V)+X

的 residual 方式回到主干;而在 HSTU 中,额外引入了一条并行的 用户状态流 U U U ,并在后续通过逐点乘(gating)的方式对 attention 结果进行调制。 也就是说, U U U 并不是一个简单的 residual,而是一个可学习、可调节强度的控制信号 ,用于在不使用 softmax 的前提下稳定 attention 的输出并保留偏好强度信息。

2) 其次 操作上存在差异 ,在标准 Transformer 里,我们通常会说 Q、K、V 各自有一套线性映射 ,每个 head 都对应一组 W Q , W K , W V W_Q, W_K, W_V WQ,WK,WV。HSTU 这里做的事情,其实不是换了一种能力更弱的做法,而是换了一种更工程友好的实现方式 :它直接用一个大的线性层,一次性把需要的表示全算出来: W = [   W U ∣ W Q ∣ W K ∣ W V   ] W = [\, W_U \mid W_Q \mid W_K \mid W_V \,] W=[WU∣WQ∣WK∣WV]

也就是说,一次线性变换得到一个 4 d 4d 4d 维的结果,然后再按通道把它拆成 U , Q , K , V U, Q, K, V U,Q,K,V。

从表达能力上讲,这和"每个分支单独一个 W W W"是完全等价的 ,并没有少学任何东西;差别主要体现在工程和结构设计上:

a 算得更省:一次大矩阵乘就能搞定,比反复对同一个输入做多次小矩阵乘更省访存,也更容易做 kernel fusion,在 GPU 上跑得更快;

b 数值更稳: U U U 在 HSTU 里不是普通的 residual,而是后面要用来做 gating 的用户状态流。让 U U U 和 Q K V QKV QKV 都来自同一个变换,能保证它们在尺度和分布上是一致的,训练起来更稳定。

c 语义更清楚:这种写法在结构上明确表达了一点------U U U 和 Q K V QKV QKV 本质上是同一份表示的不同"用途",而不是额外拼出来的一条旁路

2 Spatial Aggregation

Attention计算方式和Transformer的也有差别,经典的Transformer如下公式:

本文无softmax,第一项是QK的乘积,第二项为bias项,p,t分别为positional(位置)和temporal(时间)的信息。 ϕ 2 \phi_2 ϕ2还是SiLU函数。

1)对于bias项目,论文中没有给出公式,扒了下代码,attention里面没有这一项,其实这个bias是加在input embedding的。
Step 1:把每个 token 的"位置 id"算出来(pos_inds)
pos_inds 是一个 [B, max_seq_len] 的整数矩阵,每个整数表示"这个位置用第几个 position embedding"。

  • 越靠近序列结尾(越新)→ position id 越小
  • 越靠前(越旧)→ position id 越大
    ** Step 2:把每个 token 的"时间差桶 id"算出来(ts)**
    它会先找到每个序列的 query_time = 最后一个 token 的 timestamp
    然后对每个位置算:
    Δ t = q u e r y _ t i m e − t i m e s t a m p [ t o k e n ] \Delta t = query\_time - timestamp[token] Δt=query_time−timestamp[token]
    再除以 60(大概按分钟尺度),再做 sqrtlog 压缩,最后取整 → 得到 bucket id
  • 离最后一个 token 越近(越新)→ bucket id 越小
  • 越久远 → bucket id 越大
    Step 3:查表拿到两个 embedding
  • position_embeddings = pos_embeddings[pos_inds]
  • time_embeddings = ts_embeddings[ts]
    它们都是 [B, max_seq_len, d] 的张量。
    Step 4:加到 token embedding 上

对每个 token:
x ′ = x + p o s _ e m b + t i m e _ e m b x' = x + pos\_emb + time\_emb x′=x+pos_emb+time_emb

2) 本文的工作抛弃了softmax的归一化,原因在3.1Pointwise aggregated attention部分进行了阐述:

  • 推荐里面除了排序还需要有强度信息,softmax只能做相对排序,不能兼顾intensity信息
  • softmax适合固定类别集合,但是推荐是流式场景,items的数量经常变化,导致softmax分母一
    直在变化,历史分数不再有意义。
    本工作中放弃了概率分布,直接建模"可加的强度",通过 pointwise aggregated attention 将 attention 从"概率分布建模"转为"强度建模":历史交互的贡献以可加形式累积,从而保留偏好强度;同时,由于不再依赖全局归一化分母,attention 分数不受候选 item 集合变化的影响,更适合推荐系统中的流式、非平稳场景。
    对于无归一化后延伸数值稳定性问题,则通过 U-gating 与 LayerNorm 共同保证。

    除了理论验证,文章也做了对应的实验,没有softmax的HR@10/50指标都有不同程度的提升。

3 Pointwise Transformation

这里的设计实际上是对 3.1 中 softmax attention 缺乏强度建模能力这一问题的直接改进。由于 softmax 只能刻画相对重要性而无法表达偏好强度,HSTU 引入了显式的 U-gating 机制。U 在功能上类似于 MoE 中的 gate,作为一个强度缩放器,对 pointwise aggregated attention 的结果进行逐点调制,从而补足了 softmax 在强度建模上的不足。

Ref:
Transformer解读
论文地址
git地址
知乎解读

相关推荐
liulanba2 小时前
AI Agent技术完整指南 第一部分:基础理论
数据库·人工智能·oracle
自动化代码美学2 小时前
【AI白皮书】AI应用运行时
人工智能
小CC吃豆子2 小时前
openGauss :核心定位 + 核心优势 + 适用场景
人工智能
一瞬祈望2 小时前
⭐ 深度学习入门体系(第 7 篇): 什么是损失函数?
人工智能·深度学习·cnn·损失函数
徐小夕@趣谈前端2 小时前
15k star的开源项目 Next AI Draw.io:AI 加持下的图表绘制工具
人工智能·开源·draw.io
优爱蛋白2 小时前
MMP-9(20-469) His Tag 蛋白:高活性可溶性催化结构域的研究工具
人工智能·健康医疗
阿正的梦工坊2 小时前
Kronecker积详解
人工智能·深度学习·机器学习
Rui_Freely2 小时前
Vins-Fusion之ROS2(节点创建、订阅者、发布者)(一)
人工智能·计算机视觉
快降重2 小时前
投稿前的“精准体检”:自查查重,如何选择可靠的第三方工具?
人工智能·aigc·写作·降重·查重·降ai