| name | massspecgym |
| description | MassSpecGym benchmark reference — dataset schema, Python API, transforms, evaluation metrics, retrieval pipeline, and common patterns. Use when working with MassSpecGym data, models, or evaluation. |
MassSpecGym Reference
NeurIPS 2024 Spotlight benchmark for molecular discovery from MS/MS spectra. Three challenges: de novo generation, molecule retrieval, spectrum simulation.
Installation
pip install massspecgym
pip install massspecgym[notebooks,dev]
Dataset Schema
231,104 spectra total (train: 194,119 / val: 19,429 / test: 17,556). HuggingFace dataset with a single main subset.
When loaded via load_massspecgym(), identifier becomes the DataFrame index (not a column). The 13 columns are:
| Column | Type (loaded) | Type (raw HF) | Description |
|---|
mzs | np.ndarray (float64) | str | m/z values per peak |
intensities | np.ndarray (float64) | str | Intensity values per peak |
smiles | str | str | SMILES string (5–255 chars) |
inchikey | str | str | InChI Key (14 chars) |
formula | str | str | Molecular formula |
precursor_formula | str | str | Precursor formula |
parent_mass | float64 | float64 | Parent mass (59–998) |
precursor_mz | float64 | float64 | Precursor m/z (60–999) |
adduct | str | str | [M+H]+ or [M+Na]+ |
instrument_type | str | str | Orbitrap or QTOF |
collision_energy | float64 | float64 | 0–358, nullable |
fold | str | str | train, val, or test |
simulation_challenge | bool | bool | Part of simulation challenge |
Important: In the raw HuggingFace Parquet files, mzs and intensities are comma-separated strings. load_massspecgym() parses them to numpy arrays automatically.
Data Loading
Quick load (pandas DataFrame)
from massspecgym.utils import load_massspecgym
df = load_massspecgym()
df = load_massspecgym(fold="train")
Returns DataFrame with mzs/intensities already parsed to numpy arrays.
Direct HuggingFace load (no massspecgym dependency)
from datasets import load_dataset
ds = load_dataset("roman-bushuiev/MassSpecGym")
Direct Parquet load
import pandas as pd
df = pd.read_parquet("hf://datasets/roman-bushuiev/MassSpecGym/main/val/*.parquet")
Utility functions
from massspecgym.utils import (
load_massspecgym,
load_massspecgym_mols,
load_train_mols,
load_val_mols,
load_unlabeled_mols,
parse_spec_array,
pad_spectrum,
compute_mass,
)
Splits
Fold assignments are in the fold column: train, val, test. Unique molecules: 25,046 in train, 31,602 total. The MassSpecDataModule reads these from the dataset metadata or a custom TSV with identifier and fold columns.
Transforms
Spectrum transforms (SpecTransform subclasses)
from massspecgym.data.transforms import SpecTokenizer, SpecBinner, SpecToMzsInts
SpecTokenizer(n_peaks=60, prec_mz_intensity=1.1)
SpecBinner(max_mz=1005, bin_width=1, to_rel_intensities=True)
SpecToMzsInts(n_peaks=None, mz_from=10.0, mz_to=1000.0, mz_bin_res=0.01)
Molecule transforms (MolTransform subclasses)
from massspecgym.data.transforms import (
MolFingerprinter,
MolToInChIKey,
MolToFormulaVector,
MolToPyG,
MolToFingerprints,
)
MolFingerprinter(type="morgan", fp_size=2048, radius=2)
MolToInChIKey(twod=True)
MolToFormulaVector()
MolToPyG(pyg_node_feats=[...], pyg_edge_feats=[...])
MolToFingerprints(fp_types=["morgan", "maccs", "rdkit"])
Dataset Classes
Base: MassSpecDataset
from massspecgym.data import MassSpecDataset
dataset = MassSpecDataset(
spec_transform=SpecTokenizer(n_peaks=60),
mol_transform=MolFingerprinter(),
pth=None,
return_mol_freq=False,
return_identifier=False,
identifiers_subset=None,
dtype=torch.float32,
)
__getitem__ returns dict. Base keys always present: spec, mol, precursor_mz, adduct. With defaults, that's all 4 keys. Set return_mol_freq=True to add mol_freq (tensor), return_identifier=True to add identifier (str).
RetrievalDataset
from massspecgym.data import RetrievalDataset
dataset = RetrievalDataset(
spec_transform=SpecTokenizer(n_peaks=60),
mol_transform=MolFingerprinter(fp_size=4096),
mol_label_transform=MolToInChIKey(),
candidates_pth=None,
)
Per-item keys (from __getitem__): base keys plus smiles (str), candidates_smiles (list[str]), candidates_mol (tensor [N_cands, fp_size]), labels (list[bool]), identifier, mol_freq.
Per-batch keys (after collate_fn): candidates are flattened across the batch:
candidates_mol: (total_cands, fp_size) — all candidates concatenated
candidates_smiles: flat list of all candidate SMILES
labels: (total_cands,) bool tensor
batch_ptr: (batch_size,) int64 tensor — number of candidates per sample
smiles: list of query SMILES
- Plus standard:
spec, mol, precursor_mz, adduct, mol_freq, identifier
Candidate lists are variable-length per sample. Use batch_ptr to reconstruct per-sample groups (e.g., torch.split(scores, batch_ptr.tolist())).
DataModule
from massspecgym.data import MassSpecDataModule
dm = MassSpecDataModule(
dataset=dataset,
batch_size=32,
num_workers=0,
persistent_workers=True,
split_pth=None,
)
Splits into train/val/test subsets based on the fold column. Training loader shuffles; val/test do not.
Model Base Classes
Built on PyTorch Lightning.
MassSpecGymModel (abstract base)
from massspecgym.models.base import MassSpecGymModel, Stage
class MassSpecGymModel(pl.LightningModule, ABC):
def __init__(self, lr=1e-4, weight_decay=0.0, ...):
...
@abstractmethod
def step(self, batch: dict, stage: Stage) -> dict:
...
def configure_optimizers(self):
...
RetrievalMassSpecGymModel
from massspecgym.models.retrieval.base import RetrievalMassSpecGymModel
class MyRetrieval(RetrievalMassSpecGymModel):
def __init__(self, at_ks=(1, 5, 20), myopic_mces_kwargs=None, **kwargs):
super().__init__(at_ks=at_ks, **kwargs)
def step(self, batch: dict, stage: Stage) -> dict:
...
Evaluation methods (called automatically):
evaluate_retrieval_step() — computes HitRate@K (Recall@K) using torchmetrics.functional.retrieval
evaluate_mces_at_1() — MCES distance between top-ranked and ground-truth molecule
test_step() — sorts candidates by score, optionally saves to DataFrame
DeNovoMassSpecGymModel
For structure generation from spectra.
SimulationMassSpecGymModel
For spectrum prediction from molecular structures.
Evaluation Metrics
| Metric | Description | Direction |
|---|
| Recall@K (HitRate@K) | Ground truth in top-K ranked | higher = better |
| MRR | Mean Reciprocal Rank | higher = better |
| MCES@1 | Max Common Edge Subgraph distance at rank 1 | lower = better |
MCES is computed via MyopicMCES — graph edit distance based on maximum common edge subgraph.
Retrieval Candidate Libraries
MassSpecGym provides two candidate libraries per spectrum for retrieval:
- Weight-based: candidates by molecular weight from precursor m/z
- Formula-based: candidates by matching chemical formula
Candidate mappings are JSON files: {query_smiles: [candidate_smiles_1, ...]}. Downloaded automatically by RetrievalDataset.
Special Tokens
from massspecgym.definitions import PAD_TOKEN, SOS_TOKEN, EOS_TOKEN, UNK_TOKEN
Common Patterns
Bypassing massspecgym for custom pipelines (like GLMR)
The massspecgym package is PyTorch-Lightning-heavy. For custom training loops:
from massspecgym.utils import load_massspecgym
df = load_massspecgym(fold="train")
from datasets import load_dataset
ds = load_dataset("roman-bushuiev/MassSpecGym")
df = ds["val"].to_pandas()
import numpy as np
df["mzs"] = df["mzs"].apply(lambda s: np.fromstring(s, sep=","))
df["intensities"] = df["intensities"].apply(lambda s: np.fromstring(s, sep=","))
Retrieval evaluation without their framework
def recall_at_k(ranks, k):
return (ranks <= k).float().mean().item() * 100
def mrr(ranks):
return (1.0 / ranks).mean().item() * 100
from myopic_mces import MCES
mces_dist = MCES(mol1, mol2)
Loading candidate libraries directly
Candidate JSONs map each query SMILES to its candidate list. They're auto-downloaded by RetrievalDataset, but you can also download them manually from the HuggingFace repo's data files.
Key Gotchas
- Python ≥3.11 required for
massspecgym package. If using Python 3.10, load data via HuggingFace datasets directly instead.
- mzs/intensities are strings in the raw HuggingFace dataset — must parse to arrays.
- Variable-length candidates — each query has a different number of candidates. Handle with padding or batch_ptr.
- Fold column — use
fold column for splits, not random splitting. The test fold is held out.
- InChIKey for matching — ground-truth matching uses InChIKey comparison, not SMILES string equality.
- SpecTokenizer prepends precursor — if
prec_mz_intensity is set, an extra row (precursor_mz, prec_mz_intensity) is prepended, making output shape (n_peaks+1, 2).
- MolFingerprinter returns int32 but the dataset casts tensors to float32 by default (
dtype param). So mol in batch is float32 even though raw fingerprints are int32.
- RetrievalDataset always returns mol_freq/identifier regardless of the
return_mol_freq/return_identifier flags (those only apply to base MassSpecDataset).