You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
| Mean absolute error |**0.71**| Average rating distance — most misses are off-by-one |
50
+
| Log loss | 1.25 | Probability quality |
51
+
| Expected calibration error | 0.091 | Confidence-vs-accuracy gap (see reliability diagram) |
48
52
49
-
Chance for a balanced 5-class problem is 0.20, so the model reaches roughly **2.4×** the baseline. This is a deliberately honest, moderate result: satisfaction is noisy by construction, and adjacent ratings are genuinely hard to separate — which is what a real ordinal satisfaction signal looks like. Metrics are written to [`reports/metrics/metrics.json`](reports/metrics/metrics.json) on every training run.
53
+
The single-split accuracy (0.483) and the cross-validated mean (0.429 ± 0.036) are reported together so the headline isn't cherry-picked. The high **quadratic-weighted kappa (0.73)** is the important number for an ordered target: the model rarely makes large errors — most mistakes are adjacent ratings. All values are written to [`reports/metrics/metrics.json`](reports/metrics/metrics.json) on every training run.
54
+
55
+
### Is ordinal regression better here?
56
+
57
+
The 1–5 target is ordered, so a natural question is whether a dedicated **ordinal** model beats flat multi-class. This is tested reproducibly in [`src/evaluate.py`](src/evaluate.py) (`compare_ordinal_vs_flat`), which fits a Frank & Hall ordinal wrapper ([`src/ordinal.py`](src/ordinal.py)) on the same split and writes [`reports/metrics/ordinal_comparison.json`](reports/metrics/ordinal_comparison.json):
| Frank & Hall ordinal RF | 0.397 | 1.01 | 0.640 |
63
+
64
+
The flat forest wins on every metric — it already respects the ordering via `class_weight="balanced"` and the SHAP-visible structure — so it stays the default. The ordinal model is kept as a documented, tested alternative rather than dropped, so the conclusion is checkable, not asserted.
65
+
66
+
### Calibration
67
+
68
+
`predict_proba` reliability is measured with a reliability diagram and Expected Calibration Error (ECE):
The RandomForest is moderately calibrated (ECE ≈ 0.09). Post-hoc **sigmoid (Platt) recalibration** reduces ECE to ≈ 0.06 but costs ~3 points of accuracy, so it is reported rather than applied by default; enable it downstream with `sklearn.calibration.CalibratedClassifierCV` if trustworthy probabilities matter more than exact-rating accuracy for your use case.
`models/satisfaction_pipeline.joblib` is a **build artifact**, not committed to git (it is `.gitignore`d). Because the whole pipeline is deterministic, you reproduce it exactly with `python -m src.train_model`. CI also trains it on every run and publishes it (plus `reports/`) as a downloadable **GitHub Actions artifact**. Run steps 1–3 above before launching the dashboard or scoring new sessions.
262
+
231
263
---
232
264
233
265
# **Quality Gate**
@@ -238,10 +270,12 @@ This repo ships a CI-enforced quality gate (see [`.github/workflows/ci.yml`](.gi
pytest # unit tests (heavy shap/streamlit tests skip cleanly)
273
+
pytest #20 unit tests (heavy shap/streamlit tests skip cleanly)
242
274
```
243
275
244
-
Tests cover time-feature engineering, the training pipeline, the stratified split, batch scoring round-trips, the documented data signal, and the version-robust SHAP shape helpers.
276
+
CI runs two jobs: a **quality-gate** job (the four commands above, on Python 3.10 and 3.12) and a **pipeline** job that executes the full `generate_data → data_prep → train_model → evaluate → explain` chain end-to-end and uploads the trained model and reports as artifacts — so the training path itself is exercised in CI, not just the unit tests.
277
+
278
+
The 20 tests cover time-feature engineering, the training pipeline, the stratified split, batch scoring round-trips, the documented data signal, the version-robust SHAP shape helpers, the ordinal (Frank & Hall) classifier, and the calibration/ordinal metric helpers (ECE, MAE, QWK).
* Synthetic data: results reflect a *designed* signal, not real users. Real satisfaction would be noisier and confounded.
277
-
* Moderate accuracy (~0.48) is intentional — the noise term keeps the task realistic.
278
-
*Treated as flat multi-class rather than ordinal.
310
+
* Synthetic data: results reflect a *designed* signal, not real users. Real satisfaction would be noisier and confounded — this is the main bound on the findings.
311
+
* Moderate accuracy (~0.48, CV 0.43) is intentional — the noise term keeps the task realistic; quadratic-weighted kappa (0.73) shows errors are mostly adjacent.
312
+
*The target is ordered; a Frank & Hall ordinal model was evaluated and did **not** beat the flat forest here (see Results), so flat multi-class is retained.
279
313
* No per-user personalization or temporal modeling.
280
314
281
315
---
282
316
283
317
# **Future Work**
284
318
285
-
* Reframe as **ordinal regression** to exploit the natural ordering.
286
-
* Add calibration plots and probability calibration.
287
-
* Add temporal / sequence features per user.
288
-
* Deploy the scorer as a FastAPI microservice.
319
+
* Validate on a **real** (non-synthetic) dataset — the single biggest open item.
320
+
* Add temporal / sequence features per user and per-user personalization.
321
+
* Deploy the scorer as a FastAPI microservice with the CI-built model artifact.
0 commit comments