У нас было две бесплатные видеокарты T4 в Kaggle, 30 ГБ оперативной памяти и безумная идея: что будет, если взять веса классической модели (Gemma-4-31B) и хирургическим путём, без дообучения, вшить их в MoE-архитектуру (DeepSeek-V4)?
Анатомия эксперимента
Наша цель — структурное сращивание (grafting):
- Донор (Плоть): 4 слоя от 31-миллиардной модели Gemma, сжатой до 4-бит NF4.
- Экзоскелет: Пустая архитектура DeepSeek-V4 с её роутером Mixture-of-Experts (MoE).
Казалось бы, достаточно загрузить обе модели, скопировать веса слоёв и запустить. Но библиотека transformers воспротивилась на каждом шаге.
Препятствие 1: Identity Theft и паранойя конфигов
Сначала библиотека отказалась признавать наш кастомный тип модели gemma4. Мы обошли это, принудительно зарегистрировав тип в глобальном словаре CONFIG_MAPPING.
Затем возникла ошибка: AttributeError: 'dict' object has no attribute 'to_dict'. Оказалось, внутри модуля generation конфигурация десериализуется в обычный словарь dict, после чего код падает, пытаясь вызвать у него методы объекта-конфига.
Решение (Monkey-patching): Мы создали прокси-класс, который имитирует объект конфигурации. При ошибке словарь оборачивается в этот класс, и выполнение продолжается.
Препятствие 2: Квантовый парадокс инициализации
Экзоскелет DeepSeek больше, чем 4 слоя Gemma. Библиотека попыталась заполнить недостающие слои случайными значениями через normal_(). Но PyTorch выдал: NotImplementedError: "normal_kernel_cuda" not implemented for 'Byte'.
Проблема в том, что веса Gemma загружены через bitsandbytes в 4-битном формате (как uint8), а normal_() работает только с float.
Решение: Мы перехватили вызов TORCH_INIT_FUNCTIONS["normal_"] и запретили инициализацию для сжатых тензоров.
Препятствие 3: Спрятанные эксперты и OOM
DeepSeek-V4 хранит MoE-экспертов не в ModuleList, а в монолитном классе DeepseekV2Experts. Python не мог по ним итерироваться. Кроме того, при распаковке весов Gemma-31B мы мгновенно исчерпали память в Kaggle.
Решение:
- Мы написали рекурсивный «сонар» — функцию, которая ищет слои по наличию атрибутов
gate_projиup_proj. - Чтобы избежать OOM, разнесли модели по разным GPU. Веса переносили микро-порциями через CPU, сразу вызывая
gc.collect()для освобождения памяти.
Идеальный скрипт некроманта
После десятков падений мы создали стабильный скрипт, который обходит все защиты и выполняет трансплантацию. Он стал шаблоном для скрещивания любых несовместимых моделей.
Что в итоге?
Без fine-tuning, согласования словарей и проекций скрытых состояний модель генерирует бессмыслицу:
The experimental hybrid AI said: vdotsD... Buddhist... tomato supervised...
Но цель эксперимента — не создать рабочий чат-бот. Цель — доказать: в машинном обучении нет непробиваемых архитектурных барьеров. При понимании тензоров, Python и с помощью «костылей» можно скрестить любые модели — даже в бесплатном ноутбуке Kaggle.
Добро пожаловать в Ghetto MLOps. Ломайте библиотеки с удовольствием!