Multi-LoRA serving (y cierre del Checkpoint C1)
servir varios adaptadores LoRA sobre un único modelo base, con selección por petición, y medir el coste real de hacerlo. Es el patrón que te permite tener "muchos modelos especializados" sin pagar la VRAM de muchos modelos. Esta lección cierra C1.
6.1La idea: un base, muchos adaptadores
Un adaptador LoRA (lo entrenarás en el Nivel 2) son unas matrices pequeñas ΔW = BA que modifican un modelo base congelado para una tarea concreta. La observación de serving: el base (caro, GBs) es compartido; los adaptadores (baratos, MBs) son intercambiables. En lugar de cargar N modelos completos, cargas un base y conmutas entre N adaptadores por petición.
Esto viene de S-LoRA (Sheng et al. 2023), cuyas técnicas vLLM incorpora: mantener los adaptadores en un pool, paginar entre VRAM y RAM (CPU) con política LRU, y aplicar el adaptador correcto a cada secuencia del batch — incluso si en un mismo batch conviven peticiones que usan adaptadores distintos.
6.2Cómo se sirve en vLLM
1vllm serve Qwen/Qwen3-8B \
2 --enable-lora \
3 --max-loras 8 \ # nº de adaptadores activos simultáneamente en VRAM
4 --max-lora-rank 32 \ # rank máximo de los adaptadores (debe cubrir el tuyo)
5 --max-cpu-loras 32 \ # pool en CPU desde el que se paginan a VRAM (LRU)
6 --lora-modules sql=./adapters/sql resumen=./adapters/resumen clasif=./adapters/clasifY en la petición eliges el adaptador por nombre, como si fuera un modelo:
1import httpx
2httpx.post("http://localhost:8000/v1/completions", json={
3 "model": "sql", # <-- nombre del adaptador, no del base
4 "prompt": "Convierte a SQL: usuarios registrados el último mes",
5 "max_tokens": 128, "temperature": 0.0,
6})Líneas no triviales:
--max-loras(en VRAM) vs--max-cpu-loras(pool en CPU): si pides un adaptador que no está en VRAM, vLLM lo pagina desde el pool CPU (coste de latencia el primer uso → "cold adapter").--max-lora-rankdebe ser ≥ el rank de tus adaptadores; si entrenaste con r=64, ponlo a 64 (consume más VRAM por adaptador).- vLLM reserva un % fijo de VRAM para LoRA (por defecto ~0.2) → tenlo en cuenta en tu presupuesto de N0·L3.
6.3El coste real (lo que tienes que medir)
Multi-LoRA no es gratis. Bajo carga variable, la asignación estática de HBM de vLLM puede provocar picos de TTFT cuando el sistema pagina adaptadores o reorganiza KV-cache: estudios sobre escenarios chatbot / traducción / agente personal muestran TTFT medios que se disparan en ciertos periodos por insuficiente espacio para KV o LoRAs. Tu trabajo no es evitar el coste (es inherente), sino medirlo y dimensionar --max-loras/--max-cpu-loras para tu patrón de tráfico.
Las dos métricas que importan:
- Latencia de adaptador "frío" (primera petición a un adaptador que estaba en CPU) vs "caliente" (ya en VRAM).
- Throughput agregado con mezcla de adaptadores en el mismo batch vs un solo adaptador.
6.4Laboratorio L6.1 — Reto integrador: el "gateway de inferencia personal"
Construye el servidor que cierra el nivel. Tres adaptadores genéricos (los entrenarás de verdad en el Nivel 2; aquí puedes usar adaptadores públicos o stubs para medir el sistema de serving):
1# lab_n1l6_gateway.py — mide latencia fría/caliente y throughput con mezcla de adaptadores
2import asyncio, time, httpx, random
3
4URL = "http://localhost:8000/v1/completions"
5ADAPTERS = ["sql", "resumen", "clasif"]
6PROMPTS = {
7 "sql": "Convierte a SQL: pedidos por cliente este año",
8 "resumen": "Resume en una frase: " + ("texto largo. " * 40),
9 "clasif": "Clasifica el sentimiento: 'no ha estado mal del todo'",
10}
11
12async def call(client, adapter):
13 t0 = time.perf_counter()
14 await client.post(URL, json={"model": adapter, "prompt": PROMPTS[adapter],
15 "max_tokens": 64, "temperature": 0.0}, timeout=120)
16 return time.perf_counter() - t0
17
18async def main():
19 async with httpx.AsyncClient() as client:
20 # 1) latencia fría: primer uso de cada adaptador (recién paginado)
21 cold = {a: await call(client, a) for a in ADAPTERS}
22 # 2) latencia caliente: segundo uso (ya en VRAM)
23 hot = {a: await call(client, a) for a in ADAPTERS}
24 # 3) throughput con MEZCLA de adaptadores concurrentes (el caso real)
25 t0 = time.perf_counter()
26 await asyncio.gather(*[call(client, random.choice(ADAPTERS)) for _ in range(64)])
27 wall = time.perf_counter() - t0
28 print("fría: ", {a: f"{cold[a]*1000:.0f}ms" for a in ADAPTERS})
29 print("calie:", {a: f"{hot[a]*1000:.0f}ms" for a in ADAPTERS})
30 print(f"mezcla 64 conc: wall={wall:.2f}s")
31
32asyncio.run(main())Añade prefix caching (sha256_cbor) y, si tu workload lo justifica, n-gram speculative. Expón las métricas vía el endpoint /metrics de vLLM y haz un dashboard mínimo (un script que parsee Prometheus, o Grafana si quieres lucirte). Eso es tu gateway personal: el banco de pruebas de serving para el resto del curso.
6.5CHECKPOINT C1 — criterio de aprobado
Marca C1 solo cuando, en segundo pase reproducible:
- Roofline: sirves un modelo y demuestras que tu throughput está dentro del X% del techo teórico que calculaste (N0·L1, N1·L1); explicas el gap.
- RadixAttention: reproduces el speedup de SGLang sobre vLLM en una workload prefix-heavy real, con el número (N1·L5).
- Multi-LoRA: sirves ≥3 adaptadores con selección por petición y reportas latencia fría/caliente + throughput con mezcla + hit-rate de prefix cache (N1·L6).
- Sabes elegir y justificar motor (vLLM/SGLang), precisión (FP8/NVFP4) y spec decoding para un caso dado, con tus números.
Rúbrica: Nivel 4 (innovador) si tu gateway combina conscientemente varias técnicas y mides el efecto de cada una; Nivel 3 si reproduces los tres puntos y explicas el porqué. El aprobado de C1 es Nivel 3+.
Subproducto público opcional: repo blackwell-serving-cookbook (presets vLLM/SGLang/NVFP4 para 5090 + benchmarks) y un modelo NVFP4 propio en HF.
6.6Cierre del Nivel 1
Ya no "usas vLLM": entiendes el motor, mides contra el techo físico, eliges motor/precisión/spec con criterio, y sirves muchos modelos especializados sobre un base. Esto es el spine secundario a nivel world-class. En el Nivel 2 (el spine principal) crearás los adaptadores que aquí solo serviste — y cerrarás el círculo entrenar↔servir.
6.7Referencias
- Sheng et al., "S-LoRA: Serving Thousands of Concurrent LoRA Adapters" (2023). Docs vLLM "LoRA Adapters". Estudios de multi-LoRA serving y gestión de KV (arXiv 2505.03756, 2407.00066). Knoop & Holtmann, "Private LLM Inference on Consumer Blackwell GPUs" (arXiv 2601.09527) — benchmarks multi-LoRA en RTX 5090.