Как я использовал модели с открытым исходным кодом для исследования новых горизонтов в области эффективной генерации кода, используя свой MacBook и локальные LLM-ы.
Делиться

Введение
С самого детства меня завораживало рисование. Меня поражало не только само рисование, но и мысль о том, что каждый рисунок можно постоянно улучшать. Я помню, как достигал очень высокого уровня в своем стиле рисования. Однако, достигнув вершины совершенства, я пытался понять, как еще можно улучшить рисунок – увы, с катастрофическими результатами.
С тех пор я всегда помню один и тот же принцип: «совершенствуйся, итеративно совершенствуйся, и ты достигнешь совершенства». В университете я много раз перечитывал книги, расширяя свои знания, ища другие источники, чтобы находить скрытые смысловые пласты в каждой концепции. Сегодня я применяю ту же философию к искусственному интеллекту/машинному обучению и программированию.
Мы знаем, что умножение матриц (для простоты — matmul) является основной частью любого процесса искусственного интеллекта. В прошлом я разработал LLM.rust, Rust-версию LLM.c от Karpathy. Самым сложным моментом в реализации на Rust было умножение матриц. Поскольку нам приходится выполнять тысячи итераций для тонкой настройки модели на основе GPT, нам необходима эффективная операция умножения матриц. Для этой цели мне пришлось использовать библиотеку BLAS, реализовав небезопасную стратегию для преодоления ограничений и барьеров. Использование небезопасных методов в Rust противоречит философии языка, поэтому я всегда ищу более безопасные способы улучшения умножения матриц в этом контексте.
Вдохновившись высказыванием Сэма Альтмана — «спросите GPT, как создать ценность» — я решил попросить местных магистров права разработать, протестировать и усовершенствовать собственные алгоритмы для создания более совершенной, нативной реализации вычисления матриц на Rust.
Задача имеет ряд ограничений:
- Нам нужно использовать нашу локальную среду. В моем случае это MacBook Pro, M3, 36 ГБ ОЗУ;
- Преодолейте ограничения, связанные с использованием токенов;
- Проведите замер времени и сравнительный анализ производительности кода непосредственно в цикле генерации.
Я понимаю, что достичь производительности уровня BLAS с помощью этого метода практически невозможно, но я хочу показать, как мы можем использовать ИИ для решения индивидуальных задач, даже с нашими «миниатюрными» ноутбуками, чтобы открывать новые идеи и расширять границы в любой области. Эта статья призвана вдохновить практиков и людей, желающих лучше познакомиться с Microsoft Autogen и локальным развертыванием LLM.
Вся реализация кода находится в этом репозитории на GitHub. Это продолжающийся эксперимент, и в будущем будет внесено множество изменений и улучшений.
Общая идея
Основная идея заключается в создании круглого стола для агентов. Отправной точкой является локальная модель MrAderMacher Mixtral 8x7B Q4 K_M. На основе этой модели мы создаём 5 сущностей:
- В предложении используется новый алгоритм, похожий на алгоритм Штрассена, для поиска лучшего и более эффективного способа выполнения матричных умножений;
- Верификатор проверяет формулировку matmul с помощью символических вычислений;
- Программист создает базовый код на языке Rust;
- Тестировщик выполняет операцию и сохраняет всю информацию в векторную базу данных;
- Менеджер действует незаметно, контролируя весь рабочий процесс.
| Агент | Функция роли |
| Предложивший | Анализирует время выполнения тестовых заданий и предлагает новые параметры настройки и формулировки матричных уравнений. |
| Верификатор | (В настоящее время отключено в коде). Проверяет математическую формулировку автора посредством символической проверки. |
| Программист | Программа принимает параметры и вычисляет шаблонный код на Rust. |
| Тестер | Программа запускает код на Rust, сохраняет его и вычисляет время выполнения бенчмарка. |
| Менеджер | Общий контроль над рабочим процессом. |
Общий рабочий процесс можно организовать с помощью Microsoft Autogen, как показано на рис. 1.

Подготовьте входные данные и векторную базу данных.
Входные данные собраны из всех научных статей, посвященных оптимизации умножения матриц. Многие из этих статей упоминаются в статье Штрассена, опубликованной компанией DeepMind, и связаны с ней. Я хочу начать с простого, поэтому я собрал 50 статей, опубликованных с 2020 по 2025 год, которые конкретно посвящены умножению матриц.
Далее я использовал Chroma для создания векторной базы данных. Критически важным аспектом при создании новой векторной базы данных является способ разбиения PDF-файлов на блоки. В данном контексте я использовал семантический блокировщик . В отличие от методов разделения текста, семантический блокировщик использует фактическое значение текста для определения мест разбиения. Цель состоит в том, чтобы сохранить связанные предложения вместе в одном блоке, что делает итоговую векторную базу данных более связной и точной. Это достигается с помощью локальной модели BAAI/bge-base-en-v1.5. Полная реализация представлена в приведенном ниже фрагменте кода на Github.
Основной код: autogen-core и модели GGML.
Я использовал Microsoft Autogen, в частности, вариант autogen-core (версия 0.7.5). В отличие от высокоуровневого чата, в autogen-core у нас есть доступ к низкоуровневым событийно-ориентированным строительным блокам, необходимым для создания рабочего процесса, управляемого конечным автоматом, в соответствии с нашими потребностями. По сути, задача состоит в поддержании строгого рабочего процесса. Все действующие агенты должны действовать в определенном порядке: инициатор –> верификатор –> программист –> тестировщик.
Ключевой частью является класс BaseMatMulAgent, наследующий от класса RoutedAgent из AutoGen. Этот базовый класс позволяет стандартизировать участие агентов LLM в чате и их поведение.
Из приведенного выше кода видно, что класс предназначен для участия в асинхронном групповом чате, обработки истории переписки, вызовов внешних инструментов и генерации ответов через локальный LLM.
Ключевым компонентом является декоратор `@message_handler`, который регистрирует метод в качестве слушателя или подписчика в зависимости от типа сообщения. Декоратор автоматически определяет подсказку типа первого аргумента метода — в нашем случае это `message: GroupChatMessage`. Затем он подписывает агента на получение любых событий этого типа, отправляемых в топик агента. Асинхронный метод `handle_message` отвечает за обновление внутренней памяти агента без генерации ответа.
После того, как механизм «слушатель-подписчик» налажен, мы можем сосредоточиться на классе Manager. Класс MatMulManager наследует RoutedAgent и координирует общий поток агентов.
Приведённый выше код обрабатывает всех агентов. Часть, отвечающую за верификацию, пока опускаем. Кодер публикует окончательный код, а тестировщик заботится о сохранении как кода, так и всего контекста в базу данных векторов. Таким образом, мы можем избежать использования всех токенов нашей локальной модели. При каждом новом запуске модель будет обновлять алгоритмы, сгенерированные в базе данных векторов, и предлагать новое решение.
Очень важное замечание: чтобы убедиться, что autogen-core работает с моделями лам на MacOS, используйте следующий фрагмент кода:
#!/bin/bash CMAKE_ARGS=»-DGGML_METAL=on» FORCE_CMAKE=1 pip install —upgrade —verbose —force-reinstall llama-cpp-python —no-cache-dir
На рис. 2 представлен общий код. Код можно условно разделить на 3 основных блока:
- BaseAgent обрабатывает сообщения через агентов LLM, оценивает математическую формулировку и генерирует код;
- MatMulManager координирует весь рабочий процесс агентов;
- Функция autogen_core.SingleThreadedAgentRuntime позволяет нам реализовать весь рабочий процесс.

Результаты и контрольные показатели
Весь код на Rust был пересмотрен и повторно запущен вручную. Хотя рабочий процесс надежен, работа с LLM требует критического подхода. Несколько раз модель выдавала ошибки*, генерируя код, который выглядел оптимизированным, но не выполнял фактическую работу с матрицами.
Первая же итерация генерирует алгоритм, подобный алгоритму Штрассена (код «Запуск 0» на рис. 3):
Модель предлагает более удачные реализации, более похожие на Rust-NEON, поэтому после 4 итераций она выдает следующий код («Запуск 3» на рис. 3):
Мы видим использование таких функций, как vaddq_f32, специфичной инструкции ЦП для процессоров ARM, заимствованной из std::arch::aarch64. Модель использует rayon для разделения рабочего процесса между несколькими ядрами ЦП, а внутри параллельных потоков используются встроенные функции NEON. Сам код не совсем корректен, кроме того, я заметил, что при работе с матрицами 1024×1024 возникает ошибка нехватки памяти. Мне пришлось вручную переработать код, чтобы он заработал.
Это возвращает нас к нашему девизу «итерация до совершенства», и мы можем задаться вопросом: «Может ли локальный агент автономно совершенствовать код Rust до такой степени, чтобы освоить сложные встроенные функции NEON?». Результаты показывают, что да, даже на потребительском оборудовании такой уровень оптимизации достижим.
На рис. 3 показаны окончательные результаты, полученные после каждой итерации.

В нулевом и втором тестах обнаружены ошибки, поскольку физически невозможно достичь таких результатов на умножении матрицы размером 1024×1024 на процессоре:
- Первый вариант кода страдает от диагональной ошибки, поэтому он вычисляет только диагональные блоки матрицы, игнорируя остальные;
- Во втором фрагменте кода обнаружена ошибка в буфере, поскольку он многократно перезаписывает небольшой, «горячий» буфер (1028 чисел с плавающей запятой), вместо того чтобы обработать все 1 миллион элементов.
Однако код выдал два реальных результата: первый и третий. Первая итерация показала время выполнения 760 мс и является реальным базовым показателем. Она страдает от промахов кэша и отсутствия SIMD-векторизации. Третий результат составил 359 мс, улучшение обусловлено реализацией NEON SIMD и параллелизма Rayon.
*: Я специально написал «модель конфабулирует». С медицинской точки зрения, все LLM не галлюцинируют, а конфабулируют. Галлюцинации — это совершенно другая ситуация по сравнению с тем, что делают LLM, когда бормочут и генерируют «неправильные» ответы.
Выводы
Этот эксперимент начался с вопроса, который казался неразрешимой задачей: «Можем ли мы использовать локальные LLM-модели потребительского класса для поиска высокопроизводительных алгоритмов Rust, способных конкурировать с реализацией BLAS?».
Мы можем ответить «да», или, по крайней мере, у нас есть веская и прочная база знаний, позволяющая создавать более качественный код, подобный BLAS, на Rust.
В публикации было показано, как взаимодействовать с Microsoft Autogen, autogen-core и как создать круглый стол для участников.
Используемая базовая модель предоставлена компанией GGUF и может работать на MacBook Pro M3 с 36 ГБ оперативной памяти.
Конечно, мы пока не нашли ничего лучше BLAS в одном простом примере кода. Однако мы доказали, что локальный агентный рабочий процесс на MacBook Pro может достичь того, что ранее считалось необходимым для работы огромного кластера и больших моделей. В итоге модель смогла найти приемлемую реализацию на Rust-NEON, «Запуск 3 выше», которая обеспечивает ускорение более чем на 50% по сравнению со стандартной реализацией Rayon. Следует отметить, что базовая реализация была сгенерирована искусственным интеллектом.
Границы открыты. Надеюсь, эта запись в блоге вдохновит вас на попытку понять, какие ограничения мы можем преодолеть при локальном развертывании LLM.
Источник: towardsdatascience.com























