上篇讲了配置与成绩(56.8%),本篇是全部踩坑实录。每个坑按"现象→原因→解法→更好的办法"记录——最后的"更好的办法"是赛后复盘时最诚实的部分:这些坑里,其实有一半在发生前就能避开。
目录
第一幕:环境(5 个坑)
第二幕:训练(6 个坑)
第三幕:仿真渲染(8 个坑,约 10 小时——全场最贵的弯路)
第四幕:评测与提交(10 个坑)
如果重来:六个系统性优化
第一幕:环境(5 个坑)
1. flash-attn 编译 OOM nvcc fatal: Out of memory。24 核机器默认全核并行编译,每个 nvcc 进程吃约 4GB 内存。 解法:MAX_JOBS=2 限制并行数。 更好的办法:编译大型 CUDA 项目,第一反应就该查内存总量、设 MAX_JOBS——这 30 分钟是纯知识税,交一次就该记一辈子。
2. huggingface_hub 版本冲突 MoGe 安装时把 huggingface_hub 拉到 2.2.0,破坏 transformers 4.57.3。 解法:pip install huggingface_hub==0.34.3。 更好的办法:装新包前先 pip install –dry-run 看依赖变动;装完立刻 import 冒烟。连锁冲突要追根(是 MoGe 带进来的),只回滚不追根,下次还会炸。
3. git clone 503 / HuggingFace 下载慢 解法:ghfast.top 镜像 + AutoDL 学术加速 source /etc/network_turbo + 清华 pip 源。 更好的办法:国内云上开发,网络三件套(镜像、加速、源)应该写在开机第一天的配置脚本里,而不是等 503 了再查。
4. aria2c -c 续传文件损坏 断点续传解压报 unexpected end of file。 解法:删掉重下,不用 -c。 更好的办法:大文件下载后校验 hash(一条 sha256sum 的事)。宁可重下,不要信任续传的完整性。
5. LeRobot 数据格式不兼容 数据集 v2.1,训练框架要 v3.0。 解法:官方转换脚本(注意:就地修改,转换前备份)。 更好的办法:项目第一天把所有组件的版本要求列成一张核对表(数据格式、Python 版本、PyTorch 版本、CUDA 架构),半小时能避免后面无数小时。
第二幕:训练(6 个坑)
6. checkpoint 清理脚本 sort bug(代价最大的人祸) 清理脚本用字典序排序,global_step_10000 排在 global_step_9000 前面,脚本删掉了"字典序最大"的——也就是最新的 checkpoint,重训 1 小时。 解法:sort -t_ -k3 -n 数字序。 更好的办法:任何自动删除脚本上线前先 dry-run 打印待删清单人工过目;涉及数字的排序永远用 -n/-V。这条教训值 1 小时学费,终身打折。
7. 磁盘占满 checkpoint 写一半崩溃 单份完整 checkpoint 74G,多存两份就满,存盘瞬间峰值还要双倍空间。 解法:只保留 hf_ckpt(评测用)+ 最新一份 model/optimizer(续训用)。 更好的办法:训练启动前算清楚"每份 ckpt × 保留份数 × 2(峰值)< 磁盘余量",把磁盘预算写进启动检查清单,而不是等崩溃了再删。
8. batch=4 过拟合(最重要的方法论教训) 首次训练照抄配置模板:global_batch_size=4,10k 步,loss 收敛到 0.08——看起来很美。smoke 评测 7.7%,后来确认换了更大 batch 才正常。 解法:换大 batch 重训 50k 步。 更好的办法:低 loss + 低成功率 = 优化配置问题,不是再加步数。看到这个组合,第一反应应该是查 batch size / 学习率 / 数据量,而不是训更多步。另外:官方模板的超参也是要审的,模板假设的是多卡大 batch,单卡照抄就是 batch 4。
9. 中期评测 0% 的恐慌 训练中期去评测,0%,一度以为方向全错。 真相:数据指令碎片化(单指令 2-5 样本),50k 步累计才 0.73-1 个 epoch,欠拟合是正常的。 更好的办法:训练前就该算这笔账:总样本量 × 目标 epoch ÷ batch = 需要的步数。官方 93 epoch vs 我们 0.73 epoch 的差距,用一条乘除法就能在第一天算出来——当时如果算了,就会更早决定"接受现状先交一版"还是"全力拉训练量"。
10. torch.compile 显存碎片 OOM 启用 torch.compile 后训练 OOM。 解法:PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True。 更好的办法:新架构(sm_120)上任何编译优化默认关闭,先正确再优化——这个原则 CodeArts 复盘里写过,但训练阶段又开了一次,学费交了两遍。
11. 重复启动多个训练进程 显存争抢,所有进程一起 OOM。 解法:nvidia-smi 确认干净再启动。 更好的办法:写一个 10 行的启动前检查脚本(GPU 显存、磁盘水位、无残留进程),每次启动前跑一遍。
第三幕:仿真渲染(8 个坑,约 10 小时——全场最贵的弯路)
12-16. Vulkan 五连:系统 loader 1.3.204 太旧 → 源码编译 1.4.365;glibc 不够新 → 只能源码编译;ICD 接口版本 8 不兼容 → patch loader 加 dlsym fallback;vkCreateInstance 返回 -3 → 写最小测试程序隔离定位(直接调 ICD 成功 → 问题在 loader);无显示器渲染 → EGL + GBM + DRM render node。
这一幕占掉了全部调试时间的一半。值得肯定的是隔离测试法:当"A→B→C 失败"时,分别测试 A→B 和 B→C,逐段定位——这是全场最专业的一次排障。
更好的办法(这次是认真的):
- 这类"云容器 + SAPIEN 仿真 + 渲染崩溃"的问题,官方 issues 里几乎必有人踩过。先搜 RoboTwin/SAPIEN 仓库 issues 十分钟,可能省掉十小时。我们选择了从源码硬啃,学到了很多 Vulkan 知识——但比赛的语境下,这不是最优路径。
- 分离"管线通"和"渲染快"两件事:先用软件渲染/xvfb 把评测管线跑通(哪怕帧率低),渲染问题后置。评测要的是成功率,不是帧率。
17. SAPIEN 3.0 API 变化:VulkanRenderer → SapienRenderer。大版本升级先查 CHANGELOG,5 分钟的事。
第四幕:评测与提交(10 个坑)
18. websocket client 异常处理不完整 推理服务器加载 6B 模型要时间,客户端先连上了,收到 InvalidMessage/EOFError 直接崩。 解法:patch 重试逻辑,覆盖三种异常。 更好的办法:写重试逻辑时穷举异常类型(ConnectionRefused 只是其中一种);或者干脆在客户端启动前加"服务器就绪探测"。
19. 端口冲突:残留进程占端口。pkill -9 -f "python.*infer"。启停频繁的调试期,启动前先清理应该成肌肉记忆。
20. 非致命警告干扰排查:missing pytorch3d、OIDN Error 都不影响评测。更好的办法:先看警告是否致命(查源码确认有没有 fallback),再决定要不要管它。被非致命警告带偏排查方向,是排障里最隐蔽的时间黑洞。
21. 评测默认 100 episodes,全量要 125 小时 解法:显式改脚本 test_num,官方允许不足 100 次如实提交。 更好的办法:拿到评测脚本第一天就通读一遍,找出所有"默认值不适用"的参数(test_num、任务顺序、落盘机制),列成清单。我们是跑到一半才发现的。
22. 评测指令与训练指令不一致 → 成绩失真:评测前先抽查指令一致性,否则成功率完全没有参考价值。
23. AutoDL 无 crontab:定时任务用 nohup + while 循环脚本实现。
24. ps 看不到训练进程:容器环境限制,判断进程存活用 nvidia-smi 显存占用 + 日志时间戳。
25. 终端粘贴多行/heredoc 必坏 多行文本、含中文的内容粘贴进终端会错乱,两次搞坏脚本。 解法:单行 base64 分块传输(echo … | base64 -d > file),实测 100% 可靠。 更好的办法:远程无剪贴板可靠性的环境下,文件传输走 scp/rsync/OSS,不要依赖终端粘贴。这个方案是被逼出来的,但它本可以在第一天就建立。
26. tail 卡住看不到长任务输出:nohup … > log 2>&1 & + tail -f,外加 disown。
27. 实例克隆数据盘陷阱:AutoDL 克隆实例时新盘小于原盘,数据不复制。跨实例迁移前先核对磁盘容量。
28. 提交物差点旧版上传 打包流程改过好几轮(追加分析章节、更新成绩数字),差点把旧版 zip 传上天池。 解法:打包后 tail 检查 README 结尾 + 对比文件时间戳 + sha256 留证。 更好的办法:提交物按"版本号 + 构建时间戳 + 清单核对"管理,打包脚本一步生成并自校验,人只看最终输出。最后一次提交前,宁可花 3 分钟验证,不要凭记忆相信"应该是新的"。
29. 统计置信度的心里没底 5 episodes 版 58.0%,不敢直接交,重跑 20 episodes 得 56.8%,白白多等一轮。 更好的办法:懂一点二项分布置信区间。n=250 时 58% 的 95% 置信区间约 ±6%,n=1000 时约 ±3%——两版本来就在误差范围内。先算区间再决定要不要加测,能省下整整一轮评测时间。
30. 文档与事实脱节 提交 README 模板里写着"100,000 steps / 3.5 天"(照抄官方计划值),GPU 架构记录前后矛盾(sm_120 vs 8.9)。 更好的办法:文档里区分"计划值"和"实测值",实测值必须附带产生它的命令输出。写文档的时间是投资,不是开销——比赛后半程,我们靠文档救回来的时间远多于写它的时间。
31. 工具切换的交接成本 比赛用了两个 AI 编码工具接力(CodeArts 前期、Trae 后期),经验和教训散落在各自的日志里。比如"先正确再优化"在前期就总结过,后期还是交了一遍学费。 更好的办法:比赛第一天建一份统一的实验日志(日期 / 目的 / 命令 / 结果 / 结论,五行一表),换任何工具都续写同一份。教训只有被读到才能避免重交学费。
如果重来:六个系统性优化
下篇:初学者的补课清单(7 个 Stage)+ 优化方向。

