单卡 A6000 跑 Qwen -Drive:怎样留下可比较的第一条基线?
把 direct_planning 改成 reasoning_planning,再运行一次同一条命令,终端很快显示任务完成。能把这次耗时当作“加入推理之后的速度”吗?
Qwen-Drive 官方批处理脚本给出了一个容易漏掉的答案:它默认读取输出文件中的场景 token,跳过已经完成的场景。脚本先加载模型,再筛选尚未完成的 token;如果全部存在,就输出 nothing to do 并返回。改变模式不会自动使旧记录失效。你可能真的加载了权重,却根本没有执行新的轨迹预测。1
这也是单卡研究的第一个任务:留下能够证明“这一次究竟执行了什么”的基线。本文以 RTX A6000 为目标设备,核对截至 2026-10-18 的官方源码与权重元数据,给出待执行的研究方法。本文没有下载模型权重,没有运行 GPU、训练或驾驶评测;文中的硬件能力不是本机兼容性结论,全部延迟、峰值显存和成功率保持未知。
输出目录复用,会把配置变更变成一次假比较
官方runner先加载模型,再按已有 输出token 筛选;全跳过时没有新轨迹预测,不能称未使用GPU。配置变更不使旧token失效;新配置用新目录,同配置才续跑。作者源码机制示意,未执行。
官方load_done_tokens只读取已有token,主流程在模型加载后筛选pending;全部已有返回nothing to do。源码只说明没有新预测,加载阶段仍可能使用GPU;保留待执行与新增计数,固定配置才续跑。
官方 load_done_tokens() 读取完整 JSON 行,将 token 加入已完成集合,并清理中断留下的不完整尾行。后续筛选仅检查场景 token 是否已经存在,没有核对上一次的权重、模式、seed 或候选数。这个机制服务于同配置中断续跑,不负责判断两个实验是否等价。1
因此,假设第一次完成 SFT -direct,第二次更换 RL 专家却仍写入同一个 predictions.jsonl,旧 token 可以使新预测被跳过;若只剩部分场景,结果还可能混合不同配置。最后得到的文件行数正确、JSON 能解析,都不能说明文件对应当前实验。
更稳妥的动作是在推理前固定配置并计算配置摘要,用摘要和运行编号命名新目录。续跑只允许使用配置完全一致的目录;新实验不覆盖旧证据。官方也提供 --no-resume,但它会删除现有输出,适合确认可覆盖的临时目录,不能成为保留实验记录的替代方案。
目录应保存配置、原始终端日志、输入清单和输出,而不是只保存截图。核对日志里待执行场景数、新增预测数和输入 token 对应关系,才能区分模型加载完成、场景跳过和实际生成。脚本的整体墙钟还包括不同操作阶段,不能从“总耗时除以行数”直接得到单次纯模型延迟。
例如某次旧文件已含场景A和B,新配置仍请求A、B、C。按官方按token过滤的逻辑,只有C会进入这次待预测集合,最终文件却可能同时保留旧配置的A、B和新配置的C。这个三场景例子是对源码分支的说明,没有运行模型。若只按最终三行算平均耗时,分母把旧结果也算入,分子却包含这次加载和一次新预测,得到的数无法解释任一配置。要复查就保留请求清单与本轮新增清单,逐token关联配置目录;仅比较文件总行数不足以裁决。
先固定两个版本,再判断这张卡能否承载基线
代码commit与权重revision分开记录。sdpa是attention参数选择,pyproject仍声明flash-attn、causal-conv1d与flash-linear-attention依赖,不能保证消除安装或混合注意力限制。A6000目标设备未实测。
本次重新查询的官方代码提交为 28091c1532e869bc7aee91fc0aef6b3e6fd0b2e0,权重仓库 Qwen/Qwen-Drive-1.0-4B 的完整 revision 为 28484089a7cc8c335cf5089fb0745cf7c49b6eaa。它们是代码和发布权重两个不同版本,不能只记录 Git 提交,或者把会变化的 main 当成固定实验身份。种子研究里的短版本 2848408,在本次资料核对中已补成完整值。2
RTX A6000 属于 Ampere,官方标注 48GB GDDR6,名称不能与 RTX 6000 Ada 混用。容量和架构是选定研究设备的依据;是否可用、驱动是否匹配、目标算子能否安装,仍须在实际机器上验证。3
权重文件体积也不能代替峰值显存。主干、一个规划专家、配置及输入处理只是驻留的一部分;图像 token、语言生成缓存、专家中间张量、 CUDA 工作区和分配器预留都会影响运行峰值。SFT 与 RL 是替代的专家选择,不意味着基线必须同时加载两份;两个独立完整服务进程还可能重复驻留主干。本文不凭磁盘字节数宣布“48GB 一定够”,也不把量化设为第一次运行的默认条件。
安装前应阅读固定提交的 pyproject.toml:它声明 Python 至少 3.10, PyTorch 至少 2.8,Transformers 窗口为 >=5.14.0,<5.15.0,同时依赖 FlashAttention、causal-conv1d 和 flash-linear-attention。Parquet 图像归档还涉及 archive 可选依赖。4
官方 runner 的 attention 参数允许 sdpa 与 flash_attention_2,但 SDPA 选择并不会从项目依赖中删除这些扩展,也不能保证覆盖混合注意力的全部计算路径。先在独立环境保存 Python、驱动、CUDA 构建、依赖锁和安装日志;导入失败时首先定位版本与扩展,不把“换一个 attention 参数”误写成通用兼容方案。
第一条预测用 SFT-direct,把输入和输出都验到位
SFT支持direct与 reasoning ,RL官方建议reasoning;首次选SFT-direct单候选仅减少变量。候选共享主干缓存,多候选不是多场景吞吐,完整请求耗时不能除以候选数当请求延迟。
官方 cookbook 明确说明:planner-sft 支持 direct 和 reasoning 两种模式;planner-rl 的奖励优化来自 reasoning 条件下的 rollout,建议运行 reasoning 模式。选择 SFT-direct、单候选作为第一条基线,是为了减少初次排查的变量,不是宣称它的效果最好。5
下面是固定源码下的待执行示例。前提是已经按上述完整 revision 准备匹配的模型目录、规划专家、示例场景及图像归档,并完成目标设备上的依赖检查。路径需要对应自己的工作目录;此命令在本文中没有执行。
python scripts/run_planning.py \
--model Qwen-Drive-1.0-4B \
--planner Qwen-Drive-1.0-4B/planner-sft \
--scenes data/demo/planning_scenes.jsonl \
--image-archive data/demo/frames.parquet \
--output outputs/sft_direct_n1_run001/predictions.jsonl \
--mode direct_planning --num-samples 1 --num-steps 10 --seed 42 \
--dtype bfloat16 --attn-implementation sdpa
这里把积分步数和 seed 显式写出,防止默认值漂移。第一次执行要使用不存在或确认为空的新输出目录;不因路径名含 sft_direct 就假定内容真的来自该配置。候选数一、十步积分、seed 42 是这条待执行研究基线的选择,不是官方速度承诺。
输入验证要先于解释模型行为。原生规划协议包含三路视角、四个时刻,以及 16 个历史位姿点和速度、加速度、导航条件。重复一张图片来凑齐张数只适合测试张量通路,不能等同于真实多视角时间输入。整数导航与状态中的方向编码需要显式映射;相机顺序、图像时间、历史参考系和弧度单位都应保留。6
输出则逐个场景核对 token、候选数、(N,50,3) 形状和有限值,以及 reasoning 是否符合模式。五十个点对应当前自车参考下的未来平面位置和航向,单位是米与弧度;它们不是地图坐标或控制器指令。形状和有限值检查通过,最多证明接口生成了数值合法的轨迹;即使轨迹看起来平滑,也没有证明任务质量、底盘可执行性或安全。
数值验收也应保留失败场景的原始输出。比如某候选形状正确,但一个航向值是NaN,不能因为另外两条候选有限就把整个请求计作三个成功候选;反过来,全部值有限也不能证明轨迹在道路上。场景成功、候选数值合法与任务质量是不同观察,记录时须说明哪一种作为成功分母。这里的NaN例子仅用于解释检查逻辑,不是模型已出现该故障。
一个计时结果,必须同时说清入口、出口和未包含部分
作者选定窗口:CPU输入准备后,同步目标设备开始墙钟;调用plan_from_inputs、转CPU和数值检查,再同步结束。包含输入转移与返回检查,排除权重加载、图像读取解码、CPU 预处理 、网络与控制。非纯kernel或端到端服务计时。
allocated活跃张量与reserved分配器保留空间口径不同,重置后稳态峰值不代表加载峰值。固定场景20次线性插值p99只由样本决定,不是实际驾驶尾部;失败、超时、OOM另计,全部运行结果未知。
CUDA 默认异步执行。如果 CPU 侧刚提交 GPU 操作就停止墙钟,测到的可能主要是提交开销。PyTorch 官方要求在准确计时中使用适当同步或 CUDA event;具体方法应与要报告的边界匹配。7
对这篇文章的第一条基线,建议选择一个容易复核的边界:先完成 CPU 场景准备和预处理,在调用 plan_from_inputs 前同步目标设备并开始墙钟;等该调用返回、轨迹复制到 CPU 且形状与有限值检查完成,再同步并结束。这样计入 CPU 到设备的数据转移、规划计算、返回与检查,排除权重加载、图像读取解码和 CPU 预处理。
这只是一个选定的模型调用微基准,不是纯 CUDA kernel 计时,也不是端到端服务计时。若另外测 GPU event,它反映指定设备流中的设备时间,不能与包含 CPU 处理的墙钟混作同一个量。权重加载与第一次调用应单独保存;预热次数、预热是否成功、测量是否复用相同场景,也必须写进记录。
原研究包里的 profile_inference.py 与 qwen_factory.py 正是沿类似边界组织的待执行辅助脚本:factory 初始化及模型加载在稳态耗时之外,场景在 CPU 预处理,测量 callable 包含输入转移、轨迹返回和有限值检查。它们不是官方性能工具,本次只读取源码核对边界,未导入或运行 GPU 路径,不据此宣称已经复现。
显存同样需要写阶段。max_memory_allocated 与 max_memory_reserved 分别观察分配器中的活跃张量分配和保留空间,口径不同;后者也不等同于设备上全部进程的用量。稳态预热后重置峰值,只说明重置后的窗口,不能反向代表加载峰值。应分别保留冷启动、第一次调用、稳态窗口和设备其他进程情况,不能只剩一张 nvidia-smi 截图。
如果固定同一示例只重复二十次,那么 p99 只是这二十个耗时经过指定分位数算法得到的样本统计。原辅助脚本用线性插值,二十个数的 p99 落在最大的两个值之间,几乎由它们决定。它不能估计真实驾驶输入、并发和网络扰动共同作用下的尾部。失败、超时和 OOM 也要单独计数,不应删除失败后只报成功样本的漂亮分位数。
两个同名耗时还可能包含不同CPU工作:一个在调用返回就停止,另一个等轨迹拷回CPU并做有限值检查才停止。即便前后同步都写对,它们的出口不同,差值也不能全部归因于模型速度。保存计时代码入口、终点与所用设备,能让后来的人判断新增检查是否落在窗口内。若将CPU预处理移入窗口,应另起口径名称并保留原始样本,不能沿用旧延迟列覆盖。同步本身保证边界可观察,不会使两个不同窗口自动等价。
从基线增加一项变量,才能解释一次“更快”
SFT-direct 成功后,下一项可以是同权重、同输入和同候选数的 SFT-reasoning。官方说明 reasoning 模式会生成理由,并让专家读取包含生成文本的缓存;变化不仅是最终多显示了一段文字。记录语言 token 数和上限,才有机会解释耗时来自何处。5
再比较 SFT-reasoning 与 RL-reasoning,固定输入、seed、积分步数、候选数和 attention 后端,将专家 checkpoint 的变化单独列出。这个比较衡量不同专家在相同模式下的整体行为,不等于仅改变了训练中的某个因素;不能从一次推理比较反推 RL 的全部训练因果。
候选数则另外从一改为三或六。官方实现共享一次主干调用的缓存,将专家条件扩展为多个候选,因此六条候选不是六个不同场景的吞吐。应同时报告一次请求耗时、候选数与候选输出,不能把总时间除以六后称为完整请求延迟。如果最终需要从候选中选一条执行,选择与约束检查也要进入服务时延。5
量化、attention 后端、图像像素预算和积分步数在之后分别研究。它们作用于不同环节,也可能改变输出:减少权重驻留未必降低墙钟,改变图像预算会改变输入,减少积分步会改变动作采样过程。先记录未量化基线,再做单项变更;除延迟与显存,还要比较轨迹差、异常值和同协议任务指标。没有证据支持在 Ampere 上直接套用 Hopper 或 Blackwell 的低精度宣传结果。
最后仍需区分输出的时间与系统的时间。五十点覆盖 0.1 到 5 秒,表示轨迹网格,不表示模型每 0.1 秒完成一次。计划进入设备之前,还经历采集、同步、网络、排队、解码和下游控制;这些都不在前述 callable 微基准内。持续控制要另测从观测时间到实际采用计划的年龄、超时比例和过期计划处置。吞吐增加却继续执行旧观测生成的计划,并未解决闭环新鲜度问题。6
改变候选数时还要保留样本的归属:同一场景的一次请求返回六条轨迹,样本仍是一条请求耗时,六个候选可以各做数值检查。若应用需要一次输出一条动作,则候选选择的失败与成本也要归入实际应用口径。第一次记录先保留这些原始关系,比把多个层次压成一个“每轨迹毫秒数”更方便复查;它没有预先判断多候选一定更快或更好。
让第一条记录支持复查,而不是替未知结果填数
单条待执行记录保留代码与权重双身份、目标配置、续跑计数、同步计时边界及原始日志;实际硬件、延迟、显存、质量均UNKNOWN。没有权重下载、GPU、预测、训练或性能实测;36项仅静态核验。
下面是本文唯一主要产物:一条待执行的基线与计时边界记录。它把固定资料身份、将来运行配置和实际结果分开,执行前只填写已经知道的项;未知字段不能以源码静态检查通过代填。
status: NOT_RUN
code_commit: 28091c1532e869bc7aee91fc0aef6b3e6fd0b2e0
weights_repo: Qwen/Qwen-Drive-1.0-4B
weights_revision: 28484089a7cc8c335cf5089fb0745cf7c49b6eaa
weight_checksums: UNKNOWN # 在实际下载后逐文件计算
hardware_target: NVIDIA RTX A6000 48GB
hardware_observed: UNKNOWN
python_driver_cuda_and_dependency_lock: UNKNOWN
input_scene_and_image_hashes: UNKNOWN
config:
planner: planner-sft
mode: direct_planning
num_samples: 1
num_steps: 10
seed: 42
dtype: bfloat16
attention: sdpa
image_pixel_budget_and_reasoning_limit: UNKNOWN
run_directory_and_config_hash: UNKNOWN
resume_config_matches: UNKNOWN
requested_new_completed_skipped_failed_counts: UNKNOWN
timing:
scope: CPU-prepared inputs through CPU trajectory and validation
excludes: load, image I/O, decode, CPU preprocessing, network, control
synchronization: target device before start and after validation
cold_load_and_first_call_ms: UNKNOWN
warmup_and_repeat_counts: UNKNOWN
raw_latency_ms_and_percentile_method: UNKNOWN
peak_allocated_and_reserved_bytes_by_stage: UNKNOWN
output_token_shape_finite_checks: UNKNOWN
trajectory_quality_or_task_scores: UNKNOWN
observation_to_adoption_age_and_timeouts: UNKNOWN
raw_logs_and_predictions: UNKNOWN
这份记录形成的判断很有限,也很具体:能否确认目标机器上确实执行了指定配置的预测,这个测量包含什么,以及下一次改变变量时能否比较。它不承担机器人迁移、安全认证或完整驾驶评测的结论。
如果眼下只想确认文件和依赖是否齐全,可以先做不加载 GPU 的文件清单与版本检查;若现有规则控制就足够,也无需为了“接入模型”建设实时服务。等第一条真实记录存在,再讨论速度瓶颈和优化收益,才能让每次新增复杂度都有可检验的依据。
参考来源
-
官方批处理 runner,固定提交。本篇续跑、输出和参数事实来自此文件。 ↩︎ ↩︎
-
官方代码提交;权重固定 revision 文件树。完整 revision 已通过官方 API 核对,未下载权重。 ↩︎
-
NVIDIA RTX A6000 官方产品说明。硬件规格不代表本次运行结果。 ↩︎
-
官方 cookbook,固定提交;规划实现。模式和候选缓存共享事实来自官方说明与实现。 ↩︎ ↩︎ ↩︎
-
PyTorch CUDA 计时与内存管理说明。本篇计时边界由作者选择,不是官方 A6000 跑分。 ↩︎