kermt-finetune
Finetune a pretrained KERMT encoder on a user-supplied labeled CSV. The skill
is the workflow orchestrator: validate ckpt, validate data, prepare data,
launch the runner detached, return a run directory + container name.
Hardware requirements
- GPUs: 1 by default (single-GPU); pass
--gpus 0 (or whichever id) to
select one. For faster training on a multi-GPU host, pass --num-gpus N
(N>1) to run data-parallel DDP across N GPUs — --batch-size is then
per-GPU (effective global batch = batch_size × N).
- VRAM: ≥ 8 GB for the default
batch_size 32 configuration. Lower VRAM
works at smaller batch sizes — pass --batch-size N to override.
- Disk: a few GB per run (checkpoint + features + logs).
- Driver / CUDA: any host supporting CUDA 12.6 (the kermt image base).
kermt-setup validates this up-front.
Inputs
Required:
--csv <path> — labeled CSV. First column is smiles; every other column
is a target.
Checkpoint (optional — defaults to the released model if omitted):
--ckpt <path> — input pretrain checkpoint (grover_base / cmim / hybrid).
The validator refuses already-finetuned ckpts with a redirect to
kermt-infer. If omitted, the skill offers to download the released
pretrained hybrid model nvidia/NV-KERMT-70M-v2 and finetune from it —
see "Resolve & validate the checkpoint" (workflow step 3).
--pretrained-release — explicit opt-in to use the released model without
the interactive prompt (for non-interactive / agent runs). Mutually
exclusive with --ckpt.
--model-dir <dir> — where to save the downloaded bundle (default
$KERMT_REPO/models/NV-KERMT-70M-v2/). An already-complete bundle there is
reused, not re-downloaded.
Optional:
--dataset-type {regression | classification | multiclass} — default
regression (from defaults_finetune.json). Drives loss, metric defaults,
and head initialization. For classification tasks pass
--dataset-type classification.
--targets COL [COL ...] — explicit target column names. If omitted, the
validator auto-detects numeric non-smiles columns and the skill confirms
with the user before proceeding.
--val-csv <path> and --test-csv <path> — user-provided val + test
splits. Either pass both or pass neither (the skill auto-splits using the
configured --split-type).
--split-type {random | scaffold_balanced | index_predetermined} —
default scaffold_balanced from defaults_finetune.json.
random and scaffold_balanced: build the val/test split internally
from the train CSV. No --val-csv / --test-csv needed.
index_predetermined: requires pre-split CSVs passed via
--val-csv + --test-csv (and, separately, per-fold index files —
see kermt/util/utils.split_data). Use this when the dataset ships
its own canonical split (e.g. tests/data/Biogen_for_grover/scaffold/ balance/<endpoint>/{train,val,test}.csv).
--metric NAME — mae (regression default), auc (classification default),
or any name kermt.util.metrics.get_metric_func accepts.
--epochs N / --batch-size N / --init-lr F / --max-lr F /
--final-lr F / --warmup-epochs F / --weight-decay F / --dropout F /
--bond-drop-rate F / --dist-coff F / --early-stop-epoch N /
--seed N — training-hyperparameter overrides. Anything not given is
filled from agent/config/defaults_finetune.json.
--ffn-hidden-size N / --ffn-num-layers N — shared FFN trunk dims.
--ffn-num-task-specific-layers N / --ffn-task-specific-hidden-size H —
per-target FFN heads (default 0 = off; useful for heterogeneous multi-target
finetunes). Both must be set together when N > 0.
--ensemble-size N / --num-folds N — multi-model / k-fold CV. Default 1
each.
--gpus 0 — single GPU id for single-process finetune (default 0). Ignored
when --num-gpus > 1.
--num-gpus N — number of GPUs for data-parallel DDP finetune. Default 1
(single-process, unchanged). N>1 runs main.py finetune with WORLD_SIZE=N
(one process per GPU); --batch-size is per-GPU.
--from-prepare <dir> — skip the prepare step and reuse an existing
prepare_data.json in <dir>. Useful when iterating on hyperparameters.
Workflow
Let $KERMT_REPO be the path to your kermt repo checkout, and assume
kermt-setup has built kermt:latest. All paths below are on the host; the
helper bind-mounts them at known container paths.
Pre-flight: ensure container + system probe.
$KERMT_REPO/agent/scripts/kermt_container.sh check_system | python -c "
import json, sys; d = json.load(sys.stdin)
if not d['ok']:
print('System check failed:', d['gaps']); sys.exit(1)
print(f'OK: {len(d[\"gpus\"])} GPU(s); CUDA via container toolkit')
"
Refuse to proceed if ok: false.
Compute run directory.
RUN_DIR=$KERMT_REPO/runs/finetune_$(date -u +%Y-%m-%dT%H-%M-%SZ)
Resolve & validate the checkpoint.
Resolve — only if --ckpt was omitted. Default to the released
pretrained hybrid model nvidia/NV-KERMT-70M-v2:
Validate the resolved (or user-provided) ckpt:
$KERMT_REPO/agent/scripts/kermt_container.sh run --ckpt <user-ckpt> -- \
"python agent/scripts/check_checkpoint.py --mode finetune_init --ckpt /ckpt"
Parse the JSON. Abort on ok: false. The validator rejects already-
finetuned ckpts (has_task_ffn: true) with a redirect to kermt-infer.
Validate the data.
$KERMT_REPO/agent/scripts/kermt_container.sh run --data <user-csv> -- \
"python agent/scripts/check_data.py --mode finetune --csv /data/<basename> [--targets COL1 COL2 ...]"
If --targets was not given by the user, surface auto_detected_targets
from the JSON and ask the user to confirm before continuing. Abort on
ok: false.
Prepare the data (skip if --from-prepare given).
Pre-flight: check for sibling val.csv / test.csv. Before invoking
prepare_data, inspect the parent directory of <user-csv>. If a
canonical-looking sibling val.csv (or val_*.csv — common variants
include val_T.csv, val_clean.csv) AND a matching test.csv /
test_*.csv exist next to the train CSV, the dataset ships its own
pre-defined split. In that case set --split-type index_predetermined
AND pass --val-csv / --test-csv — otherwise the configured
split_type (default scaffold_balanced) will re-split the train CSV
from scratch and silently discard the user's val/test files. When in
doubt — or when the sibling files use non-canonical suffixes (_T,
_v2, etc.) — surface the situation to the user and ask which they
want.
Quoting target names. If any of the --targets column names
contain shell metacharacters (>, &, |, (, ), $, etc.),
single-quote each one when passing on the CLI to keep the shell from
eating part of the name. Example: --targets 'Log_Caco2_Papp_A>B' 'logD'. The CSV header itself is read directly by the downstream
trainer and is unaffected, but the prepare_data.json manifest's
targets[] field captures whatever the shell delivers — unquoted
metacharacters get truncated there.
Mount note: kermt_container.sh --data <host-csv> mounts the
parent directory of <host-csv> at /data. --val-csv and
--test-csv must therefore reference files in that same parent
directory. If val/test live in a separate directory (e.g. a sibling
splits/ folder), mount the parent of all three using --data <dir>
on a directory rather than a file.
$KERMT_REPO/agent/scripts/kermt_container.sh run --data <user-csv> --run-dir $RUN_DIR -- \
"python agent/scripts/prepare_data.py --mode finetune \\
--csv /data/<basename> --out /runs/data \\
--split-type <split_type> \\
[--val-csv /data/<val-basename> --test-csv /data/<test-basename>] \\
[--val-frac 0.1 --test-frac 0.1 --seed 0] \\
--targets <COL1> [COL2 ...]"
Outputs land at $RUN_DIR/data/prepare_data.json. For scaffold_balanced
and index_predetermined, prep emits a single clean_full_csv + .npz;
the runner passes them through to main.py finetune which calls
split_data internally with the user-supplied seed.
Estimate runtime + echo applied defaults.
- Finetune wall time is typically minutes-to-hours on 1 GPU.
- Surface a summary of every flag that was filled from the defaults
vs user-supplied, so the user knows what was assumed. The runner
records this in
args_applied.
- Sample message:
"Filling from defaults_finetune.json: epochs=30, batch_size=32, split_type=scaffold_balanced. Override any of these with --<flag>."
Targets confirmation gate (hard requirement). Before launching the
runner, regardless of how the targets list was determined (CLI --targets,
auto-detection in step 4, or a user natural-language request like
"finetune on Caco2 and HLM"), echo the final targets list to the user with
an explicit count:
"Will finetune on N target(s): COL1, COL2, ...". If the user's request
specified a subset that doesn't match this list (e.g., they asked for 2
tasks via natural language but the list still has 4), treat it as a
discrepancy and re-prompt with the diff — never silently proceed on the
wrong target set. Wait for explicit confirmation before launching unless
--yes was given.
Launch the runner detached. (Consistent with the pretrain skills.)
$KERMT_REPO/agent/scripts/kermt_container.sh run_detached \\
--name kermt-finetune-<ts> \\
--ckpt <user-ckpt> --run-dir $RUN_DIR -- \\
"python agent/scripts/run_finetune_local.py \\
--ckpt /ckpt \\
--prepare-manifest /runs/data/prepare_data.json \\
--dataset-type <type> \\
--out /runs \\
[--gpus 0] \\
[--num-gpus N] \\
[--epochs N --batch-size N --init-lr F ...] \\
[--ffn-num-task-specific-layers N --ffn-task-specific-hidden-size H]"
Returns the container name + id + log file path.
Report to the user. Output a short summary:
- Container name + id
$RUN_DIR/run.json (manifest with cmd_replay + image digest)
- Log file:
$RUN_DIR/logs/finetune.log
- TensorBoard:
$RUN_DIR/logs/tb (open with tensorboard --logdir $RUN_DIR/logs/tb)
- Final checkpoints land at
$RUN_DIR/ckpt/fold_0/model_0/model.pt
(best-val) and last_checkpoint.pt (sibling, auto-resume target).
Held-out test predictions + metrics land at
$RUN_DIR/ckpt/fold_0/test_result.csv. Paths vary with --num-folds
/ --ensemble-size.
- To follow progress:
kermt-monitor <RUN_DIR> (one-shot) or
docker logs -f <container-name> (streaming).
- To block until the run finishes (useful for short test runs):
docker wait <container-name> — prints the exit code on completion.
Hard rules
- Never download the released model without consent. When
--ckpt is
omitted, download nvidia/NV-KERMT-70M-v2 only after an explicit user "yes"
or an explicit --pretrained-release flag. --ckpt and
--pretrained-release are mutually exclusive.
- Never modify the user's input ckpt. The runner passes its path via
--checkpoint_path; task/train.py loads it read-only into the model and
attaches a new FFN head. The source file stays untouched.
- Arch comes from the ckpt, not from CLI/defaults. The runner extracts
hidden_size, depth, num_attn_head, activation, embedding_output_type,
self_attention (+ attn_hidden / attn_out when applicable) from the
ckpt's saved_args. There is no --hidden-size flag on this runner.
- Never block on the long-running finetune. The skill launches via
run_detached and returns immediately after step 9. Use kermt-monitor.
- Echo applied defaults back to the user. The
args_applied field of
run.json records every flag's value + source (user / default-config).
Surface a one-line summary of every filled-from-default flag so the user
knows what was assumed.
Common errors
finetune_init requires a pretrain ckpt (grover_base / cmim / hybrid) →
the ckpt you passed is already finetuned (has task FFN heads). Pick a
pretrain ckpt instead, or use kermt-infer if you want to run
predictions with the existing finetuned model. To resume a finetune on
the SAME dataset, bypass the skill and call
python main.py finetune --checkpoint_path <ckpt> ... directly — the
agent skill doesn't support resume because saved-task identity
can't be machine-verified against the new training data.
prepare_data manifest reports ok=False → check errors for the failed
step (typically clean_smiles or save_features). Fix and re-run.
ffn_num_task_specific_layers=N>0 but ffn_task_specific_hidden_size is unset
→ MTL heads need an explicit hidden size. Pass --ffn-task-specific-hidden-size H.
finetune is single-GPU (from --gpus 0,1) → --gpus selects one device
for single-process finetune. For multi-GPU, use --num-gpus N (DDP) instead.
Replayability
The run.json cmd_replay field is a single-line command that re-runs the
finetune with the same inputs, hyperparameters, and arch. To replay inside
the kermt container:
$(jq -r .cmd_replay $RUN_DIR/run.json)
If ok_to_replay: false in the manifest (because the kermt repo working
tree was dirty at launch time), the replay may not be bit-exact — pin the
exact commit via the repo.commit field and git checkout it
first.
1---2name: kermt-finetune3description: Finetune a pretrained KERMT encoder on a labeled CSV. The skill validates the input checkpoint (must be a pretrain ckpt — grover_base / cmim / hybrid), validates the labeled CSV, prepares the data (clean + features + optional split), then launches main.py finetune inside the kermt container (detached for hours-scale runs). Hyperparameters come from agent/config/defaults_finetune.json with per-flag CLI override.4license: Apache-2.05---67# kermt-finetune89Finetune a pretrained KERMT encoder on a user-supplied labeled CSV. The skill10is the workflow orchestrator: validate ckpt, validate data, prepare data,11launch the runner detached, return a run directory + container name.1213## Hardware requirements1415- **GPUs**: 1 by default (single-GPU); pass `--gpus 0` (or whichever id) to16 select one. For faster training on a multi-GPU host, pass `--num-gpus N`17 (N>1) to run data-parallel DDP across N GPUs — `--batch-size` is then18 per-GPU (effective global batch = batch_size × N).19- **VRAM**: ≥ 8 GB for the default `batch_size 32` configuration. Lower VRAM20 works at smaller batch sizes — pass `--batch-size N` to override.21- **Disk**: a few GB per run (checkpoint + features + logs).22- **Driver / CUDA**: any host supporting CUDA 12.6 (the kermt image base).23 `kermt-setup` validates this up-front.2425## Inputs2627Required:2829- `--csv <path>` — labeled CSV. First column is `smiles`; every other column30 is a target.3132Checkpoint (optional — defaults to the released model if omitted):3334- `--ckpt <path>` — input pretrain checkpoint (grover_base / cmim / hybrid).35 The validator refuses already-finetuned ckpts with a redirect to36 `kermt-infer`. **If omitted**, the skill offers to download the released37 pretrained hybrid model **nvidia/NV-KERMT-70M-v2** and finetune from it —38 see "Resolve & validate the checkpoint" (workflow step 3).39- `--pretrained-release` — explicit opt-in to use the released model without40 the interactive prompt (for non-interactive / agent runs). Mutually41 exclusive with `--ckpt`.42- `--model-dir <dir>` — where to save the downloaded bundle (default43 `$KERMT_REPO/models/NV-KERMT-70M-v2/`). An already-complete bundle there is44 reused, not re-downloaded.4546Optional:4748- `--dataset-type {regression | classification | multiclass}` — default49 `regression` (from `defaults_finetune.json`). Drives loss, metric defaults,50 and head initialization. For classification tasks pass51 `--dataset-type classification`.5253- `--targets COL [COL ...]` — explicit target column names. If omitted, the54 validator auto-detects numeric non-smiles columns and the skill confirms55 with the user before proceeding.56- `--val-csv <path>` and `--test-csv <path>` — user-provided val + test57 splits. Either pass both or pass neither (the skill auto-splits using the58 configured `--split-type`).59- `--split-type {random | scaffold_balanced | index_predetermined}` —60 default `scaffold_balanced` from `defaults_finetune.json`.61 - `random` and `scaffold_balanced`: build the val/test split internally62 from the train CSV. No `--val-csv` / `--test-csv` needed.63 - `index_predetermined`: **requires** pre-split CSVs passed via64 `--val-csv` + `--test-csv` (and, separately, per-fold index files —65 see `kermt/util/utils.split_data`). Use this when the dataset ships66 its own canonical split (e.g. `tests/data/Biogen_for_grover/scaffold/67 balance/<endpoint>/{train,val,test}.csv`).68- `--metric NAME` — `mae` (regression default), `auc` (classification default),69 or any name `kermt.util.metrics.get_metric_func` accepts.70- `--epochs N` / `--batch-size N` / `--init-lr F` / `--max-lr F` /71 `--final-lr F` / `--warmup-epochs F` / `--weight-decay F` / `--dropout F` /72 `--bond-drop-rate F` / `--dist-coff F` / `--early-stop-epoch N` /73 `--seed N` — training-hyperparameter overrides. Anything not given is74 filled from `agent/config/defaults_finetune.json`.75- `--ffn-hidden-size N` / `--ffn-num-layers N` — shared FFN trunk dims.76- `--ffn-num-task-specific-layers N` / `--ffn-task-specific-hidden-size H` —77 per-target FFN heads (default 0 = off; useful for heterogeneous multi-target78 finetunes). Both must be set together when N > 0.79- `--ensemble-size N` / `--num-folds N` — multi-model / k-fold CV. Default 180 each.81- `--gpus 0` — single GPU id for single-process finetune (default 0). Ignored82 when `--num-gpus > 1`.83- `--num-gpus N` — number of GPUs for data-parallel DDP finetune. Default 184 (single-process, unchanged). N>1 runs `main.py finetune` with `WORLD_SIZE=N`85 (one process per GPU); `--batch-size` is per-GPU.86- `--from-prepare <dir>` — skip the prepare step and reuse an existing87 `prepare_data.json` in `<dir>`. Useful when iterating on hyperparameters.8889## Workflow9091Let `$KERMT_REPO` be the path to your kermt repo checkout, and assume92`kermt-setup` has built `kermt:latest`. All paths below are on the host; the93helper bind-mounts them at known container paths.94951. **Pre-flight: ensure container + system probe.**96 ```97 $KERMT_REPO/agent/scripts/kermt_container.sh check_system | python -c "98 import json, sys; d = json.load(sys.stdin)99 if not d['ok']:100 print('System check failed:', d['gaps']); sys.exit(1)101 print(f'OK: {len(d[\"gpus\"])} GPU(s); CUDA via container toolkit')102 "103 ```104 Refuse to proceed if `ok: false`.1051062. **Compute run directory.**107 ```108 RUN_DIR=$KERMT_REPO/runs/finetune_$(date -u +%Y-%m-%dT%H-%M-%SZ)109 ```1101113. **Resolve & validate the checkpoint.**112113 **Resolve — only if `--ckpt` was omitted.** Default to the released114 pretrained hybrid model **nvidia/NV-KERMT-70M-v2**:115 - **Consent gate.** Unless `--pretrained-release` was passed, ask the user:116 "No checkpoint given — download the released model nvidia/NV-KERMT-70M-v2117 (NVIDIA Open Model License, https://huggingface.co/nvidia/NV-KERMT-70M-v2)118 and finetune from it? [y/N]". **Never download without an explicit yes**119 (or `--pretrained-release`). If both `--ckpt` and `--pretrained-release`120 are given, abort — they conflict.121 - **Save location.** Default `$KERMT_REPO/models/NV-KERMT-70M-v2/`; honor122 `--model-dir <dir>` if given. An already-complete bundle is reused.123 - **Download** (foreground; ~282 MB on first fetch):124 ```125 $KERMT_REPO/agent/scripts/kermt_container.sh run --model-dir <save-dir> -- \126 "python agent/scripts/fetch_released_model.py --out /model"127 ```128 Parse the JSON; abort on `ok: false` (surface `errors`). On success set129 `<user-ckpt> = <save-dir>/kermt_contrastive_v2.0.pt`.130131 **Validate** the resolved (or user-provided) ckpt:132 ```133 $KERMT_REPO/agent/scripts/kermt_container.sh run --ckpt <user-ckpt> -- \134 "python agent/scripts/check_checkpoint.py --mode finetune_init --ckpt /ckpt"135 ```136 Parse the JSON. Abort on `ok: false`. The validator rejects already-137 finetuned ckpts (`has_task_ffn: true`) with a redirect to `kermt-infer`.1381394. **Validate the data.**140 ```141 $KERMT_REPO/agent/scripts/kermt_container.sh run --data <user-csv> -- \142 "python agent/scripts/check_data.py --mode finetune --csv /data/<basename> [--targets COL1 COL2 ...]"143 ```144 If `--targets` was not given by the user, surface `auto_detected_targets`145 from the JSON and ask the user to confirm before continuing. Abort on146 `ok: false`.1471485. **Prepare the data** (skip if `--from-prepare` given).149150 **Pre-flight: check for sibling val.csv / test.csv.** Before invoking151 prepare_data, inspect the parent directory of `<user-csv>`. If a152 canonical-looking sibling `val.csv` (or `val_*.csv` — common variants153 include `val_T.csv`, `val_clean.csv`) AND a matching `test.csv` /154 `test_*.csv` exist next to the train CSV, the dataset ships its own155 pre-defined split. **In that case set `--split-type index_predetermined`156 AND pass `--val-csv` / `--test-csv`** — otherwise the configured157 `split_type` (default `scaffold_balanced`) will re-split the train CSV158 from scratch and silently discard the user's val/test files. When in159 doubt — or when the sibling files use non-canonical suffixes (`_T`,160 `_v2`, etc.) — surface the situation to the user and ask which they161 want.162163 **Quoting target names.** If any of the `--targets` column names164 contain shell metacharacters (`>`, `&`, `|`, `(`, `)`, `$`, etc.),165 single-quote each one when passing on the CLI to keep the shell from166 eating part of the name. Example: `--targets 'Log_Caco2_Papp_A>B'167 'logD'`. The CSV header itself is read directly by the downstream168 trainer and is unaffected, but the prepare_data.json manifest's169 `targets[]` field captures whatever the shell delivers — unquoted170 metacharacters get truncated there.171172 **Mount note:** `kermt_container.sh --data <host-csv>` mounts the173 parent directory of `<host-csv>` at `/data`. `--val-csv` and174 `--test-csv` must therefore reference files in that same parent175 directory. If val/test live in a separate directory (e.g. a sibling176 `splits/` folder), mount the parent of all three using `--data <dir>`177 on a directory rather than a file.178179 ```180 $KERMT_REPO/agent/scripts/kermt_container.sh run --data <user-csv> --run-dir $RUN_DIR -- \181 "python agent/scripts/prepare_data.py --mode finetune \\182 --csv /data/<basename> --out /runs/data \\183 --split-type <split_type> \\184 [--val-csv /data/<val-basename> --test-csv /data/<test-basename>] \\185 [--val-frac 0.1 --test-frac 0.1 --seed 0] \\186 --targets <COL1> [COL2 ...]"187 ```188 Outputs land at `$RUN_DIR/data/prepare_data.json`. For `scaffold_balanced`189 and `index_predetermined`, prep emits a single `clean_full_csv` + `.npz`;190 the runner passes them through to `main.py finetune` which calls191 `split_data` internally with the user-supplied seed.1921936. **Estimate runtime + echo applied defaults.**194 - Finetune wall time is typically minutes-to-hours on 1 GPU.195 - Surface a summary of every flag that was filled from the defaults196 vs user-supplied, so the user knows what was assumed. The runner197 records this in `args_applied`.198 - Sample message:199 `"Filling from defaults_finetune.json: epochs=30, batch_size=32,200 split_type=scaffold_balanced. Override any of these with --<flag>."`2012027. **Targets confirmation gate (hard requirement).** Before launching the203 runner, regardless of how the targets list was determined (CLI `--targets`,204 auto-detection in step 4, or a user natural-language request like205 "finetune on Caco2 and HLM"), echo the final targets list to the user with206 an explicit count:207 `"Will finetune on N target(s): COL1, COL2, ..."`. If the user's request208 specified a subset that doesn't match this list (e.g., they asked for 2209 tasks via natural language but the list still has 4), treat it as a210 discrepancy and re-prompt with the diff — never silently proceed on the211 wrong target set. Wait for explicit confirmation before launching unless212 `--yes` was given.2132148. **Launch the runner detached.** (Consistent with the pretrain skills.)215 ```216 $KERMT_REPO/agent/scripts/kermt_container.sh run_detached \\217 --name kermt-finetune-<ts> \\218 --ckpt <user-ckpt> --run-dir $RUN_DIR -- \\219 "python agent/scripts/run_finetune_local.py \\220 --ckpt /ckpt \\221 --prepare-manifest /runs/data/prepare_data.json \\222 --dataset-type <type> \\223 --out /runs \\224 [--gpus 0] \\225 [--num-gpus N] \\226 [--epochs N --batch-size N --init-lr F ...] \\227 [--ffn-num-task-specific-layers N --ffn-task-specific-hidden-size H]"228 ```229 Returns the container name + id + log file path.2302319. **Report to the user.** Output a short summary:232 - Container name + id233 - `$RUN_DIR/run.json` (manifest with cmd_replay + image digest)234 - Log file: `$RUN_DIR/logs/finetune.log`235 - TensorBoard: `$RUN_DIR/logs/tb` (open with `tensorboard --logdir236 $RUN_DIR/logs/tb`)237 - Final checkpoints land at `$RUN_DIR/ckpt/fold_0/model_0/model.pt`238 (best-val) and `last_checkpoint.pt` (sibling, auto-resume target).239 Held-out test predictions + metrics land at240 `$RUN_DIR/ckpt/fold_0/test_result.csv`. Paths vary with `--num-folds`241 / `--ensemble-size`.242 - To follow progress: `kermt-monitor <RUN_DIR>` (one-shot) or243 `docker logs -f <container-name>` (streaming).244 - To block until the run finishes (useful for short test runs):245 `docker wait <container-name>` — prints the exit code on completion.246247## Hard rules248249- **Never download the released model without consent.** When `--ckpt` is250 omitted, download `nvidia/NV-KERMT-70M-v2` only after an explicit user "yes"251 or an explicit `--pretrained-release` flag. `--ckpt` and252 `--pretrained-release` are mutually exclusive.253- **Never modify the user's input ckpt.** The runner passes its path via254 `--checkpoint_path`; `task/train.py` loads it read-only into the model and255 attaches a new FFN head. The source file stays untouched.256- **Arch comes from the ckpt, not from CLI/defaults.** The runner extracts257 `hidden_size`, `depth`, `num_attn_head`, `activation`, `embedding_output_type`,258 `self_attention` (+ `attn_hidden` / `attn_out` when applicable) from the259 ckpt's saved_args. There is no `--hidden-size` flag on this runner.260- **Never block on the long-running finetune.** The skill launches via261 `run_detached` and returns immediately after step 9. Use `kermt-monitor`.262- **Echo applied defaults back to the user.** The `args_applied` field of263 `run.json` records every flag's value + source (user / default-config).264 Surface a one-line summary of every filled-from-default flag so the user265 knows what was assumed.266267## Common errors268269- `finetune_init requires a pretrain ckpt (grover_base / cmim / hybrid)` →270 the ckpt you passed is already finetuned (has task FFN heads). Pick a271 pretrain ckpt instead, or use `kermt-infer` if you want to run272 predictions with the existing finetuned model. To resume a finetune on273 the SAME dataset, bypass the skill and call274 `python main.py finetune --checkpoint_path <ckpt> ...` directly — the275 agent skill doesn't support resume because saved-task identity276 can't be machine-verified against the new training data.277- `prepare_data manifest reports ok=False` → check `errors` for the failed278 step (typically clean_smiles or save_features). Fix and re-run.279- `ffn_num_task_specific_layers=N>0 but ffn_task_specific_hidden_size is unset`280 → MTL heads need an explicit hidden size. Pass `--ffn-task-specific-hidden-size H`.281- `finetune is single-GPU` (from `--gpus 0,1`) → `--gpus` selects one device282 for single-process finetune. For multi-GPU, use `--num-gpus N` (DDP) instead.283284## Replayability285286The `run.json` `cmd_replay` field is a single-line command that re-runs the287finetune with the same inputs, hyperparameters, and arch. To replay inside288the kermt container:289290```bash291$(jq -r .cmd_replay $RUN_DIR/run.json)292```293294If `ok_to_replay: false` in the manifest (because the kermt repo working295tree was dirty at launch time), the replay may not be bit-exact — pin the296exact commit via the `repo.commit` field and `git checkout` it297first.