Spatial Frequency Loss
Spatial Frequency Loss (the SFL) is a subband-aware loss designed to mitigate spectral bias in deep neural networks and improve the learning of fine-scale image structures. The SFL measures reconstruction errors in overlapping spatial-frequency subbands produced by a Difference-of-Gaussians filter bank.
Key features
- Frequency-selective measurements: A Difference-of-Gaussians filter bank measures discrepancies between corresponding spatial-frequency responses of output and target images.
- Data-driven subband weighting: The Inverse Error Ratio (IER) determines the subband weights automatically from the training data.
- Fidelity-oriented learning: The SFL does not require adversarial training or nonlinear features from a pre-trained model. In the evaluated experiments, the SFL improved fine-scale image structures without introducing noticeable additional artifacts.
- Minimal integration: The SFL can be integrated without modifying the model architecture, data loader, or optimizer. It is used as a regular PyTorch module, and its weighted loss is simply added to the existing loss function with only a minimal change to the training loop.
- Efficient default backend: Separable filtering is the recommended default backend. The original two-dimensional convolution backend remains available for compatibility and numerical validation.
The fidelity-oriented design of the SFL may be particularly valuable when artificially generated structures are undesirable, including potential applications in medical imaging and physics-based image processing.
Requirements
The public implementation uses:
- Python
- PyTorch
- NumPy
Use versions compatible with the PyTorch environment of the target project.
Reproducibility and older PyTorch releases
Both the conv2d and separable filter backends use replicate padding
for finite Gaussian filtering. This padding mode is part of the numerical
definition of the implemented SFL filter bank. Do not replace it with a
different padding mode when reproducing SFL results.
On older PyTorch releases, CUDA backward for replicate padding may not provide a deterministic implementation. In such environments, the strict setting
torch.use_deterministic_algorithms(True)
may raise a RuntimeError during backpropagation through the SFL. This
behavior does not indicate an error in the SFL forward decomposition or loss
definition.
Users requiring strict bitwise reproducibility should use a PyTorch and CUDA environment that provides deterministic backward support for the relevant padding operation. When strict execution is not available, the application may instead use
torch.use_deterministic_algorithms(
True,
warn_only=True,
)
to continue execution while retaining warnings for operations without a deterministic implementation. This fallback does not guarantee bitwise-identical gradients for those operations. Complete reproducibility is also not guaranteed across different PyTorch releases, platforms, or CUDA environments.
Installation
Install from PyPI
For most users, install the released package from PyPI:
python3 -m pip install spatial-frequency-loss==1.0.1.post6
Install from GitHub
To use the source code, tests, and documentation from GitHub, clone the repository and install it in editable mode:
git clone https://github.com/NaoyukiIchimura/spatial-frequency-loss.git
cd spatial-frequency-loss
python3 -m pip install -e .
Alternatively, spatial_frequency_loss.py can be copied directly into an
existing project when a local single-file integration is preferred.
Definitions of the SFL and the IER
The Spatial Frequency Loss is a weighted sum of the L2 errors computed between corresponding Difference-of-Gaussians subband responses of the output and target images. Smaller Gaussian scales correspond approximately to higher spatial frequencies, so the subband losses quantify reconstruction errors from higher to lower spatial-frequency ranges.
The Inverse Error Ratio compares the pixel-wise L2 error over the entire image with the error in each subband. A subband with a smaller reconstruction error receives a larger IER value. The subband weights are obtained by averaging the IER over the calibration data, which helps balance learning across spatial frequencies. In the public implementation, the calibration workflow estimates these weights before normal optimization and then keeps the calibrated weights fixed during training.
See the project page for the complete definitions and equations (13)-(17).
Quick start
The following example shows the simplest workflow in which the SFL automatically determines the subband weights using the IER before training.
1. Import the SFL
import torch
from spatial_frequency_loss import SpatialFrequencyLoss
2. Initialize the SFL
sfl = SpatialFrequencyLoss(
num_image_channels=3,
base_scale=0.6,
num_subbands=7,
ier_update_mode="calibration",
).to(device)
mse_loss = torch.nn.MSELoss()
ier_update_mode="calibration" is written explicitly for clarity, although
"calibration" is the default update mode for the IER. The default filter backend is
"separable". The values base_scale=0.6 and num_subbands=7 match the
published experiments for RGB images.
3. Determine the subband weights using the IER
model.eval()
sfl.eval()
with torch.no_grad():
for inputs, targets in training_loader:
inputs = inputs.to(device)
targets = targets.to(device)
outputs = model(inputs)
sfl.accumulate_calibration_ier(
outputs,
targets,
)
sfl.finalize_ier_calibration()
The model forward pass and DataLoader traversal remain responsibilities of the
training program. The same training data are used for this forward-only pass for the IER,
and model parameters are
not updated. accumulate_calibration_ier() collects the IER statistics, and
finalize_ier_calibration() sets the subband weights from the accumulated
statistics.
4. Train the model with the SFL
model.train()
sfl.train()
for epoch in range(num_epochs):
for inputs, targets in train_loader:
inputs = inputs.to(device)
targets = targets.to(device)
optimizer.zero_grad()
outputs = model(inputs)
l2_loss = mse_loss(
outputs,
targets,
)
(
subband_losses,
weighted_sfl,
_,
_,
_,
) = sfl(
outputs,
targets,
)
total_loss = l2_loss + weighted_sfl
total_loss.backward()
optimizer.step()
The calibration step determines the subband weights only. During optimization, the weighted SFL is simply added to the existing pixel-wise L2 loss.
IER calibration workflow
- Construct SFL with
ier_update_mode="calibration". - Set the model and the SFL module to evaluation mode.
- Traverse the training data without gradient computation or optimizer updates.
- Pass each output-target pair to
accumulate_calibration_ier(). - Call
finalize_ier_calibration()once after the complete calibration pass. - Start normal optimization and call the SFL through
forward().
The default ier_reduction="sample" computes one IER vector per sample and
averages the vectors over all training samples. The alternative
ier_reduction="batch" computes one vector from the batch-mean losses and
averages the vectors across calibration batches.
After finalize_ier_calibration() has been called, the calibrated weights are
fixed during optimization. To repeat calibration, construct a new SFL module.
Initialization parameters
| Parameter | Default | Description |
|---|---|---|
num_image_channels |
Required | Number of image channels. |
base_scale |
Required | Base Gaussian scale. The published RGB experiments used 0.6. |
num_subbands |
Required | Number of the SFL subbands. The published experiments used 7. |
scale_radix |
2.0 |
Ratio between adjacent Gaussian scales. |
filter_size_factor |
8.0 |
Factor used to determine each finite filter size. |
filter_backend |
"separable" |
"separable" is recommended; "conv2d" is retained for compatibility and validation. |
ier_update_mode |
"calibration" |
"calibration" determines weights from a forward-only pass; "fixed" uses supplied weights. |
ier_reduction |
"sample" |
Averages sample-wise or batch-wise IER vectors. |
eps |
1.0e-12 |
Positive lower bound for IER denominators. |
subband_weights |
None |
Explicit weights. Required when ier_update_mode="fixed". |
normalize_gaussian |
False |
Whether each finite Gaussian kernel is normalized to sum to one. |
Explicitly fixed subband weights
Use fixed mode when the subband weights are already known.
sfl = SpatialFrequencyLoss(
num_image_channels=3,
base_scale=0.6,
num_subbands=7,
ier_update_mode="fixed",
subband_weights=[
weight_sb1,
weight_sb2,
weight_sb3,
weight_sb4,
weight_sb5,
weight_sb6,
weight_sb7,
],
).to(device)
To observe how another loss function changes the subband losses without adding
the SFL to the optimization objective, use fixed mode with all subband weights
set to zero. The returned weighted_sfl is then zero, while the returned
subband_losses remain available for monitoring.
sfl_for_monitoring = SpatialFrequencyLoss(
num_image_channels=3,
base_scale=0.6,
num_subbands=7,
ier_update_mode="fixed",
subband_weights=[
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
],
).to(device)
(
subband_losses,
weighted_sfl,
_,
_,
_,
) = sfl_for_monitoring(
outputs,
targets,
)
In this monitoring configuration, optimize the model with the other loss
function only and record subband_losses to analyze the spatial-frequency error
in each subband.
Return values
Calling the SFL returns five values:
(
subband_losses,
weighted_sfl,
output_subbands,
target_subbands,
active_weights,
) = sfl(
outputs,
targets,
)
subband_losses: one batch-mean L2 loss per subband.weighted_sfl: scalar weighted sum of the subband losses.output_subbands: subband images obtained from the output image, ordered from high to low spatial frequency.target_subbands: subband images obtained from the target image, in the same order.active_weights: detached copy of the active subband weights.
The public filter_bank() method may also be used when the subband images are
needed explicitly.
output_subbands = sfl.filter_bank(outputs)
Filter backends
Separable backend
The default "separable" backend applies each isotropic Gaussian as consecutive
horizontal and vertical depthwise convolutions. It is mathematically equivalent
to the corresponding two-dimensional Gaussian filtering operation and is the
recommended backend for normal use.
Conv2d backend
The "conv2d" backend applies two-dimensional depthwise Gaussian convolution.
It was the initial implementation used throughout the paper to maintain fair
comparisons across the reported experiments. It remains available for
compatibility and numerical validation.
sfl = SpatialFrequencyLoss(
num_image_channels=3,
base_scale=0.6,
num_subbands=7,
filter_backend="conv2d",
ier_update_mode="calibration",
).to(device)
The test suite checks agreement between the backends within floating-point numerical tolerances.
Checkpoint save and restore
The active weights based on the IER, accumulated calibration statistics, observation count,
and calibration-ready state are persistent buffers in state_dict().
torch.save(
{
"model": model.state_dict(),
"sfl": sfl.state_dict(),
"optimizer": optimizer.state_dict(),
},
checkpoint_path,
)
Reconstruct the model and SFL module with the intended configuration before loading the checkpoint.
checkpoint = torch.load(
checkpoint_path,
map_location=device,
)
model.load_state_dict(checkpoint["model"])
sfl.load_state_dict(checkpoint["sfl"])
optimizer.load_state_dict(checkpoint["optimizer"])
filter_backend is a construction-time configuration and is not selected by
state_dict(). Construct SFL with the backend intended for the restored run.
Computation time
The paper measured FFHQ training with 40,000 images resized to 256 x 256, batch size 4, and three complete epochs of 10,000 iterations each. The interval included GPU transfer, forward propagation, loss computation, backward propagation, and the optimizer update.
| Loss condition | Training time (ms/iteration) | Relative time |
|---|---|---|
| Baseline | 50.50 +/- 0.043 | 1.00 |
| Baseline + FFL | 52.15 +/- 0.033 | 1.03 |
| Baseline + FRL | 59.67 +/- 0.063 | 1.18 |
| Baseline + SFL, conv2d | 267.9 +/- 0.724 | 5.31 |
| Baseline + SFL, separable | 66.85 +/- 0.021 | 1.32 |
The first four rows are from Table 6 of the paper, where FFL and FRL stand for Focal Frequency Loss and Feature Reconstruction Loss, respectively. The original SFL implementation used the conv2d backend, which was retained throughout the paper to ensure fair comparisons across experiments. Because separable filtering is mathematically equivalent and substantially faster, the separable backend was implemented after publication and is used as the default in the public software.
A post-publication measurement using the same measurement procedure found an approximately 4.01-fold speed-up over the original SFL implementation and an approximately 75.05% reduction in the SFL training time.
Testing
Run the public test suite from the repository root.
python3 -m pytest -q
This command asks pytest to discover and execute the public test files in the
repository. The -q option selects quieter output. The individual test cases
should be reviewed separately before a detailed coverage list is added here.
Public repository scope
This repository contains the public SFL implementation, documentation, and unit tests. The public API supports calibrated and explicitly fixed weights based on the IER.
Results and project information
The published experiments evaluated the SFL with VQ-VAE models on FFHQ and COCO2017. The SFL improved the reported validation image-quality metrics and reduced high-frequency subband losses relative to the pixel-wise baseline. See the project page for visual examples, quantitative tables, computation-time context, and post-publication information.
Paper
Naoyuki Ichimura, "Spatial frequency loss for mitigating spectral bias in deep neural networks," Machine Vision and Applications 37, 173 (2026). DOI: 10.1007/s00138-026-01927-5
Citation and archival
Released versions of spatial-frequency-loss are preserved through the
GitHub-Zenodo integration. For research use, please cite the methodology paper
and the archived software release corresponding to the version used.
Citation metadata are provided in CITATION.cff. GitHub users can also select Cite this repository on the repository page to obtain formatted citation information. The Zenodo DOI badge and software citation will be added after the first archived release is published.
License
The Spatial Frequency Loss software is licensed under the GNU General Public
License, version 3 only (GPL-3.0-only). Commercial use, modification, and
redistribution are permitted under the terms of the license. If a modified
version of the SFL or a derivative program based on the SFL is distributed, the
corresponding source code must be provided under GPLv3.
See LICENSE for the complete license terms.
Copyright (C) 2026 Naoyuki Ichimura
Metadata
Release files for spatial-frequency-loss 1.0.1.post6
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| spatial_frequency_loss-1.0.1.post6.tar.gz | 32.3 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| spatial_frequency_loss-1.0.1.post6-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 58.6 kB
Release files / spatial_frequency_loss-1.0.1.post6.tar.gz
| Download URL | spatial_frequency_loss-1.0.1.post6.tar.gz |
|---|---|
| Size | 32.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
49cdec84e54819a1b2af9635207ced9fa7b9f780a7bd8519ce4116f709ea0d8b
|
|
BLAKE2b-256 checksum How to use checksums |
f1ebbdb2230f07279ccb8e745f9c2c343025d13fedd6272c4f6a28a3f159e4d6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 23, 2026.
Transparency logRelease files / spatial_frequency_loss-1.0.1.post6-py3-none-any.whl
| Download URL | spatial_frequency_loss-1.0.1.post6-py3-none-any.whl |
|---|---|
| Size | 26.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
52846c9879d1b76c919c372a0da0600535b38ecc7cbb3dc2adac8f269dbe623b
|
|
BLAKE2b-256 checksum How to use checksums |
a7a774d6631abb4d3574612d38488b6c2dfc4b4a81110134b18fe0026afac4d4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 23, 2026.
Transparency logSecurity for spatial-frequency-loss 1.0.1.post6
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for spatial_frequency_loss-1.0.1.post6.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 23, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.