Dory 1.0 4B — небольшая рассуждающая модель из NVIDIA Nemotron Labs, у которой средние слои образуют общий блок. Этот блок можно применить к каждому токену один, два, три или четыре раза. Каждый дополнительный проход тратит больше вычислений на токен, но не добавляет ни одного параметра: один и тот же чекпойнт работает при четырёх разных вычислительных бюджетах без переобучения. Ниже — порядок действий из разбора: собрать нужную ветку llama.cpp, скачать GGUF, прочитать архитектуру прямо из файла, а затем сверить предсказания с работающим сервером.
Почему нельзя взять готовый бинарник
Поддержка Dory в llama.cpp на момент разбора остаётся открытым черновиком pull request, поэтому ни один выпущенный бинарник модель не загрузит. Нужна конкретная ветка, которую закрепляет карточка модели:
FORK = "https://github.com/brianchmielNV/llama.cpp.git"
FORK_COMMIT = "98a74c97ea09ac72f6ceda1559808fc0a2f447ce"
Скрипт определяет платформу и передаёт флаги Metal на Mac либо компилирует CUDA-ядра только под установленную видеокарту в Colab. Ядра flash-attention при этом отключаются — их всё равно не использует --flash-attn off из команд карточки, а сборка на T4 остаётся подъёмной. После сборки скрипт проверяет, что выгружен именно закреплённый коммит, чтобы все дальнейшие числа относились к одной известной версии кода.
GGUF: имя файла в карточке не совпадает с репозиторием
Файл Q5_K_M скачивается с Hugging Face на фиксированной ревизии и хешируется до того, как его коснётся что-либо ещё. Карточка называет файл dory-q5_k_m.gguf, а в репозитории он лежит как NVIDIA-Nemotron-Labs-Dory1.0-4B-Q5_K_M.gguf — дословное копирование команды из карточки падает. Связка «настоящее имя + размер + SHA-256» ловит и чужой файл, и повреждённую загрузку. Файл на 3,67 ГБ совпадает с опубликованной контрольной суммой, так что все измерения идут на тех же байтах, что выложила NVIDIA.
Что заголовок GGUF говорит об архитектуре
72 записи слоёв делятся на входную группу из 16, общую рекуррентную из 40 и выходную из 16, чередуя внимание и MLP, с одним полным вниманием на каждые три слоя скользящего окна. Суммирование размеров тензоров по группам показывает: рекуррентный блок — это 1,84 из 3,98 млрд параметров модели.
В заголовке припрятана ловушка: встроенное число циклов равно 4, тогда как карточка рекомендует 2. Сервер, запущенный без переопределения, молча работает в самом дорогом режиме.
Сколько стоит лишний цикл: параметры и KV-кэш
Токен проходит через входной блок, рекуррентный блок (по разу на цикл), выходной блок и LM head. Отсюда четыре цикла применяют в 2,51 раза больше параметров на токен, чем один, и читают в 2,28 раза больше байт весов — вторая цифра важнее для декодера, ограниченного памятью.
KV-кэш подчиняется правилу выделения в этой ветке. Режим per_loop держит отдельный кэш для каждого цикла каждого рекуррентного слоя внимания и растёт с 306 MiB при одном цикле до 816 MiB при четырёх для контекста 4096 токенов. Режим last_loop остаётся на 306 MiB при любом числе циклов.
Проверка переопределения и замер скорости
Число циклов и режим кэша читаются при загрузке модели, поэтому каждая настройка требует отдельного процесса сервера. Вспомогательный класс запускает по одному на конфигурацию, ждёт health check и разбирает строки KV-кэша из лога. Разобранное число слоёв сверяется с предсказанием — так видно, что переопределение действительно сработало, а не просто флаг принят. Отдельный сервер стартует вообще без переопределения как отрицательный контроль.
Дальше запускаются семь конфигураций — от одного до четырёх циклов в обоих режимах кэша — и измеренная память KV записывается рядом с предсказанной. Скорость декодирования берётся из собственных таймингов сервера на фиксированном промпте: 192 токена жадно, три прогона, медиана. Единственное, что меняется между строками, — настройка циклов.
Гейты корректности: повторяемость и совпадение с CPU
Сравнивать настройки имеет смысл, только если измерения повторяются, а ускоренная сборка считает ту же модель, что и эталонный путь на CPU. Два чат-запроса с одним и тем же seed при температуре сэмплирования из карточки обязаны вернуть идентичные сообщения — иначе сравнение по вопросам смешает поведение модели с шумом сэмплирования. Затем поднимается сервер только на CPU, без выгруженных слоёв, и наиболее вероятный следующий токен на пяти промптах сравнивается с GPU- или Metal-сервером: поддержка Dory не прошла ревью вышестоящего проекта на всех бэкендах.
Что меняет лишний цикл: распределения следующего токена
На 50 вопросах GSM8K сравниваются распределения следующего токена. Отдельно проверяется, не меняет ли предсказания более дешёвый кэш last_loop по сравнению с per_loop, в том числе когда промпт подаётся кусками по 16 токенов, а не одним микро-батчем на 512. Затем идут пары «1 цикл против 2», «2 против 4» и «1 против 4»: так видно, растёт ли расхождение с каждым добавленным проходом.
Завершает разбор парная оценка на GSM8K: как вместе с числом циклов двигаются точность, длина ответа и время на решённый вопрос. Для администратора, который выбирает режим для локального инференса, это и есть главный компромисс — качество рассуждения против памяти и задержки. Если вы уже гоняете небольшие модели на своём железе, пригодятся и другие материалы: про малую модель Nemotron 3.5 Bolt E4B для локальных голосовых агентов и про то, как NVIDIA NeMo превращает сбои ИИ-агента в тесты. А если задача — уложиться в бюджет вычислений, посмотрите на TokenEconomizer от Microsoft: он снижает расходы кодинг-агентов до 30,9%.
Частые вопросы
Что такое Dory 1.0 4B?
Небольшая рассуждающая модель NVIDIA Nemotron Labs, у которой средние слои образуют общий блок, применяемый к каждому токену несколько раз. Дополнительные проходы увеличивают вычисления, но не число параметров.
Почему не получается загрузить модель в обычный llama.cpp?
Поддержка Dory остаётся открытым черновиком pull request, поэтому выпущенные бинарники модель не загружают. Нужна конкретная ветка с коммитом, закреплённым в карточке модели.
Почему команда из карточки модели падает при скачивании?
Карточка называет файл dory-q5_k_m.gguf, а в репозитории он лежит под именем NVIDIA-Nemotron-Labs-Dory1.0-4B-Q5_K_M.gguf. Копировать команду дословно нельзя.
Какой режим KV-кэша экономичнее?
last_loop: он держит кэш на размере одного цикла при любом их числе, тогда как per_loop растёт с каждым добавленным проходом.
Сколько памяти занимает KV-кэш при четырёх циклах?
При контексте 4096 токенов предсказано 816 MiB в режиме per_loop против 306 MiB в last_loop.
Что будет, если запустить сервер без переопределения циклов?
Модель пойдёт на встроенном значении 4 — самом дорогом режиме, хотя карточка рекомендует 2. Переопределение задаётся через --override-kv.
Зачем сравнивать GPU-сборку с CPU?
Чтобы убедиться, что ускоренный бэкенд считает ту же модель: поддержка Dory не прошла ревью вышестоящего проекта на всех бэкендах, и расхождение исказило бы все замеры.
Как проверяется воспроизводимость замеров?
Два чат-запроса с одинаковым seed при температуре из карточки должны вернуть идентичные сообщения, а скорость декодирования берётся как медиана трёх прогонов.
Источник: marktechpost.com