GPT-OSS en el Día 0

Soporte de Día 0 para GPT‑OSS en H200 mediante la adaptación de ROSE con FP8, sumideros de atención y optimizaciones de MoE

AutoresPerplexity Engineering

Perplexity tiene el compromiso de poner los mejores modelos de IA al alcance de las personas curiosas que exigen respuestas precisas y confiables, así como acciones de agentes. OpenAI anunció recientemente sus modelos de pesos abiertos (gpt-oss-20b y gpt-oss-120b). Al igual que con muchos modelos líderes, Perplexity se encuentra entre las primeras organizaciones en evaluar gpt-oss-20b y gpt-oss-120b.

En esta publicación, compartimos las decisiones de infraestructura de nuestra pila de inferencia interna que hicieron posible el soporte de Día 0. Nos enfocamos en ofrecer estos modelos en GPU NVIDIA H200, detallando los cambios en los núcleos, las opciones de implementación y las compensaciones entre velocidad y costo.

GPT-OSS en Hopper

Los modelos de pesos abiertos se distribuyen mediante la cuantización MXFP4, lo que les ayuda a ajustarse a la memoria en hardware de consumo general o a alcanzar el rendimiento máximo en NVIDIA Blackwell. Sin embargo, para la evaluación inicial, queríamos ejecutarlos en clústeres Hopper H200 existentes con un mínimo de cambios en el motor de inferencia. Hopper no cuenta con núcleos tensoriales FP4 dedicados, los cuales fueron introducidos por Blackwell. En consecuencia, decidimos servir los modelos con precisión FP8 para minimizar la necesidad de núcleos personalizados y aprovechar al máximo el hardware disponible.

A grandes rasgos, los modelos LLM de tipo transformer son estructuralmente simples:

Incrustación de entrada

Una secuencia de capas transformer. Cada una contiene un bloque de atención y un bloque MLP denso o MoE disperso.

Proyección de logits de salida

Al implementar un nuevo modelo transformer, nos interesa principalmente saber hasta qué punto coincide con los modelos existentes para identificar los cambios necesarios. OpenAI compartió amablemente un resumen de sus modelos con anticipación, lo que nos permitió ajustar nuestra configuración de inferencia en consecuencia. Descubrimos que el modelo requería una cantidad considerable de pequeños cambios.

Atención: GQA con sumideros de atención

Cada cabeza de atención tiene un parámetro de sumidero preentrenado que se toma en cuenta en Softmax

Codificación posicional YaRN, similar a DeepSeek-V3

Las proyecciones QKV tienen sesgos, de manera similar a Qwen 2

La proyección de salida tiene sesgo

MLP: Mezcla dispersa de expertos (MoE)

Función de activación SwiGLU, diferente a la de la mayoría de los modelos de pesos abiertos

Softmax después de Top-K para los pesos de los expertos

Los expertos tienen sesgos

Dado que esperábamos que el número de parámetros estuviera entre 100B y 200B, decidimos:

Ampliar FlashInfer para admitir sumideros de atención

Reutilizar la implementación de MoE ya creada para la familia de modelos DeepSeek-V3

Elegir el escalado de bloques FP8 de 128x128 como esquema de cuantización, ampliando los núcleos DeepGEMM para admitir un término de sesgo.

Motor de inferencia interno

Nuestros esfuerzos de inferencia están respaldados por nuestro motor de inferencia interno, ROSE. Estamos desarrollando ROSE para que sea un marco flexible que permita implementar nuevos modelos rápidamente. Además, una vez que nos comprometemos a servir un modelo a escala, ofrece una plataforma para optimizar y potenciar el rendimiento hasta alcanzar el estado del arte.

ROSE expone un motor LLM capaz de cargar un modelo y generar tokens decodificados mediante múltiples decodificadores, incluidos los decodificadores especulativos y MTP. Los modelos en sí se componen de un conjunto personalizado de capas que admiten varias opciones de configuración para la cuantización y el paralelismo. Por debajo de las capas, incluye núcleos de Triton y CUDA que implementan y fusionan de manera eficiente los cálculos realizados por cada capa individual.

ROSE está construido principalmente en Python y depende de PyTorch para las definiciones de los modelos. Aunque Python nos otorga flexibilidad y capacidad de adaptación a nuevos modelos, estamos migrando la mayoría de los componentes sensibles al rendimiento, como el servicio y la programación por lotes, a Rust.

Para implementar un nuevo modelo, primero definimos la jerarquía del modelo y construimos un conversor de pesos para este. Una vez que se contabilizan todos los pesos, definimos un paso hacia adelante (forward pass) para el caso de uso más simple con TP=1 y nos aseguramos de que funcione de extremo a extremo. Finalmente, implementamos y probamos el soporte para diversas formas de paralelismo antes de lanzar un nuevo contenedor y desplegar el modelo en un clúster para su evaluación. Luego, iteramos en el rendimiento utilizando los comentarios de los paneles y de pruebas de rendimiento (benchmarks) más detalladas.

ROSE opera con tokens numéricos y tiene un conocimiento limitado de los formatos de chat, salvo en las pruebas de integración. De forma paralela, tuvimos que investigar la adaptación de la infraestructura circundante para admitir el tokenizador Harmony y asignar correctamente las solicitudes a las secuencias de tokens de entrada.

Con el modelo GPT OSS, descubrimos que no se requería ningún cambio en nuestros decodificadores, entornos de grafos CUDA ni en la infraestructura circundante, lo que nos permitió centrar nuestros esfuerzos en ajustar los núcleos que respaldan al modelo.

Núcleos para GPT-OSS

Atención con sumidero

La atención con sumidero introduce un factor de sesgo adicional antes de softmax, acumulado con el producto de Q y K:

plaintext
attn = softmax(q @ k^T * sm_scale + sink) * V

Aunque una implementación ingenua es bastante trivial, las implementaciones de atención eficientes, como FlashInfer, paralelizan el cálculo de la atención tanto entre cabezas independientes como a lo largo de la longitud de la secuencia KV. Además, para garantizar la estabilidad numérica, Softmax se calcula en línea restando también el elemento máximo:

plaintext
softmax(x) = e^x / sum(e^x) = e ^ (x - max(x)) / sum(e ^ (x - max(x))

La implementación en línea rastreó un valor máximo dinámico m inicializado en -inf y una escala d en 1. Al inicializar el valor máximo con el valor del sumidero de la cabeza actual, nos aseguraríamos de que se acumulara correctamente en Softmax. Tuvimos que asegurarnos de que este valor máximo solo se incluyera en el primer fragmento en caso de que la atención se dividiera en varios bloques a lo largo de la longitud de la secuencia.

Hemos ajustado la plantilla del núcleo FlashInfer FA2, utilizada tanto para el prellenado (prefill) como para la decodificación, para incluir los parámetros de los sumideros.

MoE

La principal diferencia entre la capa MoE de distintos modelos suele ser el esquema de enrutamiento. Al prever una variabilidad considerable, ROSE implementa este núcleo en Triton, lo que nos permite adaptarlo fácilmente a nuevos modelos. En el caso de GPT-OSS, tuvimos que ajustar la puntuación de los pesos para realizar softmax únicamente entre los expertos seleccionados Top-K, a diferencia de algunos modelos de estilo DeepSeek que calculan softmax en todos los expertos. Descubrimos que es fundamental para la precisión calcular los pesos de los expertos con precisión bfloat16.

El desafío con la capa MoE se debió a nuestra decisión de reutilizar los núcleos DeepGEMM existentes, los cuales carecían de soporte para términos de sesgo. Afortunadamente, con el tiempo nos integramos profundamente con ellos, realizando modificaciones para conectarlos mejor con nuestros núcleos de distribución all-to-all personalizados. Añadimos soporte para sesgos a DeepGEMM inicializando los registros acumuladores WGMMA con los valores de sesgo cargados desde la memoria. Esto ocurre una sola vez durante el procesamiento de un bloque MN, por lo que el costo probablemente sea insignificante y la caché L2 garantiza un acceso eficiente a estos valores.

Dado que estos modelos no se benefician directamente del paralelismo de datos (Data Parallelism), reutilizamos los núcleos de combinación y distribución exclusivos de TP. Las entradas para la distribución se replican en todos los rangos, por lo que la distribución selecciona los tokens para los expertos disponibles localmente. La combinación realiza la acumulación ponderada a través de NVLink.

Ajustes de rendimiento

Aunque nuestro objetivo era implementar los modelos rápidamente, queríamos garantizar que el rendimiento siguiera siendo razonable. Tras comprobar la corrección frente a una implementación de referencia proporcionada por OpenAI, perfilamos nuestra implementación utilizando el generador de perfiles predeterminado de PyTorch, prestando atención a las asignaciones de bloques y hilos en los núcleos. La funcionalidad que reutilizamos se había diseñado para modelos con una dimensión oculta mayor, de 7168, sustancialmente superior a los 2880 de los modelos GPT-OSS. Especializamos los lanzadores de núcleos para elegir mejores dimensiones de cuadrícula, lo que incrementó significativamente el rendimiento.

Tokenización y canales

El formato de chat de Harmony cuenta con varios conceptos importantes para estructurar las conversaciones, el contenido de razonamiento y las llamadas a funciones. En este formato, cada mensaje consta de una cabecera y un contenido:

plaintext
<|start|>{header}<|message|>{content}<|end|>

La cabecera contiene metadatos —principalmente el rol (sistema, desarrollador, usuario, asistente, herramienta), el canal y un destinatario como to=functions.<function-name> cuando se invoca una llamada a una herramienta—.

Los canales (como análisis, comentarios y respuesta final) hacen que las salidas del modelo sean más transparentes y segmentadas. Como el formato impone esta separación, el modelo puede razonar de forma transparente (y ser inspeccionado para la depuración) sin filtrar la cadena de pensamiento (chain-of-thought) en producción. Las etiquetas de destinatario (cabeceras to=) indican exactamente al tiempo de ejecución (runtime) qué actor debe recibir la siguiente carga útil, ya sea una función, el propio modelo o el usuario final.

Nuestro motor de inferencia ROSE es agnóstico respecto al formato. Recibe un lote de tokens de entrada y transmite de vuelta los tokens de salida. En nuestra pila de inferencia, la tarea de formatear las entradas en un aviso (prompt) tokenizado y analizar los resultados del LLM para convertirlos nuevamente en mensajes estructurados es gestionada por el servicio frontal de API JSON dedicado. Integramos Harmony agregando un formateador/analizador conectable a este front-end, lo que permite que el back-end permanezca intacto mientras el front-end gestiona de manera limpia las reglas del formato de chat, las llamadas a herramientas y la transmisión. Esta separación de intereses mantiene la arquitectura modular, lo que facilita la rápida adopción de nuevos formatos y funciones de respuesta con mínimos cambios en el back-end.

Costo y rendimiento

Dado el tamaño relativamente pequeño de los modelos GPT-OSS, limitamos el despliegue de una réplica a un solo nodo para evitar la sobrecarga de comunicación entre nodos. Nuestra implementación admite combinaciones flexibles de paralelismo de expertos (EP), paralelismo de datos (DP) y paralelismo de tensores (TP). Para determinar la configuración de despliegue óptima, realizamos pruebas comparativas de todas las combinaciones de estrategias de fragmentación (sharding) en tamaños de lote de 1 a 128. Gracias a la desagregación de prellenado y decodificación, podemos analizar la compensación entre rendimiento y costo por separado para el prellenado y la decodificación.

Para las estimaciones de costos, asumimos una tarifa de mercado nominal para H200 de $3.00/hora (nota: este no es el precio real de nuestras GPU).

Descubrimos que configurar batch_size=1 y DP=1 ofrece el mejor rendimiento de prellenado. Las diferentes configuraciones de TP proporcionan compensaciones variadas entre la latencia del primer token y el costo. Las tablas siguientes muestran la latencia de prellenado y el costo por millón de tokens de entrada para varias configuraciones y longitudes de entrada.

GPT-OSS 120B:

Longitud de entrada

8192

32768

65536

128000

EP1 DP1 TP1

0.364s, $0.037

2.421s, $0.062

7.375s, $0.094

23.902s, $0.156

EP2 DP1 TP2

0.190s, $0.039

1.245s, $0.063

3.786s, $0.096

12.116s, $0.158

EP4 DP1 TP4

0.165s, $0.067

0.900s, $0.092

2.449s, $0.125

7.165s, $0.187

EP8 DP1 TP8

0.179s, $0.145

0.839s, $0.171

2.003s, $0.204

5.119s, $0.267

Latencia de prellenado de GPT-OSS 120B y costo por millón de tokens de entrada según la configuración de paralelismo y la longitud de entrada.

GPT-OSS 20B:

Longitud de entrada

8192

32768

65536

128000

EP1 DP1 TP1

0.239s, $0.024

1.610s, $0.041

4.895s, $0.062

16.037s, $0.104

EP2 DP1 TP2

0.127s, $0.026

0.833s, $0.042

2.522s, $0.064

8.143s, $0.106

EP4 DP1 TP4

0.108s, $0.044

0.593s, $0.060

1.616s, $0.082

4.766s, $0.124

EP8 DP1 TP8

0.116s, $0.094

0.547s, $0.111

1.310s, $0.133

3.365s, $0.175

Latencia de prellenado de GPT-OSS 20B y costo por millón de tokens de entrada según la configuración de paralelismo y la longitud de entrada.

Para la decodificación, descubrimos que EP4 DP1 TP4 es la mejor configuración en la mayoría de los casos. Los cuatro gráficos siguientes ilustran la compensación entre la velocidad de decodificación y el costo por millón de tokens de salida para los modelos de 120B y 20B con longitudes de secuencia de 8192 y 32768.

Costo de solo decodificación por millón de tokens de salida frente a la velocidad de decodificación para GPT-OSS 120B con una longitud de secuencia de 8,192, comparando las configuraciones EP1, EP2, EP4 y EP8.
Costo de solo decodificación por millón de tokens de salida frente a la velocidad de decodificación para GPT-OSS 120B con una longitud de secuencia de 32,768, comparando las configuraciones EP1, EP2, EP4 y EP8.
Costo de solo decodificación por millón de tokens de salida frente a la velocidad de decodificación para GPT-OSS 20B con una longitud de secuencia de 8,192, comparando las configuraciones EP1, EP2, EP4 y EP8.
Costo de solo decodificación por millón de tokens de salida frente a la velocidad de decodificación para GPT-OSS 20B con una longitud de secuencia de 32,768, comparando las configuraciones EP1, EP2, EP4 y EP8.

También hemos considerado utilizar el modelo más pequeño de 20B como un modelo borrador de decodificación especulativa para el modelo más grande de 120B. Sin embargo, dado que el número de parámetros activados es similar (3B frente a 5B), las mejoras de velocidad resultantes fueron marginales.

Referencias

Prefill y decodificación desagregados

Aceleración de Sonar mediante especulación​

Menor latencia y mayor rendimiento con despliegue de DeepSeek en múltiples nodos

​Comunicación eficiente y portátil en modelos de mezcla de expertos

​pplx-kernels

DeepGEMM

FlashInfer: Motor de atención eficiente y personalizable para servicio de inferencia LLM

Modelos de lenguaje en flujo eficientes con sumideros de atención

Informe técnico de DeepSeek-V3​

​Formato de respuesta de OpenAI Harmony | Libro de recetas de OpenAI​

Citación

BibTeX
@online{perplexity2025GptOssOnDay0,
  author = {{Perplexity} and Nandor Licker and Lequn Chen and Vladimir Zaytsev and Kevin Hu and Lauren Kolm and Jesse Dwyer},
  title = {{GPT-OSS on day 0}},
  date = {2025-08-08},
  url = {https://www.perplexity.ai/hub/blog/gpt-oss-on-day-0}
}