Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

110 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

AMIST

Adaptive Multimodal Integration for Spatial Transcriptomics

arXiv Python 3.10+ PyTorch License: MIT

A robust multimodal deep learning framework for spatial niche identification, seamlessly integrating gene expression and H&E histology via bidirectional cross-attention, adaptive gating, and masked graph autoencoding.


πŸ“– Table of Contents


🌟 Overview

Spatial Transcriptomics (ST) uniquely enables the measurement of gene expression while preserving spatial tissue context. However, relying on a single modality is challenging: transcriptomic profiles suffer from dropout noise, while morphological features (H&E images) often lack deep molecular specificity.

AMIST bridges this gap by harmonizing both modalities through an adaptive fusion mechanism. It leverages a spatial graph autoencoder to inherently preserve tissue topology, enabling highly accurate and biologically meaningful spatial niche identification.

Training Paradigm

Our framework utilizes a highly optimized Two-Stage Pipeline:

  1. Contrastive Alignment: Aligns gene and histology embeddings in a shared latent space using a symmetric NT-Xent contrastive loss.
  2. Spatial Graph Fine-Tuning: Fine-tunes the multi-modal embeddings using a Masked Graph Autoencoder (GAE) to reconstruct spatial neighborhoods, deeply capturing structural tissue morphology.

πŸš€ Key Contributions

  • Foundation Model Synergy: Leverages robust pre-trained foundation models (scGPT-human for transcriptomics, UNI2-h or standard ViT for histology). By pre-computing embeddings offline, AMIST achieves massive computational savings during training.
  • Bidirectional Cross-Attention: Facilitates context-aware, bidirectional feature aggregation between molecular profiles and morphological image patches.
  • Adaptive Modality Gating: Dynamically assesses the reliability of each modality at every spatial spot. It shifts reliance automatically (e.g., heavily weighting histology in regions with sparse RNA reads).
  • Spatial Consistency via GAE: Explicitly models tissue connectivity using a kNN graph, drastically improving the coherence of the identified spatial domains.

πŸ—οΈ Architecture

AMIST is designed for scalability and rapid iteration. Pre-computing foundation model features reduces per-epoch training time significantly while retaining deep representational power.

OFFLINE (once per sample β€” run scripts/extract_embeddings.py):
  scGPT(adata.X)  β†’  adata.obsm['X_scgpt']  (N, 512)  β€” frozen backbone
  ViT(patches)    β†’  adata.obsm['X_uni']    (N, 768)  β€” frozen backbone

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                         AMIST Training Forward Pass (forward_from_features)                 β”‚
β”‚                                                                                             β”‚
β”‚  PRE-COMPUTED FEATURES  (loaded from adata.obsm, never recomputed during training)          β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                                  β”‚
β”‚  β”‚  feat_g (N, 512)     β”‚        β”‚  feat_i (N, 768)      β”‚                                  β”‚
β”‚  β”‚  scGPT CLS embedding β”‚        β”‚  ViT backbone output  β”‚                                  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜        β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                                  β”‚
β”‚             β”‚                                  β”‚                                            β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                                  β”‚
β”‚  β”‚  proj_g  (trainable) β”‚        β”‚  proj_i  (trainable)  β”‚
β”‚  β”‚  Linear(512β†’512)     β”‚        β”‚  Linear(768β†’768)      β”‚
β”‚  β”‚  GELU + LayerNorm    β”‚        β”‚  GELU + LayerNorm     β”‚                                  β”‚
β”‚  β”‚  Linear(512β†’256)     β”‚        β”‚  Linear(768β†’256)      β”‚                                  β”‚
β”‚  β”‚  + LayerNorm         β”‚        β”‚  + LayerNorm          β”‚                                  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜        β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                                  β”‚
β”‚             β”‚  z_g (N, 256)                    β”‚  z_i (N, 256)                              β”‚
β”‚             β”‚                                  β”‚                                            β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                                 β”‚
β”‚  β”‚            BidirectionalCrossAttention                 β”‚                                 β”‚
β”‚  β”‚                                                        β”‚                                 β”‚
β”‚  β”‚   z_g_att = Attn(Q=z_g, K=z_i, V=z_i)  GE←Hist         β”‚                                 β”‚
β”‚  β”‚   z_i_att = Attn(Q=z_i, K=z_g, V=z_g)  Hist←GE         β”‚                                 β”‚
β”‚  β”‚   + residual + LayerNorm on each side                  β”‚                                 β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                                 β”‚
β”‚             β”‚  z_g_att (N, 256)                β”‚  z_i_att (N, 256)                          β”‚
β”‚             β”‚                                  β”‚                                            β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                                 β”‚
β”‚  β”‚                  AdaptiveGate                          β”‚                                 β”‚
β”‚  β”‚                                                        β”‚                                 β”‚
β”‚  β”‚   [modality dropout p=0.3 during training]             β”‚                                 β”‚
β”‚  β”‚   g = sigmoid(W [z_g_att ; z_i_att] + b)               β”‚                                 β”‚
β”‚  β”‚   z_f = g βŠ™ z_g_att + (1-g) βŠ™ z_i_att                  β”‚                                 β”‚
│  │   g→1: rely on transcriptomics (noisy histology)       │                                 │
│  │   g→0: rely on histology (strong morphology signal)    │                                 │
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                                 β”‚
β”‚                           β”‚  z_f (N, 256)  +  gate_vals (N, 256)                            β”‚
β”‚                           β”‚                                                                 β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                                 β”‚
β”‚  β”‚              MaskedGraphAutoEncoder  (Stage 2 only)     β”‚                                 β”‚
β”‚  β”‚                                                         β”‚                                 β”‚
β”‚  β”‚   Spatial kNN graph G=(V,E), k=6   β€” full-graph pass    β”‚                                 β”‚
β”‚  β”‚   Mask ratio=0.3 β†’ replace with learnable mask token    β”‚                                 β”‚
β”‚  β”‚   GATConv Γ— 3 layers (4 heads, residual + LayerNorm)    β”‚
β”‚  β”‚   β†’ h (N, 256)   gradient checkpointing per layer       β”‚                                 β”‚
β”‚  β”‚   Decoder β†’ z_hat  β†’  L_recon on masked nodes only      β”‚                                 β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                                 β”‚
β”‚                           β”‚  h (N, 256)  ← final embeddings                                 β”‚
β”‚                           β”‚                                                                 β”‚
β”‚  OUTPUT                   β–Ό                                                                 β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                                 β”‚
β”‚  β”‚  Leiden Clustering on h  β†’  Spatial Niches             β”‚                                 β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                                 β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜


LOSS FUNCTIONS
──────────────
  Stage 1   L_contrastive   NT-Xent on (z_g, z_i) pairs β€” same spot = positive, others = negative
  Stage 2   L_recon         MSE reconstruction of masked nodes in GAE
            L_reg           L2 regularization on h
            ─────────────────────────────────────────────────────────
            L_stage2  =  Ξ»1 Β· L_recon  +  Ξ»2 Β· L_reg

TRAINING STAGES
───────────────
  Stage 1   Contrastive alignment  (mini-batch B=256, all samples per epoch)
              - Both scGPT and ViT backbones fully frozen (features pre-computed)
              - Train: proj_g, proj_i, cross-attention, adaptive gate
              - GAE: frozen and skipped (run_gae=False)
              - Loss: NT-Xent contrastive on (z_g, z_i)
              - feat_g / feat_i sliced by random mini-batch index each step;
                edge_index is passed but ignored since GAE does not run

  Stage 2   Spatial GAE fine-tune  (full-graph, all samples per epoch)
              - Both backbones remain fully frozen
              - proj_g, proj_i, cross-attention, adaptive gate: ALL FROZEN
              - Train: GAE only (learns spatial context from fixed z_f)
              - Loss: GAE masked node reconstruction + L2 reg on h
              - AMP (autocast + GradScaler) enabled to halve GATConv memory usage
              - Gradient checkpointing in GATConv layers to fit N~192K on GPU

WHY FULL-GRAPH FOR STAGE 2?
────────────────────────────
  The MaskedGraphAutoEncoder uses GATConv graph convolution on the spatial kNN graph.
  GATConv aggregates each cell's embedding from its k spatial neighbors, so all N cells
  and their edges must be present in a single forward pass. Mini-batching would produce
  edge indices pointing to cells outside the batch, making the graph convolution invalid.
  AMP (float16 autocast) + gradient checkpointing are used to fit the GATConv message
  tensor (E Γ— heads Γ— dim) on GPU at N=192K, k=6.

WHY PRE-COMPUTE?
────────────────
  Both scGPT and ViT backbones are fully frozen throughout training β€” their outputs
  never change. Running them every epoch wastes GPU compute (each backbone forward pass
  costs ~10–30s per sample). Pre-computing once and loading from disk saves this cost
  for every subsequent epoch, reducing training time from O(epochs Γ— backbone_cost)
  to O(backbone_cost) + O(epochs Γ— lightweight_cost).


πŸ› οΈ Installation

AMIST relies on robust open-source libraries. We recommend managing your environment with conda or venv.

# 1. Clone the repository
git clone https://github.com/biniyamlombe/AMIST.git
cd AMIST

# 2. Create and activate a virtual environment
python -m venv .venv
source .venv/bin/activate

# 3. Install AMIST and dependencies
pip install -e .

Pre-trained Foundation Models

AMIST leverages pretrained foundation models that need to be downloaded prior to training.

1. Transcriptomics: scGPT-human
pip install scgpt huggingface_hub

python - <<'EOF'
from huggingface_hub import snapshot_download
snapshot_download(
    repo_id="bowang-lab/scGPT_human",
    local_dir="checkpoints/scGPT_human",
)
EOF

This downloads ~500 MB to checkpoints/scGPT_human/ containing weights, vocabulary, and configuration.

2. Histology: UNI2-h (Optional but Recommended)

Note: Requires accepting the model license on HuggingFace.

  1. Visit MahmoodLab/UNI2-h to accept the license.
  2. Generate an access token at huggingface.co/settings/tokens.
  3. Download the model:
python - <<'EOF'
from huggingface_hub import snapshot_download
snapshot_download(
    repo_id="MahmoodLab/UNI2-h",
    local_dir="checkpoints/UNI2-h",
    token="YOUR_HF_TOKEN",
)
EOF

Fallback: If UNI2-h is not accessible, you can set hist_encoder.backbone: "vit_base_patch16_224" in configs/default.yaml to use a standard ImageNet ViT.


πŸ“‚ Data Preparation

Data is typically processed at the cell-level into .h5ad and .h5 objects. Example data structure for Prostate Adenocarcinoma (PRAD) samples:

/path/to/data/PRAD_cell/
β”œβ”€β”€ TENX157/
β”‚   β”œβ”€β”€ TENX157_cells.h5ad          # AnnData: (N, 2000) log-normalized HVGs + spatial coords
β”‚   └── TENX157_cell_patches.h5     # HDF5: pre-extracted 128Γ—128 H&E patches per cell
└── ...

Both files are identically ordered and row-aligned (adata.obs_names[i] matches patches['cell_ids'][i]). You can generate custom aligned data using scripts/preprocess_data_cell_level.py.


⚑ Quick Start

After installing dependencies and organizing your data, you can run AMIST:

# 1. Pre-compute offline backbone embeddings (Dramatically speeds up training)
python scripts/extract_embeddings.py \
    --data_dir /path/to/data/PRAD_cell \
    --scgpt_model_dir checkpoints/scGPT_human \
    --vit_backbone vit_base_patch16_224 \
    --batch_size 64 \
    --device cuda

# 2. Train the AMIST model end-to-end
python scripts/train.py \
    --config configs/default.yaml \
    --data_dir /path/to/data/PRAD_cell \
    --out_dir outputs/amist_run1

# 3. Test functionality with synthetic data (Smoke test)
python scripts/dry_run.py

For HPC clusters, SLURM scripts are readily available in scripts/slurm/.


πŸ“‚ Repository Structure

AMIST is structured modularly. Here is the complete layout of the codebase:

amist/
β”œβ”€β”€ README.md
β”œβ”€β”€ requirements.txt
β”‚
β”œβ”€β”€ configs/                  # Configuration files
β”‚   └── default.yaml          # Hyperparameters for all training stages
β”‚
β”œβ”€β”€ notebooks/                # Jupyter Notebooks for exploration and evaluation
β”‚   β”œβ”€β”€ data_exploration.ipynb
β”‚   β”œβ”€β”€ amist_eval_with_output.ipynb
β”‚   └── processed_data_usage.md
β”‚
β”œβ”€β”€ src/                      # Core framework implementation
β”‚   β”œβ”€β”€ data/
β”‚   β”‚   β”œβ”€β”€ dataset.py            # SpatialDataset: loads .h5ad, returns (gene, patch, coord)
β”‚   β”‚   β”œβ”€β”€ preprocessing.py      # HVG selection, normalization, log1p
β”‚   β”‚   β”œβ”€β”€ patch_extractor.py    # Per-spot H&E patch cropping + transforms
β”‚   β”‚   └── graph_builder.py      # Spatial kNN graph β†’ edge_index COO tensor
β”‚   β”‚
β”‚   β”œβ”€β”€ models/
β”‚   β”‚   β”œβ”€β”€ gene_encoder.py       # ScGPTGeneEncoder (default), GeneEncoder (ablation)
β”‚   β”‚   β”œβ”€β”€ hist_encoder.py       # HistologyEncoder (ViT + projection head)
β”‚   β”‚   β”œβ”€β”€ cross_attention.py    # BidirectionalCrossAttention implementation
β”‚   β”‚   β”œβ”€β”€ adaptive_gate.py      # AdaptiveGate (gating + modality dropout)
β”‚   β”‚   β”œβ”€β”€ graph_autoencoder.py  # MaskedGraphAutoEncoder (GATConv)
β”‚   β”‚   └── amist.py              # Core AMIST model + forward pass logic
β”‚   β”‚
β”‚   β”œβ”€β”€ training/
β”‚   β”‚   β”œβ”€β”€ trainer.py            # AMISTTrainer: runs both stages, handles checkpoints
β”‚   β”‚   β”œβ”€β”€ losses.py             # NT-Xent contrastive, GAE recon, L2 regularizations
β”‚   β”‚   └── schedulers.py         # LR schedulers, staged optimizer setup
β”‚   β”‚
β”‚   └── utils/
β”‚       β”œβ”€β”€ io.py                 # Load/save AnnData, embeddings, checkpoints
β”‚       └── logging.py            # Metric logging (console + wandb)
β”‚
β”œβ”€β”€ scripts/                  # Executable scripts
β”‚   β”œβ”€β”€ train.py                  # CLI for stage 1 & 2 training
β”‚   β”œβ”€β”€ extract_embeddings.py     # Offline backbone extraction
β”‚   β”œβ”€β”€ run_baselines.py          # Baselines (gene_only, image_only, stagate)
β”‚   β”œβ”€β”€ preprocess_data_cell_level.py # Data preparation pipeline
β”‚   β”œβ”€β”€ blur.py                   # Histology blurring utility
β”‚   β”œβ”€β”€ verify_blur.py            # Script to verify blurring efficacy
β”‚   β”œβ”€β”€ dry_run.py                # Synthetic smoke test
β”‚   └── slurm/                    # SLURM batch scripts for HPC clusters
β”‚       β”œβ”€β”€ train.sh
β”‚       β”œβ”€β”€ extract_embeddings.sh
β”‚       β”œβ”€β”€ blur.sh
β”‚       └── stagate.sh
β”‚
β”œβ”€β”€ logs/                     # Logging outputs (e.g., blur_verification.png)
└── results/                  # Generated outputs
    β”œβ”€β”€ checkpoints/          # Saved model weights
    β”œβ”€β”€ embeddings/           # Extracted and trained latent representations
    β”œβ”€β”€ figures/              # Generated visualizations
    └── metrics/              # Quantitative evaluation scores

πŸ“Š Evaluation & Baselines

AMIST naturally supports competitive benchmarking. You can execute models with ablated components directly:

python scripts/run_baselines.py --data_dir /path/to/data/PRAD_cell --model_type [gene_only|image_only|concat|stagate]

Extensive evaluation suites are available in the src/evaluation module and notebooks/ to validate clustering coherence, robustness, and biological relevance.


πŸ“ Citation

If you use AMIST in your research, please cite it:

@article{AMIST2026,
  title={AMIST: Adaptive Multimodal Integration for Spatial Transcriptomics},
  author={Biniyam Lombe and Yundi Chen and Yifan Deng and Ethan Smith},
  year={2026}
}

πŸ“œ License

This project is licensed under the MIT License.

About

A multimodal deep learning framework for spatial transcriptomics, integrating scRNA-seq and H&E histology via cross-attention and graph autoencoding.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages