nvidia/nvalchemi-fine-tuning
>- How to fine-tune nvalchemi-compatible models with FineTuningStrategy, pretrained checkpoint initialization, module patches, trainable-parameter filters, conservative optimizer defaults, validation, restart checkpoints, and model-agnostic MACE, AIMNet2, custom BaseModelMixin, or PyTorch inputs. Use when adapting a pretrained MLIP (e.g. MACE-MP) to new reference data, freezing or patching submodules during training, or resuming an interrupted fine-tune from a checkpoint.
npx skills add https://github.com/NVIDIA/nvalchemi-toolkit --skill nvalchemi-fine-tuning
Use FineTuningStrategy when adapting pretrained weights to a new dataset,
objective, trainable parameter set, or model head. Link users to
docs/userguide/finetuning.md, docs/userguide/training.md,
docs/userguide/models.md, and docs/userguide/losses.md for full details.
import torch
from nvalchemi.training import (
CheckpointHook,
EnergyMSELoss,
FineTuningStrategy,
ForceMSELoss,
OptimizerConfig,
ValidationConfig,
create_model_spec,
default_training_fn,
)
Use nvalchemi-training finetune when the user wants quick experimentation:
an offline JSON spec, a scaffold for a supported source model, a Rich intent
report, or direct CLI execution without needing full API knowledge. Use a
Python script with FineTuningStrategy when the user needs arbitrary code,
custom model construction, dynamic data routing, dynamic losses, or non-standard
orchestration. Use nvalchemi-training train init for training-from-scratch
specs. The main groups are train, finetune, schema (dump, template),
and spec (report, run). Fine-tuning sources live under finetune init:
checkpoint, mace, aimnet2, and custom.
Common flow:
nvalchemi-training finetune init mace small-0b \
--dataset data/train.zarr \
--output-dir runs/mace-ft \
--loss-dtype-policy prediction_to_target \
--out mace-ft.json
nvalchemi-training spec report mace-ft.json
nvalchemi-training spec run mace-ft.json
Use --loss-dtype-policy on finetune init or train init when the CLI
scaffold should serialize dtype alignment in strategy.loss_fn_spec. `spec
report` renders the selected policy before execution.
Repeat --dataset to record a MultiDataset workflow. Use `torchrun ... -m
nvalchemi.training.cli spec run SPEC --distributed` for DDP; the CLI initializes
DistributedManager, prepends DDPHook, builds the dataset(s), constructs the
strategy, and calls run(...).
Runtime hooks belong in source.hooks. Each entry contains a spec object
that is the serialized BaseSpec itself: cls_path, timestamp, and the
constructor keyword fields for the hook. The CLI builds the hook during spec
validation and rejects entries that are not Hook or CheckpointableHook
instances. The optional stages list uses TrainingStage names to override
where the hook fires, and spec report lists hook firing order chronologically.
For model-input transforms such as neighbor lists, use BEFORE_FORWARD; this
stage is reused by training and strategy-owned validation. Do not add a
validation-only callback for this.
from nvalchemi.hooks import NeighborListHook
from nvalchemi.models.base import NeighborConfig
from nvalchemi.training import create_model_spec
hook_entry = {
"spec": create_model_spec(
NeighborListHook,
config=NeighborConfig(cutoff=5.0),
).model_dump(mode="json"),
"stages": ["BEFORE_FORWARD"],
}
Expect spec report to include warnings for common mistakes such as high
fine-tuning learning rates, missing validation data, unsafe checkpoint output
paths, or MACE compile settings.
FineTuningStrategy(models=...) when the user already loaded or built atrainable model.
FineTuningStrategy.from_pretrained_checkpoint(...) to start a freshfine-tuning run from model weights in a native nvalchemi checkpoint.
FineTuningStrategy.load_checkpoint(...) only to resume an interruptedfine-tuning run with optimizer/scheduler/counters/hook state.
from_pretrained_checkpoint loads the complete checkpoint model set. A
single-model checkpoint becomes a single model input; multi-model checkpoints
preserve their named mapping. Source optimizer state, hooks, validation
settings, counters, and num_epochs/num_steps do not carry over. If the user
omits loss_fn or optimizer_configs, they may opt into source metadata with
use_original_loss=True or use_original_opt_class=True. Reused optimizer
configs get optimizer_lr=1e-5 by default; pass optimizer_lr=None to keep the
checkpoint LR.
loss_fn = EnergyMSELoss() + ForceMSELoss(normalize_by_atom_count=True)
loss_fn.dtype_policy = "prediction_to_target" # optional dtype alignment
strategy = FineTuningStrategy(
models=pretrained_model,
trainable_patterns=("main.model.readout.*",),
optimizer_configs=OptimizerConfig(
optimizer_cls=torch.optim.AdamW,
optimizer_kwargs={"lr": 3e-4, "weight_decay": 1e-6},
),
training_fn=default_training_fn,
loss_fn=loss_fn,
validation_config=ValidationConfig(validation_data=val_loader, every_n_epochs=1),
hooks=[CheckpointHook("runs/finetune/checkpoints", epoch_interval=1)],
num_epochs=10,
)
strategy.run(train_loader)
Use low learning rates for full-model fine-tuning. Prefer trainable_patterns
for head-only or adapter-style workflows; patterns match fully qualified names
such as "main.model.readout.weight".
Use this when a previous nvalchemi run produced a restartable checkpoint but the
new task should get fresh fine-tuning counters and optional source loss/optimizer
metadata.
strategy = FineTuningStrategy.from_pretrained_checkpoint(
"runs/pretrain/checkpoints",
use_original_loss=True,
use_original_opt_class=True,
optimizer_lr=1e-5,
training_fn=default_training_fn,
trainable_patterns=("main.model.readout.*",),
num_steps=2_000,
)
For multi-model checkpoints, write training_fn(models, batch) and pass
optimizer_configs keyed by the model(s) to update. Models omitted from
optimizer_configs are frozen/eval during training but can be used as teachers
or references. use_original_loss and use_original_opt_class require native
strategy metadata; they do not work with component-only checkpoints.
Prefer native wrapper constructors for supported pretrained models, for example
MACEWrapper.from_checkpoint(..., compile_model=False), because they preserve
reconstruction metadata for later strategy checkpoints. compile_model=True is
inference-only for MACE and freezes parameters.
For arbitrary PyTorch checkpoints:
create_model_spec(wrapper_cls_or_factory, ...) for reproducible rebuilds.state_dict; use strict=False only for intentional head oradapter changes and inspect missing/unexpected keys.
training_fn that returns themapping expected by the loss.
fresh FineTuningStrategy checkpoint before relying on resume behavior.
state = torch.load(checkpoint_path, map_location="cpu", weights_only=True)
model.load_state_dict(state["model"] if "model" in state else state, strict=False)
model_spec = create_model_spec(MyWrapper.from_pretrained, checkpoint_path=str(checkpoint_path))
Use module_patches to replace or add child modules before optimizer
construction. Use create_model_spec(...) for patches that must serialize;
direct module instances are runtime-only.
strategy = FineTuningStrategy(
models=pretrained_model,
module_patches={
"main.model.readout": create_model_spec(
torch.nn.Linear,
in_features=128,
out_features=1,
)
},
freeze_patterns=("main.model.*",),
trainable_patterns=("main.model.readout.*",),
optimizer_configs=OptimizerConfig(optimizer_cls=torch.optim.AdamW),
training_fn=default_training_fn,
loss_fn=EnergyMSELoss(),
num_steps=1_000,
)
trainable_patterns alone is an allow-list. freeze_patterns excludes broad
regions first, then trainable_patterns re-includes exceptions. Use
freeze_mode="optimizer_only" only when frozen parameters should still receive
gradients for diagnostics or custom hooks.
Typical strategies to fine-tune without catastrophic forgetting include
adding different readout/output heads or a new atom embedding table. Users
will likely need a way to route based on dataset. If the user does not specify
a strategy, discuss options tailored to the model and fine-tuning dataset.
Note that equivariant models like MACE will need specialized read-out layers
as to preserve equivariance.
dtype, device, and output shapes before training. If label and model-output
dtypes differ intentionally, make the user aware of dtype_policy: use
"prediction_to_target" to cast outputs to labels or "target_to_prediction"
to cast labels to outputs. Set it on a leaf loss, on ComposedLossFunction(...),
or as loss_fn.dtype_policy = ... after operator-sugar construction.
autograd-derived quantities.
regress quickly with mismatched data or too-large learning rates.
FineTuningStrategy.load_checkpoint, notfrom_pretrained_checkpoint.
Take nvidia/nvalchemi-fine-tuning from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.