GPT-OSS на първия ден
Поддръжка още на първия ден за GPT‑OSS на H200 чрез адаптиране на ROSE с FP8, sink attention и MoE оптимизации
Perplexity се ангажира да предостави най-добрите AI модели на любознателни потребители, които изискват точни и надеждни отговори и автономни действия (agentic actions). OpenAI наскоро обяви своите модели с отворени тегла (gpt-oss-20b и gpt-oss-120b). Както при много водещи модели, Perplexity е сред първите организации, които оценяват gpt-oss-20b и gpt-oss-120b.
В тази публикация споделяме решенията за инфраструктурата на нашия собствен стек за инференс, които направиха възможна поддръжката още на първия ден (Day-0). Фокусираме се върху обслужването на тези модели на GPU-та NVIDIA H200, като описваме подробно промените в кернелите, изборите за внедряване и компромисите между скорост и разходи.
GPT-OSS на Hopper
Моделите с отворени тегла се доставят с MXFP4 квантоване, което им помага да се поберат в паметта на хардуер от потребителски клас или да постигнат максимална пропускателна способност на NVIDIA Blackwell. Въпреки това, за първоначална оценка искахме да ги стартираме на съществуващи клъстери H200 Hopper с минимални промени в двигателя за инференс. Hopper няма специализирани FP4 тензорни ядра, които бяха въведени от Blackwell. Следователно решихме да обслужваме моделите с FP8 прецизност, за да минимизираме нуждата от потребителски кернели и да използваме максимално наличния хардуер.
На високо ниво трансформерните LLM са структурно прости:
Входно вграждане (Input embedding)
Поредица от трансформерни слоеве. Всеки съдържа блоков внимание (attention block) и плътен MLP или разреден MoE блок.
Проекция на изходни логити (Output logit projection)
Когато внедряваме нов трансформерен модел, основно се интересуваме да разберем до каква степен те съответстват на съществуващите модели, за да идентифицираме необходимите промени. OpenAI любезно сподели преглед на своите модели предварително, което ни позволи да настроим съответно нашата настройка за инференс. Установихме, че моделът наистина изисква доста малки промени.
Внимание (Attention): GQA с Attention Sinks
Всяка глава за внимание (attention head) има предварително обучен параметър sink, който се отчита в Softmax
YaRN позиционно кодиране, подобно на DeepSeek-V3
QKV проекциите имат отклонения (biases), подобно на Qwen 2
Изходната проекция има отклонение (bias)
MLP: Разреден смесен експертен модел (Sparse Mixture-of-Expert - MoE)
SwiGLU активираща функция, различна от повечето модели с отворени тегла
Softmax след Top-K за експертни тегла
Експертите имат отклонения (biases)
Очаквайки броят на параметрите да бъде около 100B-200B, решихме да:
Разширим FlashInfer за поддръжка на attention sinks
Използваме отново MoE имплементацията, вече изградена за семейството модели DeepSeek-V3
Изберем мащабиране на блокове FP8 128x128 като схема за квантоване, като разширим кернелите DeepGEMM за поддръжка на член с отклонение (bias term).
Собствен двигател за инференс (In-house Inference Engine)
Нашите усилия за инференс се поддържат от нашия собствен двигател за инференс ROSE. Изграждаме ROSE като гъвкава рамка, която позволява бързо внедряване на нови модели. Освен това, след като се ангажираме да обслужваме модел в мащаб, тя предоставя платформа за оптимизиране и повишаване на производителността, за да се достигне съвременното ниво (state of the art).
ROSE излага LLM двигател, който може да зарежда модел и да генерира декомпресирани токени за него чрез множество декодери, включително спекулативни и MTP декодери. Самите модели се състоят от персонализиран набор от слоеве, които поддържат различни конфигурационни параметри за квантоване и паралелизъм. Под слоевете той обединява както Triton, така и CUDA кернели, които ефективно имплементират и обединяват изчисленията, извършвани от отделните слоеве.
ROSE е изграден предимно на Python и разчита на PyTorch за дефинициите на моделите. Въпреки че Python ни позволява да бъдем гъвкави и да се адаптираме към нови модели, ние мигрираме повечето чувствителни към производителността компоненти, като обслужване и планиране на партиди (batch scheduling), към Rust.
За да стартираме нов модел, първо дефинираме йерархията на модела и изграждаме конвертор на тегла за него. След като всички тегла бъдат отчетени, дефинираме право насочено подаване (forward pass) за най-опростения случай на използване TP=1 и гарантираме, че работи от край до край. И накрая, имплементираме и тестваме поддръжката за различни форми на паралелизъм, преди да пуснем нов контейнер и да разположим модела в клъстер за оценка. След това надграждаме производителността, разчитайки на обратна връзка както от таблата за управление (dashboards), така и от по-фино настроени бенчмаркове.
ROSE работи с числови токени, с малко познания за форматите за чат извън интеграционните тестове. Паралелно с това трябваше да проучим адаптирането на прилежащата инфраструктура за поддръжка на токенизатора Harmony и правилното картографиране на заявките към входни последователности от токени.
С модела GPT OSS установихме, че не са необходими промени в нашите декодери, CUDA графични харнеси или прилежаща инфраструктура, което ни позволи да насочим усилията си към настройка на кернелите, поддържащи модела.
Кернели за GPT-OSS
Sink Attention
Sink attention въвежда допълнителен фактор на отклонение преди softmax, акумулиран с произведението на Q и K:
attn = softmax(q @ k^T * sm_scale + sink) * VВъпреки че наивната имплементация е доста елементарна, ефективните имплементации на вниманието, като FlashInfer, паралелизират изчисляването на вниманието както през независими глави, така и по дължината на KV последователността. Освен това, за числена стабилност Softmax се изчислява онлайн, като същевременно се изважда максималният елемент за числена стабилност:
softmax(x) = e^x / sum(e^x) = e ^ (x - max(x)) / sum(e ^ (x - max(x))Онлайн имплементацията проследява текущ максимум m, инициализиран на -inf, и мащаб d към 1. Чрез инициализиране на максимума към стойността на sink на текущата глава, щяхме да гарантираме, че той ще бъде акумулиран правилно в Softmax. Трябваше да внимаваме да гарантираме, че този максимум ще бъде включен само в първия чанк, ако вниманието е разделено на множество блокове по дължината на последователността.
Адаптирахме шаблона на кернела FlashInfer FA2, използван както за prefill, така и за decode, за да включва параметрите sink.
MoE
Основната разлика между MoE слоя на различните модели обикновено е схемата на маршрутизиране. Очаквайки съществена вариативност, ROSE имплементира този керсел в Triton, което ни позволява лесно да го адаптираме към нови модели. В случай на GPT-OSS трябваше да адаптираме оценяването на теглата, за да извършваме softmax само за избраните експерти Top-K, за разлика от някои модели в стил DeepSeek, които изчисляват softmax за всички експерти. Установихме, че за точността е от критично значение експертните тегла да се изчисляват в bfloat16 прецизност.
Предизвикателството с MoE слоя се дължеше на нашето решение да използваме отново съществуващите кернели DeepGEMM, на които липсваше поддръжка за членове с отклонение (bias terms). За щастие, с течение на времето се интегрирахме дълбоко с тях, правейки промени за по-добра връзка с нашите потребителски all-to-all диспечерски кернели. Добавихме поддръжка за bias към DeepGEMM чрез инициализиране на акумулаторните регистри WGMMA към стойностите на bias, заредени от паметта. Това е еднократно действие по време на обработката на MN блок, така че цената вероятно е пренебрежимо малка, а кешът L2 осигурява ефективен достъп до тези стойности.
Тъй като тези модели не се възползват директно от Data Parallelism, използвахме отново диспечерски и комбинирани кернели само за TP (TP-only dispatch and combine kernels). Входовете за диспечера се репликират във всички ранкове, така че диспечерът избира токените за локално наличните експерти. Комбинирането (Combine) извършва претеглената акумулация през NVLink.
Оптимизации на производителността
Въпреки че целта ни беше да пуснем моделите бързо, искахме да се уверим, че производителността все пак е разумна. След като се уверихме в коректността спрямо референтна имплементация, предоставена от OpenAI, профилирахме нашата имплементация, използвайки стандартния PyTorch профилатор, следейки за разпределението на блокове и нишки (threads) между кернелите. Функционалността, която използвахме повторно, беше изградена за модели, които имаха по-голяма скрита размерност (hidden dimension) от 7168, значително по-голяма от 2880 на моделите GPT-OSS. Специализирахме стартиращите кернели (kernel launchers), за да избират по-добри размери на мрежата, което значително увеличи пропускателната способност.
Токенизация и канали
Форматът за чат на Harmony има няколко важни концепции за структуриране на разговори, съдържание за разсъждения и извиквания на функции. В този формат всяко съобщение се състои от заглавка и съдържание:
<|start|>{header}<|message|>{content}<|end|>Заглавката носи метаданни — най-важното е ролята (система, разработчик, потребител, асистент, инструмент), канала и получател, като to=functions.<function-name> при извикване на инструмент.
Каналите (като анализ, коментар и краен отговор) правят изходите на модела по-прозрачни и сегментирани. Тъй като форматът налага това разделяне, моделът може да разсъждава прозрачно (и да бъде инспектиран за отстраняване на грешки), без да изтича верига на мисли (chain-of-thought) в продукция. Таговете за получатели (заглавки to=) казват точно на изпълняемата среда кой актор трябва да получи следващия полезен товар (payload) — независимо дали това е функция, самият модел или краен потребител.
Нашият двигател за инференс ROSE е независим от формата. Той получава партида входни токени и стриймва обратно изходни токени. В нашия стек за инференс задачата за форматиране на входни данни в токенизиран промпт и парсиране на резултатите от LLM обратно в структурирани съобщения се обработва от специализирания преден край (frontend service) на JSON API. Интегрирахме Harmony, като добавихме нов модулен форматър/парсър към този преден край, което позволява на задния край (backend) да остане непроменен, докато предният край изчистено управлява правилата за формата за чат, извикванията на инструменти и стрийминга. Това разделяне на отговорностите поддържа архитектурата модулна, което позволява бързо приемане на нови формати и функции за отговор с минимални промени в задния край.
Разходи и производителност
Предвид относително малкия размер на моделите GPT-OSS, ограничаваме разполагането на реплика до един единствен нод, за да избегнем овърхеда от комуникация между нодовете. Нашата имплементация поддържа гъвкави комбинации от експертен паралелизъм (EP), паралелизъм на данните (DP) и тензорен паралелизъм (TP). За да определим оптималната настройка за разполагане, направихме бенчмарк на всички комбинации от стратегии за шардиране в рамките на размери на партидите от 1 до 128. Благодарение на разделянето на предварителен преглед и декодиране (Prefill-Decode Disaggregation), можем да обсъдим компромиса между производителност и разходи отделно за prefill и decode.
За оценките на разходите приемаме номинална пазарна цена за H200 от $3.00/час (забележка: това не е нашата действителна цена за GPU).
Установихме, че задаването на batch_size=1 и DP=1 дава най-добра производителност при prefill. Различните TP конфигурации след това осигуряват различни компромиси между латентност на първия токен и разходи. Таблиците по-долу представят латентността при prefill и разходите за милион входни токени за различни конфигурации и входни дължини.
GPT-OSS 120B:
Входна дължина | 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 |
Забавяне на предварителното зареждане и цена на милион входни токена за GPT-OSS 120B според конфигурацията на паралелизма и дължината на входа.
GPT-OSS 20B:
Входна дължина | 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 |
Забавяне на предварителното зареждане и цена на милион входни токена за GPT-OSS 20B според конфигурацията на паралелизма и дължината на входа.
За декодиране (decode) установихме, че EP4 DP1 TP4 е най-добрата конфигурация за повечето случаи. Четирите графики по-долу илюстрират компромиса между скоростта на декодиране и разходите за милион изходни токени както за моделите 120B, така и за 20B при дължини на последователността 8192 и 32768.
Обмислихме също така използването на по-малкия модел 20B като чернова модел за спекулативно декодиране (speculative decoding) за по-големия модел 120B. Въпреки това, тъй като броят на активираните параметри е подобен (3B спрямо 5B), полученото ускорение беше пренебрежимо малко.
Референции
Разделени префил и декодиране (Disaggregated Prefill and Decode)
Ускоряване на Sonar чрез спекулации (Accelerating Sonar Through Speculation)
Технически отчет за DeepSeek-V3 (DeepSeek-V3 Technical Report)
Цитиране
@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}
}