custom/custom 只跑一个文件:experiments/<name>/train.sh。平台不读 FRAMEWORK 变量,不去找
run.py,也从不因为别的适配器失败而回退到 custom。那个文件做什么完全由你决定。
脚手架
train.sh,以及 config.yaml、recipe.lock.json 和 README.md。recipe 的入口按名字就是
train.sh,所以改名会让提交失败并报 custom entrypoint is missing or escapes the experiment directory。
适配器执行 bash <train.sh 的绝对路径>。工作目录是作业包的根,不是实验目录,所以要把 Python
指向 "${TUNEPLANE_EXP_DIR}/train.py",而不是一个相对路径。
custom 适配器只编译训练那一种操作。对 custom 实验用 tp export 和 tp eval 会被拒绝。
环境契约
脚手架用:? 在缺任何一项时直接退出。不要改这些名字。
部署方有的话,
HF_HOME、HF_TOKEN 和 *_DATA_DIR 变量也会到。完整清单在
作业环境变量参考。
配额和看门狗是按 TUNEPLANE_CLUSTER_* 衡量的。占用超过这个数会让作业被告警、甚至被停掉,所以把
同样的数字传给 accelerate launch --num_processes 或 torchrun --nproc_per_node。
你的脚本必须做的三件事
- 把你想留下的一切写到
$TUNEPLANE_OUT_DIR下。容器退出时临时目录就没了。 - 通过
tuneplane.report上报标量,或声明 TensorBoard/JSONL 输出,由 独立采集器采集。只有 stdout 日志不会产生指标点。 - 尊重
TUNEPLANE_CLUSTER_*。
写 train.sh
set -euo pipefail 已经在里面了。把脚手架的 exit 1 换成一次启动。
cd 到某处然后写 ./checkpoints。用这些变量。
上报指标
PyPI 名字是tuneplane,import 也是 tuneplane。上报模块既不引 transformers 也不引 Ray,而且它
从不往你的训练循环里抛异常。没有 TUNEPLANE_JOB_TOKEN 时它什么都不做,这正是你在作业之外想要的。
init() 是幂等的。嵌套字典会被拉平,无法归约成均值的非标量会被丢掉。键本身没带命名空间时,
prefix= 会给它加上。
TunePlaneCallback 是鸭子类型的,不是 TrainerCallback 的子类。它在 train begin 调 init,在
on_log 调 log,在 evaluate 调 log(..., prefix="validation"),在结束时调 finish。
硬件采样用 init(monitor_hardware=True) 开启,只在 rank 0 上 —— 一个作业一个采样器,不是一张卡
一个。间隔是 TUNEPLANE_JOB_MONITOR_INTERVAL,单位秒,默认 10。
两件不要做的事:不要自己往 ingest API 发 POST,因为 stdout 已经在转发了,第二条通路会让日志重复;
不要硬编码控制台地址,因为容器里已经有 TUNEPLANE_JOB_ENDPOINT。
为什么 —observability-url 是必填的
已发布的custom/custom recipe 声明 observability: external。两个后果:
- 提交必须带
--observability-url。没有它,编译会失败并报custom external observability requires spec.framework.observability_url。你们团队用的任何 URL 都行;平台不会替你起 wandb。 它以TUNEPLANE_EXTERNAL_OBSERVABILITY_URL进入作业。 - 适配器不会改写
PYTHONPATH。只有当 wheel 在你的镜像里、或你自己把它放进PYTHONPATH,import tuneplane才能用。
observability: platform 的 recipe 行为正好相反:--observability-url 会被拒绝,runner 会把
capsule 的根目录前置到 PYTHONPATH,于是镜像里没有 wheel 也能
from tuneplane.report import log。这个开关你没法从实验目录里翻 —— 它是已发布 recipe 的属性。
此 recipe 如使用 SDK 上报曲线,需要在镜像里安装 tuneplane,并继续传入 --observability-url。
原生采集则需要独立的输出声明和采集器部署配置。
TUNEPLANE_JOB_ENABLED=1 是另一件事。那是作业有 ingest 令牌时服务端总会设的绑定。
提交
--image 就是镜像本身:custom 在 catalog 里没有钉子,它的 runtime.default_version 是
user-managed。标签可用;生产上钉 @sha256:…,这样一个移动的标签不会在准入之后改变作业。
TUNEPLANE_ALLOWED_IMAGE_REGISTRIES 为空会拒绝 custom 用户镜像,而 catalog 框架仍然可以用
部署的默认镜像。让管理员把你的仓库主机名加上。见自定义镜像。
确认成功
tp job logs 会显示你自己那行启动命令,然后是你训练器的输出。Charts 标签页在你第一次调 log()
之后拿到第一个点。
在容器里:
不成功的时候
config.yaml 是你的
custom 适配器里没有任何东西会读它。custom recipe 没有声明参数,所以tp validate 没什么可查,
--set 的值会落进 spec.hyperparams 但不会进到你的 argv。在 train.py 里自己解析这个文件。
--model 和 --train-data 是 verl 和 TRL 的绑定。custom 忽略它们:用环境变量和你打包进去的文件。