DPGR: Dual-Domain Spatiotemporal Generative Retrieval for Intent-Aware Local Life Service Recommendation
Meituan (Dianping)
把本地生活推荐里的「内容点击」和「POI 收藏」看成同一个潜在意图的两种表现,不再当两个独立问题;生成目标也从 item 抬到 intent 层。DPGR 在 HSTU 上把内容/POI 行为用分域投影塞进同一序列,用时空状态(时段/位置/在家-出差-休闲)在输入重加权、注意力加一条状态分支、末位上下文 token 三处条件化;再用单层 VQ-VAE 把双域「类目+行为类型」量化成 256 个意图码,多标签预测 top-K 意图,每个意图码嵌入作为 prompt token 各自聚合出一个用户向量并行查 ANN。
核心思路
本地生活推荐(大众点评)在同一个 session 里同时有两类行为:内容域(读笔记、看视频、看点评)和 POI 域(点击、收藏餐厅、计划打卡)。论文的核心判断是:一次美食点评点击和随后的餐厅收藏,是同一个潜在意图的两次观测,因此统一建模是问题本身的要求,而不只是把两个模型拼在一起。
DPGR 在此基础上做了三件事:
- 双域统一序列:每条交互表示为 (item, action, context),内容和 POI 用独立的投影映射到同一空间,交给 HSTU 编码。
- 时空状态条件化:上下文(时段、位置、在家/出差/休闲)在输入、注意力、输出三处参与编码,不只作为一个静态特征。
- 意图级生成:不直接生成 item,而是用 VQ-VAE 把双域「类目+行为类型」量化成离散意图码,先多标签预测 top-K 意图,再用每个意图的码本嵌入作 prompt 得到 K 个用户向量,并行查 ANN。
整体实现思路
- 输入:
X = [画像 token × P, 行为 token × N, 目标上下文 token c],序列长度 1000。 - 骨干:4 层 HSTU,4 头,hidden 256,稀疏特征嵌入 32 维。
- 时空状态条件化:Token Reweighting(输入)→ Token Interaction(每层注意力加状态分支)→ Token Aggregation(末位上下文 token 作为聚合位)。
- 多意图:末位上下文 token 输出 → M=256 维 sigmoid 多标签意图预测;序列末尾追加意图 token(码本嵌入),意图 token 互相不可见,每个意图位置输出一个用户向量,用 InfoNCE 训练。
- 损失:
L = L_VQ + L_intent + L_item;码本 EMA 更新,与主梯度解耦。 - 在线:一次前向得到意图分布和所有意图 token 输出,取 top-K(K=3)个向量并行查 item ANN,每路 N 个。
子模块实现(可复现细节)
分域投影层(Field-wise Projection)
- 画像:
ê_profile(p) = LN_profile(F_profile(e_profile(p))) - 行为:
ê_seq(n) = LN_seq(F_domain(i_n)(e_i_n) || F_action(e_a_n) || F_ctx(e_c_n)),内容 item 用F_content,POI 用F_POI,参数独立。动机是两域特征结构不同(文本语义 vs 地理坐标),各自投影后再进共享空间。 - 目标上下文:
ê_c = LN_ctx(F_ctx(e_c)) H(0)为上述拼接,H(l) = HSTU(l)(H(l-1))。
Token Reweighting(输入层)
α_n = 2·σ(MLP(e_c || e_cn)) ∈ (0, 2),比较的是目标上下文与历史上下文(context-to-context),不是 item 相关性。ẽ_seq(n) = α_n · ê_seq(n),替换 H(0) 中的行为 token。- 取值范围 (0,2) 让它既能压低上下文不匹配的历史(比如上次出差时的餐厅搜索),也能适度放大一致的历史,同时保持有界以利于优化。
Token Interaction(注意力层)
每层 HSTU 打分拆成两项:
s_jn = (W_Q h_j)^T (W_K h_n) + (W_Q^c h_j^c)^T (W_K^c h_n^c)
- 第一项是全 token 注意力(覆盖画像、行为、目标上下文)。
- 第二项是状态分支,h^c 由每条交互的上下文嵌入得到,只在行为序列位置之间生效,画像和目标上下文 token 在这一项贡献为 0。
- 输出
AttentionValue_n = Σ_{j≤n} attentionScore(s_jn) · W_V h_j(HSTU pointwise 形式)。 - 论文的理由:双域序列里内容-POI 相似度信号更强,单一打分会把上下文亲和度淹没,加性拆分让上下文在每层都有独立、可学的路由信号。
Token Aggregation(输出层)
目标上下文 token 放在序列末位,在因果 mask 下看到全部前文,输出 h_c^(L) 作为意图预测的输入。
意图定义(VQ-VAE)
- 输入
e_cat&act = [e_cat^content; e_act^content; e_cat^POI; e_act^POI],即两域的类目/语义嵌入和行为类型嵌入拼接。 z = MLP_enc(e),intent_id = argmin_m ||z − q_m||²,ê = MLP_dec(q_intent_id)。L_VQ = ||ê − e||² + β·||z − sg(q)||²,β = 0.25,码本 EMA 更新,M = 256。- 附录 A:M ∈ {256, 512, 1024} 中 256 的码本利用率最高,更大码本会出现很少被激活的冗余码。
- 离散化在这里同时承担两个作用:给意图预测当伪标签;码本嵌入在推理时直接复用为 prompt token。论文认为这是连续多兴趣方法做不到的。
非自回归多意图预测
p̂ = σ(W_intent h_c^(L) + b) ∈ (0,1)^M,W ∈ R^{M×d}。- 多标签 BCE:
L_intent = −(1/M) Σ_m [y_m log p̂_m + (1−y_m) log(1−p̂_m)],y 是目标 session 内 item 出现过的意图 multi-hot。
意图条件化 item 生成
- 训练时:若目标 session 含意图 {intent1, intent2, intent3},把对应码本嵌入追加到序列末尾:
X+ = [..., c, intent1, intent2, intent3]。 - 专用 mask:意图 token 能看到所有画像/行为/上下文 token,意图 token 之间互不可见,防止主意图通过注意力把弱意图拉向同一模式。
- 每个意图位置输出
h_intent_m^(L)与该意图对应的真实 item 做 InfoNCE:L_item = −log [exp(h·e_i_m) / Σ_{j∈B} exp(h·e_i_j)],批内负例,温度 τ = 0.07。
在线服务
- 构造 X+ 时追加全部 M 个码本嵌入作为意图 prompt,一次前向同时得到意图概率与 M 个意图向量;取 top-K 意图的向量作为 query,各自查 item ANN 取 N 个,并行发出。
- 训练:Adam,lr 1e-4,weight decay 1e-5,batch 256 个 session。
实验设置与结果
数据集
| 数据集 | 用户 | Item | 交互 | 时空上下文数 |
|---|---|---|---|---|
| KuaiRand-Pure | 27,285 | 7,583 | 650,283 | 2 |
| Internal(点评) | 56.9M | 69.6M | 6.88B | 12 |
KuaiRand-Pure 只用来验证骨干改动不损伤单域表现;内部数据包含双域、行为类型和完整时空上下文(场景、时段、位置、出行状态),消融和线上都在内部数据上做。指标 Recall@N,N ∈ {100, 200, 500, 1000},线上召回配额 200;top3 版本是 3×N 候选按内积重排后取前 N。
主结果(Recall@N)
| 数据集 | 指标 | SASRec | ComiRec | TPG | HSTU | TBGRecall | DPGR | DPGR_top3 |
|---|---|---|---|---|---|---|---|---|
| KuaiRand-Pure | R@100 | 0.2578 | 0.2351 | 0.2965 | 0.3184 | 0.3227 | 0.3256 | 0.3265 |
| KuaiRand-Pure | R@200 | 0.3964 | 0.3610 | 0.4500 | 0.4737 | 0.4752 | 0.4828 | 0.4846 |
| KuaiRand-Pure | R@1000 | 0.8019 | 0.7615 | 0.8469 | 0.8561 | 0.8554 | 0.8575 | 0.8587 |
| Internal | R@100 | 0.0452 | 0.1643 | 0.1480 | 0.2274 | 0.2571 | 0.4152 | 0.5939 |
| Internal | R@200 | 0.0881 | 0.2546 | 0.2307 | 0.3206 | 0.3517 | 0.4777 | 0.6594 |
| Internal | R@500 | 0.1846 | 0.4205 | 0.3762 | 0.4732 | 0.5061 | 0.5744 | 0.7260 |
| Internal | R@1000 | 0.2962 | 0.5640 | 0.4893 | 0.5935 | 0.6330 | 0.6570 | 0.7674 |
(DSSM / GRU4Rec / ROTAN 更弱,例如内部 R@100 分别为 0.0262 / 0.0130 / 0.0227。)
消融(内部数据)
| 变体 | R@100 | R@200 | R@500 | R@1000 |
|---|---|---|---|---|
| w/o All(纯 HSTU) | 0.3002 | 0.3419 | 0.4500 | 0.6078 |
| w/o Intent | 0.3107 | 0.3589 | 0.4773 | 0.6426 |
| w/o State-Cond. | 0.3780 | 0.4378 | 0.5413 | 0.6345 |
| DPGR | 0.4152 | 0.4777 | 0.5744 | 0.6570 |
意图模块贡献最大;状态条件化在 R@100 上也有约 +0.037。
Scaling
序列长度从 200 到 10k,R@100 累计提升 24.9%;在同样长度下加深加宽仍有稳定增益,R@100 随 GFLOPs 近似对数线性增长(只给了图)。
线上 A/B(7 天,15% 流量,对照为线上双塔,累计值,p<0.05)
| 阶段 | 内容浏览 | 内容时长 | POI 点击 | POI 收藏 |
|---|---|---|---|---|
| Stage 1: GR-Base | +0.493% | +0.526% | +0.169% | +3.226% |
| Stage 2: +State-Cond. | +1.103% | +1.015% | +0.406% | +3.314% |
| Stage 3: +Multi-Intent | +1.486% | +1.015% | +0.527% | +5.209% |
部署在点评首页 feed 和沉浸式视频流,日活千万级。
延迟(高峰时段)
| 变体 | Avg (ms) | P99 (ms) | CPU |
|---|---|---|---|
| DPGR (K=1) | 2.7 | 15.5 | 30.93% |
| DPGR_top3 (K=3) | 3.1 | 33.1 | 55.73% |
意图码质量
- 102 个样本充足的码:top-1 类目占比均值内容 80.8%、POI 85.1%,中位数都是 100%。
- 4,115 条同时含内容类目和 POI 类目的记录中,码内 (内容, POI) 对语义相关的比例为 74.0%,随机打乱为 42.6%(1.74×)。
- 下一次点击类目命中率:DPGR POI 74.1% / 内容 73.9%;最强线上召回通道 70.7% / 69.0%。
- 示例:Code 1 = 日料/西餐/东南亚美食内容 ↔ 收藏对应餐厅;Code 143 = 酒吧/KTV/SPA;Code 226 = K12/语言/高教内容 ↔ 收藏辅导、语言培训机构。
思考与可参考价值
值得借鉴的地方
- 跨域意图码:电商里内容(短视频、直播、图文)和商品也是一对强耦合的双域。把两域的「类目 + 行为类型」拼起来做 VQ,可以得到跨域共享的粗粒度意图码,用作多路召回的路由,或者作为 SID 的粗前缀/条件 token。
- 上下文对齐门:
α = 2σ(MLP(e_c || e_cn))几乎零成本,可以直接用于大促/日常、搜索/推荐入口、国家、时段这类场景态,给长序列做场景去噪。 - 状态注意力分支:只在上下文子表示上额外加一组 QK,避免场景信号被 item 语义淹没,对 HSTU 类骨干改动很小。
- 意图 prompt + 互不可见 mask:把单向量 GR 变成一次前向出 K 个向量的多兴趣召回,比自回归 beam 生成 SID 便宜;也可以和历史压缩(Q-Former、PTR 等)叠加。
需要警惕的地方
- 离线口径有疑点:表 1 的 HSTU(0.2274)与消融中的 w/o All(0.3002)不一致;内部集上相对 TBGRecall +61% 的 R@100 与 KuaiRand 上不到 1% 的提升差距很大,论文归因于双域和时空信号,但缺少更细的拆解。
- top3 的离线评估是 3×N 候选重排后取 N,候选池本身更大,没有和同预算的多向量基线(ComiRec K=3 是有的,但骨干弱很多)做公平对照。
- 训练时用目标 session 的真实意图做 prompt,推理时用预测的 top-K,论文没有报告意图预测命中率。
- 「无额外延迟」:平均只多 0.4ms,但 P99 从 15.5ms 涨到 33.1ms,CPU 从 30.9% 涨到 55.7%;在线追加全部 256 个意图 token 也增加了序列长度。
- 意图码只有单层 256 个、基于类目+行为类型,本质是类目级多兴趣路由;只有 102 个码样本充足(推断利用率约 40%)。它和 SID 自回归生成不是一回事,借鉴时应定位为「多路召回 / 意图前缀」,而不是 item 标识。
- 表 8 正文写 POI +4.8pp,但表中 74.1% − 70.7% = 3.4pp,存在笔误。
落地前建议核对:意图预测 top-K 的召回率和错判时的损失;同候选预算下与 MIND/ComiRec 多向量的对照;只追加 top-K 意图 token(两次前向)与追加全部 M 个(一次前向)的成本权衡;长尾类目是否被少数高频码吞掉。