Inicio / Inteligencia Artificial / AI Engineering Pro / TensorFlow, Keras y Serving

TensorFlow, Keras y Serving

Keras 3 multi-backend, TF Serving, TF Lite y comparativa con PyTorch.

Intermedio
🔒 Solo lectura
📖

Estás en modo lectura

Puedes leer toda la lección, pero para marcar progreso, hacer ejercicios y ganar XP necesitas una cuenta Pro.

Desbloquear por $9/mes

TensorFlow y Keras: Entrenamiento y Serving

TensorFlow en el Ecosistema de IA

TensorFlow, desarrollado por Google, ofrece un ecosistema completo desde entrenamiento hasta producción. Mientras PyTorch domina en investigación, TensorFlow tiene fortalezas en:

  • TF Serving: Serving de modelos en producción con bajo overhead
  • TF Lite: Deployment en dispositivos móviles y edge
  • TF.js: Modelos en el navegador
  • Ecosistema Keras: API de alto nivel, rápida experimentación

Keras 3: API Unificada

import keras  # Keras 3 — backend agnóstico (TF, PyTorch, JAX)
from keras import layers, Model

class TransformerBlock(layers.Layer):
    def __init__(self, embed_dim, num_heads, ff_dim, dropout=0.1):
        super().__init__()
        self.attention = layers.MultiHeadAttention(
            num_heads=num_heads, key_dim=embed_dim
        )
        self.ffn = keras.Sequential([
            layers.Dense(ff_dim, activation="gelu"),
            layers.Dense(embed_dim),
        ])
        self.norm1 = layers.LayerNormalization(epsilon=1e-6)
        self.norm2 = layers.LayerNormalization(epsilon=1e-6)
        self.dropout1 = layers.Dropout(dropout)
        self.dropout2 = layers.Dropout(dropout)

    def call(self, inputs, training=False):
        # Self-attention: inputs se usa como Query, Key y Value simultáneamente
        attn_output = self.attention(inputs, inputs)
        # Dropout solo se aplica durante entrenamiento (training=True)
        attn_output = self.dropout1(attn_output, training=training)
        # Conexión residual (inputs + ...) + Layer Norm: patrón fundamental de transformers
        # La conexión residual permite que los gradientes fluyan sin degradarse
        out1 = self.norm1(inputs + attn_output)
        # Feed-forward: expande a ff_dim con GELU (activación estándar en transformers),
        # luego comprime de vuelta a embed_dim
        ffn_output = self.ffn(out1)
        ffn_output = self.dropout2(ffn_output, training=training)
        # Segunda conexión residual + normalización
        return self.norm2(out1 + ffn_output)

Entrenamiento de un Clasificador de Texto

# TextVectorization: tokenizador integrado de Keras (texto → secuencia de enteros)
from keras.layers import TextVectorization

max_tokens = 20000    # Vocabulario máximo: las 20K palabras más frecuentes
max_length = 256      # Largo fijo de secuencias (padding/truncado automático)

vectorizer = TextVectorization(
    max_tokens=max_tokens,
    output_sequence_length=max_length,
)
# adapt() construye el vocabulario analizando los textos de entrenamiento
vectorizer.adapt(train_texts)

# Modelo funcional de Keras: Input acepta strings directamente
inputs = keras.Input(shape=(1,), dtype="string")
x = vectorizer(inputs)                             # String → secuencia de enteros
x = layers.Embedding(max_tokens, 128)(x)           # Enteros → vectores densos de 128 dims
x = TransformerBlock(128, num_heads=4, ff_dim=256)(x)  # ff_dim=256 (2x embed para expandir/comprimir)
x = layers.GlobalAveragePooling1D()(x)             # (batch, seq, 128) → (batch, 128): promedia secuencia
x = layers.Dropout(0.3)(x)
x = layers.Dense(64, activation="relu")(x)
# softmax: convierte logits a probabilidades que suman 1
outputs = layers.Dense(num_classes, activation="softmax")(x)

model = Model(inputs, outputs)

model.compile(
    optimizer=keras.optimizers.Adam(learning_rate=1e-4),
    # sparse_categorical_crossentropy: labels son enteros (0,1,2...)
    # (vs categorical_crossentropy que requiere one-hot encoding)
    loss="sparse_categorical_crossentropy",
    metrics=["accuracy"],
)

# Callbacks para controlar el entrenamiento automáticamente
callbacks = [
    # Para entrenamiento si val_loss no mejora en 3 épocas consecutivas
    keras.callbacks.EarlyStopping(
        monitor="val_loss", patience=3, restore_best_weights=True
    ),
    # Reduce learning rate a la mitad si val_loss estanca por 2 épocas
    keras.callbacks.ReduceLROnPlateau(
        monitor="val_loss", factor=0.5, patience=2
    ),
    # Guarda logs para visualizar curvas de entrenamiento en TensorBoard
    keras.callbacks.TensorBoard(log_dir="./logs"),
    # Guarda solo el modelo con mejor val_loss (no el último)
    keras.callbacks.ModelCheckpoint(
        "best_model.keras", save_best_only=True
    ),
]

history = model.fit(
    train_ds, validation_data=val_ds,
    epochs=20, callbacks=callbacks
)

Transfer Learning con Modelos Pre-Entrenados

from transformers import TFAutoModelForSequenceClassification

# Cargar modelo pre-entrenado de Hugging Face con backend TF
model = TFAutoModelForSequenceClassification.from_pretrained(
    "bert-base-multilingual-cased",
    num_labels=5,     # Número de clases a clasificar
)

# Fine-tuning con tf.keras
# learning_rate=2e-5: LR bajo estándar para fine-tuning de BERT
# (muy alto destruiría los pesos pre-entrenados)
optimizer = keras.optimizers.Adam(learning_rate=2e-5)
# model.compute_loss: función de pérdida interna de HuggingFace
# que maneja correctamente la estructura de salida del modelo
model.compile(optimizer=optimizer, loss=model.compute_loss, metrics=["accuracy"])

model.fit(
    # shuffle(1000): buffer de 1000 ejemplos para aleatorizar (mayor = mejor mezcla, más RAM)
    # batch(16): lotes de 16 ejemplos en train (limitado por VRAM con gradientes)
    tf_train_dataset.shuffle(1000).batch(16),
    # batch(32): en eval se puede usar batch más grande (sin gradientes)
    validation_data=tf_val_dataset.batch(32),
    epochs=3,
)

TensorFlow Serving: Producción

Exportar SavedModel

# Guardar para serving
model.save("saved_model/intent_classifier/1")  # Versión 1

# Estructura:
# saved_model/intent_classifier/1/
#   ├── saved_model.pb
#   ├── variables/
#   │   ├── variables.data-00000-of-00001
#   │   └── variables.index
#   └── assets/

Deploy con TF Serving (Docker)

# TF Serving expone: puerto 8501 (REST API) y 8500 (gRPC)
docker run -p 8501:8501 \
  # Monta el directorio de modelos local en /models (ruta por defecto de TF Serving)
  --mount type=bind,source=$(pwd)/saved_model,target=/models \
  # TF Serving buscará el modelo en /models/intent_classifier/{versión}/
  -e MODEL_NAME=intent_classifier \
  tensorflow/serving:latest

# POST al endpoint :predict con formato "instances" (cada elemento = 1 ejemplo)
# Tokens BERT: 101=[CLS], 7592=token de texto, 102=[SEP]
curl -X POST http://localhost:8501/v1/models/intent_classifier:predict \
  -H "Content-Type: application/json" \
  -d '{"instances": [{"input_ids": [101, 7592, 102]}]}'

gRPC para Alta Performance

# gRPC (Google Remote Procedure Call): usa Protocol Buffers (serialización binaria)
# ~2-5x más rápido que REST-JSON para serving de modelos
import grpc
from tensorflow_serving.apis import predict_pb2, prediction_service_pb2_grpc

# insecure_channel: conexión sin TLS (solo para desarrollo)
# Puerto 8500 = gRPC (vs 8501 = REST)
channel = grpc.insecure_channel("localhost:8500")
stub = prediction_service_pb2_grpc.PredictionServiceStub(channel)

request = predict_pb2.PredictRequest()
request.model_spec.name = "intent_classifier"
# signature_name: define las entradas/salidas del modelo
# "serving_default" es la firma exportada por defecto
request.model_spec.signature_name = "serving_default"

TensorFlow Lite: Edge Deployment

# Convertir modelo para mobile/edge
converter = tf.lite.TFLiteConverter.from_saved_model("saved_model/1")
# Optimize.DEFAULT: cuantiza pesos de float32 → int8 automáticamente
# Resultado: ~4x menos tamaño, ~2x más rápido, con pérdida mínima (<1%)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
tflite_model = converter.convert()

with open("model.tflite", "wb") as f:
    f.write(tflite_model)

# TF Lite Interpreter: runtime minimalista para dispositivos edge (móvil, IoT)
# No usa el grafo TF completo — optimizado para baja latencia y poca memoria
interpreter = tf.lite.Interpreter(model_path="model.tflite")
# allocate_tensors: reserva memoria para todos los tensores (obligatorio antes de inferir)
interpreter.allocate_tensors()

# Obtiene metadatos de entrada/salida: shape, dtype, e "index" (ID del tensor)
input_details = interpreter.get_input_details()
output_details = interpreter.get_output_details()

# Asigna el input usando el index del primer tensor de entrada
interpreter.set_tensor(input_details[0]["index"], input_data)
# invoke() ejecuta el forward pass completo del modelo
interpreter.invoke()
# Lee el resultado del primer tensor de salida
output = interpreter.get_tensor(output_details[0]["index"])

Comparativa: PyTorch vs TensorFlow

Aspecto PyTorch TensorFlow
Investigación Dominante (>80% papers) Menos usado
Producción TorchServe, Triton TF Serving (más maduro)
Mobile/Edge ExecuTorch TF Lite (más maduro)
Debugging Eager mode nativo Eager + Graph mode
Ecosistema HuggingFace, PEFT TFX, TF Extended
Curva de aprendizaje Más pythonico Más verboso

¿Cuál Usar?

  • Fine-tuning / RAG / Agentes: PyTorch + HuggingFace
  • Serving en producción con bajo overhead: TF Serving
  • Mobile / Edge: TF Lite
  • Prototipado rápido: Keras (cualquier backend)
  • La mayoría de proyectos de AI Engineering: PyTorch

TensorBoard: Visualización de Entrenamiento

# En el código de entrenamiento
from torch.utils.tensorboard import SummaryWriter

writer = SummaryWriter("runs/experiment_1")

for epoch in range(num_epochs):
    writer.add_scalar("Loss/train", train_loss, epoch)
    writer.add_scalar("Loss/val", val_loss, epoch)
    writer.add_scalar("Accuracy/val", val_acc, epoch)
    writer.add_scalar("LR", current_lr, epoch)

writer.close()
# Visualizar
tensorboard --logdir runs/ --port 6006

TensorBoard es compatible tanto con PyTorch como con TensorFlow, y es la herramienta estándar para visualizar métricas de entrenamiento.

Resumen

TensorFlow y Keras complementan a PyTorch en el toolkit del AI Engineer:

  1. Keras 3: Prototipado rápido con backend agnóstico
  2. TF Serving: Serving de modelos en producción con autoscaling
  3. TF Lite: Deployment en dispositivos edge
  4. Transfer Learning: Fine-tuning eficiente de modelos pre-entrenados
  5. TensorBoard: Visualización y debugging de entrenamiento

🧠 Preguntas de Repaso

1. ¿Cuál es la principal ventaja de TF Serving sobre TorchServe para producción?

  • A) TF Serving es más rápido en investigación
  • B) TF Serving es más maduro y tiene mejor soporte para autoscaling en producción
  • C) TF Serving soporta más lenguajes de programación
  • D) TF Serving es open-source y TorchServe no

Respuesta: B) — TF Serving es considerado más maduro para producción, con mejor soporte para autoscaling, versionado de modelos y canary deployments. En contraste, PyTorch domina en investigación (>80% de papers).

2. En TF Serving con Docker, ¿qué protocolo es ~2-5x más rápido que REST-JSON y en qué puerto escucha?

  • A) WebSocket en el puerto 8080
  • B) gRPC en el puerto 8500, usando Protocol Buffers (serialización binaria)
  • C) GraphQL en el puerto 9090
  • D) HTTP/2 en el puerto 443

Respuesta: B) — gRPC escucha en el puerto 8500 (REST en 8501) y usa Protocol Buffers para serialización binaria, lo que lo hace ~2-5x más rápido que REST-JSON.

3. ¿Qué logra TF Lite Optimize.DEFAULT al convertir un modelo?

  • A) Aumenta la precisión del modelo a float64
  • B) Cuantiza de float32 a int8 logrando ~4x menos tamaño y ~2x más velocidad con menos de 1% de pérdida
  • C) Comprime el modelo con gzip
  • D) Elimina capas no utilizadas del grafo

Respuesta: B) — TF Lite con Optimize.DEFAULT aplica cuantización de float32 a int8, resultando en modelos ~4x más pequeños y ~2x más rápidos, con pérdida mínima de precisión (<1%).

4. ¿Cuál es la recomendación principal para la mayoría de proyectos de AI Engineering?

  • A) Usar TensorFlow exclusivamente
  • B) Usar PyTorch para la mayoría de proyectos, TF Serving para producción y TF Lite para edge
  • C) Usar JAX para todo
  • D) Usar Keras sin backend específico

Respuesta: B) — La recomendación es usar PyTorch para fine-tuning, RAG y agentes (mayoría de proyectos), TF Serving para serving en producción por su madurez, y TF Lite para deployment en dispositivos edge/mobile.

¿Te gustó esta lección?

Con Pro puedes marcar progreso, hacer ejercicios, tomar quizzes, ganar XP y obtener tu constancia.

Ver planes desde $9/mes