pyFDN.train.losses package#
Submodules#
pyFDN.train.losses.base module#
The two loss families, and how they compose.
A training objective is a weighted sum of losses, built with + and *:
loss = pyFDN.FlatMagnitude() + 0.2 * pyFDN.Sparsity(A)
Losses come in two families, distinguished by what they read:
ResponseLoss– a function of the model’s impulse response (Response).ParameterLoss– a function of one model parameter, referenced by aParamRefit holds.
Both are called with the response, so the trainer needs no special case; a parameter loss simply ignores it.
- class pyFDN.train.losses.base.Loss[source]#
Bases:
ABCBase class for training losses. Combine with
+and scale with*.- check(model)[source]#
Preflight against the model it will train, before the first step.
Override to reject or warn about a model this loss cannot be fit on – raising here beats a silently useless optimization run. Most losses place no demands on the model, hence the no-op default.
- Return type:
- property name: str#
Short label, used as the key in
pyFDN.TrainLog.loss_log.
- class pyFDN.train.losses.base.ParameterLoss(ref)[source]#
Bases:
LossA loss on one model parameter, referenced by
ref.Implement
penalty(); the response argument is ignored.- property name: str#
Short label, used as the key in
pyFDN.TrainLog.loss_log.
- class pyFDN.train.losses.base.ResponseLoss[source]#
Bases:
LossA loss on the impulse response. Implement
__call__().
- class pyFDN.train.losses.base.Scaled(weight, loss)[source]#
Bases:
Lossweight * loss. Built byLoss.__mul__(), rarely by hand.- check(model)[source]#
Preflight against the model it will train, before the first step.
Override to reject or warn about a model this loss cannot be fit on – raising here beats a silently useless optimization run. Most losses place no demands on the model, hence the no-op default.
- Return type:
- property name: str#
Short label, used as the key in
pyFDN.TrainLog.loss_log.
- class pyFDN.train.losses.base.Sum(losses)[source]#
Bases:
LossA sum of losses. Built by
Loss.__add__(), rarely by hand.- check(model)[source]#
Preflight against the model it will train, before the first step.
Override to reject or warn about a model this loss cannot be fit on – raising here beats a silently useless optimization run. Most losses place no demands on the model, hence the no-op default.
- Return type:
- property name: str#
Short label, used as the key in
pyFDN.TrainLog.loss_log.
pyFDN.train.losses.parameter module#
Costs on a model parameter, rather than on its response.
Each takes a ParamRef naming the parameter it acts
on, so it works on any FDN structure:
A = pyFDN.param(model, "feedback")
loss = pyFDN.FlatMagnitude() + 0.2 * pyFDN.Sparsity(A)
- class pyFDN.train.losses.parameter.L1(ref)[source]#
Bases:
ParameterLossMean absolute value of a parameter – pushes it toward sparse.
- class pyFDN.train.losses.parameter.L2(ref)[source]#
Bases:
ParameterLossMean squared value of a parameter – plain weight decay.
- class pyFDN.train.losses.parameter.Sparsity(ref)[source]#
Bases:
ParameterLossDensity penalty on a square mixing matrix, after Optimizing Tiny Colorless FDNs (Dal Santo et al.).
Rewards a dense matrix – 0 when \(|A|\) is maximally dense (its entries all \(1/\sqrt{N}\), i.e. best mixing) and 1 when fully sparse. Registered with a positive weight it therefore pushes the feedback matrix away from the sparse, poorly-mixing corners of SO(N) that a magnitude-only objective is otherwise happy to sit in.
- Parameters:
ref (
ParamRef) – The matrix to penalize, e.g.pyFDN.param(model, "feedback"). Must be square.
pyFDN.train.losses.spectral module#
Losses on the spectrum of the impulse response.
- class pyFDN.train.losses.spectral.AsymmetricFlatMagnitude(*, peak_power=4.0)[source]#
Bases:
ResponseLossFlatness that punishes peaks far harder than dips – colorless.
The asymmetric sibling of
FlatMagnitude: a resonant peak rings audibly at its own pitch while a dip of the same size is largely inaudible, so this measures \(|H|\) against the response’s own RMS and raises the two sides of the deviation to different powers,\[d[f] = \frac{|H[f]|}{\sqrt{\langle |H|^2 \rangle_f}} - 1, \qquad \mathcal{L} = \Big\langle \big(d^{+}\big)^{p} + \big(d^{-}\big)^{2} \Big\rangle_f,\]with
peak_power\(p \ge 2\). Flat stays the unique minimum at everypeak_powerand the loss is gain-invariant (addEnergyto pin the level). The exponent, not a weight, is what makes it bite, and the linear magnitude (not dB) is deliberate; see the design note for why, and for what a higher exponent costs in convergence.- Parameters:
peak_power (
float) – Exponent on the peak side; dips are always quadratic. Must be at least 2 (the symmetric-shape reference, still peak-biased since a peak is unbounded and a dip is not). 4 is the default; 6 is slower but steadier. The advantage overFlatMagnitudeis not unconditional – measure your own case. Loss values are not comparable acrosspeak_poweror withFlatMagnitude; compare the responses.
Notes
A lossless FDN has every pole exactly on the unit circle, where the frequency-domain evaluation breaks down;
check()warns if the model was built without thealias_decay_dbthat avoids it.
- class pyFDN.train.losses.spectral.FlatMagnitude(target=1.0, *, channels='sum')[source]#
Bases:
ResponseLossMean squared error of \(|H|\) against a flat target – colorless.
Fits the magnitude spectrum of the (rectangularly truncated) impulse response to a constant, after Differentiable FDNs for Colorless Reverberation (Dal Santo et al.). Its frequency resolution is the model’s
nfft, which makes the fit sensitive to it; see the design note andFlatSpectrogramfor a resolution-independent alternative.- Parameters:
target (
float) – The flat magnitude to fit. The default of 1 matchespyFDN.build_fdn()’s normalized input/output gains, which put the initial \(|H|\) near unity.channels (
Literal['sum','mean','none']) – How the output channels are combined before the comparison."sum"(default) reproduces FLAMO’smse_lossconvention."none"fits each input/output pair to flat on its own, the well-posed choice for a multi-output FDN.
Notes
The optimization crosses long plateaus on this objective;
train_fdn’s defaultpatience=10stops inside one. Raise it (~100) for a converged fit. A lossless FDN has every pole exactly on the unit circle, where the frequency-domain evaluation breaks down;check()warns if the model was built without thealias_decay_dbthat avoids it.
- class pyFDN.train.losses.spectral.FlatSpectrogram(*, nfft=(256, 512, 1024, 2048), overlap=0.75)[source]#
Bases:
ResponseLossFlatness measured on multi-resolution smoothed spectra – colorless.
The multi-scale sibling of
FlatMagnitude, and the one whose frequency resolution is its own business rather than the model’s. For each analysis window \(n\), the short-time magnitudes are averaged over frames into a smoothed (Welch) spectral estimate, which is then normalized by its own mean and fitted to flat:\[P_n[f] = \sqrt{\big\langle |S_n[t, f]|^2 \big\rangle_t}, \qquad \mathcal{L} = \frac{1}{|W|} \sum_{n \in W} \Big\langle \Big( \frac{P_n[f]}{\langle P_n \rangle_f} - 1 \Big)^2 \Big\rangle_f\]A short window smooths heavily and constrains the broad spectral tilt; a long one resolves individual modes. Because each scale is normalized by its own mean, the loss is invariant to overall gain – it fits spectral shape only, and needs no assumption that \(|H| \approx 1\). Averaging over frames before measuring flatness is the whole design (per-frame flatness rewards an impulsive, comb-filtered IR); see the design note.
- Parameters:
nfft (
tuple[int,...]) – STFT window sizes, each no longer than the model’snfft. The default spans a factor of eight, which is what makes the objective multi-scale; one window alone is just a smoothedFlatMagnitude.overlap (
float) – Fractional overlap between frames (0.75 -> hop of a quarter window).
Notes
The loss value is not comparable with
FlatMagnitude’s (the smoothing removes most of the mode-to-mode fluctuation), but it is far more stable against the model’snfft.
- class pyFDN.train.losses.spectral.MatchMagnitude(target, *, channels='none')[source]#
Bases:
ResponseLossMean squared error of \(|H|\) against a reference impulse response.
The magnitude-only sibling of
MatchImpulseResponse: fits the spectral envelope while ignoring phase.
- class pyFDN.train.losses.spectral.MatchMelSpectrogram(target, *, nfft=(256, 512, 1024), device=None, **kwargs)[source]#
Bases:
_FlamoSpectrogramLossMel-scaled multi-resolution STFT distance (FLAMO’s
mel_mss_loss).
- class pyFDN.train.losses.spectral.MatchSpectrogram(target, *, nfft=(256, 512, 1024), device=None, **kwargs)[source]#
Bases:
_FlamoSpectrogramLossMulti-resolution STFT distance to a reference impulse response.
Wraps FLAMO’s
mss_loss.nfftis the tuple of STFT window sizes; the loss’s own frequency resolution therefore comes from these, independent of the model’snfft.
pyFDN.train.losses.temporal module#
Losses on the impulse response in the time domain.
- class pyFDN.train.losses.temporal.Energy(target=1.0)[source]#
Bases:
ResponseLossSquared deviation of the response’s total energy from
target.A blunt level anchor: useful next to a magnitude-only loss to stop an objective that is invariant to overall gain from drifting.
- class pyFDN.train.losses.temporal.MatchCumulativeEnergy(target, *, window=1024, hop=None, power=0.5, floor_db=-100.0, frequency='descending')[source]#
Bases:
ResponseLossDoubly-cumulated energy against a reference – decay and colour, no bands.
Takes the short-time power spectrum of both signals and integrates it twice, backwards in time and downwards in frequency:
\[E[f, t] = \sum_{t' \ge t} \; \sum_{f' \ge f} \big| S[f', t'] \big|^2\]so \(E[f, t]\) is the energy still to come after time \(t\) in the band above \(f\), and \(E[0, 0]\) is the total energy. The loss is the RMS difference of the two surfaces after a compressive power.
Cumulating twice carries both things a fit needs: read down the \(t = 0\) edge and you have the integrated spectrum (the colour), read across the \(f = 0\) edge and you have the full-band decay, and the interior ties them together band by band – without the arbitrary quantization of octave bands. The compressive
power(rather than a logarithm) keeps the surface’s six-orders-of-magnitude dynamic range visible to the loss while staying bounded. See the design note for the reasoning behind the defaults.- Parameters:
target (
Any) – Reference IR, shape(n_samples,),(n_samples, n_out)or(n_samples, n_out, n_in). Zero-padded or truncated to the model’snfft.window (
int) – STFT window and hop in samples. UnlikeMatchEnergyDecaythis loss needs no window long enough to resolve an octave – it never splits into octaves – so the default is short.hop (
int|None) – STFT window and hop in samples. UnlikeMatchEnergyDecaythis loss needs no window long enough to resolve an octave – it never splits into octaves – so the default is short.power (
float) – The compression exponent \(p \in (0, 1]\) applied to the normalized surface. 1 is no compression (raw energies), 0.5 the default, 0.25 stronger.floor_db (
float) – Hard floor on the normalized surface, in energy dB below the reference’s total energy. It bounds the gradient of the compression near zero and keeps the fit off the numerical floor of the render;clampmeans no gradient flows from anything below it.frequency (
str) – Which way the frequency cumulation runs, and with it the loss’s balance between the ends of the spectrum. The default"descending"(the plain reading of “energy above this frequency”) gives a low band little gradient;"both"scores both directions and averages, and is what to reach for when the fit has to find a decay it was not given. See the design note for the comparison.
Notes
The surface is normalized by the reference’s total energy, one constant – not each surface by its own – so a level error is a genuine error of the loss rather than something it is blind to. Fitting shape only, with the level left to another term, is the one thing this loss deliberately does not do.
- class pyFDN.train.losses.temporal.MatchEnergyDecay(target, *, window=4096, hop=None, bands=None, floor_db=-45.0)[source]#
Bases:
ResponseLossRMS dB error of the octave-band energy decay curves against a reference.
The loss that sees the decay – and the one to add when the decay is a trained parameter (a
AttenuationFilterin thepost_delayhook). A magnitude spectrogram distance is not a substitute for fitting a decay; see the design note.Each band’s Schroeder curve is normalized to its own value at \(t=0\), so the loss reads the decay and nothing else – level is left to whatever else is in the objective.
The value is in dB, which puts it many orders of magnitude above a spectrogram distance: weight accordingly, and read
TrainLog.loss_log(which stores every term unweighted) to see what each term is worth.- Parameters:
target (
Any) – Reference IR, shape(n_samples,),(n_samples, n_out)or(n_samples, n_out, n_in). Zero-padded or truncated to the model’snfft.window (
int) – STFT window and hop in samples for the band energies. The default 4096 (85 ms at 48 kHz) resolves the 63 Hz octave; shorter windows leave the low bands with too few bins to be worth reading.hop (
int|None) – STFT window and hop in samples for the band energies. The default 4096 (85 ms at 48 kHz) resolves the 63 Hz octave; shorter windows leave the low bands with too few bins to be worth reading.bands (
Any) – Band edges in Hz; defaults to the octave bands from 44 Hz to 11.3 kHz.floor_db (
float) – Only the part of each band’s curve where the target is still above this level is compared. Past it a measurement is reading its own noise floor, and fitting that would fit the microphone.
- class pyFDN.train.losses.temporal.MatchImpulseResponse(target)[source]#
Bases:
ResponseLossMean squared error against a reference impulse response, sample by sample.
The strictest of the matching losses – it fits phase as well as magnitude, which for a reverberator is usually more than you want. Reach for
MatchSpectrogramunless you are fitting an early part or a short filter.- Parameters:
target (
Any) – Reference IR, shape(n_samples,),(n_samples, n_out)or(n_samples, n_out, n_in). Zero-padded or truncated to the model’snfft.
Module contents#
Training losses for FDNs – see pyFDN.train.losses.base for how they
compose into an objective.