Contingut
- El context de negoci: per què importa predir el churn
- El dataset:
clients_churn.csv - El notebook inicial, complet i explicat
- Diagnòstic: què està malament en aquest notebook
- L'stack del curs: etapa → eina → mòdul
- Full de ruta de la resta del curs
El context de negoci: per què importa predir el churn
CineClick és una plataforma de streaming per subscripció amb plans mensuals (bàsic, estàndard i premium). Com tot negoci de subscripció, la seva salut depèn d'una aritmètica senzilla i despietada:
- Adquirir un client nou és car: campanyes, descomptes de benvinguda, mesos gratis. Al sector s'assumeix que captar un subscriptor costa diverses vegades més que retenir-ne un d'existent (una regla habitual diu que entre 5 i 7 vegades més, tot i que el múltiple exacte depèn del negoci).
- Retenir és barat... si saps a qui retenir: una trucada, un descompte puntual o una recomanació de contingut poden ser suficients. Però oferir descomptes a tots els clients destrueix marge; la clau és adreçar la retenció a qui realment està en risc.
- El churn s'acumula: un 5% de baixes mensuals sembla poc, però sostingut vol dir perdre prop de la meitat de la base de clients en un any si no es compensa amb adquisició.
D'aquí ve el projecte: un model que, per a cada subscriptor actiu, estimi la probabilitat que es doni de baixa properament, perquè l'equip de retenció concentri els seus recursos en els clients de més risc. La mètrica de negoci és clara (baixes evitades i ingressos retinguts davant del cost de les campanyes), i la de ML la discutirem críticament més avall — avanç: el notebook actual fa servir l'equivocada.
Totes les dades del projecte són fictícies i generades sintèticament: representen patrons plausibles d'un negoci de streaming, però cap client real. És la pràctica correcta per a formació i per a desenvolupament: no facis servir mai dades reals de persones on no toca.
El dataset: clients_churn.csv
El data engineer de CineClick genera periòdicament un extracte amb una fila per subscriptor i aquestes columnes:
| Columna | Tipus | Descripció | Valors d'exemple |
|---|---|---|---|
id_client |
enter | Identificador únic del subscriptor (sense significat predictiu) | 10482 |
antiguitat_mesos |
enter | Mesos des de l'alta | 1, 8, 36 |
hores_setmana |
decimal | Mitjana d'hores de visualització setmanals de l'últim mes | 0.5, 6.2, 21.0 |
tiquets_suport |
enter | Tiquets de suport oberts en els últims 3 mesos | 0, 1, 4 |
pla |
categòrica | Tipus de pla contractat | basic, estandard, premium |
metode_pagament |
categòrica | Mètode de pagament de la subscripció | targeta, domiciliacio, paypal |
descompte_actiu |
booleà (0/1) | Si té actualment algun descompte promocional | 0, 1 |
abandonament |
booleà (0/1) | Etiqueta: 1 si es va donar de baixa en els 30 dies següents a l'extracte | 0, 1 |
Dos trets del dataset que seran importants durant tot el curs:
- Les classes estan desbalancejades: només al voltant del 15% de les files tenen
abandonament = 1. És el normal en churn (per sort per a CineClick, la majoria dels clients es queda), i té conseqüències directes sobre quines mètriques serveixen i quines enganyen. - Barreja de tipus: numèriques (
antiguitat_mesos,hores_setmana,tiquets_suport), categòriques (pla,metode_pagament) i binàries (descompte_actiu). Les categòriques necessiten codificació abans d'entrenar.
El notebook inicial, complet i explicat
Aquest és l'estat de l'art a CineClick avui: un únic notebook, churn_final_v3_DEFINITIU.ipynb (el nom ja explica una història), al portàtil de la data scientist. El seu contingut, bolcat com un sol bloc, és aquest:
# churn_final_v3_DEFINITIU.ipynb
import pandas as pd
import pickle
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
# Carregar les dades
df = pd.read_csv("C:/Users/laura/Desktop/datos/clients_churn.csv")
# Neteja: fora nuls i algun negatiu estrany que venia a les hores
df = df.dropna()
df = df[df["hores_setmana"] >= 0]
# Treure l'id, que no aporta res
df = df.drop(columns=["id_client"])
# Passar les categoriques a columnes 0/1
df = pd.get_dummies(df, columns=["pla", "metode_pagament"])
# Separar features i etiqueta
X = df.drop(columns=["abandonament"])
y = df["abandonament"]
# Partir en train i test
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
# Entrenar el model
model = RandomForestClassifier(n_estimators=100)
model.fit(X_train, y_train)
# Avaluar
prediccions = model.predict(X_test)
print("Accuracy:", accuracy_score(y_test, prediccions))
# Ha sortit 0.87, millor que la v2!
# Desar el model
with open("C:/Users/laura/Desktop/modelos/model_churn.pkl", "wb") as f:
pickle.dump(model, f)Expliquem-lo pas a pas, perquè com a codi d'exploració és perfectament raonable i convé entendre què fa cada part:
- Càrrega (
pd.read_csv): llegeix el CSV des d'una ruta de l'escriptori de Laura cap a un DataFrame de pandas — l'estructura tabular amb què treballarem sempre. - Neteja (
dropna, filtre de negatius): elimina les files amb valors nuls i les que tenen hores de visualització negatives (un error conegut del sistema de logs). Decisions defensables, però preses sense registrar quantes files es perden ni per què. - Eliminació de l'id (
drop): correcte —id_clientés un identificador arbitrari; deixar-lo permetria al model "memoritzar" clients en lloc d'aprendre patrons. - One-hot encoding (
pd.get_dummies): converteix cada categòrica en columnes binàries.plaes converteix enpla_basic,pla_estandard,pla_premiumamb valors 0/1, i anàlogamentmetode_pagament. És necessari perquè els algorismes de scikit-learn requereixen entrades numèriques. - Separació X/y:
Xsón les features (totes les columnes menys l'etiqueta) iyl'etiquetaabandonament. train_test_split(X, y, test_size=0.2): reserva el 20% de les files per avaluar el model amb dades que no ha vist durant l'entrenament. Fixa't en el que falta entre els parèntesis; hi tornarem de seguida.RandomForestClassifier(n_estimators=100): un bosc aleatori de 100 arbres de decisió. Elecció raonable per a dades tabulars: robust, admet barreja de tipus de features i funciona decentment gairebé sense ajust.model.fit(...): l'entrenament pròpiament dit.- Avaluació amb
accuracy_score: percentatge de prediccions correctes sobre el conjunt de test. El comentari# Ha sortit 0.87és literalment tot el registre experimental que existeix del projecte. pickle.dump(...): serialitza el model entrenat a un fitxer binari.pkla l'escriptori, que és el que Laura enviaria per xat si algú li demanés "el model".
Insistim-hi: aquest notebook no és obra d'una mala professional. És el resultat natural de la fase d'exploració, i com a exploració va complir la seva missió: va demostrar que hi ha senyal a les dades. El problema és que CineClick el vol fer servir en producció tal qual, i per a això acumula tots els pecats que ara anomenarem.
Diagnòstic: què està malament en aquest notebook
Passem la llista amb la mirada MLOps de les lliçons anteriors. Cada problema és concret i té una conseqüència concreta:
| # | Problema | On es veu | Conseqüència |
|---|---|---|---|
| 1 | Rutes absolutes i personals | C:/Users/laura/Desktop/... en càrrega i desat |
El codi només funciona al portàtil de Laura. En qualsevol altra màquina (o servidor) falla a la primera línia útil. |
| 2 | Sense llavor aleatòria | train_test_split sense random_state; RandomForestClassifier sense random_state |
Cada execució parteix les dades de manera diferent i entrena arbres diferents: el "0.87" és irrepetible. Impossible saber si un canvi va millorar el model o va ser atzar. |
| 3 | Dades sense versionar | El CSV de l'escriptori se sobreescriu amb cada extracte | No es pot reproduir el model actual ni comparar entrenaments: el dataset de la "v3" ja no existeix. |
| 4 | Model sense versionar ni metadades | model_churn.pkl desat a mà, trepitjant l'anterior |
Ningú no sap quin codi, dades ni paràmetres van produir el .pkl que circula. Les "versions" viuen al nom del notebook. |
| 5 | Mètrica única i enganyosa | Només accuracy amb ~15% de positius |
Un model inútil que predigui sempre "no abandona" ja obté ~0.85 d'accuracy. El 0.87 celebrat podria estar detectant poquíssims abandonaments reals — que és l'únic que li importa a retenció. |
| 6 | Sense tests de cap tipus | Tot el notebook | Res no comprova que la neteja fa el que s'espera, que l'esquema del CSV és el previst o que el model supera un mínim. Qualsevol canvi es valida "a ull". |
| 7 | Sense registre d'experiments | El comentari # Ha sortit 0.87, millor que la v2! |
La història del projecte viu en comentaris i memòria. Què era la v2? Amb quines dades? Ningú no ho pot respondre. |
| 8 | Dependències implícites | Cap fitxer no declara versions de pandas/scikit-learn | Un altre company amb altres versions pot obtenir resultats diferents o errors. "A la meva màquina funciona" garantit. |
| 9 | Preprocessament no reutilitzable | get_dummies i neteja inline sobre el DataFrame |
Per predir sobre clients nous caldrà reimplementar la mateixa transformació al servei; qualsevol divergència (una categoria absent, columnes en un altre ordre) degradarà el model en silenci. |
| 10 | Procés 100% manual | Tot | Reentrenar = localitzar la Laura, el seu portàtil i la seva memòria. Nivell 0 de manual. |
Val la pena aturar-se en el problema 5, perquè és l'únic que enganya fins i tot amb el notebook al davant. Amb classes desbalancejades (85% "no abandona"), l'accuracy és una mètrica gairebé buida: mesura sobretot allò fàcil (encertar els que es queden) i ignora allò valuós (trobar els que se'n van). Les mètriques adequades per a aquest problema —precision i recall sobre la classe positiva, i les seves derivades— apareixeran quan muntem l'avaluació seriosa del pipeline al mòdul 2 i les comparacions d'experiments al mòdul 3. De moment, queda't amb la sospita: aquest 0.87 no diu el que Laura creu que diu.
L'stack del curs: etapa → eina → mòdul
Cada problema del diagnòstic té la seva resposta en una etapa del cicle de vida, i cada etapa la seva eina en aquest curs. Totes són de codi obert o tenen capa gratuïta, i totes són estàndards de facto o candidates serioses a ser-ho — però recorda la lliçó 01-01: l'important és la pràctica que encarnen, no la marca.
| Etapa del cicle | Eina | Què resol del diagnòstic | Mòdul |
|---|---|---|---|
| Versionament de codi | Git | Base de tot; els problemes 1 i 10 comencen a resoldre's aquí | Transversal (s'assumeix conegut) |
| Estructura i entorn reproduïble | Empaquetament Python + gestió de dependències | Problemes 1, 8 i 9 | Mòdul 2 |
| Versionament de dades | DVC | Problema 3 (i enllaça dades amb codi) | Mòdul 2 |
| Pipeline d'entrenament | DVC pipelines | Problemes 2, 9 i 10 (procés repetible) | Mòdul 2 |
| Tracking d'experiments | MLflow | Problemes 5 (mètriques múltiples) i 7 | Mòdul 3 |
| Registre de models | MLflow Model Registry | Problema 4 | Mòdul 3 |
| Servei de predicció | FastAPI | Portar el model a qui el necessita | Mòdul 4 |
| Empaquetament i desplegament | Docker (+ Kubernetes/serverless) | Problemes 1 i 8 en producció | Mòdul 4 |
| CI/CD | GitHub Actions | Problema 6 (tests) i automatització del desplegament | Mòdul 5 |
| Orquestració de pipelines | Prefect | Executar i programar els pipelines de manera fiable | Mòdul 5 |
| Monitoratge i deriva | Evidently (+ mètriques de servei) | Vigilar el model viu; tancar el cicle amb el reentrenament | Mòdul 6 |
Un avís sobre expectatives: aquest stack és una combinació coherent i molt utilitzada, no la veritat revelada. A la teva empresa potser es fa servir un altre orquestrador o una altra plataforma de tracking; els conceptes que aprendràs es transfereixen gairebé un a un, perquè totes les eines de cada categoria resolen el mateix problema del cicle de vida.
Full de ruta de la resta del curs
Així es desenvoluparà la transformació de CineClick, mòdul a mòdul:
flowchart TD
M1["Mòdul 1 (aquí)<br/>Fonaments: el notebook<br/>i els seus problemes"] --> M2["Mòdul 2<br/>Codi reproduïble:<br/>projecte estructurat, entorns,<br/>DVC, pipeline d'entrenament"]
M2 --> M3["Mòdul 3<br/>Experiments i registre:<br/>MLflow tracking i model registry,<br/>feature stores"]
M3 --> M4["Mòdul 4<br/>Servir en producció:<br/>FastAPI, Docker,<br/>Kubernetes, optimització"]
M4 --> M5["Mòdul 5<br/>Automatització:<br/>CI/CD amb GitHub Actions,<br/>Prefect, estratègies de release"]
M5 --> M6["Mòdul 6<br/>Operació:<br/>monitoratge, deriva, reentrenament,<br/>governança i projecte final"]- Mòdul 2 — Del notebook al codi reproduïble: refactoritzarem aquest notebook en un projecte Python estructurat, amb entorn reproduïble, el dataset sota control de DVC i un pipeline d'entrenament que qualsevol pot executar. En acabar-lo, els problemes 1, 2, 3, 8, 9 i 10 estaran resolts o encarrilats.
- Mòdul 3 — Experiments i registre: cada entrenament quedarà registrat a MLflow amb els seus paràmetres i mètriques (les correctes, no només l'accuracy), i els models es versionaran i promocionaran al model registry. Adeu als problemes 4, 5 i 7.
- Mòdul 4 — Servir en producció: el model deixarà el
.pklde l'escriptori per convertir-se en un servei FastAPI empaquetat amb Docker i desplegat de manera escalable. - Mòdul 5 — Automatització: tests de codi, dades i model en CI (problema 6), desplegament automatitzat i orquestració dels pipelines — el salt del nivell 1 al nivell 2 de maduresa.
- Mòdul 6 — Operació: monitorarem el servei i el model, detectarem deriva, automatitzarem el reentrenament amb seny (recordant els bucles de realimentació de la lliçó 01-02) i tancarem amb governança, documentació i el projecte final integrador.
Errors Comuns i Consells
- Error: menysprear el notebook de la Laura. El notebook va demostrar que el problema té solució amb les dades disponibles — això és la part difícil i irreemplaçable. MLOps no substitueix l'exploració: la industrialitza. Tracta el codi exploratori amb respecte i la seva autora com a aliada, no com a culpable.
- Error: refiar-se de l'accuracy amb classes desbalancejades. És l'error de modelatge més comú en churn, frau o detecció de fallades. Abans de celebrar una mètrica, pregunta't sempre: què obtindria un model trivial que prediu sempre la classe majoritària?
- Error: "arreglar" el notebook afegint-hi cel·les. La temptació és apedaçar: una cel·la que fixa la llavor, una altra que copia el CSV amb la data al nom... El notebook continuaria sent un notebook, amb el seu estat ocult i la seva execució fora d'ordre. La solució del mòdul 2 és estructural, no cosmètica.
- Error: voler aplicar les onze eines de la taula la setmana que ve. L'stack s'adopta en l'ordre del curs per una raó: cada peça es recolza en l'anterior (no pots fer CI d'un pipeline que no existeix). A la teva feina real, segueix el mateix ordre incremental.
- Consell: guarda una còpia d'aquest notebook tal com està. Al final del curs, comparar-lo amb la plataforma final serà la millor mesura del que has après — i un exercici d'humilitat útil: gairebé tots tenim un
_final_v3_DEFINITIUen algun calaix.
Exercicis
Exercici 1
Sense executar res, raona numèricament: el dataset té un 15% de clients amb abandonament = 1. (1) Quina accuracy obtindria un "model" que prediu sempre abandonament = 0? (2) Quants dels clients que abandonaran detectaria aquest model? (3) Què et diu això sobre el 0.87 del notebook i sobre què hauria de preguntar-li l'equip de retenció a la Laura en lloc de "quina accuracy té?"?
Exercici 2
Executes el notebook de la Laura dues vegades seguides, sense canviar ni una línia ni el CSV, i obtens accuracy 0.87 la primera vegada i 0.86 la segona. (1) Assenyala les dues línies exactes del codi responsables d'aquesta variabilitat i explica el mecanisme de cadascuna. (2) Explica per què això és greu més enllà de la molèstia: quina pregunta fonamental del treball experimental resulta impossible de respondre?
Exercici 3
Relaciona cada problema del diagnòstic amb el seu remei: per als problemes 3, 5, 6 i 9 de la taula de diagnòstic, indica quina eina de l'stack (i quin mòdul del curs) l'aborda principalment, i escriu en una frase quina pràctica (més enllà de l'eina) és la que realment el resol.
Solucions
Solució 1:
- Predient sempre "no abandona" encertes tots els negatius (85%) i falles tots els positius (15%): accuracy = 0.85.
- Zero. Aquest model no detecta ni un sol abandonament: és perfectament inútil per a retenció malgrat el seu 0.85.
- El 0.87 del notebook és només dos punts per sobre del model trivial, així que la major part d'aquesta accuracy és "encertar allò fàcil". Podria estar detectant molts abandonaments o gairebé cap: l'accuracy no ho distingeix. Retenció hauria de preguntar: "dels clients que realment abandonen, quina fracció detecta el model?" (recall de la classe positiva) i "dels que el model marca com a risc, quants abandonen de debò?" (precision) — perquè aquestes dues xifres determinen, respectivament, quantes baixes pot evitar la campanya i quants diners es malbaraten en falsos positius.
Solució 2:
- Les dues línies:
train_test_split(X, y, test_size=0.2)— senserandom_state, cada execució remena les dades amb una llavor diferent, així que train i test contenen files diferents cada vegada; iRandomForestClassifier(n_estimators=100)— senserandom_state, el mostreig bootstrap de cada arbre i la selecció aleatòria de features a cada divisió canvien entre execucions, produint boscos diferents fins i tot amb les mateixes dades d'entrenament. - La gravetat: resulta impossible respondre la pregunta "aquest canvi va millorar el model?". Si demà la Laura afegeix una feature nova i l'accuracy passa de 0.86 a 0.875, no hi ha manera de saber si la millora es deu a la feature o al mateix soroll aleatori que ja produeix oscil·lacions de ±0.01 sense tocar res. Sense reproduïbilitat no hi ha experimentació vàlida: tot el procés de millora del model queda edificat sobre comparacions sense valor.
Solució 3:
- Problema 3 (dades sense versionar) → DVC, mòdul 2. La pràctica real: cada dataset d'entrenament queda immutablement identificat i enllaçat a la versió del codi que el va utilitzar, de manera que qualsevol model passat es pugui reconstruir.
- Problema 5 (mètrica única enganyosa) → MLflow, mòdul 3 (amb les mètriques correctes introduïdes ja al pipeline del mòdul 2). La pràctica real: definir abans d'entrenar un conjunt de mètriques alineat amb el negoci i registrar-les totes a cada experiment, per comparar models sobre allò que importa.
- Problema 6 (sense tests) → GitHub Actions, mòdul 5. La pràctica real: codificar les expectatives sobre codi, dades i model com a comprovacions automàtiques que s'executen a cada canvi — el CI és només el motor que les executa.
- Problema 9 (preprocessament no reutilitzable) → estructura de projecte Python, mòdul 2 (i la seva reutilització al servei, mòdul 4). La pràctica real: un únic mòdul de preprocessament, testat i versionat, compartit per entrenament i inferència, perquè sigui impossible per construcció que producció transformi les dades d'una altra manera.
Conclusió
Ja tenim el punt de partida complet i sense maquillatge: un negoci amb un problema real i quantificable (el churn s'acumula, i retenir és molt més barat que adquirir), un dataset fictici però versemblant amb les seves features numèriques i categòriques i la seva etiqueta desbalancejada, i un notebook monolític que demostra que hi ha senyal a les dades però que acumula deu problemes concrets —rutes personals, atzar sense control, dades i model sense versionar, una accuracy que no diu el que sembla, zero tests i un procés que viu al portàtil d'una persona—. També tenim el pla: un stack d'eines (Git, DVC, MLflow, FastAPI, Docker, GitHub Actions, Prefect, Evidently) mapat etapa a etapa sobre el cicle de vida, i un full de ruta que recorre aquest mapa mòdul a mòdul. Amb això tanquem els fonaments. Al mòdul 2 ens hi posem de valent: agafarem aquest notebook exactament tal com està i el convertirem, pas a pas, en un projecte Python estructurat i reproduïble — la primera pedra, i la més important, del camí de CineClick cap a la producció.
Curs de MLOps
Mòdul 1: Fonaments de MLOps
- Què és MLOps i per què els models moren al notebook
- El cicle de vida d'un model de ML en producció
- Nivells de maduresa MLOps i rols de l'equip
- El projecte del curs: del notebook a producció
Mòdul 2: Del notebook al codi reproduïble
- Estructura d'un projecte de ML: del notebook al paquet
- Entorns reproduïbles i gestió de dependències
- Versionat de dades amb DVC
- Pipelines d'entrenament reproduïbles
Mòdul 3: Experiments i registre de models
- Tracking d'experiments amb MLflow
- Model registry: versionar i promocionar models
- Feature stores: quan i per a què
Mòdul 4: Servir models en producció
- Patrons de desplegament: batch, online i streaming
- Un servei de predicció amb FastAPI
- Empaquetatge amb Docker
- Escalat i desplegament: Kubernetes i serverless
- Optimització de la inferència: latència i cost
Mòdul 5: Automatització: CI/CD i orquestració
- CI per a ML: tests de codi, dades i models
- CD: automatitzar el desplegament del model
- Orquestració de pipelines de ML
- Estratègies de release: shadow, canary i A/B
