# 03 - Modelling and the Severson reproduction [Back to 02: Feature engineering](02_feature_engineering.md) | [README](../README.md) Covers **Phase 6**, including **Gate 2** - the credibility anchor. Every table on this page is generated from a file in [`outputs/reports/`](../outputs/reports/). --- ## 1. GATE 2 - reproducing Severson et al. (2019) > **Verdict: PASSED.** Full model, primary test: **12.42%** mean percent > error against a published **9.1%** - a ratio of **1.36x**, inside the > 2.0x tolerance. Everything downstream - conformal risk control, the cost frontier, chamber allocation - assumes this pipeline extracts the same signal the source literature extracted. If that assumption were wrong, the later results would be internally consistent and externally meaningless. Reproducing a known number is the only way to test it. | Model | Features | Train | Primary test | Secondary test | |---|---:|---:|---:|---:| | variance | 1 | **14.18%** _(pub 9.8%)_ | **14.74%** _(pub 14.1%)_ | **11.39%** _(pub 14.1%)_ | | discharge | 6 | **7.97%** _(pub 5.6%)_ | **14.94%** _(pub 7.5%)_ | **9.73%** _(pub 10.7%)_ | | full | 9 | **6.77%** _(pub 4.9%)_ | **12.42%** _(pub 9.1%)_ | **13.99%** _(pub 15.6%)_ | | _dummy_mean (floor)_ | 0 | 29.38% | 34.65% | 35.90% | | _dummy_median (floor)_ | 0 | 23.98% | 29.27% | 44.94% | ### The evaluation design is the published one Severson et al. used a fixed three-way split: train and "primary test" **interleaved over batches 1 and 2**, with batch 3 held out entirely as a "secondary test". Our cohort reproduces the published set sizes exactly: **41 / 43 / 40**. Using this project's own repeated grouped CV instead would give a number that is arguably better methodology but **not comparable** to the published one, and comparability is the entire point of a reproduction. Both are reported: the published design here, the project's CV in section 3. ### The gate did its job - it caught a real error The first attempt read the design as *train = batch1, test = batch2*, because batch 1 contains exactly 41 cells and the published training set also contains 41. That coincidence is a trap. Gate 2 failed at **25.84%**, 2.84x the benchmark. The failure was diagnostic rather than merely bad: - Train and secondary-test errors reproduced **well**; only the primary test was wrong. A broken feature pipeline would have degraded all three. - The **error ordering was inverted**. Published primary (9.1%) is *lower* than secondary (15.6%), which is only possible if the primary test is in-distribution. Ours had primary *above* secondary. - The magnitude had a mechanism: batch 2 contains cells down to 150 cycles while batch 1 bottoms out at 533, so training on batch 1 alone forces extrapolation far below the training range - and mean percent error punishes short-lived cells hardest, since predicting 300 cycles for a cell that lasts 150 is a 100% error on its own. This is precisely what a gate is for: it caught a methodological error **before** anything was built on top of it. The corrected split is pinned by a regression test. ### Honest accounting of the residual gap The reproduction is 1.36x the published value, not 1.0x. **There are two candidate explanations, and they are not equally favourable, so they were distinguished by measurement rather than assumed.** | Explanation | What it would imply | Prediction it makes | |---|---|---| | **(a) Feature-list reconstruction** - the multi-feature sets were rebuilt from the paper's prose, not copied from the authors' code | The pipeline is sound; the inferred feature lists differ in detail | The gap should be **largest where reconstruction was hardest** and near zero where there was nothing to infer | | **(b) Feature-pipeline error** - something upstream computes the ΔQ(V) quantities differently from the source | The pipeline is wrong, and every downstream result is suspect | The gap should appear **everywhere**, including in the simplest model | The **variance model settles it**. It uses exactly ONE feature, `log10|var(ΔQ(V))|`, so there is no feature-list ambiguity to reconstruct at all - either the pipeline computes that quantity as the source did, or it does not. It lands at **14.74% against a published 14.1%, a ratio of 1.05x**. The multi-feature sets, whose composition had to be inferred, sit further out (discharge 1.99x, full 1.36x). That is the pattern explanation (a) predicts and the opposite of what (b) predicts: **a broken feature pipeline would fail the unambiguous single-feature model first, and it does not.** Gate 1 supports the same conclusion independently: the log10 var(ΔQ(V)) vs log10 cycle-life relationship reproduced at R² = 0.8588 against a published ρ = -0.93 (implied R² ≈ 0.86), which is the same ΔQ(V) machinery measured a different way. This is stated at length because the favourable explanation is the one a reviewer should be most sceptical of, and asserting it without the discriminating test would be exactly the move that deserves scepticism. Two further differences that cannot be ruled out and are not claimed to be excluded: the cohort here is independently re-derived (including a cycle-life definition recomputed from the capacity series rather than inherited), and three excluded cells could not be independently justified (see [docs/01](01_dataset_and_eda.md)). ## 2. Does linear fragility limit the reproduction? No. Phase 5 raised a concern that heavy-tailed features would make linear models fragile, and suggested robust or tree estimators might be needed. Tested directly on the identical split: | Estimator | Train | Primary test | Secondary test | |---|---:|---:|---:| | huber (robust linear) | 6.34% | 13.35% | 16.70% | | random_forest | 5.43% | 14.70% | 14.62% | | lightgbm | 2.11% | 17.66% | 15.42% | **The concern does not hold in this setting.** Elastic net is the *best* of the four on the primary test. LightGBM achieves by far the lowest training error and the *worst* test error - textbook overfitting with only 41 training cells and 9 features. The earlier concern was not unfounded, it was scoped differently: it arose with the full 57-feature matrix under cross-validation, where a single extrapolating cell (b1c41) dominated MAPE. On the curated 9-feature set the linear model is well-conditioned. Both statements are true; the recommendation that followed from the first was wrong for this case, and the measurement settles it. ## 3. Benchmark: model x budget 10 models x 5 budgets, evaluated over **50 outer folds** of the repeated grouped scheme (5 folds x 10 repeats). Every value is mean ± std across folds; the full table with bootstrap 95% CIs is in [`benchmarks.csv`](../outputs/reports/benchmarks.csv). ### RMSE on log10 cycle life | Model | N=5 | N=10 | N=20 | N=50 | N=100 | |---|---|---|---|---|---| | `extra_trees` | 0.0798 ± 0.0271 | 0.0752 ± 0.0247 | 0.0776 ± 0.0257 | 0.0665 ± 0.0232 | 0.0580 ± 0.0240 | | `xgboost` | 0.0879 ± 0.0258 | 0.0861 ± 0.0242 | 0.0831 ± 0.0245 | 0.0684 ± 0.0208 | 0.0603 ± 0.0215 | | `random_forest` | 0.0860 ± 0.0253 | 0.0845 ± 0.0228 | 0.0871 ± 0.0244 | 0.0704 ± 0.0213 | 0.0627 ± 0.0214 | | `lightgbm` | 0.0878 ± 0.0264 | 0.0836 ± 0.0231 | 0.0894 ± 0.0254 | 0.0686 ± 0.0227 | 0.0665 ± 0.0207 | | `ridge` | 0.0853 ± 0.0170 | 0.0808 ± 0.0179 | 0.0923 ± 0.0247 | 0.1610 ± 0.1766 | 0.1016 ± 0.0316 | | `lasso` | 0.0861 ± 0.0170 | 0.0823 ± 0.0177 | 0.1002 ± 0.0301 | 0.1754 ± 0.1423 | 0.1164 ± 0.0468 | | `elastic_net` | 0.0850 ± 0.0169 | 0.0821 ± 0.0170 | 0.0973 ± 0.0270 | 0.1666 ± 0.1555 | 0.1190 ± 0.0452 | | `huber` | 0.0870 ± 0.0188 | 0.0768 ± 0.0190 | 0.0991 ± 0.0369 | 0.1786 ± 0.2212 | 0.1201 ± 0.0654 | | `dummy_mean` | 0.1868 ± 0.0278 | 0.1868 ± 0.0278 | 0.1868 ± 0.0278 | 0.1868 ± 0.0278 | 0.1868 ± 0.0278 | | `dummy_median` | 0.1879 ± 0.0271 | 0.1879 ± 0.0271 | 0.1879 ± 0.0271 | 0.1879 ± 0.0271 | 0.1879 ± 0.0271 | ### Mean absolute percentage error, raw scale | Model | N=5 | N=10 | N=20 | N=50 | N=100 | |---|---|---|---|---|---| | `extra_trees` | 12.83 ± 4.26 | 12.30 ± 3.87 | 12.67 ± 4.08 | 10.81 ± 3.41 | 9.57 ± 3.35 | | `xgboost` | 14.94 ± 4.67 | 14.54 ± 4.55 | 14.06 ± 4.23 | 11.37 ± 3.20 | 10.18 ± 3.22 | | `random_forest` | 14.68 ± 4.37 | 14.63 ± 4.08 | 14.99 ± 4.48 | 12.01 ± 3.30 | 10.39 ± 2.94 | | `lightgbm` | 14.89 ± 4.54 | 13.89 ± 3.94 | 15.24 ± 4.33 | 11.53 ± 3.50 | 10.98 ± 3.12 | | `ridge` | 14.13 ± 2.77 | 13.45 ± 2.77 | 15.69 ± 4.65 | 462.65 ± 1384.36 | 18.35 ± 9.26 | | `lasso` | 13.91 ± 2.68 | 13.20 ± 2.51 | 17.04 ± 5.89 | 322.25 ± 1494.57 | 22.22 ± 17.92 | | `elastic_net` | 13.73 ± 2.64 | 13.22 ± 2.55 | 16.45 ± 5.18 | 335.73 ± 1218.39 | 23.58 ± 25.54 | | `huber` | 13.69 ± 2.96 | 11.84 ± 2.44 | 16.43 ± 7.55 | 1601.84 ± 4476.87 | 26.10 ± 40.86 | | `dummy_mean` | 36.95 ± 7.89 | 36.95 ± 7.89 | 36.95 ± 7.89 | 36.95 ± 7.89 | 36.95 ± 7.89 | | `dummy_median` | 37.94 ± 8.01 | 37.94 ± 8.01 | 37.94 ± 8.01 | 37.94 ± 8.01 | 37.94 ± 8.01 | ### The linear-model tail failure, and why it disqualifies them here Read the RMSE and MAPE tables together at **N=50**. Ridge reports a MAPE of 462.65% and Huber 1601.84% - many times worse than the dummy floor of 36.95%. Per-fold inspection shows this is not a poor average but a **catastrophic tail**: ridge's *median* fold at N=50 is a perfectly reasonable 13.95%, yet **9 of 50 folds exceed 100%**, the worst reaching 7136%. Extra trees over the same folds never exceeds 19.93% and has no fold above 100%. The mechanism is extrapolation. A linear model extended beyond its training range produces an unbounded prediction, and Phase 4 documented exactly the kind of feature that triggers it - b1c41's `dq_kurtosis` sits ~140 robust-z from the cohort median because its DeltaQ(V) contains one localized spike. Tree ensembles cannot extrapolate past the training range by construction, so they are structurally immune. **For this project the tail is what matters, not the median.** A QC system is accountable for its escape rate, and a model that is excellent 82% of the time and unbounded the rest cannot carry a risk guarantee. This is the concrete reason Phases 7-8 build on tree ensembles. It also settles the Phase 5 question precisely. The concern raised there was right *in this setting* - the full 57-feature matrix under cross-validation - and wrong for the curated 9-feature Severson reproduction, where elastic net was the best of four estimators. Both measurements stand; the scope of the claim is what needed correcting. ### Spearman rank correlation Reported because grading depends on **order**, not absolute accuracy: a model that ranks perfectly but is biased in level still grades perfectly once thresholds are calibrated. | Model | N=5 | N=10 | N=20 | N=50 | N=100 | |---|---|---|---|---|---| | `extra_trees` | 0.8957 ± 0.0400 | 0.8986 ± 0.0415 | 0.8967 ± 0.0385 | 0.9226 ± 0.0380 | 0.9436 ± 0.0251 | | `random_forest` | 0.8791 ± 0.0475 | 0.8749 ± 0.0443 | 0.8640 ± 0.0582 | 0.9121 ± 0.0399 | 0.9377 ± 0.0275 | | `xgboost` | 0.8815 ± 0.0427 | 0.8866 ± 0.0500 | 0.8716 ± 0.0647 | 0.9133 ± 0.0344 | 0.9342 ± 0.0361 | | `lightgbm` | 0.8778 ± 0.0390 | 0.8852 ± 0.0443 | 0.8678 ± 0.0644 | 0.9176 ± 0.0387 | 0.9271 ± 0.0313 | | `elastic_net` | 0.8863 ± 0.0435 | 0.8918 ± 0.0427 | 0.8796 ± 0.0455 | 0.8878 ± 0.0588 | 0.8824 ± 0.0626 | | `huber` | 0.8808 ± 0.0491 | 0.9087 ± 0.0335 | 0.8687 ± 0.0509 | 0.9096 ± 0.0358 | 0.8813 ± 0.0514 | | `ridge` | 0.8855 ± 0.0459 | 0.8940 ± 0.0446 | 0.8795 ± 0.0453 | 0.9042 ± 0.0385 | 0.8760 ± 0.0603 | | `lasso` | 0.8825 ± 0.0454 | 0.8911 ± 0.0455 | 0.8765 ± 0.0458 | 0.8731 ± 0.0669 | 0.8572 ± 0.0841 | | `dummy_mean` | n/a ± n/a | n/a ± n/a | n/a ± n/a | n/a ± n/a | n/a ± n/a | | `dummy_median` | n/a ± n/a | n/a ± n/a | n/a ± n/a | n/a ± n/a | n/a ± n/a | ### Escape rate The manufacturing metric, at a fixed reference operating point. This is what a plant is accountable for, and reporting only the statistical family would contradict the thesis of the project. | Model | N=5 | N=10 | N=20 | N=50 | N=100 | |---|---|---|---|---|---| | `extra_trees` | 0.0525 ± 0.0525 | 0.0486 ± 0.0481 | 0.0428 ± 0.0531 | 0.0480 ± 0.0530 | 0.0454 ± 0.0460 | | `xgboost` | 0.1316 ± 0.0987 | 0.1174 ± 0.0798 | 0.0965 ± 0.0762 | 0.0562 ± 0.0629 | 0.0514 ± 0.0563 | | `random_forest` | 0.1060 ± 0.0709 | 0.1016 ± 0.0708 | 0.0971 ± 0.0742 | 0.0849 ± 0.0774 | 0.0567 ± 0.0554 | | `lightgbm` | 0.1367 ± 0.0885 | 0.1046 ± 0.0676 | 0.1153 ± 0.0830 | 0.0658 ± 0.0682 | 0.0646 ± 0.0656 | | `huber` | 0.0781 ± 0.0743 | 0.0875 ± 0.0651 | 0.0913 ± 0.0774 | 0.0850 ± 0.0664 | 0.1101 ± 0.0791 | | `lasso` | 0.1066 ± 0.0798 | 0.1306 ± 0.0831 | 0.1681 ± 0.1200 | 0.2698 ± 0.1249 | 0.2170 ± 0.1102 | | `ridge` | 0.1161 ± 0.0750 | 0.1419 ± 0.0832 | 0.1465 ± 0.0846 | 0.1800 ± 0.0853 | 0.2288 ± 0.0963 | | `elastic_net` | 0.1057 ± 0.0764 | 0.1308 ± 0.0829 | 0.1668 ± 0.1179 | 0.2407 ± 0.1105 | 0.2498 ± 0.1164 | | `dummy_median` | 0.3466 ± 0.0888 | 0.3466 ± 0.0888 | 0.3466 ± 0.0888 | 0.3466 ± 0.0888 | 0.3466 ± 0.0888 | | `dummy_mean` | 0.3466 ± 0.0888 | 0.3466 ± 0.0888 | 0.3466 ± 0.0888 | 0.3466 ± 0.0888 | 0.3466 ± 0.0888 | ## 4. Grading route: ordinal regression vs direct classification The reviewed decision after Phase 3: grades come from **thresholding a predicted cycle life**, not from a three-class classifier. Phase 3 measured the realised balance as A = 11 (8.9%), B = 70 (56.5%), C = 43 (34.7%) - roughly **two grade-A cells per fold**. A direct classifier would estimate a boundary for a class it sees twice per fold. Ordinal treatment avoids that *structurally*: every cell informs one continuous target regardless of which side of a boundary it falls on. It also preserves what Phase 8 needs - the CONTINUE action requires a continuous predictive distribution to compute value of information, which a three-class posterior cannot supply. | Route | Metric | mean ± std | 95% CI | Caveat | |---|---|---|---|---| | **PRIMARY** `ordinal_from_regression` | escape_rate | 0.0789 ± 0.0596 | [0.0451, 0.1142] | | | **PRIMARY** `ordinal_from_regression` | overkill_rate | 0.0420 ± 0.0381 | [0.0192, 0.0638] | | | **PRIMARY** `ordinal_from_regression` | yield | 0.6737 ± 0.0884 | [0.6270, 0.7297] | | | **PRIMARY** `ordinal_from_regression` | recall_A | 0.9167 ± 0.1800 | [0.8000, 1.0000] | small-class: grade A has only 11 cells | | **PRIMARY** `ordinal_from_regression` | recall_B | 0.9462 ± 0.0418 | [0.9223, 0.9705] | | | **PRIMARY** `ordinal_from_regression` | recall_C | 0.9033 ± 0.1102 | [0.8350, 0.9650] | | | _secondary_ `direct_classification` | escape_rate | 0.0431 ± 0.0512 | [0.0139, 0.0745] | | | _secondary_ `direct_classification` | overkill_rate | 0.0675 ± 0.0700 | [0.0303, 0.1127] | | | _secondary_ `direct_classification` | yield | 0.6455 ± 0.1071 | [0.5840, 0.7090] | | | _secondary_ `direct_classification` | recall_A | 0.8333 ± 0.3600 | [0.6000, 1.0000] | small-class: grade A has only 11 cells | | _secondary_ `direct_classification` | recall_B | 0.9337 ± 0.0285 | [0.9185, 0.9524] | | | _secondary_ `direct_classification` | recall_C | 0.9481 ± 0.0730 | [0.9025, 0.9889] | | ## 4a. Stacked ensemble Out-of-fold predictions only, ridge meta-learner over elastic net / random forest / LightGBM (10 outer folds, N=100): | Metric | mean ± std | |---|---| | RMSE (log10) | 0.0623 ± 0.0217 | | MAPE (raw) | 10.18% ± 3.04 | | Spearman | 0.9408 ± 0.0283 | | Escape rate | 0.0536 ± 0.0441 | Mean meta-learner weights: random forest **+0.564**, LightGBM **+0.455**, elastic net **+0.146** (std 0.443 - unstable, consistent with the linear tail failure above). **Honest result: the ensemble does not beat the best single model.** Extra trees alone reaches RMSE 0.0580 ± 0.0240 at the same budget over 50 folds. With n = 124 and three base models whose errors are correlated, stacking has little disagreement to exploit and the meta-learner has very few rows to fit on. The ensemble is retained for Phase 8 comparison, not promoted as the headline. ## 5. Limitations - **Compact hyperparameter grids.** The full protocol is 50 outer fits per (model, budget), each wrapping an inner grid search. Grids are deliberately small, so a wider search might find better settings for any individual model. The comparison is between models under *equal* search budgets, which is what Phase 6 needs in order to choose a family to carry forward. - **The reproduction is 1.36x the published value**, not an exact match, for the reasons in section 1. - **Published comparison values** for the discharge and variance models are recorded from the source report; a reader verifying this work should check them against the paper directly. --- [← Feature engineering](02_feature_engineering.md) · [README](../README.md) · [Uncertainty and risk control →](04_uncertainty.md)