Fine-Tuning¶
Fine-tuning helpers live in equitrain.finetune. They keep the base model
frozen and train a smaller set of additional or selected parameters.
Use a non-zero --weight-decay/args.weight_decay and tune it on validation
data. Values such as 1e-6 to 1e-4 are typical starting points, depending on
dataset size and adapter capacity.
Fine-tuning uses the normal training loop: it runs for --epochs; there is no
separate early-stopping criterion. Equitrain evaluates before training and
after every epoch, logs to trainer.log, and saves a best checkpoint whenever
validation total loss improves. By default all best checkpoints are kept; set
--keep-best-checkpoints N to retain only the N checkpoints with the lowest
validation total loss. Use the best validation checkpoint rather than assuming
the final epoch is best.
Convergence is usually checked from the validation loss curve and from
checkpoint names such as best_val_epochs@<epoch>_e@<loss>. If validation loss
flattens or starts increasing while training loss still decreases, the run has
stopped improving or is beginning to overfit.
Delta / L2-SP¶
Delta fine-tuning gives every selected base parameter a same-shaped trainable
residual and evaluates base_parameter + delta while the base model remains
frozen. This is Equitrain's residual-parameter implementation of
L2-SP ("Starting Point") regularization from Li, Grandvalet, and
Davoine, 2018,
Explicit Inductive Bias for Transfer Learning with Convolutional Networks.
L2-SP regularizes fine-tuned parameters toward their pre-trained starting values:
Equitrain parameterizes this as theta = theta_0 + delta, so weight decay on
trainable deltas regularizes ||delta||_2^2. The base parameter theta_0 is
frozen and each delta is initialized at zero. Compared with LoRA, delta
fine-tuning uses full-size residuals rather than low-rank residuals, so it is
useful when you want the simplest residual scheme and do not need to limit
adapter size aggressively.
Implementation details:
- Torch:
DeltaFineTuneWrappermirrors selected base parameters with same-shaped delta tensors and merges them only for the forward pass or export. - JAX/NNX:
wrap_jax_module_with_deltas()/JaxDeltaFineTuneModulekeep the frozen model state underbase_paramsand the trainable residuals underparams.delta.
Torch adapter constructors wrap an Equitrain Torch model wrapper, such as a
MaceWrapper, AniWrapper, or OrbWrapper instance:
from equitrain.finetune import TorchDeltaFineTuneWrapper
args.model = TorchDeltaFineTuneWrapper(base_wrapper)
Minimal Torch training shape:
from equitrain import get_args_parser_train, train
from equitrain.finetune import TorchDeltaFineTuneWrapper
args = get_args_parser_train().parse_args([])
args.train_file = 'data/train.h5'
args.valid_file = 'data/valid.h5'
args.output_dir = 'runs/mace-delta'
args.weight_decay = 1e-6
args.model = TorchDeltaFineTuneWrapper(base_wrapper)
train(args)
JAX/NNX:
from equitrain.finetune import wrap_jax_module_with_deltas
jax_module = wrap_jax_module_with_deltas(jax_module)
variables = jax_module.init()
For Torch MACE models, semantic delta layers are ordered as:
Passing freeze_layers="2-" keeps only the node embedding and first
interaction block trainable.
Delta plus freeze_layers is targeted L2-SP
(L2-TSP): the L2-SP penalty is applied only to selected
trainable delta layers, while frozen layers keep delta = 0 and remain exactly
at their pre-trained starting values.
Freeze¶
TorchFreezeFineTuneWrapper uses the same semantic layer selection interface
without adapter tensors. It freezes selected base layers and trains the
remaining base weights directly, so exported models already contain the
fine-tuned weights and do not require a delta merge.
from equitrain.finetune import TorchFreezeFineTuneWrapper
args.model = TorchFreezeFineTuneWrapper(base_wrapper, freeze_layers='2-')
For MACE, the layer order is the same as delta fine-tuning. Thus
freeze_layers="2-" keeps the node embedding and first interaction block
trainable and freezes later blocks.
LoRA¶
LoRA adapters are available for Torch and JAX/NNX:
- Torch:
TorchLoRAFineTuneWrapper - JAX/NNX:
wrap_jax_module_with_lora()/JaxLoRAFineTuneModule
Equitrain applies LoRA only to eligible *.weight tensors with ndim >= 2.
Higher-order weights are flattened to matrices for the update and reshaped back
to their original tensor shape. Biases and 1D weights remain frozen.
Use rank_reduction to specify the percentage of rank to remove, or
rank_fraction to specify the percentage to keep. The effective update is:
where A has shape (r, in_dim), B has shape (out_dim, r), and
scale = alpha / r when alpha is provided, otherwise scale = 1.
For example, rank_reduction=75 keeps roughly 25% of the effective rank of
each eligible weight matrix, with a minimum rank of 1.
from equitrain.finetune import TorchLoRAFineTuneWrapper
args.model = TorchLoRAFineTuneWrapper(
base_wrapper,
rank_reduction=75,
alpha=16,
)
JAX helper:
from equitrain.finetune import wrap_jax_module_with_lora
lora_module = wrap_jax_module_with_lora(
jax_module,
rank_reduction=75,
alpha=16,
)
variables = lora_module.init()
For JAX, the wrapped variable tree stores the frozen imported state under
base_params and trainable LoRA weights under params.lora.
Exporting Torch Fine-Tuned Checkpoints¶
Fine-tuned Torch checkpoints should be exported before prediction or calculator use. Adapter checkpoints are merged during export when their metadata is available:
equitrain-export -v \
--model path/to/base-mace.model \
--model-wrapper mace \
--output-dir runs/mace-finetune \
--load-best-checkpoint \
--model-export runs/mace-finetune/mace-finetuned.model
For a specific checkpoint directory, replace --load-best-checkpoint with
--load-checkpoint runs/mace-finetune/best_val_epochs@....
If adapter metadata is unavailable, pass --fine-tune-wrapper delta, lora, or
freeze explicitly.
The checkpoint must have been created with current Equitrain so its args.json
contains adapter export metadata for automatic detection. Training checkpoint
directories contain optimizer/training state and are not the same artifact type
as a full exported model file.