Model card

One model, four decisions, every number checkable.

What the detector is made of, what it was trained and tested on, how it performs on curves it never saw — recomputed in your browser as you read — and where it should not be trusted.

Overview

What the model is.

PipelineLightGBM · rbio1.3
Task
four independent present / absent classifiers (AA, 5-HT, 2E, Mela)
Input
raw SWV curve, 200 points, 0.004–0.800 V, no blank subtraction
Denoising
sym4 wavelet, 3 levels, soft universal threshold, σ from the MAD of the finest band
Features
full rbio1.3 DWT, 6 levels, 228 coefficients (cA6, cD6 … cD1)
Scaling
z-score with training statistics
Classifier
LightGBM per analyte · learning rate 0.10 · 31 leaves · 300 trees
Training and decisionseed 0
Augmentation
—
Threshold rule
MaxSSS (Youden) cut on the real training rows
Thresholds
—
Training data
1,114 real curves · seed-0 outer-training partition of ACS-ML-Biomarker
Evaluation data
125 held-out curves from 46 blocks
Intended use. Research and teaching demonstration of detection plus explanation on this data set. Not a diagnostic device; do not use it to make decisions about a person.
Results · computed live

What it does on 125 curves it never saw.

Your browser loads the model, scores each held-out curve, and compares P ≥ threshold with the label (concentration > 0). Nothing below is typed in.

Per analytecomputing…
AnalytePresentAbsentAccuracyBalanced acc.SensitivitySpecificity
Probability per curveblack tick = threshold
Each dot is one held-out curve. Coloured: the analyte is present in that curve; grey: absent. Hover for the file name.
Misclassified curves—
FileAnalytePLabel

Read this as a sanity check, not a benchmark. One fitted model, one held-out partition; replicates of a mixture share a block, so curves are not independent.

Reference check

Same answers as Python.

Live, this page load
—

Decisions that differ from the Python reference probabilities stored with each curve.

max |Pbrowser − Ppython|
—
Offline parity testall 125 curves
max |Δ denoised|
4.4e-16
max |Δ features|
2.7e-15
max |Δ raw margin|
0
max |Δ probability|
8.7e-19
decision disagreements
0

Run with node tools/parity.mjs, every stage compared.

The browser engine reimplements PyWavelets (wavedec / waverec, symmetric mode) and LightGBM tree traversal. Trees, filters, scaler and thresholds are exported from Python into data/model.json; nothing is retrained or approximated in the page.

Limits

Where it should not be trusted.

  • One model, one partition. A single fitted model and a single split (seed 0). Spread across refits and partitions was not measured.
  • Held-out curves come in blocks. Replicates of one mixture sit in the same block, so the 125 curves are not independent samples.
  • Only seen these chips. ACS-ML-Biomarker data: P4VP / PVDF coatings, CHI760F, SWV 25 mV, 5 Hz. Its probabilities do not signal when a curve is from elsewhere; a feature z-score check could not separate such curves, because held-out curves already reach max |z| ≈ 950.
  • Boundary effects. A 6-level DWT on 200 points has them: 30 of the 228 coefficients depend almost only on the first or last 10 points and carry 9–20% of the model's split gain.
  • Saturating probabilities. P pins near 0 and 1, so explanations are read on the log-odds scale.
  • Detection only. The model says present or absent; it gives no concentration estimate.
Reproduce

Rebuild it yourself.

Environment—
Scripts
  • tools/export_model.py

    Refits the model through the campaign code; writes model.json and library.json.

  • tools/export_xai.py

    Writes xai.json from the XAI run, after checking that rows and model match.

  • node tools/parity.mjs

    Compares the browser engine with Python at every stage on all 125 curves.