Sections

Guide/Part three

Chapters 08–12 · Running & refining

Training runs

From a checkpoint that needs redirecting to a model trained from nothing — plus the part nobody warns you about: what the log actually means while it scrolls past.

Ch 08 From scratch Ch 09 Focus shifting Ch 10 Fullness & fusion Ch 11 Local Ch 12 Cloud

Training from scratch

You can train with no checkpoint at all. It is the right move when fine-tuning simply cannot give you the model you need — and it is also the slowest, least forgiving path.

Fine-tuning adjusts an existing model toward your data. Training from scratch builds the representation itself, which is what you want when your target is far away from anything published. In the source guide this was the route taken for a personal metal dataset: pre-train on a laptop to get something decent, then move to cloud GPUs with different parameters to push it further.

The entire change

Remove --start_check_point from your training command. That is it — the trainer will initialise the weights itself.

Expect bad metrics, for a long time

Metrics will look terrible early on — not because something is broken, but because the model started from noise. On a laptop they will look even worse, because of the hardware limits. On cloud hardware the curve is smoother; start with a smaller chunk size until it converges, then raise it.

Schematic · the shape of the two curves
VALIDATION METRIC OVER TIME 0 epochs → metric ↑ fine-tune from scratch
Why a from-scratch run feels broken. A schematic, not measured data: a fine-tuned run starts from a model that already separates audio, so it reaches useful numbers in a few epochs. A from-scratch run starts from noise, spends a long time looking like a failure, and plateaus lower unless the data and hardware are generous.
Reference point
What "big" actually means

A Roformer at batch_size: 4 fills the entire 140 GB VRAM of an H200. Plan your chunk size and batch size around the card you have, not around the config's defaults.

Order of operations
Converge small, then grow

Small chunks converge faster and teach the core separation. Once the metrics stop climbing, increase chunk_size and let the model learn longer musical context.

Shifting a model's focus between stems

A special case worth knowing: taking a model that separates vocals and turning it into one that separates instrumentals — without retraining everything from the ground up.

This one came from a real problem: needing to move a vocal model's target over to the instrumental. After a series of unsuccessful attempts, unwa suggested a transfer-learning approach and supplied the code for it.

The patch

train.py · freeze everything except the mask estimators
for param in model.parameters():
    param.requires_grad = False

for param in model.mask_estimators.parameters():
    param.requires_grad = True

Place it just above line 203 in train.py, where the optimiser parameters are assembled:

train.py · around line 203
optim_params = dict()
Figure 9.1 · the patched section in train.py
Code view showing the frozen parameters loop placed above the optimiser parameter dictionary in train.py
Where the patch lives. The two loops sit immediately before the optimiser dictionary is built, so the optimiser only ever receives the mask-estimator parameters as trainable.

Why this works

unwa's reasoning: "in my experience, it is the MaskEstimator that changes significantly when the target is changed." Freezing the Mel-band projections and the Roformer blocks and training only the final masking modules is enough to move the model from one target to another — and it cuts training time dramatically.

Mel-Roformer · what the patch trains
frozen trained
Complex spectrograminput
Mel-band projectionfrozen · weights kept
Roformer blocksfrozen · repetition L
Embedding projectiontrained
Mask estimationtrained

requires_grad = False for every parameter, then True again for model.mask_estimators. Everything up to the embedding projection keeps the knowledge the original model learned; only the final modules adapt to the new target. The same diagram, as it appears in the original architecture figure, is Figure 9.2 below.

Figure 9.2 · Mel-Roformer architecture
Diagram of the Mel-Roformer: Mel-band projection, Roformer blocks at high and low frequencies, embedding projection and mask estimation
What gets frozen, and what gets trained. The spectrogram is projected into mel bands, processed by Roformer blocks, projected back into an embedding and turned into a mask. The patch keeps the early stages fixed and lets the embedding projection and mask estimation adapt to the new target.
Limits of the trick
  • It will not fine-tune the other blocks, so it is good for switching targets — not for full training of a new model.
  • It does not always work. If the output does not improve, remove the patch from train.py and train normally.

How it actually went

The run was taken to epoch 12 to complete the switch, plus 2 further epochs to confirm the model stayed on the instrumental target — the second stretch purely to be sure it was not about to drift back.

Mind the learning rate when resuming

If the learning rate is too high for this second phase, the model starts doing strange things — for example reporting values such as "inst other fullness: 49". The most likely cause is that it learned too quickly. Compare the two results below.

Figure 9.3 · over-cooked run
Waveform and spectrogram views of a separation with smeared, noisy energy across the spectrum
Learning too fast. The vocal track on top and the instrumental below show the signature of an over-cooked phase: smeared, hazy energy that no longer follows the music. If your spectrograms look like this, lower the learning rate.
Figure 9.4 · the same run once settled
Waveform and spectrogram views of the same song after the target has settled, showing cleaner structure
What you are aiming for. The same material once the target has settled: dense, continuous instrumental structure, and a vocal region that behaves. Use this as your visual reference point when judging a checkpoint.

Fullness models and model fusion

Fullness measures how complete the target stem sounds. Training a model specifically for it takes three steps — and produces a specific, important artefact.

  1. Patch the architecture switch in train.py

    Find the line beginning if args.model_type in ['mel_band_roformer', 'bs_roformer']: and place a 1 before either mel or bs — or search the file for anything containing roformer.

  2. Use the multi-STFT loss

    Pass --use_multistft_loss as your loss argument on the training command.

  3. Add the loss block to your config

    These values are a working starting point; they can be adjusted.

config.yaml · loss section
loss_multistft:
  fft_sizes:
    - 1024
    - 2048
    - 4096
  hop_sizes:
    - 512
    - 1024
    - 2048
  win_lengths:
    - 1024
    - 2048
    - 4096
  window: "hann_window"
  scale: "mel"
  n_bins: 128
  sample_rate: 44100
  perceptual_weighting: false
  w_sc: 0.0
  w_log_mag: 1.0
  w_lin_mag: 0.0
  w_phs: 0.0
  mag_distance: "L1"
Warning

Training this way gives you fullness models with a lot of noise. That is the trade this loss makes: a fuller sounding stem, at the cost of added noise. Judge whether that is a good deal for your material before you build a workflow around it.

Model fusion (Sucial's script)

Not strictly training, but part of the same toolbox: a fusion script that merges two checkpoints into one, available at huggingface.co/Sucial/…/model_fusion.py. Download the .py file and drop it into the MSST folder.

Fusion requirements
  • All checkpoints must share the same target. You cannot fuse a vocal model with an instrumental model — two instrumentals both need their stem labelled other.
  • Possibly both checkpoints must share the same base architecture settings (for example dim 384, depth 6, mask_estimator_depth 2). This one is not confirmed.
  • Weights are set inside the script and should sum to 1 — for example 0.5 and 0.5 while testing.

Replace the placeholder checkpoint names in the script with the actual YAML names of the checkpoints you want to combine.

Running the training locally

Local training is the simplest loop there is: open a shell, paste the command, let it run. Your GPU will spin hard — that is how you know it is working.

powershell · administrator
cd C:\path\to\Music-Source-Separation-Training
python train.py --model_type mel_band_roformer --config_path config.yaml --results_path results/ --data_path C:\data\dataset --dataset_type 2 --num_workers 4 --device_ids 0 --start_check_point results/model.ckpt --valid_path C:\data\validation --metric_for_scheduler sdr --metrics sdr fullness bleedless
Figure 11.1 · console output during a local run
Console log showing train epoch, learning rate, training loss, number of overlaps, per-epoch instrumental metrics, elapsed time and stored weights 1 2 3 4 5 6
  1. Epoch and learning rate — which pass this is, and the rate in force. It moves when the scheduler reacts to a stalled metric.
  2. Training loss — how far this epoch's output is from the target. You want it trending down; the avg_loss line next to it is smoother than the instantaneous number.
  3. Num overlap — how many inference passes were averaged to produce the numbers below. Set it to 1 while training.
  4. Per-stem metric summary — the averaged verdict for the target stem: fullness, bleedless, l1_freq (and sdr if you requested it). This is the block to compare with the previous epoch.
  5. Elapsed time — wall-clock seconds for the epoch, which is how you work out what the rest of the run will cost.
  6. Store weights — where the checkpoint was written. This is the file you load later to separate audio.
The console while it runs. Each block is one epoch. Read it top to bottom as a story: how long the pass is, how wrong the model still is, how it scores on the validation tracks, and where the checkpoint landed.

How to read the log

LineWhat it tells you
Train epochWhich epoch is running now. It starts at 0 and continues as long as you let it.
Learning rateThe rate in force for this epoch — a value such as 1.0e-04, moved by the scheduler when the metric stalls.
First progress barProgress through the num_steps of the epoch, with elapsed time versus estimated time, and seconds per iteration (how fast the run is).
Training lossHow far the output is from the target — you want this trending down.
avg_lossThe average training loss across recent steps; smoother than the instantaneous number.
Second progress barValidation progress. It feels long on purpose if you validate with full tracks — and it reports n/n, how many validation tracks remain.
l1_freq / sdrPer-track separation quality for the stem being validated. In the example log, track 4 of epoch 3 sat around 13.4.
fullness / bleedlessThe same per-track numbers for the target stem: how complete it is, and how much of the other stem leaked in.
Num overlapThe inference overlap used to produce these results — 4 in the example. Set it to 1 to make everything much quicker.
Elapsed timeWall-clock seconds for the epoch, useful for working out what the rest of the run will cost.
Instr other …The averaged verdict for the instrumental stem with this checkpoint: fullness, bleedless, l1_freq and SDR. This is the number to compare against the previous epoch.
Fullness and bleedless pull against each other

Observed behaviour worth internalising: if fullness goes up, bleedless tends to go down — and the reverse. A stem cannot be maximally complete and maximally clean at the same time, so decide which trade your material needs before you chase a number.

While it runs

Training is a waiting game. Locally you cannot do anything GPU-heavy alongside it — no games, no renders, no video export — so plan something else for the hours. That changes on cloud hardware, where you are paying for the privilege of not caring about the machine.

Running the training in the cloud

A step-by-step walkthrough for renting a GPU on vast.ai. It is genuinely unintuitive, and the existing documentation is not written for this use case. Follow the steps in order.

  • Step 01Upload to DriveRepo plus both datasets, with at least 100 GB free
  • Step 02Connect the drivevast.ai → CLOUD → Connect Google Drive
  • Step 03Rent a GPUPyTorch (cuDNN) template, CUDA 12.4, SSH + Jupyter
  • Step 04Pull the dataCreate a dump folder, then download into /TRAINING_STUFF
  • Step 05Open a terminalcd, install requirements, OpenBLAS export, train
  • Step 06Training startsOnce “Collecting Metadata” appears, you are running
  1. Put everything on Google Drive

    Upload the training repository and both datasets into a folder of your choosing, and make sure you have at least 100 GB of drive space free. vast.ai offers a direct connection to Drive under CLOUD on the site (below INSTANCES): click Connect Google Drive, name the connection, and authorise it.

  2. Rent a GPU

    In the SEARCH section, pick the PyTorch (cuDNN Runtime) template with CUDA 12.4, SSH and Jupyter. Give the instance enough disk space — it is included in the hourly price, so larger storage costs more per hour.

  3. Create a dumping folder, then pull your data down

    Once the instance appears under INSTANCES, wait for it to finish loading and enter it. Create a folder for the data (in the source guide it was simply called TRAINING_STUFF). Do not close the page: go back to INSTANCES and click the cloud icon on the instance — a message appears about uploading or downloading data from cloud providers. Enter the source folder and the destination path, for example /TRAINING_STUFF, and let it run.

  4. Open a terminal and adapt the training command

    When the transfer finishes, your data is visible in the Jupyter file browser. Take the training command from earlier in the guide, point --data_path and --valid_path at the uploaded folders, and — this is the part people get wrong — put a slash both before and after the path:

    path syntax on the instance
    --data_path /TRAINING_STUFF/DATA/2stem_metal_dataset/
    --valid_path /TRAINING_STUFF/DATA/validation/

    Then open File → New → Terminal in Jupyter.

  5. Run the four commands in sequence

    In the terminal, one after the other:

    • change directory into the repository
    • install the requirements
    • the OpenBLAS export (it surfaced at the metadata stage in the source run, and turned out to be unrelated to the actual problem — but it costs nothing to set)
    • the training command
  6. Install the missing modules as they appear

    Expect No module named X. Install it, paste the training command again, repeat until you see the metadata collection stage — at which point training starts properly. The modules that came up in the source run: wandb, soundfile, auraloss, audiomentations, pedalboard, ml_collections, omegaconf, einops, beartype, rotary_embedding_torch.

“Failed to build installable wheels”

At the end of the requirements install you will likely see:

pip · wheel build error
ERROR: Failed to build installable wheels for some pyproject.toml
based projects (wxpython, diffq, pesq)

Two solutions, either of which works:

  • If pesq is the failure, install the build toolchain and pin numpy: sudo apt-get install build-essential, then pip install numpy==1.26.4.
  • Or simply delete wx-python from requirements.txt. It is only used by the GUI, not by training, and it is the package that breaks requirements installs on some systems such as Colab.
Cost control
Don't pay for a stuck run

Watch the first few hundred steps. If loss is flat or the log is repeating errors, stop the instance rather than hoping — hourly billing continues while you sleep.

Sanity first
Test locally, then rent

Run the same command on your own machine for a handful of steps. It proves the config, the dataset type and the paths before any cloud money is spent.