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 — это системный процесс: воспроизвели, локализовали, обновили или обошли. Главное — не паниковать и не переписывать модель с нуля. Обычно достаточно одного маленького фикса.
Комментарии (0)
Войдите, чтобы комментировать.
Пока нет комментариев. Будьте первым.