От криптоанализа к AI-forensics:

от автора

как мы взяли gpt-oss-20b-TurboQuant-MLX-8bit, изменили логику наблюдения и научились точечно исправлять квантованные слои

Введение

В прошлой статье я показывал, что подпись Schnorr / MuSig2 можно рассматривать не как «чёрный ящик», а как систему наблюдаемых affine-структур: через строгий BIP340 membership bridge, семейства скрытых нонсов, compression/connectivity-метрики и protocol-valid линеаризацию MuSig2 partial signatures Хабр. Это меняет саму оптику: вместо «верим протоколу на слово» мы начинаем разбирать его на проверяемые математические блоки.

Следующий шаг оказался неожиданным, но логичным.

Мы перенесли ту же самую методологию в AI.

Не в смысле «взяли LLM и начали её тюнинговать». А в смысле: взяли квантованную MLX-модель и стали исследовать её внутреннюю математику так же, как раньше исследовали подписи.

Объектом эксперимента стал локальный дистрибутив:

gpt-oss-20b-TurboQuant-MLX-8bit

Мы не меняли архитектуру модели, не переписывали attention и не занимались классическим fine-tuning. Мы сделали другое:

  • научились читать .safetensors как точную структуру;

  • построили детерминированный calibration cache;

  • начали снимать реальные BF16-активации с конкретных слоёв;

  • свели локальную коррекцию весов к ограниченной задаче целочисленной оптимизации;

  • сделали безопасную запись patch обратно в модель;

  • и добавили smoke-check, который проверяет, совпадает ли offline-математика с реальным runtime MLX.

Главный результат этой статьи звучит так:

мы не «улучшили модель магией», а построили exact AI-forensics pipeline, в котором квантованный слой перестаёт быть чёрным ящиком и становится наблюдаемым, проверяемым и локально корректируемым объектом.


Почему переход от Schnorr к AI вообще естественен

В криптографии всё началось с очень простой идеи: если объект допускает строгую нормализацию, то его можно не только использовать, но и исследовать как геометрию скрытых параметров.

Для подписи Schnorr базовое соотношение имеет вид:

s = k + e d \pmod n

где:

  • d — приватный ключ,

  • k — nonce,

  • e — challenge,

  • s — скаляр подписи.

Это уже не «магия подписи», а линейная структура.

В AI мы сделали тот же самый ход.

Мы перестали смотреть на модель как на функцию «промпт \to текст» и спустились ниже — к тому месту, где живёт её внутренняя линейная алгебра:

  • квантованные коды,

  • scale/bias,

  • входные активации,

  • эталонные выходы линейного слоя.

То есть вопрос поменялся.

Не:

«Модель отвечает хорошо или плохо?»

А:

«Можно ли представить конкретный квантованный слой как точный affine-объект, наблюдать его поведение и локально исправлять без разрушения всей модели?»

Вот это и есть AI-forensics.


Что именно мы взяли

Базой стал MLX-дистрибутив:

gpt-oss-20b-TurboQuant-MLX-8bit

Нас интересовали два конкретных слоя первого блока:

model.layers.0.self_attn.q_proj.weightmodel.layers.0.mlp.router.weight

Почему именно они?

Потому что это хороший тест на два разных сценария:

  1. слой, который может оказаться слишком «жёстким» для осмысленного patch без внешнего эталона;

  2. слой, который, наоборот, допускает локальную корректировку.

Именно на этом контрасте и проявляется ценность метода.


Что именно мы изменили

Важно сразу зафиксировать: мы не меняли архитектуру модели.

Мы изменили не «мозг модели», а способ работы с ним.

Было

  • .safetensors как opaque-контейнер;

  • квантованные веса как «что-то внутри рантайма»;

  • оценка модели только по финальному текстовому выходу.

Стало

  • точный доступ к сырому весовому представлению;

  • выделение кодов квантования, scale и bias;

  • runtime-захват входа и выхода конкретного линейного слоя;

  • локальная integer-оптимизация кодов;

  • безопасная обратная запись патча;

  • обязательная проверка, что математика offline не расходится с реальным исполнением.

Проще говоря:

мы превратили квантованный слой из «непрозрачной детали модели» в объект инженерной диагностики.


Шаг 1. Exact bridge к .safetensors

Первое, что пришлось сделать, — написать мост к MLX-квантованию.

Он умеет:

  • находить нужный тензор в модели;

  • читать его raw-представление;

  • извлекать квантованные коды;

  • читать BF16-масштабы и смещения;

  • при необходимости записывать изменённые коды обратно.

Это важный момент.

Пока веса модели воспринимаются как «готовый blob», никакой точной работы с ними не получится. Нужно сначала развернуть этот blob в нормальную математическую структуру.


Шаг 2. Детерминированный calibration cache

Следующий шаг — calibration cache.

Мы заранее собрали стабильный набор примеров:

Параметр

Значение

Всего примеров

768

Split general

512

Split logic

256

Размер шарда

128

Политика захвата

last-token

Тип активаций

BF16

Зачем это нужно?

Чтобы каждый следующий эксперимент был воспроизводимым. Не «кажется, стало лучше», а:

  • вот тот же набор,

  • вот тот же слой,

  • вот тот же вход,

  • вот тот же эталонный выход,

  • вот та же метрика ошибки.

Без этого всё быстро превращается в storytelling.


Шаг 3. Захват реальных активаций

После этого мы добавили runtime-capture для конкретных линейных слоёв.

Для каждого выбранного примера сохранялись:

  • pre_linear_input

  • linear_output_reference

Причём именно для последнего токена.

Это очень важно.

Мы не симулируем слой отдельно от модели, а снимаем его с живого MLX-runtime. То есть дальше работаем уже не с абстрактной линейной алгеброй, а с данными, которые модель реально порождает сама.


Математическая модель квантованного слоя

Теперь можно записать слой в виде нормальной формулы.

Пусть строка весов разбита на блоки длины 64. Для строки j и блока b обозначим:

  • c_{j,b} — вектор квантованных кодов длины 64;

  • s_{j,b} — scale;

  • \beta_{j,b} — bias.

Тогда деквантованный блок весов задаётся как

w_{j,b} = s_{j,b} , c_{j,b} + \beta_{j,b}\mathbf{1}

где \mathbf{1} — вектор из единиц длины 64.

Если входной вектор для примера t разбит по тем же блокам:

x_t = (x_{t,1}, x_{t,2}, \dots, x_{t,B}),

то выход по координате j равен

y_{t,j} = \sum_{b=1}^{B} \langle x_{t,b}, w_{j,b} \rangle

или, после подстановки деквантования,

y_{t,j}=\sum_{b=1}^{B}\left(s_{j,b}\langle x_{t,b}, c_{j,b}\rangle+\beta_{j,b}\langle x_{t,b}, \mathbf{1}\rangle\right)

Вот здесь и появляется главный мост.

Квантованный слой перестаёт быть «набором непонятных байтов» и становится affine-системой над дискретными кодами.


Что именно мы оптимизируем

Для выбранного блока мы ищем новый кодовый вектор \hat c, который лучше воспроизводит reference-output, но не уезжает слишком далеко от исходного.

Оптимизационная задача имеет вид:

\hat c=\arg\min_{c}\left[\sum_{t=1}^{T}\bigl(\langle x_{t,b}, s c + \beta \mathbf{1}\rangle - y^{ref}_{t,j}\bigr)^2+\lambda |c - c^{(0)}|_2^2\right]

при ограничениях

0 \le c_i \le 255

и

|c_i - c_i^{(0)}| \le \Delta

Здесь:

  • c^{(0)} — исходные коды блока;

  • \lambda — коэффициент регуляризации;

  • \Delta — максимально допустимое изменение одного кода.

В наших экспериментах для router-слоя использовались:

Параметр

Значение

Размер блока

64

Ridge lambda

16

Max code delta abs

8

То есть patch изначально делался консервативным:

  • не переписывать слой целиком;

  • не менять десятки тысяч параметров без контроля;

  • не уходить далеко от исходной структуры;

  • чинить локально и в ограниченном окне.


Важная теорема: почему без внешнего эталона честного patch не будет

Это ключевая граница метода.

Теорема

Если в качестве цели мы используем текущий же квантованный слой, то любой exact-поиск патча даёт тривиальный оптимум: ничего не менять.

Идея

Пусть исходное состояние слоя задаётся тройкой:

(C_0, S_0, B_0)

и пусть функционал ошибки имеет вид

E(C,S,B) = \Phi\big((C,S,B), (C_0,S_0,B_0)\big)

где:

  1. E(C,S,B) \ge 0 для любой допустимой конфигурации;

  2. E(C_0,S_0,B_0) = 0;

  3. ноль достигается только в точке (C_0,S_0,B_0).

Тогда минимум уже достигнут в исходном состоянии, потому что меньше нуля уйти нельзя.

Следовательно, если мы пытаемся «улучшить» слой, используя как цель сам этот слой, то лучшая стратегия — ничего не менять.

Вывод

Чтобы non-identity patch был математически осмысленным, нужен внешний эталон:

  • реальные runtime-активации;

  • reference-output;

  • отдельный holdout.

Именно поэтому в нашей системе pipeline строится вокруг capture и holdout, а не вокруг самосравнения модели с самой собой.


Что показал аудит q_proj

Первым тестовым кандидатом был слой

model.layers.0.self_attn.q_proj.weight

Мы прогнали по нему exact-аудит.

Структурная картина

Метрика

Значение

Total rows

4096

Decoded width

2880

Blocks per row

45

Total blocks

184320

Unique rows

4096

Repeated rows

0

Unique blocks

184320

Repeated blocks

0

Exact equal adjacent block pairs

0

Это очень показательная таблица.

Слой оказался структурно «жёстким»:

  • без тривиальных повторов;

  • без клонов блоков;

  • без дешёвых exact-симметрий;

  • без готовой компрессии, которую можно было бы легко использовать для patch.

В результате финальный вывод по q_proj оказался жёстким, но правильным:

Итог по q_proj

Значение

Self-target identity optimum

True

External reference required

True

Non-identity patch justified

False

Exact patch candidates found

0

И это хороший результат.

Потому что система не «придумала улучшение», а доказала границу применимости метода.


Где patch действительно сработал: router

Совсем другая история получилась для слоя

model.layers.0.mlp.router.weight

Для него был построен полный pipeline:

  • calibration cache,

  • runtime activation capture,

  • blockwise LWO-оптимизация,

  • holdout validation,

  • safe patch,

  • smoke-check.

Параметры эксперимента

Параметр

Значение

Модель

gpt-oss-20b-TurboQuant-MLX-8bit

Слой

model.layers.0.mlp.router.weight

Train shard

general_0000_0128

Holdout shard

logic_0000_0128

Train sample count

4

Holdout sample count

4

Block size

64

Ridge lambda

16

Max code delta abs

8


Результат на train

На train-shard улучшение получилось почти идеальным.

Метрика

Значение

Baseline MSE

0.022966404894191328

Patched MSE

3.000931005970632e-11

MSE gain

0.022966404864182017

Changed blocks

571

Changed codes total

9067

На этом месте легко было бы сказать: «готово, мы вылечили слой».

Но это был бы неправильный вывод.

Train почти всегда можно улучшить. Главный вопрос — переносится ли эффект дальше.


Проверка на holdout

Вот здесь начинается самое интересное.

Метрика

Значение

Baseline MSE

0.022778386753429913

Patched MSE

4.3748027306868276e-05

MSE gain

0.022734638726123045

Transfer ratio

0.9899084711155454

Holdout improves

True

Overfit suspected

False

То есть improvement почти полностью перенёсся на отдельный shard.

Можно записать это совсем кратко:

\text{transfer ratio}=\frac{\Delta_{\text{holdout}}}{\Delta_{\text{train}}}\approx 0.9899

Это уже не похоже на случайное попадание или подгонку под четыре точки. Это означает, что система действительно нашла локальную корректировку, которая несёт устойчивый эффект.


Safe patch: почему это не просто красивый отчёт

После вычисления патча его нужно было ещё записать обратно в модель.

Для этого был реализован safe patch workflow:

  1. сохранить backup;

  2. упаковать новые коды в U32;

  3. записать их в .safetensors;

  4. перечитать и проверить exact match.

Результат записи

Метрика

Значение

Changed blocks

571

Changed codes total

9067

Raw u32 words changed

3335

Write applied

True

Readback exact match

True

То есть система умеет не только посчитать патч, но и корректно внедрить его в реальный модельный файл.

Именно здесь начинается инженерия, а не просто исследование на бумаге.


Самая честная часть всей истории: smoke-check

После записи патча был выполнен runtime smoke-check.

И вот здесь система показала, зачем вообще нужна строгая проверка.

Smoke-check: General

Метрика

Значение

Input exact rows

4 / 4

Output exact rows

0 / 4

Input runtime vs reference MSE

0.0

Output runtime vs reference MSE

0.023193681612610817

Output changed raw BF16 values

128

Smoke-check: Logic

Метрика

Значение

Input exact rows

4 / 4

Output exact rows

0 / 4

Input runtime vs reference MSE

0.0

Output runtime vs reference MSE

0.021752476692199707

Output changed raw BF16 values

128

Что это значит?

Очень просто:

  • вход в слой воспроизводится идеально;

  • а вот реальный runtime-output после записи не совпал с offline-reference, несмотря на сильный результат в solver-отчётах.

На первый взгляд это выглядит как плохая новость. На самом деле — наоборот.

Это означает, что система умеет обнаруживать расхождение между:

  • offline-моделью деквантования,

  • и реальной семантикой исполнения в MLX runtime.

То есть она не просто «генерирует патчи», а умеет поймать момент, когда красивая математика перестаёт совпадать с реальностью исполнения.

Это очень важная способность.


Что мы реально изменили в модели

Чтобы не перегибать с формулировками, зафиксирую аккуратно.

Мы не меняли:

  • архитектуру модели;

  • число слоёв;

  • механизм attention;

  • tokenizer;

  • high-level inference pipeline.

Мы изменили логику работы с её внутренними квантованными слоями:

  • добавили точное чтение кодов;

  • добавили разбор по блокам;

  • добавили BF16 activation capture;

  • добавили constrained integer patching;

  • добавили safe write-back;

  • добавили post-patch reality check.

То есть фактически мы превратили MLX-дистрибутив из «готовой коробки» в систему, которую можно инструментировать, диагностировать и хирургически править.


Что теперь умеет наша система

Если собрать всё вместе, получается уже довольно сильный capability map.

1. Exact audit квантованных слоёв

Система умеет разбирать внутреннее представление веса на уровне:

  • U32-кодов,

  • uint8-декодирования,

  • BF16 scale/bias,

  • block geometry.

2. Runtime capture живой модели

Она умеет снимать реальные входы и выходы линейного слоя на настоящем MLX-runtime, а не на симуляторе.

3. Локальную integer-коррекцию

Она умеет искать ограниченный patch для конкретных блоков без разрушения всего слоя.

4. Safe patching

Она умеет записывать корректировку обратно в .safetensors с контролем readback.

5. Holdout validation

Она умеет отличать реальное улучшение от переобучения на крошечном calibration-shard.

6. Runtime mismatch detection

Она умеет обнаружить, что offline-патч красив, но среда исполнения интерпретирует его иначе.

И вот это, пожалуй, одна из самых сильных возможностей системы на текущем этапе.


Что мы пока не заявляем

Чтобы статья оставалась инженерной, а не рекламной, зафиксирую и ограничения.

Мы пока не утверждаем, что:

  1. любой слой можно исправлять одинаково успешно;

  2. q_proj уже улучшен — нет, там система честно упёрлась в границу метода;

  3. runtime-семантика MLX уже полностью раскодирована;

  4. AI-ветка уже усиливает криптоаналитический recovery end-to-end;

  5. вся lattice-ветка для AI уже production-ready.

Но мы вполне можем утверждать другое:

построена рабочая exact AI-forensics инфраструктура для квантованной MLX-модели.

И это уже немало.


Почему это интереснее обычного fine-tuning

Обычный fine-tuning отвечает на вопрос:

как сделать модель в среднем лучше на задаче?

Наш подход отвечает на другой вопрос:

что делает конкретный квантованный слой, как он устроен в exact-представлении, и можно ли изменить его локально, не теряя контроля над каждым шагом?

Это другой уровень доступа.

Не «подкрутить поведение модели», а разобрать её внутреннюю линейную механику до уровня кодов квантования.

Именно поэтому эта AI-ветка выросла у нас не из MLOps, а из криптоанализа.

Потому что и там, и здесь задача одна и та же:

перестать верить чёрному ящику и заменить веру наблюдаемой структурой.


Итог

Если сформулировать совсем коротко, то произошло следующее.

Мы взяли gpt-oss-20b-TurboQuant-MLX-8bit и добавили к нему не «ещё один AI-скрипт», а целую exact-инфраструктуру:

  • bridge к .safetensors,

  • calibration cache,

  • BF16 activation capture,

  • constrained blockwise optimization,

  • safe patch,

  • holdout validation,

  • runtime smoke-check.

Подтверждённый итог на текущем этапе двойной:

  1. router-слой действительно допускает осмысленную локальную коррекцию, причём эффект переносится на holdout;

  2. offline-модель патча и реальная MLX runtime-семантика ещё не совпадают полностью, и система это умеет честно обнаруживать.

А значит, сегодня наша система уже умеет делать не только криптографическую forensics-диагностику, но и то же самое внутри квантованной нейросети:

выделять слой, формализовать его как affine-объект, локально исправлять и проверять, не обманывает ли нас сама среда исполнения.

Именно в этом месте AI перестаёт быть «магией модели» и становится объектом точной инженерной диагностики.


Короткое послесловие

Эта работа не про «волшебное улучшение LLM». Она про другое: как перенести дисциплину криптоанализа в AI и научиться работать с квантованной моделью так же строго, как мы работаем с протоколами подписи.

То есть не «верить, что внутри что-то происходит», а видеть, измерять, патчить и проверять.

И это, похоже, только начало.


ссылка на оригинал статьи https://habr.com/ru/articles/1026340/