← 论文沉淀 / 🗂 Semantic ID · ·RecSys 2026

Guess Where You Go: Generative Next Point-of-Interest Recommendation in Amap

Penglong Zhai, Bowen Zheng, Jie Li, Yifang Yuan, Yue Liu et al. (11 人)

AMAP, Alibaba Group

把 SID tokenizer 的训练目标从重建换成纯对比:融合文本、图像、空间、协同四路 POI 表征后做 Gumbel-softmax 软残差量化,量化结果与各模态做 NT-Xent,不再用 MSE 重建,目标是提高 POI 之间的可区分度。拿到 SID 后,在 Qwen2.5 上做 SID/Geohash/描述对齐与轨迹语料的继续预训练、SFT,再用曝光日志做 EAKTO(带正奖励锚定和条件负样本更新的 KTO),把 0.5B 模型部署在高德首页「猜你想去」,线上 P-CTR +5.83%。

Contrastive RQ TokenizerNext POI RecommendationMulti-modal SID FusionExposure-Aware KTOSpatio-Temporal Continued Pretraining
📝 我的备注
加载中…

核心思路

Gwhere 是高德在首页「猜你想去」卡片上落地的生成式 next-POI 推荐系统,核心改动有三处:

  1. SID tokenizer 不用重建损失,只用对比损失:作者认为 RQ-VAE 的 MSE 重建目标和生成式检索要的「item 之间可区分」不一致,长尾 POI 容易码坍缩。于是先把文本、图像、空间、协同四路 POI 表征做注意力融合,再做 Gumbel-softmax 软残差量化,最后让量化向量和每个模态的原始表征做 NT-Xent 对比。
  2. 用两类领域语料继续预训练 LLM:SID-Geohash-描述三元组解决 SID 和 Geohash 不在词表里的问题;签到轨迹叙述语料让模型学通勤、周末出游这类时空规律。
  3. EAKTO 偏好对齐:KTO 只需要「好/不好」的二元信号,正好对应曝光日志里的点击和曝光未点。作者在 KTO 上加了正奖励锚定和条件负样本更新,处理推荐场景中的奖励漂移和噪声负样本。

上线的是 Qwen2.5-0.5B 版本,p99 约 30ms,一个月 A/B 对比生产级级联排序,P-CTR +5.83%、U-CTR +6.20%、负反馈率 -11.11%。

整体实现思路

  • 离线训练:POI 多模态特征 → 对比式软 RQ 得到 3 层 SID → Qwen2.5 继续预训练(10B token)→ SFT(三级用户画像 + 最近 50 条签到 + 当前时空情境 → 目标 POI 的 SID)→ EAKTO(点击为正,曝光未点为负)。
  • 在线服务:数据服务引擎把用户画像、最近签到、当前时间、位置、天气拼成指令 prompt,模型生成 SID,再查 SID→POI 表还原成可读地点返回客户端。
  • 问题形式化:p(p_{u,m+1} | h_u, l_u, s_u) = ∏_{k=1}^{L} p(c^k | S_u, l_u, s_u, c^{<k}),S_u 是历史签到的 SID 序列,l_u 是当前位置,s_u 是情境(天气、节假日、画像)。

子模块实现(可复现细节)

1. 多模态特征与融合

模态编码方式
文本 z_text预训练语言模型(如 BERT)编码标题、描述
图像 z_img预训练 ViT 编码 POI 图片(有图时)
空间 z_spatialGraphSAGE 编码空间图,边由 Haversine 距离构建(实现细节节说还结合 mobility flow)
协同 z_cf用户-POI 交互矩阵上做 ALS

每个模态先投影到共享 d 维空间,再用可学习注意力向量 q 加权:

  • a_m = exp(q^T z_m) / Σ_{m’} exp(q^T z_{m’})
  • z = Σ_m a_m z_m

2. 软残差量化 + 纯对比损失

  • 初始化 L 个可学习码本 C_l = {e_l^k, k=1..K},r_0 = z。
  • 每层 Gumbel-softmax 软分配:c_l^k ∝ exp((−‖r_{l−1} − e_l^k‖² + ε_l^k)/α),ε ~ Gumbel(0,1),α 退火。
  • 残差更新:r_l = r_{l−1} − Σ_k c_l^k e_l^k;量化表示 ẑ = Σ_l Σ_k c_l^k e_l^k。
  • 损失:L = −Σ_{m∈M} log [ exp(ĥ·h_m⁺/τ) / Σ_{h⁻∈B} exp(ĥ·h⁻/τ) ],ĥ = g(ẑ),h_m⁺ = g(z_m),g 是 2 层 MLP,τ = 0.1,负样本取 batch 内。没有重建项。
  • 默认配置:96 维潜向量,3 层码本,每层几十个码字(公开集 48,AMAP「更大」,未给具体数)。SID 写成 <a_82><b_59><c_191> 这种形式。

论文没有说明 SID 冲突(多个 POI 同 SID)如何消歧。

3. 时空继续预训练(CPT)

两类语料(均来自脱敏的高德轨迹和 POI 元数据):

语料内容例子
SID-Location-Description(SID, 6 位 Geohash, 文本描述) 三元组「天坛公园是北京的景点,位于 ,SID 为 <a_82><b_59><c_191>」
时空行为序列按时间排序的签到,带时间戳、动作类型、情境标注、画像提示「6 月 10 日(工作日)早 7 点去了 <a_124><b_192><c_41>(公司);晚 6 点搜索了 …(餐厅);晚 9 点导航到 …(家)」

两个训练目标:

  • Masked SID Prediction:在行为语料上根据周围签到、时间戳、情境预测被遮住的 SID。
  • SID-Attribute Alignment:在对齐语料中随机打乱 SID,训练模型区分匹配和不匹配的 (SID ↔ Geohash ↔ 描述)。

CPT 最终用了 10B token。作者观察到效果随 token 量近线性增长、尚未饱和。

4. SFT

  • Prompt:用户画像(静态画像、长期偏好、周期性需求)+ 最近签到的 SID 序列 + 当前位置/时间/情境(天气、交通),要求推荐下一个 POI。
  • Response:用户实际点击或交互的 POI 的 SID,交叉熵训练。
  • 超参:Qwen2.5 底座,lr 1e-5 常数,warm-up 20 步,200 张 H20,每卡 batch 16,梯度累积 8,序列长度 4096,输入取最近 50 条签到。

5. EAKTO(Exposure-Aware KTO)

KTO 在推荐场景里的两个问题:

  • 奖励漂移:训练久了正负样本的奖励都变成负数,对点击样本的偏好被削弱。
  • 噪声负样本:未点击常常是没注意、加载慢等临时原因,并不代表不喜欢,模型会变得过于保守。

损失形式:

  • r_θ(x,y) = log π_θ(y|x) / π_ref(y|x),z_0 = KL(π_θ(y’|x) ‖ π_ref(y’|x))
  • L_EAKTO = E_{x,y}[ λ_y − v(x,y) + α · 1[y∈Y_D] · σ_D(x,y) ]
  • 正样本(点击):v = λ_D · σ(β(r_θ − z_0))
  • 负样本(曝光未点):v = λ_U · 1[r_θ > 0] · σ(β(z_0 − r_θ)),只有模型错误地给负样本正奖励时才更新(条件负样本更新)
  • σ_D(x,y) = max(0, log π_ref(y|x)/π_θ(y|x)),即点击样本奖励跌到 0 以下时加罚(正奖励锚定;分子分母方向按 PDF 排版还原,推断)
  • 超参:λ_D = λ_U = 1.0,β = 0.1,α = 0.3

6. 在线服务

  • continuous batching + PagedAttention 管 KV-Cache。
  • 分级监控:P0 看 token 吞吐和端到端延迟,P1 看 KV-Cache 命中率和显存碎片。
  • 异步 handler + 心跳检测处理长尾请求,避免高峰期 worker 超时。
  • 结论节提到 PD 分离和 multi-token prediction,正文没有展开。

实验设置与结果

数据集

数据集用户POI交互平均序列长
NYC1,0835,135104,074136
TKY2,2937,873361,430195
CA6,59214,027349,37553
AMAP10,000k83,100k380,000k38

预处理沿用 GNPR-SID:去掉交互少于 10 的 POI 和用户,按时间 80/10/10 切分,每个用户最后一次访问作为目标,指标为 Acc@1。

公开数据集(Acc@1,基线数字引自 [13,26])

模型POI/User 表示NYCTKYCA
STHGCNRID/UID0.27340.29500.1730
ROTANRID/UID0.31060.24580.2199
LLM4POIRID/UID0.33720.30350.2065
GNPR-SID(LLaMA-7B)SID/UID0.36180.30620.2403
Gwhere-0.5BSID/UP0.37050.31250.2555
Gwhere-1.5BSID/UP0.37270.31700.2585
Gwhere-3BSID/UP0.38720.32020.2611
Gwhere-7BSID/UP0.40270.33100.2721

Gwhere-7B 相对 GNPR-SID 提升 11.3% / 8.1% / 13.2%。注意 Gwhere 用的是用户画像(UP),基线用的是 one-hot UID。

AMAP 对比级联排序(Acc@1)

Baseline 级联0.5B1.5B3B7B
0.26750.28200.28620.29230.3071(+14.8%)

消融(Gwhere-3B,AMAP)与偏好对齐对比

消融Acc@1
Gwhere(完整)0.2923
w/o SID(换随机 ID)0.2455(−16.01%)
w/o RL0.2580(−11.73%)
w/o CPT0.2632(−9.96%)
w/o SFT0.2725(−6.77%)
w/o ST(去掉 prompt 中时空描述)0.2785(−4.72%)
w/o SLD(去掉 SID-位置-描述语料)0.2822(−3.46%)
对齐方式Acc@1
SFT-only0.2492
SFT + DPO0.2634
SFT + GRPO0.2759
SFT + EAKTO0.2820

(对齐表的 0.2820 与 Gwhere-0.5B 一致,推断这张表用的是 0.5B。)

其他分析

  • 模态:逐个叠加文本、图像、空间、协同,Acc@1 单调上升,空间特征单独增益最大(仅有图,没有数值)。
  • 上下文长度:2048 token(约 30 条签到)之前增益明显,4096(约 50 条)之后基本饱和。
  • 延迟:50 QPS、2×H20 压测,级联 p99 约 20ms,Gwhere-0.5B 约 30ms;7B 离线最好但成本过高,最终上线 0.5B。

线上 A/B(高德首页「猜你想去」,一个月,相对生产级联)

版本P-CTRU-CTRSC-RateRE-RateAC-RateNF-Rate
w/o EAKTO+4.23%+4.59%+1.01%+1.65%+5.99%−5.85%
Gwhere+5.83%+6.20%+2.18%+2.60%+9.67%−11.11%

生产基线由多路召回、粗排、精排和认知重排组成,使用相同的请求上下文、流量分配、页面位置和指标口径。

思考与可参考价值

值得借鉴的:

  • EAKTO 适合直接迁移到电商。曝光未点是最便宜的负信号,但噪声大。「只有当模型给负样本正奖励时才更新」的门控,加上「点击样本奖励不能掉到 0 以下」的锚定,实现成本很低,线上负反馈率的改善(−5.85% → −11.11%)说明它确实改变了排序行为。迁移时应单独消融两项改动,并加入原版 KTO 作对照,论文这两点都没做。
  • SID 词表对齐语料。把 SID 和 Geohash、文本描述写成三元组,再加上打乱 SID 后的匹配判别任务,是一个很便宜的 CPT 设计。电商可以换成 SID + 类目/店铺/价格带 + 标题。
  • 规模取舍。0.5B 比级联多约 10ms p99,已经拿到大部分线上收益,7B 的离线提升没能转化成可上线方案。这对电商首页这类强时延场景是一个参考点。

需要打折的:

  • 对比式 tokenizer「优于重建式、缓解碰撞」是本文最核心的 SID 论点,但没有碰撞率、码本利用率、长尾分桶等直接证据,也没有和 RQ-VAE / RQ-KMeans 在同一底座下对比;唯一相关消融是 SID 对随机 ID。电商团队想换 tokenizer 时需要自己补这组对照。
  • 8310 万 POI 只用 3 层码本,码字数未公开,必然存在大量 SID 冲突,论文没说如何消歧。
  • 公开集基线数字是照抄的,Gwhere 多用了用户画像,底座模型也不同,11.3% 的提升不能全算在方法头上。
  • 只报 Acc@1。生成式召回在线上通常要 beam 出多个候选,Top-K 指标缺失会影响判断。
  • 去掉纯重建项之后,SID 是否还保留足够的语义层级(前缀相近即语义相近)也没有分析。如果下游依赖前缀做粗粒度召回或约束解码,这一点要先验证(推断)。