Al llarg del mòdul ha anat apareixent un mateix símptoma sense nom: k veïns amb k = 1 encertava el 100 % en entrenament i el 81 % en test (04-04); l'arbre sense límit de profunditat, 100 % i 82,5 %; el bosc aleatori, 0,908 en entrenament davant de 0,859 en test (04-05). En tots els casos el model aprenia "massa bé" les dades que veia i pitjor les que no. Aquest fenomen s'anomena sobreajust, i el seu oposat, el model massa simple que no aprèn ni el que hi ha, subajust. Aquesta última lliçó del mòdul explica tots dos amb la intuïció del compromís biaix-variància, ensenya a diagnosticar-los amb corbes d'aprenentatge i de validació, presenta les tècniques per combatre'ls (més dades, menys característiques, regularització L1/L2, poda d'arbres, ensamblatges, aturada anticipada) i, per acabar, aborda amb mètode la pregunta que hem anat ajornant des de 04-01: com triar els hiperparàmetres (GridSearchCV, RandomizedSearchCV, validació imbricada) sense contaminar el test. És important perquè el sobreajust és la manera més comuna de fracassar en producció: un model excel·lent al portàtil de la Marta i mediocre amb les comandes de demà. Tancarem amb el flux complet que la Marta ja domina i amb el pont cap al mòdul 5.
Contingut
- Subajust i sobreajust: l'arbre que memoritza les comandes
- El compromís biaix-variància
- Diagnòstic: corbes d'aprenentatge i corbes de validació
- Tècniques contra el sobreajust
- Regularització L1 i L2 en codi
- Hiperparàmetres davant de paràmetres: com triar-los
- Cerca en graella i aleatòria amb
GridSearchCViRandomizedSearchCV - Validació imbricada i per què el test es toca una sola vegada
- El flux complet del mòdul, d'un cop d'ull
- Errors Comuns i Consells
- Exercicis
- Conclusió
- Subajust i sobreajust: l'arbre que memoritza les comandes
Entrenem arbres de decisió de profunditat creixent sobre el pipeline de 04-03 i mesurem exactitud i AUC en entrenament i en test:
from novamarket_ml import generar_comandes_ml, embrutar_comandes, preparar_comandes, crear_preparacio
from sklearn.model_selection import train_test_split
from sklearn.pipeline import Pipeline
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import roc_auc_score
X, y = preparar_comandes(embrutar_comandes(generar_comandes_ml(3000, 42), 42))
Xtr, Xte, ytr, yte = train_test_split(X, y, test_size=0.25, random_state=42, stratify=y)
print("prof. acc_train acc_test auc_train auc_test fulles")
for prof in [1, 2, 3, 4, 6, 8, 12, None]:
p = Pipeline([("prep", crear_preparacio()),
("model", DecisionTreeClassifier(max_depth=prof, random_state=42))]).fit(Xtr, ytr)
arbre = p.named_steps["model"]
print(f"{str(prof):5s} {p.score(Xtr, ytr):9.3f} {p.score(Xte, yte):8.3f} "
f"{roc_auc_score(ytr, p.predict_proba(Xtr)[:, 1]):9.3f} "
f"{roc_auc_score(yte, p.predict_proba(Xte)[:, 1]):8.3f} {arbre.get_n_leaves():5d}")Sortida:
| Profunditat | Exactitud train | Exactitud test | AUC train | AUC test | Fulles |
|---|---|---|---|---|---|
| 1 | 0,846 | 0,827 | 0,611 | 0,570 | 2 |
| 2 | 0,861 | 0,840 | 0,743 | 0,746 | 4 |
| 3 | 0,878 | 0,849 | 0,799 | 0,793 | 8 |
| 4 | 0,886 | 0,855 | 0,830 | 0,792 | 15 |
| 6 | 0,900 | 0,844 | 0,871 | 0,769 | 38 |
| 8 | 0,924 | 0,840 | 0,924 | 0,693 | 89 |
| 12 | 0,965 | 0,811 | 0,984 | 0,634 | 200 |
| Sense límit | 1,000 | 0,788 | 1,000 | 0,648 | 299 |
Tres zones:
- Profunditat 1-2: subajust (underfitting). El model és massa simple per capturar la relació (una sola pregunta no n'hi ha prou, com vam veure a 04-01): rendeix malament en entrenament i en test.
- Profunditat 3-4: el punt dolç. Entrenament i test van aparellats i l'AUC de test és màxim (0,79).
- Profunditat 6 en endavant: sobreajust (overfitting). El rendiment en entrenament continua pujant fins al 100 % (l'arbre sense límit té 299 fulles per a 2.249 comandes: moltes fulles contenen una sola comanda i la "recorden"), mentre el de test baixa: AUC 0,65, amb prou feines millor que profunditat 2. L'arbre ha après el soroll del sorteig aleatori del generador (04-01), que a les comandes noves és un altre.
La definició pràctica: hi ha sobreajust quan la diferència entre el rendiment en entrenament i en validació creix en augmentar la complexitat del model; hi ha subajust quan tots dos són baixos. I una conseqüència que convé gravar: el rendiment en entrenament no serveix per triar el model; sempre puja amb la complexitat.
- El compromís biaix-variància
L'explicació clàssica descompon l'error d'un model en dues fonts:
- Biaix: error per suposicions massa rígides. Un model amb molt biaix (una recta per a una relació corba, un arbre de profunditat 1) s'equivoca de manera sistemàtica, faci el que faci amb les dades. És el subajust.
- Variància: error per sensibilitat excessiva a les dades concretes d'entrenament. Un model amb molta variància (un arbre sense límit, k = 1) canvia del tot si s'entrena amb una altra mostra, perquè segueix el soroll. És el sobreajust.
La imatge habitual és la diana:
| Variància baixa | Variància alta | |
|---|---|---|
| Biaix baix | Tots els trets al centre: el model ideal | Trets repartits al voltant del centre: encerta de mitjana però cada model concret és impredictible (sobreajust) |
| Biaix alt | Trets agrupats però lluny del centre: s'equivoca sempre igual (subajust) | Trets dispersos i descentrats: el pitjor de tots dos |
En augmentar la complexitat (profunditat, k més petit, més característiques, menys regularització) el biaix baixa i la variància puja; l'error total, suma de tots dos més el soroll irreductible, té forma d'U, i el mínim és el punt dolç que busquem. Mai no s'elimina el soroll: a les nostres dades l'etiqueta se sorteja amb una probabilitat, així que ni el model perfecte encertaria el 100 %; perseguir-ho és la via directa al sobreajust.
- Diagnòstic: corbes d'aprenentatge i corbes de validació
Dues gràfiques responen a les dues preguntes del diagnòstic. Totes dues es calculen amb validació creuada (04-05), mai amb el test.
3.1 Corba d'aprenentatge: ajudarien més dades?
learning_curve entrena el model amb fraccions creixents del conjunt d'entrenament i mesura el rendiment en entrenament i en validació per a cada mida:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.model_selection import learning_curve, StratifiedKFold
from sklearn.linear_model import LogisticRegression
cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
pipe_log = Pipeline([("prep", crear_preparacio()), ("model", LogisticRegression(max_iter=1000))])
tam, auc_tr, auc_va = learning_curve(pipe_log, X, y, cv=cv, scoring="roc_auc",
train_sizes=np.linspace(0.1, 1.0, 6), random_state=42)
print(pd.DataFrame({"n_train": tam, "auc_train": auc_tr.mean(1).round(3), "auc_val": auc_va.mean(1).round(3)}))
plt.plot(tam, auc_tr.mean(1), "o-", label="entrenament")
plt.plot(tam, auc_va.mean(1), "o-", label="validació")
plt.xlabel("comandes d'entrenament"); plt.ylabel("AUC"); plt.legend(); plt.show()Sortida (regressió logística):
n_train auc_train auc_val 0 239 0.921 0.611 1 671 0.842 0.821 2 1103 0.839 0.831 3 1535 0.839 0.835 4 1967 0.831 0.836 5 2399 0.841 0.836
Com es llegeix la gràfica: amb 239 comandes, la corba d'entrenament és molt alta (0,92) i la de validació molt baixa (0,61): amb poques dades, fins i tot la logística sobreajusta. A partir d'unes 1.100 comandes totes dues corbes convergeixen al voltant de 0,83-0,84 i s'aplanen: més dades ja no ajudarien aquest model, i per millorar caldria canviar de família o afegir característiques. Si repeteixes el càlcul amb l'arbre sense límit, la corba d'entrenament es queda en 1,000 per a qualsevol mida i la de validació en 0,65: una bretxa gran i persistent, la signatura del sobreajust, en què més dades sí que ajudarien una mica (la bretxa es tancaria lentament) però el que és eficaç és limitar el model.
Resum de lectura: bretxa gran entre corbes = variància (sobreajust); corbes juntes però baixes = biaix (subajust); corbes juntes i altes = bé.
3.2 Corba de validació: quin valor de l'hiperparàmetre?
validation_curve fixa la mida de les dades i varia un hiperparàmetre:
from sklearn.model_selection import validation_curve
profs = [1, 2, 3, 4, 5, 6, 8, 10, 15, 20]
pipe_arbre = Pipeline([("prep", crear_preparacio()), ("model", DecisionTreeClassifier(random_state=42))])
auc_tr, auc_va = validation_curve(pipe_arbre, X, y, param_name="model__max_depth",
param_range=profs, cv=cv, scoring="roc_auc")
print(pd.DataFrame({"prof": profs, "auc_train": auc_tr.mean(1).round(3), "auc_val": auc_va.mean(1).round(3)}))
# plt.plot(profs, auc_tr.mean(1), "o-", label="entrenament"); plt.plot(profs, auc_va.mean(1), "o-", label="validació")Sortida:
prof auc_train auc_val 0 1 0.672 0.657 1 2 0.756 0.745 2 3 0.804 0.789 3 4 0.833 0.793 4 5 0.854 0.789 5 6 0.881 0.775 6 8 0.938 0.722 7 10 0.976 0.680 8 15 0.999 0.667 9 20 1.000 0.656
A la gràfica, la corba d'entrenament puja monòtonament cap a 1 i la de validació dibuixa una U invertida amb el màxim a profunditat 4 (0,793); a l'esquerra subajust, a la dreta sobreajust. Amb validació creuada, la tria (profunditat 4) és fiable, no fruit d'una divisió afortunada. Fixa't en el nom del paràmetre: "model__max_depth" amb doble guió baix, la sintaxi per arribar a un hiperparàmetre dins d'un Pipeline (pas model, paràmetre max_depth); la farem servir a les cerques.
- Tècniques contra el sobreajust
| Tècnica | Idea | On | Comentari |
|---|---|---|---|
| Més dades | Amb més exemples, el soroll pesa menys | Qualsevol model | Només si la corba d'aprenentatge mostra bretxa; sol ser el més car |
| Menys característiques | Treure columnes sorolloses o redundants (04-03) | Qualsevol model, sobretot k-NN i lineals | Selecció per importància, correlació o L1 |
| Regularització L2 (Ridge) | Penalitzar la suma dels coeficients al quadrat: tots s'encongeixen cap a 0 | Regressió lineal (Ridge, alpha), logística (C = 1/força), SVM (C), xarxes |
Per defecte a LogisticRegression |
| Regularització L1 (Lasso) | Penalitzar la suma de valors absoluts: molts coeficients es fan exactament 0 | Lasso (alpha), logística amb penalty="l1" |
Fa selecció de característiques automàtica |
| Limitar arbres | max_depth, min_samples_leaf, min_samples_split, poda per cost-complexitat (ccp_alpha) |
Arbres i els seus ensamblatges | És el que vam fer a la secció 1 |
| Ensamblatges | Fer la mitjana de molts models d'alta variància (bagging, boscos) | Arbres sobretot | Redueixen variància sense pujar el biaix |
| Aturada anticipada (early stopping) | Aturar l'entrenament iteratiu quan l'error de validació deixa de baixar | Boosting, xarxes neuronals | Central al mòdul 5 |
| Validació creuada | No evita el sobreajust, però el detecta i evita sobreajustar la tria d'hiperparàmetres a una divisió | Sempre | Secció 7 |
| Augment de dades, dropout | Tècniques pròpies del deep learning | Mòdul 5 |
La regularització mereix una explicació a part. En entrenar una logística es minimitza la pèrdua logarítmica (04-01, 04-04). Regularitzar és afegir a aquesta pèrdua una penalització per coeficients grans: pèrdua + λ · Σ coef² (L2) o pèrdua + λ · Σ |coef| (L1). És exactament la tècnica de la penalització que vam fer servir a 03-04 per a l'assignació de comandes: convertir una preferència ("coeficients moderats") en un terme de l'objectiu. Un coeficient enorme vol dir que el model es recolza moltíssim en una columna, sovint per explicar uns pocs exemples sorollosos; penalitzar-lo obliga a explicacions més "repartides" i suaus, que generalitzen millor. A scikit-learn el paràmetre s'anomena alpha a Ridge/Lasso (més alt, més regularització) i C a LogisticRegression/SVC (és la inversa: més baix, més regularització).
- Regularització L1 i L2 en codi
Variem C a la logística del pipeline de 04-03 i observem els coeficients:
from sklearn.model_selection import cross_val_score
print("C auc_train auc_val suma|coef| coef≈0")
for C in [0.001, 0.01, 0.1, 1, 10, 100]:
p = Pipeline([("prep", crear_preparacio()), ("model", LogisticRegression(C=C, max_iter=1000))])
auc_val = cross_val_score(p, Xtr, ytr, cv=cv, scoring="roc_auc").mean()
p.fit(Xtr, ytr)
coef = p.named_steps["model"].coef_[0]
print(f"{C:<7} {roc_auc_score(ytr, p.predict_proba(Xtr)[:, 1]):9.3f} {auc_val:7.3f} "
f"{np.abs(coef).sum():10.2f} {(np.abs(coef) < 0.05).sum():6d}")Sortida:
C auc_train auc_val suma|coef| coef≈0 0.001 0.792 0.781 0.71 17 0.01 0.826 0.812 2.63 11 0.1 0.838 0.828 5.03 7 1 0.839 0.829 5.87 6 10 0.839 0.829 9.61 3 100 0.839 0.829 9.65 3
Amb C = 0,001 (regularització fortíssima) els coeficients gairebé desapareixen (suma 0,71; 17 de 21 pràcticament nuls) i el model subajusta (AUC 0,78). Entre 0,1 i 100 el rendiment en validació és el mateix (0,829): en aquestes dades la logística no sobreajusta apreciablement, així que la regularització amb prou feines importa, llevat que els coeficients creixen (5 → 9,6) sense millorar res. La L2 aquí és una xarxa de seguretat barata més que una necessitat. Amb L1 (LogisticRegression(penalty="l1", solver="liblinear", C=...)) l'efecte és diferent: amb C = 0,05 només queden 7 coeficients diferents de zero (import_comanda, num_articles, dies_lliurament, import_per_article, client_nou, categoria_electronica, categoria_llar) amb un AUC de validació de 0,821, gairebé el del model complet: L1 ha seleccionat característiques automàticament i ha descartat, entre d'altres, les tres columnes de codi_postal_zona, en línia amb el que vam veure a 04-03. Per a regressió, Ridge(alpha=...) i Lasso(alpha=...) fan el mateix amb la demanda; l'exercici 2 ho explora.
- Hiperparàmetres davant de paràmetres: com triar-los
Recordem la distinció de 04-01: els paràmetres (coeficients, llindars dels nodes, centroides) els aprèn fit a partir de les dades; els hiperparàmetres (max_depth, min_samples_leaf, n_estimators, C, alpha, n_neighbors, n_clusters) els fixem abans d'entrenar i controlen la complexitat o el comportament de l'algorisme. No hi ha fórmula per triar-los: cal provar valors i mesurar amb validació, que és el que acabem de fer a mà amb la corba de validació. Els principis:
- Es tria amb validació (validació creuada sobre l'entrenament, o un conjunt de validació a part), mai amb el test.
- Es prova una graella raonable de valors, en escala logarítmica quan el paràmetre és continu (
Cen 0,001, 0,01, 0,1, 1, 10). - Es prefereix, a igualtat de rendiment, el model més simple (menys profunditat, més regularització).
- Es comprova que l'òptim no és a la vora de la graella; si hi és, s'amplia.
- Cerca en graella i aleatòria amb
GridSearchCV i RandomizedSearchCV
GridSearchCV i RandomizedSearchCVGridSearchCV automatitza els principis anteriors: rep un pipeline, un diccionari d'hiperparàmetres amb els valors a provar, una estratègia de validació creuada i una mètrica; entrena totes les combinacions, es queda amb la millor i reentrena el pipeline amb ella sobre tot el conjunt d'entrenament. L'apliquem al bosc aleatori, que a 04-05 va quedar per darrere de la logística i mostrava signes de sobreajust:
from sklearn.model_selection import GridSearchCV
from sklearn.ensemble import RandomForestClassifier
graella = {
"model__n_estimators": [100, 300],
"model__max_depth": [3, 5, 8, None],
"model__min_samples_leaf": [1, 5, 20],
} # 2 x 4 x 3 = 24 combinacions
pipe_rf = Pipeline([("prep", crear_preparacio()), ("model", RandomForestClassifier(random_state=42))])
cerca = GridSearchCV(pipe_rf, graella, cv=cv, scoring="roc_auc", n_jobs=-1)
cerca.fit(Xtr, ytr) # 24 combinacions x 5 folds = 120 entrenaments
print("Millors hiperparàmetres:", cerca.best_params_)
print("AUC de validació creuada:", round(cerca.best_score_, 3))
resultats = pd.DataFrame(cerca.cv_results_)
columnes = ["param_model__max_depth", "param_model__min_samples_leaf", "param_model__n_estimators",
"mean_test_score", "std_test_score", "rank_test_score"]
print(resultats[columnes].sort_values("rank_test_score").head(4).round(3).to_string(index=False))
print(resultats[columnes].sort_values("rank_test_score").tail(2).round(3).to_string(index=False))
# UNA sola vegada, al final: el test
print("AUC en test del millor bosc:", round(roc_auc_score(yte, cerca.predict_proba(Xte)[:, 1]), 3))Sortida:
Millors hiperparàmetres: {'model__max_depth': 5, 'model__min_samples_leaf': 1, 'model__n_estimators': 300}
AUC de validació creuada: 0.819
param_model__max_depth param_model__min_samples_leaf param_model__n_estimators mean_test_score std_test_score rank_test_score
5 1 300 0.819 0.017 1
5 5 300 0.819 0.017 2
5 1 100 0.819 0.016 3
5 5 100 0.817 0.017 4
None 1 300 0.799 0.027 23
None 1 100 0.797 0.027 24
AUC en test del millor bosc: 0.838Lectura:
best_params_diu que la millor combinació és profunditat 5, fulla mínima 1 i 300 arbres;best_score_és el seu AUC mitjà en validació creuada (0,819).cv_results_és una taula amb totes les combinacions: la mitjana i la desviació de la mètrica als 5 folds i el rànquing. Mereix sempre una mirada: les quatre primeres files són a 0,002 les unes de les altres (menys que la desviació, 0,017), així que "profunditat 5" és el que importa i la resta és indiferent; i les pitjors són els boscos sense límit de profunditat, que sobreajusten (0,797 amb una desviació més gran, 0,027).- El bosc amb hiperparàmetres per defecte (sense límit de profunditat) tenia un AUC de validació de 0,797 i de test de 0,811; l'ajustat puja a 0,838 en test. Ajustar els hiperparàmetres ha convertit el bosc en un competidor de la logística (0,844), i amb la funció de cost de 04-05 el seu cost amb llindar 0,2 baixa de 2.830 € (per defecte, llindar 0,5) a 1.860 €.
cercaes comporta com un model ja entrenat (predict,predict_proba): per dins ha reentrenat el millor pipeline amb tot el conjunt d'entrenament. El test s'ha fet servir una sola vegada, a l'última línia.
Quan la graella és gran (cinc hiperparàmetres amb sis valors cadascun són 7.776 combinacions), la cerca exhaustiva és inviable: l'explosió combinatòria de 03-01. RandomizedSearchCV prova n_iter combinacions a l'atzar de distribucions que li indiquem, i a la pràctica troba solucions igual de bones amb una fracció del cost, perquè normalment només importen un o dos hiperparàmetres:
from sklearn.model_selection import RandomizedSearchCV
from scipy.stats import randint
distribucions = {
"model__n_estimators": randint(100, 500), # enter aleatori entre 100 i 499
"model__max_depth": [3, 4, 5, 6, 8, 10, None],
"model__min_samples_leaf": randint(1, 40),
"model__max_features": ["sqrt", 0.3, 0.5],
}
aleatoria = RandomizedSearchCV(pipe_rf, distribucions, n_iter=20, cv=cv, scoring="roc_auc",
random_state=42, n_jobs=-1).fit(Xtr, ytr)
print(aleatoria.best_params_, round(aleatoria.best_score_, 3))
# {'model__max_depth': 5, 'model__max_features': 0.5, 'model__min_samples_leaf': 11,
# 'model__n_estimators': 187} 0.822 -> AUC en test 0.840Amb 20 combinacions (100 entrenaments) troba profunditat 5 de nou i un AUC de validació lleugerament millor (0,822). És la mateixa idea de les metaheurístiques de 03-04: quan no pots recórrer tot l'espai, mostrejar amb intel·ligència. Hi ha mètodes més sofisticats (optimització bayesiana, HalvingGridSearchCV, llibreries com Optuna) que s'esmenten a 07-03.
- Validació imbricada i per què el test es toca una sola vegada
Hi ha una subtilesa que convé entendre encara que no sempre s'apliqui. Quan GridSearchCV prova 24 combinacions i es queda amb la millor, el best_score_ (0,819) és lleugerament optimista: entre 24 candidats, el guanyador ho és en part per mèrit i en part per sort en aquells folds concrets. És un petit sobreajust de la selecció a les dades de validació. Per estimar el rendiment sense aquest biaix es fa servir la validació imbricada (nested cross-validation): un bucle extern de validació creuada, i dins de cada fold extern, un GridSearchCV complet (bucle intern) que tria els hiperparàmetres només amb les dades d'entrenament d'aquell fold. A scikit-learn és una línia: cross_val_score(GridSearchCV(...), X, y, cv=cv_extern). És costosa (5 × 120 entrenaments al nostre exemple) i en projectes aplicats sovint se substitueix per l'esquema més simple de la secció anterior, que és correcte sempre que es respecti la regla final:
El test es toca una sola vegada. Tot el que hem fet en aquesta lliçó (corbes, regularització, graelles) s'ha decidit amb validació creuada sobre el conjunt d'entrenament; el test només ha aparegut al final per donar la xifra que la Marta portarà al Diego (AUC 0,838 del bosc ajustat, 0,844 de la logística). Si haguéssim triat els hiperparàmetres mirant el test, aquesta xifra ja no seria una estimació honesta del que passarà demà, sinó la millor de moltes mirades, i el desplegament portaria una decepció. És la mateixa disciplina que a 04-05 separava entrenament, validació i test.
- El flux complet del mòdul, d'un cop d'ull
La Marta ha recorregut, amb les comandes de NovaMarket, tot el flux que vam dibuixar a 04-01:
flowchart LR
A[Definir T, E, P<br/>04-01, 04-02] --> B[Dades i qualitat<br/>02-03]
B --> C[Preparació en Pipeline<br/>04-03]
C --> D[Triar algorismes<br/>04-04]
D --> E[Validació creuada,<br/>mètriques i cost<br/>04-05]
E --> F[Diagnòstic i ajust<br/>d'hiperparàmetres<br/>04-06]
F -->|no n'hi ha prou| C
F -->|a punt| G[Test una vegada,<br/>llindar, desplegament<br/>i monitoratge<br/>08-01]
| Pas | El que va fer la Marta | Resultat |
|---|---|---|
| Definir el problema | Classificació binària "retornat"; línia base: regla del Diego i "sempre no" | T, E, P clars; costos 5 €/30 € |
| Preparar les dades | embrutar_comandes → neteja, enginyeria, ColumnTransformer + Pipeline |
21 característiques, sense fuites |
| Triar algorismes | Logística, k-NN, arbre, bosc, SVM | Logística i bosc com a candidats |
| Avaluar | Matriu de confusió, AUC, F1, cost; validació creuada estratificada | Logística AUC 0,84; llindar 0,2 (1.610 € davant de 3.690 € de la línia base) |
| Ajustar | Corbes de validació; GridSearchCV del bosc |
Bosc ajustat AUC 0,84; tots dos vàlids |
| Decidir | Test una sola vegada; banda de revisió humana | Model a punt per a 08-01 |
El mateix flux, amb generar_demanda_setmanal, TimeSeriesSplit i MAE en comptes d'AUC, serveix per a la previsió de demanda; i amb generar_clients_ml, el colze i la silueta, per a la segmentació. A 09-02 el practicaràs de cap a cap amb més exercicis.
Errors Comuns i Consells
- Triar el model pel seu rendiment en entrenament. Sempre puja amb la complexitat; només la validació diu la veritat.
- Interpretar malament la corba d'aprenentatge. Corbes juntes i planes: més dades no ajuden, canvia de model o de característiques. Bretxa gran: limita el model o aconsegueix més dades.
- Confondre la direcció de
Cialpha.alphaalt = més regularització;Calt = menys regularització. - Graelles en escala lineal per a paràmetres multiplicatius.
Cen [1, 2, 3, 4] no explora gairebé res; fes servir [0,001, 0,01, 0,1, 1, 10]. - Acceptar un òptim a la vora de la graella. Si el millor és el valor més alt provat, amplia la graella.
- Mirar el test per decidir. Cada mirada el contamina. Graelles i corbes amb validació creuada; test al final, una vegada.
- Ajustar desenes d'hiperparàmetres amb poques dades. El mateix ajust sobreajusta la validació; fes servir graelles petites i, si el resultat importa molt, validació imbricada.
- Oblidar el model simple. Després de tot l'ajust, el bosc (0,838) empata amb la logística sense ajustar (0,844): a igualtat de rendiment, tria el més simple i interpretable.
Exercicis
Exercici 1. Repeteix la corba de validació de la secció 3.2 amb KNeighborsClassifier variant n_neighbors en [1, 3, 5, 9, 15, 25, 51, 101] (paràmetre "model__n_neighbors"). Descriu la forma de les dues corbes: on és el sobreajust, on el subajust i quin és el k òptim? Compara amb la secció 3 de 04-04, on k = 15 semblava el millor amb una sola divisió.
Exercici 2. Sobre la demanda del NovaClean amb les característiques de 04-04 (setmana, sen, cos, black_friday) més cinc columnes de soroll pur (rng.normal(size=(len(d), 5)) amb np.random.default_rng(0)), entrena LinearRegression, Ridge(alpha=10) i Lasso(alpha=5) amb les setmanes 1-78 i avalua el MAE a les 79-104. Quin tracta millor les columnes de soroll? Mira els coeficients de Lasso: quants són exactament zero? Escala les característiques abans (StandardScaler) perquè alpha afecti per igual totes.
Exercici 3. Amplia la cerca en graella del bosc amb "model__max_features": ["sqrt", 0.5] (48 combinacions) i canvia la mètrica a scoring="f1". Canvien els millors hiperparàmetres? Per què pot diferir l'òptim segons la mètrica, i quina triaries per a NovaMarket sabent que la decisió final es pren amb llindar i cost (04-05)?
Solucions
Solució 1. La corba d'entrenament de k-NN comença en 1,000 per a k = 1 (cada comanda és el seu propi veí) i baixa en créixer k (0,93 amb k = 3, 0,85 amb k = 15, 0,81 amb k = 101); la de validació arrenca molt baixa a k = 1 (AUC 0,57: sobreajust pur), puja amb k (0,70 a k = 5, 0,75 a k = 15, 0,77 a k = 25) i s'aplana en 0,79 per a k = 51 i k = 101. En aquest rang encara no apareix la caiguda per subajust; caldria un k de diversos centenars (fer la mitjana de gairebé tot el conjunt) per veure-la. Amb les 21 columnes del pipeline, l'òptim és un k força més gran que el 15 que semblava bo a 04-04 amb 4 columnes: com més columnes sorolloses, més veïns calen per fer la mitjana del soroll; i tot i així k-NN es queda per sota de la logística i del bosc, cosa que confirma la lectura de 04-05.
Solució 2. Amb cinc columnes de soroll i només 26 setmanes de test, els MAE queden propers i l'ordre pot sorprendre: la lineal dona 16,1, Ridge(alpha=10) 18,2 i Lasso(alpha=5) 17,3 (davant de 16,8 del model sense soroll). El que és revelador són els coeficients: la lineal assigna al soroll coeficients de fins a ±4 unitats (que en aquest test concret, per atzar, no fan nosa), Ridge els encongeix però també encongeix el senyal (cos passa de −45 a −39) i Lasso posa exactament a zero quatre dels cinc (el cinquè queda en −0,1) conservant tendència, estacionalitat i Black Friday. La diferència es fa evident en augmentar el soroll: amb 20 columnes de soroll la lineal puja a un MAE de 22 i Ridge a 23, mentre Lasso es manté en 17,6 anul·lant 18 de 20; amb 50 columnes, la lineal arriba a 26, Ridge a 23 i Lasso continua en 17,4 amb 48 de 50 a zero. És la selecció automàtica de característiques de L1. Si puges molt alpha, Lasso comença a anul·lar també coeficients útils i el MAE es dispara: subajust per excés de regularització, l'altra branca de la U. I una lliçó addicional: amb un test de 26 setmanes, diferències d'una o dues unitats de MAE són soroll d'avaluació; per afirmar alguna cosa cal fer servir TimeSeriesSplit (04-05).
Solució 3. Amb scoring="f1" l'òptim canvia: a les nostres proves guanya un bosc sense límit de profunditat amb min_samples_leaf=5, max_features=0.5 i 100 arbres (F1 mitjà 0,51), justament el tipus de configuració que amb AUC quedava a la cua. La raó és que l'F1 es calcula amb el llindar per defecte de 0,5: els boscos poc profunds amb fulles grans produeixen probabilitats moderades que poques vegades superen 0,5, així que marquen poques comandes i el seu recall (i el seu F1) és baix encara que ordenin les comandes per risc millor (més AUC). L'AUC mesura la qualitat de l'ordenació, independent del llindar; l'F1 mesura un punt concret d'aquesta ordenació. Com que a NovaMarket el llindar es triarà després amb la funció de cost i la banda de revisió, el coherent és ajustar els hiperparàmetres amb AUC (o amb precisió mitjana, si es prefereix centrar-se en la classe positiva) i deixar el llindar per al final.
Conclusió
En aquesta lliçó hem posat nom al símptoma que arrossegàvem des de 04-04: el sobreajust (l'arbre sense límit que memoritza les 2.249 comandes amb 299 fulles: AUC 1,0 en entrenament i 0,65 en test) i el seu oposat, el subajust, explicats amb el compromís biaix-variància. Hem après a diagnosticar-los amb les corbes d'aprenentatge (ajuden més dades?) i de validació (quin valor de l'hiperparàmetre?), a combatre'ls amb més dades, menys característiques, regularització L2 i L1 (C, alpha; L1 va seleccionar 7 de 21 columnes), límits als arbres, ensamblatges i aturada anticipada, i a triar els hiperparàmetres amb GridSearchCV i RandomizedSearchCV llegint best_params_ i cv_results_, amb la validació imbricada com a refinament i el test reservat per a una única mirada final. El bosc aleatori, ajustat, ha atrapat la regressió logística, i la Marta disposa ara del flux complet, de la definició del problema al model validat, sobre els tres casos d'ús del mòdul: devolucions, demanda i segmentació.
Amb això tanquem el mòdul 4. Has passat de la definició d'aprenentatge de Mitchell i el primer fit (04-01) als paradigmes supervisat, no supervisat i per reforç (04-02), la preparació de dades en un Pipeline sense fuites (04-03), els algorismes clàssics i les seves intuïcions (04-04), l'avaluació honesta amb matriu de confusió, AUC, cost en euros i validació creuada (04-05) i, en aquesta lliçó, el control del sobreajust i l'ajust d'hiperparàmetres. En tot el mòdul ha reaparegut la idea amb què vam tancar el mòdul 3: aprendre és optimitzar. fit minimitza una pèrdua sobre les dades d'entrenament; regularitzar és afegir una penalització a l'objectiu, com a 03-04; i cercar hiperparàmetres és una cerca, exhaustiva o aleatòria, en un espai que explota combinatòriament. Al mòdul 5, Xarxes Neuronals i Deep Learning, portarem aquestes idees a l'extrem: models amb milers o milions de paràmetres, en què el sobreajust és l'enemic permanent (d'aquí l'aturada anticipada, el dropout i l'augment de dades que hem deixat apuntats) i en què l'optimització es fa, pas a pas, seguint el gradient de la pèrdua: el descens del gradient i la retropropagació que vam anticipar a 03-04 i a la regressió logística de 04-04. La Marta i el Diego, amb el predictor de devolucions ja validat, es preguntaran al mòdul 5 si una xarxa neuronal pot llegir les ressenyes de ressenyes.csv i les fotos de les incidències, coses que els algorismes d'aquest mòdul no saben fer.
Fonaments d'Intel·ligència Artificial (IA)
Mòdul 1: Introducció a la Intel·ligència Artificial
Mòdul 2: Principis Bàsics de la IA
- Conceptes Fonamentals: Agents, Entorns i Racionalitat
- Tipus d'Intel·ligència Artificial
- Les Dades com a Matèria Primera de la IA
- Ètica i Consideracions en IA
Mòdul 3: Algorismes en IA
- Introducció als Algorismes
- Algorismes de Cerca
- Cerca amb Adversari: Jocs i Minimax
- Algorismes d'Optimització
Mòdul 4: Aprenentatge Automàtic (Machine Learning)
- Conceptes Bàsics de Machine Learning
- Tipus d'Aprenentatge Automàtic
- Preparació de Dades i Característiques
- Algorismes de Machine Learning
- Avaluació i Validació de Models
- Sobreajust, Regularització i Ajust d'Hiperparàmetres
Mòdul 5: Xarxes Neuronals i Deep Learning
- Introducció a les Xarxes Neuronals
- Arquitectura de Xarxes Neuronals
- Com Aprèn una Xarxa: Descens del Gradient i Retropropagació
- Deep Learning i les seves Aplicacions
- Transformers, Grans Models de Llenguatge i IA Generativa
Mòdul 6: Lògica i Sistemes Experts
- Lògica en IA
- Sistemes Experts
- Raonament amb Incertesa: Probabilitat i Xarxes Bayesianes
- Aplicacions dels Sistemes Experts
Mòdul 7: Eines i Llenguatges de Programació en IA
- Llenguatges de Programació per a IA
- Python Científic: NumPy, pandas i Matplotlib
- Eines i Llibreries Populars
- Entorns de Desenvolupament
Mòdul 8: Projectes i Casos d'Estudi
Mòdul 9: Exercicis i Pràctiques
- Exercicis d'Algorismes
- Pràctiques de Machine Learning
- Projectes de Xarxes Neuronals
- Projecte Integrador: de la Idea al Prototip
