Слогер Создать блог
Разработка

Как отладить NaN активации в vLLM на GPU V100: воспроизведение, мониторинг и фикс

V100 не поддерживает FlashAttention-2, поэтому vLLM использует кастомные ядра. В связке с квантизованными моделями это иногда даёт NaN активации. Разбираемся, как это поймать и починить.

V100 — всё ещё рабочая лошадка во многих кластерах. Но с современными моделями есть нюанс: архитектура не поддерживает FlashAttention-2, поэтому vLLM приходится использовать кастомные ядра вроде flash-attention-v100. И вот тут начинается веселье: квантизованные модели + нестандартный attention иногда дают NaN на выходе. Не стабильно, не всегда, а в самый неподходящий момент. Обычно это выглядит как бред в ответе модели или падение генерации.

Если вы запускаете на V100 квантизованную модель через vLLM (например, MiniMax-M2.7-AWQ), рано или поздно встретитесь с этой бедой. Но проблему можно системно отладить: воспроизвести на минимальном наборе промптов, добавить мониторинг активаций и найти конкретный слой, где всё ломается.

С чего всё начинается

V100 вышел в 2017 году. В нём нет аппаратной поддержки FlashAttention-2, поэтому для него пишут отдельный kernel. И вот этот kernel в связке с квантизацией (AWQ, GPTQ и т.п.) может производить NaN activations на некоторых входных данных. Чаще всего проблема уходит в attention-слои или layer norm.

Воспроизвести ошибку — уже половина дела. Поэтому первым делом пишем скрипт, который прогоняет набор простых промптов и проверяет вывод на NaN. Таких промптов нужно немного — пять штук достаточно. Например, “Explain quantum computing in simple terms” или “Write a Python function to reverse a linked list”. Если модель выдаст NaN хотя бы на одном — вы поймали баг.

Шаг 1: минимальный скрипт для воспроизведения

Обязательные условия: Python 3.10+, vLLM и flash-attention-v100, установленные через pip. Также нужен путь к квантизованной модели. Вот костяк такого скрипта:

from vllm import LLM, SamplingParams def test_single_prompt(llm, prompt, max_tokens=50): sampling_params = SamplingParams(temperature=0.0, max_tokens=max_tokens) outputs = llm.generate([prompt], sampling_params) output_text = outputs[0].outputs[0].text if "nan" in output_text.lower(): return False return True llm = LLM( model="/home/youruser/models/MiniMax-M2.7-AWQ", tensor_parallel_size=1, gpu_memory_utilization=0.85, max_model_len=2048, enforce_eager=True, dtype="float16" )

Ключевые детали: tensor_parallel_size=1 — чтобы изолировать проблему, enforce_eager=True — отключить CUDA graph, они часто маскируют ошибку. dtype=float16 — стандарт для инференса, но именно с ним и вылезают NaN.

Прогоните 5–10 промптов. Если хотя бы один упал — переходим ко второму шагу.

Шаг 2: ловим NaN до того, как он испортит ответ

Проверка текста на “nan” — это уже финал. Чтобы найти источник, нужно следить за активациями внутри модели. Для этого навешиваем forward-хуки на attention-слои и layer norm:

import torch def add_nan_monitor(model): nan_locations = [] def hook_fn(module, input, output): if isinstance(output, torch.Tensor) and torch.isnan(output).any(): nan_locations.append(f"{module.__class__.__name__} output") if isinstance(input, tuple): for i, inp in enumerate(input): if isinstance(inp, torch.Tensor) and torch.isnan(inp).any(): nan_locations.append(f"{module.__class__.__name__} input[{i}]") return output for name, module in model.named_modules(): if "attention" in name.lower() or "layernorm" in name.lower(): module.register_forward_hook(hook_fn) return nan_locations

Хуки вешаются только на интересующие слои, чтобы не тормозить инференс и не завалить память. Как только появится NaN в каком-то слое — вы узнаете первым. Дальше можно копать глубже: смотреть веса, входные данные, порядок слоёв.

Что делать, если NaN найден

Единого “лекарства” нет, но есть рабочие приёмы. Соберём их в таблицу.

Симптом Причина Что попробовать
NaN в первом же слое attention Конфликт кастомного ядра с квантизацией Обновите flash-attention-v100 или vLLM до свежей версии. Иногда проблема уже исправлена в новом релизе
NaN только на длинных промптах Нестабильность при вычислениях в fp16 Попробуйте включить dtype=float32 для проверки (только для диагностики, инференс будет медленнее). Если пропало — дело в точности
NaN на выходе, но не в активациях Проблема в сэмплинге или декодере Поставьте temperature=0 и top_p=1, чтобы исключить случайность
NaN при использовании CUDA graph Известный баг с vLLM и V100 Оставьте enforce_eager=True постоянно

Самый частый совет из практики — не геройствовать с кастомными ядрами. Если модель не обязательно гонять через FlashAttention, отключите его через vLLM и используйте обычный attention. Медленнее на 20–30%, зато стабильно.

Типичные ошибки при отладке

  • Забыть enforce_eager=True. CUDA graph скрывает проблему и делает воспроизведение недетерминированным.
  • Проверять только текст, а не токены. Иногда NaN лежит в логитах, а в текст не попадает.
  • Слишком длинные промпты в тесте. Начните с коротких — их легче анализировать.
  • Гонять на одном промпте. Это лотерея: NaN может выскочить на втором, а не на первом.
  • Смотреть на веса модели. Чаще всего проблема не в весах, а в вычислениях.

Ещё один трюк: попробуйте запустить ту же модель без квантизации (если есть доступ к оригинальным весам). Если NaN исчез — проблема в AWQ или квантованных весах. Если остался — дело в ядре.

Когда лучше не использовать V100

Если вам нужно гонять современные LLM с квантизацией на постоянной основе, подумайте о переходе на A100 или L4. На V100 вы будете вечно балансировать между скоростью кастомных ядер и их хрупкостью. Хотя для тех, у кого бюджет ограничен, наш гайд поможет продержаться ещё какое-то время.

В любом случае, прежде чем менять железо, проверьте три вещи: версия vLLM, версия flash-attention-v100, и тип данных. Половина проблем решается просто апдейтом.

Отладка NaN в vLLM — это системный процесс: воспроизвели, локализовали, обновили или обошли. Главное — не паниковать и не переписывать модель с нуля. Обычно достаточно одного маленького фикса.

По материалам: ai. Текст переработан редакцией Слогера.

← На главную

Рекламное место — Конец поста
Реклама · Слогер

Комментарии (0)

Войдите, чтобы комментировать.

Пока нет комментариев. Будьте первым.