Nova ACE LoRA Trainer ๐
Train a music LoRA without parking your ComfyUI for three hours
- lora_dir
- output_dir
- exit_code
- console
Training anything takes hours, and the usual local-audio route is ACE-Step's own Gradio app in a second terminal - which works, and which the community's own thread on it describes as "you can create a LoRA for now only with their gradio app." Fine until you want it in the same workflow as everything else.
Nova ACE LoRA Trainer ๐ runs ACE-Step's training loop over the tensors Preprocess produced, as a separate process. That's the design decision worth caring about: cancel it from the ComfyUI UI, and it can't take the server down with it. All VRAM comes back when it exits. Per-epoch progress goes to the terminal, not the node.
The inputs that matter
Twenty-odd widgets, but the ones that decide your afternoon:
- tensor_dir - wired from Preprocess.
- output_dir - where the adapter goes.
final/is the finished LoRA;checkpoints/holds per-epoch saves. Must not be the tensor folder. - checkpoint_dir and variant - must be the same tree the tensors were encoded against. See the trap below.
- preset -
recommendedis the sane default;quick_test,high_qualityand fourvram_*tiers are the others. Loaded from upstream'straining_v2/presets/. - rank / alpha - 32/64 is a good default under ten tracks. Rank is capacity, VRAM and file size, not minutes; step time is set by the base model.
- epochs, gradient_accumulation, warmup_steps - the trio people get wrong. More below.
- optimizer -
adamw8bitneedsbitsandbytes,prodigyneedsprodigyopt. Both are checked before the run starts. - dry_run - run once with this on. It performs every pre-flight check and prints the exact command without launching anything. Cheap insurance.
- resume_from - a checkpoint folder, to continue a run.
- acestep_repo_path - the ACE-Step clone, passed to the child process through
PYTHONPATH.
Outputs: lora_dir (the finished adapter - empty if the run didn't finish), output_dir, exit_code (0 is success, 130 means you cancelled), and console.
0 does not mean zero
On rank, alpha, learning rate, epochs, batch size, gradient accumulation, save_every and warmup, 0 means "take it from the preset." It is not a literal zero. The run banner prints an Overrides line naming everything you actually changed, which is how you find out you set something you didn't mean to.
Epochs are not steps
batches per epoch = tracks รท batch_size
steps per epoch = batches per epoch รท gradient_accumulation
total steps = steps per epoch ร epochs
With 6 tracks, batch size 1 and gradient accumulation 2, that's 3 optimizer steps per epoch - so 200 epochs is 600 steps, not 200. This matters because warmup_steps counts optimizer steps, and every shipped preset sets it to 100. On a small dataset that's longer than the entire run: the learning rate ramps from zero the whole way and never decays, and you've trained inside your own warmup.
The node computes the schedule and prints it:
Steps : 3/epoch x 200 epochs = 600 optimizer steps, warmup 60
Rule of thumb: warmup โ 10% of total steps. No Note: lines under that banner means the schedule is sane. The measured reference run - 6 FLAC masters, 22.7 minutes of audio, xl_sft, bf16, gradient checkpointing on, AMD RX 9070 XT - came in at 53 minutes, peak 11.3 GiB, 80.1 MiB adapter. Budget roughly 12GB of VRAM and take the vram_* presets as starting points, not answers; they all still ship epochs: 100 and warmup_steps: 100, so override both.
The trap
The .pt tensors describe one specific set of weights. Point the trainer at a different variant or checkpoint and nothing errors: the run completes, the loss falls, the adapter is quietly worthless. Keep variant identical across Setup Check, Preprocess and Trainer, and delete the tensor folder if you change your mind.
Install
ComfyUI Manager โ Nova Audio Player. Or:
cd ComfyUI/custom_nodes
git clone https://github.com/NovaFemme/ComfyUI-NovaAudioPlayer.git
Then, for this node only, the training dependencies - vector_quantize_pytorch, einx, frozendict, torch-einops-utils, and optionally tensorboard. Manager's install path runs the pack's install.py, which does that for you against your existing torch. Clone-by-hand users run training/nova_ace_setup.py, or install by hand with --no-deps. And clone ACE-Step 1.5 separately - this pack installs and downloads nothing at runtime.
Finally: don't judge the result from the loss. Run tier 3, listen to the output, and put the epoch numbers in the folder names down as what they are - single noisy averages, not a ranking.
Inputs (20)
| Name | Type | Default | Description |
|---|---|---|---|
| tensor_dir | STRING | Directory of .pt tensors to train on. | |
| output_dir | STRING | Where the adapter goes. final/ is the finished LoRA; checkpoints/ holds the per-epoch saves. | |
| checkpoint_dir | STRING | Must be the SAME checkpoint the tensors were encoded against. Different weights means the tensors describe a model you are not training. | |
| variant | COMBO | xl_sft | Match the variant used for preprocessing. |
| preset | COMBO | recommended | Upstream's preset, loaded from training_v2/presets/. Everything below set to 'from preset' or 0 comes from here. |
| rank | INT | 00โ512 | LoRA rank. 0 = take it from the preset. Higher means more capacity and more VRAM. |
| alpha | INT | 00โ1024 | LoRA alpha. 0 = from the preset. Convention is 2x rank. |
| learning_rate | FLOAT | 0.00000โ0.01 | 0 = from the preset (1e-4). |
| epochs | INT | 00โ100000 | 0 = from the preset. quick_test is 10, recommended 100, high_quality 1000. |
| batch_size | INT | 00โ64 | 0 = from the preset. |
| gradient_accumulation | INT | 00โ256 | 0 = from the preset. Effective batch is batch_size x this. |
| save_every | INT | 00โ10000 | Checkpoint every N epochs. 0 = from the preset. |
| optimizer | COMBO | from preset | adamw8bit needs bitsandbytes, prodigy needs prodigyopt. Both are checked before the run starts. |
| gradient_checkpointing | COMBO | from preset | Recomputes activations: 40-60% less VRAM, 10-30% slower. |
| device | COMBO | auto | On ROCm, cuda is correct โ that is what torch calls it. |
| precision | COMBO | bf16 | bf16 matches the bf16 checkpoints. |
| dry_run | BOOLEAN | false | Run every pre-flight check and print the exact command, without starting it. |
| warmup_steps | INT | 00โ100000 | LR warmup, counted in OPTIMIZER STEPS, not epochs. 0 = from the preset (100), which on a small dataset is longer than the entire run. The banner reports the real step count before anything launches. |
| acestep_repo_pathopt | STRING | Path to an ACE-Step clone. Passed to the child through PYTHONPATH. | |
| resume_fromopt | STRING | Resume from a saved checkpoint directory. |
Outputs (4)
| Name | Type | Description |
|---|---|---|
| lora_dir | STRING | The finished adapter โ output_dir/final. Empty if the run did not finish. |
| output_dir | STRING | The run's output directory, holding final/, checkpoints/ and runs/. |
| exit_code | INT | The trainer's exit code. 0 is success, 130 means cancelled. |
| console | STRING | Run log โ wire into Nova Console. |