Aprenentatge supervisat
- Introducció
- Terminologia
- Funció de cost
- Descens del gradient
- Regressió lineal múltiple
- Escalat de característiques
- Enginyeria de característiques
- Regressió lineal amb scikit-learn
- Com sabem si el model és bo?
- Sobreajustament i subajustament
- Després
Introducció
L’aprenentatge automàtic (machine learning) és una branca de la intel·ligència artificial que permet als algoritmes aprendre a partir de l’experiència, sense ser programats explícitament per a cada cas.
En l’aprenentatge supervisat, l’algoritme aprèn a partir de dades etiquetades: exemples on ja sabem la resposta correcta. Per contra, l’aprenentatge no supervisat treballa amb dades sense etiquetes i hi busca patrons.
En l’aprenentatge supervisat:
- L’algoritme troba una línia o corba que relaciona l’entrada amb la sortida.
- Aprèn d’exemples d’entrada i sortida coneguts.
- L’objectiu és generalitzar: fer bones prediccions sobre dades noves.
Regressió i classificació
| Regressió | Classificació | |
|---|---|---|
| Prediu | Un número (infinits valors possibles) | Una categoria (un conjunt finit de valors) |
| Exemple | El preu d’una casa a partir de la seva mida | Si un correu és spam o no |
| On es veu | En aquest document | A Classificació |
| Què aprèn | Una línia o corba que s’ajusta a les dades | Fronteres que separen les categories |
Terminologia
- Dataset d’entrenament: el conjunt de dades amb què entrenem el model.
- x: variable d’entrada o característica (feature).
- y: variable de sortida o objectiu (target).
- (xᵢ, yᵢ): l’exemple i-èssim, és a dir, una fila del dataset.
- m: nombre d’exemples d’entrenament.
- ŷ (y amb accent circumflex): la predicció del model, que comparem amb el valor real y.
Model lineal
El model més senzill per a una variable és una recta:
\[ f(x) = wx + b \]
- w: el pes (weight), la pendent de la recta.
- b: el biaix (bias), el punt on la recta talla l’eix vertical.
Entrenar el model vol dir trobar els valors de w i b que fan que la recta s’ajusti millor a les dades.
Al gràfic següent, l’eix horitzontal és la característica i l’eix vertical l’etiqueta. Com que el model és lineal, es representa com una recta.
Funció de cost
Per ajustar el model necessitem un número que digui com d’equivocat està. La funció de pèrdua mesura l’error d’una sola predicció, i la funció de cost en fa la mitjana sobre tots els exemples. Com més petit és el cost, millors són les prediccions.
La funció de cost més utilitzada per a regressió lineal és l’error quadràtic:
\[ J(w, b) = \frac{1}{2m} \sum_{i=1}^m \bigl(f(x_i) - y_i \bigr)^2 \]
- J(w, b): el cost per a uns valors concrets de w i b
- m: nombre d’exemples d’entrenament
- f(xᵢ): predicció per a l’exemple i
- yᵢ: valor real de l’exemple i
En paraules senzilles: calculem l’error de cada predicció, l’elevem al quadrat i en fem la mitjana.
Elevar al quadrat fa que els errors positius i negatius no es cancel·lin i castiga més els errors grans. Dividir per m fa que el cost no depengui del nombre d’exemples, i el 2 només simplifica els càlculs posteriors.
L’objectiu de l’entrenament és minimitzar J(w, b). Si només canviem w, el cost té forma de U. Si canviem w i b alhora, obtenim una superfície en forma de bol. El punt més baix són els millors paràmetres.
Aquesta és la representació quan tenim dues característiques:
Descens del gradient
El descens del gradient (gradient descent) és l’algoritme que troba els valors de w i b que minimitzen el cost:
- Comença amb w = 0 i b = 0.
- Mira cap a on baixa el cost.
- Fa un petit pas en aquella direcció.
- Repeteix fins que ja no pot baixar més.
La derivada: cap a on baixa
Una derivada ens diu com de ràpid canvia una cosa. Imagina que vas en una muntanya russa:
- L’altura \( f(x) \) et diu on ets.
- La derivada et diu com de ràpid puges o baixes, és a dir, la pendent:
- Si és positiva, puges.
- Si és negativa, baixes.
- Si és 0, ets en un punt pla.
La gràfica mostra la corba \( f(x) = x^2 \) i, amb línia de punts, la tangent al punt que tries amb el control lliscant. La pendent d’aquesta tangent és la derivada en aquell punt.
El cost J(w, b) depèn de dues variables. La derivada parcial respecte de w diu com canvia el cost si només movem w i deixem b fix: és la pendent en una sola direcció. El descens del gradient en fa servir dues, una per a w i una per a b, i s’escriuen \( \frac{\partial J}{\partial w} \) i \( \frac{\partial J}{\partial b} \).
L’algoritme
\[ w := w - \alpha \frac{\partial J}{\partial w} \qquad b := b - \alpha \frac{\partial J}{\partial b} \]
- ∂J/∂w i ∂J/∂b: la pendent del cost respecte de cada paràmetre
- α (alfa): la taxa d’aprenentatge (learning rate), la mida del pas
- := vol dir “substitueix el valor per”
En paraules senzilles: mira cap a on puja la muntanya (la derivada) i fes un pas cap a l’altra banda (la resta). w i b s’actualitzen alhora, amb els valors del pas anterior.
A mesura que ens apropem al mínim la pendent es fa més petita, i els passos també, sense que calgui fer res.
Les derivades del cost
Per a l’error quadràtic, les dues derivades parcials són:
\[ \frac{\partial J}{\partial w} = \frac{1}{m} \sum_{i=1}^m \bigl(f(x_i) - y_i\bigr), x_i \qquad \frac{\partial J}{\partial b} = \frac{1}{m} \sum_{i=1}^m \bigl(f(x_i) - y_i\bigr) \]
- f(xᵢ) − yᵢ: l’error de la predicció i
- xᵢ: el valor de la característica a l’exemple i
En paraules senzilles: cada exemple empeny els paràmetres en proporció al seu error. Si el model prediu massa alt (errors positius), la derivada respecte de b és positiva i el pas la resta: b baixa i les prediccions també. En derivar el quadrat surt un 2 que cancel·la el 2 del denominador del cost, i per això el cost es defineix amb \( \frac{1}{2m} \).
Taxa d’aprenentatge
- α massa petita: funciona, però molt a poc a poc.
- α massa gran: el pas “salta” el mínim i el cost pot créixer en lloc de baixar.
- α adequada: arriba al mínim de manera eficient.
Per comprovar-ho, es dibuixa el cost al llarg de les iteracions. Si baixa i s’aplana, l’entrenament ha convergit; un criteri automàtic habitual és aturar-se quan el cost canvia menys d’un valor petit (per exemple, 0.001) entre dues iteracions. Si puja o oscil·la, α és massa gran. Una seqüència típica de valors per provar és 0.001, 0.003, 0.01, 0.03, 0.1, 0.3.
Mínims locals i globals
- Mínim local: el punt més baix d’una vall concreta.
- Mínim global: el punt més baix de tota la funció.
Segons on comencem, podem acabar en un mínim local o en un altre.
La regressió lineal amb error quadràtic té una propietat especial: el seu cost té forma de bol (és convex) i només té un mínim, que és el global. El descens del gradient sempre hi arriba, comenci on comenci.
Variants per a datasets grans
La versió que hem vist fa servir tots els exemples a cada pas (batch gradient descent). Amb datasets grans es fan servir variants que només en miren una part a cada pas (per mini-lots o estocàstic, SGD), que veurem a les xarxes neuronals.
Regressió lineal múltiple
Normalment tenim més d’una característica: per exemple, la mida, el nombre d’habitacions i l’edat d’una casa. El model suma la contribució de cadascuna:
\[ f(x) = w_1 x_1 + w_2 x_2 + \dots + w_n x_n + b \]
- x₁ … xₙ: les n característiques
- w₁ … wₙ: un pes per a cada característica
Aquesta suma de productes s’anomena producte escalar (dot product) i s’escriu \( \vec{w} \cdot \vec{x} + b \).
Amb dues característiques, el model ja no és una recta sinó un pla que passa entre els punts:
Quan les dues característiques estan correlacionades (per exemple, la mida d’una casa i el nombre d’habitacions, que solen créixer juntes), els punts queden concentrats en una franja estreta, i és més difícil separar l’efecte de cadascuna:
El producte escalar es pot vectoritzar: calcular sense bucles, amb una llibreria numèrica optimitzada. Per exemple, amb tres característiques (mida, habitacions, edat):
import numpy as np
w = np.array([1.2, 15.0, -0.8]) # un pes per característica
b = 40.0
x = np.array([90, 3, 25]) # una casa: 90 m², 3 habitacions, 25 anys
# Sense vectorització: un bucle
f = 0
for j in range(len(w)):
f += w[j] * x[j]
f += b
# Amb vectorització: una sola operació
f = np.dot(w, x) + b # o també: w @ x + b
print(f) # 173.0
Les dues versions donen el mateix resultat, però np.dot fa el càlcul en codi optimitzat. Amb un milió de valors, la versió vectoritzada és unes 500 vegades més ràpida que el bucle.
El descens del gradient funciona igual: a cada pas actualitza cada pes amb la seva pròpia derivada parcial.
Notació i fórmules per a diverses variables
Per referir-nos a un exemple concret, s’escriu el número entre parèntesis a dalt: \( \vec{x}^{(2)} \) són totes les característiques de la casa número 2, i \( x_3^{(2)} \) només l’edat de la casa número 2.
L’actualització del descens del gradient per a cada pes és:
\[ w_j := w_j - \alpha \frac{\partial J(\vec{w}, b)}{\partial w_j} \quad \text{per a } j = 1 \dots n \qquad b := b - \alpha \frac{\partial J(\vec{w}, b)}{\partial b} \]
Escalat de característiques
Imagina que vols predir el preu d’una casa amb dues característiques:
- Mida: entre 50 i 300 m²
- Habitacions: entre 1 i 5
Quan les escales són tan diferents, el descens del gradient avança en zig-zag en lloc d’anar directe al mínim, i l’entrenament és molt més lent. La solució és transformar les característiques perquè tinguin escales semblants.
| Mètode | Què fa | Quan usar-lo |
|---|---|---|
| Estandardització (z-score) | Mitjana 0 i desviació estàndard 1 | Per defecte. És el que fa StandardScaler |
| Escalat mínim-màxim (min-max) | Porta els valors a l’interval [0, 1] | Quan necessites un rang fix. Sensible als outliers |
L’estandardització calcula, per a cada valor:
\[ x^{\text{std}} = \frac{x - \mu}{\sigma} \]
- μ: la mitjana de la característica
- σ: la seva desviació estàndard
En paraules senzilles: restem la mitjana i dividim per la dispersió. La majoria de valors queden entre -3 i 3.
L’escalador s’ajusta només amb les dades d’entrenament. Si fa servir també les de test, hi ha fuga de dades.
Terminologia i altres fórmules
Els termes s’utilitzen sovint com a sinònims, però tècnicament:
- Escalat (scaling): portar cada característica a un rang predefinit, com [0, 1].
- Normalització (normalization): en sentit ampli, qualsevol transformació d’aquest tipus.
- Estandardització (standardization): el cas concret de mitjana 0 i desviació estàndard 1.
Escalat mínim-màxim:
\[ x^{\text{esc}} = \frac{x - \min(x)}{\max(x) - \min(x)} \]
Normalització per la mitjana, que centra les dades al voltant de 0 amb valors aproximadament entre -0.5 i 0.5:
\[ x^{\text{norm}} = \frac{x - \mu}{\max(x) - \min(x)} \]
Enginyeria de característiques
Sovint podem millorar el model creant característiques noves a partir de les que tenim, fent servir el que sabem del problema. Això és l’enginyeria de característiques (feature engineering).
Exemple: si tenim la façana (10 m) i el fons (20 m) d’un terreny, podem crear superfície = façana × fons (200 m²). Aquesta característica nova sol ser més informativa que les dues originals per separat.
Regressió polinòmica
Si les dades segueixen una corba i no una recta, podem afegir característiques com \( x^2 \) o \( x^3 \):
\[ f(x) = w_1 x + w_2 x^2 + b \]
El model continua sent lineal en els pesos, però ara dibuixa una paràbola. Com que \( x^2 \) creix molt de pressa (\( 100^2 = 10000 \)), aquí l’escalat és imprescindible.
Altres transformacions
Els polinomis no són l’única opció. Si el que sabem del problema ho suggereix, podem transformar una característica d’altres maneres:
- Logaritme, \( \log(x) \): quan els efectes són proporcionals. Passar de 1.000 € a 2.000 € d’ingressos té un efecte semblant a passar de 10.000 € a 20.000 €.
- Arrel quadrada, \( \sqrt{x} \): quan l’efecte creix cada cop més a poc a poc.
Comença sempre pel model més simple. Ajusta primer una recta i mira els errors de les prediccions. Si els errors tenen un patró (per exemple, el model s’equivoca sempre per sota als valors grans), afegeix una transformació o un terme polinòmic. Si els errors semblen aleatoris, el model ja capta la relació i afegir complexitat només augmenta el risc de sobreajustament.
Regressió lineal amb scikit-learn
Volem predir el preu d’una casa. El fitxer houses.txt té una capçalera i les columnes size(sqft),bedrooms,floors,age,price, separades per comes. L’última és la variable objectiu.
import numpy as np
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
# Carregar el conjunt de dades
data = np.loadtxt("./data/houses.txt", delimiter=",", skiprows=1)
X = data[:, :4]
y = data[:, 4]
# Guardar un 20% de les dades per avaluar
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# Escalar i entrenar en un sol pas
model = make_pipeline(StandardScaler(), LinearRegression())
model.fit(X_train, y_train)
# Paràmetres apresos (sobre les dades escalades)
reg = model.named_steps["linearregression"]
print(f"w: {reg.coef_}, b: {reg.intercept_}")
# Prediccions sobre dades que el model no ha vist
y_pred = model.predict(X_test)
print(f"Prediccions: {y_pred[:4]}")
print(f"Valors reals: {y_test[:4]}")
make_pipeline encadena l’escalat i el model: quan crides fit, l’escalador aprèn la mitjana i la desviació només amb les dades d’entrenament. LinearRegression troba els pesos directament. Per a datasets molt grans, SGDRegressor fa el mateix amb descens del gradient estocàstic.
El gràfic següent mostra cada característica respecte del preu real (punts blaus) i del preu predit (punts taronges), amb la tendència en vermell. Volem que els punts taronges quedin a prop dels blaus.
Com sabem si el model és bo?
La funció de cost serveix per entrenar, però per saber si el model funciona necessitem dues coses més:
- Dades que el model no ha vist. Per això, a l’exemple anterior,
train_test_splitha guardat un 20% de les dades per a test. Un model sempre sembla bo amb les dades amb què s’ha entrenat. - Mètriques que es puguin interpretar. En regressió, les habituals són tres:
| Mètrica | Pregunta que respon | Exemple |
|---|---|---|
| MAE | De mitjana, quant m’equivoco? | “M’equivoco en 15.000 € de mitjana” |
| RMSE | El mateix, però castigant més els errors grans | Útil quan un error gran és especialment greu |
| R² | Quina part de la variació explica el model? (1 és perfecte, 0 és com predir sempre la mitjana) | “El model explica el 80% de la variació del preu” |
El RMSE fa la mitjana dels errors al quadrat i en treu l’arrel:
\[ \text{RMSE} = \sqrt{\frac{1}{m} \sum_{i=1}^m (y_i - \hat{y}_i)^2} \]
- yᵢ: valor real, ŷᵢ: predicció, m: nombre d’exemples
En paraules senzilles: és l’error típic, en les mateixes unitats que el que predim (euros, graus…).
from sklearn.metrics import mean_absolute_error, root_mean_squared_error, r2_score
print(f"MAE: {mean_absolute_error(y_test, y_pred):.0f}")
print(f"RMSE: {root_mean_squared_error(y_test, y_pred):.0f}")
print(f"R²: {r2_score(y_test, y_pred):.2f}")
Les fórmules del MAE i del R², i com triar entre les tres, són a Avaluació de models.
Sobreajustament i subajustament
Hi ha dos problemes que poden aparèixer quan entrenem un model:
- Subajustament (underfitting): el model és massa simple i no capta els patrons de les dades. Es diu que té biaix alt.
- Sobreajustament (overfitting): el model és massa complex i s’aprèn de memòria les dades d’entrenament, soroll inclòs. Es diu que té variància alta: petits canvis a les dades d’entrenament canvien molt el model.
Analogia: el subajustament és no estudiar prou per a un examen. El sobreajustament és memoritzar les preguntes dels exàmens anteriors: les encertes totes, però fallaràs les preguntes noves perquè no has entès els conceptes.
Exemple: un polinomi de grau 10 ajustat a només 5 punts pot passar exactament per tots els punts (error zero!), però la corba serà tan retorçada que fallarà amb qualsevol dada nova.
Com ho detectem
Comparant dos errors:
| Error d’entrenament | Error amb dades noves | Diagnòstic |
|---|---|---|
| Alt | Alt | Biaix alt (subajustament) |
| Baix | Alt | Variància alta (sobreajustament) |
| Baix | Baix | Correcte |
Per triar el grau del polinomi provem diversos graus i ens quedem amb el que dona menys error amb dades noves. Però si fem aquesta tria amb el test, el test deixa de ser una mesura honesta. Per això les dades es divideixen en tres parts: entrenament, validació (per triar) i test (per mesurar al final). Ho expliquem a Separar les dades.
Com ho evitem
- Més dades d’entrenament.
- Un model més simple: menys característiques o polinomis de grau més baix.
- Regularització: penalitzar els pesos grans durant l’entrenament. Un model amb pesos petits és més suau i generalitza millor. La força de la penalització la controla un paràmetre, λ (lambda), que es tria amb el conjunt de validació. Els detalls són a Regularització.
Després
La classificació fa servir les mateixes idees (un model amb pesos, una funció de cost i el descens del gradient) per predir categories en lloc de números.