Tot aquest mòdul — i bona part del curs — ha anat deixant una assignatura pendent: alpha a la regularització, C a la logística i les SVM, K a K-NN, max_depth als arbres, learning rate i n_estimators al boosting, capes i dropout a les xarxes… Fins ara els hem triat a ull o amb bucles manuals com els de 06-05. Aquesta lliçó tanca el mòdul automatitzant aquesta cerca: aprendràs a distingir paràmetres d'hiperparàmetres, a usar GridSearchCV i RandomizedSearchCV sobre el pipeline complet del churn de MercaFresh, a entendre la cerca bayesiana, a evitar la trampa d'avaluar amb la mateixa validació que va triar el guanyador, i a gestionar el teu pressupost de còmput amb seny.
Contingut
- Paràmetres vs. hiperparàmetres
- La cerca manual i els seus límits
- GridSearchCV en detall
- Grid search sobre el pipeline complet del churn
- RandomizedSearchCV: quan és millor mostrejar
- Cerca bayesiana: cercar amb memòria
- Validació niada: no avaluar amb el jutge que va triar
- Bones pràctiques de cerca
- Tancament del mòdul: l'arsenal complet
Paràmetres vs. hiperparàmetres
La distinció és la base de tot:
- Paràmetres: els aprèn l'algorisme de les dades durant
fit. No els tries tu. - Hiperparàmetres: els fixes tu abans d'entrenar; configuren com aprèn l'algorisme.
| Model (lliçó) | Paràmetres (els aprèn fit) |
Hiperparàmetres (els tries tu) |
|---|---|---|
| Regressió lineal (04-01) | Coeficients i intercepte | — (per això vam començar per ella) |
| Ridge/Lasso (07-01) | Coeficients | alpha, l1_ratio |
| Regressió logística (04-02) | Coeficients | C, penalty |
| SVM (04-04) | Vectors de suport i els seus pesos | C, kernel, gamma |
| K-NN (04-05) | — (memoritza el train) | n_neighbors (la K), weights |
| Arbre (04-03) | Les divisions de l'arbre | max_depth, min_samples_leaf |
| Random Forest (07-02) | Tots els arbres | n_estimators, max_features |
| Gradient boosting (07-03) | La seqüència d'arbres | learning_rate, n_estimators, max_depth |
| Xarxa neuronal (04-07, 07-04) | Pesos i biaixos | Nre. de capes i neurones, dropout, learning rate, batch size |
La regla mnemotècnica: si apareix al constructor (Ridge(alpha=...)), és hiperparàmetre; si apareix com a atribut amb guió baix final després d'entrenar (ridge.coef_), és paràmetre après. I el principi metodològic que governa la lliçó: els paràmetres s'ajusten amb el train; els hiperparàmetres es trien amb validació — mai amb el test, que segueix guardat sota clau per al veredicte final (06-01).
La cerca manual i els seus límits
Ja hem fet cerca d'hiperparàmetres tres vegades sense posar-li nom: les corbes de validació sobre max_depth a 06-05, el bucle d'alphas a 07-01 i les combinacions de η/n_estimators a 07-03. El mètode manual — bucle, CV, apuntar resultats — funciona, però escala fatal:
- Explosió combinatòria: amb 4 hiperparàmetres i 5 valors cadascun són 625 combinacions; a mà, impossible.
- Interaccions: el millor
learning_ratedepèn den_estimators(ho vam veure a 07-03); explorar cada control per separat es perd les combinacions creuades. - Errors de disciplina: en bucles artesanals és fàcil ajustar sense voler amb dades de test, o escalar fora de la CV (la fuita de 06-01).
- Irreproduïbilitat: "vaig provar coses i em vaig quedar amb aquesta" no és un procediment auditable.
sklearn empaqueta la solució en dues eines que fan exactament el que fèiem a mà, però de manera exhaustiva, paral·lela i sense fuites.
GridSearchCV en detall
GridSearchCV rep un estimador, una graella (grid) de valors per hiperparàmetre, i una estratègia de CV; entrena i avalua totes les combinacions amb validació creuada i es queda amb la millor.
Les seves peces:
param_grid: diccionari{hiperparàmetre: llista de valors}. El producte cartesià defineix les combinacions.cv: l'estratègia de validació creuada — per al churn, laStratifiedKFoldde 06-03.scoring: la mètrica que decideix (06-02):"f1","roc_auc","neg_root_mean_squared_error"… Triar bé aquesta mètrica és triar què significa "millor"; per al churn desbalancejat de MercaFresh, F1 o AUC, no accuracy.refit=True(per defecte): després de la cerca, reentrena automàticament la millor combinació sobre tot el train — l'objecte resultant ja és un model llest per predir.n_jobs=-1: paral·lelitza en tots els nuclis; les combinacions són independents entre si.
I els seus resultats:
best_params_: la combinació guanyadora.best_score_: la seva puntuació mitjana de CV.cv_results_: la taula completa de la cerca (totes les combinacions, mitjanes, desviacions, temps) — converteix-la en DataFrame i estudia-la: val més que el guanyador sol.best_estimator_: el model reentrenat (sirefit=True).
Grid search sobre el pipeline complet del churn
La regla d'or de 06-03 continua vigent: el que se cerca és el pipeline complet, no el model solt, perquè el preprocessament es reajusti dins de cada fold sense fuites. Els hiperparàmetres d'un pas del pipeline s'anomenen amb la notació pas__parametre (doble guió baix) que ja vam conèixer llavors — i que permet fins i tot tractar decisions de preprocessament com hiperparàmetres més.
import numpy as np
import pandas as pd
from sklearn.model_selection import StratifiedKFold, GridSearchCV, train_test_split
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
# Dataset de churn de MercaFresh (features RFM, com a 07-02/07-03)
rng = np.random.default_rng(42)
n = 1000
recencia = rng.gamma(2, 15, n)
frequencia = rng.poisson(5, n) + 1
monetari = rng.gamma(3, 40, n)
antiguitat = rng.uniform(1, 60, n)
incidencies = rng.poisson(0.5, n)
logits = 0.05 * recencia - 0.4 * frequencia + 0.6 * incidencies - 0.01 * antiguitat - 0.5
y = (rng.random(n) < 1 / (1 + np.exp(-logits))).astype(int)
X = pd.DataFrame({"recencia": recencia, "frequencia": frequencia,
"monetari": monetari, "antiguitat": antiguitat,
"incidencies": incidencies})
# Test apartat ABANS de cap cerca (06-01)
X_entrenament, X_prova, y_entrenament, y_prova = train_test_split(
X, y, test_size=0.2, stratify=y, random_state=42)
pipe = Pipeline([
("scaler", StandardScaler()),
("clf", LogisticRegression(max_iter=5000)),
])
param_grid = {
"clf__C": np.logspace(-3, 2, 6), # 0.001 ... 100, escala log (07-01)
"clf__penalty": ["l1", "l2"], # Lasso o Ridge sobre la logistica
"clf__solver": ["liblinear"], # solver compatible amb totes dues
}
cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
cerca = GridSearchCV(pipe, param_grid, cv=cv, scoring="f1",
n_jobs=-1, refit=True)
cerca.fit(X_entrenament, y_entrenament) # 6 x 2 = 12 combinacions x 5 folds = 60 fits
print("Millor combinacio:", cerca.best_params_)
print(f"Millor F1 (CV): {cerca.best_score_:.3f}")
# La taula completa, per entendre el paisatge i no nomes el pic
resultats = pd.DataFrame(cerca.cv_results_)
print(resultats[["param_clf__C", "param_clf__penalty",
"mean_test_score", "std_test_score"]]
.sort_values("mean_test_score", ascending=False).head())
# Veredicte final: la prova intacta, una sola vegada
from sklearn.metrics import f1_score
print(f"F1 en prova: {f1_score(y_prova, cerca.predict(X_prova)):.3f}")Punts que mereixen relectura:
- El cost real és
combinacions × foldsentrenaments (aquí 60): la graella creix multiplicativament i la CV la multiplica una altra vegada. Pressuposta abans de llançar. cercas'usa després com un model normal (predict,predict_proba): gràcies arefit, a dins porta el millor pipeline reentrenat amb tot el train.- Mira sempre
std_test_score: un "guanyador" per 0.002 de mitjana amb desviacions de 0.03 és un empat tècnic; en aquest cas tria la combinació més simple/regularitzada, no la primera de la taula. - L'F1 de prova sol quedar una mica per sota de
best_score_— i aquesta diferència té nom i secció pròpia més avall.
RandomizedSearchCV: quan és millor mostrejar
La graella exhaustiva mor per combinatòria: amb el gradient boosting de 07-03 voldríem explorar learning_rate, max_iter, max_depth, min_samples_leaf, subsample… 5 valors de cadascun = 3.125 combinacions × 5 folds = 15.625 entrenaments.
RandomizedSearchCV canvia l'estratègia: en lloc de provar-ho tot, mostreja n_iter combinacions a l'atzar de distribucions que tu defineixes. Avantatges decisius:
- Pressupost fix: tu decideixes quants entrenaments pagues (
n_iter=50), sigui quina sigui la mida de l'espai. - Distribucions contínues en lloc de llistes:
loguniform(0.001, 0.3)per al learning rate explora tot el rang, no 5 punts solts. - Millor cobertura del que importa: a la pràctica només uns pocs hiperparàmetres són influents; el mostreig aleatori prova molts valors diferents de cada hiperparàmetre, mentre que la graella malbarata pressupost repetint els mateixos valors de l'hiperparàmetre influent per a cada valor de l'irrellevant.
from sklearn.model_selection import RandomizedSearchCV
from sklearn.ensemble import HistGradientBoostingClassifier
from scipy.stats import loguniform, randint
pipe_gb = Pipeline([("clf", HistGradientBoostingClassifier(random_state=42))])
param_dist = {
"clf__learning_rate": loguniform(0.005, 0.3), # continu, escala log
"clf__max_iter": randint(100, 1000),
"clf__max_depth": randint(2, 8),
"clf__min_samples_leaf": randint(10, 60),
}
atzar = RandomizedSearchCV(pipe_gb, param_dist, n_iter=50, cv=cv,
scoring="f1", n_jobs=-1, random_state=42)
atzar.fit(X_entrenament, y_entrenament) # 50 x 5 = 250 fits, decidit per tu
print(atzar.best_params_, f"F1-CV: {atzar.best_score_:.3f}")Regla pràctica: grid per a espais petits i discrets (2-3 hiperparàmetres amb pocs valors raonables, com l'exemple de la logística); randomized per a tota la resta, i especialment per a boosting i xarxes.
Cerca bayesiana: cercar amb memòria
Grid i randomized comparteixen una ingenuïtat: cada combinació es tria sense mirar els resultats anteriors. La cerca bayesiana (o optimització bayesiana) és l'evolució natural: construeix un model probabilístic de la funció "hiperparàmetres → puntuació" — sí, un model de ML per optimitzar models de ML — i l'usa per decidir quina combinació provar a continuació, equilibrant explotar les zones que pinten bé i explorar les zones incertes. És la mateixa lògica d'actualització de creences amb evidència del teorema de Bayes (02-05), aplicada a la cerca.
Amb pressupostos ajustats (desenes d'avaluacions d'un model car), sol trobar millors combinacions que l'atzar pur. La biblioteca de referència és Optuna (també scikit-optimize); la seva API s'integra bé amb sklearn i afegeix extres com descartar a mig entrenament les combinacions que van malament (pruning). No la desenvolupem aquí: conceptualment ja tens l'essencial, i la seva sintaxi s'aprèn en una tarda quan la necessitis.
| Estratègia | Com tria | Quan usar-la |
|---|---|---|
| Manual (06-05) | La teva intuïció | Exploració inicial, aprenentatge |
| GridSearchCV | Totes les combinacions | Espais petits i discrets |
| RandomizedSearchCV | Mostreig aleatori amb pressupost | Espais grans; opció per defecte |
| Bayesiana (Optuna) | Model que aprèn dels intents previs | Models cars d'entrenar, pressupost escàs |
Validació niada: no avaluar amb el jutge que va triar
Una subtilesa important que ja va treure el cap a l'exemple del grid: best_score_ és una estimació optimista del rendiment real. Per què? Perquè vas provar moltes combinacions sobre la mateixa CV i et vas quedar amb la que millor va puntuar en aquesta CV concreta: part del seu avantatge és mèrit i part és sort amb aquests folds. És el mateix biaix de selecció que a 06-01 ens va portar a separar validació i test — reapareixent un nivell més amunt.
La solució neta és la validació niada (nested CV): un bucle extern de CV per estimar rendiment, i dins de cada fold extern, un bucle intern complet de cerca que tria els hiperparàmetres. Així, les dades que puntuen no van participar mai en l'elecció:
from sklearn.model_selection import cross_val_score
# La cerca sencera (bucle intern) es tracta com un estimador mes
# i s'avalua amb una CV externa que ella no veu mai
f1_niat = cross_val_score(cerca, X_entrenament, y_entrenament, cv=5, scoring="f1")
print(f"F1 honest (niat): {f1_niat.mean():.3f} +/- {f1_niat.std():.3f}")El cost es multiplica (5 × 60 = 300 fits en el nostre exemple), així que a la pràctica es reserva per a quan la xifra de rendiment ha de ser rigorosa — comparar algorismes per a un informe, o estimar què rendirà el model abans de comprometre's amb negoci. Per al dia a dia n'hi ha prou amb l'esquema d'aquesta lliçó: cercar amb CV sobre el train i donar el veredicte final una única vegada sobre el test intacte.
Bones pràctiques de cerca
- De gruixut a fi. Primera passada amb rangs amplis i pocs punts (o
n_itermoderat); miracv_results_, localitza la zona bona i llança una segona passada refinada al seu voltant. Dues cerques barates rendeixen més que una de caríssima. - Escala logarítmica per als multiplicatius.
alpha,C,learning_rate,gammaactuen per ordres de magnitud: usanp.logspace/loguniform. Per als estructurals (max_depth,n_neighbors), escala lineal. - Pressuposta abans de llançar. Calcula
combinacions × folds × temps_per_fiti decideix si t'ho pots permetre; si no, redueix la graella, abaixa folds (5 → 3) o passa a randomized. Mesura primer un fit solt amb%timeitotime.perf_counter. - Cerca menys on importa menys. No tots els hiperparàmetres mereixen graella: a Random Forest,
n_estimatorses fixa alt i prou (07-02); en boosting, l'early stopping trian_estimatorsper tu (07-03) — treu-los de l'espai de cerca. - Fixa
random_statea tot arreu (CV, models, cerca) perquè la cerca sigui reproduïble i les comparacions justes. - Guarda
cv_results_. El paisatge complet diu coses que el guanyador calla: quins hiperparàmetres influeixen de veritat, on el model és robust i on fràgil.
Tancament del mòdul: l'arsenal complet
Aquest mòdul ha respost la pregunta amb què el vam obrir — "puc fer-ho encara millor?" — amb cinc tècniques complementàries:
| Tècnica (lliçó) | Què millora | Quan aplicar-la |
|---|---|---|
| Regularització L1/L2/EN (07-01) | Doma la variància dels models lineals; L1 a més selecciona features | Models lineals amb moltes features o correlacionades; sempre amb escalat |
| Ensembles: bagging/RF, voting, stacking (07-02) | Cancel·la errors descorrelacionats; RF com a baseline forta universal | Gairebé sempre en tabular: comença aquí |
| Gradient boosting (07-03) | Sostre de rendiment en dades tabulars | Quan cada punt de mètrica val diners i pots pagar l'ajust |
| Deep learning (07-04) | Aprèn representacions jeràrquiques | Imatges, àudio, text; tabular només amb dades massives |
| Optimització d'hiperparàmetres (07-05) | Esprem qualsevol de les anteriors de manera sistemàtica i sense fuites | Sempre, amb pressupost proporcional al que hi ha en joc |
I el flux que les uneix per a un problema tabular com el churn de MercaFresh: baseline Dummy (06-02) → model simple interpretable → Random Forest → gradient boosting amb early stopping → cerca d'hiperparàmetres del finalista → veredicte únic sobre el test.
Errors Comuns i Consells
- Cercar hiperparàmetres usant el test. L'error capital. Si el test influeix en qualsevol decisió — hiperparàmetres, features, llindar — deixa de mesurar generalització. Test intacte fins al final, una sola avaluació.
- Passar el model solt en lloc del pipeline. Si escales abans i per fora de la cerca, cada fold de la CV veu estadístiques de la resta: la fuita de 06-01 en versió subtil. Cerca sempre sobre el
Pipelineamb la notaciópas__parametre. - Graelles lineals per a hiperparàmetres multiplicatius.
Ca[1, 2, 3, 4, 5]explora un racó minúscul del rang útil;np.logspace(-3, 2, 6)explora cinc ordres de magnitud amb el mateix cost. - Prendre
best_score_com el rendiment esperat. És optimista per biaix de selecció; el número honest surt del test final o de la validació niada. - Coronar guanyadors per diferències dins del soroll. Compara
mean_test_scoreal costat destd_test_score; en empat tècnic, guanya el model més simple. - Consell: tracta la cerca com un experiment científic: hipòtesi (rangs raonats, no arbitraris), procediment reproduïble (llavors fixades), resultats arxivats (
cv_results_a CSV). El teu jo d'aquí a tres mesos — o l'auditor del model — t'ho agrairà.
Exercicis
- Grid sobre el K-NN del churn. Construeix un pipeline
StandardScaler+KNeighborsClassifieri cerca ambGridSearchCV(scoring="f1", la CV estratificada de la lliçó) sobreclf__n_neighborsa[3, 5, 9, 15, 25, 41]iclf__weightsa["uniform", "distance"]. Reportabest_params_,best_score_i l'F1 en prova. Quants entrenaments ha executat la cerca? - Grid vs. randomized amb el mateix pressupost. Sobre el
HistGradientBoostingClassifierde l'exemple, compara: (a) unGridSearchCVamb 3 valors delearning_rate× 3 demax_depth(9 combinacions) i (b) unRandomizedSearchCVambn_iter=9sobre les distribucions contínues de l'exemple. Mateixcv, mateixscoring. Repeteix (b) amb tresrandom_statediferents. Qui guanya i quina estabilitat hi observes? - La mida de l'optimisme. Calcula per a la cerca de la logística de l'exemple: (a)
best_score_, (b) l'F1 de validació niada ambcross_val_score(cerca, ...), i (c) l'F1 en prova. Ordena les tres xifres i explica cada diferència amb els conceptes de la lliçó.
Solucions
- Estructura:
Pipeline([("scaler", StandardScaler()), ("clf", KNeighborsClassifier())])i la graella amb la notacióclf__. Amb aquestes dades, el guanyador sol rondarn_neighborsentre 15 i 41 ambweights="distance"(el churn té soroll i els veïnatges amplis promitgen millor), amb F1-CV proper al de la logística. Entrenaments: 6 × 2 = 12 combinacions × 5 folds = 60 fits (més 1 de final delrefit). L'F1 de prova hauria de quedar a l'entorn delbest_score_, lleugerament per sota. - Amb només 2 hiperparàmetres ben acotats, el grid és competitiu i de vegades guanya; el randomized queda molt a prop i, segons la llavor, el supera — els seus 9 punts cobreixen valors de learning rate que el grid ni tan sols prova. Entre llavors, el
best_score_del randomized oscil·la (típicament a la 2a-3a xifra decimal): amb unn_itertan baix, l'atzar de quines combinacions surten pesa. Moralitat doble: en espais petits el grid és defensable; així que l'espai creix, la cobertura contínua del randomized guanya — i amb mésn_iter, la seva variància entre llavors s'encongeix. - L'ordre esperat és
best_score_≥ F1 niat ≈ F1 prova. (a) > (b):best_score_porta el biaix de selecció — la combinació guanyadora ho és en part per sort amb aquests folds concrets, i la CV externa del niat, que no va participar en l'elecció, ho descompta. (b) ≈ (c): tots dos són estimacions honestes sobre dades no usades en cap decisió; difereixen només per soroll de mostreig (la prova és una única partició; el niat en promitja cinc). Si en la teva execució (c) surt per sobre de (a), també és lliçó: amb datasets d'aquesta mida, el soroll entre particions pot superar els biaixos que mesurem.
Conclusió
Has tancat el cercle que va obrir el mòdul 6: els paràmetres els aprèn fit, els hiperparàmetres els tria una cerca disciplinada — GridSearchCV per a espais petits, RandomizedSearchCV amb distribucions i pressupost per als grans, cerca bayesiana quan cada entrenament costa car —, sempre sobre el pipeline complet, sempre amb CV sobre el train, amb la validació niada com a àrbitre quan la xifra ha de ser irreprotxable i el test intacte per al veredicte final. Amb això, el mòdul 7 queda complet: regularització per contenir, ensembles i boosting per combinar, xarxes profundes per a les dades no estructurades i optimització sistemàtica per esprémer-ho tot. L'equip de MercaFresh té per fi el seu millor model de churn, afinat i avaluat amb honestedat; però un model que viu en un notebook no reté cap client. El millor model del món no val res si no arriba a producció, i a això dediquem el mòdul 8: frameworks, desplegament, monitoratge i les consideracions ètiques de posar machine learning davant de persones reals.
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
