← Todos los temas
Día 16 · IA en el mundo real

MLOps: mantener viva una IA

Qué es MLOps y por qué los modelos de IA necesitan mantenimiento.

Definición

MLOps (machine learning operations, operaciones de aprendizaje automático) es el conjunto de prácticas para llevar un modelo de aprendizaje automático a uso real y mantenerlo funcionando bien con el tiempo: datos y entrenamiento reproducibles, versiones registradas, despliegue seguro, monitoreo y reentrenamiento. Aplica a los modelos las ideas de DevOps y CI/CD (día 7), con una diferencia clave: un modelo puede degradarse sin que nadie cambie el código, porque el mundo del que aprendió cambia.

Google Cloud lo resume en su guía de MLOps: en un sistema de ML real, solo una pequeña fracción es código de ML; el resto es recolección y validación de datos, infraestructura de servicio, monitoreo y automatización. Ese «resto» es lo que MLOps organiza.

El ciclo de vida de un modelo

EtapaQué ocurreArtefacto que queda
DatosRecolectar, limpiar, validar el esquema y versionar el conjunto de datosConjunto de datos con versión
EntrenamientoAjustar el modelo con código y parámetros reproduciblesModelo entrenado + parámetros + versión del código
EvaluaciónMedir con datos que el modelo no vio, contra el modelo actualMétricas y reporte de comparación
RegistroGuardar la versión con sus métricas, datos de origen y estadoEntrada en el registro de modelos
DesplieguePonerlo a responder peticiones reales, de forma gradualVersión en producción
MonitoreoVigilar datos de entrada, predicciones, métricas de negocio y latenciaAlertas y tableros
ReentrenamientoVolver a entrenar con datos recientes, por calendario o por alertaNueva versión candidata

Deriva: por qué un modelo se desfasa

La deriva (drift) es el cambio, con el tiempo, entre el mundo con el que se entrenó el modelo y el mundo en el que trabaja.

TipoQué cambiaEjemplo
Deriva de datos (data drift)La distribución de las entradasUna app de entregas empieza a recibir más viajes largos
Deriva de concepto (concept drift)La relación entre entradas y resultadoCon más tráfico, cada kilómetro ahora tarda más minutos

La deriva de datos se puede ver sin esperar resultados reales; la de concepto solo aparece cuando llegan las etiquetas (el resultado verdadero), que muchas veces se conocen días o semanas después (etiquetas tardías).

Cómo se detecta

MétodoQué comparaLectura habitual
PSI (Population Stability Index)Distribución de una variable en entrenamiento contra la actual, por intervalosRegla práctica común: < 0.10 estable, 0.10–0.25 vigilar, > 0.25 cambio fuerte
Prueba de Kolmogorov-Smirnov (KS)Distancia máxima entre dos distribuciones acumuladasSi el estadístico D supera el valor crítico, las distribuciones difieren
Caída de métricasError o acierto cuando llegan las etiquetas; mientras tanto, métricas de negocio (quejas, devoluciones)Alerta si empeora más de un margen fijado (por ejemplo 20 %)

Una variable con deriva no siempre daña al modelo, y un modelo puede fallar sin deriva visible en las entradas. Por eso se vigilan las dos cosas: entradas y resultados.

Registro de modelos y versiones

Un registro de modelos (model registry) es el catálogo de todas las versiones de un modelo: con qué datos y código se entrenó, sus métricas, quién lo aprobó y en qué estado está (candidato, producción, archivado). Permite reproducir cualquier versión y volver a la anterior en minutos.

MLflow es una plataforma de código abierto (licencia Apache 2.0) que incluye seguimiento de experimentos y un registro de modelos con versiones, etiquetas y alias (por ejemplo @champion para la versión en producción). Las nubes ofrecen registros equivalentes en sus plataformas de ML.

Despliegue seguro

EstrategiaCómo funcionaPara qué
Sombra (shadow deployment)El modelo nuevo recibe el tráfico real y predice, pero sus respuestas no se usanComparar con el actual sin riesgo
Canario (canary)Un porcentaje pequeño del tráfico va al modelo nuevo; si sus métricas son buenas, se amplíaLimitar el daño si falla
Vuelta atrás (rollback)Regresar a la versión anterior del registro, de forma manual o automáticaRecuperarse rápido de una mala versión

Regla de promoción: el candidato sustituye al actual solo si mejora en datos recientes que ninguno de los dos vio, sin empeorar en los segmentos importantes.

Niveles de madurez según Google Cloud

NivelNombreCaracterísticas
0Proceso manualCada paso se hace a mano; el modelo se entrega a ingeniería para desplegarlo; reentrenar es raro. El salto más valioso suele ser del 0 al 1
1Automatización del pipeline de MLUn pipeline reentrena con datos nuevos y valida datos y modelos automáticamente (entrenamiento continuo)
2Automatización de CI/CD del pipelineLos cambios al propio pipeline se construyen, prueban y despliegan de forma automática

MLOps y LLMOps

LLMOps aplica estas ideas a apps con modelos de lenguaje. Cambia lo que se versiona y se vigila:

MLOps (modelo propio)LLMOps (app con LLM)
Lo que se versionaDatos, código de entrenamiento y pesosPrompts, modelo del proveedor, índice de RAG, herramientas
Cómo se evalúaMétricas numéricas con etiquetasEvals con reglas, LLM como juez y revisión humana (día 14)
Por qué se degradaDeriva de datos y de conceptoCambio de versión del modelo, documentos desactualizados, nuevos tipos de preguntas
Costo principalEntrenamiento e infraestructuraTokens por llamada (día 15)

Caso real: Uber Michelangelo. Uber presentó en 2017 Michelangelo, su plataforma interna de ML, que cubre el flujo completo: gestionar datos, entrenar, evaluar, desplegar, predecir y monitorear. En una publicación del 30 de octubre de 2025, Uber explica que Michelangelo soporta todo el ciclo de vida de ML desde 2016, ejecuta más de 400 casos de uso activos y más de 20,000 entrenamientos al mes, y advierte que un modelo puede ir bien fuera de línea y fallar en condiciones reales por deriva de datos. Por eso usa pruebas en sombra, despliegues que empiezan con una porción pequeña del tráfico y vuelta atrás automática a la última versión buena.

Práctica: el ciclo completo en un script

El script recorre el ciclo con un modelo trivial: una regresión lineal hecha a mano que predice minutos de entrega según distancia y pedidos en cola. Entrena la v1, la registra en un archivo JSON, simula datos recientes con deriva (más viajes largos y más minutos por kilómetro), calcula PSI y KS por variable, detecta la caída del error cuando llegan las etiquetas, reentrena una v2, la compara con datos no vistos y decide si promoverla. Al final prueba como canario una v3 entrenada con un error en el pipeline y vuelve atrás. Todos los datos son ficticios.

1. Guarda el script como mini_mlops.py:

"""MLOps mínimo: entrenar, registrar versiones, detectar deriva (PSI y KS), reentrenar y decidir promover o volver atrás.

Solo biblioteca estándar. Datos FICTICIOS: minutos de entrega según distancia (km) y pedidos en cola.
"""
import json
import math
import random

REGISTRO = "registro_modelos.json"

def datos(n, semilla, km_media, min_por_km):
    rnd = random.Random(semilla)
    filas = []
    for _ in range(n):
        km, cola = max(0.5, rnd.gauss(km_media, 2.0)), rnd.randint(0, 8)
        filas.append((km, cola, 8 + min_por_km * km + 2.0 * cola + rnd.gauss(0, 3)))
    return filas

def entrenar(filas):
    """Regresión lineal y = b0 + b1·km + b2·cola por mínimos cuadrados (ecuaciones normales)."""
    X = [(1.0, km, c) for km, c, _ in filas]
    A = [[sum(x[i] * x[j] for x in X) for j in range(3)] + [sum(x[i] * f[2] for x, f in zip(X, filas))] for i in range(3)]
    for i in range(3):  # eliminación de Gauss-Jordan
        A[i] = [v / A[i][i] for v in A[i]]
        for k in range(3):
            if k != i:
                A[k] = [a - A[k][i] * b for a, b in zip(A[k], A[i])]
    return [round(A[i][3], 3) for i in range(3)]

def mae(coef, filas):
    return sum(abs(coef[0] + coef[1] * km + coef[2] * c - y) for km, c, y in filas) / len(filas)

def psi(ref, nuevo, cortes=10):
    """Population Stability Index: compara la distribución de una variable contra la de entrenamiento."""
    orden = sorted(ref)
    limites = [orden[int(len(orden) * i / cortes)] for i in range(1, cortes)]
    def proporciones(v):
        cuenta = [0] * cortes
        for x in v:
            cuenta[sum(x > lim for lim in limites)] += 1
        return [max(c / len(v), 1e-4) for c in cuenta]
    return sum((n - r) * math.log(n / r) for r, n in zip(proporciones(ref), proporciones(nuevo)))

def ks(a, b):
    """Estadístico D de Kolmogorov-Smirnov (evaluado en 1 de cada 10 puntos) y su valor crítico aproximado al 5 %."""
    puntos = sorted(set(a) | set(b))
    a, b = sorted(a), sorted(b)
    d = max(abs(sum(x <= p for x in a) / len(a) - sum(x <= p for x in b) / len(b)) for p in puntos[::10])
    return d, 1.36 * math.sqrt((len(a) + len(b)) / (len(a) * len(b)))

def registrar(reg, version, coef, metrica, origen, estado):
    for m in reg["modelos"]:
        if estado == "produccion" and m["estado"] == "produccion":
            m["estado"] = "archivado"
    reg["modelos"].append({"version": version, "coef": coef, "mae_validacion": round(metrica, 2), "datos": origen, "estado": estado})
    with open(REGISTRO, "w", encoding="utf-8") as f:
        json.dump(reg, f, ensure_ascii=False, indent=1)

if __name__ == "__main__":
    reg = {"modelos": []}
    historico = datos(2000, 1, km_media=5, min_por_km=3.0)           # el mundo de hace un año
    v1 = entrenar(historico[:1500])
    registrar(reg, 1, v1, mae(v1, historico[1500:]), "histórico", "produccion")
    print(f"v1 entrenado: coeficientes {v1} · MAE de validación {mae(v1, historico[1500:]):.2f} min")

    recientes = datos(1000, 2, km_media=8, min_por_km=4.2)          # más viajes largos y más tráfico
    print("\nDeriva de datos (PSI: <0.10 estable · 0.10-0.25 vigilar · >0.25 cambio fuerte):")
    for nombre, i in (("km", 0), ("cola", 1)):
        ref, nuevo = [f[i] for f in historico], [f[i] for f in recientes]
        d, critico = ks(ref, nuevo)
        print(f"  {nombre:<5} PSI {psi(ref, nuevo):5.2f} · KS D={d:.2f} (crítico {critico:.2f})")

    mae_v1 = mae(v1, recientes[:500])                                # etiquetas reales que llegan después
    alerta = mae_v1 > 1.2 * reg["modelos"][0]["mae_validacion"]
    print(f"\nv1 con datos recientes: MAE {mae_v1:.2f} min -> {'ALERTA: el modelo se degradó' if alerta else 'sin cambio'}")

    v2 = entrenar(recientes[:500])                                   # reentrenar con el mundo actual
    prueba = recientes[500:]                                         # datos recientes que ningún modelo vio
    m1, m2 = mae(v1, prueba), mae(v2, prueba)
    print(f"v2 reentrenado: coeficientes {v2}")
    print(f"Comparación en datos recientes no vistos: v1 MAE {m1:.2f} · v2 MAE {m2:.2f}")
    if m2 < 0.9 * m1:
        registrar(reg, 2, v2, m2, "recientes", "produccion")
        print("Decisión: PROMOVER v2 (mejora de más de 10 %)")
    else:
        registrar(reg, 2, v2, m2, "recientes", "rechazado")
        print("Decisión: MANTENER v1")

    actual = next(m for m in reg["modelos"] if m["estado"] == "produccion")
    v3 = entrenar([(km, c, y / 60) for km, c, y in recientes[:500]])  # error en el pipeline: etiquetas en horas
    trafico = datos(100, 3, km_media=8, min_por_km=4.2)               # 5 % del tráfico va al canario
    m_actual, m3 = mae(actual["coef"], trafico), mae(v3, trafico)
    print(f"\nCanario v3: MAE {m3:.2f} contra v{actual['version']} MAE {m_actual:.2f}")
    if m3 > 1.1 * m_actual:
        registrar(reg, 3, v3, m3, "recientes (pipeline con error)", "revertido")
        print(f"Decisión: VOLVER ATRÁS; todo el tráfico regresa a v{actual['version']}")
    print("\nRegistro:", json.dumps([(m["version"], m["estado"], m["mae_validacion"]) for m in reg["modelos"]], ensure_ascii=False))

2. Ejecútalo: python mini_mlops.py. Salida real (Python 3.12, Windows):

v1 entrenado: coeficientes [8.358, 2.971, 1.981] · MAE de validación 2.25 min

Deriva de datos (PSI: <0.10 estable · 0.10-0.25 vigilar · >0.25 cambio fuerte):
  km    PSI  1.91 · KS D=0.55 (crítico 0.05)
  cola  PSI  0.01 · KS D=0.02 (crítico 0.05)

v1 con datos recientes: MAE 9.32 min -> ALERTA: el modelo se degradó
v2 reentrenado: coeficientes [7.614, 4.221, 2.033]
Comparación en datos recientes no vistos: v1 MAE 9.69 · v2 MAE 2.47
Decisión: PROMOVER v2 (mejora de más de 10 %)

Canario v3: MAE 48.13 contra v2 MAE 2.10
Decisión: VOLVER ATRÁS; todo el tráfico regresa a v2

Registro: [[1, "archivado", 2.25], [2, "produccion", 2.47], [3, "revertido", 48.13]]

3. Interpreta el resultado. La v1 aprendió bien su mundo (error medio de 2.25 minutos). Con los datos recientes, el PSI de km (1.91) y su KS muy por encima del valor crítico delatan la deriva de datos; cola sigue estable. Cuando llegan las etiquetas, el error de la v1 sube a 9.32 minutos: hay también deriva de concepto (cada kilómetro tarda más). La v2, entrenada con datos recientes, baja el error a 2.47 en datos que ningún modelo vio y se promueve; la v1 queda archivada en el registro (ábrelo: registro_modelos.json). La v3, con etiquetas en horas por un error del pipeline, falla en el canario y el tráfico regresa a la v2 sin que la mayoría de los usuarios lo note.

Preguntas frecuentes

¿Qué es MLOps en palabras simples?

Es el mantenimiento de un modelo de IA: vigilar que siga acertando, reentrenarlo cuando el mundo cambia y guardar cada versión para poder volver a la anterior.

¿Qué es la deriva de un modelo?

Es cuando los datos o la realidad cambian respecto a lo que el modelo aprendió y sus predicciones empeoran, aunque nadie haya tocado el código.

¿Necesito MLOps si uso ChatGPT o Claude por API?

No entrenas el modelo, pero sí necesitas su equivalente, LLMOps: versionar prompts, correr evaluaciones antes de cambiar de modelo y vigilar costo y calidad.

En el reel lo explicamos así

Una IA que hoy funciona puede fallar en seis meses sin que nadie la toque, porque el mundo cambia: eso es la deriva (drift). Es la diferencia entre una receta que sale bien en tu cocina y producirla en una fábrica con control de calidad. Se vigilan los aciertos y se reentrena con datos nuevos; cada versión se guarda en un registro (model registry) para volver a la anterior si la nueva sale peor. Uber tiene Michelangelo: más de 400 usos activos y más de 20 mil entrenamientos al mes.

Referencias

En el reel lo explicamos así

La diferencia entre una receta que sale bien en tu cocina y producirla en una fábrica con control de calidad.

Glosario: del inglés al español

MLOpsoperaciones de aprendizaje automático
Machine learningaprendizaje automático
Driftderiva (el modelo se desfasa)
Model registryregistro de versiones del modelo