Monitoratge i reentrenament
- Per què canvien les dades
- Tipus de drift
- Què cal registrar
- Detectar el drift
- Què vigilar
- Reentrenar
- Provar en producció
- Practicar amb un dataset fix
- Resum
- Referències
Un model desplegat no falla com falla el codi. No llança cap excepció: continua responent, però les seves prediccions empitjoren a poc a poc perquè el món ha canviat i les dades ja no s’assemblen a les de l’entrenament. Aquest document explica què cal registrar, com detectar-ho i com actualitzar el model quan cal.
Per què canvien les dades
Imagina un model que valora cotxes de segona mà entrenat amb anuncis del 2023. Un any després:
- Hi ha més cotxes elèctrics, i el model n’havia vist pocs.
- La inflació ha pujat tots els preus un 8%.
- L’aplicació ha afegit un camp nou i ha canviat el format d’un altre.
Res d’això trenca l’API, però totes tres coses fan que les prediccions s’allunyin dels preus reals. Aquest fenomen s’anomena drift (deriva): un canvi entre les dades d’entrenament i les de producció.
Tipus de drift
Distingim tres tipus segons què canvia. Per descriure’ls, anomenem entrades les features que rep el model i sortida el que prediu.
Covariate shift (canvi en les entrades): canvia la distribució de les features, però la relació entre entrades i sortida es manté. Exemple: arriben molts més cotxes elèctrics. Un elèctric de 50.000 km continua valent el mateix que abans, però el model n’ha vist pocs i les seves prediccions per a aquest grup són poc fiables.
Label shift (canvi en la sortida): canvia la proporció dels resultats. Exemple: un model de diagnòstic entrenat amb un 5% de casos positius rep, durant un brot, un 20% de positius. Els seus llindars estaven calibrats per a una situació diferent.
Concept drift (canvi de concepte): canvia la relació entre entrades i sortida. Les mateixes features porten a un resultat diferent. Exemple: després d’una crisi, clients amb bons ingressos i bon historial ja no paguen els préstecs com abans.
| Tipus | Què canvia | Com es detecta |
|---|---|---|
| Covariate shift | Distribució de les features | Comparant les features de producció amb les de referència |
| Label shift | Proporció de resultats | Comparant la distribució de les prediccions al llarg del temps |
| Concept drift | La relació entre features i resultat | Només amb el resultat real: comparant prediccions amb el que ha passat de veritat |
El resultat real verificat s’anomena ground truth (veritat de referència). En valoració de cotxes, és el preu al qual s’ha venut finalment el cotxe; en frau, la confirmació que la transacció era fraudulenta. Sovint arriba amb dies o setmanes de retard, o no arriba mai. Per això els mètodes estadístics que veurem detecten sobretot el covariate shift: és l’únic que es pot veure només mirant les entrades.
En un sistema LLM el canvi equivalent és que el model mateix canviï de comportament davant d’entrades similars; es detecta amb evals, no amb tests estadístics (vegeu Detecció de regressions).
Què cal registrar
Per detectar qualsevol canvi, primer cal saber què ha rebut i què ha respost el model. Cada predicció s’ha de guardar amb la informació que permeti analitzar-la després:
| Camp | Per a què serveix |
|---|---|
timestamp | Agrupar per finestres de temps |
request_id | Trobar una predicció concreta i lligar-la amb el ground truth quan arribi |
features | Detectar covariate shift i reproduir la predicció |
prediction | Detectar canvis en la distribució de les prediccions |
model_version | Comparar versions i saber quina va fer cada predicció |
latency_ms | Vigilar el temps de resposta |
El format més senzill és un fitxer JSON Lines (.jsonl), amb una predicció per línia, fàcil de llegir amb pandas:
{"timestamp": "2026-10-01T10:30:45", "request_id": "a1b2c3", "features": {"year": 2015, "odometer": 80000, "manufacturer": "toyota"}, "prediction": 14250.0, "model_version": "v2", "latency_ms": 45.2}
Aquest registre són dades de l’aplicació, no un log del servei: ha de sobreviure als redesplegaments, així que va fora del contenidor, en un volum o una base de dades (vegeu Docker Compose). Un fitxer JSON Lines és el més senzill per començar; amb molt volum o diverses instàncies, una base de dades com SQLite o PostgreSQL és més pràctica.
Privacitat: no guardis dades personals sense necessitat. Si cal un identificador d’usuari per relacionar prediccions, guarda’n un hash:
import hashlib
SENSITIVE = {"email", "phone"}
def anonymize(features: dict) -> dict:
"""Elimina els camps sensibles i substitueix l'identificador d'usuari pel seu hash."""
safe = {k: v for k, v in features.items() if k not in SENSITIVE}
if "user_id" in safe:
safe["user_id"] = hashlib.sha256(str(safe["user_id"]).encode()).hexdigest()[:16]
return safe
Detectar el drift
Per detectar drift, comparem dues mostres: les dades de referència (el production set, o un període en què el model funcionava bé) i les dades recents de producció.
flowchart LR
A[Referència<br/>production set] --> C[Comparar]
B[Recents<br/>última setmana] --> C
C --> D{Drift?}
D -->|Sí| E[Alerta]
D -->|No| F[Tot correcte]
La finestra recent pot ser d’hores, dies o setmanes. Una finestra massa curta té poques dades i dona falses alarmes; una de massa llarga detecta els canvis tard.
Test de Kolmogorov-Smirnov
El test KS respon una pregunta: aquestes dues mostres d’una variable numèrica podrien venir de la mateixa distribució? Retorna un p-valor: com més baix, menys probable és que les dues mostres siguin iguals.
from scipy import stats
statistic, p_value = stats.ks_2samp(reference["odometer"], recent["odometer"])
if p_value < 0.05:
print(f"Drift in odometer (p={p_value:.4f})")
Per a variables categòriques (fabricant, combustible), el test chi-quadrat (scipy.stats.chisquare) compara les freqüències de cada categoria.
Amb moltes dades, aquests tests detecten diferències minúscules que no afecten el model. Per això el p-valor sol, sense mirar la magnitud del canvi, dona massa alarmes.
Population Stability Index
El PSI mesura quant ha canviat una distribució, i té llindars coneguts. La idea: es divideix la variable en trams (per exemple, deu trams de quilometratge), es calcula quin percentatge de dades cau a cada tram a la referència i a les dades recents, i se sumen les diferències.
import numpy as np
def calculate_psi(reference, current, bins: int = 10) -> float:
"""Population Stability Index entre dues mostres d'una feature numèrica."""
# Límits dels trams a partir de la distribució de referència
edges = np.percentile(reference, np.linspace(0, 100, bins + 1))
edges[0], edges[-1] = -np.inf, np.inf
ref_pct = np.histogram(reference, edges)[0] / len(reference)
cur_pct = np.histogram(current, edges)[0] / len(current)
# Evita log(0) als trams buits
ref_pct = np.clip(ref_pct, 1e-4, None)
cur_pct = np.clip(cur_pct, 1e-4, None)
return float(np.sum((cur_pct - ref_pct) * np.log(cur_pct / ref_pct)))
| PSI | Interpretació |
|---|---|
| Menys de 0,1 | Canvi insignificant |
| De 0,1 a 0,2 | Canvi moderat: cal investigar |
| Més de 0,2 | Canvi significatiu: cal actuar |
Simular drift per provar el codi
Abans de tenir dades de producció, com saps si el codi de detecció funciona? Creant un drift conegut: agafes una còpia del production set, la modifiques a propòsit i comproves que el detector reacciona.
| Escenari | Modificació | Què esperes |
|---|---|---|
| Sense drift | Les dades originals | Cap alarma |
| Lleu | year + 1 | Potser cap alarma: ajusta el llindar |
| Moderat | odometer × 1,15 | Alarma de nivell “investigar” |
| Fort | odometer × 2, un 30% de “gas” passa a “electric” | Alarma clara |
Aquests escenaris es poden convertir en tests automàtics del detector. És una eina per provar el codi i calibrar els llindars, no un substitut del monitoratge amb dades reals.
Què vigilar
Un servei de ML es vigila en dos nivells. El primer és el de qualsevol servei web: latència, errors, ús de recursos i health checks, amb alertes quan se superen uns llindars (vegeu Operació en producció). El segon és específic del model:
- Distribució de les features: PSI o KS per feature, comparat amb la referència.
- Distribució de les prediccions: si el preu mitjà predit puja un 30% en una setmana, alguna cosa ha canviat, encara que no tinguem el ground truth.
- Mètriques del model: les de Avaluació de models (MAE, F1), quan arriba el ground truth. És l’única manera de detectar concept drift.
Per començar, n’hi ha prou amb una arquitectura senzilla: un script programat (per exemple, cada nit) llegeix el .jsonl, calcula aquestes mesures i envia una alerta si se supera un llindar.
flowchart LR
A[API] --> B[predictions.jsonl]
B --> C[Script diari]
C --> D{Llindars<br/>superats?}
D -->|Sí| E[Alerta]
D -->|No| F[Informe]
| Mesura | Llindar d’exemple | Severitat |
|---|---|---|
| PSI d’una feature | Més de 0,2 | Avís |
| Preu mitjà predit | Canvi de més del 20% respecte a la referència | Avís |
| MAE amb ground truth | 10% pitjor que en validar | Crítica |
Calibra els llindars amb cura: un sistema que avisa cada dia acaba ignorat (fatiga d’alertes).
Qualitat de les dades
A banda del drift, les dades poden simplement estar malament. Abans d’usar-les per predir o per entrenar, comprova:
- Valors absents: quin percentatge de cada feature arriba buit. Un salt sobtat sol indicar un canvi a l’origen.
- Valors fora de rang: un any 3500, un quilometratge negatiu.
- Categories noves: valors que el model no ha vist mai.
- Esquema: columnes que falten, tipus incorrectes.
El drift mesura canvis en la distribució; la qualitat mesura dades errònies. Un problema greu de qualitat ha d’aturar el pipeline i avisar.
Reentrenar
Quan el monitoratge detecta que el model s’ha degradat, cal actualitzar-lo. També hi ha altres motius: hi ha més dades disponibles, una feature nova, o un bug corregit al preprocessament.
Dades noves o model nou
Hi ha dues respostes diferents a una degradació:
| Situació | Causa probable | Resposta |
|---|---|---|
| Funcionava bé i empitjora amb el temps | Les dades han canviat | Iteració de dades: reentrenar el mateix model amb dades recents |
| No ha arribat mai al nivell esperat | El model o les features no són prou bons | Iteració de model: canviar d’algorisme o afegir features |
| No millora tot i tenir més dades | Les features no tenen prou informació | Iteració de model |
La iteració de dades és la que es fa amb regularitat i la que es pot automatitzar. La iteració de model és un projecte en si: es fa de tant en tant, amb els datasets fixats per poder comparar candidats.
Amb quina freqüència
| Estratègia | Quan es reentrena | Adequada per a |
|---|---|---|
| Manual | Quan algú ho decideix | Dades estables, canvis rars |
| Programada | Cada setmana o cada mes | Canvis graduals i previsibles |
| Per disparador | Quan una alerta detecta drift o degradació | Canvis imprevisibles |
Comença amb un reentrenament manual o programat. Automatitzar el disparador només val la pena quan les dades canvien de pressa, arriben prou dades noves i la validació és fiable sense revisió humana.
Del ground truth al dataset
Per reentrenar amb dades de producció cal convertir les prediccions en exemples d’entrenament:
- Guardar cada predicció amb un identificador (
request_id), com hem vist a Què cal registrar. - Lligar-hi el ground truth quan arribi. Pot ser automàtic (el preu de venda final, un clic) o manual (un expert que revisa). Les prediccions que no reben mai el resultat real no entren al dataset d’entrenament.
- Triar la finestra de dades:
- Expansiva: totes les dades des del principi. Més dades, però els patrons antics pesen tant com els recents.
- Lliscant: només els últims N dies. S’adapta de pressa, però oblida patrons estacionals.
- Híbrida: una mostra de dades antigues més totes les recents.
- Validar el dataset: prou exemples, cap salt estrany en la distribució de resultats, i cap resultat amb data anterior a la predicció (seria una fuita d’informació del futur).
El pipeline d’actualització
L’actualització segueix el mateix pipeline que el primer entrenament:
flowchart LR
A[Dades noves] --> B[Validar dades]
B --> C[Entrenar candidat]
C --> D{Compleix criteris<br/>i és millor que l'actual?}
D -->|Sí| E[Nova versió<br/>aprovada]
D -->|No| F[Descartar i avisar]
E --> G[Promocionar]
Dues condicions per acceptar el candidat:
- Compleix els criteris de desplegament, els mateixos de Validació i CI/CD.
- No és pitjor que el model actual, avaluats tots dos sobre les mateixes dades. Un candidat amb un MAE de 800 sembla bo fins que veus que l’actual en fa 750.
Si s’accepta, l’entrenament crea una versió nova i la promoció segueix el mateix camí que qualsevol altra versió (vegeu Quina versió se serveix). Per programar el reentrenament n’hi ha prou amb cron:
# Cada dilluns a les 03:00
0 3 * * 1 cd /opt/project && make train >> logs/cron.log 2>&1
Una variant és continuar entrenant el model existent només amb les dades noves (entrenament incremental) en lloc de reentrenar des de zero. És més ràpid, però el model pot oblidar el que havia après abans. Per als models de scikit-learn d’aquests materials, reentrenar des de zero és el més senzill i el més segur.
Provar en producció
Les proves offline amb dades històriques no capturen tot: el comportament real dels usuaris, l’efecte en el negoci. Per això un model nou es pot provar amb tràfic real de manera controlada. Les estratègies generals (shadow, canary, A/B) es descriuen a Estratègies de desplegament; aquí veiem què aporta cadascuna a un model:
- Shadow: el model nou rep una còpia de cada petició, però la seva predicció no arriba a l’usuari, només es registra. Permet comparar les prediccions dels dos models sobre les mateixes dades, sense cap risc. No diu res de l’efecte en el negoci.
- Canary: el model nou serveix una fracció petita del tràfic (un 5%) i, si les mètriques aguanten, es va ampliant. Respon la pregunta “el model nou falla?”.
- A/B testing: el tràfic es reparteix entre els dos models durant prou temps per comparar una mètrica de negoci (vendes, clics). Respon la pregunta “quin és millor?”. Per repartir de manera consistent, es fa un hash de l’identificador d’usuari: el mateix usuari sempre rep el mateix model. Cada predicció registra la
model_versionper analitzar els resultats per grup.
Una seqüència raonable per a un equip petit: primer shadow, per veure que el model nou no falla; després canary, per veure’l amb tràfic real; i un A/B només si cal mesurar l’impacte en el negoci. Hi ha variants que mouen el tràfic automàticament cap al model que va millor (multi-armed bandits), però són més complexes i poques vegades calen.
Practicar amb un dataset fix
Sense un flux real de dades, el cicle de monitoratge i reentrenament es pot practicar dividint el production set en lots que simulen l’arribada de dades al llarg del temps:
- Entrenament inicial: entrenar amb el training set, validar amb el validation set i desplegar la versió
v1. - Lot 1: predir-lo, calcular-ne les mètriques i guardar-les com a referència.
- Lot 2: comparar-ne la distribució amb el lot 1 (PSI) i avaluar si el rendiment cau. Per fer-ho més realista, s’hi pot introduir un drift simulat.
- Decidir: si el rendiment cau més d’un llindar (per exemple, un 5%), reentrenar amb el training set més el lot 1, comparar el candidat amb
v1sobre el lot 2, i crearv2si és millor. - Repetir amb els lots següents, acumulant dades i versions.
Aquesta simulació valida el pipeline, però no reprodueix el retard real del ground truth ni els patrons estacionals de les dades reals.
Resum
- El drift fa que un model empitjori sense cap error visible. El covariate shift es detecta mirant les entrades; el concept drift necessita el ground truth.
- Cal registrar cada predicció amb un identificador, les features, la predicció i la versió del model, sense dades personals.
- KS i chi-quadrat diuen si dues distribucions són diferents; el PSI diu quant, amb llindars coneguts.
- Es vigila el servei com qualsevol altre i, a més, les distribucions de features i prediccions, i les mètriques quan arriba el ground truth.
- El reentrenament segueix el mateix pipeline: el candidat ha de complir els criteris i no ser pitjor que l’actual. Promocionar-lo és un canvi de configuració.
- Shadow, canary i A/B proven un model nou amb tràfic real, cadascun responent una pregunta diferent.
A Sistemes LLM en producció recorrem aquestes mateixes etapes (desplegar, validar, observar i actualitzar) per a un sistema que fa servir un LLM.