La regressió lineal va tancar la lliçó anterior topant contra un mur: no sap respondre preguntes de sí o no. I la pregunta estrella de MercaFresh és exactament d'aquest tipus — abandonarà aquest client en els pròxims 90 dies? En aquesta lliçó transformem la recta en una corba en forma de S que produeix probabilitats, entenem per què aquesta transformació és necessària i què signifiquen els seus coeficients en termes d'odds, i entrenem el primer classificador del curs sobre el dataset de churn que vam construir al mòdul 3, amb el seu preprocessador inclòs. Malgrat el seu nom, la regressió logística és un algorisme de classificació — probablement el més utilitzat del món en producció.
Contingut
- Per què la regressió lineal no serveix per classificar
- La funció sigmoide: de números a probabilitats
- Probabilitat i llindar de decisió
- Log-odds i coeficients: interpretació amb odds ratio
- La funció de cost: log-loss
- Implementació: churn de MercaFresh amb el preprocessador del mòdul 3
- Classificació multiclasse: one-vs-rest i softmax
Per què la regressió lineal no serveix per classificar
Primera temptació: codificar el churn com a 0/1 i ajustar una regressió lineal. Sembla raonable — la predicció seria "una cosa semblant a la probabilitat de churn". Falla per tres motius:
- Prediccions fora de rang: un hiperplà no està acotat. Per a un client molt inactiu predirà 1.4, i per a un de molt fidel, −0.3. Probabilitat del 140%? Cap interpretació no ho salva.
- Sensibilitat absurda als casos extrems: recorda l'exercici 3 de 04-01 — els punts llunyans estiren la recta. Un client claríssim de churn (recència de 300 dies) desplaça la recta i empitjora la classificació dels casos dubtosos, que són justament els que importen.
- L'error quadràtic castiga malament: penalitzar amb MSE que el model predigui 1.4 en un client que efectivament va abandonar (etiqueta 1) és castigar per excés de raó.
La solució no és abandonar la combinació lineal $z = w_0 + w_1 x_1 + \dots + w_n x_n$ — que continua sent el cor del model — sinó passar-la per una funció que la comprimeixi a l'interval (0, 1).
La funció sigmoide: de números a probabilitats
Aquesta funció és la sigmoide (o funció logística, d'aquí el nom de l'algorisme):
$$\sigma(z) = \frac{1}{1 + e^{-z}}$$
Les seves propietats són exactament les que necessitem:
| Entrada $z$ | Sortida $\sigma(z)$ | Lectura |
|---|---|---|
| $z \to -\infty$ | → 0 | Certesa de "no churn" |
| $z = -2$ | 0.12 | Probablement es queda |
| $z = 0$ | 0.50 | Màxima incertesa |
| $z = +2$ | 0.88 | Probablement abandona |
| $z \to +\infty$ | → 1 | Certesa de "churn" |
import numpy as np
import matplotlib.pyplot as plt
z = np.linspace(-6, 6, 200)
sigma = 1 / (1 + np.exp(-z))
plt.plot(z, sigma)
plt.axhline(0.5, color="gray", linestyle="--")
plt.axvline(0, color="gray", linestyle="--")
plt.xlabel("z = w0 + w1*x1 + ... (puntuacio lineal)")
plt.ylabel("P(churn = 1)")
plt.title("La funcio sigmoide")
plt.show()La corba té forma de S: plana als extrems (on el model ja n'està segur, més evidència gairebé no canvia res) i dreta al centre (on cada dècima de $z$ mou molt la probabilitat). El model complet és:
$$P(\text{churn} = 1 \mid \mathbf{x}) = \sigma(w_0 + w_1 x_1 + \dots + w_n x_n)$$
Fixa't en la notació: és una probabilitat condicionada, la mateixa criatura que vam manejar al teorema de Bayes (02-05). La regressió logística estima directament $P(\text{classe} \mid \text{dades})$ — el que allà anomenàvem posterior — sense passar per priors i versemblances: aprèn la probabilitat condicionada d'una tacada, ajustant pesos. A 04-06 veurem l'enfocament contrari (Naive Bayes), que sí que construeix el posterior peça a peça com a 02-05.
Probabilitat i llindar de decisió
El model retorna probabilitats; el negoci necessita decisions. El pont és el llindar: per defecte, si $P(\text{churn}) \geq 0.5$ es prediu churn; si no, permanència. Geomètricament, $\sigma(z) = 0.5$ passa quan $z = 0$: l'equació $w_0 + w_1 x_1 + \dots = 0$ defineix una frontera de decisió lineal — un hiperplà, com a 04-01, però ara separant classes en lloc d'ajustar valors.
L'important: el llindar 0.5 és una convenció, no una llei. Per a MercaFresh, deixar escapar un client valuós (fals negatiu) costa molt més que enviar un cupó innecessari (fals positiu) — el mateix dilema de costos asimètrics que el detector de frau de 02-05. Abaixant el llindar a 0.3, la campanya de retenció captura més clients en risc a canvi de més cupons malgastats:
probs = model.predict_proba(X_prova)[:, 1] # probabilitat de churn
decisio_estandard = probs >= 0.5
decisio_prudent = probs >= 0.3 # retenir davant del dubteCom triar el llindar òptim i com mesurar aquest compromís amb rigor és matèria de la corba ROC (06-04) i les mètriques de classificació (06-02); aquí n'hi ha prou de saber que la probabilitat és la sortida rica i la decisió és una capa a sobre, ajustable al cost de negoci.
Log-odds i coeficients: interpretació amb odds ratio
En regressió lineal, "pujar $x_1$ una unitat suma $w_1$ euros". I aquí? La sigmoide complica la lectura directa en probabilitats, però hi ha una reformulació elegant. Aïllant $z$:
$$\ln\left(\frac{P}{1-P}\right) = w_0 + w_1 x_1 + \dots + w_n x_n$$
El quocient $\frac{P}{1-P}$ són els odds: amb P = 0.75, els odds són 3 — "3 a 1 a favor del churn". El model és lineal en el logaritme dels odds (log-odds). D'aquí surt la interpretació pràctica:
Pujar $x_i$ una unitat multiplica els odds per $e^{w_i}$ (l'odds ratio), mantenint la resta constant.
| Coeficient $w_i$ | Odds ratio $e^{w_i}$ | Lectura MercaFresh (exemple) |
|---|---|---|
| +0.9 (recència escalada) | 2.46 | Cada unitat més de recència multiplica els odds de churn per ~2.5 |
| 0.0 | 1.00 | La feature no aporta res |
| −0.7 (freqüència escalada) | 0.50 | Cada unitat més de freqüència redueix a la meitat els odds de churn |
Dues cauteles heretades de 04-01: les features escalades (el nostre preprocessador fa servir RobustScaler) fan que "una unitat" sigui una quantitat robusta tipus IQR, no una unitat natural; i amb features correlacionades els coeficients individuals es reparteixen l'efecte de manera inestable — la lectura és orientativa, no un veredicte causal (02-03).
La funció de cost: log-loss
No podem entrenar minimitzant el MSE: combinat amb la sigmoide produeix un paisatge d'error amb valls falses on el descens de gradient s'encalla. La funció de cost natural per a probabilitats és la log-loss (o entropia creuada binària). La seva lògica, cas per cas:
- Si l'etiqueta real és 1 (churn), el cost és $-\ln(p)$: predir $p = 0.9$ costa poc (0.105); predir $p = 0.01$ costa moltíssim (4.6).
- Si l'etiqueta real és 0, el cost és $-\ln(1-p)$: el simètric.
$$\text{LogLoss} = -\frac{1}{n}\sum_{i=1}^{n} \left[ y_i \ln(p_i) + (1 - y_i)\ln(1 - p_i) \right]$$
La propietat clau: el cost d'una equivocació segura tendeix a infinit. Dir "97% segur que es queda" d'un client que abandona surt caríssim; dir-ne "60%", molt menys. La log-loss no premia encertar sense més: premia probabilitats honestes. Amb ella, el paisatge d'error torna a ser un bol amb un únic mínim, i el descens de gradient de 04-01 el troba sense fórmula tancada (aquí no existeix l'equació normal: l'entrenament és sempre iteratiu).
Implementació: churn de MercaFresh amb el preprocessador del mòdul 3
Moment de cobrar la inversió del mòdul 3: el ColumnTransformer de 03-06 s'encadena amb el classificador en un únic Pipeline. Un sol objecte fa tot el viatge dades crues → predicció:
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.pipeline import Pipeline
from sklearn.compose import ColumnTransformer
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import (OneHotEncoder, OrdinalEncoder,
PowerTransformer, RobustScaler)
from sklearn.linear_model import LogisticRegression
# --- El preprocessador construit a 03-06 (resumit) ---
branca_numerica = Pipeline([
("imputar", SimpleImputer(strategy="median", add_indicator=True)),
("desasimetritzar", PowerTransformer(method="yeo-johnson")),
("escalar", RobustScaler()),
])
preprocessador = ColumnTransformer([
("num", branca_numerica,
["edat", "satisfaccio", "recencia_dies", "comandes_per_mes",
"despesa_mitjana_comanda", "ratio_inactivitat", "tendencia"]),
("cat_nominal", OneHotEncoder(sparse_output=False, handle_unknown="ignore"),
["ciutat"]),
("cat_ordinal", OrdinalEncoder(categories=[["basic", "estandard", "premium"]]),
["pla"]),
])
# --- Dataset de churn simulat amb l'estructura de 03-06 ---
rng = np.random.default_rng(7)
n = 800
df = pd.DataFrame({
"edat": rng.integers(18, 75, n),
"satisfaccio": np.where(rng.random(n) < 0.1, np.nan, rng.integers(1, 11, n)),
"recencia_dies": rng.integers(1, 180, n),
"comandes_per_mes": rng.gamma(3, 1.2, n).round(1),
"despesa_mitjana_comanda": rng.gamma(9, 5, n).round(2),
"ciutat": rng.choice(["Valencia", "Madrid", "Barcelona"], n),
"pla": rng.choice(["basic", "estandard", "premium"], n, p=[0.5, 0.3, 0.2]),
})
df["ratio_inactivitat"] = (df["recencia_dies"] / 365).round(3)
df["tendencia"] = rng.uniform(0.1, 2.5, n).round(2)
# El churn depen sobretot de recencia alta i tendencia a la baixa
logits = 0.03 * df["recencia_dies"] - 1.5 * df["tendencia"] - 0.4 * df["comandes_per_mes"] + 1.0
df["churn"] = (rng.random(n) < 1 / (1 + np.exp(-logits))).astype(int)
X = df.drop(columns="churn")
y = df["churn"]
X_entrenament, X_prova, y_entrenament, y_prova = train_test_split(
X, y, test_size=0.2, stratify=y, random_state=42)
# --- Pipeline complet: preprocessar + classificar ---
clf = Pipeline([
("prep", preprocessador),
("model", LogisticRegression(max_iter=1000)),
])
clf.fit(X_entrenament, y_entrenament) # fit del preprocessador I del model, nomes amb entrenament
accuracy = clf.score(X_prova, y_prova) # % d'encerts, com a primera referencia
print(f"Accuracy en prova: {accuracy:.2%}")
# Probabilitats per a la campanya de retencio
en_risc = clf.predict_proba(X_prova)[:, 1]
print(X_prova.assign(p_churn=en_risc.round(2))
.sort_values("p_churn", ascending=False)
.head(3)[["recencia_dies", "tendencia", "p_churn"]])Punts que mereixen una lectura lenta:
stratify=y: manté la proporció de churn en entrenament i prova — important quan una classe és minoritària (més a 06-01).- El
Pipelineresol la fuita de dades: en ferclf.fit(X_entrenament, ...), el preprocessador aprèn medianes i quartils només de l'entrenament; en predir sobre prova, aplica aquests mateixos paràmetres. Exactament la disciplina de 03-02 i 03-05, ara automatitzada. predict_proba: la sortida de negoci real. La llista ordenada perp_churnés la llista de trucades de l'equip de retenció — connecta amb la featurevalor_en_riscde l'exercici de 03-06.scoreretorna l'accuracy (fracció d'encerts): ens serveix com a primera referència, però és una mètrica enganyosa amb classes desequilibrades; la seva crítica i les seves alternatives arriben a 06-02.max_iter=1000: l'entrenament és iteratiu (descens de gradient i variants); amb features mal escalades o poques iteracions no convergeix — una altra raó per la qual escalar (03-05) importa aquí, a diferència de 04-01.
Com a la regressió lineal, existeix una versió regularitzada que penalitza pesos grans — de fet LogisticRegression la porta activada per defecte (paràmetre C); entendre-la a fons és matèria de 07-01.
Classificació multiclasse: one-vs-rest i softmax
I si les classes no són dues? MercaFresh podria voler predir la categoria favorita d'un client (frescos / rebost / llar). Dues estratègies:
- One-vs-rest (OvR): entrena un classificador binari per classe ("frescos contra la resta", "rebost contra la resta"...) i tria la classe el model de la qual doni la probabilitat més alta. Simple i paral·lelitzable.
- Softmax (regressió logística multinomial): generalitza la sigmoide a K classes d'una tacada — calcula una puntuació $z_k$ per classe i les converteix en K probabilitats que sumen 1. És el que
LogisticRegressionfa servir per defecte en multiclasse, i reapareixerà com a capa de sortida de les xarxes neuronals (04-07).
# Res no canvia al codi: sklearn detecta les classes automaticament
# model = LogisticRegression(max_iter=1000) # softmax si y te 3+ classes
# model.predict_proba(X) -> una columna de probabilitat per classePer a la resta del curs ens basta el cas binari, que és el del churn.
Errors Comuns i Consells
- Llegir
predictquan el negoci necessitapredict_proba. L'etiqueta dura perd informació: un client amb p = 0.51 i un altre amb p = 0.99 són "churn" tots dos, però no mereixen la mateixa trucada. Treballa amb probabilitats i decideix el llindar al final. - Interpretar coeficients com a probabilitats. Un coeficient de 0.9 no significa "+90% de probabilitat": significa odds multiplicats per e^0.9 ≈ 2.5. L'efecte en probabilitat depèn del punt de partida (la S és plana als extrems).
- Oblidar l'escalat. A diferència de
LinearRegression, aquí l'entrenament és iteratiu i regularitzat per defecte: sense escalar, convergeix malament i la penalització castiga arbitràriament les features d'escala gran. El nostrePipelineho fa impossible d'oblidar — fes-lo servir sempre. - Refiar-se d'una accuracy alta amb classes desequilibrades. Amb un 90% de clients fidels, un model que sempre diu "es queda" encerta el 90%... i és inútil. La solució completa, a 06-02.
- Consell: la regressió logística és la línia base obligada de qualsevol classificació, com la lineal ho era de la regressió. Interpretable, ràpida, amb probabilitats ben calibrades. Els models de les pròximes lliçons se l'han de guanyar.
Exercicis
Exercici 1. Sense executar codi: un model de churn té $w_0 = -1$ i un únic coeficient $w_1 = 0.5$ sobre recencia_escalada. Calcula $P(\text{churn})$ per a clients amb recència escalada 0, 2 i 6. A partir de quina recència escalada el model prediu churn amb el llindar 0.5?
Exercici 2. Amb el Pipeline de la lliçó entrenat, extreu els coeficients del model (clf.named_steps["model"].coef_) juntament amb els noms de les features transformades (clf.named_steps["prep"].get_feature_names_out()). Ordena per valor absolut i tradueix les dues features més influents a odds ratio amb una frase de negoci cadascuna.
Exercici 3. L'equip de retenció només pot trucar al 10% dels clients. Escriu el codi que selecciona, del conjunt de prova, el 10% amb més probabilitat de churn, i explica per què això és millor que fer servir predict amb el llindar 0.5.
Solucions
Exercici 1
- Recència 0: $z = -1$, $\sigma(-1) = 1/(1+e^{1}) \approx 0.27$.
- Recència 2: $z = 0$, $\sigma(0) = 0.50$ — just a la frontera.
- Recència 6: $z = 2$, $\sigma(2) \approx 0.88$.
La frontera és on $z = 0$: $-1 + 0.5 \cdot x = 0 \Rightarrow x = 2$. Per sobre de la recència escalada 2, el model prediu churn. Observa que la frontera de decisió és un punt (en 1D), una recta (en 2D), un hiperplà en general: la logística continua sent un classificador lineal.
Exercici 2
noms = clf.named_steps["prep"].get_feature_names_out()
coefs = clf.named_steps["model"].coef_[0]
taula = (pd.DataFrame({"feature": noms, "coef": coefs})
.assign(odds_ratio=lambda t: np.exp(t["coef"]).round(2))
.reindex(pd.Series(coefs).abs().sort_values(ascending=False).index))
print(taula.head())Amb les dades simulades, a dalt apareixen num__recencia_dies (coeficient positiu: cada unitat robusta de recència extra multiplica els odds de churn pel seu odds ratio — el client que emmudeix és el que se'n va) i num__tendencia (coeficient negatiu, odds ratio < 1: una tendència d'activitat creixent divideix els odds d'abandonament). Les dues frases coincideixen amb la intuïció de negoci que va motivar aquestes features a 03-06 — bon senyal: el model ha après el que esperàvem que aprengués.
Exercici 3
probs = clf.predict_proba(X_prova)[:, 1]
tall = np.quantile(probs, 0.90) # percentil 90 (02-01)
llista_trucades = X_prova[probs >= tall]
print(f"Llindar efectiu: {tall:.2f} | clients a trucar: {len(llista_trucades)}")Amb el llindar 0.5 la llista tindria una mida arbitrària — potser el 30% dels clients (impossible de trucar), potser el 2% (capacitat desaprofitada). Ordenar per probabilitat i tallar per capacitat fa servir el recurs exacte disponible i el concentra en els casos de més risc: el llindar el dicta el negoci, no la convenció matemàtica.
Conclusió
Has convertit la recta de 04-01 en un classificador: la sigmoide comprimeix la puntuació lineal en una probabilitat, la log-loss castiga les equivocacions segures i premia l'honestedat probabilística, els coeficients es llegeixen com a odds ratios, i el llindar de decisió és una palanca de negoci, no una constant sagrada. A més, has estrenat el patró professional definitiu: Pipeline(preprocessador, model), on la feina del mòdul 3 i el classificador viatgen junts sense risc de fuites.
Però la regressió logística té la mateixa ànima que la lineal: la seva frontera de decisió és un hiperplà. Si els clients que abandonen no se separen dels fidels amb un tall recte — "churn si recència alta i pla bàsic, però també si despesa alta i tendència desplomada" — necessitem un model que pensi en regles i no en pesos. Això és exactament un arbre de decisió, i és la pròxima 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
