Early ML work · Cabify Spain · Nov 2025 to Mar 2026, extended in May
Demand & KPI Forecasting Engine
Before most of the automation work, I spent my first months building models in Colab for the Customer Operations team: forecasts of support volume and ride requests, quarterly KPI targets, a shift planner for a contact centre and a first computer-vision prototype. The team used them in its weekly reviews. This page is what they did and what I learned.
1. Daily support volume
The team planned staff against an official daily forecast of contact volume per audience (riders, drivers, business clients) that was often far off. I went through about thirty notebook versions in three months. The main steps:
- NeuralProphet with yearly and weekly seasonality, then with Spanish public holidays and a hand-kept events sheet, then an ensemble of 20 models with different seeds.
- Back to Prophet, which was easier to explain to the team and much easier to tune.
- Tuning. First an adaptive random search over the three prior scales, then a full grid of 2,160 configurations (changepoint, seasonality and holiday priors, additive or multiplicative, changepoint range), with the champion chosen by MAE on the last 28 days, which the model never saw.
- Bias correction on recent errors, then the forecast is written to a frozen log before the actuals arrive.
Over 15 December to 14 February the model's error was 14.4% against 34.9% for the official forecast. Weekly accuracy for the rider series reached 94.1% (one minus weighted MAPE, first week of February). Business-client volume was much harder: 74.5% accuracy in the same kind of check, because the daily counts are small and lumpy.
2. Ride requests by city
The same approach, run as a batch with one events tab per city and a 14-day bias correction applied only when the gap is between 1% and 25% (a larger gap usually means something changed that a factor would hide). In May I rebuilt it as a local pipeline with walk-forward validation: LightGBM with quantile loss on lags, rolling statistics, calendar features and event leads and lags; Prophet with Spanish holidays; and SARIMAX(1,1,1)(1,1,1,7). The ensemble weights each model by the inverse of its MAPE, gives a P10 to P90 band, and anomalies are flagged with a robust z-score on residuals (median absolute deviation above 3.5), marked as explained when an event matches.
In Madrid the single SARIMAX model had the lowest error (6.6% MAPE) and the ensemble came third at 7.5%. The ensemble stayed because it is more stable across cities, but it is not the best everywhere.
3. Quarterly KPI targets
Targets for about 140 critical-process KPIs across four audiences were set by hand every quarter. One notebook now fits a Prophet model per KPI, switches to logistic growth with a cap and floor when the KPI is a bounded percentage, and writes three scenarios (probable, optimistic, pessimistic, from the forecast and its interval) into a formatted sheet per audience. The same engine produced monthly targets for the mystery-shopper programme.
4. Contact-centre staffing
Erlang C, written with log-gamma terms so it stays numerically stable, gives the minimum agents per interval for the service level. OR-Tools CP-SAT then builds the roster: shifts of 4 to 10 hours starting at any hour, one shift per day, at most 44 hours and five days a week, at least 12 hours between shifts and a weekly rest. Uncovered demand is a slack variable with a very high cost, overtime a lower one, so the solver prefers any legal roster over an infeasible one. Days off and blocked slots (training, appointments) can be entered before solving.
5. First computer-vision prototype
For the vehicle audit I built a dataset of 2,156 photos in 10 vinyl classes and trained a CNN from scratch in Keras: four convolution blocks, 256 px input, augmentation without horizontal flips so logos are never mirrored. Plates were read with YOLOv8 to find the car and EasyOCR with several contrast and threshold variants, checked against the Spanish plate format. A third notebook aligned each photo to the reference design with SIFT features and a RANSAC homography to estimate how much of the design was covered.
It ran in batch over real submissions, but it confused similar designs, and I did not keep the evaluation results, so there is no number I can quote. That, plus the need to describe damage in words, is why the production system uses a vision LLM with a reference collage instead. A later ResNet18 cascade with a confidence gate, meant to handle the easy cases locally and send the rest to the LLM, got as far as a baseline. The full story is in the hybrid vision engine.
What I would change now
- The notebooks were long single cells with interactive menus that only I could run. The May rebuild is a package with a config file, walk-forward folds and a report per city, which is how all of it should have started.
- Every model should have shipped with its backtest next to it. The volume forecast had one because of the frozen log; the vision prototype did not, and it cost me the ability to say how good it was.
No volumes are shown: accuracy figures only. The contact-centre provider and internal datasets are not named.