Una dada incòmoda que convé tenir present en començar aquesta lliçó: la majoria dels models d'aprenentatge automàtic que s'entrenen a les empreses no arriben mai a producció. No perquè el model sigui dolent. Perquè ningú no va construir el sistema que l'envolta.
AlpinaShop és justament en aquell punt. En sis lliçons ha construït un recomanador heurístic en SQL, un classificador de cistelles, un classificador d'imatges, un model de dues torres, vuit mil ressenyes analitzades, vint mil imatges processades, dues mil quatre-centes fitxes generades, un cercador semàntic i un assistent amb RAG.
I ara, les preguntes que ningú no ha fet encara:
Qui executa tot això? La Lucía llança el procés de sentiment des del seu notebook quan se'n recorda; en Dani regenera fitxes amb un script al seu portàtil. Si demà tots dos estan de vacances alhora, no s'executa res i ningú no se n'assabenta.
Com sabem que el model nou és millor? No ho sabem. El model de dues torres es va entrenar al març. Si es reentrena avui amb sis mesos més de dades, ningú no compararà el resultat amb el que és en producció: es desplegarà el nou perquè és el nou.
Amb quines dades es va entrenar el model que va fer aquesta recomanació? Ningú no ho sap. No està escrit enlloc.
I si les dades canvien i un model es degrada? Ningú no se n'assabenta, fins que algú de negoci digui "això ja no funciona com abans"; i llavors ja portarà mesos funcionant malament.
Aquelles quatre preguntes són MLOps. I respondre-les és el que separa un experiment d'un producte.
Contingut
- Per què els models no arriben a producció
- Què és MLOps i els seus nivells de maduresa
- Vertex AI Pipelines: components, artefactes i graf
- El pipeline de recomanació d'AlpinaShop
- Component 1: extreure les dades
- Component 2: validar la qualitat
- Component 3: entrenar
- Component 4: avaluar contra producció
- La porta de decisió: només es desplega si millora
- Components 5 i 6: registrar i desplegar
- Compilar i executar el pipeline
- Execució programada i disparada per esdeveniments
- Metadades i llinatge: el que et salva en una auditoria
- Model Monitoring i el reentrenament
- Versionatge i reproductibilitat
- Cost i sostenibilitat del cicle
- El checklist de ML responsable
- Per què els models no arriben a producció
La causa arrel és sempre la mateixa: el model és una part petita del sistema. El codi de modelatge és una fracció mínima del total; tota la resta és infraestructura que cal construir i que gairebé mai no es pressuposta.
Els sis obstacles concrets, amb el que ha passat a AlpinaShop:
| Obstacle | Manifestació a AlpinaShop |
|---|---|
| No hi ha procés reproduïble | El model de març no es pot tornar a obtenir |
| L'execució depèn de persones | La Lucía llança el procés "quan se'n recorda" |
| No hi ha criteri de desplegament | Es desplegaria el model nou sense comparar |
| No hi ha traçabilitat | Ningú no sap quines dades van entrenar quina versió |
| No hi ha vigilància | La degradació es detecta per queixa de negoci |
| No hi ha marxa enrere | Si el model nou va malament, no hi ha procediment |
Fixa't en una cosa important: cap dels sis no és un problema d'aprenentatge automàtic. Són problemes d'enginyeria de programari i d'operacions, i per això la solució tampoc no és d'aprenentatge automàtic. I hi ha una asimetria que convé entendre, perquè explica per què MLOps no és simplement DevOps aplicat a models:
| Aspecte | Programari tradicional | Sistema de ML |
|---|---|---|
| Què canvia | El codi | El codi i les dades |
| Què es versiona | Codi | Codi, dades, model, hiperparàmetres |
| Es degrada sol | No | Sí: el món canvia i el model no |
| Prova d'acceptació | Determinista: passa o falla | Estadística: "és millor que l'anterior" |
| Fallada típica | Excepció visible | Silenciosa: prediu pitjor sense errors |
L'última fila és la més perillosa. Un servei web caigut es detecta en minuts. Un model que encerta un 15 % menys no produeix cap error: continua responent, continua retornant prediccions ben formades, i ningú no ho nota.
- Què és MLOps i els seus nivells de maduresa
MLOps és el conjunt de pràctiques que porten models a producció de forma fiable, reproduïble i sostenible. Combina DevOps amb les particularitats del ML: les dades són part de l'artefacte i la qualitat es mesura estadísticament. Tres nivells de maduresa, i no cal arribar a l'últim per tenir un sistema sa:
| Nivell | Com és | Qui executa | Riscos |
|---|---|---|---|
| 0 — Manual | Notebooks, passos a mà, desplegament manual | Una persona | Irreproduïble, dependent, sense vigilància |
| 1 — Entrenament automatitzat | Pipeline que executa el cicle complet, programat | Un orquestrador | Falta CI/CD del mateix pipeline |
| 2 — CI/CD complet | El pipeline es construeix, prova i desplega sol en canviar el codi | Un sistema de CI/CD | Complexitat; requereix equip |
AlpinaShop és al nivell 0 i en aquesta lliçó va al nivell 1. El nivell 2 requereix Cloud Build i disparadors des del repositori, que és matèria de 06-01, i per a una empresa de 40 persones pot ser més del que necessita. Un consell que estalvia projectes: el salt de 0 a 1 aporta la major part del valor, perquè un pipeline reproduïble que s'executa sol, amb porta de decisió i llinatge, resol les quatre preguntes de l'inici de la lliçó. El salt d'1 a 2 és refinament.
- Vertex AI Pipelines: components, artefactes i graf
Vertex AI Pipelines executa pipelines definits amb el SDK de Kubeflow Pipelines (KFP) sense que hagis de gestionar cap clúster. És serverless: descrius el graf, el compiles, l'executes i pagues pel que consumeix. Quatre conceptes basten.
Un component és un pas del pipeline: una funció de Python empaquetada en un contenidor, amb entrades i sortides declarades. Un artefacte és una cosa que un component produeix o consumeix i que la plataforma rastreja —un dataset, un model, unes mètriques—; els artefactes són la clau del llinatge, perquè cadascun sap quin component el va produir i amb quines entrades. Un paràmetre és un valor simple que es passa entre components o al pipeline sencer. I el graf és dirigit i acíclic, amb les dependències inferides soles: si B rep la sortida d'A, s'executa després d'A, sense declarar cap ordre.
flowchart TD
A[Extreure dades<br/>d alpinashop_analitica] --> B[Validar qualitat<br/>regles de Dataplex]
B -->|qualitat OK| C[Entrenar model]
B -->|qualitat KO| Z[Aturar el pipeline]
C --> D[Avaluar candidat]
E[Model en produccio] --> D
D --> F{Millora el llindar?}
F -->|si| G[Registrar versio]
F -->|no| H[Notificar i aturar]
G --> I[Desplegar / publicar taula]
I --> J[Registrar metadades i llinatge]
Davant de Cloud Composer i Workflows (04-06), que ja coneixes, la diferència de propòsit és clara:
| Aspecte | Workflows / Composer | Vertex AI Pipelines |
|---|---|---|
| Propòsit | Orquestració general | Cicle de vida de models |
| Rastreig d'artefactes | No | Sí, natiu |
| Llinatge de ML | No | Sí |
| Integració amb Model Registry | Manual | Nativa |
| Definició | YAML / DAG d'Airflow | Python amb KFP |
No competeixen: es complementen. Workflows continua orquestrant el procés nocturn de dades i dispara el pipeline de ML quan toca.
- El pipeline de recomanació d'AlpinaShop
L'objectiu: reentrenar el recomanador two-tower de 05-03 cada setmana, comparant-lo amb el model en producció i amb la línia base heurística de 05-01, i desplegar-lo només si millora.
from kfp import dsl
from kfp.dsl import component, Input, Output, Dataset, Model, Metrics
@dsl.pipeline(
name="reco-alpinashop",
description="Reentrenament setmanal del recomanador d AlpinaShop",
pipeline_root="gs://alpinashop-datalake/pipelines/reco",
)
def pipeline_reco(
proyecto: str = "alpinashop-datos",
region: str = "europe-west1",
dias_historico: int = 730,
umbral_mejora: float = 0.02, # 2% de millora relativa minima
dim_embedding: int = 64,
epocas: int = 25,
):
extraer = extraer_datos(
proyecto=proyecto, dias=dias_historico)
validar = validar_calidad(
datos=extraer.outputs["datos"], proyecto=proyecto)
with dsl.If(validar.outputs["calidad_ok"] == True, name="calidad-correcta"):
entrenar = entrenar_modelo(
datos=extraer.outputs["datos"],
dim=dim_embedding, epocas=epocas)
evaluar = evaluar_contra_produccion(
modelo_candidato=entrenar.outputs["modelo"],
datos_test=extraer.outputs["datos_test"],
proyecto=proyecto)
with dsl.If(evaluar.outputs["mejora"] >= umbral_mejora, name="puerta"):
registrar = registrar_modelo(
modelo=entrenar.outputs["modelo"],
metricas=evaluar.outputs["metricas"],
proyecto=proyecto, region=region)
publicar_recomendaciones(
modelo_recurso=registrar.outputs["recurso"],
proyecto=proyecto)pipeline_root és el prefix de Cloud Storage on es desen tots els artefactes de totes les execucions: és el que fa que un model entrenat fa tres mesos continuï existint i continuï sent recuperable. I els paràmetres són a la signatura, no dins del codi: umbral_mejora=0.02 es pot canviar en llançar l'execució sense tocar res, que és el que permet provar configuracions sense editar el pipeline.
Els dos dsl.If imbricats són les dues portes del sistema: la de qualitat de dades i la de millora del model. Si qualsevol falla, el pipeline acaba sense desplegar res. Aquesta és la propietat més important de tot el disseny.
- Component 1: extreure les dades
@component(base_image="python:3.11",
packages_to_install=["google-cloud-bigquery==3.25.0",
"pandas==2.2.2", "pyarrow==17.0.0"])
def extraer_datos(proyecto: str, dias: int,
datos: Output[Dataset], datos_test: Output[Dataset]):
from google.cloud import bigquery
import pandas as pd
consulta = f"""
SELECT l.cliente_hash, l.sku, l.fecha_pedido,
p.categoria, p.precio, p.puntuacion_media,
h.pedidos_previos, h.ticket_medio_previo, h.dias_desde_ultimo
FROM `{proyecto}.alpinashop_analitica.lineas_pedido` l
JOIN `{proyecto}.alpinashop_analitica.productos` p USING (sku)
LEFT JOIN `{proyecto}.alpinashop_analitica.hist_cliente_diario` h
ON h.cliente_hash = l.cliente_hash
AND h.fecha = DATE_SUB(l.fecha_pedido, INTERVAL 1 DAY)
WHERE l.fecha_pedido >= DATE_SUB(CURRENT_DATE(), INTERVAL {dias} DAY)
AND l.cliente_hash IS NOT NULL AND p.activo = TRUE
"""
df = bigquery.Client(project=proyecto).query(consulta).to_dataframe()
# Divisio TEMPORAL: les ultimes 4 setmanes son el test
corte = df["fecha_pedido"].max() - pd.Timedelta(days=28)
df[df["fecha_pedido"] < corte].to_parquet(datos.path)
df[df["fecha_pedido"] >= corte].to_parquet(datos_test.path)
datos.metadata["consulta"] = consulta
datos.metadata["fecha_corte"] = str(corte)
datos.metadata["filas_train"] = int((df["fecha_pedido"] < corte).sum())Quatre decisions que aquest component hereta del mòdul. El JOIN amb hist_cliente_diario a data anterior és la prevenció de fuita temporal de 05-02: l'historial del client s'agafa del dia anterior a la comanda, no d'avui. La divisió és temporal, no aleatòria: les últimes quatre setmanes són el test, perquè en un problema amb estacionalitat la divisió aleatòria produeix mètriques falses. p.activo = TRUE: no s'entrena amb productes descatalogats, la mateixa regla que al recomanador heurístic de 05-01. I les metadades de l'artefacte: datos.metadata["consulta"] desa la consulta exacta que va produir aquestes dades, que és la resposta a "amb quines dades es va entrenar aquest model?" i no costa res escriure-la.
I una nota sobre les versions fixades a packages_to_install: sense elles, el component que avui funciona es pot trencar d'aquí a tres mesos perquè una llibreria ha canviat. Mateixa disciplina que les etiquetes d'imatge de 05-01.
- Component 2: validar la qualitat
Aquí és on el mòdul 4 torna, i no com a decoració.
@component(base_image="python:3.11",
packages_to_install=["pandas==2.2.2", "pyarrow==17.0.0"])
def validar_calidad(datos: Input[Dataset], proyecto: str,
informe: Output[Metrics]) -> bool:
import pandas as pd
df = pd.read_parquet(datos.path)
reglas = {
"volumen_minimo": len(df) >= 50_000,
"clientes_minimos": df["cliente_hash"].nunique() >= 5_000,
"productos_minimos": df["sku"].nunique() >= 500,
"sin_nulos_clave": df[["cliente_hash", "sku"]].isna().sum().sum() == 0,
"precios_positivos": (df["precio"] > 0).all(),
"sin_duplicados": not df.duplicated(
["cliente_hash", "sku", "fecha_pedido"]).any(),
"cobertura_historial": df["pedidos_previos"].notna().mean() >= 0.80,
}
for nombre, ok in reglas.items():
informe.log_metric(nombre, 1.0 if ok else 0.0)
informe.log_metric("filas", float(len(df)))
fallidas = [n for n, ok in reglas.items() if not ok]
if fallidas:
print(f"REGLES FALLIDES: {fallidas}")
return len(fallidas) == 0Les set regles no són arbitràries: són les dimensions de qualitat de Dataplex que vas definir a 04-07, aplicades al conjunt d'entrenament. Volum, unicitat, completitud, validesa, consistència. La mateixa disciplina, en un altre punt del flux.
I val la pena aturar-se en dues. cobertura_historial >= 0.80: si el LEFT JOIN amb l'historial falla per al 40 % de les files —perquè el procés que construeix hist_cliente_diario no es va executar—, el model entrenaria amb la majoria de les seves característiques buides, i sense aquesta regla aquella fallada seria completament invisible: l'entrenament acabaria bé, les mètriques serien una mica pitjors, i ningú no sabria per què. I sin_duplicados: un duplicat esbiaixa el model cap a aquells exemples i, si cau en train i en test alhora, infla les mètriques.
Que una fallada de qualitat aturi el pipeline és la decisió de disseny més important d'aquesta lliçó. L'alternativa —avisar i continuar— produeix el pitjor dels dos mons: un model entrenat amb dades dolentes, desplegat, amb una alerta que ningú no va llegir. Mateix principi que atura el Workflow nocturn de 04-06 quan la comprovació de qualitat falla.
- Component 3: entrenar
L'entrenament no es fa dins del component: el component llança un treball de Vertex AI i espera. Així es pot utilitzar GPU i aïllar recursos sense que el pipeline sencer necessiti una màquina gran.
@component(base_image="python:3.11",
packages_to_install=["google-cloud-aiplatform==1.71.0"])
def entrenar_modelo(datos: Input[Dataset], dim: int, epocas: int,
modelo: Output[Model]):
from google.cloud import aiplatform
aiplatform.init(project="alpinashop-datos", location="europe-west1")
trabajo = aiplatform.CustomContainerTrainingJob(
display_name="reco-two-tower-pipeline",
container_uri=("europe-west1-docker.pkg.dev/alpinashop-prod/"
"alpinashop/entrena-reco:1.0.3"),
)
trabajo.run(
args=[f"--datos={datos.path}", f"--dim={dim}", f"--epocas={epocas}",
f"--salida={modelo.path}"],
replica_count=1, machine_type="n1-standard-8",
accelerator_type="NVIDIA_TESLA_T4", accelerator_count=1,
base_output_dir=modelo.path,
)
modelo.metadata.update({"framework": "tensorflow", "arquitectura": "two-tower",
"dim_embedding": dim, "epocas": epocas,
"imagen": "entrena-reco:1.0.3"})La imatge està versionada (1.0.3, mai latest), com a 05-01 i 05-03. I les metadades del model registren l'arquitectura i els hiperparàmetres, no per documentar sinó perquè són el que es compara quan un model va pitjor que un altre.
- Component 4: avaluar contra producció
Aquest component és el cor del pipeline i conté la idea que més s'oblida als projectes reals.
@component(base_image="python:3.11",
packages_to_install=["google-cloud-aiplatform==1.71.0",
"pandas==2.2.2", "pyarrow==17.0.0"])
def evaluar_contra_produccion(modelo_candidato: Input[Model],
datos_test: Input[Dataset], proyecto: str,
metricas: Output[Metrics]) -> float:
"""Retorna la millora relativa del candidat sobre el model en produccio."""
import pandas as pd
df_test = pd.read_parquet(datos_test.path)
r_cand = calcular_recall_at_k(modelo_candidato.path, df_test, k=10)
r_prod = obtener_metrica_produccion(proyecto, "recall_at_10")
r_base = calcular_recall_baseline_sql(proyecto, df_test, k=10) # 05-01
mejora = (r_cand - r_prod) / max(r_prod, 1e-9)
metricas.log_metric("recall_at_10_candidato", r_cand)
metricas.log_metric("recall_at_10_produccion", r_prod)
metricas.log_metric("recall_at_10_baseline", r_base)
metricas.log_metric("mejora_relativa", mejora)
metricas.log_metric("supera_baseline", 1.0 if r_cand > r_base else 0.0)
# Si no supera ni l heuristica de 30 linies de SQL, no hi ha res a discutir
return -1.0 if r_cand <= r_base else mejoraLes tres mètriques i per què són tres. Comparar el candidat només contra el model en producció té un punt cec: si el model en producció ja era pitjor que l'heurística, un candidat lleugerament millor que ell continua sent pitjor que trenta línies de SQL. Per això la línia base de la decisió DA-003 es mesura en cada execució, per sempre. No és un ritual: és el terra per sota del qual el sistema sencer no mereix existir. I el return -1.0 quan no la supera garanteix que la porta de decisió no s'obri sota cap configuració de llindar.
Un avís realista sobre les mètriques offline, que ja va aparèixer a 05-03: recall@10 mesura sobre comportament passat, i una millora offline no garanteix una millora de negoci. El pipeline decideix quin model és candidat; la validació final continua sent un A/B test amb mètriques de negoci. El que el pipeline evita és que arribi a A/B test una cosa que ni tan sols millora al laboratori.
- La porta de decisió: només es desplega si millora
with dsl.If(evaluar.outputs["mejora"] >= umbral_mejora, name="puerta"):
registrar = registrar_modelo(...)
publicar_recomendaciones(...)Tres línies de codi que converteixen un procés de reentrenament en un sistema amb criteri.
Per què el llindar és 2 % i no 0 %. Perquè una millora del 0,3 % és dins del soroll del mostreig, i desplegar per soroll significa canviar el model cada setmana sense guany real, amb tot el cost de validació, risc i confusió que això comporta. El llindar ha de ser més gran que la variabilitat natural de la mètrica. Com estimar-lo: entrenar el mateix model cinc vegades canviant només la llavor aleatòria i mesurar la desviació de recall@10; si varia un ±1,5 % entre execucions idèntiques, un llindar del 2 % és just i el del 3 % prudent.
Què passa quan la porta no s'obre. El pipeline acaba correctament. No és una fallada: és el sistema funcionant. El model en producció continua, es registra l'execució amb les seves mètriques i es notifica l'equip. Si la porta porta vuit setmanes tancada, això també és informació valuosa: el model actual és bo, o cal canviar d'enfocament en lloc de reentrenar el mateix.
Les quatre portes d'un sistema madur, de les quals AlpinaShop té les dues primeres: qualitat de dades (són utilitzables?, component 2), millora del model (és millor que producció i que l'heurística?, component 4), biaix per segment (millora per a tothom o només en agregat?, ampliació recomanada) i A/B test (millora el negoci?, fora del pipeline).
La tercera mereix una nota, perquè connecta amb 05-02: un model pot millorar el recall@10 global i empitjorar per als clients que compren des de mòbil o per als nous. Afegir una porta que comprovi que cap segment rellevant no empitjora més d'un llindar és una de les millors ampliacions possibles d'aquest pipeline.
- Components 5 i 6: registrar i desplegar
@component(base_image="python:3.11",
packages_to_install=["google-cloud-aiplatform==1.71.0"])
def registrar_modelo(modelo: Input[Model], metricas: Input[Metrics],
proyecto: str, region: str, recurso: Output[str]):
from google.cloud import aiplatform
aiplatform.init(project=proyecto, location=region)
previos = aiplatform.Model.list(filter='display_name="recomendador-alpinashop"')
registrado = aiplatform.Model.upload(
display_name="recomendador-alpinashop",
artifact_uri=modelo.uri,
serving_container_image_uri=(
"europe-docker.pkg.dev/vertex-ai/prediction/tf2-cpu.2-15:latest"),
parent_model=previos[0].resource_name if previos else None,
version_aliases=["candidato"],
labels={"origen": "pipeline", "centro-coste": "analitica"},
)
recurso.value = registrado.resource_nameparent_model fa que sigui una nova versió del model existent i no un model diferent: és el que manté l'historial i permet comparar versions i revertir. I l'àlies és candidato, no produccion: el pipeline registra, i la promoció a producció és un pas explícit —automàtic després de l'A/B test o manual—. Separar registre de promoció és el que permet tenir un model a punt sense que estigui servint.
I el desplegament, que a AlpinaShop no és un endpoint, per la decisió DA-002:
@component(base_image="python:3.11",
packages_to_install=["google-cloud-bigquery==3.25.0",
"google-cloud-aiplatform==1.71.0"])
def publicar_recomendaciones(modelo_recurso: str, proyecto: str):
"""Prediu per lots i publica la taula que consumeix la botiga."""
from google.cloud import aiplatform, bigquery
aiplatform.init(project=proyecto, location="europe-west1")
aiplatform.Model(modelo_recurso).batch_predict(
job_display_name="reco-batch-semanal",
bigquery_source=f"bq://{proyecto}.alpinashop_analitica.v_clientes_activos",
bigquery_destination_prefix=f"bq://{proyecto}.alpinashop_analitica",
machine_type="n1-standard-4", sync=True)
bigquery.Client(project=proyecto).query(f"""
CREATE OR REPLACE TABLE `{proyecto}.alpinashop_analitica.reco_publicada` AS
SELECT cliente_hash, sku, afinidad, posicion,
CURRENT_TIMESTAMP() AS actualizada_en,
'{modelo_recurso}' AS modelo_origen
FROM `{proyecto}.alpinashop_analitica.reco_candidata`
WHERE posicion <= 20
""").result()La columna modelo_origen a la taula publicada és petita i val or: cada recomanació que veu un client porta gravat quina versió del model la va generar. És el que permet respondre, mesos després, "quin model va fer aquesta recomanació?".
- Compilar i executar el pipeline
from kfp import compiler
from google.cloud import aiplatform
compiler.Compiler().compile(pipeline_func=pipeline_reco,
package_path="reco_pipeline.yaml")
aiplatform.init(project="alpinashop-datos", location="europe-west1")
aiplatform.PipelineJob(
display_name="reco-semanal-2026-08-05",
template_path="reco_pipeline.yaml",
pipeline_root="gs://alpinashop-datalake/pipelines/reco",
parameter_values={"dias_historico": 730, "umbral_mejora": 0.02},
enable_caching=True,
).submit(service_account="[email protected]")El YAML compilat és l'artefacte versionable. Va a Git juntament amb el codi Python: és la definició exacta del pipeline i permet reproduir una execució de fa mesos.
enable_caching=True és una funció excel·lent i un parany a parts iguals. Si un component s'executa amb exactament les mateixes entrades que una vegada anterior, Vertex AI reutilitza el resultat sense tornar-lo a executar, cosa que en desenvolupament estalvia moltíssim temps i diners. Però compte: si el component d'extracció té els mateixos paràmetres, la memòria cau pot retornar dades antigues encara que BigQuery tingui dades noves. La defensa és incloure un paràmetre que canviï —la data d'execució— als components que llegeixen dades fresques.
service_account també importa: el pipeline s'executa amb un compte dedicat de permisos mínims —llegir les vistes necessàries, escriure al bucket d'artefactes, gestionar models— i cap més. És 03-04 aplicat a un procés automàtic.
- Execució programada i disparada per esdeveniments
Tres maneres de llançar el pipeline, i AlpinaShop les fa servir totes tres:
Programada. Vertex AI Pipelines té un programador propi, que es crea amb gcloud ai pipeline-jobs schedules create --cron="0 3 * * 1" --pipeline-job-file=reco_pipeline.yaml --max-concurrent-run-count=1. Aquell últim paràmetre importa: evita que una execució que s'allargui se solapi amb la següent, cosa que produeix condicions de cursa en escriure a la mateixa taula.
Des de Workflows, que és el coherent amb la decisió de 04-06. El procés nocturn de dades ja existeix; el pipeline de ML s'encadena després que les dades estiguin a punt i validades:
- lanzar_pipeline_reco:
call: http.post
args:
url: ${"https://" + region + "-aiplatform.googleapis.com/v1/projects/"
+ proyecto + "/locations/" + region + "/pipelineJobs"}
auth: {type: OAuth2}
body:
displayName: ${"reco-" + text.substring(time.format(sys.now()), 0, 10)}
templateUri: "gs://alpinashop-datalake/pipelines/reco_pipeline.yaml"
runtimeConfig:
gcsOutputDirectory: "gs://alpinashop-datalake/pipelines/reco"
parameterValues: {dias_historico: 730, umbral_mejora: 0.02}
result: respuestaAquesta és la integració que importa i és la raó d'encadenar-ho així: el pipeline de ML no s'executa si les dades de la nit van fallar. Sense aquell encadenament, un dilluns en què el procés de dades hagués fallat, el pipeline entrenaria amb dades incompletes —i encara que la porta de qualitat probablement l'aturaria, és millor no arribar a aquell punt.
Per esdeveniments. El dispar des del repositori en canviar el codi del pipeline és el nivell 2 de maduresa i correspon a Cloud Build (06-01): un push a la branca principal reconstrueix la imatge d'entrenament, recompila el YAML, executa el pipeline en desenvolupament i, si passa, el promou. Aquí queda anticipat.
- Metadades i llinatge: el que et salva en una auditoria
Vertex ML Metadata registra automàticament cada execució, cada artefacte i cada relació entre ells. No cal fer res especial: utilitzar components de KFP amb entrades i sortides tipades ja ho produeix.
Les cinc preguntes que respon, i quan les necessites:
| Pregunta | Quan la fas | Què la respon |
|---|---|---|
| Amb quines dades es va entrenar aquesta versió? | Auditoria, incidència | Llinatge de l'artefacte Dataset |
| Quina versió va fer aquesta predicció? | Reclamació de client | modelo_origen a la taula publicada |
| Què va canviar entre la v7 i la v8? | El model va empitjorar | Metadades i mètriques de totes dues versions |
| Quins models fan servir aquesta dada? | Petició de supressió (RGPD) | Llinatge invers |
| Es pot reproduir el model de març? | Verificació | YAML + paràmetres + artefactes |
La quarta és la que sorprèn tothom. Un client exerceix el seu dret de supressió i les seves dades s'esborren de pedidos. Però aquell client va contribuir a l'entrenament de tres models, i les seves dades estan incorporades als pesos. El llinatge permet saber quins models el van incloure i decidir amb criteri jurídic què fer —normalment, assegurar que el pròxim reentrenament no l'inclogui i documentar el raonament—. Sense llinatge, la pregunta és directament irresponsable.
Consultar-ho és directe: aiplatform.PipelineJob.list(filter='display_name:"reco-*"', order_by="create_time desc") enumera totes les execucions amb el seu estat, i PipelineJob.get(...) amb task_details retorna els artefactes d'una execució concreta amb les seves metadades.
I la pràctica complementària: escriure la fitxa de model (model card) com a artefacte del mateix pipeline, amb quatre apartats —per a què serveix, amb quines dades es va entrenar, quines mètriques té, quines limitacions conegudes— generats automàticament a partir de les metadades. Documentació que no queda obsoleta perquè es regenera a cada execució.
- Model Monitoring i el reentrenament
El pipeline resol el "com reentrenar". Falta el "quan".
Tres estratègies, i la millor és la combinació:
| Estratègia | Com funciona | Avantatge | Risc |
|---|---|---|---|
| Periòdica | Cada setmana, passi el que passi | Simple i predictible | Reentrena sense necessitat |
| Per deriva | Quan Model Monitoring alerta | Eficient | Pot trigar a detectar |
| Per degradació | Quan la mètrica de negoci baixa | Directament rellevant | Ja hi va haver dany |
Per a AlpinaShop: periòdica setmanal com a base, amb reentrenament addicional si la deriva o la mètrica de negoci ho demanen. Costa poc (apartat 16) i la porta de decisió garanteix que no es desplegui res que no millori. Els dos fenòmens que cal vigilar, ja vistos a 05-01. Deriva de dades: canvia la distribució de les entrades, i a AlpinaShop és estacional i esperada —a l'octubre entra material d'hivern on al juliol hi havia sandàlies de trekking—, així que una alerta de deriva no és una avaria. Deriva de concepte: canvia la relació entre entrades i resultat —un competidor abaixa preus i canvia el comportament de compra—, i és més difícil de detectar i més greu.
Com que AlpinaShop serveix per lots i no té endpoint, el monitoratge es fa sobre les dades, comparant la distribució setmanal contra la de l'entrenament:
CREATE OR REPLACE VIEW `alpinashop-datos.alpinashop_analitica.v_deriva_semanal` AS
WITH actual AS (
SELECT p.categoria, COUNT(*) / SUM(COUNT(*)) OVER () AS pct
FROM `alpinashop-datos.alpinashop_analitica.lineas_pedido` l
JOIN `alpinashop-datos.alpinashop_analitica.productos` p USING (sku)
WHERE l.fecha_pedido >= DATE_SUB(CURRENT_DATE(), INTERVAL 7 DAY)
GROUP BY p.categoria
)
SELECT a.categoria,
ROUND(a.pct, 4) AS pct_actual,
ROUND(r.pct, 4) AS pct_entrenamiento,
ROUND(ABS(a.pct - r.pct), 4) AS diferencia,
IF(ABS(a.pct - r.pct) > 0.10, 'ALERTA', 'OK') AS estado
FROM actual a
FULL OUTER JOIN `alpinashop-datos.alpinashop_analitica.dist_entrenamiento` r
USING (categoria)
ORDER BY diferencia DESC;I l'advertiment de 05-01, vigent: un llindar massa sensible produeix soroll, el soroll produeix indiferència, i la indiferència fa que ningú no miri l'alerta que sí que importava.
- Versionatge i reproductibilitat
Reproduir un model exigeix que quatre coses estiguin versionades. Gairebé tots els equips versionen la primera i cap de les altres tres.
| Què | Com es versiona | On viu |
|---|---|---|
| Codi | Git | Cloud Source Repositories (06-02) o GitHub |
| Dades | Consulta + data de tall a les metadades | Metadades de l'artefacte |
| Model | Versions del Model Registry | Vertex AI |
| Entorn | Imatge de contenidor amb etiqueta | Artifact Registry |
Les dades són la part difícil. Una taula de BigQuery canvia constantment: no se'n pot "fer commit". Tres solucions, de menor a major cost: desar la consulta i la data de tall a les metadades de l'artefacte (el que fa el component 1), barat i suficient si les taules són append-only; fer servir els viatges en el temps de BigQuery (FOR SYSTEM_TIME AS OF), que consulten l'estat d'una taula en un instant passat dins de la finestra de retenció; i materialitzar una instantània del conjunt a Cloud Storage, que és el que el pipeline_root fa de facto en desar el Parquet.
La llista de comprovació de reproductibilitat, que convé verificar una vegada i no tornar-se'n a preocupar:
- [ ] El YAML compilat és a Git amb la seva etiqueta de versió.
- [ ] La imatge d'entrenament té etiqueta fixa, mai
latest. - [ ] Els
packages_to_installporten versió fixada. - [ ] La consulta d'extracció és a les metadades de l'artefacte.
- [ ] Les llavors aleatòries estan fixades i registrades.
- [ ] Els hiperparàmetres són a metadades, no al codi.
- [ ] Els artefactes de cada execució persisteixen a
pipeline_root.
- Cost i sostenibilitat del cicle
Aquí es tanca l'argument que va començar a 05-01 amb la decisió DA-002, i ara amb números.
Opció A: endpoint en línia 24×7. Dos nodes n1-standard-4 encesos tot el mes per servir recomanacions que canvien una vegada per setmana, facturant 24 hores al dia hi hagi trànsit o no: de l'ordre de centenars d'euros al mes.
Opció B: predicció per lots setmanal. Un treball que s'executa els dilluns, processa 45.000 clients en uns minuts i mor, més l'entrenament setmanal en una T4 i l'execució del pipeline: de l'ordre d'unes poques desenes d'euros al mes.
Un ordre de magnitud de diferència, i sense cap pèrdua funcional: les recomanacions se serveixen des d'una taula precalculada a Firestore o Memorystore (02-06), amb latència de microsegons i sense dependre que cap model estigui viu. Verifica els preus vigents al tarificador oficial; el que no canvia és la proporció.
Les cinc mesures de sostenibilitat del cicle: predicció per lots en lloc d'endpoint sempre que el resultat no depengui de la sessió en curs; VM Spot amb punts de control per a l'entrenament (05-03), perquè els reentrenaments setmanals no tenen urgència; memòria cau del pipeline en desenvolupament, amb la precaució de l'apartat 11; freqüència de reentrenament proporcional al ritme de canvi, ja que reentrenar diàriament un model els patrons del qual canvien en mesos és gastar per gastar; i retenció d'artefactes, amb una política de cicle de vida a Cloud Storage (02-02) que passi a Nearline als 30 dies i esborri als 180, perquè els Parquet i els punts de control de cada execució s'acumulen a pipeline_root sense límit.
I l'observació de fons, més enllà de la factura: el cost computacional té un cost energètic. Reentrenar innecessàriament, deixar GPU enceses o servir 24×7 el que es pot precalcular no és només car: és consum sense propòsit.
- El checklist de ML responsable
Aquest apartat recull tot el que el mòdul ha anat deixant pel camí. És el checklist que cal completar abans que un model prengui decisions que afectin persones.
Dades
- [ ] Quina és la base legal i està la finalitat coberta per la informació donada en recollir les dades?
- [ ] Es fa servir la mínima dada necessària, entrenant sobre vistes pseudonimitzades (04-07) i no sobre taules amb dades personals directes?
- [ ] Hi ha termini de conservació del conjunt d'entrenament i es pot atendre una supressió? El llinatge diu quins models van incloure les dades d'una persona?
- [ ] Les dades són representatives de la població sobre la qual s'aplicarà el model?
Biaix i equitat
- [ ] S'ha mesurat el rendiment per segment —dispositiu, canal, geografia, antiguitat— i no només en agregat? Hi ha algun grup per al qual funcioni significativament pitjor?
- [ ] Existeix un bucle de retroalimentació en què el model fabriqui la realitat que prediu (05-02, apartat 15)?
- [ ] La mètrica d'avaluació reflecteix el que importa al negoci i a les persones afectades?
Explicabilitat
- [ ] Es pot explicar per què el model va prendre una decisió concreta, amb atribucions locals, i en termes comprensibles per a qui rep l'explicació?
- [ ] La importància global de les característiques té sentit, o n'hi ha alguna amb pes sospitosament alt que reveli una fuita?
Supervisió humana
- [ ] Hi ha una persona en el bucle per a les decisions amb impacte significatiu, i un canal de reclamació amb termini de resposta?
- [ ] Es pot desactivar el model ràpidament i tornar al procés anterior?
- [ ] Algú té assignada la responsabilitat de vigilar-lo, amb nom i cognoms?
Documentació
- [ ] Existeix una fitxa de model amb propòsit, dades, mètriques i limitacions conegudes?
- [ ] Està registrat qui va aprovar el desplegament i sobre quina evidència, i permet el llinatge reconstruir quines dades van entrenar quina versió?
Compliment
- [ ] Es tracta d'una decisió automatitzada de l'article 22 del RGPD? Si hi ha dubte, cal preguntar.
- [ ] Quina és la classificació sota l'AI Act, quines obligacions comporta, i continua l'ús actual corresponent a aquella classificació?
- [ ] Cal una avaluació d'impacte (AIPD)? Es compleixen les obligacions de transparència?
- [ ] Ho ha revisat compliment normatiu o el DPD?
Recomanació expressa i final del mòdul. Aquest checklist és una ajuda per estructurar el treball tècnic i no substitueix un dictamen jurídic. La classificació d'un sistema sota l'AI Act, la determinació de si un tractament constitueix decisió automatitzada sota l'article 22 del RGPD, la necessitat d'una avaluació d'impacte i l'abast de les obligacions de transparència han de ser determinats per un professional de compliment normatiu o pel delegat de protecció de dades abans de posar el sistema en producció. Totes les dades i escenaris d'aquest curs són ficticis.
I la regla que resumeix el mòdul sencer: si en recórrer aquest checklist hi ha més de dues caselles que no pots marcar amb seguretat, el model no està a punt per a producció, per bo que sigui el seu recall@10.
Errors Habituals i Consells
No tenir porta de decisió. Desplegar el model nou perquè és el nou és l'error més freqüent i el més car. Tres línies de dsl.If ho eviten.
No mesurar contra la línia base. Si el candidat no supera trenta línies de SQL, no hi ha sistema de ML que justificar.
Llindar de millora al 0 %. Desplegar per soroll estadístic produeix canvis setmanals sense guany real. El llindar ha de superar la variabilitat natural de la mètrica.
Refiar-se de la memòria cau del pipeline sense pensar. Pot retornar dades antigues d'una execució prèvia. Inclou la data com a paràmetre als components que llegeixen dades fresques.
Que una fallada de qualitat no aturi el pipeline. Avisar i continuar produeix el pitjor: model dolent desplegat amb una alerta que ningú no va llegir.
Utilitzar latest en imatges o no fixar versions de llibreries, que trenca la reproductibilitat; i executar el pipeline amb permisos amplis en lloc d'un compte de servei dedicat de permisos mínims.
Confondre millora offline amb millora de negoci. El pipeline decideix què és candidat; l'A/B test decideix què va a producció.
Reentrenar més del que canvia el fenomen. Diàriament quan els patrons canvien en mesos és despesa sense retorn.
Consell: registra les tres mètriques sempre —candidat, producció i línia base—: converteix qualsevol discussió sobre si el model millora en una consulta. Posa el nom d'una persona responsable de cada model, perquè un model sense amo és un model que ningú no mira. I genera la fitxa de model com a artefacte del pipeline: la documentació que es regenera sola no queda obsoleta.
Exercicis
Exercici 1
El pipeline fa sis setmanes que s'executa. En les sis, la porta de decisió no s'ha obert: cap candidat no ha superat el llindar del 2 %. En Dani proposa abaixar el llindar al 0,5 % "perquè almenys s'actualitzi". Analitza la proposta i digues què faries.
Exercici 2
Dissenya la porta de biaix per segment que falta al pipeline. Indica quins segments comprovaries, quin criteri aplicaries i com ho implementaries com a component.
Exercici 3
Un client reclama: diu que el web li va recomanar uns grampons incompatibles amb les seves botes, els va comprar, i no li serveixen. Demana explicacions. Enumera quina informació necessites, què hauria d'estar registrat, i què canviaries al sistema.
Solucions
Solució 1
La proposta d'en Dani és incorrecta, però la pregunta que hi ha al darrere és bona.
Per què abaixar el llindar és un error. El llindar del 2 % es va fixar perquè és més gran que la variabilitat natural de la mètrica. Amb un 0,5 %, es desplegarien models la "millora" dels quals és indistingible del soroll de la llavor aleatòria. Es canviaria de model cada setmana, cada canvi requeriria validació, i el sistema aniria fent tombs sense cap guany. I l'objectiu del pipeline no és actualitzar el model: és que el millor model sigui en producció. Que la porta no s'obri significa que el model actual continua sent el millor, que és exactament el resultat desitjat.
Però sis setmanes seguides sí que és informació, i cal investigar-la. Quatre hipòtesis, per ordre de probabilitat. (1) El model actual ja és bo i l'enfocament ha arribat al seu sostre, el més probable: reentrenar la mateixa arquitectura amb dades lleugerament diferents no produeix salts, i la resposta no és canviar el llindar sinó canviar d'enfocament —afegir característiques, una altra arquitectura, senyals de sessió— o acceptar que el model està bé i abaixar la freqüència a mensual. (2) No hi ha prou dades noves: amb 730 dies d'història, una setmana més és un 0,14 % addicional. (3) Hi ha un problema al pipeline: està la memòria cau retornant sempre les mateixes dades? agafa el component d'extracció la mateixa finestra? És la primera comprovació tècnica. (4) La mètrica no captura la millora: recall@10 pot estar saturat, i potser el model millora en una cosa que no veu —diversitat, cobertura del catàleg, resultats per a clients nous—.
-- Evolucio de les tres metriques al llarg de les execucions
SELECT
fecha_ejecucion,
ROUND(recall_candidato, 4) AS candidato,
ROUND(recall_produccion, 4) AS produccion,
ROUND(recall_baseline, 4) AS baseline,
ROUND(mejora_relativa, 4) AS mejora,
filas_entrenamiento
FROM `alpinashop-datos.alpinashop_analitica.historico_pipeline_reco`
ORDER BY fecha_ejecucion DESC LIMIT 12;Què faria, en tres passos. Primer, mantenir el llindar al 2 %: no es toca un criteri de qualitat per aconseguir el resultat que un vol veure, i això és exactament el que el llindar existeix per impedir. Segon, investigar amb la consulta anterior: si el recall_candidato és pràcticament idèntic setmana rere setmana, és la hipòtesi 2 o 3; si oscil·la sense tendència, la 1. I tercer, actuar segons el diagnòstic: si l'enfocament va tocar sostre, abaixar la freqüència a mensual (estalvi directe) i obrir una línia de treball amb característiques noves; si és un problema de pipeline, arreglar-lo; si és la mètrica, afegir mètriques secundàries de diversitat i cobertura i considerar un A/B test encara que la millora offline sigui petita.
I una observació cultural que val més que la tècnica: que un equip tingui un criteri objectiu i el respecti quan el resultat no li agrada és el senyal més fiable de maduresa. El llindar només serveix si es manté també quan incomoda.
Solució 2
Segments a comprovar a AlpinaShop:
| Segment | Valors | Per què importa |
|---|---|---|
| Antiguitat del client | Nou (<3 comandes) / recurrent | L'arrencada en fred afecta els nous |
| Dispositiu | Mòbil / escriptori | Bucle de retroalimentació (05-02) |
| Geografia | Península / Balears / Canàries | Catàleg i logística diferents |
| Categoria de compra | Tèxtil / material tècnic / calçat | Volums molt diferents |
| Volum de despesa | Alt / mitjà / baix | Que no millori només per als que més compren |
Criteri de la porta, amb tres condicions: que cap segment amb volum rellevant no empitjori més d'un 5 % relatiu respecte del model en producció; que la millora agregada del 2 % es mantingui; i que només s'avaluïn segments amb almenys 500 clients al test, perquè per sota la mètrica és soroll.
@component(base_image="python:3.11",
packages_to_install=["pandas==2.2.2", "pyarrow==17.0.0"])
def validar_segmentos(modelo_candidato: Input[Model], datos_test: Input[Dataset],
metricas_segmento: Output[Metrics],
tolerancia: float = -0.05,
minimo_clientes: int = 500) -> bool:
import pandas as pd
df = pd.read_parquet(datos_test.path)
problemas = []
for columna in ["segmento_antiguedad", "dispositivo", "zona", "categoria_top"]:
for valor, grupo in df.groupby(columna):
n = grupo["cliente_hash"].nunique()
if n < minimo_clientes:
continue
r_cand = calcular_recall_at_k(modelo_candidato.path, grupo, k=10)
r_prod = obtener_metrica_produccion_segmento(columna, valor)
delta = (r_cand - r_prod) / max(r_prod, 1e-9)
metricas_segmento.log_metric(f"{columna}={valor}", delta)
if delta < tolerancia:
problemas.append(f"{columna}={valor}: {delta:+.1%} (n={n})")
if problemas:
print("SEGMENTS QUE EMPITJOREN:", problemas)
return len(problemas) == 0On va al pipeline: entre l'avaluació agregada i la porta de decisió, imbricant un segon dsl.If sobre segmentos.output == True dins del dsl.If de la millora agregada, de manera que la porta exigeixi totes dues condicions.
Tres consideracions importants. El mínim de 500 clients és imprescindible: sense ell, un segment amb 12 clients produiria oscil·lacions enormes que bloquejarien desplegaments vàlids, i una porta que bloqueja sempre acaba desactivada. La tolerància del −5 % no és zero per una raó: exigir que cap segment no empitjori gens és impossible a la pràctica, i el que es persegueix és evitar degradacions significatives, no fluctuacions. I els resultats es registren encara que no bloquegin: metricas_segmento desa el delta de tots els segments avaluats, i amb el temps aquella sèrie revela tendències —si "mòbil" porta vuit setmanes per sota de l'agregat sense arribar a bloquejar, hi ha un problema estructural que cap execució individual no hauria destapat—.
I un advertiment d'honestedat: aquesta porta detecta que un model empitjora per a un grup respecte de l'anterior, però no detecta que funcioni malament per a aquell grup des de sempre. Per a això cal mirar els valors absoluts per segment, no només els deltes. Totes dues coses van al checklist de l'apartat 17.
Solució 3
Quina informació necessites per respondre: quina recomanació exacta se li va mostrar, quan i en quin context (fitxa, cistella, correu); quina versió del model la va generar; quines dades van entrenar aquella versió; per què el model va puntuar alt aquell producte per a aquell client (atribució local); quina informació de compatibilitat existia a la fitxa en aquell moment; i quin text acompanyava la recomanació al web.
Què hauria d'estar registrat. La major part ja ho està si es va seguir la lliçó:
| Dada | On és | Existeix? |
|---|---|---|
| Recomanació mostrada | reco_publicada amb actualizada_en |
Sí |
| Versió del model | Columna modelo_origen |
Sí, apartat 10 |
| Dades d'entrenament | Metadades de l'artefacte Dataset |
Sí, apartat 5 |
| Mètriques d'aquella versió | Model Registry | Sí |
| Fitxa del producte en aquella data | Historial de canvis del catàleg | Probablement no |
| Text mostrat al costat de la recomanació | Configuració del front | Probablement no |
Les dues que falten són justament les que més importen aquí, i això ja és una troballa de l'anàlisi.
I ara la part incòmoda, que és on hi ha la lliçó real: encara que tot estigués registrat, la resposta tècnica seria "el model va suggerir aquell producte perquè clients amb un perfil de compra similar van comprar tots dos". Això és cert, és explicable, i no resol res per al client, perquè el problema no és que la recomanació fos estadísticament rara: és que el sistema no sap res de compatibilitat tècnica. Un recomanador aprèn associacions de compra i no sap que un grampó automàtic necessita una bota amb insert a la puntera. No ho va aprendre mai perquè ningú no l'hi va ensenyar, i aquella informació no és a lineas_pedido: és a la fitxa tècnica, i cap component del pipeline no la mira.
Què canviaria al sistema, en quatre mesures. La primera, un filtre dur de compatibilitat aplicat abans que cap model, que és la que resol el problema de debò:
CREATE OR REPLACE TABLE `alpinashop-datos.alpinashop_analitica.reco_publicada` AS
SELECT r.*
FROM `alpinashop-datos.alpinashop_analitica.reco_candidata` r
LEFT JOIN `alpinashop-datos.alpinashop_analitica.incompatibilidades` i
ON i.sku_a = r.sku
WHERE r.posicion <= 20
AND NOT EXISTS (
-- No recomanar res incompatible amb el que el client ja te
SELECT 1
FROM `alpinashop-datos.alpinashop_analitica.v_productos_cliente` c
JOIN `alpinashop-datos.alpinashop_analitica.incompatibilidades` x
ON x.sku_a = c.sku AND x.sku_b = r.sku
WHERE c.cliente_hash = r.cliente_hash
);És la mateixa lliçó que el stock > 0 de 05-01, en una altra dimensió. Hi ha regles de negoci que no s'han d'aprendre de les dades: s'apliquen com a filtre. Un model, per bo que sigui, no ha de ser l'última línia de defensa davant d'una recomanació tècnicament perillosa.
La segona, avisos de compatibilitat a la interfície: els productes amb requisits tècnics —grampons, fixacions, sistemes d'ancoratge— porten un avís visible al costat de la recomanació ("Requereix bota amb insert davanter. Comprova la compatibilitat."). És barat i evita la majoria d'aquests casos. La tercera, llenguatge honest: "altres clients també van comprar" descriu el que el sistema fa de debò, mentre que "et recomanem" o "compatible amb la teva compra" suggereixen un judici tècnic que el sistema no està fent, i la formulació importa jurídicament, no només estèticament. I la quarta, una porta addicional al pipeline: cap desplegament si la taula d'incompatibilitats no està actualitzada o si el filtre no s'aplica, comprovat amb un cas de prova conegut.
La resposta al client: reconèixer el problema sense escudar-se en el sistema, acceptar la devolució encara que el producte s'hagi utilitzat per provar-lo, explicar en llenguatge planer que el suggeriment es basava en patrons de compra i no en una verificació de compatibilitat, i informar que s'ha corregit. I registrar el cas, perquè un client que reclama representa molts que no ho van fer.
I la reflexió que tanca el mòdul: aquest cas no és una fallada del model, que va fer exactament el que se li va demanar —trobar productes que gent semblant compra junts—. La fallada és del sistema que envolta el model, que no va incorporar una regla de negoci evident. I això és MLOps: no la part d'entrenar, sinó tota la part que impedeix que un model correcte produeixi un resultat incorrecte.
Conclusió
El mòdul 5 es tanca aquí, i es tanca amb el sistema complet.
Saps per què la majoria dels models no arriben a producció, i cap de les sis raons no és d'aprenentatge automàtic: manca de reproductibilitat, dependència de persones, absència de criteri de desplegament, manca de traçabilitat, absència de vigilància i impossibilitat de fer marxa enrere. Tots són problemes d'enginyeria i d'operacions. I coneixes l'asimetria que fa que MLOps no sigui DevOps amb un altre nom: en un sistema de ML canvien el codi i les dades, la degradació passa sola perquè el món es mou, la prova d'acceptació és estadística i no determinista, i la fallada típica és silenciosa: un model que encerta un 15 % menys no llança cap excepció.
Coneixes els tres nivells de maduresa i que el salt del 0 a l'1 —de notebooks i execucions manuals a un pipeline reproduïble que s'executa sol— aporta la major part del valor. El nivell 2, amb CI/CD complet, arriba amb Cloud Build a 06-01.
Has construït el pipeline real d'AlpinaShop amb Vertex AI Pipelines i el SDK de KFP: components, artefactes, paràmetres i un graf les dependències del qual s'infereixen soles. Extreure les dades amb la divisió temporal i el JOIN històric que evita la fuita de 05-02. Validar la qualitat reutilitzant les regles de Dataplex de 04-07, amb la decisió de disseny més important de la lliçó: que una fallada de qualitat atura el pipeline en lloc d'avisar i continuar. Entrenar llançant un treball de Vertex AI amb imatge versionada. Avaluar contra el model en producció i contra la línia base heurística, sempre les tres mètriques, perquè si el candidat no supera trenta línies de SQL no hi ha sistema que justificar. I la porta de decisió: tres línies de dsl.If que converteixen un procés de reentrenament en un sistema amb criteri, amb un llindar més gran que la variabilitat natural de la mètrica i amb la disposició a respectar-lo quan el resultat no agrada.
Saps registrar com a nova versió amb parent_model i àlies candidato —separant registre de promoció—, i publicar per lots la taula que consumeix la botiga, amb la columna modelo_origen que fa traçable cada recomanació que veu un client. Compiles el pipeline a un YAML que va a Git, l'executes amb un compte de servei de permisos mínims, coneixes el parany de la memòria cau, i l'encadenes des de Workflows perquè no s'executi si el procés nocturn de dades va fallar.
Tens les metadades i el llinatge responent a les cinc preguntes d'una auditoria, inclosa la que sorprèn tothom: quins models van incloure les dades d'un client que exerceix el seu dret de supressió. Saps quan reentrenar combinant periodicitat, deriva i degradació, amb l'advertiment que una alerta de deriva estacional no és una avaria i que el soroll produeix indiferència. Tens les quatre coses que cal versionar —codi, dades, model i entorn— amb la seva llista de comprovació. I tens el càlcul que tanca l'argument del mòdul: un endpoint 24×7 per servir recomanacions que canvien una vegada per setmana costa un ordre de magnitud més que la predicció per lots, sense cap avantatge funcional.
Finalment, el checklist de ML responsable: dades amb base legal i mínim necessari, biaix mesurat per segment i no només en agregat, explicabilitat comprensible per a qui la rep, supervisió humana amb canal de reclamació i capacitat de desactivar, documentació amb fitxa de model i llinatge, i compliment amb RGPD i AI Act revisat per compliment normatiu abans de producció, no després. Amb la regla que resumeix el mòdul: si hi ha més de dues caselles que no pots marcar amb seguretat, el model no està a punt, per bo que sigui el seu recall@10.
I mira on és AlpinaShop ara. Té una plataforma de dades governada del mòdul 4, models que prediuen, classifiquen, generen i recomanen, i un pipeline que reentrena sol, valida sol, decideix sol i desplega només quan millora, amb traçabilitat completa i amb portes que impedeixen que una dada dolenta o un model pitjor arribin a producció.
Però ara aixeca la vista de l'aprenentatge automàtic i mira la resta del sistema, perquè la comparació és demolidora. El recomanador té més disciplina d'enginyeria que la botiga.
El catàleg web en Flask que en Dani manté es desplega copiant fitxers a mà. La infraestructura del mòdul 3 —la VPC, el balancejador, les regles de Cloud Armor, els certificats— es va crear amb comandes de gcloud que són a l'historial del terminal de la Marta i enlloc més: si calgués reconstruir l'entorn en una altra regió, ningú no sabria exactament com. No hi ha proves automàtiques: es puja i es comprova mirant el web. No hi ha entorns separats de debò: alpinashop-dev existeix, però el que s'hi prova no és el que es desplega a alpinashop-prod. I quan alguna cosa va malament en producció, la manera d'assabentar-se'n és que un client escrigui un correu. És exactament el nivell 0 de maduresa que acabes de superar per als models, aplicat a tota la resta.
Al mòdul 6, DevOps i monitoratge, AlpinaShop porta a la resta de la plataforma la disciplina que acaba d'aplicar al cicle del model. Començarem per 06-01, Cloud Build, la integració contínua que construeix, prova i publica cada canvi de forma automàtica i reproduïble —i que, de passada, és la peça que faltava per portar el pipeline d'aquesta lliçó del nivell 1 al nivell 2—. Després arribaran Cloud Source Repositories i la gestió del codi, Cloud Functions per a les peces per esdeveniments que aquest mòdul ha anat deixant pendents —l'anàlisi de cada imatge nova del topic imagenes-subidas, entre altres—, Cloud Monitoring per saber què està passant sense obrir la consola, Deployment Manager i Terraform perquè la infraestructura deixi de viure a l'historial d'un terminal i passi a estar escrita, versionada i revisable com qualsevol altre codi, i Cloud Logging i Trace perquè quan alguna cosa falli es pugui esbrinar per què en minuts i no en tardes.
Hi ha aplicació, hi ha dades i hi ha models. Tots funcionen, i tots depenen que dues persones se'n recordin d'executar-los. Ha arribat el moment d'automatitzar el lliurament, escriure la infraestructura com a codi i assabentar-se dels problemes abans que els clients.
Curs de Google Cloud Platform (GCP)
Mòdul 1: Introducció a Google Cloud Platform
- Què és Google Cloud Platform?
- Configuració del teu compte de GCP
- Descripció general de la consola de GCP
- Projectes, jerarquia de recursos i facturació
- Regions, zones i model de responsabilitat compartida
- Cloud Shell i la CLI de gcloud
Mòdul 2: Serveis principals de GCP
- Compute Engine: màquines virtuals a Google Cloud
- Cloud Storage: emmagatzematge d'objectes
- Cloud SQL: bases de dades relacionals gestionades
- App Engine: plataforma com a servei
- Google Kubernetes Engine (GKE)
- Bases de dades NoSQL: Firestore, Bigtable i Spanner
- Com triar el servei de còmput adequat
Mòdul 3: Xarxes i seguretat
- Xarxes VPC
- Balanceig de càrrega al núvol
- Cloud CDN
- Gestió d'identitat i accés (IAM)
- Cloud Armor
- Secrets i xifratge: Secret Manager i Cloud KMS
- Cloud DNS, certificats TLS i publicació segura de serveis
Mòdul 4: Dades i anàlisi
- BigQuery: el magatzem de dades analític
- Cloud Dataflow: processament de dades per lots i en temps real
- Cloud Dataproc: Spark i Hadoop gestionats
- Cloud Pub/Sub: missatgeria asíncrona
- Cloud Data Fusion: integració de dades sense codi
- Orquestració de pipelines amb Cloud Composer i Workflows
- Govern de les dades i taulers amb Dataplex i Looker Studio
Mòdul 5: Aprenentatge automàtic i IA
- Vertex AI: la plataforma d'aprenentatge automàtic de GCP
- AutoML: models a mida sense escriure codi
- TensorFlow a GCP: entrenament i servei de models
- API de llenguatge natural
- API de visió
- IA generativa a Vertex AI: models Gemini i incrustacions
- MLOps: del model al producte amb Vertex AI Pipelines
Mòdul 6: DevOps i monitoratge
- Cloud Build: integració contínua a GCP
- Cloud Source Repositories i gestió del codi font
- Cloud Functions: funcions sense servidor
- Cloud Monitoring (abans Stackdriver): mètriques, taulers i alertes
- Cloud Deployment Manager i infraestructura com a codi nativa
- Cloud Logging i Cloud Trace: registres, traces i diagnòstic
- Terraform a GCP: infraestructura com a codi a la pràctica
Mòdul 7: Temes avançats de GCP
- Híbrid i multinúvol amb Anthos
- Computació sense servidor amb Cloud Run
- Xarxes avançades: VPC compartida, aparellament i connectivitat híbrida
- Bones pràctiques de seguretat
- Gestió i optimització de costos
- Fiabilitat: SLO, alta disponibilitat i recuperació de desastres
- Govern a escala: organització, polítiques i auditoria
