Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Classificació

A l’aprenentatge supervisat hem vist com predir un número. Ara volem predir una categoria: si un correu és spam o no, si un client comprarà, quin dígit hi ha en una imatge. Les eines són les mateixes (un model amb pesos, una funció de cost i el descens del gradient), però la sortida del model ha de ser una probabilitat.

Classificació binària

Imagina que vols separar dos tipus d’objectes. La classificació binària combina les característiques de manera semblant a la regressió lineal, i hi traça una línia de separació (en 2D) o un pla de separació (en 3D) que divideix l’espai en dues regions, una per a cada classe. Aquest model s’anomena regressió logística.

Com funciona la regressió logística

La regressió lineal ens dona un número qualsevol: 7, -0.2, -150. Per classificar, en canvi, volem una probabilitat, un valor entre 0 i 1 que ens digui com de probable és que l’exemple pertanyi a la classe 1. La regressió logística hi arriba en dos passos.

Farem servir un exemple al llarg de tota la secció: volem predir si un client comprarà un producte (classe 1) o no (classe 0) a partir de la seva edat i els seus ingressos.

Pas 1: calcular una puntuació

Primer, combinem les característiques de l’exemple igual que a la regressió lineal:

\[ z = w \cdot x + b \]

On:

  • \(x\) són les característiques d’entrada de l’exemple
  • \(w\) són els pesos que ajustem durant l’entrenament
  • \(b\) és el terme independent (bias)
  • \(z\) és la puntuació resultant

Suposem que l’entrenament ja ha trobat els pesos \(w_1 = 0.1\) per a l’edat i \(w_2 = 0.00003\) per als ingressos, i el terme independent \(b = -5\). Per a un client de 30 anys que guanya 60000 € l’any:

\[ z = 0.1 \cdot 30 + 0.00003 \cdot 60000 - 5 = 3 + 1.8 - 5 = -0.2 \]

La puntuació pot ser qualsevol número, però el seu signe ja ens diu quina classe és més probable:

  • \(z > 0\): més probable la classe 1
  • \(z < 0\): més probable la classe 0
  • Com més lluny del zero, més segura és la predicció

En el nostre exemple, \(z = -0.2\) indica que el client probablement no comprarà, però el model no n’està gaire segur.

Pas 2: convertir-la en probabilitat

Per passar de la puntuació a una probabilitat fem servir la funció sigmoide:

\[ g(z) = \frac{1}{1 + e^{-z}} \]

On \(e\) és el nombre d’Euler (aproximadament 2.718) i \(g(z)\) és la probabilitat que l’exemple pertanyi a la classe 1. Sigui quin sigui \(z\), el resultat sempre queda entre 0 i 1:

\(z\)\(g(z)\)Interpretació
-100.00005Gairebé segur classe 0
-20.12Probablement classe 0
00.5Dubte total
20.88Probablement classe 1
100.99995Gairebé segur classe 1

Per això la sigmoide té forma d’S: puja suaument al voltant del zero i s’aplana a prop de 0 i de 1 sense arribar-hi mai.

En el nostre exemple, \(g(-0.2) \approx 0.45\): hi ha un 45% de probabilitat que el client compri, i un 55% que no ho faci.

Per què la sigmoide?

Hi ha moltes funcions que comprimeixen un número entre 0 i 1. La sigmoide no s’ha triat perquè “queda bé”, sinó per una interpretació concreta de la puntuació \(z\).

Si un equip guanya el 80% dels partits, diem que té unes oportunitats de “4 contra 1”, perquè 0.8 / 0.2 = 4. En general, les oportunitats (odds) divideixen la probabilitat que passi una cosa entre la probabilitat que no passi, i si en prenem el logaritme natural obtenim el logaritme de les oportunitats (log odds):

\[ \text{log odds} = \ln\frac{p}{1-p} \]

On \(p\) és la probabilitat que l’exemple pertanyi a la classe 1.

ProbabilitatOportunitatsLog odds
0.20.25 (1 contra 4)-1.39
0.51 (1 contra 1)0
0.84 (4 contra 1)1.39

El log odds val 0 quan hi ha dubte total, és simètric i va de -∞ a +∞, exactament com la puntuació \(z\). Per això la regressió logística interpreta \(z\) com el log odds de la classe 1, i l’entrenament ajusta \(w\) i \(b\) perquè aquesta interpretació sigui correcta.

Si aïllem \(p\) d’aquesta interpretació, surt la sigmoide:

\[ z = \ln\frac{p}{1-p} \quad\Rightarrow\quad e^z = \frac{p}{1-p} \quad\Rightarrow\quad p = \frac{e^z}{1 + e^z} = \frac{1}{1 + e^{-z}} \]

En paraules senzilles: la sigmoide és la funció inversa del log odds. Converteix la puntuació, llegida com a log odds, en la probabilitat corresponent.

El model complet

Característiques → [Puntuació z] → [Sigmoide] → [Probabilitat]
      x          →  w·x + b      →    g(z)    →    0 a 1

Frontera de decisió

Per decidir la classe, la regla habitual és dir “classe 1” si la probabilitat passa de 0.5. Com que la sigmoide val exactament 0.5 quan z = 0, això és el mateix que dir “classe 1” si la puntuació z és positiva.

La frontera de decisió és el conjunt de punts on z = 0, és a dir, on el model dubta del tot. Amb dues característiques és una recta al pla: els punts d’un costat es classifiquen com a 1, i els de l’altre com a 0.

Fronteres no lineals

Per obtenir fronteres corbes (cercles, el·lipses…) podem afegir característiques polinòmiques, igual que a la regressió polinòmica. Per exemple, si fem servir \( x_1^2 \) i \( x_2^2 \) en lloc de \( x_1 \) i \( x_2 \), la frontera pot ser un cercle.

L'àlgebra de la frontera

Amb dues característiques, la frontera és:

\[ w_1 x_1 + w_2 x_2 + b = 0 \]

que és l’equació d’una recta. Si fem servir \( x_1^2 \) i \( x_2^2 \), la frontera és \( w_1 x_1^2 + w_2 x_2^2 + b = 0 \). En el cas senzill \( w_1 = w_2 = 1 \), queda \( x_1^2 + x_2^2 = -b \): un cercle de radi \( \sqrt{-b} \). Amb pesos diferents o termes creuats (\( x_1 x_2 \)) es poden obtenir el·lipses, paràboles o formes més complexes.

Funció de cost

Per a classificació no fem servir l’error quadràtic de la regressió, sinó la pèrdua logarítmica (entropia creuada, cross-entropy). La idea és castigar molt les prediccions molt segures i equivocades: si el model diu 0.99 per a la classe 1 i en realitat és 0, el cost és enorme. A més, amb aquesta funció el cost torna a tenir forma de bol, i el descens del gradient funciona bé.

  • Quan l’etiqueta real és 1 (corba verda): si la predicció s’apropa a 1, el cost és baix; si s’apropa a 0, el cost es dispara.
  • Quan l’etiqueta real és 0 (corba vermella): passa el contrari.
La fórmula

\[ L(f(x_i), y_i) = \begin{cases} -\log(f(x_i)) & \text{si } y_i = 1 \\ -\log(1 - f(x_i)) & \text{si } y_i = 0 \end{cases} \]

  • f(xᵢ): la probabilitat que el model dona a la classe 1
  • yᵢ: la classe real

El cost J és la mitjana d’aquesta pèrdua sobre tots els exemples.

Regressió logística amb scikit-learn

import numpy as np
from sklearn.linear_model import LogisticRegression

X = np.array([[0.5, 1.5], [1, 1], [1.5, 0.5], [3, 0.5], [2, 2], [1, 2.5]])
y = np.array([0, 0, 0, 1, 1, 1])

model = LogisticRegression()
model.fit(X, y)

# Prediccions sobre les mateixes dades d'entrenament, només per veure què ha après.
# Per avaluar-lo de veritat cal un conjunt de test, com veurem a continuació.

print("Prediccions:", model.predict(X))
print("Probabilitats de la classe 1:", model.predict_proba(X)[:, 1])

El resultat sobre aquestes dades: els punts de cada classe i la frontera de decisió que ha après el model.

Avaluar un classificador

La trampa de la taxa d’encerts

La taxa d’encerts (accuracy) és el percentatge de prediccions correctes: si el model n’encerta 90 de 100, és del 90%.

Pot enganyar molt. Imagina un detector de frau en un banc on només 1 de cada 100 transaccions és fraudulenta. Un “model” que sempre diu “no és frau” encerta 99 de cada 100: un 99% de taxa d’encerts, sense detectar cap frau. Quan una classe és molt més freqüent que l’altra (classes desbalancejades), cal mirar els errors per separat.

Matriu de confusió

La matriu de confusió compta els quatre resultats possibles. Exemple amb 100 pacients, 10 dels quals estan malalts:

Predicció: malaltPredicció: sa
Real: malalt8 (detectats)2 (escapats)
Real: sa5 (falses alarmes)85 (correctes)

A partir d’aquests quatre números calculem dues mètriques:

\[ \text{Precisió} = \frac{\text{detectats}}{\text{detectats} + \text{falses alarmes}} = \frac{8}{13} = 0.62 \qquad \text{Recall} = \frac{\text{detectats}}{\text{detectats} + \text{escapats}} = \frac{8}{10} = 0.80 \]

En paraules senzilles:

  • Precisió (precision): quan el model diu “malalt”, quantes vegades té raó?
  • Recall: de tots els malalts reals, quants n’ha trobat?

El F1 les combina en un sol número, i baixa molt si una de les dues és baixa:

\[ \text{F1} = 2 \times \frac{\text{Precisió} \times \text{Recall}}{\text{Precisió} + \text{Recall}} = 2 \times \frac{0.62 \times 0.80}{0.62 + 0.80} \approx 0.70 \]

Quina és més important depèn del cost de cada error. En un diagnòstic mèdic, deixar escapar un malalt és greu: vigilem el recall. En un filtre de spam, enviar a la paperera un correu important és greu: vigilem la precisió.

El llindar és una elecció

La regressió logística retorna una probabilitat, i nosaltres decidim a partir de quin valor diem “classe 1”. El valor de 0.5 és el llindar per defecte, però no és una regla:

  • Llindar més baix (per exemple, 0.3): marquem més casos com a positius. Escapen menys malalts (més recall), però hi ha més falses alarmes (menys precisió).
  • Llindar més alt: menys falses alarmes, però escapen més casos.

El llindar es tria, com el grau d’un polinomi, amb el conjunt de validació.

Amb scikit-learn

Vegem-ho tot amb dades reals: el dataset breast_cancer de scikit-learn té 30 mesures de 569 tumors. Com que volem detectar els malignes, els marquem com a classe 1:

from sklearn.datasets import load_breast_cancer
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import confusion_matrix, classification_report
from sklearn.model_selection import train_test_split
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler

X, y = load_breast_cancer(return_X_y=True)
y = 1 - y  # al dataset, 0 és maligne: el girem perquè 1 sigui "maligne"
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, stratify=y, random_state=42)

model = make_pipeline(StandardScaler(), LogisticRegression())
model.fit(X_train, y_train)

y_pred = model.predict(X_test)  # llindar 0.5
print(confusion_matrix(y_test, y_pred))
print(classification_report(y_test, y_pred))  # precisió, recall i F1 per classe

# Un llindar més baix (aquí el comparem amb el test només per veure l'efecte)
y_prob = model.predict_proba(X_test)[:, 1]
y_pred_03 = (y_prob > 0.3).astype(int)
print(confusion_matrix(y_test, y_pred_03))
[[71  1]     llindar 0.5: 39 malignes detectats, 3 escapats, 1 falsa alarma
 [ 3 39]]
[[71  1]     llindar 0.3: 41 malignes detectats, 1 escapat, 1 falsa alarma
 [ 1 41]]

scikit-learn ordena la matriu per classe (0, 1): la primera fila són els tumors benignes reals i la segona els malignes. Baixar el llindar a 0.3 troba dos malignes més sense cap falsa alarma nova. Amb altres dades, el preu sol ser més falses alarmes: per això el llindar es tria mirant el conjunt de validació.

stratify=y fa que el test tingui la mateixa proporció de malignes que el total (un 37%). En classificació és el que cal fer sempre, i encara més quan una classe és minoritària.

Com comparar classificadors amb tots els llindars alhora (corbes ROC i Precision-Recall) i un exemple complet de frau són a Avaluació de models.

Classificació multiclasse

Fins ara hem classificat en dues classes. Però sovint n’hi ha més: reconèixer quin dígit (de 0 a 9) hi ha en una imatge, o quina espècie de flor és.

La regressió softmax generalitza la regressió logística:

  • La regressió logística calcula una puntuació z i la converteix en una probabilitat amb la sigmoide.
  • La regressió softmax calcula una puntuació per a cada classe (amb els seus propis pesos) i les converteix totes alhora en probabilitats que sumen 1, amb la funció softmax.

Exemple: una imatge d’un “3” escrit a mà, amb 10 classes possibles (els dígits del 0 al 9).

Dígit0123456789
Puntuació0.1-0.50.34.20.8-1.00.2-0.30.50.1
Probabilitat0.020.010.030.850.040.010.020.010.030.02

La softmax fa tres coses:

  1. Converteix puntuacions qualssevol en valors entre 0 i 1.
  2. Fa que sumin exactament 1.
  3. Manté l’ordre: la puntuació més alta dona la probabilitat més alta.

El model prediu la classe amb més probabilitat: aquí, un 85% de confiança que és un “3”.

Per entrenar-lo es fa servir de nou l’entropia creuada, que ara només mira la probabilitat que el model ha donat a la classe correcta: com més baixa, més gran és la pèrdua.

Les fórmules

Per a cada classe j de les N possibles, el model calcula una puntuació:

\[ z_j = w_j \cdot x + b_j \]

i la softmax la converteix en probabilitat:

\[ a_j = \frac{e^{z_j}}{\sum_{k=1}^{N} e^{z_k}} \]

  • wⱼ, bⱼ: els pesos i el biaix de la classe j
  • aⱼ: la probabilitat que l’exemple sigui de la classe j

En paraules senzilles: elevem e a cada puntuació (així totes són positives) i dividim per la suma de totes, de manera que sumin 1.

La pèrdua per a un exemple que és de la classe j és:

\[ L = -\log(a_j) \]

Amb scikit-learn

LogisticRegression detecta sola quan hi ha més de dues classes i fa servir la softmax:

from sklearn.datasets import load_digits
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler

X, y = load_digits(return_X_y=True)  # imatges de 8×8 píxels, dígits del 0 al 9
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

model = make_pipeline(StandardScaler(), LogisticRegression(max_iter=1000))
model.fit(X_train, y_train)

print(model.predict(X_test[:5]))        # classes predites
print(model.predict_proba(X_test[:1]))  # 10 probabilitats que sumen 1
print(model.score(X_test, y_test))      # taxa d'encerts

Avaluar amb més de dues classes

La matriu de confusió passa a ser de N×N: cada fila és la classe real i cada columna la predita. Exemple amb tres flors:

                Predicció
              Rosa  Tulipa  Marguerida
Real   Rosa     2      1        0
      Tulipa    1      3        0
  Marguerida    1      0        2

La diagonal són els encerts. Fora de la diagonal veiem quines classes es confonen entre elles: aquí, el model confon sobretot roses i tulipes.

La precisió, el recall i el F1 es calculen per a cada classe, tractant-la com “aquesta classe contra totes les altres”. classification_report els mostra tots. Com combinar-los en un sol número (mitjanes macro, micro i ponderada) és a Avaluació de models.

Les xarxes neuronals fan servir exactament la mateixa idea a la seva última capa.

Last change: , commit: cc6333d