Instalación y Configuración de TensorFlow Serving

TensorFlow Serving es el sistema de producción oficial de Google para servir modelos TensorFlow con alta eficiencia, soporte para versionado de modelos y APIs REST y gRPC listas para producción. A diferencia de servir modelos con Flask o FastAPI, TensorFlow Serving gestiona automáticamente el ciclo de vida de los modelos, la carga bajo demanda y el balanceo entre versiones. Esta guía cubre la instalación mediante Docker y nativa, la configuración de modelos, ambas APIs y las técnicas de optimización de rendimiento.

Requisitos Previos

  • Servidor Linux (Ubuntu 20.04+)
  • Docker instalado (para el método Docker)
  • Modelo TensorFlow guardado en formato SavedModel
  • Al menos 2 GB de RAM
  • GPU NVIDIA con drivers y CUDA instalados (opcional)
# Verificar Docker
docker --version

# Verificar que TensorFlow puede guardar modelos en el servidor de entrenamiento
python3 -c "import tensorflow as tf; print(tf.__version__)"

Instalación con Docker

La instalación con Docker es la más sencilla y recomendada para entornos de producción.

# Descargar la imagen oficial de TensorFlow Serving
docker pull tensorflow/serving:latest

# Para soporte GPU
docker pull tensorflow/serving:latest-gpu

# Verificar la imagen descargada
docker images | grep tensorflow/serving

Ejecutar con un modelo de ejemplo

# Descargar el modelo de ejemplo Half Plus Two
mkdir -p /tmp/tfserving
git clone https://github.com/tensorflow/serving /tmp/tfserving-repo

# Directorio del modelo de ejemplo
MODELO_DIR=/tmp/tfserving-repo/tensorflow_serving/servables/tensorflow/testdata/saved_model_half_plus_two_cpu

# Iniciar el servidor con el modelo de ejemplo
docker run -p 8501:8501 \
  -v "$MODELO_DIR:/models/half_plus_two" \
  -e MODEL_NAME=half_plus_two \
  tensorflow/serving &

# Probar con una solicitud REST
curl -d '{"instances": [1.0, 2.0, 5.0]}' \
  -X POST http://localhost:8501/v1/models/half_plus_two:predict

Instalación Nativa en Ubuntu

# Añadir el repositorio de TensorFlow Serving
echo "deb [arch=amd64] http://storage.googleapis.com/tensorflow-serving-apt stable tensorflow-model-server tensorflow-model-server-universal" | \
  sudo tee /etc/apt/sources.list.d/tensorflow-serving.list

curl https://storage.googleapis.com/tensorflow-serving-apt/tensorflow-serving.release.pub.gpg | \
  sudo apt-key add -

sudo apt-get update

# Instalar TensorFlow Model Server
sudo apt-get install -y tensorflow-model-server

# Verificar la instalación
tensorflow_model_server --version

Preparar el Modelo para Serving

Los modelos deben estar en formato SavedModel con una estructura de directorios específica.

#!/usr/bin/env python3
# Guardar un modelo en el formato correcto para TensorFlow Serving

import tensorflow as tf
import numpy as np

# Crear un modelo sencillo de ejemplo
modelo = tf.keras.Sequential([
    tf.keras.layers.Dense(64, activation='relu', input_shape=(10,)),
    tf.keras.layers.Dense(32, activation='relu'),
    tf.keras.layers.Dense(1, activation='sigmoid')
])

modelo.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])

# Entrenamiento rápido con datos de ejemplo
X = np.random.randn(1000, 10)
y = np.random.randint(0, 2, 1000)
modelo.fit(X, y, epochs=3, verbose=0)

# Guardar en el formato SavedModel para TF Serving
# La estructura debe ser: /ruta/al/modelo/NOMBRE/VERSION/
RUTA_MODELO = "/opt/tfserving/models/mi-clasificador/1"
tf.saved_model.save(modelo, RUTA_MODELO)

print(f"Modelo guardado en: {RUTA_MODELO}")
print("Estructura de archivos:")
import os
for r, d, f in os.walk(RUTA_MODELO):
    nivel = r.replace(RUTA_MODELO, '').count(os.sep)
    print(f"{'  ' * nivel}{os.path.basename(r)}/")
    for archivo in f:
        print(f"{'  ' * (nivel + 1)}{archivo}")

Inspeccionar la firma del modelo

# Ver las entradas y salidas del modelo guardado
saved_model_cli show \
  --dir /opt/tfserving/models/mi-clasificador/1 \
  --all

# Ver solo las firmas de servicio
saved_model_cli show \
  --dir /opt/tfserving/models/mi-clasificador/1 \
  --tag_set serve \
  --signature_def serving_default

Configuración del Servidor

Configuración básica

# Crear la estructura de directorios
mkdir -p /opt/tfserving/{models,configs,logs}

# Iniciar el servidor apuntando a un único modelo
tensorflow_model_server \
  --rest_api_port=8501 \
  --grpc_port=8500 \
  --model_name=mi-clasificador \
  --model_base_path=/opt/tfserving/models/mi-clasificador

Archivo de configuración para múltiples modelos

# Crear el archivo de configuración de modelos
cat > /opt/tfserving/configs/models.config << 'EOF'
model_config_list {
  config {
    name: "mi-clasificador"
    base_path: "/opt/tfserving/models/mi-clasificador"
    model_platform: "tensorflow"
    model_version_policy {
      latest {
        num_versions: 2
      }
    }
  }

  config {
    name: "modelo-nlp"
    base_path: "/opt/tfserving/models/modelo-nlp"
    model_platform: "tensorflow"
    model_version_policy {
      specific {
        versions: 3
        versions: 5
      }
    }
  }
}
EOF

# Iniciar el servidor con el archivo de configuración
tensorflow_model_server \
  --rest_api_port=8501 \
  --grpc_port=8500 \
  --model_config_file=/opt/tfserving/configs/models.config \
  --model_config_file_poll_wait_seconds=60

Configurar como servicio systemd

sudo cat > /etc/systemd/system/tfserving.service << 'EOF'
[Unit]
Description=TensorFlow Serving
After=network.target

[Service]
Type=simple
User=tfserving
Group=tfserving
ExecStart=/usr/bin/tensorflow_model_server \
    --rest_api_port=8501 \
    --grpc_port=8500 \
    --model_config_file=/opt/tfserving/configs/models.config \
    --model_config_file_poll_wait_seconds=60 \
    --enable_model_warmup=true
Restart=on-failure
RestartSec=10

[Install]
WantedBy=multi-user.target
EOF

sudo useradd -r -s /bin/false tfserving
sudo chown -R tfserving:tfserving /opt/tfserving/
sudo systemctl daemon-reload
sudo systemctl enable tfserving
sudo systemctl start tfserving

API REST

# Verificar que el modelo está cargado
curl http://localhost:8501/v1/models/mi-clasificador

# Ver las versiones del modelo
curl http://localhost:8501/v1/models/mi-clasificador/versions

# Hacer una predicción (una sola instancia)
curl -s -X POST http://localhost:8501/v1/models/mi-clasificador:predict \
  -H "Content-Type: application/json" \
  -d '{
    "instances": [[1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0]]
  }' | python3 -m json.tool

# Predicción de múltiples instancias (batch)
curl -s -X POST http://localhost:8501/v1/models/mi-clasificador:predict \
  -H "Content-Type: application/json" \
  -d '{
    "instances": [
      [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0],
      [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]
    ]
  }'

# Predicción en una versión específica del modelo
curl -s -X POST http://localhost:8501/v1/models/mi-clasificador/versions/2:predict \
  -H "Content-Type: application/json" \
  -d '{"instances": [[1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0]]}'

Cliente Python para la API REST

import requests
import numpy as np

def predecir(datos, modelo="mi-clasificador", servidor="http://localhost:8501"):
    """Enviar datos para inferencia via API REST."""
    url = f"{servidor}/v1/models/{modelo}:predict"
    payload = {"instances": datos.tolist()}
    
    respuesta = requests.post(url, json=payload)
    respuesta.raise_for_status()
    
    return respuesta.json()["predictions"]

# Ejemplo de uso
datos = np.random.randn(5, 10)  # 5 muestras, 10 features
predicciones = predecir(datos)
print(f"Predicciones: {predicciones}")

API gRPC

gRPC es más eficiente que REST para volúmenes altos de solicitudes.

# Instalar las dependencias para el cliente gRPC
pip install grpcio tensorflow-serving-api
#!/usr/bin/env python3
# Cliente gRPC para TensorFlow Serving

import grpc
import numpy as np
from tensorflow_serving.apis import predict_pb2
from tensorflow_serving.apis import prediction_service_pb2_grpc
import tensorflow as tf

def predecir_grpc(datos, modelo="mi-clasificador", host="localhost:8500"):
    """Inferencia usando la API gRPC de TF Serving."""
    # Crear el canal gRPC
    canal = grpc.insecure_channel(host)
    stub = prediction_service_pb2_grpc.PredictionServiceStub(canal)
    
    # Construir la solicitud
    solicitud = predict_pb2.PredictRequest()
    solicitud.model_spec.name = modelo
    solicitud.model_spec.signature_name = "serving_default"
    
    # Convertir los datos a tensor proto
    solicitud.inputs["dense_input"].CopyFrom(
        tf.make_tensor_proto(datos, dtype=tf.float32)
    )
    
    # Enviar la solicitud con timeout
    resultado = stub.Predict(solicitud, 10.0)  # 10 segundos de timeout
    
    return resultado.outputs["dense_1"].float_val

# Ejemplo de uso
datos = np.random.randn(3, 10).astype(np.float32)
predicciones = predecir_grpc(datos)
print(f"Predicciones gRPC: {list(predicciones)}")

Soporte GPU

# Usar la imagen GPU de Docker
docker run --gpus all -p 8501:8501 -p 8500:8500 \
  -v /opt/tfserving/models:/models \
  -e MODEL_NAME=mi-clasificador \
  tensorflow/serving:latest-gpu

# Para la instalación nativa, el servidor detecta la GPU automáticamente
# si los drivers CUDA están correctamente instalados

# Limitar el uso de VRAM por modelo (en el archivo de configuración)
# Añadir al archivo models.config:
# experimental_fixed_model_config {
#   tf_session_parallelism: 4
# }

Versionado y Actualización de Modelos

# Añadir una nueva versión del modelo sin reiniciar el servidor
# Crear el directorio de la versión 2
mkdir -p /opt/tfserving/models/mi-clasificador/2

# Copiar/guardar el nuevo modelo en ese directorio
# TF Serving cargará automáticamente la nueva versión

# Ver las versiones activas
curl http://localhost:8501/v1/models/mi-clasificador

# Actualización de la configuración en caliente
curl -X POST http://localhost:8501/v1/models/mi-clasificador/reload

Solución de Problemas

Error: Failed to load model

# Ver los logs del servidor
sudo journalctl -u tfserving -f

# Verificar la estructura del modelo
saved_model_cli show --dir /opt/tfserving/models/mi-clasificador/1 --all

# Verificar permisos
ls -la /opt/tfserving/models/mi-clasificador/1/

Error de dimensiones en las predicciones

# Ver la firma exacta del modelo para conocer las dimensiones esperadas
saved_model_cli show \
  --dir /opt/tfserving/models/mi-clasificador/1 \
  --tag_set serve \
  --signature_def serving_default

Alta latencia en las predicciones

# Habilitar el batching automático
cat > /opt/tfserving/configs/batching.txt << 'EOF'
max_batch_size { value: 32 }
batch_timeout_micros { value: 10000 }
max_enqueued_batches { value: 100 }
num_batch_threads { value: 4 }
EOF

tensorflow_model_server \
  --enable_batching=true \
  --batching_parameters_file=/opt/tfserving/configs/batching.txt \
  --model_config_file=/opt/tfserving/configs/models.config

Conclusión

TensorFlow Serving ofrece una solución robusta y de alto rendimiento para servir modelos en producción con gestión automática de versiones, APIs REST y gRPC, y soporte nativo para GPU. La combinación de archivos de configuración en caliente y el versionado automático permite actualizar los modelos sin interrupciones del servicio, convirtiéndolo en la opción preferida para sistemas de inferencia de alta disponibilidad.