Spaces:
Running on Zero
Running on Zero
File size: 17,318 Bytes
749bffa | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 | # 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)
|