El pipeline de CD de la lliçó 05-02 va deixar una limitació anunciada: després de staging i de l'aprovació, la versió nova arriba a producció de cop, a totes les rèpliques. Per al codi de l'API és un risc acotat; per a un model és una altra història, perquè el test set versionat — per rigorós que sigui — no és el trànsit real. Un challenger amb recall 0.71 offline pot comportar-se de manera estranya amb els clients que arriben avui pel flux de cancel·lació: distribucions que l'històric no contenia, combinacions rares, volums diferents. Aquesta lliçó presenta les tres estratègies perquè un model nou es guanyi la confiança gradualment: shadow (prediu a l'ombra, sense conseqüències), canary (rep un percentatge creixent de trànsit real) i A/B (experiment controlat per respondre una pregunta de negoci). Les tres s'implementen sobre el que ja tenim, i tanquen el mòdul — i amb ell, un nivell sencer de maduresa MLOps.
Contingut
- El problema: offline no garanteix online
- Shadow deployment: predir sense conseqüències
- Què es compara després de dues setmanes d'ombra
- Canary release: trànsit real, a poc a poc
- Mètriques de guàrdia i rollback automàtic
- A/B testing: quan la pregunta és de negoci
- Comparativa i decisió de CineClick
- Tancament del mòdul: on som a l'escala de maduresa
El problema: offline no garanteix online
Les mètriques offline del challenger es calculen sobre el test set versionat: un split de dades històriques, amb random_state=42, congelat amb DVC. Això el fa comparable (tots els candidats es mesuren al mateix terreny), però no el fa representatiu del futur. Motius concrets pels quals un bon model offline pot decebre online:
- El trànsit real no és una mostra aleatòria de l'històric. A l'endpoint
/predirhi arriben sobretot clients al flux de cancel·lació — un subconjunt esbiaixadíssim de la base de clients. El test set conté de tot. - El món s'ha mogut. Entre el tall del dataset i avui hi ha hagut campanyes, pujades de preu, un pla nou. El test set és una foto; el trànsit és la pel·lícula.
- Efectes que només existeixen en producció. Latència del model nou sota càrrega, interacció amb la cache TTL de 04-05, valors extrems que la validació permet però que l'entrenament amb prou feines va veure.
- La mètrica de ML no és la mètrica de negoci. El model serveix per retenir clients; recall i precision són aproximacions. Pot pujar l'F1 i no moure — o empitjorar — la retenció (ho veurem a l'A/B).
Conclusió operativa: staging + smoke test verifiquen que el sistema funciona; no verifiquen que el model nou decideixi bé amb el trànsit d'avui. Per a això cal exposar-lo a trànsit real — la qüestió és quant risc acceptes mentre ho comproves. Les tres estratègies són tres respostes a aquesta pregunta:
flowchart LR
A[Challenger aprovat<br/>offline: 05-01/05-02] --> B[Shadow<br/>risc zero: ningú no veu les seves prediccions]
B --> C[Canary<br/>risc acotat: 5% -> 25% -> 100%]
C --> D[A/B<br/>risc repartit: mesura l'impacte de NEGOCI]Shadow deployment: predir sense conseqüències
En un desplegament en ombra, el challenger rep tot el trànsit en còpia: per a cada petició real es calcula també la seva predicció, es registra, i es descarta — la resposta al client surt sempre del champion. Risc funcional: zero. Informació obtinguda: com es comportaria el challenger amb exactament el trànsit de producció.
Hi ha dues maneres de muntar-ho: un segon deployment que rep trànsit duplicat (a nivell d'infraestructura, amb un mirror a l'ingress/service mesh), o — l'opció simple que farà servir CineClick — que el mateix servei carregui tots dos models i registri les dues probabilitats. Amb el logging JSON per predicció que ja existeix des de 04-02, la implementació és petita:
# src/cineclick_churn/api/main.py (fragment: mode ombra)
import os
import time
import mlflow
MODE_OMBRA = os.getenv("MODE_OMBRA", "false").lower() == "true"
model_champion = mlflow.sklearn.load_model("models:/churn-cineclick@champion")
model_ombra = (
mlflow.sklearn.load_model("models:/churn-cineclick@challenger")
if MODE_OMBRA else None
)
@app.post("/predir")
def predir(entrada: ClientEntrada):
X = construir_features(entrada.com_a_dataframe())
t0 = time.perf_counter()
prob = float(model_champion.predict_proba(X)[0, 1])
latencia_ms = (time.perf_counter() - t0) * 1000
registre = {
"hash_entrada": hash_entrada(entrada),
"probabilitat": round(prob, 4),
"versio_model": VERSIO_CHAMPION,
"latencia_ms": round(latencia_ms, 1),
}
if model_ombra is not None:
t0 = time.perf_counter()
prob_ombra = float(model_ombra.predict_proba(X)[0, 1])
registre["probabilitat_ombra"] = round(prob_ombra, 4)
registre["versio_model_ombra"] = VERSIO_CHALLENGER
registre["latencia_ombra_ms"] = round((time.perf_counter() - t0) * 1000, 1)
logger.info(json.dumps(registre))
# La RESPOSTA surt sempre del champion: l'ombra no decideix res.
llindar = float(os.getenv("LLINDAR_ABANDONAMENT", "0.5"))
return {"probabilitat_abandonament": prob, "abandonara": prob >= llindar}Detalls que importen:
MODE_OMBRAés configuració, no codi: s'activa afegint la variable al ConfigMapchurn-api-configi reiniciant — sense release d'imatge. Apagar-la és igual de barat.- Les features es calculen una vegada i es reutilitzen per a tots dos models: mateix cost de features, doble cost només d'inferència.
- La resposta i el camp
probabilitatdel log no canvien de significat: qualsevol dashboard o consumidor existent continua funcionant; l'ombra només afegeix camps. - El cost real és latència i CPU: la segona inferència suma uns quants mil·lisegons dins del pressupost (p95 = 92 ms sobre un pressupost de 300 ms: hi ha marge de sobres, però es mesura, no se suposa — per a això es registra
latencia_ombra_ms). - En rigor, un
predict_probaextra al mateix procés també pot fallar; s'embolcalla en untry/exceptque registri l'error i mai no afecti la resposta (queda com a exercici 1).
Què es compara després de dues setmanes d'ombra
L'ombra no emet un veredicte sola: acumula dades per a una anàlisi. A CineClick el període fixat són dues setmanes (cobreix dos cicles setmanals complets, inclòs el patró de cap de setmana), i l'anàlisi mira tres coses sobre els logs JSON:
- Distribució de probabilitats. El challenger assigna probabilitats amb una forma raonable, semblant a la del champion o desplaçada de manera explicable? Un challenger que ho concentra tot en 0.45-0.55 o que dispara la taxa de positius del ~15% esperat al 40% és sospitós encara que les seves mètriques offline fossin bones.
- Desacords champion/challenger. En quin percentatge de peticions creuen el llindar en sentits oposats? Aquests casos discordants són la lupa: s'extreuen (via
hash_entrada) i la Laura els revisa — el challenger encerta on el champion fallava, o a l'inrevés? Nota honesta: en churn l'etiqueta real triga setmanes a conèixer-se, així que aquesta revisió és qualitativa i de plausibilitat; la confirmació amb etiquetes arriba més tard (i sistematitzar-la és matèria del mòdul 6). - Latència afegida.
latencia_ombra_mses comporta? El p95 conjunt continua dins del pressupost de 300 ms? Un challenger 4 vegades més lent és un problema operatiu encara que predigui de meravella.
Si les tres sortides són sanes, el challenger ha demostrat que funciona amb trànsit real. El que l'ombra no pot demostrar és que les seves decisions produeixin millors resultats — les seves prediccions no es van fer servir, així que no van causar res. Per a això cal donar-li trànsit de debò: canary o A/B.
Canary release: trànsit real, a poc a poc
El canary (pel canari de les mines) enruta un percentatge petit i creixent de trànsit real cap al model nou: a CineClick, 5% → 25% → 100%, avançant només si les mètriques de guàrdia es mantenen sanes a cada esglaó (típicament 24-48 h per esglaó). Si alguna cosa es degrada, es torna al 0% en segons i només un 5% de les peticions s'ha vist afectat.
Dos nivells on implementar-ho:
- Nivell Kubernetes: dos deployments (
churn-api-estableichurn-api-canary) darrere del mateix Service; el repartiment per nombre de rèpliques dona percentatges gruixuts (1 pod canary de 4 ≈ 25%). Per a percentatges fins i control real de pesos es fa servir un ingress amb suport de pesos o un service mesh (Istio, Linkerd) — existeixen, funcionen, i no en farem el tutorial aquí: per a la mida de CineClick seria més infraestructura que problema. - Nivell aplicació (l'opció de CineClick): el servei carrega tots dos models i decideix per petició quin fa servir. Requisit clau: l'assignació ha de ser estable per client — que el mateix client rebi sempre el mateix model mentre duri el canary, perquè la seva experiència sigui coherent i els grups siguin comparables. Un
random()per petició no ho compleix; un hash de l'id_client, sí:
# src/cineclick_churn/api/enrutament.py
"""Enrutament canary: assignació estable de client a model."""
import hashlib
import os
def cub_client(id_client: str) -> int:
"""Projecta l'id a un enter 0-99, estable entre processos i rèpliques.
md5 no és criptografia aquí: només un mesclador determinista i uniforme
(hash() de Python NO serveix: canvia entre processos per PYTHONHASHSEED)."""
digest = hashlib.md5(id_client.encode("utf-8")).hexdigest()
return int(digest, 16) % 100
def usar_canary(id_client: str) -> bool:
percentatge = int(os.getenv("PERCENTATGE_CANARY", "0"))
return cub_client(id_client) < percentatgeI a l'endpoint:
model = model_canary if usar_canary(entrada.id_client) else model_champion registre["grup"] = "canary" if usar_canary(entrada.id_client) else "estable"
Propietats d'aquest disseny, que convé assaborir:
- Estabilitat: el client
C-0042cau sempre al mateix cub, a qualsevol rèplica, avui i demà. - Monotonia en escalar: en passar
PERCENTATGE_CANARYde 5 a 25, els cubs 0-4 continuen al canary i s'hi afegeixen el 5-24. Ningú no rebota entre models en pujar l'esglaó. - Operació sense release: el percentatge viu al ConfigMap
churn-api-config; pujar l'esglaó és editar un valor i reiniciar. El rollback (a 0) també. - El camp
grupal log JSON és el que permet comparar mètriques per grup — sense ell, el canary és trànsit cec.
Mètriques de guàrdia i rollback automàtic
Un canary sense vigilància automàtica és una ruleta amb passos extra. Les mètriques de guàrdia (guardrail metrics) són el conjunt petit de senyals que, si es degraden al grup canary respecte a l'estable, aborten la progressió sense esperar cap humà:
| Mètrica de guàrdia | Llindar a CineClick | Per què |
|---|---|---|
| Taxa d'errors 5xx (grup canary) | > 1% durant 10 min | El model nou trenca amb entrades que l'estable tolera |
| Latència p95 (grup canary) | > 300 ms (el pressupost de 04-05) | Model més pesat del que s'assumia |
| Taxa de prediccions positives | Fora del 10%-25% | L'històric volta el 15%; una taxa del 45% assenyala un model desbaratat, encara que tècnicament "funcioni" |
Les dues primeres són de servei; la tercera és la interessant en ML: no necessita etiquetes (que en churn triguen setmanes) i tot i així detecta els desastres grans a l'instant. La mecànica del rollback automàtic: un chequeig recurrent compara les mètriques per grup i, si un llindar es viola de manera sostinguda, posa PERCENTATGE_CANARY=0 i alerta — l'humà arriba després, amb l'incident ja contingut. És l'evolució natural del rollback "semiautomàtic" que vam deixar anotat a 05-02: aquí el tret sí que s'automatitza, perquè el criteri és objectiu i el cost d'un fals positiu (retirar un canary sa) és mínim. Com es recullen i es consulten aquestes mètriques de manera contínua — dashboards, alerting de debò — és exactament el tema de 06-01; aquí ens basta el contracte: sense mètriques de guàrdia definides abans de començar, no hi ha canary.
A/B testing: quan la pregunta és de negoci
Shadow i canary responen preguntes de comportament del sistema: prediu raonablement? Aguanta? Però la pregunta que de debò importa a CineClick és una altra: el model nou reté més clients? Aquesta és una pregunta causal sobre un resultat de negoci, i només la respon un experiment: el test A/B.
Disseny per a CineClick:
- Unitat: el client (assignat pel mateix hash estable del canary — el codi ja existeix).
- Grup A: els seus clients en risc s'identifiquen amb el champion; grup B: amb el challenger. En tots dos, els identificats reben l'oferta de retenció de màrqueting.
- Grup de control: una fracció de clients en risc que no rep oferta. Reapareix aquí el problema que vam diagnosticar al mòdul 1: la intervenció contamina les dades — si un client en risc rep un descompte i es queda, el model "va fallar" en predir el seu abandonament, o l'oferta va funcionar? Sense control és impossible separar la qualitat del model de l'efecte de l'oferta, i de passada el control dona les dades netes que els reentrenaments futurs necessitaran.
- Mètrica primària: taxa de retenció a 30 dies de l'oferta. Secundàries: cost de les ofertes emeses, ingressos retinguts.
- Durada mínima: fixada abans de començar, calculant quants clients calen per detectar l'efecte que importa (amb ~2.000 clients en risc/mes per grup, detectar una millora de 5 punts de retenció exigeix de l'ordre d'un mes; efectes més petits, força més). Aturar l'experiment "quan va guanyant B" és la manera clàssica d'enganyar-se.
Significació estadística, versió pràctica
La intuïció: si B reté el 23% i A el 20%, és una millora real o soroll de mostreig? La pregunta operativa és "si no hi hagués diferència real, com de rar seria veure aquesta diferència per atzar?" — això és el p-valor. Amb dues proporcions, la prova estàndard és un test de khi quadrat sobre la taula de contingència:
# Comparar taxes de retenció de dos grups (dades fictícies)
from scipy.stats import chi2_contingency
# grup A (champion): 2000 clients amb oferta, 400 retinguts (20.0%)
# grup B (challenger): 2000 clients amb oferta, 460 retinguts (23.0%)
taula = [
[400, 1600], # A: retinguts, no retinguts
[460, 1540], # B: retinguts, no retinguts
]
chi2, p_valor, _, _ = chi2_contingency(taula)
print(f"p-valor: {p_valor:.4f}") # p-valor: 0.0233Lectura pràctica: p = 0.023 < 0.05 → si els models fossin igual de bons, una diferència així apareixeria per atzar només ~2 de cada 100 vegades; és raonable atribuir-la al challenger. Tres matisos que eviten el 90% dels mals usos: el llindar 0.05 és convenció, no física; amb mostres enormes gairebé tot surt "significatiu" — pregunta't també si els 3 punts importen al negoci (aquí: 60 clients/mes retinguts de més, clarament sí); i mirar el p-valor cada dia i aturar quan creua 0.05 ho invalida — durada fixada per endavant.
Quan negoci contradiu ML
El resultat més instructiu possible: el challenger té millor recall offline i pitjor retenció a l'A/B. Com? Per exemple: el challenger identifica més abandonaments reals (recall ↑) però els que afegeix són clients ja decidits a marxar, a qui l'oferta no mou — mentre que deixa d'assenyalar indecisos que el champion sí que capturava i que l'oferta sí que retenia. La mètrica de ML mesura l'encert en la predicció; la de negoci mesura l'efecte de l'acció presa amb la predicció. Quan xoquen, mana la de negoci — és l'única raó per la qual el model existeix. Aquest escenari, impossible de veure al test set i invisible per a l'ombra, és la justificació definitiva de l'A/B.
Comparativa i decisió de CineClick
| Shadow | Canary | Test A/B | |
|---|---|---|---|
| El model nou afecta usuaris? | No | Sí, a un % creixent | Sí, al ~50% |
| Risc | Cap (funcional) | Acotat i reversible | Repartit per disseny |
| Cost | Doble inferència + anàlisi | Operació de dues versions + vigilància | El més gran: disseny, durada, disciplina |
| Quina pregunta respon | Es comporta raonablement amb trànsit real? | Funciona bé servint de debò, sense degradar el servei? | Millora el resultat de NEGOCI? |
| Necessita etiquetes/resultats | No | No (guàrdies sense etiqueta) | Sí (retenció a 30 dies) |
| Durada típica | 1-2 setmanes | Dies per esglaó | Setmanes-mesos |
| Quan fer-la servir | Sempre, com a primer filtre barat | Per promocionar amb xarxa | Quan la decisió depèn de l'impacte de negoci |
No són alternatives excloents sinó capes: cadascuna filtra el que l'anterior no pot veure. La política que CineClick deixa escrita:
- Tot challenger que aspiri a
@championpassa 2 setmanes en ombra (a més dels gates offline de 05-01 i l'aprovació de 05-02). Barat, risc zero, caça els desastres. - La promoció s'executa com a canary 5% → 25% → 100% amb les mètriques de guàrdia d'aquesta lliçó i rollback automàtic al 0%.
- Test A/B reservat per a decisions de negoci: canvis de llindar (
LLINDAR_ABANDONAMENT), canvis d'oferta de retenció, o challengers amb un cas de valor discutit. Amb grup de control permanent, que a més protegeix les dades d'entrenament futures.
Tancament del mòdul: on som a l'escala de maduresa
Val la pena mirar enrere amb la vara de mesurar del mòdul 1. CineClick partia del nivell 0: notebook, mans i memòria. Els mòduls 2-4 van construir les peces (paquet, DVC, MLflow, servei, imatge, Kubernetes) i aquest mòdul les ha fet moure's soles:
- CI (05-01): cada canvi executa tests de codi, de dades (pandera) i de model (fum, comportament, llindars de negoci).
- CD (05-02): dos pipelines desacoblats — imatge per tag amb aprovació, model per àlies amb humà al bucle — cadascun amb el seu rollback.
- Orquestració (05-03): el batch dels dilluns corre sol, es reintenta, avisa i deixa rastre.
- Release (05-04): els models nous es guanyen la confiança per capes — ombra, canary amb guàrdies, A/B per al negoci.
Això és un nivell 1 llarg, fregant el 2. Què falta exactament per al 2? Que alguna cosa dispari tot això sense que una persona ho decideixi: avui, el reentrenament es llança a mà, l'ombra s'activa a mà, i ningú no sap — amb dades — quan el champion comença a envellir. La peça que falta no és més automatització: és observació. Saber com es comporten el servei i el model en producció, detectar quan les dades deriven, i fer servir aquests senyals per disparar els pipelines que aquest mòdul ha deixat a punt. Aquest és el mòdul 6.
Errors Comuns i Consells
- Saltar-se l'ombra "perquè les mètriques offline són molt bones". L'ombra existeix precisament perquè aquesta frase no és un argument: és la hipòtesi a comprovar. Dues setmanes d'ombra costen gairebé res; un champion desbaratat al flux de cancel·lació costa clients.
- Canary amb assignació aleatòria per petició. El mateix client rep el model A a les 10:00 i el B a les 10:05: experiència incoherent i grups incomparables. Hash estable de l'
id_client, sempre. - Fer servir
hash()de Python per a l'enrutament. Des de Python 3.3,hash()de strings canvia entre processos (PYTHONHASHSEED): cada rèplica assignaria cubs diferents i l'"assignació estable" seria una il·lusió.hashlibés determinista. - Canary sense mètriques de guàrdia definides per endavant. Si decideixes els llindars mirant les dades del canary en marxa, decidiràs el que confirmi les teves ganes de promocionar. Guàrdies i llindars per escrit abans del 5%.
- Aturar l'A/B així que el p-valor creua 0.05. El peeking infla els falsos positius: mirant cada dia, la probabilitat de creuar 0.05 per pur atzar en algun moment és molt més gran que el 5%. Durada fixada per endavant; anàlisi al final.
- A/B sense grup de control. Mesuraràs quin model assenyala clients "millors per a l'oferta", però no si l'oferta mateixa aporta res, i contaminaràs l'històric per als reentrenaments (la lliçó del mòdul 1). El control no és opcional: és el preu de poder aprendre.
- Consell: escriu la política de release (els tres punts de la secció anterior) al repo, al costat del procediment de promoció de 05-02. Quan d'aquí a sis mesos algú proposi "promocionar directe, que hi ha pressa", el cost de l'excepció serà visible i la decisió, conscient.
Exercicis
Exercici 1
Endureix el mode ombra: reescriu el bloc de la inferència en ombra perquè (a) una fallada del challenger mai no afecti la resposta, (b) la fallada quedi registrada al JSON (error_ombra), i (c) si el challenger falla més vegades del compte, tinguis manera de detectar-ho després des dels logs.
Exercici 2
Amb PERCENTATGE_CANARY=25, indica de manera raonada si aquests clients reben el canary, fent servir cub_client: (a) un client amb cub 4; (b) un amb cub 24; (c) un amb cub 25; (d) què passa amb cadascun quan l'esglaó puja a 100? I si a mig esglaó del 25% hi ha rollback a 0?
Exercici 3
A l'A/B de l'oferta de retenció: grup A (champion) 1.800 clients amb oferta i 342 retinguts; grup B (challenger) 1.750 i 368 retinguts. Calcula les taxes, executa el test de khi quadrat amb scipy i interpreta el resultat tal com l'explicaries a la responsable de retenció (sense argot). Quina decisió recomanes?
Solucions
Exercici 1
if model_ombra is not None:
try:
t0 = time.perf_counter()
prob_ombra = float(model_ombra.predict_proba(X)[0, 1])
registre["probabilitat_ombra"] = round(prob_ombra, 4)
registre["latencia_ombra_ms"] = round((time.perf_counter() - t0) * 1000, 1)
except Exception as exc: # noqa: BLE001 — l'ombra mai no tomba la resposta
registre["error_ombra"] = type(exc).__name__(a) El try/except embolcalla només la inferència en ombra: passi el que passi, la resposta del champion ja està calculada i es retorna intacta. (b) Es registra el tipus d'excepció, suficient per al diagnòstic sense abocar traces gegants a cada línia de log. (c) Com que és un camp del JSON estructurat, la taxa de fallades de l'ombra s'obté comptant línies amb error_ombra sobre el total — i un challenger que falla en el 3% de les peticions reals t'acaba de donar una informació valuosíssima abans de tocar ni un sol usuari: exactament per a això hi havia l'ombra.
Exercici 2
La regla és cub < percentatge. (a) cub 4 < 25 → canary (i ja ho era amb el 5%: els cubs 0-4 van entrar al primer esglaó). (b) cub 24 < 25 → canary; va entrar a l'esglaó del 25%. (c) cub 25 no és < 25 → estable; entrarà quan el percentatge superi 25 (al nostre pla, directament al 100%). (d) Amb 100, tots els cubs (0-99) compleixen la condició → tots al canary, que en aquell punt passa a ser el nou estable i es consolida movent @champion (el flux de 05-02). Amb rollback a 0, cap cub no compleix cub < 0 → tots tornen al champion a l'instant, amb una edició de ConfigMap i sense release. La gràcia del disseny és que les quatre respostes surten d'una sola línia de codi i són idèntiques a totes les rèpliques.
Exercici 3
from scipy.stats import chi2_contingency
taula = [
[342, 1800 - 342], # A: 19.0% de retenció
[368, 1750 - 368], # B: 21.0% de retenció
]
chi2, p_valor, _, _ = chi2_contingency(taula)
print(f"A: {342/1800:.1%} B: {368/1750:.1%} p-valor: {p_valor:.3f}")
# A: 19.0% B: 21.0% p-valor: 0.137Taxes: A 19,0%, B 21,0%. El p-valor ≈ 0,14 significa: si tots dos models fossin igual de bons retenint, veuríem una diferència com aquesta (o més gran) per pur atzar més o menys una de cada set vegades. És massa freqüent per descartar l'atzar. Traducció per a la responsable de retenció: "B pinta una mica millor, però amb aquests números no ho podem distingir de la sort; no és un 'no', és un 'encara no ho sabem'". Recomanació: no promocionar encara per aquesta evidència — si l'experiment tenia una durada prevista més llarga, continuar fins a completar-la (sense anar mirant el p-valor pel camí); si ja s'ha acabat, la millora real, si existeix, és probablement més petita que la que l'experiment estava dimensionat per detectar, i toca decidir si una millora tan petita justifica un experiment més llarg o quedar-se amb el champion.
Conclusió
El mòdul 5 va començar amb una llista de tasques manuals i acaba amb un sistema que es mou sol i — més difícil — que canvia amb xarxa de seguretat. La CI executa a cada canvi els tests de codi, de dades i de model, amb el criteri de negoci convertit en llindar bloquejant; el CD lliura per dues vies desacoblades — la imatge per tag amb aprovació a l'environment, el model per àlies amb revisió humana documentada — cadascuna amb el seu rollback assajat; Prefect llança el scoring dels dilluns a les 06:00 amb reintents, alertes i rastre; i aquesta última lliçó ha posat la peça que faltava entre "aprovat a staging" i "servint al 100%": dues setmanes d'ombra per a tot challenger, canary 5% → 25% → 100% amb mètriques de guàrdia i rollback automàtic a zero, i A/B amb grup de control quan la pregunta és de negoci — perquè la retenció, no el recall, és la raó que aquest model existeixi. CineClick viu ja en un nivell 1 de maduresa molt llarg, fregant el 2. El que li falta per creuar té nom: res de tot això no es dispara sol. L'ombra s'activa a mà, el reentrenament espera que algú el demani, i l'única manera de saber si el champion envelleix és que un humà es recordi de mirar. El mòdul 6 construeix els ulls del sistema — monitoratge de servei i de model, detecció de deriva de dades i de concepte — i amb ells, els reflexos: reentrenar quan toca, governar el que es desplega i documentar el que es decideix. Les peces ja es mouen soles; ara han d'aprendre a mirar.
Curs de MLOps
Mòdul 1: Fonaments de MLOps
- Què és MLOps i per què els models moren al notebook
- El cicle de vida d'un model de ML en producció
- Nivells de maduresa MLOps i rols de l'equip
- El projecte del curs: del notebook a producció
Mòdul 2: Del notebook al codi reproduïble
- Estructura d'un projecte de ML: del notebook al paquet
- Entorns reproduïbles i gestió de dependències
- Versionat de dades amb DVC
- Pipelines d'entrenament reproduïbles
Mòdul 3: Experiments i registre de models
- Tracking d'experiments amb MLflow
- Model registry: versionar i promocionar models
- Feature stores: quan i per a què
Mòdul 4: Servir models en producció
- Patrons de desplegament: batch, online i streaming
- Un servei de predicció amb FastAPI
- Empaquetatge amb Docker
- Escalat i desplegament: Kubernetes i serverless
- Optimització de la inferència: latència i cost
Mòdul 5: Automatització: CI/CD i orquestració
- CI per a ML: tests de codi, dades i models
- CD: automatitzar el desplegament del model
- Orquestració de pipelines de ML
- Estratègies de release: shadow, canary i A/B
