Diffusion Noise Models

This page describes the diffusion noise model available in synference. In contrast to the empirical noise models discussed in the previous sections, which simply model per-band uncertainties as a function of magnitude using binned interpolation, the diffusion noise model is a generative model that learns to the full latent distribution of noise in the data. This allows it to capture more complex noise properties, such as correlations between bands, and to generate more realistic noise realizations.

This noise model is based on the diffusion probabilistic model framework, which has been successfully applied to a wide range of generative modeling tasks in machine learning. The basic idea is to model the noise as a stochastic process that gradually transforms a simple initial distribution (e.g., Gaussian noise) into the complex noise distribution observed in the data. By learning this transformation, the model can generate realistic noise samples that can be added to synthetic photometry to better match the properties of real observations.

This is implemented as the ScoreBasedUncertaintyModel, which learns the conditional uncertainty distribution p(σ|m), where σ is the uncertainty and m is the magnitude. It lives in the standalone syntillate package, which Synference installs as a dependency and re-exports from synference.noise_models, so it can be used and serialized alongside Synference’s other uncertainty models. The model is trained on a dataset of real photometric measurements, where the input is the magnitude and the target is the corresponding (log-scaled) uncertainty. Once trained, the model can be used to generate noise samples for synthetic photometry by sampling from the learned distribution.

We will demonstate this on the standard COSMOSO2020 ‘Farmer’ catalog (Weaver et al. 2020). If you want to train the noise model yourself, you will have to download the full catalog (~3 GB) from here.

[1]:
from syntillate import ScoreBasedUncertaintyModel

?ScoreBasedUncertaintyModel

Training the Noise Model

The first part is just loading and cleaning the COSMOS2020 catalog, which is fairly standard. We filter low signal to noise sources which are faint in IRAC Ch. 1. As we are interested in learning the full conditional distribution of noise across all the filters, we also remove sources with missing photometry in any of the filters.

```python

from astropy.table import Table import numpy as np

table = Table.read(‘COSMOS2020_FARMER_R1_v2.2_p3.fits’, memmap=True)

bands = [‘CFHT_u’, ‘HSC_g’, ‘HSC_r’, ‘HSC_i’, ‘HSC_z’, ‘HSC_y’, ‘UVISTA_Y’, ‘UVISTA_J’, ‘UVISTA_H’, ‘UVISTA_Ks’, ‘IRAC_CH1’, ‘IRAC_CH2’]

snr_filter = table[‘IRAC_CH1_MAG’] < 26 table = table[snr_filter]

for band in bands: table = table[np.isfinite(table[band + ‘_MAG’])]

We then construct our training dataset, which consists of the magnitudes and uncertainties for each band.

mag_array = np.array([table[band + '_MAG'] for band in bands]).T
flux_error_array = np.array([table[band + '_FLUXERR'] for band in bands]).T

Next, we can train the ScoreBasedUncertaintyModel on this dataset. This will learn the conditional distribution of uncertainties given magnitudes for each band. We will leave the model architecture as it’s default, which is a simple MLP with 5 layers and a hidden dimension of 256. The training process can take some time, especially if you are using a CPU, so be patient!

You can adjust the number of epochs and batch size as needed –> larger batch sizes will speed up training but require more memory, while more epochs will allow the model to learn better but will take longer.

from syntillate import ScoreBasedUncertaintyModel
noise_model = ScoreBasedUncertaintyModel(filter_names=bands)

# COSMOS2020 FLUXERR columns are in uJy; the model reports sigma back in
# the same units (see noise_model.sigma_units).
noise_model.fit(
    mag_array,
    flux_error_array,
    flux_uncertainty_units='uJy',
    n_epochs=200,
    batch_size=1024,
)

Saving the Model

We can save the trained model to disk for later use - the model can be serialized directly into a custom HDF5 format, and re-loaded later without needing to re-train, or rely on unstable dependencies like pickle or torch.save.

import h5py
with h5py.File('COSMOS_noise_model.h5', 'w') as f:
    noise_model.serialize_to_hdf5(f.create_group('noise_model'))

Loading the Model

We can load in the saved model from disk, just to prove it works! Here we will switch to our pre-trained model which you can get by running synference-download, but feel free to load in your own trained model if you have one.

[2]:
import h5py

with h5py.File("COSMOS_noise_model.h5", "r") as f:
    noise_model = ScoreBasedUncertaintyModel._from_hdf5_group(f["noise_model"])
---------------------------------------------------------------------------
FileNotFoundError                         Traceback (most recent call last)
Cell In[2], line 3
      1 import h5py
----> 3 with h5py.File("COSMOS_noise_model.h5", "r") as f:
      4     noise_model = ScoreBasedUncertaintyModel._from_hdf5_group(f["noise_model"])

File /opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages/h5py/_hl/files.py:555, in File.__init__(self, name, mode, driver, libver, userblock_size, swmr, rdcc_nslots, rdcc_nbytes, rdcc_w0, track_order, fs_strategy, fs_persist, fs_threshold, fs_page_size, page_buf_size, min_meta_keep, min_raw_keep, locking, alignment_threshold, alignment_interval, meta_block_size, track_times, **kwds)
    546     fapl = make_fapl(driver, libver, rdcc_nslots, rdcc_nbytes, rdcc_w0,
    547                      locking, page_buf_size, min_meta_keep, min_raw_keep,
    548                      alignment_threshold=alignment_threshold,
    549                      alignment_interval=alignment_interval,
    550                      meta_block_size=meta_block_size,
    551                      **kwds)
    552     fcpl = make_fcpl(track_order=track_order, track_times=track_times,
    553                      fs_strategy=fs_strategy, fs_persist=fs_persist,
    554                      fs_threshold=fs_threshold, fs_page_size=fs_page_size)
--> 555     fid = make_fid(name, mode, userblock_size, fapl, fcpl, swmr=swmr)
    557 if isinstance(libver, tuple):
    558     self._libver = libver

File /opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages/h5py/_hl/files.py:232, in make_fid(name, mode, userblock_size, fapl, fcpl, swmr)
    230     if swmr:
    231         flags |= h5f.ACC_SWMR_READ
--> 232     fid = h5f.open(name, flags, fapl=fapl)
    233 elif mode == 'r+':
    234     fid = h5f.open(name, h5f.ACC_RDWR, fapl=fapl)

File h5py/_objects.pyx:54, in h5py._objects.with_phil.wrapper()

File h5py/_objects.pyx:55, in h5py._objects.with_phil.wrapper()

File h5py/h5f.pyx:106, in h5py.h5f.open()

FileNotFoundError: [Errno 2] Unable to synchronously open file (unable to open file: name = 'COSMOS_noise_model.h5', errno = 2, error message = 'No such file or directory', flags = 0, o_flags = 0)

Validating the Noise Model

Using the Noise Model in Synference