24. Guardar y reutilizar modelos entrenados

24.1 Entrenar cada vez no siempre tiene sentido

Cuando terminamos de entrenar un modelo y estamos conformes con su rendimiento, no queremos repetir todo el proceso cada vez que necesitemos hacer una predicción.

En un proyecto real, lo habitual es:

  • entrenar una vez;
  • guardar el modelo resultante;
  • cargarlo más adelante para reutilizarlo.

Eso ahorra tiempo y permite llevar el modelo a otros scripts, servidores o aplicaciones.

24.2 Qué significa guardar un modelo

Guardar un modelo significa serializar el objeto entrenado para poder recuperarlo más adelante exactamente como quedó después del aprendizaje.

Si además usamos un Pipeline, al guardar el pipeline se conservan también los pasos de preprocesamiento.

24.3 Herramientas habituales: joblib y pickle

En Python suelen usarse dos herramientas muy conocidas:

  • pickle;
  • joblib.

En el ecosistema de Scikit-learn, joblib es una opción muy común y recomendable para guardar modelos.

24.4 Qué conviene guardar

En general conviene guardar el flujo completo que realmente se usará en producción:

  • si entrenaste solo el modelo, guardas el modelo;
  • si entrenaste un pipeline con imputación, escalado y modelo, conviene guardar el pipeline completo.

Así te aseguras de que los nuevos datos reciban exactamente el mismo tratamiento que durante el entrenamiento.

24.5 Ejemplo muy claro: entrenar, guardar y volver a cargar

En este ejemplo entrenaremos un pipeline con escalado y regresión logística, lo guardaremos con joblib, lo volveremos a cargar y haremos una predicción.

import pandas as pd
import joblib
from sklearn.model_selection import train_test_split
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score

clientes = pd.DataFrame({
    "paginas_vistas": [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 5, 8],
    "minutos_sitio": [1, 1, 2, 2, 3, 4, 5, 5, 6, 7, 3, 4],
    "compra": [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1]
})

X = clientes[["paginas_vistas", "minutos_sitio"]]
y = clientes["compra"]

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.25, random_state=42, stratify=y
)

modelo = Pipeline([
    ("escalador", StandardScaler()),
    ("clasificador", LogisticRegression())
])

modelo.fit(X_train, y_train)
y_pred = modelo.predict(X_test)
print("Exactitud antes de guardar:", accuracy_score(y_test, y_pred))

joblib.dump(modelo, "modelo_clientes.joblib")
modelo_cargado = joblib.load("modelo_clientes.joblib")

nuevo_cliente = pd.DataFrame({
    "paginas_vistas": [7],
    "minutos_sitio": [4]
})

prediccion = modelo_cargado.predict(nuevo_cliente)[0]
probabilidad = modelo_cargado.predict_proba(nuevo_cliente)[0, 1]

print("Predicción con el modelo cargado:", prediccion)
print(f"Probabilidad de compra: {probabilidad:.3f}")

Salida resumida esperada:

Exactitud antes de guardar: ...
Predicción con el modelo cargado: ...
Probabilidad de compra: ...

24.6 Qué está pasando en el ejemplo

Primero entrenamos el pipeline como en cualquier problema supervisado. Después lo guardamos en un archivo con joblib.dump. Luego lo recuperamos con joblib.load y verificamos que sigue sirviendo para predecir.

Eso muestra una idea central: el modelo entrenado puede separarse del proceso de entrenamiento y reutilizarse en otro momento.

24.7 Por qué guardar el pipeline completo es mejor

Si solo guardáramos la regresión logística, tendríamos que recordar aplicar el mismo escalado manualmente cada vez que llegue un nuevo caso.

En cambio, al guardar el pipeline completo:

  • el escalado ya viene incorporado;
  • las predicciones futuras usan exactamente el mismo flujo;
  • se reducen errores operativos.

24.8 Explicación detallada del código

  • joblib.dump(modelo, "modelo_clientes.joblib"): guarda el objeto entrenado en disco.
  • joblib.load("modelo_clientes.joblib"): recupera el objeto guardado.
  • modelo_cargado.predict(...): demuestra que el objeto cargado sigue funcionando como antes.
  • Pipeline([...]): permite conservar preprocesamiento y modelo en un solo archivo.

24.9 Cuidados importantes

  • El archivo del modelo depende del entorno y de las versiones de librerías usadas al crearlo.
  • Conviene guardar también información sobre la versión de Python y Scikit-learn.
  • No conviene cargar archivos de modelos provenientes de fuentes no confiables.

24.10 Particularidad del simulador online

En el simulador del curso no trabajamos con archivos persistentes reales entre sesiones. Por eso, el ejemplo online mostrará la misma idea usando serialización en memoria con joblib, para que se entienda el concepto sin depender del sistema de archivos del navegador.

24.11 Errores frecuentes

  • Guardar solo el modelo y olvidar el preprocesamiento: puede romper las predicciones futuras.
  • Suponer que cualquier archivo de modelo sirve en cualquier entorno: las versiones importan.
  • No probar el modelo cargado: conviene validar que realmente funcione después de recuperarlo.
  • Usar archivos no confiables: cargar objetos serializados externos puede ser riesgoso.

24.12 Qué deberías retener

  • Un modelo entrenado puede guardarse y reutilizarse después.
  • joblib es una herramienta muy usada para eso en Scikit-learn.
  • Conviene guardar el pipeline completo cuando hay preprocesamiento.
  • Después de cargarlo, el modelo debería comportarse igual que antes.
  • Guardar modelos es un paso clave para llevar Machine Learning a aplicaciones reales.