Despliegue e Inferencia
Del Modelo Entrenado a una API en Produccion
Tu modelo esta fine-tuned, evaluado, fusionado y exportado. Ahora necesitas servirlo a los usuarios. El motor de inferencia que elijas determina tu latencia, rendimiento, costo y complejidad operacional. Esta leccion cubre las cuatro rutas principales de despliegue y te ayuda a elegir la correcta para tu caso de uso.
Comparacion de Motores de Inferencia
| Motor | Ideal Para | Rendimiento | Facilidad | GPU Requerida | |-------|-----------|-------------|-----------|--------------| | vLLM | Produccion alto rendimiento | El mas alto | Medio | Si | | TGI (HuggingFace) | Integracion ecosistema HF | Alto | Medio | Si | | Ollama | Desarrollo local / pequena escala | Medio | El mas facil | Opcional | | llama.cpp | Edge / inferencia CPU | Bajo-Medio | Medio | No |
vLLM: Produccion de Alto Rendimiento
vLLM usa PagedAttention y batching continuo para lograr el mayor rendimiento entre los motores de inferencia de codigo abierto. Es la opcion preferida para despliegues en produccion que manejan muchas solicitudes concurrentes.
Instalacion y Servicio Basico
pip install vllm
# Servir tu modelo fine-tuned
vllm serve ./output/merged-model \
--host 0.0.0.0 \
--port 8000 \
--max-model-len 4096 \
--gpu-memory-utilization 0.90 \
--dtype auto
Esto inicia un servidor API compatible con OpenAI. Tu codigo existente que llama a GPT-4 puede cambiar a tu modelo fine-tuned solo cambiando la URL base:
import openai
client = openai.OpenAI(
base_url="http://localhost:8000/v1",
api_key="not-needed", # vLLM no requiere autenticacion por defecto
)
response = client.chat.completions.create(
model="./output/merged-model",
messages=[
{"role": "system", "content": "Eres un analista de documentos legales."},
{"role": "user", "content": "Resume esta clausula del contrato..."},
],
temperature=0.7,
max_tokens=512,
)
print(response.choices[0].message.content)
Modelos AWQ con vLLM
Para maxima eficiencia, sirve modelos cuantizados con AWQ:
vllm serve ./output/merged-model-awq \
--quantization awq \
--max-model-len 4096 \
--gpu-memory-utilization 0.90
Configuracion Clave de vLLM
vllm serve ./output/merged-model \
--tensor-parallel-size 2 \ # Dividir entre 2 GPUs
--max-num-seqs 256 \ # Max secuencias concurrentes
--max-num-batched-tokens 8192 \ # Max tokens por batch
--enable-prefix-caching \ # Cachear prefijos comunes
--gpu-memory-utilization 0.90 # Usar 90% de memoria GPU
Text-Generation-Inference (TGI)
El servidor de inferencia de produccion de Hugging Face. Excelente integracion con el ecosistema HF y despliegue nativo con Docker.
# Usando Docker (recomendado)
docker run --gpus all -p 8080:80 \
-v ./output/merged-model:/data \
ghcr.io/huggingface/text-generation-inference:latest \
--model-id /data \
--max-input-length 2048 \
--max-total-tokens 4096 \
--max-batch-prefill-tokens 4096
# Uso del cliente
import requests
response = requests.post(
"http://localhost:8080/generate",
json={
"inputs": "Resume esta clausula del contrato: ...",
"parameters": {
"max_new_tokens": 256,
"temperature": 0.7,
}
}
)
print(response.json()["generated_text"])
Ollama: Despliegue Local y de Pequena Escala
Ollama es la forma mas facil de ejecutar modelos localmente. Maneja la gestion de modelos, cuantizacion y servicio a traves de una CLI simple.
Creando un Modelo Personalizado
# Crear un Modelfile
cat > Modelfile << 'EOF'
FROM ./output/gguf/model-q4_k_m.gguf
TEMPLATE """{{ if .System }}<|start_header_id|>system<|end_header_id|>
{{ .System }}<|eot_id|>{{ end }}{{ if .Prompt }}<|start_header_id|>user<|end_header_id|>
{{ .Prompt }}<|eot_id|>{{ end }}<|start_header_id|>assistant<|end_header_id|>
{{ .Response }}<|eot_id|>"""
SYSTEM "Eres un analista de documentos legales especializado en revision de contratos."
PARAMETER temperature 0.7
PARAMETER top_p 0.9
PARAMETER stop "<|eot_id|>"
EOF
# Crear el modelo
ollama create legal-assistant -f Modelfile
# Ejecutarlo
ollama run legal-assistant "Resume esta clausula de NDA..."
API de Ollama
Ollama expone una API REST en el puerto 11434:
import requests
response = requests.post(
"http://localhost:11434/api/chat",
json={
"model": "legal-assistant",
"messages": [
{"role": "user", "content": "Resume esta clausula..."}
],
"stream": False,
}
)
print(response.json()["message"]["content"])
Endpoint Compatible con OpenAI
Ollama tambien proporciona una API compatible con OpenAI:
import openai
client = openai.OpenAI(
base_url="http://localhost:11434/v1",
api_key="ollama",
)
response = client.chat.completions.create(
model="legal-assistant",
messages=[{"role": "user", "content": "Analiza este contrato..."}],
)
llama.cpp: Inferencia Edge y CPU
Para entornos sin GPUs o para despliegue en edge:
# Ejecutar el modelo GGUF directamente
./llama-cli -m model-q4_k_m.gguf \
-p "Resume esta clausula legal:" \
-n 256 \
--temp 0.7 \
-ngl 0 # 0 = solo CPU, aumentar para capas en GPU
Para una configuracion de servidor:
./llama-server -m model-q4_k_m.gguf \
--host 0.0.0.0 \
--port 8080 \
-ngl 35 \
-c 4096
Construyendo un Wrapper con FastAPI
Para logica de API personalizada, envuelve tu motor de inferencia en una aplicacion FastAPI:
from fastapi import FastAPI
from pydantic import BaseModel
import openai
app = FastAPI()
# Conectar a tu backend de inferencia (vLLM, Ollama, etc.)
client = openai.OpenAI(
base_url="http://localhost:8000/v1",
api_key="not-needed",
)
class ChatRequest(BaseModel):
message: str
system_prompt: str = "Eres un asistente legal util."
temperature: float = 0.7
max_tokens: int = 512
class ChatResponse(BaseModel):
response: str
model: str
tokens_used: int
@app.post("/chat", response_model=ChatResponse)
async def chat(request: ChatRequest):
response = client.chat.completions.create(
model="legal-assistant",
messages=[
{"role": "system", "content": request.system_prompt},
{"role": "user", "content": request.message},
],
temperature=request.temperature,
max_tokens=request.max_tokens,
)
return ChatResponse(
response=response.choices[0].message.content,
model=response.model,
tokens_used=response.usage.total_tokens,
)
uvicorn api:app --host 0.0.0.0 --port 3000
Cuantizacion para Produccion
Elegir la cuantizacion correcta para despliegue:
- GPU con VRAM abundante: Usa AWQ 4-bit con vLLM. Mejor rendimiento.
- GPU con VRAM limitada: Usa GGUF Q4_K_M con Ollama o llama.cpp con descarga a GPU.
- Solo CPU: Usa GGUF Q4_K_M con llama.cpp. Mas lento pero funcional.
- Dispositivos edge: Usa GGUF Q4_0 o Q3_K_S para tamano minimo.
Monitoreando Latencia y Rendimiento
Rastrea estas metricas de produccion:
import time
import statistics
latencies = []
for prompt in test_prompts:
start = time.time()
response = client.chat.completions.create(
model="legal-assistant",
messages=[{"role": "user", "content": prompt}],
max_tokens=256,
)
latency = time.time() - start
latencies.append(latency)
print(f"Latencia mediana: {statistics.median(latencies):.2f}s")
print(f"Latencia P95: {sorted(latencies)[int(len(latencies)*0.95)]:.2f}s")
print(f"Rendimiento: {len(latencies)/sum(latencies):.1f} solicitudes/seg")
Optimizacion de Costos
Estrategias clave para reducir costos de inferencia:
- Cuantiza agresivamente. Q4_K_M usualmente es suficiente y reduce memoria en 75%.
- Usa system prompts mas cortos. El fine-tuning incorpora el comportamiento, asi que necesitas menos tokens de prompt.
- Agrupa solicitudes. El batching continuo de vLLM lo maneja automaticamente.
- Cachea prefijos comunes. El caching de prefijos de vLLM evita recomputar system prompts compartidos.
- Dimensiona correctamente tu GPU. Una A10G (24GB, ~$0.75/hr en AWS) maneja un modelo de 7B en Q4 con espacio de sobra.
- Usa instancias spot. Para cargas de trabajo no criticas en latencia, las instancias spot reducen costos en 60-70%.
En la leccion final, unimos todo en un proyecto capstone completo — desde la creacion del dataset hasta una API desplegada.