模型训练的配置与路由
model_training 块的每个键,运行如何排队、打分和存储,管理员与代理路由,以及常见运行错误的修复方法。
v2.14.0 新增
本页是 Potato 配置中 model_training 块的参考,也说明运行启动之后会发生什么。本页写给负责运行研究的人,他们想要限制运行、把运行发送到另一台机器、向标注者展示模型的预测,或从脚本里启动训练。下面的每个键、默认值、路由和消息都对照 Potato 的代码核对过。第一次运行请从训练你的第一个模型开始。训练器及其参数列在训练器里。
完整配置
下面的配置在商品评论上训练一个情感分类器。auto_retrain 在每 25 条新标注之后重新拟合 sklearn-text。使用默认的 TF-IDF 特征时,sklearn-text 在 CPU 上训练,只需要普通的 Potato 安装,因此频繁的自动运行开销很小。回写会向标注者展示每个置信度至少为 0.8 的预测。设置了 include_unlabeled 后,运行还会对无人标注的评论做预测。hf-text-classification 的默认值和 gpu-server 后端用于从训练页面手动启动的运行:
annotation_task_name: "Review sentiment with model training"
task_dir: "."
data_files:
- "data/reviews.json"
item_properties:
id_key: "id"
text_key: "text"
output_annotation_dir: "annotation_output"
user_config:
allow_all_users: true
annotation_schemes:
- annotation_type: "radio"
name: "sentiment"
description: "Is this review positive or negative?"
labels: ["positive", "negative"]
model_training:
enabled: true
split_seed: 0
splits: {train: 0.7, val: 0.15, test: 0.15}
aggregation: consensus
retain_runs: 5
queue_max: 10
max_wall_seconds: 3600
max_ram_mb: 8192
num_threads: 4
trainer_params:
sklearn-text:
classifier: sklearn.linear_model.LogisticRegression
classifier_kwargs: {max_iter: 1000}
hf-text-classification:
model_name: answerdotai/ModernBERT-base
epochs: 4
lora: true
auto_retrain:
enabled: true
trainer: sklearn-text
schemas: [sentiment]
min_interval_s: 600
update_frequency: 25
writeback:
schemas: [sentiment]
min_confidence: 0.8
include_unlabeled: true
backends:
gpu-server:
type: ssh
host: gpu-server
python: /opt/conda/bin/python3
gpus: auto
min_free_gpu_mb: 4000在研究的整个生命周期里,请保持 split_seed 不变。换一个种子会重新分配每个条目,原本留出的条目会进入训练,之后的得分就无法与之前的得分比较。min_confidence 下限 0.8 只是一个起点,合适的下限取决于第一次留出得分。等这个得分足以支撑展示预标签之后,再打开 include_unlabeled,因为预标签会把标注者锚定在模型的答案上。
model_training 中的键
每个键都位于 model_training 之下:
| 键 | 默认值 | 含义 |
|---|---|---|
enabled | false | 注册训练页面、/admin/training 下的路由和 /api/training 下的代理路由。没有 enabled: true,这些路由就不存在。 |
retain_runs | 5 | 在运行台账中保留的已完成运行数。较早的运行会离开台账,除非它的预测仍被存储。运行目录仍留在磁盘上。 |
grace_seconds | 20 | 已取消的运行在 Potato 杀掉进程之前获得的退出秒数。0 或更大,并随作业发送给类型为 ssh、slurm 和 agent 的后端。 |
queue_max | 10 | 可以在进行中的运行后面等待的运行数。 |
split_seed | 0 | 把条目分配到 train、val 和 test 划分的种子。 |
splits | {train: 0.7, val: 0.15, test: 0.15} | 各划分的比例,会归一化到总和为 1。名称为 train、val 和 test,train 必须有。 |
aggregation | consensus | 多位标注者在一个条目上的标签如何变成一个标签。研究有多于一位标注者时,consensus 拟合 Dawid-Skene 模型,拟合失败时退回多数投票。majority 取多数投票。 |
max_wall_seconds | 未设置 | 一次运行的时间上限。Potato 在本地运行每次报告事件时检查,对 ssh 或代理运行则每秒检查一次。 |
max_ram_mb | 未设置 | 训练进程的地址空间上限,用 setrlimit 设置。在不支持该限制的平台上,运行会记录一条警告并继续。 |
device | auto | auto、cpu、cuda、cuda:N 或 mps。auto 先选 CUDA,再选 Apple 的 Metal,最后选 CPU。 |
num_threads | 未设置 | 为这次运行设置 OMP_NUM_THREADS、MKL_NUM_THREADS 和 OPENBLAS_NUM_THREADS,除非环境已经设置了这些变量。 |
auto_retrain | 关 | 随标注到来而重新训练。见 auto_retrain。 |
writeback | 无 | 把预测发布为预标签。见 writeback。 |
backend | local | 未指定其他后端的运行所用的后端。 |
backends | 无 | 具名的 ssh、slurm 和 agent 后端。见后端。 |
max_predictions_mb | 512 | 远程代理可以上传的 predictions.jsonl 的最大体积。 |
trainer_params | 无 | 参数默认值,以训练器名称为键。 |
tiling | enabled: auto | 为检测和分割训练器把大图像切成图块。见 tiling。 |
一次运行自己的 params 覆盖 trainer_params,后者又覆盖训练器的默认值。训练器不接受的参数名,如果写在 trainer_params 下,会在服务器启动时被拒绝,其他情况下则在运行启动时被拒绝。训练器用一个例子给出这个顺序。
auto_retrain
自动重新训练在每次保存标注之后检查是否需要新的运行:
| 键 | 默认值 | 含义 |
|---|---|---|
enabled | false | 打开检查。 |
trainer | 无 | 训练器名称。必填。 |
schemas | 无 | 要训练的方案。必填。 |
min_interval_s | 600 | 自上次运行结束后经过多少秒才会启动另一次运行。 |
update_frequency | 25 | 自上次自动运行以来要收到多少条新标注才会启动另一次运行。 |
params | {} | 训练器参数,与 API 运行的 params 相同。 |
backend | model_training.backend | 自动运行所用的后端。 |
accept_licence | false | 确认需要确认许可证的训练器的许可证(ultralytics-detect、ultralytics-segment、layoutlmv3)。没有这个确认,这些训练器的自动运行会被跳过。 |
当某次检查决定不运行时,原因会出现在 GET /admin/training/api/status 的 skip_reason 字段里,例如“waiting for 3 more annotation(s)”或“a run is already in progress”。自动运行从不加入运行队列。队列清空之后保存的下一条标注会再次检查,此时有更多标注可供训练。Potato 把标注计数保存在内存里,因此重启后,曾经训练过的项目从重启时开始统计新标注。
writeback
回写决定标注者能看到哪些预测:
| 键 | 默认值 | 含义 |
|---|---|---|
schemas | [] | 其预测会被标注者看到的方案。空列表表示一个也不显示。 |
min_confidence | 0.0 | 低于下限的预测会被存储但不显示。没有置信度的预测可以通过。 |
field | predictions | 预标签写入的条目字段。 |
include_unlabeled | false | 同时对无人标注的条目做预测,并给这些条目打上预标签。没有 include_unlabeled 时,运行只在自己的 val 和 test 条目上做预测。 |
tiling
检测和分割训练器把每幅图像缩放到几百像素。在这个尺寸下,全玻片图像或卫星场景上的目标会丢失。因此对于 image_annotation、microscopy 或 geo_annotation 方案,Potato 在构建这次运行的训练数据时,会把每幅大图像切成图块。图像属于哪个划分,是在切块之前决定的,所以同一张玻片的图块不会落入两个划分。预测会映射回整幅图像,留出得分在整幅图像上计算。视觉教程介绍玻片和地图的实际操作。
| 键 | 默认值 | 含义 |
|---|---|---|
enabled | auto | auto 对玻片格式(.svs、.ndpi、.mrxs、.scn 等)和较长边至少为 auto_min_side 的图像切块。true 对每幅图像切块,false 一幅也不切。 |
auto_min_side | 4096 | auto 开始对图像切块的较长边长度,单位为像素。 |
tile_size | 1024 | 图块的边长,单位是训练器看到的像素。 |
overlap | 128 | 相邻图块共享的像素数,最多为 tile_size 的一半。重叠至少要与目标一样大,否则大目标会被分成几块找到。 |
downsample | 1 | 每个图块像素对应的全分辨率像素数。 |
mpp | 无 | 每个图块像素对应的微米数。对记录了自身分辨率的玻片,它会覆盖 downsample。 |
background | 0.25 | 每个含目标的图块对应采样的空图块数。对只标注了一部分的玻片,请设置 background: 0。 |
max_predict_tiles | 2000 | 每幅图像最多预测的图块数。保留组织最多的图块。 |
min_visible | 0.4 | 目标必须有多大比例落在图块内,才会在该图块里用于训练。 |
tissue_threshold | 0.05 | 玻片图块中必须有多大比例是组织,模型才会在该图块上做预测。 |
玻片由 openslide 读取,它通过 pip install "potato-annotation[wsi]" 安装,安装在构建训练数据的机器上。远程后端下载的是图块,从不下载玻片。
运行队列
同一时间只执行一次运行。在另一次运行进行时从训练页面或 API 启动的运行会等待,而不是被拒绝。启动请求返回 202,带有 queued: true 和这次运行的 position,Runs 表把这次运行列为 waiting #N,并带有 Cancel run 按钮。进行中的运行结束后,最早等待的运行开始。
等待中的运行是运行台账里的记录,所以队列在重启后依然存在,服务器回来时第一个等待中的运行会启动。上一个服务器正在进行的运行,同时被标记为 interrupted。等待中的运行的训练数据在这次运行开始时才构建,所以它用的是那一刻已有的标注。本该被拒绝的启动,例如指定了未知方案的启动,会立即被拒绝,而不是等轮到它时才拒绝。如果一次运行的方案或训练器在它等待期间从配置里消失,它会以 invalid_when_dequeued 失败,随后下一次运行开始。
在请求体里发送 "queue": false,就会得到 409,而不是排进队列。已经有 queue_max 个运行在等待时,新的启动会得到 429。GET /admin/training/api/status 在 queue 下按顺序列出等待中的运行。
运行前的检查
potato validate 一次报告 model_training 块里的所有问题,服务器遇到同样的问题也会拒绝启动:
potato validate config.yaml --strict这些检查涵盖以下情形:
splits的名称不是train、val和test,比例小于 0 或不是数字,比例总和为 0,或没有train。writeback或auto_retrain没有的键,不是方案的writeback.schemas条目,或 0 到 1 之外的min_confidence。accept_license会被报告为accept_licence的拼写错误。- 已启用的
pre_annotation块的field与writeback.field不同。这样的块之下不会显示任何训练预标签。 - 在
auto_retrain.enabled下,不存在的训练器或方案,训练器无法学习的方案,训练器不接受或类型错误的参数,backends里没有的backend,或local之外的后端上的decision-calibrate。 trainer_params下训练器不接受的参数,或类型错误的参数。在dp之内,检查还覆盖每个键以及该键的取值范围。- 小于 0 或不是数字的
grace_seconds,或小于 0 或不是整数的retain_runs或split_seed。 - 在
backends下,缺失或未知的type,该类型没有的键,没有host的 ssh 后端,auto、none或 GPU 编号之外的gpus值,名为local的远程后端,或未定义的model_training.backend。
potato training check 在运行使用某个后端之前测试这个后端。对 local,该命令打印运行会使用的设备,例如在 Apple 芯片的 Mac 上是 local: this machine, Apple Metal (mps)。对 ssh 后端,该命令报告 Python 版本、每块 GPU 及其空闲显存、运行会分到的 GPU,以及远程解释器缺少的训练包。训练页面上的 Check 按钮运行同样的测试,--json 以 JSON 打印报告:
potato training check local config.yaml
potato training check gpu-server config.yaml从训练页面或 API 启动的运行,在运行开始时会再检查一次。状态码列在路由之下。
后端
运行在一个后端上执行,训练页面的 Runs on 菜单列出已配置的后端。model_training.backend 设置默认值,API 运行或 auto_retrain 可以用 backend 指定另一个后端。Potato 有四种后端类型:
| 类型 | 训练器在哪里运行 | 谁启动训练器 |
|---|---|---|
local | 这台机器,以子进程运行。始终可用,是默认值。 | Potato |
ssh | Potato 能通过 ssh 连上的另一台机器,例如 GPU 服务器。 | Potato,通过 ssh |
slurm | 集群的计算节点,作为批处理作业。 | Potato,先 ssh 到登录节点,再用 sbatch |
agent | Potato 连不上的机器,例如 NAT 后面的工作站。 | 人,用 potato training agent |
ssh 后端使用密钥认证,并打开 BatchMode,因此密码提示会直接失败而不是挂起。slurm 后端只使用 sbatch、squeue 和 scancel,在提供 scontrol 的集群上还会使用 scontrol,并且需要计算节点共享的存储上的 remote_dir。agent 后端不需要入站端口,因为每个连接都由代理发起。local 这个名称保留给这台机器,decision-calibrate 只在 local 上运行。在 GPU 服务器、SLURM 集群或 NAT 后的机器上训练逐一演示这三种远程类型及其键。
留出得分
每次运行都在 val 和 test 条目上,对照合并后的人工标签,给这次运行自己的预测打分。指标名带有划分,例如 val_macro_f1 或 test_f1。有标签而没有预测的条目算作错误,因此丢掉难条目无法抬高训练器的得分。<split>_coverage 报告得到预测的条目所占的比例。
主得分显示在 Runs 表的 Held-out score 列里,提示框里带有划分和指标。主得分是第一个方案的主指标,取自 val 和 test 中第一个有条目的划分。当训练器按 val 得分挑选了最佳检查点时,Potato 优先尝试 test,因为训练器已经见过 val 得分。保留最佳 epoch 的微调训练器,例如 hf-text-classification、torchvision-detect 和 llm-sft,主得分取自 test。sklearn-text、setfit、rdkit-property、决策模型训练器和推理桥不在 val 上做任何选择,这些训练器的运行只要 val 有条目,主得分就取自 val。没有 val 或 test 条目的运行显示为“not scored”。
主指标取决于方案的标注类型,下表列出常见类型:
| 标注类型 | 主指标 |
|---|---|
radio, select, likert | macro-F1(另有准确率,数值型 likert 标签另有平均绝对误差) |
multiselect | micro-F1(另有 macro-F1 和完全匹配) |
span | 起点、终点和标签都匹配的跨度上的 F1 |
text | token F1(另有完全匹配) |
binary 模式下的 pairwise | 在未作为平局回答的条目上的准确率 |
ranking | Kendall's tau |
image_annotation, microscopy, geo_annotation | IoU 0.5 下的 COCO mAP(map_50),同时记录 map_50_95 |
audio_annotation | IoU 至少 0.5 且标签相同的片段上的 F1 |
speech_transcript | word_accuracy,即 1 减去词错误率 |
volume_annotation | mean_dice |
token F1 会给用词正确但顺序错误的答案记分,所以请把 token F1 与完全匹配一起看。检测的主指标用 IoU 0.5,而不是 0.5 到 0.95 的平均,因为两位标注者给同一目标画的框很少在 IoU 0.9 上重合。上游留出得分表列出每种标注类型的指标。
回写
一次运行成功之后,Potato 存储这次运行的预测,并把通过四项检查的预测写入每个条目的 predictions 字段,或 writeback.field 指定的字段。这些检查按以下顺序进行,运行的回写报告会统计每一项跳过了多少:
- train 划分的条目永远不会被打预标签。 这项检查覆盖台账中仍保留的每次运行的 train 划分,较早一次运行留在某个条目上的预标签,在后来的运行用这个条目训练时会被撤下。
- 只发布
writeback.schemas里的方案。 列表默认为空,所以开启训练不会改变标注者看到的任何内容。 - 被拒绝的配对会被跳过。 在模型审核中得到
reject判定的条目与方案配对不会再次显示。 - 应用置信度下限。 低于
min_confidence的预测会被存储但不发布。
回写从不改动人工标签,因为预测进入条目数据,而标注保存在每位标注者的用户状态里。预标签显示为标注者可以编辑的预填答案,例如选择题显示为选中的选项,自由文本答案显示在方案的文本框里,并且不需要 pre_annotation 块。只有当标注者编辑了预标签或转到下一个条目时,预标签才会成为标注者的答案。因此没人编辑过、也没人越过的预标签永远不会变成训练标签。被 ai_support.holdout 放进研究的无辅助对照组的标注者与条目配对不显示预标签,为这个配对发送的条目数据也同时去掉 writeback.field 和 pre_annotation.field。回写报告是 GET /admin/training/api/status 的 writeback 字段,报告的 summary 会说明一次运行为什么什么也没发布。
Potato 为每个条目和方案只保留一个预测,较新运行的预测会替换较早运行的预测。当较新的预测被存储却不显示,原因是它低于 min_confidence、为空或已被拒绝时,较早运行的预标签会立即从条目上撤下,回写报告会把这个条目计入 n_superseded_cleared。随数据一起提供的预标签不受影响。启动时,writeback.schemas 里各方案已存储的预测会按同样的四项检查写回条目数据,所以预标签能在重启后保留。要撤回一次运行的预标签,请发送 DELETE /admin/training/api/runs/<run_id>/predictions。该请求会移除存储的预测,并把预标签从条目上撤下。
修正过的条目和固定的条目始终参与训练
两类条目会进入之后每次运行的 train 划分,无论它们在稳定划分中的位置如何:
- 审核者修正过的条目。 模型审核中的
correct判定表示有人改正了模型的答案。修正来自模型最不擅长的条目,所以val或test中被修正的条目,会在模型自己的错误样本上给模型打分。accept和reject判定让条目留在原处。 - 固定的条目。 目录页面上某个切片旁边的 Train on these 会固定这个切片所匹配的条目,例如内置的
model-found-nothing切片。被固定的条目一旦有人标注就会参与训练。
把留出条目移入 train 的运行会记录一条带数量的警告。这次运行的留出得分是在去掉这些条目之后算出的,因此不能与较早运行的得分直接比较。数量在训练数据的 manifest.json 的 stats.forced_train 下。
把一个划分保存为数据集版本
运行页面上的 Keep this split as a dataset 会把这次运行的 val、test 或 train 条目连同其一致的人工标签,保存为一个数据集的新版本。数据集版本此后不再改变,所以之后的模型和 LLM 提示词可以在恰好这些条目上比较。这个功能需要 datasets.enabled: true。每个保存的版本只包含一个划分,并且从零开始,而不是追加到数据集的上一个版本之后。否则,把第 2 次运行的 val 条目加到第 1 次运行的版本上,就会包含第 2 次运行训练过的条目。每个样例的 inputs 都不含预标签字段,所以评估读不到模型的答案。
预标签从哪里来
GET /admin/model-review 为审核队列中每个预标签由训练运行写入的条目都包含 provenance。对每个方案,这一项给出运行 id、训练器和这次运行的留出得分。随数据一起提供的预标签没有这一项。
运行存放在哪里
每次运行在 <task_dir>/<output_annotation_dir>/training/runs/<run_id>/ 有一个目录:
| 路径 | 内容 |
|---|---|
bundle/ | 训练数据:train.jsonl、val.jsonl、test.jsonl、带 include_unlabeled 时的 unlabeled.jsonl、labels.json、manifest.json 和 media/。 |
artifacts/ | model_card.json、predictions.jsonl 和模型,例如 sklearn-text 的 model.pkl,或 transformer 训练器的 model/ 目录。 |
spec.json | 这次运行被要求做什么。 |
events.jsonl, stdout.log, stderr.log | 这次运行的进度事件和输出。 |
agent.log | 在 ssh 后端上,远程代理自己的输出。 |
bundle.tar.gz | 在远程后端上,代理下载的训练数据,包括媒体。 |
运行台账位于项目数据库 <task_dir>/project.sqlite 中,记录各次运行、每次运行中每个条目的划分、固定用于训练的条目、存储的预测、代理令牌,以及向 Hugging Face Hub 的推送。retain_runs 会修剪台账,但从不修剪预测仍被存储的运行,因此每个预标签都能追溯到写下它的那次运行。修剪不会删除磁盘上的运行目录。
LoRA 运行只保存适配器,几 MB 到几百 MB,在运行做预测时按 model_name 加载基础模型。完整微调则保存整个模型。把 POTATO_MODEL_CACHE 环境变量设为某个目录,训练进程就会把该目录用作 HF_HOME、TRANSFORMERS_CACHE 和 SENTENCE_TRANSFORMERS_HOME,除非这些变量已经设置。在断网机器上,请预先把训练器指定的模型放进该目录。
路由
训练页面及其 JSON 路由位于 /admin/training 之下,需要管理员权限。管理员权限指管理员会话或 X-API-Key 请求头。来自浏览器的 POST 和 DELETE 请求必须同源。Potato 的 OpenAPI 规范用 x-potato-requires-config: model_training.enabled: true 标记了全部 26 个训练操作:
| 路由 | 用途 |
|---|---|
GET /admin/training | 训练页面,带有运行表单、运行列表和训练器。 |
GET /admin/training/runs/<run_id> | 一次运行的页面,带有状态、指标、事件和每个条目的划分。 |
GET /admin/training/api/trainers | 每个训练器,带有训练器的种类、模态、许可证和安装状态。 |
GET /admin/training/api/schemas | 研究的方案、每个方案的种类,以及适合每个方案的训练器。 |
GET /admin/training/api/status | 当前或最近一次运行,带有状态、进度、指标、警告、skip_reason、writeback 和 queue。 |
GET /admin/training/api/runs | 运行历史。?limit= 默认为 50,最大为 500。 |
POST /admin/training/api/runs | 启动一次运行。运行开始时返回 200,运行等待时返回 202,带有 queued: true 和 position。 |
GET /admin/training/api/runs/<run_id> | 一次运行的记录,外加 review,即这次运行的预测在审核判定下的精确率。 |
GET /admin/training/api/runs/<run_id>/events | 这次运行的事件日志。?limit= 默认为 200。 |
POST /admin/training/api/runs/<run_id>/cancel | 取消进行中的运行或等待中的运行。对任何其他运行 id 返回 400。 |
DELETE /admin/training/api/runs/<run_id>/predictions | 撤回这次运行存储的预测和预标签。 |
POST /admin/training/api/runs/<run_id>/push-to-hub | 把这次运行的模型发布到 Hugging Face Hub。见发布到 Hugging Face Hub。 |
POST /admin/training/api/runs/<run_id>/pin-split | 把一个划分保存为数据集版本,请求体为 {"dataset": name, "split": "val"}。 |
GET /admin/training/api/backends | 已配置的后端。 |
POST /admin/training/api/backends/<name>/check | 测试一个后端,与 potato training check 相同。 |
GET /admin/training/api/pinned | 被固定的条目,按原因分组。 |
POST /admin/training/api/pinned | 把条目固定到 train,用 {"slice": name} 或 {"instance_ids": [...]}。 |
DELETE /admin/training/api/pinned | 取消固定所列的 instance_ids,请求体没有列出时则取消固定每个被固定的条目。 |
运行请求接受 trainer、schemas(列表或单个名称)、params、backend、accept_licence 和 queue。每个训练器只拟合所指定的第一个方案。下面的请求在示例的 sentiment 方案上启动一次 sklearn-text 运行:
curl -X POST -H "X-API-Key: $KEY" -H "Content-Type: application/json" \
http://localhost:8000/admin/training/api/runs \
-d '{"trainer": "sklearn-text", "schemas": ["sentiment"]}'被拒绝的启动会在 error 里给出原因:
| 状态 | 何时 |
|---|---|
400 | 请求体里没有训练器或没有方案,训练器或方案未知,训练器无法学习该方案,项目无法运行该训练器,或训练器的许可证需要确认,此时带有 needs_licence_ack: true,直到请求设置了 accept_licence: true |
409 | 训练器不接受的参数,或类型、取值范围错误的参数,未知的后端,local 之外的后端上的 decision-calibrate,或已有运行在进行而请求带有 "queue": false |
429 | 已有 queue_max 个运行在等待 |
代理协议
model_training.enabled 还会注册训练代理所调用的路由,位于 /api/training 之下。代理把自己的令牌作为 Authorization: Bearer <token> 发送,缺失或无效的令牌会得到 401。注册后端则需要管理员 API 密钥。potato/training/protocol.py 为编写新代理的人描述了协议,potato/training/agent.py 是参考实现:
| 路由 | 用途 |
|---|---|
POST /api/training/backends/register | 凭 X-API-Key 为一个后端名称签发代理令牌。potato training register 调用这个路由。 |
POST /api/training/backends/<name>/heartbeat | 空闲的代理报告自己还活着。给其他后端签发的令牌返回 403。 |
GET /api/training/jobs/next?wait=30 | 长轮询。有作业时返回 200,在 wait 秒内没有作业到来时返回 204。 |
GET /api/training/jobs/<run_id> | 作业、作业的规格,以及作业是否已被取消。 |
GET /api/training/jobs/<run_id>/bundle | tar.gz 形式的训练数据,包括媒体。该路由响应 Range 请求,因此代理可以恢复中断的下载,最多尝试五次。数据包已不存在时返回 410。 |
POST /api/training/jobs/<run_id>/progress | 进度事件。取消的信息会在响应里返回。 |
POST /api/training/jobs/<run_id>/predictions | predictions.jsonl。超过 max_predictions_mb 时返回 413。 |
POST /api/training/jobs/<run_id>/complete | 退出码和最终事件。 |
QLoRA
llm-sft、llm-dpo、hf-text-classification、vlm-sft 和 reward-model 接受 lora: {quantize: 4bit},这是 QLoRA(Dettmers 等,2023)的设置。基础模型以 4 位 NF4 权重和双重量化加载,在 GPU 支持 bf16 的情况下,LoRA 适配器在其上以 bf16 训练。quantize: 8bit 是折中的选项,预测时以同样的方式加载基础模型。
model_training:
trainer_params:
llm-sft:
model_name: Qwen/Qwen2.5-7B-Instruct
lora: {quantize: 4bit}在 4 位下,基础权重占用的内存是同样的权重在 bf16 下的四分之一,激活、适配器和优化器状态则要另加。Potato 还没有在 GPU 上测量过一次 QLoRA 运行的内存,所以请根据这次运行自己的“Peak GPU memory”一行来规划。每次 transformer 运行都会在结束时记录这一行。QLoRA 需要 CUDA GPU,以及与该机器的 CUDA 匹配的 bitsandbytes 构建。运行在加载模型之前,会通过在 GPU 上量化几个数来检查这两者,当两者的版本不匹配时,错误信息会写出两个版本。LLM 微调教程把 QLoRA 用在 SFT 和 DPO 运行上。
发布到 Hugging Face Hub
模型位于 Potato 服务器上的成功运行,在运行页面上有 Publish to the Hugging Face Hub。推送会上传模型,以及一份根据这次运行写成的 README.md 模型卡,卡里写明训练器、基础模型和适配器、方案和标签、各划分的大小、留出得分及测得该得分的划分,以及运行 id 和数据包 id。dp 运行的模型卡还有一行“Differential privacy”,给出相对于目标已花费的 epsilon、delta,以及该保证所覆盖的 train 划分。运行没有记录目标或 delta 的值时,这一行会省略它。
上传的是一份固定的文件列表,而不是整个 artifacts/ 目录。这份列表包含权重文件,例如 .safetensors、.bin、.pt 和 .pkl,保存的 model/ 目录里的配置和分词器文件,本地路径已换成文件名的 model_card.json,以及 calibration.json。模型在项目条目上的答案(predictions.jsonl,决策模型还有 raw_answers.jsonl)留在服务器上。检查点、优化器状态、某些训练器生成的训练数据副本(yolo_data/、wev_data/、laya_data/),以及任何指向 artifacts/ 之外的符号链接,同样不会上传。
这个表单从多个方面防止误推送或公开推送:
- 仓库名要输入两次,两次一致时推送才会进行。
- Private 默认勾选。勾选 Private 时,Potato 不会推送到已存在的公开仓库,API 会回答
409。 - 表单指定服务器上的一个环境变量,默认是
HF_TOKEN,其中保存 Hugging Face 的写入令牌。令牌本身从不输入到页面里。变量名必须包含TOKEN或以HF_开头,变量值必须以hf_开头,因此其他变量的值都无法到达 Hub。 - 当 Hub 无法确定仓库是否存在时,什么也不会推送,推送创建的仓库如果后来上传失败,会被再次删除。
- 模型卡的许可证是你给出的 Hub 许可证 id,例如
apache-2.0。没有给出时,卡片会说明适用基础模型的许可证。需要确认许可证的训练器,例如ultralytics-detect(AGPL-3.0),推送时也需要同样的确认。 - 留在设置了
fetch_artifacts: false的远程主机上的模型,或在 agent 后端上训练的模型,无法从服务器推送,错误信息会说明模型在哪里。
每次推送是一个提交。运行页面列出每次推送的仓库、可见性和提交,台账记录是谁做的推送。通过 API,POST /admin/training/api/runs/<run_id>/push-to-hub 的请求体接受 repo_id、confirm_repo_id、private(默认 true)、token_env(默认 HF_TOKEN)、licence 和 accept_licence。该路由成功时返回带有提交的 201,名称不一致、缺少令牌变量、没有模型文件或许可证未确认时返回 400,Hub 拒绝时返回 502。
故障排查
下面的条目涵盖第一次设置时最可能遇到的错误。上游故障排查列表涵盖 GPU 驱动、CUDA 构建和各训练器专有的消息。
/admin/training返回 404。model_training.enabled不是true,Potato 只有在这个键为true时才注册这些路由。- “Only N items in the train split”。 请标注更多条目,或降低这次运行
params里的min_instances。train 划分默认占已标注条目的 70%。 - “Schema 'x' has 1 distinct label(s) in the data”。 分类器需要至少使用两个标签。
- “No resolved annotations to train on”。 运行里指定的方案与正在标注的方案不同,或者还没有人标注任何内容。
- 这次运行没有发布任何预标签。 请查看
GET /admin/training/api/status里的writeback报告。“No predictions were published: write-back is not enabled for any schema”表示writeback.schemas为空,跳过计数显示每项检查移除了多少预测。 <trainer> does not take <name>。 运行里或trainer_params下的某个参数拼错了,或属于另一个训练器。消息会列出该训练器接受的名字。- 机器内存耗尽后运行被杀掉。 进程因 SIGKILL 退出,通常来自内存不足时的 OOM 杀手。请换更小的模型或使用更少的条目,或设置
max_ram_mb,让运行在自己的限制内失败。 - “Stopped: the run took longer than model_training.max_wall_seconds”。 运行达到了时间上限。请提高
max_wall_seconds,或用更少的条目训练。 - “The GPU ran out of memory”。 降低
batch_size并提高gradient_accumulation以保持有效批大小,设置lora: true或lora: {quantize: 4bit},对llm-sft则设置gradient_checkpointing: true或选用更小的模型。 - “ssh to host failed: Permission denied”。 Potato 以
BatchMode=yes运行ssh,所以从服务器的账户执行ssh <host> true必须无需提示即可成功。当ssh-agent持有密钥时,带口令的密钥也能工作。 - agent 后端上的运行一直在排队。 没有使用该后端名称的代理在轮询。请用
potato training agent --backend <name>启动代理,并用训练页面上的 Check 检查令牌。
延伸阅读
- 用你自己的标注训练模型:训练循环、训练页面和快速开始。
- 训练器:每个训练器、每个训练器学习的方案和安装命令。
- 在 GPU 服务器、SLURM 集群或 NAT 后的机器上训练:逐步设置 ssh、SLURM 和轮询代理后端。
- 循环中的模型:在同一项研究里使用回写、审核、
auto_retrain和数据集版本。 - 主动学习:按模型的不确定性给条目排序。
- 识别橡皮图章式预标签:检查标注者是否不读预标签就接受。
- 上游参考:每个训练器的完整参数表。
参考文献
- Dawid, A. P., and Skene, A. M. (1979). Maximum Likelihood Estimation of Observer Error-Rates Using the EM Algorithm. Applied Statistics, 28(1). https://doi.org/10.2307/2346806
- Dettmers, T., Pagnoni, A., Holtzman, A., and Zettlemoyer, L. (2023). QLoRA: Efficient Finetuning of Quantized LLMs. Advances in Neural Information Processing Systems 36 (NeurIPS 2023). https://proceedings.neurips.cc/paper_files/paper/2023/hash/1feb87871436031bdc0f2beaa62a049b-Abstract-Conference.html