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

  1. Paràmetres vs. hiperparàmetres
  2. La cerca manual i els seus límits
  3. GridSearchCV en detall
  4. Grid search sobre el pipeline complet del churn
  5. RandomizedSearchCV: quan és millor mostrejar
  6. Cerca bayesiana: cercar amb memòria
  7. Validació niada: no avaluar amb el jutge que va triar
  8. Bones pràctiques de cerca
  9. 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_rate depèn de n_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, la StratifiedKFold de 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 (si refit=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 × folds entrenaments (aquí 60): la graella creix multiplicativament i la CV la multiplica una altra vegada. Pressuposta abans de llançar.
  • cerca s'usa després com un model normal (predict, predict_proba): gràcies a refit, 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

  1. De gruixut a fi. Primera passada amb rangs amplis i pocs punts (o n_iter moderat); mira cv_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.
  2. Escala logarítmica per als multiplicatius. alpha, C, learning_rate, gamma actuen per ordres de magnitud: usa np.logspace / loguniform. Per als estructurals (max_depth, n_neighbors), escala lineal.
  3. Pressuposta abans de llançar. Calcula combinacions × folds × temps_per_fit i 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 %timeit o time.perf_counter.
  4. Cerca menys on importa menys. No tots els hiperparàmetres mereixen graella: a Random Forest, n_estimators es fixa alt i prou (07-02); en boosting, l'early stopping tria n_estimators per tu (07-03) — treu-los de l'espai de cerca.
  5. Fixa random_state a tot arreu (CV, models, cerca) perquè la cerca sigui reproduïble i les comparacions justes.
  6. 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 Pipeline amb la notació pas__parametre.
  • Graelles lineals per a hiperparàmetres multiplicatius. C a [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_score al costat de std_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

  1. Grid sobre el K-NN del churn. Construeix un pipeline StandardScaler + KNeighborsClassifier i cerca amb GridSearchCV (scoring="f1", la CV estratificada de la lliçó) sobre clf__n_neighbors a [3, 5, 9, 15, 25, 41] i clf__weights a ["uniform", "distance"]. Reporta best_params_, best_score_ i l'F1 en prova. Quants entrenaments ha executat la cerca?
  2. Grid vs. randomized amb el mateix pressupost. Sobre el HistGradientBoostingClassifier de l'exemple, compara: (a) un GridSearchCV amb 3 valors de learning_rate × 3 de max_depth (9 combinacions) i (b) un RandomizedSearchCV amb n_iter=9 sobre les distribucions contínues de l'exemple. Mateix cv, mateix scoring. Repeteix (b) amb tres random_state diferents. Qui guanya i quina estabilitat hi observes?
  3. 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 amb cross_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

  1. Estructura: Pipeline([("scaler", StandardScaler()), ("clf", KNeighborsClassifier())]) i la graella amb la notació clf__. Amb aquestes dades, el guanyador sol rondar n_neighbors entre 15 i 41 amb weights="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 del refit). L'F1 de prova hauria de quedar a l'entorn del best_score_, lleugerament per sota.
  2. 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 un n_iter tan 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és n_iter, la seva variància entre llavors s'encongeix.
  3. 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

Mòdul 2: Fonaments d'Estadística i Probabilitat

Mòdul 3: Preprocessament de Dades

Mòdul 4: Algorismes de Machine Learning Supervisat

Mòdul 5: Algorismes de Machine Learning No Supervisat

Mòdul 6: Avaluació i Validació de Models

Mòdul 7: Tècniques Avançades i Optimització

Mòdul 8: Implementació i Desplegament de Models

Mòdul 9: Projectes Pràctics

Mòdul 10: Recursos Addicionals

© Copyright 2026. Tots els drets reservats