Skip to content

fix(loss): use raw mel magnitude for the mag_weight term in MultiScal… - #12

Open
andrewwhitecdw wants to merge 2 commits into
NVIDIA:mainfrom
andrewwhitecdw:sweep/bugfix-mag-weight-loss
Open

fix(loss): use raw mel magnitude for the mag_weight term in MultiScal…#12
andrewwhitecdw wants to merge 2 commits into
NVIDIA:mainfrom
andrewwhitecdw:sweep/bugfix-mag-weight-loss

Conversation

@andrewwhitecdw

Copy link
Copy Markdown

…eMelSpectrogramLoss

Summary

In MultiScaleMelSpectrogramLoss.forward() (loss.py), the magnitude (mag_weight) portion of the multi-scale mel loss is computed between the log-mel spectrograms instead of the raw mel spectrograms. As a result, setting mag_weight > 0 does not add a magnitude-domain loss at all — it just adds a second, differently-weighted copy of the log-mel loss.

This is latent with the default mag_weight=0.0 (term is multiplied by zero), so default BigVGAN training is unaffected; any user enabling mag_weight gets the wrong objective.

Root cause

Copy-paste error duplicating the log-mel line:

loss += self.log_weight * self.loss_fn(x_logmels, y_logmels)
loss += self.mag_weight * self.loss_fn(x_logmels, y_logmels)  # wrong operands

The class docstring states it is copied from descriptinc/descript-audio-codec dac/nn/loss.py, where the upstream MelSpectrogramLoss.forward() reads:

loss += self.log_weight * self.loss_fn(x_logmels, y_logmels)
loss += self.mag_weight * self.loss_fn(x_mels, y_mels)

(and DAC's MultiScaleSTFTLoss likewise compares raw magnitudes for the mag_weight term).

Fix

One-line change: compare x_mels/y_mels (raw) for the magnitude term:

loss += self.mag_weight * self.loss_fn(x_mels, y_mels)

Testing

⚠️ The repo's own tests (tests/test_activation*.py, tests/test_cuda_vs_torch_model.py) require a CUDA GPU and the fused CUDA kernel build; no GPU is available on this machine, so they could not be run here — please rely on CI.

Ran a standalone regression script (torch CPU, loss.py imported directly): instantiated MultiScaleMelSpectrogramLoss(log_weight=0, mag_weight=1) and compared its output against a hand-computed L1 between raw mel spectrograms:

  • with fix: loss = 0.061000 == expected 0.061000 → PASS
  • with fix stashed (unmodified code): loss = 0.193965 (= L1 of log-mels) → FAIL, confirming the bug is real and pre-existing on main

Also verified the default path is unchanged: with mag_weight=0.0 the term is multiplied by zero, so default training behavior is identical.

Why existing tests missed it

The test suite only covers the Snake activations and CUDA-vs-torch kernel parity; nothing exercises MultiScaleMelSpectrogramLoss, and the default mag_weight=0.0 masks the bug even if it did.

…eMelSpectrogramLoss

## Summary

In `MultiScaleMelSpectrogramLoss.forward()` (loss.py), the magnitude
(`mag_weight`) portion of the multi-scale mel loss is computed between the
*log*-mel spectrograms instead of the *raw* mel spectrograms. As a result,
setting `mag_weight > 0` does not add a magnitude-domain loss at all — it
just adds a second, differently-weighted copy of the log-mel loss.

This is latent with the default `mag_weight=0.0` (term is multiplied by
zero), so default BigVGAN training is unaffected; any user enabling
`mag_weight` gets the wrong objective.

## Root cause

Copy-paste error duplicating the log-mel line:

```python
loss += self.log_weight * self.loss_fn(x_logmels, y_logmels)
loss += self.mag_weight * self.loss_fn(x_logmels, y_logmels)  # wrong operands
```

The class docstring states it is copied from descriptinc/descript-audio-codec
`dac/nn/loss.py`, where the upstream `MelSpectrogramLoss.forward()` reads:

```python
loss += self.log_weight * self.loss_fn(x_logmels, y_logmels)
loss += self.mag_weight * self.loss_fn(x_mels, y_mels)
```

(and DAC's `MultiScaleSTFTLoss` likewise compares raw magnitudes for the
`mag_weight` term).

## Fix

One-line change: compare `x_mels`/`y_mels` (raw) for the magnitude term:

```python
loss += self.mag_weight * self.loss_fn(x_mels, y_mels)
```

## Testing

⚠️ The repo's own tests (tests/test_activation*.py,
tests/test_cuda_vs_torch_model.py) require a CUDA GPU and the fused CUDA
kernel build; no GPU is available on this machine, so they could not be run
here — please rely on CI.

Ran a standalone regression script (torch CPU, loss.py imported directly):
instantiated `MultiScaleMelSpectrogramLoss(log_weight=0, mag_weight=1)` and
compared its output against a hand-computed L1 between raw mel spectrograms:

- with fix: loss = 0.061000 == expected 0.061000 → PASS
- with fix stashed (unmodified code): loss = 0.193965 (= L1 of *log*-mels)
  → FAIL, confirming the bug is real and pre-existing on `main`

Also verified the default path is unchanged: with `mag_weight=0.0` the term
is multiplied by zero, so default training behavior is identical.

## Why existing tests missed it

The test suite only covers the Snake activations and CUDA-vs-torch kernel
parity; nothing exercises `MultiScaleMelSpectrogramLoss`, and the default
`mag_weight=0.0` masks the bug even if it did.
Flags the bug where MultiScaleMelSpectrogramLoss computed the mag_weight
term between log-mel spectrograms instead of raw mel spectrograms, so
mag_weight > 0 added a second copy of the log-mel loss instead of a
magnitude-domain loss (masked by the default mag_weight=0.0).

CPU-only; no CUDA/fused kernels required. Also guards that the default
mag_weight=0.0 path still equals the log-mel L1 loss.

Red-green verification:
- uv run --with pytest --with torch --with librosa --with scipy --index https://download.pytorch.org/whl/cpu pytest tests/test_mag_weight_loss.py
  -> 2 passed with the fix.
- git show HEAD~1:loss.py > loss.py, same command
  -> 1 failed (loss equals log-mel L1, not raw-mel L1), 1 passed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant