Despliegue de Modelos
¿Qué es?
Desplegar un modelo es sacarlo del cuaderno de entrenamiento y ponerlo a predecir sobre datos nuevos de forma fiable y reproducible. Implica tres cosas: guardar los pesos entrenados, cargarlos en cualquier máquina, y hacer inferencia (predecir) replicando exactamente el mismo preprocesado que usaste al entrenar.
¿Cómo funciona?
PyTorch separa la arquitectura (el código de tu nn.Module) de los parámetros (los números aprendidos). Lo que se guarda son los parámetros, en el state_dict. Para usar el modelo otra vez necesitas dos piezas: el código que recrea la arquitectura y el archivo .pt con los pesos. Cargas los pesos en una instancia fresca, pones el modelo en modo evaluación, y predices sin calcular gradientes.
La parte más olvidada y más peligrosa: el preprocesado. Si al entrenar escalaste las features o normalizaste las imágenes, en producción debes aplicar exactamente la misma transformación, con los mismos parámetros. Por eso también se guarda el scaler.
¿Para qué sirve?
Para que tu modelo tenga valor fuera del experimento: una API que recibe datos de un estudiante y devuelve su riesgo, una app que clasifica una foto. Sin despliegue, un modelo entrenado es solo un archivo que no hace nada.
La pieza del proyecto que construimos aquí
Cerramos el proyecto: tomamos el modelo_riesgo.pt del módulo de redes neuronales, lo cargamos limpio, empaquetamos el preprocesado, e implementamos una función de inferencia que recibe los datos de un estudiante nuevo y devuelve su probabilidad de riesgo. Eso es un modelo "útil" de punta a punta.
Paso 1: guardar también el preprocesado
Al entrenar guardamos los pesos, pero el StandardScaler también es parte del modelo. Lo serializamos.
import joblib
# (scaler es el StandardScaler ajustado en el módulo 1)
joblib.dump(scaler, "scaler_riesgo.joblib")
Olvidar el scaler es un error clásico y silencioso: el modelo carga bien y predice, pero da basura porque recibe features sin escalar, en un rango que nunca vio. Guarda el preprocesado junto a los pesos, siempre.
Paso 2: recrear la arquitectura y cargar pesos
import torch
import torch.nn as nn
class RedRiesgo(nn.Module):
def __init__(self, n_features, n_hidden=16):
super().__init__()
self.red = nn.Sequential(
nn.Linear(n_features, n_hidden),
nn.ReLU(),
nn.Linear(n_hidden, 1),
)
def forward(self, x):
return self.red(x)
modelo = RedRiesgo(n_features=2)
modelo.load_state_dict(torch.load("modelo_riesgo.pt", map_location="cpu"))
modelo.eval() # ¡imprescindible para inferir!
Si olvidas modelo.eval(), capas como Dropout o BatchNorm siguen en modo entrenamiento y las predicciones serán inconsistentes. En inferencia, siempre eval().
Paso 3: función de inferencia de punta a punta
import numpy as np
import joblib
scaler = joblib.load("scaler_riesgo.joblib")
def predecir_riesgo(horas_estudio, asistencia):
# 1) mismo preprocesado que en el entrenamiento
x = np.array([[horas_estudio, asistencia]], dtype=np.float32)
x_s = scaler.transform(x)
x_t = torch.tensor(x_s, dtype=torch.float32)
# 2) inferencia sin gradientes
with torch.no_grad():
logit = modelo(x_t)
prob = torch.sigmoid(logit).item()
etiqueta = "EN RIESGO" if prob > 0.5 else "a salvo"
return prob, etiqueta
# Probamos con un estudiante nuevo
prob, etiqueta = predecir_riesgo(horas_estudio=2.0, asistencia=0.3)
print(f"Probabilidad de riesgo: {prob:.2%} -> {etiqueta}")
El bloque with torch.no_grad() en inferencia ahorra memoria y acelera: no necesitamos gradientes para predecir, solo para entrenar.
Paso 4 (opcional): exportar a TorchScript
Para servir el modelo sin depender del código fuente de la clase:
ejemplo = torch.randn(1, 2)
scripted = torch.jit.trace(modelo, ejemplo)
scripted.save("modelo_riesgo_scripted.pt")
# Se carga con torch.jit.load(...) sin necesidad de la clase RedRiesgo
Tech English: inference (inferencia), checkpoint (punto de guardado), state dict (diccionario de pesos), serialize (serializar), deployment (despliegue).
Ejercicios
- Lee la coherencia del modelo. Llama a
predecir_riesgocon varios estudiantes: uno con muchas horas y alta asistencia, otro con pocas de ambas, y casos intermedios. ¿Las probabilidades cambian en la dirección esperada? Si un estudiante claramente aplicado sale "en riesgo", ¿qué revisarías primero: el modelo o el preprocesado? - Detecta el fallo del preprocesado. Crea una segunda función que prediga sin aplicar el
scaler(pasando las features crudas al modelo). Compara su salida con la función correcta para el mismo estudiante. Documenta la diferencia y explica por qué este es uno de los bugs más difíciles de detectar: el programa no falla, solo miente.