С развитием технологий машинного обучения и искусственного интеллекта всё больше специалистов и энтузиастов стремятся адаптировать большие языковые модели (LLM) под свои задачи. В последние годы появилось множество моделей с миллиардами параметров, способных решать сложные задачи генерации и понимания естественного языка. Одним из таких мощных решений стала модель Mistral-7B — 7-миллиардный параметр, выпущенный компанией Mistral AI в конце 2023 года. Несмотря на относительно компактный размер по сравнению с некоторыми крупными моделями, Mistral-7B демонстрирует впечатляющие возможности, особенно после тонкой настройки под специализированные задачи. Однако для многих пользователей Mac на базе Apple Silicon остается неочевидным вопрос: возможно ли эффективно дообучать такие крупные модели локально, без обращения к облачным вычислениям с NVIDIA GPU? В своей статье мне хотелось поделиться личным опытом тонкой настройки Mistral-7B на Mac с процессором M3 Ultra, используя инструменты Axolotl и LoRA, а также обсудить возникавшие сложности и пути их решения.
В традиционном подразделении задач машинного обучения широко распространена идея, что для эффективной дотонкой настройки больших моделей необходимы мощные графические процессоры с поддержкой CUDA от NVIDIA. Это связано с тем, что большинство фреймворков и оптимизаторов ориентированы именно на эту архитектуру. Однако в последние годы Apple активно развивает поддержку Metal Performance Shaders (MPS) — своего API для ускорения вычислений на GPU, встроенном в чипы M-серии. Несмотря на прогресс, PyTorch и многие сопутствующие библиотеки по-прежнему имеют ограничения в полной поддержке MPS. Из-за этого возможность тонкой настройки таких моделей локально на Mac представляется более сложной задачей.
Для начала я решил испытать Axolotl — открытый и удобный инструмент для упрощения обучения LLM, который поддерживает широкий спектр моделей, включая Mistral и Falcon. Его архитектура ориентирована на использование YAML конфигураций, что позволяет минимизировать необходимость писать собственный код. Идея была проста: воспользоваться готовым инструментом, который автоматизирует подготовку данных, применение LoRA (Low-Rank Adaptation) и процесс слияния весов, чтобы сосредоточиться на подборе данных и конфигурации. Однако работа с Axolotl на Apple Silicon вскоре выявила ряд технических сложностей. Главным препятствием стала несовместимость с bitsandbytes — библиотекой для 8-битных оптимизаций, широко используемой в CUDA среде.
На Macbitsandbytes не поддерживается, а Axolotl по умолчанию пытается применить её для загрузки моделей или ускорения оптимизации при использовании методов квантизации, таких как QLoRA. Приходилось отключать все возможности 4- и 8-битного обучения, переключаясь на более тяжелые 16- или 32-битные режимы. В итоге обучение шло дольше, но без фатальных сбоев. Кроме того, некоторые рутинные вызовы CUDA-функций вызывали предупреждения при завершении работы, что свидетельствовало о недостаточной готовности Axolotl к работе вне NVIDIA GPU. Дополнительным вызовом стала проблема с форматом датасета.
Мой исходный датасет содержал пары инструкций и ответов в JSON формате, но Axolotl ожидал другой формат данных, соответствующий шаблонам диалогового общения на основе сообщений с ролями. После ряда ошибок я адаптировал конфигурацию под свой формат, явно указав поля с инструкциями и ответами. Это позволило успешно загрузить данные и начать обучение. Тем не менее, одна из ключевых ошибок проявилась на этапе слияния весов LoRA с базовой моделью. Метод merge_and_unload, который должен объединять веса, отсутствовал в классе MistralForCausalLM, используемом в Axolotl.
Эта ошибка не позволяла получить итоговую модель — по факту после нескольких часов обучения сохранялась только конфигурация, но не LoRA-адаптер, что сводило всю работу на нет. Эта проблема была известна сообществу, но в момент моего эксперимента её обход был не очевиден, и поддержки полноценной работы Axolotl с Mistral на Mac не было. После нескольких попыток и поиска решений я принял решение отказаться от Axolotl и написать собственный сценарий обучения на основе библиотеки Transformers и PEFT от Hugging Face. Такой подход требует больше усилий, но позволяет контролировать все детали и адаптировать процесс под ограничения Apple Silicon без завязки на CUDA. Для начала была организована среда с последней версией PyTorch с поддержкой MPS, а также установлены необходимые библиотеки — Transformers, Datasets, PEFT и safetensors.
Особое внимание уделялось отключению Weights & Biases (W&B) для избежания ненужных интерактивных окон и логов. Структура проекта была упорядочена: отдельные папки для базовой модели, датасета, сохранения adapter-ов и объединенной модели. Загрузка модели и токенизатора осуществлялась локально, с загрузкой модели в full precision, учитывая невозможность использования 8-битного режима на Mac. Для размещения модели на графическом процессоре MPS использовался параметр device_map. Так как Mistral строится на архитектуре, схожей с LLaMA, в качестве целевых слоёв для применения LoRA выбирались q_proj, k_proj, v_proj и o_proj с рангом 16 и параметрами lora_alpha и dropout, подобранными эмпирически.
Датасет загружался при помощи Datasets, а примеры обрабатывались функцией форматирования, объединяющей инструкцию с ответом в единый текст в стиле диалога. Обработка данных была адаптирована под формат модели, чтобы облегчить обучение без ошибок. Тонкая настройка происходила через стандартный Trainer с небольшим batch_size и градиентным накоплением для компенсации меньшего объёма памяти. Включение fp16 обеспечивало частичное ускорение вычислений на MPS. Обучение шло медленно, но стабильно — после нескольких эпох удалось добиться заметного снижения ошибки.
После окончания обучения LoRA-адаптер успешно сохранился, предоставляя возможность загружать базовую модель вместе с адаптером без необходимости хранить полную копию модели. Затем последовал этап слияния весов LoRA с базовой моделью. Работу выполнял отдельный скрипт, загружающий базовую модель на CPU, применяющий adapter-ы и вызывающий метод merge_and_unload для интеграции весов и освобождения памяти. Результат сохранялся в формате safetensors с безопасной сериализацией. Этот шаг прошёл без сбоев, в отличие от Axolotl.
Далее настала очередь конвертации модели в формат GGUF для использования с llama.cpp — популярным инструментом для запуска локальных LLM на CPU и Mac. При помощи официального скрипта конвертации была создана квантизированная 4-битная версия модели (q4_0), которая значительно сократила размер и повысила скорость инференса, позволяя запускать модель в исходном или оптимизированном виде в Mac-friendly UI под названием LM Studio. При установке модели в LM Studio нужно было соблюсти определённую иерархию папок и расположение файла .gguf с учётом соглашений приложения.
После этого модель появилась в списке доступных для запуска, и первое тестирование показало, что модель отвечает с заметным знанием Rust — того языка программирования, на котором обучался датасет. За время работы над проектом стало понятно, что обучение и дообучение крупных моделей на Mac — это вполне выполнимая задача, если иметь минимум терпения и понимать ограничения текущих инструментов. Хоть Axolotl предлагает удобства и шаблоны, для Apple Silicon имеют смысл кастомные решения с использованием проверенных библиотек PyTorch и PEFT. Для тех, кто планирует заняться подобной задачей, важно тщательно выстраивать окружение, отключать ненужные логгеры и разбираться в формате данных. Внимательное разделение папок для моделей, данных и результата помогает избежать путаницы.