Пересадить алгоритм из маленькой модели в GPT-подобный LLM? Почему это почти невозможно
Разбираемся, можно ли "впрыснуть" логику из TinyML в GPT-подобные LLM без fine-tuning. Изучаем эксперименты с residual stream и почему это сложно.

Вы разработали высокоэффективный, но крошечный модуль, который решает сложную задачу (например, модульную арифметику) идеально. Теперь вы хотите "встроить" эту логику в огромный, дорогой и универсальный LLM (как GPT-4), не тратя миллионы на fine-tuning. Если бы это было возможно, это бы изменило правила игры в AI-продуктах.
Вместо простого ответа "нет", мы прошли через серию экспериментов с архитектурой трансформеров и выяснили, что проблема не в математике, а в фундаментальной разнице между языковым и численным базисами.
Что такое "компилированный алгоритм" в нейросети
Чтобы понять, где ломается мост, нужно понять, что мы пытались перенести.
В мире машинного обучения часто говорят о "черных ящиках" (black box) — мы знаем, что LLM работают, но не понимаем, как. Механистическая интерпретируемость (mechanistic interpretability) — это направление, которое пытается заглянуть внутрь нейросети и найти именно алгоритмы, зашитые в её весах.
На примере модульной арифметики (например, сложение чисел по модулю 100) стало очевидно: модель не просто запомнила 9409 пар примеров. Она сконструировала сам алгоритм.
Что это значит на практике: Когда маленькая модель обучается решать чистую, изолированную задачу (например, сложение), её внутреннее состояние (residual stream) не просто хранит данные — оно формирует геометрическую структуру. В данном случае, числа лежали на окружности, и операция сложения сводилась к простому вращению векторов. Это и есть "скомпилированный алгоритм".
Именно эту структуру мы пытались вытащить из крошечной модели и "впрыснуть" в гигантскую LLM.
Почему готовые патчинг-решения не работают
Наша амбициозная гипотеза была следующей: если алгоритм — это геометрическая структура в векторе, то между двумя моделями (маленькой и большой), решающими одну задачу, должен существовать линейный оператор $W$, который переводит структуру из одной в другую.
Проще говоря: взять выходной вектор $h_A$ из маленькой модели, умножить его на $W$, и получить вектор $h_B$, который большая модель поймет и обработает.
Мы протестировали несколько подходов, и каждый раз сталкивались с провалом.
❌ Попытка 1: Случайная проекция (Проблема геометрии)
Мы взяли случайную матрицу $W$ для преобразования вектора.
- Результат: Косинусное сходство (cos\_sim) было низким, а прирост точности был статистическим шумом.
- Вывод: Случайное преобразование полностью разрушает тщательно выстроенную геометрию алгоритма. Проекция $W$ должна быть обучена.
❌ Попытка 2: Обучение через MSE (Проблема цели)
Мы обучили $W$ минимизировать среднеквадратичную ошибку (MSE) между активациями двух моделей.
- Результат: cos\_sim остался низким, а прирост точности был нулевым ($\Delta=0$).
- Почему провал: MSE оптимизирует глобальную близость между точками. Она заставляет векторы находиться рядом, но это не гарантирует, что эти векторы станут линейно разделимыми — то есть, что мы сможем провести прямую линию, чтобы отделить класс "0" от класса "1". Мы оптимизировали близость, а не структуру.
❌ Попытка 3: Обход токенизатора (Проблема языка)
Мы попробовали подать в LLM не текст, а готовые эмбеддинги (inputs\_embeds), минуя токенизатор.
- Результат: Точность резко упала.
- Вывод: LLM — это не просто сложный математический вычислитель. Это языковая модель. Без контекста, заданного текстовым промптом ("What is X + Y?"), она не понимает, что от неё хотят. Для нее два вектора — это белый шум.
Где ломается мост: Фурье против Языка
Самый важный эксперимент — это патчинг прямо в residual stream с контекстом. Мы сохранили текстовый промпт, но заменили вектор, содержащий ответ, на вектор, полученный из маленькой модели.
Диагностика: Противоречие в архитектуре
Мы обнаружили критическое противоречие:
- Проба (Probe): Когда мы поставили линейный зонд (Linear Probe) на слое L+1, он показал точность $1.0$. Это означает: информация о правильном ответе действительно есть в residual stream.
- Логит (Logit Lens): Но когда мы подали этот же вектор напрямую в финальный слой модели (LM head), точность упала до $0.005$. Это означает: финальный слой модели не может эту информацию прочитать.
Главный вывод: Маленькая модель кодировала числа в Фурье-базисе (как вращения на окружности). LLM (например, Phi-2) кодируют числа в языковом базисе (контекст, синтаксис, семантика).
Вы можете перенести геометрически правильный вектор, но если финальный слой LLM обучен читать только свой родной языковой базис, он увидит ваш вектор как "иероглиф" и не сможет его расшифровать.
Подводные камни: Почему это не просто патчинг
Попытка "пересадить" алгоритм — это попытка заставить языковую модель работать не как языковая, а как чистый математический процессор.
Это не просто проблема "вставить вектор". Это проблема различия в внутреннем представлении знаний:
- Язык vs. Математика: LLM построены на принципах вероятности и контекста. Они предсказывают следующее слово, основываясь на миллиардах примеров естественного языка. Это фундаментально отличается от чистых математических преобразований, где правила абсолютны.
- Архитектурная жесткость: LLM — это авторегрессивные трансформеры декодерного типа. Они имеют жесткий поток информации, где каждый слой и каждый механизм (Multi-Head Attention) ожидают вход в определенном "языковом" формате.
- Недостаточность механизма внимания: Хотя внимание (Attention) — это мощный механизм, он всё равно оперирует контекстом. Если вы подаете ему чистый, "неязыковой" вектор, он не знает, как его интерпретировать в рамках языковой структуры.
Что попробовать дальше
Если вам нужно встроить надежную, узкоспециализированную логику в LLM, обходные пути, основанные на чистом патчинге векторов, скорее всего, не сработают. Вместо этого стоит рассмотреть следующие архитектурные подходы:
- RAG (Retrieval-Augmented Generation): Это самый надежный и промышленно используемый метод. Вы не передаете логику, а передаете факты или правила. LLM читает эти правила из контекста и генерирует ответ, который должен соответствовать заданным ограничениям.
- Function Calling / Tool Use: Это лучший способ "вынести" расчет из LLM. Вы не пытаетесь заставить LLM вычислять $A+B$. Вы просите LLM определить, что ей нужно использовать внешний инструмент (функцию), которая и выполнит расчет, а затем передать результат обратно в LLM для генерации финального, языкового ответа.
- Hybrid Architectures (Гибридные системы): Создание многоступенчатых пайплайнов, где LLM используется только для планирования и интерпретации задачи, а специализированный, маленький, высокоэффективный модуль (например, на PyTorch или ONNX) используется для выполнения критических вычислений.
Таким образом, вместо того чтобы пытаться пересадить алгоритм в LLM, вы должны научить LLM вызывать внешний, надежный вычислительный модуль.