Cross-modal alignment: Legacy Surveys images <-> DESI spectra

AstroCLIP-style contrastive alignment of galaxy images (Legacy Surveys grz) and spectra (DESI EDR SV3) into a shared embedding space, cross-matched on sky position from the Multimodal Universe HATS catalogs. A frozen CLIP ViT-L/14 image tower (features cached once) with a trainable projection, a small trainable 1-D CNN spectrum encoder, and a symmetric InfoNCE loss.

Code: https://github.com/crimsonKn1ght/galaxy-image-spectrum-clip — this repo stores the trained artifacts, metrics, precomputed features, and the full write-up (docs/phase2_findings.md).

Repository layout

full_run_110k/            main model: 110k train pairs, no regularization
  checkpoints/            model.safetensors (trainable weights only) + optimizer/scheduler + meta.json
  metrics/                baseline.json (Phase-1 untrained control) + aligned.json (evaluate output)
  loss_curve.csv          per-checkpoint training and validation loss + val recall@k
ab_regularized_110k/      regularized variant: same data with dropout + spectrum augmentation + weight decay
  checkpoints/
  metrics/
  loss_curve.csv
precomputed_features/     cached CLIP ViT-L/14 image features + resampled 1024-bin spectra + manifest
configs/                  the exact YAML configs used
docs/phase2_findings.md   training setup, results, and analysis
README.md                 this card

raw_crossmatch/ (the ~40 GB raw image+spectrum build) is included only if exported with --include-raw; it is regenerable from the code and configs.

Results

Retrieval is image -> spectrum. pooled recall@k averages over 20 random 2000-object candidate pools (the size-independent comparison; see below). gap is final train minus val InfoNCE loss.

run train pairs pooled recall@1 pooled recall@10 val loss train/val gap image probe R2
Phase-1 baseline (untrained) - ~0.0005 (chance) ~0.005 ~5.5 - 0.576
trained, 20k 16k 0.0090 0.0745 4.81 2.35 0.659
trained, 110k (main) 110k 0.0176 0.1186 4.17 1.21 0.663
110k + regularized 110k 0.0142 0.1056 4.005 0.20 0.684

The redshift linear-probe R2 (held-out) reaches 0.84 on the spectrum tower and 0.66-0.68 on the (frozen) image tower.

What the numbers say

  • The pipeline learns real alignment: on genuinely-alignable synthetic data it reaches recall@1 ~0.6; the Phase-1 baseline here is chance.
  • More data is the dominant lever. Going 20k -> 110k pairs halved the overfitting gap (2.35 -> 1.21) and roughly doubled pooled recall@1 (0.0090 -> 0.0176). (Raw recall@k over the full test set is not comparable across runs because the test pool grows with the dataset - hence the fixed-pool metric.)
  • Regularization is not the bottleneck at 110k. It nearly closed the gap (-> 0.20) but retrieval did not improve, which locates the remaining ceiling at the frozen CLIP image tower (image probe R2 0.67 vs spectrum 0.84). CLIP was never trained on galaxy cutouts; a galaxy-appropriate image encoder is the next lever.

Retrieval is coarse: it ranks the true spectrum into roughly the top ~12% of a 2000-object pool (recall@10 ~0.12) but rarely at rank 1 - the shared, generalizable signal is largely redshift plus broad galaxy type.

Using a checkpoint

Checkpoints hold only the trainable parts (projections + spectrum encoder + temperature); the frozen image tower is rebuilt at load. With the code repo:

from common import load_config, build_model, resolve_device
from training.checkpoint import load_checkpoint

config = load_config("configs/align_cached.yaml")   # from configs/ in this repo
device = resolve_device(config)
model = build_model(config, device)
load_checkpoint(model, "full_run_110k/checkpoints/best", map_location=str(device))
model.eval()

configs/align_cached.yaml uses image_encoder.type: identity, i.e. it expects precomputed CLIP features as the image input (see precomputed_features/). To run on new raw images instead, use configs/align.yaml, which runs CLIP ViT-L/14 on the fly.

Reproduce

git clone https://github.com/crimsonKn1ght/galaxy-image-spectrum-clip && cd galaxy-image-spectrum-clip
pip install -r requirements.txt
export HF_TOKEN=...   # avoids rate-limited HATS reads
python build_crossmatch.py    --config configs/crossmatch_legacy_desi.yaml   # ~40 GB (n_objects: 140000)
python precompute_features.py --config configs/align.yaml --out-dir aligned/legacy_desi_clipfeat
python run_baseline.py --config configs/align_cached.yaml
python train.py        --config configs/align_cached.yaml
python evaluate.py     --config configs/align_cached.yaml --checkpoint checkpoints/align_cached/best
python scripts/pooled_retrieval.py --config configs/align_cached.yaml --checkpoint checkpoints/align_cached/best

Lower output.n_objects for a quick trial. Full method and rationale in docs/phase2_findings.md.

Data sources and licensing

Base datasets: MultimodalUniverse/legacysurvey (images) and MultimodalUniverse/desi (spectra); HATS versions in the UniverseTBD/multimodal-universe-hats collection. Each underlying survey carries its own terms - check the dataset cards before redistributing data or trained weights. The code is released under the MIT License; these artifacts are derived from the surveys above.

Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support