看到别人用 BlenderWorld 报出更高分,先别把两个数字放一起比。BlenderWorld 的分数是“任务集 × 渲染设置 × Agent 配置 × 判分环境”的联合结果,任何一项不同,分差都可能来自配置而不是能力。要横比,先确认:两边跑的是同一份任务清单(版本标识和条目数一致、子集过滤一致)、同一组渲染参数(分辨率、采样数、输出格式、色彩管理)、同一组生成侧参数(最大步数、解码参数、提示词版本),并在同一判分环境里用同一份固定输出复判。四项都对齐,分数才有横比的前提;缺一项,只能描述为“各自跑通了一个配置”,不能相互排名。下面的清单按可验证的顺序排:先比任务,再比渲染,再比生成,最后比判分。
横比 BlenderWorld 分数前,先对齐四件事:任务集版本与条目数、渲染参数、Agent 最大步数与解码参数、判分环境。做法是把两边的配置文件和运行日志摊开逐项核对,再用同一份固定输出重复判分一次,确认判分可复现。任一项缺失或不可查,分数就不具可比性,只能各自与自己基线对比。核对成本通常在十几分钟,比事后推翻结论便宜。
核对任务集标识与条目数
任务集不同是最常见、也最容易被忽略的差异来源:同名的 benchmark 可能因为版本推进、子集划分或过滤条件不同,条目数已经变了。先看清单文件的版本字段和条目数,两边的哈希也要一致,这样能排除“文件同名但内容不同”。下面路径按实际仓库结构调整。
# 1) 任务清单的版本标识与条目数(示例路径)
sha256sum benchmarks/blenderworld/tasks/index.json
python - <<'PY'
import json
d = json.load(open('benchmarks/blenderworld/tasks/index.json'))
print('version:', d.get('version'))
print('count:', len(d['tasks']))
print('first:', d['tasks'][0].get('id'))
PY
# 2) 运行时是否做了过滤:看启动日志里的 split / task_ids
rg -n 'split|task_ids|limit|shuffle|seed' logs/run.log | head -n 20
需要确认三件事:清单文件的 sha256 是否一致;条目数是否一致;运行时有没有额外过滤(split 名称、`--limit`、随机抽样种子)。如果两边条目数一样但任务 id 集合不同,用 jq -S .tasks[].id index.json | sort 对两边做 diff,比只比数字可靠。
核对分辨率、采样数与输出格式
渲染差异会直接改变任务难度和判分结果,所以下面这些参数必须逐项一致。参数通常落在两处:runner 的渲染配置(例如 configs/render/*.yaml)和场景文件里的 scene.render.*。最终以 Blender 进程里实际生效的值为准,配置文件只代表意图。
- 引擎:
scene.render.engine(CYCLES / BLENDER_EEVEE 等,两者采样语义完全不同) - 分辨率:
resolution_x、resolution_y、resolution_percentage(百分比不同等于改了分辨率) - 采样数:CYCLES 看
scene.cycles.samples,EEVEE 看scene.eevee.taa_render_samples;降噪开关cycles.use_denoising也要一致 - 输出:
image_settings.file_format、color_depth、color_mode - 色彩管理:
view_settings.view_transform、look、exposure、gamma - 帧范围与随机性:
frame_start/frame_end、cycles.seed、film_transparent
读取实际生效值的做法是在渲染脚本里打印一次,再和对方对齐:
import bpy
s = bpy.context.scene
print('engine', s.render.engine)
print('res', s.render.resolution_x, s.render.resolution_y, s.render.resolution_percentage)
print('samples', s.cycles.samples if s.render.engine == 'CYCLES' else s.eevee.taa_render_samples)
print('fmt', s.render.image_settings.file_format, s.render.image_settings.color_depth)
print('cm', s.view_settings.view_transform, s.view_settings.look, s.view_settings.exposure)
print('seed', s.cycles.seed, 'denoise', s.cycles.use_denoising)
如果这些行没有出现在日志里,就说明当前运行没有留下可核对的渲染记录,这样的分数先当作不可比处理。
核对 Agent 侧的解码参数与最大步数
生成侧的差异同样会拉开分数,尤其是最大步数:步数上限低的一方可能在任务中途被截断,看起来像“模型不行”,实际是预算不同。需要一致的参数清单包括:max_steps(或 max_turns / 工具调用次数上限)、temperature、top_p、seed、max_tokens、系统提示词版本、重试与超时策略。
读取方式分两层:先看配置文件的默认值,再看日志里的“解析后配置”。中间常有命令行覆盖,所以以解析后为准。
# 打印解析后的最终配置(`--print-config` 为示例开关,按实际 runner 替换)
python run_eval.py `--config` configs/agent.yaml `--print-config` \
| grep -E 'max_steps|temperature|top_p|seed|max_tokens|prompt_version'
# 日志里确认实际生效值与是否被截断
rg -n 'resolved config|max_steps|step=.*/' logs/run.log | tail -n 30
判断是否被截断的方法很直接:看最后一次记录的 step 序号是否等于 max_steps。相等说明这轮可能停在预算边界,结论要标注“达到步数上限”,不能当作任务本身失败。
用同一份固定输出复核判分
确认前两项之后,还要排除判分器本身的差异。做法是把一边已经产出的结果目录原样保存,不改动任何文件,用同一套判分器跑两次,比较两次结果是否一致;再用同一份输出换到对方环境跑一次,看分数是否落在同一位置。固定输出是关键,任何重跑生成都会引入新的变量。
# 记录判分侧版本
sha256sum judge.py
blender `--version` | head -n 1
pip freeze | grep -iE 'blender|numpy|pillow' > env.txt
# 同一份固定输出,重复判分两次
python judge.py `--pred` preds/fixed_run `--tasks` benchmarks/blenderworld/tasks/index.json `--out` judge_a.json
python judge.py `--pred` preds/fixed_run `--tasks` benchmarks/blenderworld/tasks/index.json `--out` judge_b.json
diff <(jq -S . judge_a.json) <(jq -S . judge_b.json) && echo SAME
两次结果一致,说明判分在该配置下可复现;出现差异,优先查容差阈值、图像比对精度、是否有未固定的随机或时间戳。两次不一致的判分器,不适合承载横比结论。
整理成一张可比性对照表
核对完上面四组信息,把它们写进一张表,比较结论才有依据。字段定义:任务集版本与 sha256 前缀、条目数与 split、引擎、分辨率(含 percentage)、采样数与降噪、输出格式与色深、色彩管理、Agent 最大步数、解码参数、提示词版本、判分器哈希、固定输出复判是否一致、可比结论。一行填写示例(示例值,需按实际环境替换):
| 字段 | 取值 |
|---|---|
| 任务集版本 / sha256 前 8 位 | vN / a1b2c3d4 |
| 条目数 / split | 与对方一致 / test |
| 引擎 / 分辨率 | CYCLES / 1920x1080 @100% |
| 采样数 / 降噪 | 与对方一致 / 开 |
| 输出格式 / 色彩管理 | PNG 8bit / Filmic, look=none |
| Agent 最大步数 / 解码参数 | 未达上限 / temperature、top_p、seed 一致 |
| 判分器哈希 / 复判 | 与对方一致 / 两次结果相同 |
| 可比结论 | 可比 / 不可比(注明差异项) |
表格里只要有一行对不上,结论就写“不可比”并标出差异项,而不是折算出一个近似分数。多数情况下,把差异项写清楚本身比多跑一轮更有价值。