HMOC Binary Classifier (5-model ensemble)

A 1D-CNN ensemble that flags High-Mass Organic Cation (HMOC) spectra inside large pools of CDA (Cosmic Dust Analyzer) mass spectra. It is an expert triage model: it does not guarantee a clean label, but it ranks the top candidates with a high density of true HMOCs for manual review.

Task & data

  • Formulation: anomaly/retrieval binary classification β€” HMOC (class 1) vs. clean, non-HMOC background (class 0).
  • Imbalance: only 83 labeled HMOC rows vs. 2,600 curated clean negatives (31:1). ? and Noise classes were dropped to keep the negative class high-quality. No SMOTE / augmentation.
  • Input: each raw spectrum is truncated or zero-padded to 1000 bins, then min-max normalised to [0, 1] per spectrum.

Architecture

Shallow 1D CNN (deliberately small to avoid overfitting 83 positives):

Conv1d(1->32, k=7, p=3) + BN + ReLU
Conv1d(32->64, k=5, p=2) + BN + ReLU
Conv1d(64->128, k=3, p=1) + BN + ReLU
AdaptiveAvgPool1d(1)
Linear(128->64) + ReLU + Dropout(0.4)
Linear(64->1)  -> sigmoid

Ensemble

Five independent models, one per StratifiedKFold fold, trained with seeds [42, 100, 2026, 777, 888]. At inference the mean of the 5 sigmoid outputs is the final HMOC probability (lower variance than any single model).

Training

  • Loss: Focal Loss (alpha=0.85, gamma=2.0) to focus on hard/rare positives.
  • Optimizer: AdamW (lr=1e-3, weight_decay=1e-4).
  • Batches: WeightedRandomSampler by inverse class frequency.
  • Early stopping on validation AUPRC (patience=15).

Metrics (per seed, validation fold)

Seed Fold Precision Recall F1 AUPRC Best threshold
42 0 0.435 0.625 0.513 0.508 0.759
100 1 0.400 0.706 0.511 0.359 0.446
2026 2 0.571 0.471 0.516 0.455 0.680
777 3 0.636 0.438 0.519 0.484 0.826
888 4 0.700 0.438 0.538 0.567 0.768

AUPRC baseline for a random guess here is ~0.03, so 0.36–0.57 is well above chance.

Usage

import numpy as np
from model import predict_ensemble, SEEDS, default_weights_dir

d = default_weights_dir()
paths = [f"{d}/hmoc_model_seed_{s}.pth" for s in SEEDS]

raw_spectra = [...]  # list of 1-D intensity arrays (any length)
probs = predict_ensemble(paths, raw_spectra)   # -> np.ndarray of HMOC probabilities

# rank candidates
order = np.argsort(probs)[::-1]
top = [(raw_spectra[i], float(probs[i])) for i in order[:100]]

Single-model variant:

from model import load_model
net = load_model(f"{d}/hmoc_model_seed_888.pth")

Files

Path Contents
model.py Self-contained architecture, preprocessing and ensemble inference
config.json Architecture, training hyperparameters, per-seed metrics/thresholds
hmoc_model_seed_{42,100,2026,777,888}.pth model.state_dict() for each seed
scripts/train_hmoc_ensemble.py 5-fold training of the ensemble
scripts/generate_metrics.py Per-fold validation metrics
scripts/infer_70k.py Scores the ~70k unlabelled pool with the ensemble
predictions/top_359_hmoc_predictions.csv Top-ranked HMOC candidates (sclk, confidence %)
docs/METHODOLOGY.md Full write-up of data curation, architecture, loss and evaluation
docs/hmoc_metrics.md Metrics table

The scripts expect the training / unlabelled parquet files under data/; the data itself is not included. The full 70k-row candidate table (1.2 GB, includes spectra) is also left out; rerun scripts/infer_70k.py to regenerate it.

License

No open-source license is provided for these weights; all rights reserved. Do not reuse, redistribute, or build upon this model without the author's permission.

Downloads last month
20
Inference Providers NEW
This model isn't deployed by any Inference Provider. πŸ™‹ Ask for provider support