Универсальные языковые модели умеют решать множество задач, но за эту универсальность приходится платить: они большие, тяжёлые и содержат множество переплетённых способностей сразу. Мне хотелось научиться брать уже обученную LLM и компилировать её под конкретную работу, получая компактного специалиста, который можно запускать на более дешевом оборудовании.
При этом одна универсальная модель могла бы служить основой для целого семейства таких специалистов с автоматическим выбором подходящего под каждый запрос.
Важно уточнить: под «компиляцией» здесь я понимаю не экспорт весов и не равномерное уменьшение разрядности. Обычное сжатие отвечает на вопрос «как записать те же веса меньшим числом битов?», а задачная компиляция — на другой: «какое вычисление действительно нужно для выбранного класса задач?»
Системная задумка была похожа на Mixture-of-Experts (MoE), но с одним ключевым отличием: экспертами должны были становиться не отдельные блоки внутри одной большой сети, а самостоятельные модели.
┌─ специалист по коду запрос ──► маршрутизатор ──────────┼─ специалист по математике ├─ специалист по документам └─ универсальная модель, если уверенности нет
Технология маршрутизатора у меня уже была — сейчас можно использовать по API Allaigate Routing API. Он определяет тип и сложность запроса, оценивает уверенность выбора, а владелец системы сам решает, с какой моделью (локальной или облачной) его сопоставить, можно использовать с моей разработкой Cortiq-gateway и плагин для DeepSeek Harness (напишу отдельную статью) . По сути, это маршрутизация на уровне запросов — своего рода MoE, работающий над целым парком LLM.
Не хватало главного компонента: способа получать таких экспертов-специалистов непосредственно из уже обученной модели.
Первую проверочную задачу я не придумывал сам — попросил предложить её на форуме. Мне предложили решать дифференциальные уравнения первого и второго порядка. Это оказался хороший тест: недостаточно просто сделать модель меньше — специалист должен был существенно лучше решать именно тот домен, ради которого его выделили.
После эксперимента (Qwen3.5 0.6B) активными осталось около 20% исходных весов и был приятный сюрприз, что качество на этой задаче по использованной тогда метрике выросло в несколько раз, проще объяснить так, представьте зал разных специалистов и мы оставляем специалистов которые отвечают только за конкретную задачу, а остальные ненужные “шумные” специалисты уходят, точность возрастает.
Дальше передо мной встал вопрос: как хранить и исполнять такую сеть, не превращая обнулённые веса обратно в гигабайты и не разрушая согласованную структуру связей между тензорами? Ответом на него стали CMF (Cortiq Model Format) и исполнитель Cortiq. Решится разрабатывать свой формат было довольно тяжело я представлял сколько потребуется работы и решил раз делать то подойти основательно, вначале продумал архитектуру и гибкость формата + у меня уже было несколько своих открытий и патентов в сфере ИИ которые я решил внедрить.
Это первая подробная открытая публикация, в которой CMF представлен как самостоятельный формат. Спецификацию, конвертер, исполнитель, вычислительные ядра и мобильный путь я разрабатывал в одиночку от первого прототипа до текущего состояния. Теперь проект открыт — и далее в статье я чётко разделяю то, что уже реализовано, протестировано и измерено, от того, что ещё требует независимой проверки.
Четыре уровня одной системы
Чтобы не путать разные компоненты проекта между собой, удобнее разделить их по назначению:
Компонент | Назначение |
|---|---|
DTG-MA и | Выделяют задачную маску из общей модели и обучающего корпуса, восстанавливают качество специалиста, при необходимости физически удаляют неактивные структуры. |
CMF | Хранит получившуюся структуру: формы тензоров, маски, навыки ( |
Cortiq | Загружает и исполняет CMF-файлы на CPU/GPU, поднимает локальный OpenAI-совместимый API и умеет распределять слои между устройствами. |
CMF появился на стыке первых двух уровней. Метод задачной компиляции уже создавал структуру, которую обычный плотный весовой файл описывал неполно — новому артефакту потребовались собственные чёткие правила хранения и исполнения.
Как корпус задачи превращается в специалиста
Текущая рабочая цепочка задачной компиляции выглядит следующим образом:
обученная LLM + корпус задачи ↓ L1-регуляризованная маска активных FFN-координат ↓ поиск оптимума по ошибке на отложенной выборке ↓ FCD: тонкая подстройка последних слоёв по точному учителю ↓ defrag (физическое удаление неактивных нейронов) ↓ самостоятельный CMF-специалист
Маска применяется согласованно: одни и те же активные промежуточные координаты отмечаются одновременно для gate_proj и up_proj, а также для соответствующих столбцов down_proj в FFN. При усилении L1-регуляризации часть нейронов постепенно выключается. Сначала это нередко улучшает результат — сеть избавляется от нерелевантных для домена связей, — а затем качество начинает резко падать.
Ключевое правило здесь — сохранять точку минимума качества на отложенной выборке, а не ту маску, у которой получился максимально красивый процент удаления.
Эффективность подхода сильно зависит от домена: там, где базовая модель изначально слаба в какой-то предметной области, задачная компиляция обычно помогает. (скоро напишу подробную статью как создавать свои специализированные модели на примере прогнозирования финансового рынка)
Почему обычного сжатия оказалось недостаточно
Первую версию специалистов я пробовал сохранять в формате GGUF. Выбор был логичным: GGUF — зрелый, самоописывающийся формат, отлично приспособлен для mmap, хранит токенизатор, метаданные и опирается на огромную экосистему llama.cpp.
Проблема проявилась сразу после задачной компиляции. Большая часть весов становилась нулевой, но плотный тензор при этом не уменьшался в размере — нулевое значение в файле занимает ровно столько же байтов, сколько и ненулевое той же разрядности.
Очевидное решение — упаковать только ненулевые значения плотнее. На практике оно упёрлось в согласованность осей тензоров. В классическом FFN промежуточная координата жёстко связана сразу между тремя проекциями: она соответствует строкам gate_proj и up_proj и одновременно — столбцу down_proj. В MoE к этой связанности добавляются строки маршрутизатора, номера экспертов и правила выбора активных экспертов. Если удалить или переставить координату лишь в одной из этих матриц, вычисление всей сети становится некорректным.
Я пробовал разные подходы: упаковку разреженных значений, согласованные перестановки строк и столбцов, выравнивание масок между слоями, удаление редко используемых MoE-экспертов. Файл действительно становился меньше, но стабильное сохранение качества оказывалось ненадёжным. Причина крылась не в конкретном контейнере — задачная специализация требует собственной семантики исполнения.
Можно было бы добавить кастомные поля в GGUF, но одно лишь новое поле не научило бы существующий исполнитель читать задачные маски, подменять тензоры навыками, проверять совместимость с базовой моделью или физически пропускать удалённые структуры. В любом случае пришлось бы писать новый загрузчик и, по сути, новую логику исполнения. Поэтому было честнее не пытаться «подогнать» GGUF под эту семантику, а чётко зафиксировать все необходимые правила в отдельном формате.
Два способа сохранить специалиста
В CMF реализовано два принципиально разных способа хранения результата специализации. Они решают разные практические сценарии: автономный одиночный специалист или семейство нескольких специалистов на общей основе.
1. Автономный специалист — физическое удаление
Если модель предназначена только для одной задачи, ненужные FFN-нейроны можно удалить физически. На диске вместо исходных больших тензоров хранятся меньшие по форме: количество строк у gate_proj и up_proj становится равным количеству столбцов у down_proj. При этом для разных слоёв эта величина может быть разной.
Речь идёт не об обнулении или усечении байтов. Алгоритм работает так: исходный тензор деквантуется, из него согласованно выбираются только «живые» (активные) координаты, затем полученная меньшая матрица квантуется заново, с пересчётом масштаба и получением нового хеша. Для MoE аналогичным образом удаляются целые неиспользуемые эксперты вместе с соответствующими строками маршрутизатора. После этой операции ненужные веса действительно отсутствуют — и в файле, и в вычислении.
2. Несколько специалистов — общая основа и навыки (Skills)
Если одна базовая модель должна обслуживать сразу несколько разных задач, физически вырезать основу нельзя: вес, бесполезный для одного специалиста, может быть критически важным для другого.
В этом случае CMF использует задачные маски и замещающие тензоры — «навыки» (skills).
Маска упакована побитово и отмечает, какие именно FFN-нейроны, головы внимания, слои или MoE-эксперты должны считаться активными для данной задачи.
Общие веса при этом ни разу не дублируются. Маска просто определяет, какую часть единой основы использовать при выполнении конкретного навыка.
Навык (
skill.<имя>.<тензор>) хранит только те тензоры полной логической формы, которые отличаются от общей основы. Он не является ни второй полной моделью, ни классическим LoRA-адаптером (хотя концептуально родствен ему). Во время прямого прохода исполнитель для каждого тензора выбирает источник: либо исходная основа, либо тензор соответствующего навыка.
В результате стоимость хранения выглядит так:
размер общей основы + сумма реально изменённых тензоров всех навыков, а не (количество навыков) × (размер полной модели).
Неактивный навык не загружается в оперативную память и не потребляет вычислительных ресурсов. Каждый отдельный файл навыка жёстко привязывается к своей основе через поле base_dir_hash — это исключает ошибку наложения навыка на похожую, но квантованную иначе или иным образом модифицированную модель.
В CMF также может храниться небольшой дескриптор подпространства скрытых состояний (latent space descriptor). Опираясь на ошибку реконструкции скрытого состояния, исполнитель способен автоматически выбрать наиболее подходящий навык, смешать два лучших или переключаться между навыками прямо в процессе генерации. Здесь появляется второй уровень маршрутизации: Allaigate выбирает модель из парка, а CMF — подходящую способность внутри выбранной общей основы.
У этой аналогии с классическим компилятором есть важное принципиальное отличие. Компилятор программы обязан полностью сохранять её семантику. Задачная компиляция модели же гарантирует сохранение (и, как правило, улучшение) качества только на заданном целевом распределении задач. Именно поэтому спецификация CMF не позволяет указывать «качество 1.0» по умолчанию — метрики качества записываются только вместе с конкретным измерением, отложенной выборкой и её контрольной суммой. Если измерение не проводилось — соответствующее поле остаётся null.
CMF на диске: предсказуемая структура и жёсткие проверки
Спецификация CMF v2 построена по принципу «минимум магии, максимум проверяемости». Файл начинается с фиксированного 128-байтового конверта, в котором хранится адресация всех секций. Благодаря этому читатель никогда не вычисляет смещения вручную — структура полностью самодостаточна.
Общая компоновка файла выглядит так:
128 байт: конверт и таблица секций ↓ JSON: архитектура, происхождение, чат-шаблон, реестр навыков ↓ двоичный каталог тензоров: имя, тип, форма, смещение, длина, hash64 ↓ блок весов, выровненный для эффективного mmap ↓ необязательные секции: маски, токенизатор, индекс и др.
Блок весов выровнен по границе 4096 байт, а каждый тензор — по 64-байтовой границе. Это позволяет отображать CMF-файл в память целиком через mmap: страницы, к которым ещё не было обращений, физически не загружаются в резидентную оперативную память.
Каждый тензор имеет 64-битный хеш содержимого. Отдельно защищены каталог, заголовок и необязательные секции. Команда cortiq verify проверяет корректность всех границ, выравниваний и полную цепочку целостности. Такая проверка позволяет надёжно обнаружить повреждение файла.
При этом контроль целостности отделён от подтверждения авторства. Для последнего в формате предусмотрена отдельная подпись Ed25519 поверх SHA-256 всего файла — эти два механизма намеренно не смешиваются.
CMF также может хранить токенизатор и чат-шаблон прямо в том же файле. Исполнитель применяет шаблон диалога, записанный в модели, и ориентируется на записанные в ней идентификаторы конца генерации (EOS), а не полагается на жёстко зашитые предположения о семействе моделей.
Разрядность — не глобальная, а точечная
Удобный подход «сделать всю модель четырёхбитной» работает не всегда. Разные тензоры имеют разную чувствительность к квантованию: небольшие управляющие матрицы, нормализации и ключевые таблицы обычно чувствительнее крупных плотных матриц. В CMF тип квантования задаётся для каждого тензора отдельно, а в MoE — ещё и для каждого эксперта.
Ниже перечислены кодеки, которые появились в ответ на конкретные практические проблемы:
Кодек | Что хранит | Назначение |
|---|---|---|
q8_2f | Целочисленные значения и два масштаба: по строкам выходной матрицы и по входным каналам ( | Позволяет точнее обрабатывать выбросы, не заставляя один строковый масштаб покрывать всю строку. |
q4tp | 4-битные значения и 5-битная ступень геометрической шкалы масштаба | Даёт ~4,17 бита на вес вместо ~4,50 у |
q2tp | 2-битная сетка из 4 уровней с предсказанной шкалой; нулевая ступень кодирует точный ноль | Полезен для компактных профилей с избыточными экспертами MoE: удалённые группы не возвращаются в виде малого шума. |
vbit_ro | От 3 до 8 бит на строку с таблицей смещений по строкам | Позволяет распределять битовый бюджет неравномерно (больше бит — важным строкам), сохраняя доступ к любой строке за O(1). |
q1 / q1t | Бинарное ( | Предназначены для моделей, специально подготовленных под экстремально низкую разрядность. Не стоит рассматривать агрессивное 1-битное квантование как безусловно эквивалентное исходным весам без измерений. |
Гибкость распределения разрядности не отменяет необходимости в замерах качества. Два файла одинакового размера могут распределять ошибку квантования совершенно по-разному. Формат даёт строительные блоки — конкретный профиль квантования всегда нужно выбирать на основе проверки качества на релевантных данных.
O(1)-внимание: постоянный объём KV-кеша
У классического точного внимания объём KV-кэша линейно растёт с длиной контекста: каждый новый токен добавляет ключи и значения, которые потребуются для всех последующих шагов генерации. На длинных диалогах часто оказывается, что модель ещё помещается в память, а история диалога — уже нет.
В Cortiq режим --o1 заменяет выбранные слои точного внимания потоковым (streaming) оператором внимания с фиксированным бюджетом состояния. Он хранит несколько точных опорных ключей, точное недавнее окно токенов и сжатое представление более старой части контекста. После достижения заданного бюджета объём состояния перестаёт расти с увеличением числа обработанных токенов.
Здесь важно чётко обозначить ограничение: это приближённое внимание. При включении --o1 веса модели не меняются и переобучение не требуется, но поведение модели (а значит, и качество генерации) может отличаться от точного внимания. Поэтому этот режим рекомендуется включать только после измерений на конкретной модели и конкретных задачах. При необходимости отдельный этап FCD позволяет тонко подстроить преобразованные слои и оставляет результат в CMF только после проверки генерации.
CMF хранит параметры и подсказки для этого режима, но само O(1)-поведение реализуется исключительно в Cortiq. Другой читатель CMF-файла не получит потокового внимания автоматически — это часть исполнительной семантики, а не декларация в формате.
Один контейнер — не только для текстовых LLM
Каталог CMF не ограничивается декодерами текстовых моделей. В одном адресном пространстве можно хранить сразу несколько компонентов мультимодийного конвейера: текстовый кодировщик, диффузионный трансформер, VAE, звуковой декодер, конфигурации, токенизатор и другие модули.
Например, опубликованная модель MiniMax-H3 Turbo объединяет в одном .cmf-файле диффузионный трансформер, кодировщик запросов Qwen3-VL, видео-VAE и звуковые компоненты. Команда cortiq animate по одному текстовому (или мультимодальному) запросу генерирует видео с синхронной стереодорожкой. Аналогично для Lumina реализована генерация изображений, а для LTX-2.5 — генерация видео.
Именно в мультимодальных цепочках единый контейнер дал особенно заметный практический выигрыш. Рабочая сборка MiniMax-H3 в виде разрозненных исходных файлов, промежуточных артефактов и утилит занимала у меня около 200 ГБ. Готовая рабочая цепочка в CMF распространяется одним файлом: 14,48 ГБ в компактном варианте или 25,70 ГБ — с полным кодировщиком запроса. Здесь речь идёт не о «сжатии модели в 14 раз», а о замене целой россыпи необходимых для запуска файлов одним проверяемым, самодостаточным артефактом.
При этом важно понимать границу: CMF хранит компоненты и их точное описание архитектуры, но не магическим образом реализует любую архитектуру. Cortiq должен уметь построить соответствующий вычислительный граф для этих компонентов. Разделение между форматом (хранение и описание) и исполнителем (вычисление) здесь строго соблюдается.
Телефон как самостоятельный вычислительный узел
Cortiq: Local AI Models уже доступен в Google Play для Android. Приложение запускает готовые CMF-модели прямо на устройстве без облачной учётной записи, умеет конвертировать модели из SafeTensors в CMF, а также открывает загруженную модель по локальной сети через OpenAI-совместимый API. Таким образом, телефон можно использовать как локальный LLM-сервер для ноутбука, планшета или другого клиента.
Версия Cortiq для устройств Apple (iOS) в настоящее время проходит процесс проверки в App Store.
CMF, GGUF и SafeTensors — разные инструменты для разных задач
Неправильно сравнивать эти три формата по единой «лучшести» — они оптимизированы под разные рабочие процессы.
Критерий | CMF | GGUF | SafeTensors |
|---|---|---|---|
Основное назначение | Исполняемая модель или семейство специалистов на общей основе. | Исполняемая локальная модель для инференса. | Контейнер для безопасного хранения и передачи тензоров. |
Токенизатор и чат-шаблон | Могут храниться прямо в том же файле. | Чаще всего хранятся в том же файле. | Обычно лежат во внешних файлах репозитория. |
Квантование | Гибкое: тип задаётся для каждого тензора (и для каждого MoE-эксперта). | Зрелый, фиксированный набор GGML-квантований. | Не определяет схему квантования — она задаётся внешним кодом. |
Задачные маски и несколько навыков на общей основе | Встроены в спецификацию (первоклассная сущность). | Обычно реализуются через внешние адаптеры. | Вне зоны ответственности формата. |
Проверка целостности | Обязательные хеши каждого тензора ( | Не является обязательным требованием формата. | Безопасный парсинг, но без обязательного хеша каждого тензора. |
Экосистема | Молодая, пока в основном сосредоточена вокруг Cortiq. | Крупнейшая экосистема для локального запуска LLM. | Стандарт распространения весов в Hugging Face. |
Рекомендация простая: если нужен максимальный охват совместимости с существующими локальными моделями и железом — сегодня логичнее выбрать GGUF. Если вы передаёте «сырые» тензоры между библиотеками — SafeTensors. CMF создан для другого класса сценариев: готовый специалист (или семейство специалистов), полученный в результате задачной компиляции, несущий несколько способностей на общей основе, с поэтапной проверяемостью и способностью исполняться без тяжёлой ML-библиотеки.
Cortiq поддерживает инференс на CPU, использует Metal на Apple Silicon и wgpu (Vulkan/DirectX 12) на GPU там, где эта платформа поддержана. На проверенных архитектурах и моделях качество генерации при сопоставимых настройках удалось довести близко к llama.cpp. Это утверждение относится именно к проверенным конфигурациям, а не к обещанию полного совпадения логитов, производительности или поддержки любой новой архитектуры.
Проект уже можно опробовать на реальных моделях
Ниже приведены несколько опубликованных CMF-моделей на все готовые модели на Hugging Face с 18 августа Hugging Face полностью поддерживает формат CMF.
Самые популярные модели | downloads | Пример опубликованного CMF |
|---|---|---|
6 539 | 14,48 ГБ (компактный Q4TP), 25,70 ГБ (с полным кодировщиком запросов) | |
962 | 14,27 ГБ (Q4TP), 27,38 ГБ (Q8_2F) | |
593 | 15,64 ГБ (Granite-4.2 30B, Q4TP) |
Для Hugging Face также добавлена интеграция с Cortiq: в карточках моделей проставлен тег library_name: cortiq, благодаря чему CMF-модели корректно отображаются на Hub как модели библиотеки Cortiq, а не как неизвестные бинарные файлы. В открытом доступе уже представлены текстовые и мультимодийные модели, плотные архитектуры и MoE, а также разные профили точности — это реальные CMF-файлы, которые можно скачать и запустить прямо сейчас.
Быстрый старт: попробуйте за 1 минуту
Конвертер Cortiq написан на Rust и не требует Python или PyTorch. Он может взять модель либо из локальной папки в формате Hugging Face, либо по идентификатору репозитория на Hub.
Установите CLI:
cargo install cortiq-cli
Конвертируйте небольшую модель в CMF с квантованием q4tp:
cortiq convert \ --model Qwen/Qwen3-0.6B \ --quant q4tp \ --output qwen.cmf
Проверьте целостность файла:
cortiq verify qwen.cmf
Запустите генерацию в терминале:
cortiq run qwen.cmf \ --prompt "Объясни, чем mmap отличается от обычного чтения файла" \ --greedy --no-think
Тот же файл можно поднять как локальный OpenAI-совместимый сервер:
cortiq serve qwen.cmf --port 8080
Если у вас уже есть модель в формате GGUF, её можно импортировать в CMF напрямую, без промежуточных шагов в Python:
cortiq import-gguf model.gguf --quant q4tp --output model.cmf cortiq verify model.cmf
Спецификация CMF v2, независимый Python-читатель и весь исходный код Cortiq опубликованы в репозитории проекта под лицензией Apache 2.0.
Почему я публикую проект сейчас
До этого момента я сознательно доводил всю цепочку в одиночку: формат, конвертер, исполнение на CPU и GPU, механику навыков на общей основе, потоковое внимание, мультимедиа и мобильный запуск. Такой подход позволял оперативно менять сразу все уровни системы, пока ключевые архитектурные решения ещё не устоялись.
Сейчас одиночная разработка стала скорее ограничением, чем преимуществом. Формату нужны независимые читатели, проверка на разном железе, а также тестирование на моделях и задачах, которые выбрал не автор. Особенно ценным будет вклад тех, кто готов:
Добавить и протестировать поддержку новых семейств архитектур LLM/VLM;
Улучшить вычислительные ядра для Metal, Vulkan и DirectX 12;
Провести независимое сравнение качества и производительности с
llama.cppи исходными SafeTensors;Развивать методы задачной компиляции: маски, навыки, работу с MoE-экспертами;
Проверить и доработать сетевое распределение модели на разных телефонах и компьютерах.
Методы, лежащие в основе проекта, описаны в четырёх патентных заявках США: № 19/452,440 (маршрутизация запросов), № 19/452,464 (управляемая задачей компрессия), № 19/731,402 (несколько специалистов на общей основе), № 19/738,763 (потоковое внимание с постоянным объёмом памяти). Точная привязка этих заявок к коду проекта приведена в PATENTS.md.
Код распространяется по лицензии Apache 2.0, а раздел 3 этой лицензии предоставляет пользователям патентную лицензию на те патентные притязания, которые неизбежно реализуются распространяемым исходным кодом.
Следующий этап — независимые эксперименты на других моделях, доменах и аппаратных платформах.
Если идея задачной компиляции LLM вам близка — CMF уже можно брать, запускать, тестировать, расширять и использовать как основу для собственных решений.
Поддержка проекта
Для тестирования и развития формата требуется аренда дорогостоящих GPU серверов вы можете поддержать донатом.

