GRATÍCULAinstrumento de maestría
BancoRTX 5090 · GB202
Rev2026.06
Entrar
N1 · Serving world-class/L6

Multi-LoRA serving (y cierre del Checkpoint C1)

Objetivo de maestría

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

bash
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/clasif

Y en la petición eliges el adaptador por nombre, como si fuera un modelo:

python
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-rank debe 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):

python
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.