Обученная модель считает медленно и занимает много памяти. , компиляция и сокращают и то и другое, обычно почти без потери качества.
Ключевые тезисы
- и квантизация до 8 бит дают кратное ускорение при падении метрики в доли процента.
- Компиляция (, ONNX Runtime, torch.compile) сливает операции и убирает накладные расходы интерпретатора.
- Узкое место чаще в пропускной способности памяти, а не в арифметике — отсюда важность арифметической интенсивности.
Какую задачу решает
Обучение занимает часы один раз, работает круглосуточно. Поэтому экономика сервиса определяется не тем, сколько стоило обучить модель, а тем, сколько стоит каждый её вызов — и вот здесь оптимизация окупается немедленно.
Инструментов три, и они складываются. Уменьшить точность чисел — вдвое-вчетверо меньше памяти и быстрее арифметика. Скомпилировать — слить операции, убрать накладные расходы, подобрать реализации под конкретное железо. Собрать запросы в — использовать параллелизм ускорителя, который на одиночном запросе простаивает.
Понимать при этом надо, во что вы упираетесь. Большинство слоёв в ограничены не арифметикой, а скоростью чтения весов из памяти: процессор ждёт данные. Оптимизация арифметики такому слою не поможет, а уменьшение размера весов — поможет вдвое.
Слой перемножает весов на одного запроса. Весов сто миллионов, то есть 400 МБ в float32; операций — те же сто миллионов. получается меньше единицы: на каждый прочитанный байт приходится доля операции. Карта, умеющая триллионы операций в секунду, простаивает — она ждёт, пока веса приедут из памяти. Значит, ускорять надо не счёт, а чтение: квантизация до 8 бит уменьшает объём вчетверо и даёт почти четырёхкратное ускорение. работает по той же причине с другой стороны: веса читаются один раз на весь , и интенсивность растёт пропорционально его размеру.
Подробный разбор
| Тип | Бит | Память относительно fp32 | Типичная потеря качества |
|---|---|---|---|
| float32 | 32 | 1× | — |
| bfloat16 / float16 | 16 | 0,5× | почти нет |
| int8 | 8 | 0,25× | доли процента |
| int4 | 4 | 0,125× | заметная, нужна проверка |
- Посттренировочная квантизация: готовая модель переводится в int8 по калибровочной выборке. Быстро, обычно достаточно.
- Квантизация с обучением: модель дообучается с учётом округления. Дороже, но спасает там, где первая просела.
- Только веса: остаются в fp16. Стандарт для языковых моделей — там узкое место именно веса.
Проверять надо на своих данных, а не на бенчмарке. Падение метрики от распределено неравномерно: чаще всего страдают редкие классы и хвост распределения — ровно то, что в среднем не видно.
У современных ускорителей арифметика на порядок дешевле памяти: карта успевает сделать сотни операций за время, пока читает один байт. Значит, слой с низкой интенсивностью упирается в память, и ускорять в нём счёт бессмысленно.
| Операция | Интенсивность | Во что упирается |
|---|---|---|
| × ( 1) | < 1 | в память |
| × матрица (большой ) | десятки-сотни | в арифметику |
| поэлементные (, сложение) | ≈ 0,1 | в память |
| на длинном контексте | низкая | в память |
Отсюда практический вывод: если упор в память, помогают (меньше байт) и слияние операций (меньше проходов). Если в арифметику — тензорные ядра и смешанная точность.
В обычном режиме каждая операция — отдельный вызов ядра: прочитать тензор из памяти, посчитать, записать обратно. Цепочка « → → » делает три прохода по памяти там, где хватило бы одного.
- Слияние: несколько операций превращаются в одно ядро, промежуточный результат не покидает регистры.
- Постоянное свёртывание: то, что известно заранее, считается один раз при компиляции.
- Подбор реализаций: под конкретное железо и конкретные формы выбирается лучший алгоритм.
| Инструмент | Где применяют |
|---|---|
| torch.compile | PyTorch, минимальные изменения кода |
| Runtime | переносимость между языками и платформами |
| максимум на NVIDIA, ценой привязки | |
| OpenVINO | на CPU Intel |
Компиляция платит за скорость гибкостью. Меняющиеся формы входа вызывают перекомпиляцию, и на переменной длине последовательностей выигрыш может обнулиться — формы приходится группировать по бакетам.
Одиночный запрос читает все веса модели ради одного примера. из тридцати двух читает те же веса один раз на тридцать два примера: пропускная способность растёт почти линейно, пока не упрётся в арифметику.
- Сервер ждёт до N миллисекунд или до заполнения — что наступит раньше.
- отдельного запроса растёт на величину окна ожидания.
- Пропускная способность растёт в разы — то есть стоимость запроса падает в разы.
Бюджет — 100 мс, сама модель считает 20 мс на батче из 32. в 10 мс съедает десятую часть бюджета и при нагрузке в тысячу запросов в секунду успевает набрать почти всегда. Окно в 50 мс наберёт батч гарантированно, но при низкой нагрузке будет добавлять полсотни миллисекунд к каждому запросу впустую. Практическое правило: окно порядка времени счёта одного батча, и обязательно измерить задержку под реальной нагрузкой, а не под равномерной.
- Дистилляция: маленькая модель учится повторять выходы большой. Даёт кратное сокращение, но требует обучения и данных.
- Обрезка (pruning): удаление незначимых весов. Структурная обрезка реально ускоряет, неструктурная чаще только экономит память.
- Каскад: дешёвая модель отвечает на простые случаи, дорогая подключается на сложных. Часто окупается лучше всего.
Порядок действий обычно такой: сначала измерить, во что упираетесь; потом и компиляция — они дешёвые; потом ; и только если этого мало — менять саму модель. Начинать с последнего дорого и часто не нужно.
Где применяется
- Сервис под нагрузкойВтрое меньше при той же пропускной способности.
- на устройствеМодель, не влезавшая в память телефона, влезает после .
- Генерация ограничена памятью — даёт почти линейное ускорение.
- Пакетная обработкаНочной прогон по миллионам записей: важна пропускная способность, а не .
Плюсы, минусы и альтернативы
Плюсы
- Кратное ускорение при падении качества в доли процента.
- Меньше памяти — больше модель влезает на ту же карту.
- Прямая экономия: меньше ускорителей на ту же нагрузку.
Минусы
- иногда бьёт по качеству непредсказуемо — нужна проверка на реальных данных.
- Скомпилированный теряет гибкость: динамические формы поддерживаются хуже.
- увеличивает задержку отдельного запроса ради общей пропускной способности.
- Каждый инструмент привязан к своему железу и версиям — обновление становится рискованным.
Брать, если
- Сервис под нагрузкой, и счёт за ускорители заметен.
- Модель не влезает по памяти или не укладывается в бюджет .
Не брать, если
- Нагрузка мала: сложность эксплуатации не окупится.
- Качество на пределе допустимого — съест остаток.
Чем заменяют
Видеолекции
Записи университетских курсов, где эта тема звучит. Ссылка открывает запись с той секунды, где о ней говорят, — искать по трёхчасовой лекции не нужно.
Всё, что названо в описании, разобрано здесь же или в соседней теме — переходы под каждым определением.
Квантизация
Переход от 32 бит к 16 или 8: вчетверо меньше памяти, кратно быстрее.
Разбор ниже: КвантизацияАрифметическая интенсивность
Сколько операций приходится на байт, прочитанный из памяти. Определяет, во что упираешься.
Разбор ниже: Во что вы упираетесьКомпиляция графа
Слияние операций и подбор реализаций под железо: , ONNX Runtime, torch.compile.
Разбор ниже: Компиляция графаДинамический батчинг
Сервер копит запросы миллисекунды и считает пачкой — пропускная способность растёт кратно.
Разбор ниже: Динамический батчингДистилляция
Маленькая модель учится повторять большую. Радикальнее и дороже в подготовке.
Разбор ниже: Когда оптимизаций малоПрофильная компетенция для роли: Продуктовый инженер.
Усиливает профиль: MLOps-инженер, LLM-инженер.
Спрашивают на собеседовании: Т-Банк — ML-инженерия и продакшен, Dodo Brands — Зрение на кухне, Ростелеком — Видеоаналитика, Группа Астра — Ассистенты для администраторов, YADRO — Эффективность моделей, Selectel — Платформа инференса, Cloud.ru — Модели как сервис.
Тема встречается в тесте по направлениям — ошибки приведут обратно на эту страницу.
Глава «MLOps» последний раз правилась . Нашли ошибку — напишите.