← Back to selected work

15 / Machine learning / Prediction

Apprentice Chef

Two prediction studies on a meal-kit business, judged by how far each model's test score falls below its training score, and by whether the result survives a re-run.

Decision signalML / MODEL 15
0.047 committed0.179 re-run0.004 logistic00.100.20
0.047 → 0.179Final cross-sell blend's train–test AUC gap: committed run vs re-run
Model chart / cross-sell, train → test AUC1,946 customers
Cross-sell classifier: train versus test AUC by modelLogistic regression: 0.729 train, 0.726 test. KNN: 0.758, 0.616. Random forest: 0.897, 0.685. First-draft GBM: 0.896, 0.687. Neural network: 1.000, 0.591. Final blend as committed: 0.769, 0.721. The same blend on a re-run: 0.890, 0.712. 0.60 0.70 0.80 0.90 1.00 Logistic regression gap 0.004 KNN gap 0.142 Random forest gap 0.212 GBM, first draft gap 0.208 Neural network gap 0.409 Final blend (committed) gap 0.047 Final blend (re-run) gap 0.179
logisticfinal blend○ train · ● test · coral ring = re-run
AUC · stratified 75/25 split, seed 702 · the length of each line is the train–test gap
0.726logistic test AUC · gap 0.004
0.777revenue blend test R² · MAPE 15.6%
80% vs 42%subscribe rate, professional vs consumer email domains

Which customers spend more, and who signs up?

Apprentice Chef is a meal-kit subscription business. The dataset has 1,946 customers and 28 raw features. Part 1 predicts revenue (a regression), and Part 2 predicts who subscribes to the Halfway There cross-sell promotion (a classification).

Both parts are scored on held-out data, and on how far the test score falls below the training score. A model that fits its own training set but not new customers is not a model to act on.

Many learners, one weighted blend each.

Revenue. Revenue is log-transformed and the features standardised before a 75/25 split. KNN, random forest, gradient boosting, ridge, lasso, elastic net and a neural network are compared. The final model is a weighted blend of a tuned GBM (0.6), KNN (0.2) and Lasso (0.2).

Cross-sell. Email addresses are split into domains and bucketed as professional, personal or junk. The 75/25 split is stratified on the 68/32 class balance, and models are scored on AUC because predicting that everyone subscribes already gets about 68% accuracy. The final blend is logistic regression (0.6), GBM (0.3) and KNN (0.1) on predicted probabilities.

The email address is the funnel.

The promotion is sent by email, and conversion depends on which kind of address the customer gave.

Cross-sell subscription rate by email domain group
Domain groupCustomersSubscribed
Professional69680.0%
Personal86169.9%
Junk38941.6%

Two behavioural signals point the same way. Subscribers cancel before noon more often than non-subscribers (a mean of about 1.58 versus 1.04), which is a penalty-free action used by customers who actively manage their orders. Customers registered with a mobile number subscribe at about 70%, against 55% for those without one.

On the revenue side, median revenue peaks at a median meal rating of 4 rather than 5, a central-tendency bias in self-reported ratings, and it rises with customer-service contacts up to about 9 to 10 before falling.

The flexible models memorise. The simple one doesn't.

In the cross-sell study the logistic regression scores 0.729 on training data and 0.726 on test data. The flexible models lose 0.14 to 0.41 AUC out of sample, and the neural network reaches a perfect 1.000 training AUC and 0.591 on test.

The plain logistic regression (0.726 test AUC, gap 0.004) scores slightly above the final blend (0.721 as committed, 0.712 on a re-run). On this dataset the blend adds no out-of-sample lift over its best single member.
Model chart / revenue, train → test R²log revenue
Revenue regression: train versus test R-squared by modelLasso: 0.785 train, 0.701 test. Final blend: 0.916, 0.777. Tuned GBM: 0.957, 0.784. Neural network: 0.967, 0.430 as committed and 0.880, 0.403 on a re-run. 0.40 0.50 0.60 0.70 0.80 0.90 1.00 Lasso gap 0.085 Final blend gap 0.140 Tuned GBM gap 0.173 Neural network (committed) gap 0.537 Neural network (re-run) gap 0.477
final blend○ train · ● test · coral ring = re-run
R² on log revenue · 75/25 split, seed 702 · the tuned GBM has the best test R², the blend the smaller gap

In the revenue study the tuned GBM alone has the highest test R² (0.784), but also a gap of 0.173. Blending it with Lasso and KNN gives 0.777 with a gap of 0.140. On a re-run the blend has a MAPE of 15.6%, and 75.6% of predictions land within 20% of actual revenue.

Some results hold. Some depend on the tuner.

Both notebooks were re-run end to end with the same data and split.

Committed notebook outputs vs a re-run
ResultCommittedRe-run
Revenue blend, train / test R²0.916 / 0.7770.916 / 0.777
Revenue tuned GBM, train / test R²0.957 / 0.7840.957 / 0.784
Revenue neural network, train / test R²0.967 / 0.4300.880 / 0.403
Revenue “final” R² / MAPE0.763 / 16.3%cell fails
Cross-sell logistic, test AUC / gap0.7258 / 0.00350.7266 / 0.0016
Cross-sell tuned GBM, train / test AUC0.817 / 0.7120.994 / 0.674
Cross-sell final blend, test AUC0.7210.712
Cross-sell final blend, gap0.0470.179

The Optuna studies are created without a seed, and the cross-sell GBM is fit with study.best_params. Its random_state is fixed, but the search is not, so the tuned GBM, and the blend that gives it 30% of the weight, changes from run to run. The revenue GBM, whose parameters are hard-coded, reproduces exactly. The logistic regression also has its regularisation strength searched, but it lands in nearly the same place (0.7258 vs 0.7266 test AUC).

The revenue “final” R² of 0.763 and MAPE of 16.3% come from a cell that is not in the committed notebook. Re-running the reporting cell fails with an undefined variable (final_test_pred).

Seed the search, benchmark the blend.

Seed the Optuna studies, or hard-code the chosen parameters, so every reported number can be regenerated. Choose blend weights by cross-validation instead of by comparing test scores, and report cross-validated AUC and R² with a spread rather than a single split. Keep the logistic regression as the benchmark any blend has to beat, and restore or remove the missing revenue cell so the final figures can be traced.