A la lliçó 04-02 vam descobrir que la regressió logística no prediu classes, sinó probabilitats (predict_proba), i que el llindar de 0.5 amb què es converteixen en "churn / no churn" és una decisió nostra — una palanca de negoci. A la lliçó anterior vam comparar models amb aquell llindar clavat a 0.5; en aquesta l'alliberem. La corba ROC avalua un classificador en tots els llindars alhora, i l'AUC resumeix aquella corba en un sol número que permet comparar models independentment del llindar. Després farem el camí invers: triar el llindar operatiu concret que convé a la campanya de retenció de MercaFresh segons els seus costos i el seu pressupost. Tancarem amb la corba precision-recall, l'alternativa preferible sota desequilibri extrem, i amb les limitacions de l'AUC.
Contingut
- El llindar de decisió com a paràmetre lliure
- TPR i FPR: les dues coordenades de la ROC
- Construcció de la corba ROC punt a punt (a mà)
- AUC: interpretació probabilística
- ROC i AUC en scikit-learn: comparació de models
- Triar el llindar operatiu segons costos de negoci
- La corba precision-recall com a alternativa
- Limitacions de l'AUC
El llindar de decisió com a paràmetre lliure
Recordem el mecanisme de 04-02:
probs = model.predict_proba(X_prova)[:, 1] # P(churn) de cada client
y_pred_05 = (probs >= 0.5).astype(int) # llindar per defecte
y_pred_03 = (probs >= 0.3).astype(int) # llindar mes "paranoic"Abaixar el llindar a 0.3 fa que el model assenyali com a churn més clients: caça més fugues reals (puja el recall) a costa de més falses alarmes (baixa la precision). Apujar-lo a 0.7 fa el contrari. El mateix model entrenat genera infinits classificadors diferents, un per llindar. Avaluar només el de 0.5 és mirar un fotograma d'una pel·lícula; la corba ROC mostra la pel·lícula sencera.
TPR i FPR: les dues coordenades de la ROC
Per a cada llindar es calculen dues taxes a partir de la matriu de confusió de 06-02:
- TPR (taxa de veritables positius) = TP / (TP + FN). És exactament el recall: dels clients que marxen, quina fracció en detectem? Volem que sigui alta.
- FPR (taxa de falsos positius) = FP / (FP + TN). Dels clients fidels, quina fracció molestem amb una falsa alarma? Volem que sigui baixa.
Fixa't en l'elegància: cada taxa es calcula dins de la seva pròpia classe (TPR entre els positius reals, FPR entre els negatius reals), de manera que cap de les dues no depèn de la proporció de churn del dataset — un detall que serà rellevant al final de la lliçó.
Construcció de la corba ROC punt a punt (a mà)
Res no fixa millor la idea que calcular-la a mà. Prenguem 10 clients de prova de MercaFresh amb les seves probabilitats de churn segons el model, ordenats de major a menor probabilitat (4 churn reals, 6 fidels):
| Client | P(churn) | Real |
|---|---|---|
| C1 | 0.92 | Churn |
| C2 | 0.81 | Churn |
| C3 | 0.74 | Fidel |
| C4 | 0.66 | Churn |
| C5 | 0.55 | Fidel |
| C6 | 0.47 | Fidel |
| C7 | 0.39 | Churn |
| C8 | 0.30 | Fidel |
| C9 | 0.18 | Fidel |
| C10 | 0.05 | Fidel |
Ara fem lliscar el llindar des de dalt: per a cada valor, tot client amb probabilitat ≥ llindar es prediu com a churn, i comptem TP, FP → TPR = TP/4, FPR = FP/6:
| Llindar | Predits churn | TP | FP | TPR | FPR |
|---|---|---|---|---|---|
| > 0.92 | (ningú) | 0 | 0 | 0.00 | 0.00 |
| 0.92 | C1 | 1 | 0 | 0.25 | 0.00 |
| 0.81 | C1–C2 | 2 | 0 | 0.50 | 0.00 |
| 0.74 | C1–C3 | 2 | 1 | 0.50 | 0.17 |
| 0.66 | C1–C4 | 3 | 1 | 0.75 | 0.17 |
| 0.55 | C1–C5 | 3 | 2 | 0.75 | 0.33 |
| 0.47 | C1–C6 | 3 | 3 | 0.75 | 0.50 |
| 0.39 | C1–C7 | 4 | 3 | 1.00 | 0.50 |
| 0.30 | C1–C8 | 4 | 4 | 1.00 | 0.67 |
| 0.18 | C1–C9 | 4 | 5 | 1.00 | 0.83 |
| 0.05 | tots | 4 | 6 | 1.00 | 1.00 |
La corba ROC és el dibuix d'aquests punts (FPR a l'eix X, TPR a l'eix Y), des de (0,0) — llindar impossible de superar: ningú no és assenyalat — fins a (1,1) — llindar zero: tots assenyalats. Cada vegada que el lliscament "s'empassa" un churn real, la corba puja; cada vegada que s'empassa un fidel, avança cap a la dreta. Un model bo ordena els churn per davant dels fidels, així que la seva corba puja aviat i molt abans d'avançar: s'enganxa al racó superior esquerre.
Dues referències al gràfic:
- La diagonal de (0,0) a (1,1) és l'atzar: un "model" que assignés probabilitats aleatòries puja i avança al mateix ritme. Tota corba útil ha de quedar per sobre; una corba per sota de la diagonal indica un model que ordena al revés (invertir les seves prediccions el tornaria útil!).
- El punt ideal és (0, 1): 100 % de fugues detectades, 0 % de fidels molestats. Cap model real no l'assoleix, però la distància a aquell racó resumeix la qualitat de cada llindar.
AUC: interpretació probabilística
L'AUC (Area Under the Curve) és l'àrea sota la corba ROC: un número entre 0 i 1 que condensa el rendiment en tots els llindars.
| AUC | Lectura |
|---|---|
| 0.5 | Atzar pur (la diagonal) |
| 0.6–0.7 | Discriminació feble |
| 0.7–0.8 | Acceptable |
| 0.8–0.9 | Bona |
| > 0.9 | Excel·lent (verifica que no hi hagi fuita de dades, 06-01!) |
La seva interpretació més útil és probabilística: l'AUC és la probabilitat que, prenent a l'atzar un client que va marxar i un de fidel, el model assigni més probabilitat de churn al que va marxar. És a dir, mesura la qualitat del rànquing, no de la classificació. Comprovem-ho amb la nostra taula: hi ha 4 × 6 = 24 parelles (churn, fidel) possibles; comptant quantes n'ordena bé el model (el churn amb més probabilitat que el fidel): C1 i C2 superen els 6 fidels (12 parelles), C4 en supera 5 (tots menys C3), C7 en supera 3 (C8, C9, C10). Total: 12 + 5 + 3 = 20 parelles ben ordenades de 24 → AUC = 20/24 ≈ 0.83, que coincideix exactament amb l'àrea sota la corba esglaonada anterior.
Aquesta lectura explica per què l'AUC és ideal per comparar models abans de decidir el llindar: un model amb millor AUC ordena millor els clients per risc, i aquella ordenació és la matèria primera de qualsevol campanya ("trucar primer als de més risc").
ROC i AUC en scikit-learn: comparació de models
from sklearn.metrics import roc_curve, roc_auc_score
from sklearn.linear_model import LogisticRegression
from sklearn.tree import DecisionTreeClassifier
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
import matplotlib.pyplot as plt
models = {
"Regressio logistica": Pipeline([("sc", StandardScaler()),
("clf", LogisticRegression(max_iter=1000))]),
"Arbre (max_depth=5)": DecisionTreeClassifier(max_depth=5, random_state=42),
}
plt.figure(figsize=(6, 6))
for nom, m in models.items():
m.fit(X_entrenament, y_entrenament)
probs = m.predict_proba(X_prova)[:, 1] # necessitem probabilitats, no classes
fpr, tpr, llindars = roc_curve(y_prova, probs)
auc = roc_auc_score(y_prova, probs)
plt.plot(fpr, tpr, label=f"{nom} (AUC = {auc:.3f})")
plt.plot([0, 1], [0, 1], "k--", label="Atzar (AUC = 0.5)") # la diagonal
plt.xlabel("FPR (fidels molestats)")
plt.ylabel("TPR / Recall (fugues detectades)")
plt.title("Corbes ROC - churn MercaFresh")
plt.legend()
plt.show()Detalls importants:
roc_curveretorna tres arrays:fpr,tpri elsllindarscorresponents a cada punt — els usarem ara per triar el llindar operatiu.- A
roc_auc_scorese li passen les probabilitats, maipredict()(amb classes 0/1 la "corba" tindria un sol punt útil i l'AUC sortiria distorsionat). - Per a models sense
predict_probaben calibrat (comSVC), s'usadecision_function, que també ordena. - Si dues corbes es creuen, un model pot ser millor a la zona d'FPR baix i l'altre a la d'FPR alt: l'AUC global pot empatar mentre que, per al teu rang operatiu, un dels dos sigui clarament preferible. Mirar la corba, no només el número.
Triar el llindar operatiu segons costos de negoci
L'AUC compara models; per usar el model triat cal fixar un llindar. Escenari MercaFresh: l'equip de retenció pot trucar a 600 clients al mes (pressupost limitat) d'una cartera de 10.000, amb churn esperat del 20 %. Dos enfocaments complementaris:
Enfocament 1 — capacitat: si només pots trucar a 600, el llindar és simplement el que assenyala els 600 clients de més probabilitat:
import numpy as np
probs_tots = model.predict_proba(X_cartera)[:, 1]
llindar_capacitat = np.sort(probs_tots)[-600] # prob. del client num. 600 per risc
print(f"Llindar operatiu: {llindar_capacitat:.2f}")Enfocament 2 — costos: amb costos explícits (trucada + val ≈ 15 €; client perdut ≈ 300 € de valor anual; la retenció funciona, diguem, en el 30 % dels contactats a temps), es pot calcular el benefici esperat de cada llindar i quedar-se amb el màxim:
fpr, tpr, llindars = roc_curve(y_prova, probs)
n_pos, n_neg = y_prova.sum(), (1 - y_prova).sum()
cost_contacte, valor_client, efic = 15, 300, 0.30
benefici = (tpr * n_pos * efic * valor_client # fugues salvades
- (tpr * n_pos + fpr * n_neg) * cost_contacte) # cost de contactar
millor = np.argmax(benefici)
print(f"Llindar optim: {llindars[millor]:.2f} (TPR={tpr[millor]:.2f}, FPR={fpr[millor]:.2f})")La conclusió habitual en churn: el llindar òptim queda molt per sota de 0.5 (sovint 0.2–0.35), perquè un FN costa 20 vegades més que un FP. El 0.5 per defecte no és neutral: pressuposa costos simètrics que gairebé mai no són els del negoci. I una nota metodològica: el llindar es tria amb dades de validació, no de prova (regla de 06-01: la prova no participa en decisions).
La corba precision-recall com a alternativa
Amb desequilibri fort (positius a l'1–5 %: frau, malalties rares... o un churn mensual molt baix), la ROC pot pintar un panorama massa optimista: com que l'FPR es calcula sobre l'enorme classe negativa, un FPR "baix" del 5 % pot significar milers de falses alarmes que sepulten els veritables positius. La corba precision-recall (precision a l'eix Y, recall a l'eix X, un punt per llindar) usa la precision en lloc de l'FPR, i la precision sí que sent cada fals positiu directament.
from sklearn.metrics import precision_recall_curve, average_precision_score
prec, rec, llind = precision_recall_curve(y_prova, probs)
ap = average_precision_score(y_prova, probs) # analeg a l'AUC per a aquesta corba
plt.plot(rec, prec, label=f"AP = {ap:.3f}")
plt.xlabel("Recall"); plt.ylabel("Precision"); plt.legend(); plt.show()Regla pràctica: churn al 20 % → la ROC funciona bé; positius per sota del ~5 % o interès centrat exclusivament en la classe minoritària → afegeix (o prioritza) la corba precision-recall. Aquí la seva línia base no és la diagonal, sinó l'horitzontal a la prevalença (0.20 en el nostre cas): aquella és la precision d'assenyalar a l'atzar.
Limitacions de l'AUC
- És cec als costos: resumeix tots els llindars per igual, inclosos els que mai no usaries. Dos models amb el mateix AUC poden rendir molt diferent a la teva zona operativa (FPR baix, si el pressupost és curt).
- No mesura calibració: avalua l'ordre, no si "0.7" significa realment un 70 % de probabilitat. Un model pot tenir AUC 0.85 amb probabilitats sistemàticament inflades; si has d'usar les probabilitats com a tals (càlculs de benefici esperat), la calibració importa.
- Optimista sota desequilibri extrem, com acabem de veure: complementa amb precision-recall.
- Un número no substitueix la corba: amb corbes que es creuen, el resum escalar amaga la diferència que t'afecta.
- Res d'això no l'invalida: com a mètrica de comparació de models independent del llindar, continua sent l'estàndard (i per això
scoring="roc_auc"encaixa de manera natural en la validació creuada de 06-03).
Errors Comuns i Consells
- Passar
predict()en lloc depredict_proba()aroc_auc_score. Amb classes dures l'AUC es calcula sobre un rànquing de només dos valors i surt artificialment baix. Probabilitats sempre. - Quedar-se amb el llindar 0.5 "perquè és el defecte". En problemes amb costos asimètrics (gairebé tots els reals), 0.5 és una elecció arbitrària heretada, no una decisió.
- Triar el llindar mirant la prova. El llindar és una decisió més del modelatge: es pren amb validació (o CV) i es verifica una única vegada en prova.
- Comparar AUCs amb tres decimals. La lliçó 06-03 s'aplica també aquí: un AUC de 0.842 no és millor que un de 0.838 sense mirar la variabilitat entre folds.
- Usar la ROC com a única lent amb desequilibri extrem. Acompanya-la de la corba precision-recall.
- Consell: lliura al negoci la corba amb dos o tres punts operatius anotats ("amb llindar 0.3: detectem el 78 % de les fugues trucant a 1.900 clients") — és infinitament més accionable que "AUC = 0.84".
Exercicis
Exercici 1
Amb aquesta minitaula (3 churn reals, 3 fidels), calcula els punts (FPR, TPR) per als llindars 0.9, 0.6 i 0.3, i l'AUC pel mètode de comptar parelles ben ordenades.
| Client | P(churn) | Real |
|---|---|---|
| A | 0.90 | Churn |
| B | 0.70 | Fidel |
| C | 0.60 | Churn |
| D | 0.40 | Churn |
| E | 0.20 | Fidel |
| F | 0.10 | Fidel |
Exercici 2
El model de churn de MercaFresh té AUC = 0.84. Explica a un directiu, sense tecnicismes, què significa aquell 0.84, i per què aquell número per si sol no diu quants clients cal trucar cada mes.
Exercici 3
Escriu el codi que, donats probs (probabilitats sobre validació) i y_val, troba el llindar més baix amb un recall ≥ 0.80 i reporta quants clients caldria contactar amb ell (sobre una cartera de 10.000 amb la mateixa distribució). Pista: roc_curve et dona tpr i llindars alineats.
Solucions
Solució 1. Positius = 3 (A, C, D), negatius = 3 (B, E, F).
- Llindar 0.9 → assenyalats {A}: TP = 1, FP = 0 → (FPR, TPR) = (0, 0.33).
- Llindar 0.6 → assenyalats {A, B, C}: TP = 2, FP = 1 → (0.33, 0.67).
- Llindar 0.3 → assenyalats {A, B, C, D}: TP = 3, FP = 1 → (0.33, 1.00).
Parelles (churn, fidel) = 9. Ben ordenades: A supera B, E, F (3); C supera E, F (2); D supera E, F (2). Total 7/9 → AUC ≈ 0.78.
Solució 2. "Si agafem a l'atzar un client que va acabar donant-se de baixa i un altre que es va quedar, el model assigna més risc al que es va donar de baixa el 84 % de les vegades: ordena bé els clients per risc de fuga." No diu a quants trucar perquè l'AUC avalua l'ordenació completa, sense fixar cap punt de tall: quants en truquem depèn d'on tallem aquella llista ordenada, i aquella és una decisió de negoci (pressupost de la campanya, cost per contacte, valor del client), no una propietat del model.
Solució 3.
import numpy as np
from sklearn.metrics import roc_curve
fpr, tpr, llindars = roc_curve(y_val, probs)
# Primer punt (recorrent llindars de major a menor) amb recall >= 0.80:
idx = np.argmax(tpr >= 0.80) # primer index que compleix la condicio
llindar_op = llindars[idx]
print(f"Llindar operatiu: {llindar_op:.3f} (TPR={tpr[idx]:.2f}, FPR={fpr[idx]:.2f})")
# Fraccio de la cartera assenyalada amb aquest llindar:
frac_contactada = (probs >= llindar_op).mean()
print(f"Contactes sobre 10.000 clients: {frac_contactada * 10000:.0f}")Com que roc_curve ordena els llindars de major a menor i tpr creix al llarg de l'array, el primer índex amb tpr >= 0.80 correspon al llindar més alt (i per tant amb menys contactes) que assoleix aquell recall; és el punt més barat que compleix l'objectiu de detecció.
Conclusió
Hem alliberat el llindar de decisió: TPR i FPR descriuen cada tall possible, la corba ROC els dibuixa tots (construïda a mà, punt a punt, sobre deu clients de MercaFresh), i l'AUC la resumeix amb una interpretació memorable — la probabilitat de rankejar bé una parella churn/fidel. Amb roc_curve i roc_auc_score comparem models més enllà del 0.5 per defecte, triem el llindar operatiu amb criteris de negoci (pressupost de la campanya, cost de cada tipus d'error) i hem après quan la corba precision-recall és millor lent i què no explica l'AUC. Ja tenim l'arsenal complet d'avaluació; queda la pregunta de diagnòstic que ho travessa tot: quan un model rendeix malament — o sospitosament bé — és perquè memoritza o perquè no aprèn prou? Overfitting i underfitting, el diagnòstic central de tot el Machine Learning, tanquen el mòdul a la propera lliçó.
Curs de Machine Learning
Mòdul 1: Introducció al Machine Learning
- Què és el Machine Learning?
- Història i evolució del Machine Learning
- Tipus de Machine Learning
- Aplicacions del Machine Learning
- El flux de treball d'un projecte de Machine Learning
Mòdul 2: Fonaments d'Estadística i Probabilitat
- Conceptes bàsics d'estadística
- Distribucions de probabilitat
- Correlació i covariància
- Inferència estadística
- Teorema de Bayes
Mòdul 3: Preprocessament de Dades
- Neteja de dades
- Gestió de dades mancants
- Transformació de dades
- Codificació de variables categòriques
- Normalització i estandardització
- Enginyeria de característiques
Mòdul 4: Algorismes de Machine Learning Supervisat
- Regressió lineal
- Regressió logística
- Arbres de decisió
- Màquines de suport vectorial (SVM)
- K veïns més propers (K-NN)
- Naive Bayes
- Xarxes neuronals
Mòdul 5: Algorismes de Machine Learning No Supervisat
- Clustering: K-means
- Clustering jeràrquic
- Anàlisi de components principals (PCA)
- Anàlisi d'agrupament DBSCAN
- Visualització de dades amb t-SNE i UMAP
Mòdul 6: Avaluació i Validació de Models
- Divisió de dades: entrenament, validació i prova
- Mètriques d'avaluació
- Validació creuada
- Corba ROC i AUC
- Overfitting i underfitting
Mòdul 7: Tècniques Avançades i Optimització
- Regularització: Ridge, Lasso i Elastic Net
- Ensemble Learning
- Gradient Boosting
- Xarxes neuronals profundes (Deep Learning)
- Optimització d'hiperparàmetres
Mòdul 8: Implementació i Desplegament de Models
- Frameworks i biblioteques populars
- Implementació de models en producció
- Manteniment i monitoratge de models
- Consideracions ètiques i de privadesa
Mòdul 9: Projectes Pràctics
- Projecte 1: Predicció de preus d'habitatges
- Projecte 2: Classificació d'imatges
- Projecte 3: Anàlisi de sentiments a les xarxes socials
- Projecte 4: Detecció de fraus
- Projecte 5: Segmentació de clients
