在 NVIDIA 开源的 Alpamayo 项目中,实现了将连续的自车历史轨迹坐标离散化为 Token,并无缝注入到大模型的 Prompt 中,让大模型不仅“看懂”图像和文本,还能“理解”车辆过去的行驶轨迹。
本文围绕推理过程中 fuse_traj_tokens 函数的调用,解析了自车历史轨迹从连续坐标到离散 token、再到替换 prompt 占位符的过程,并与 OpenDriveVLA 的多模态特征注入机制进行了简要对比。


一、input_ids

在推理代码 test_inference.py 中,模型首先通过 HuggingFace 的 processor 对输入消息进行 tokenize,得到 input_ids:

# dict_keys(['input_ids', 'attention_mask', 'pixel_values', 'image_grid_thw'])
tokenized_data = data["tokenized_data"]    
input_ids = tokenized_data.pop("input_ids")
  • input_ids 是什么?
    是一个形状为 [B, seq_len] 的 PyTorch LongTensor,包含了 VLM 分词后的完整文本 Prompt。
  • 主要包括哪些字段?
    为了支持多模态和轨迹预测,input_ids 中不仅包含常规文本,还包括许多特殊的占位符 Token:
内容模块 说明
系统/用户 Prompt 常规的指令文字
图像占位符 对应 pixel_values 中的图片特征占位,如 <|image_pad|>
历史轨迹占位符 <|traj_history_start|><|traj_history|>...<|traj_history_end|>这是本文的核心,后续将被替换为真实的历史轨迹 Token
推理/生成标记 <|cot_start|>(思维链起始)、<|traj_future_start|>(未来轨迹生成起始)
  • input_ids 中的历史轨迹占位符
    上述表格中, input_ids 中包含了历史轨迹占位符 token。在构造消息时(helper.create_message),有这样一段设计:
num_traj_token = 48
hist_traj_placeholder = (
    f"<|traj_history_start|>{'<|traj_history|>' * num_traj_token}<|traj_history_end|>")

这意味着 prompt 中预先放了 48 个 <|traj_history|> token,前后分别用 <|traj_history_start|><|traj_history_end|> 包裹。这些 token 在 tokenize 后变成对应的整数 ID,占据 input_ids 中的固定位置。
这些占位符的作用就是预留的slot——后续会用量化后的真实轨迹 token 来替换。

二、自车轨迹的离散化与量化

大语言模型(LLM)的自回归架构只能处理离散的 Token(整数),无法直接理解连续的 XYZ 坐标。因此,Alpamayo 引入了 Delta Tokenizer(增量分词器),将连续的轨迹转化为离散的数字 ID。
在这里插入图片描述
量化过程主要分为以下 4 个关键步骤(核心代码位于 delta_tokenizer.pyencode 函数):

  • 1. 计算增量(Delta)位置
    模型不关心车辆在整个地图上的绝对坐标,只关心 “相对于上一步,车辆移动了多少”。
# shape: (1,16,3) --> (1,17,3), 对于3D tensor, 在第二维前面填充一个元素, 默认是0
xyz = torch.nn.functional.pad(fut_xyz, [0, 0, 1, 0, 0, 0])
xyz = xyz[:, 1:] - xyz[:, :-1]

通过相邻帧求差,将绝对坐标转换为步进增量。第一个时间步的增量是相对于原点 (0, 0, 0) 的偏移。这降低了对绝对坐标的依赖,使量化范围更集中。

  • 2. 归一化到 [0, 1] 区间
    为了统一量纲,根据配置的物理范围(例如 x/y 轴 [-4m, 4m],z 轴 [-10m, 10m]),将增量值线性映射到 [0, 1] 之间。
ego_xyz_max = torch.tensor(self.ego_xyz_max, dtype=xyz.dtype, device=xyz.device)
ego_xyz_min = torch.tensor(self.ego_xyz_min, dtype=xyz.dtype, device=xyz.device)
xyz = (xyz - ego_xyz_min) / (ego_xyz_max - ego_xyz_min)
  • 3. 量化为整数 Token
    将 [0, 1] 的浮点数映射到有限的词汇表(Bins)中。默认情况下,词汇表大小为 1000(num_bins=1000)。
# self.num_bins = 1000
xyz = (xyz * (self.num_bins - 1)).round().long()  # 乘以 999 并四舍五入
xyz = xyz.clamp(0, self.num_bins - 1)             # 安全裁剪到 [0, 999]

本质:这是一个向量量化编码器。连续的物理位移被离散化为了 0~999 的整数 Token。

  • 4. 展平与词表偏移
    将形状从 (B, Th, 3) 展平为 (B, Th×3)。对于 16 个历史时间步,每步 3 个 token(x, y, z),共输出 48 个 token。
    随后,在 tokenize_history_trajectory 中,还会加上一个 start_idxself.hist_token_start_idx = 154669),将历史轨迹 Token 偏移到独立的词表区间,与未来轨迹 Token 互不重叠,让模型能通过 ID 范围直接区分二者。

三、将量化 token 注入 Prompt

轨迹量化完成后,得到了一串离散的 Token 序列(形状为 [B, 48] 的 hist_idx),然后需要将其嵌入到 input_ids 中。
在这里插入图片描述
这一流程调用了 replace_pad_token 函数来实现:

def replace_pad_token(input_ids: torch.Tensor, new_ids: torch.Tensor, pad_idx: int) -> torch.Tensor:
    """Replace pad tokens in input_ids with new token values."""
    mask = input_ids == pad_idx
    return input_ids.masked_scatter(mask, new_ids)

找到 input_ids 中所有等于 pad_idx(占位符 ID = 155684)的位置,生成布尔掩码 mask;
使用 PyTorch 的 masked_scatter 操作,将量化好的 new_ids 按顺序填充到这些位置中。

四、OpenDriveVLA 与 Alpamayo 的 Prompt 构建差异

对比 Alpamayo 与 OpenDriveVLA,核心区别体现在视觉特征处理历史轨迹注入两个维度:
1. 视觉特征的注入:BEV感知特征 vs 原始 2D 图像

  • OpenDriveVLA:高度依赖 BEV(鸟瞰图) 3D 结构化感知结果。它将 3D 视觉特征(Scene/Track/Map)通过投影层映射为连续的特征向量序列,拼接到 Prompt 中。

📌 参考笔者对OpenDriveVLA特征嵌入的文章:
(四)OpenDriveVLA的5类Prompt设计与跨模态特征注入机制
(五)揭秘 OpenDriveVLA:结构化感知如何“跨模态对齐”大模型?

  • Alpamayo:放弃复杂的 BEV 特征提取,直接依赖视觉编码器提取的 2D 多视角图像特征,让大模型直接从原始 2D 图像中理解 3D 空间。

2. 历史轨迹的嵌入:文本化拼接 vs Token 量化

  • OpenDriveVLA:将历史轨迹(如过去 2 秒的坐标点)直接格式化为人类可读的文本字符串(例如 “Historical trajectory (last 2 seconds): [(-0.30,-17.01),…]”),作为文本 Prompt 的一部分直接拼接进去。LLM 的 Tokenizer 会将其切分为普通的数字 Token,完全依赖大模型的文本阅读与推理能力来理解这些物理坐标。

📌 参考笔者OpenDriveVLA推理数据加载的文章:
(三)深度拆解 OpenDriveVLA 推理数据加载:基于__getitem__()方法获取单帧数据

  • Alpamayo:历史轨迹作为独立的外部物理条件,将过去若干帧的真实历史轨迹序列(XYZ坐标+旋转)进行 Delta 量化,转为离散 Token,显式嵌入到 Prompt 序列中。

参考

  • 项目地址:NVIDIA/alpamayo
  • 核心文件:
    • src/alpamayo_r1/models/alpamayo_r1.py — 推理入口
    • src/alpamayo_r1/models/base_model.pyfuse_traj_tokenstokenize_history_trajectory
    • src/alpamayo_r1/models/delta_tokenizer.pyDeltaTrajectoryTokenizer.encode
    • src/alpamayo_r1/helper.py — prompt 模板构造

(本文为笔者阅读与跟踪ALpamayo开源代码后,撰写的CSDN原创文章,转载请注明出处。)

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐