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).?andNoiseclasses 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:
WeightedRandomSamplerby 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