Меня долгое время интересовал запуск больших языковых моделей на пользовательских устройствах: есть что‑то в том, чтобы запустить одну из лучших языковых моделей на обычном домашнем компьютере или на мобильном телефоне, помещающемся в карман.

В этом посте я расскажу о своём пет‑проекте AQLM.rs. Я написал инференс модели Llama 3.1 8B, работающий в браузере на WebAssembly без использования GPU, с помощью алгоритма сжатия, разработанного нашей лабораторией.

Попробовать можно на сайте проекта.

Почему 8B

Запуск языковых моделей на пользовательском девайсе — далеко не новая идея. Например, такие модели, как Llama 3.2 1B и Llama 3.2 3B, изначально создавались с целью запуска на маломощных устройствах.

Модели, имеющие 8 млрд параметров, идеально подходят для демонстрации того, что может быть помещено в браузер с помощью продвинутых алгоритмов сжатия.

В разжатом виде на каждый параметр приходится по 16 бит, и, как следствие, модель 8B весит 16 ГБ. Используя стандартные 4-битные методы сжатия, такие как nf4, можно сжать её до 4 ГБ. В этом проекте я использую экстремальное сжатие в 2 бита, сжимающее тело модели в 8 раз. Для слоёв головы и эмбеддингов я всё ещё использую 4-битное и 8-битное сжатие, поэтому модель получается размером в районе 2,5 ГБ.

Плюс экстремального сжатия заключается в том, что с уменьшением размера сама модель ускоряется, поскольку скорость вычисления такого рода сильно упирается в работу с памятью. Меньше памяти — значит, больше скорость.

Отдельно хочется упомянуть, что Llama 3.1 8B, сжатая до 2 бит нашим алгоритмом, всё ещё лучше, чем Llama 3.2 3B в несжатом виде, потому что занимает в 2 раза меньше места.

Много больших матриц

Большие языковые модели состоят из матриц. Основная вычислительная нагрузка при работе модели — это обыкновенное умножение матрицы на вектор. Оптимизацией именно этой части занимаются методы сжатия. Они пытаются уменьшить размер матриц, меняя их представление на более компактное, минимизируя потери качества.

В мае 2024 года наша команда из Yandex Research вместе с коллегами из университетов IST Austria и KAUST опубликовала исследование, в котором мы описали алгоритм PV‑Tuning для улучшения методов сжатия больших языковых моделей без изменения формата сжатых весов (я писал про неё на «Хабре»).

В моём проекте в качестве базового метода, улучшаемого PV‑Tuning, я использую AQLM. В этом методе сжатым представлением является аддитивная векторная квантизация. Для 2-битной квантизации (это когда на один параметр приходится всего 2 бита, а не 16, как у изначальной модели, то есть в 8 раз меньше) каждая строчка матрицы собирается из маленьких кусков по восемь чисел. Каждый такой кусок — это сумма двух векторов из словарей по 256 элементов каждый. Получается, что для хранения значения восьми элементов матрицы надо потратить 2 раза по 8 бит на индексы. Отсюда и берётся 2 бита на параметр.

WebAssembly и Rust

Благодаря развитию WebAssembly программы для браузера стало можно писать почти на любом языке. Несколько лет назад я прошёл курс по Rust в ШАД, влюбился в язык, но никак не мог найти ему применение в своих пет‑проектах. Я не мог упустить этот шанс и реализовал весь инференс на нём.

Приятным сюрпризом было то, что на Rust написано очень много фундаментальных библиотек для инфраструктуры вокруг LLM.

Например, у Hugging Face есть формат под названием safetensors. Оказалось, что его реализация полностью написана на Rust! Каждый раз, когда кто‑нибудь в своём коде на Python использует safetensors‑модель с Hugging Face, он использует Rust.

На Rust также написан токенайзер под названием tiktoken от OpenAI, который используется в новых «ламах».

Многопоточность

Чтобы ускорить работу модели, я реализовал многопоточность с помощью веб‑воркеров. Они поддерживают двунаправленную связь между тредами через передачу сообщений. Моё решение основано на model‑parallel‑подходе: все матрицы разделяются по размерности выхода, и каждый воркер получает свой кусок каждой матрицы.

Самая сложная часть заключалась в организации взаимодействия между воркерами и основным тредом. Для этого я написал кастомный RPC‑стек для воркеров с интеропом между Rust и JavaScript.

Когда главному треду нужно умножить вектор на матрицу, он составляет запрос к каждому воркеру, сериализует его и отправляет в JavaScript‑рантайм. Затем JavaScript пересылает его воркеру, который десериализует, обрабатывает запрос и сериализует результат перед отправкой обратно. После этого JavaScript отправляет результат в главный тред, где тот десериализуется.

Этот подход позволил увеличить производительность примерно в 2 раза.

Где попробовать

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

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

Исходный код проекта загружен на GitHub. Буду рад критике и предложениям.

Комментарии (20)


  1. Siberex
    09.12.2024 09:02

    Ваше сжатие выигрывает у Microsoft BitNet?


    1. galqiwi Автор
      09.12.2024 09:02

      Сейчас, насколько я знаю, все 8B BitNet модели с открытыми весами имеют хуже качество, чем у нас.
      Алгоритмы фундаментально разные, мы сжимаем любую модель после обучения, а BitNet в своём базовом виде занимается обучением модели с нуля.


  1. frenzis
    09.12.2024 09:02

    Было бы замечательно если бы вы поддержали AQLM в llama.cpp


    1. galqiwi Автор
      09.12.2024 09:02

      Мы пытались, но код llama.cpp очень хрупкий, и не заточен на векторную квантизацию с кастомными кодбуками. За 20-30 человеко-часов значимо продвинуться не получилось


      1. frenzis
        09.12.2024 09:02

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


        1. galqiwi Автор
          09.12.2024 09:02

          Для такого у нас есть поддержка vllm и huggingface бэкендов. Подробнее тут.


          1. frenzis
            09.12.2024 09:02

            llama.cpp и популярная обертка вокруг него ollama выстрелила как царь пушка, потому что не нужно тащить с собой все эти python/numpy/pytorch и прочие сотни мегабайт бинарей "бэкендов" чтобы просто матмулы делать, так что vllm не является равнозначной альтернативой этому


  1. Xirikk
    09.12.2024 09:02

    классный проект

    а модель поддерживает только определенные наборы английских слов?

    интересно что на одно слово, модель зациклилась


    1. galqiwi Автор
      09.12.2024 09:02

      Модель поддерживает любые строчки — даже на русском (правда на русском она заметно глупее). Просто иногда тупит

      Если спросить её что-то осознанное, она ответит


  1. raamid
    09.12.2024 09:02

    Здравствуйте, очень интересно, планируется или использование WebGL/WebGPU для ускорения работы?


    1. galqiwi Автор
      09.12.2024 09:02

      Нет, всё работает на cpu.

      Можно было написать кернелы для webgpu, но, если честно, у меня банально не было на это времени.

      Зато портативность хорошая. Работает на всём, что имеет cpu и достаточное количество ram. Я ещё хочу релизнуть поддержку маленьких моделей, чтобы даже на телефонах можно было запускать (и сейчас можно, но не на всех)


  1. vagon333
    09.12.2024 09:02

    Я ещё хочу релизнуть поддержку маленьких моделей, чтобы даже на телефонах можно было запускать (и сейчас можно, но не на всех)

    А ресурсов хватит?
    Конечно, у телефона ресурсов как у ноута, но и на ноуте на CPU подтупливает знатно.


  1. venanen
    09.12.2024 09:02

    В разжатом виде на каждый параметр приходится по 16 бит, и, как следствие, модель 8B весит 16 ГБ. Используя стандартные 4-битные методы сжатия, такие как nf4, можно сжать её до 4 ГБ. В этом проекте я использую экстремальное сжатие в 2 бита, сжимающее тело модели в 8 раз. Для слоёв головы и эмбеддингов я всё ещё использую 4-битное и 8-битное сжатие, поэтому модель получается размером в районе 2,5 ГБ.

    Ну, думаю, чудеса. 8b которая так себе работает даже на довольно мощном проце, а тут - браузера. А чудес то никаких не произошло, просто ловкость рук - назвать квантование aka урезание точности - сжатием, хотя это совсем не одно и то же. Сжатие подразумевает обратное разжатие либо без потерь (zip, gzip, tar, etc), либо без значимых в общем смысле потерь (png, mp3, jpeg, etc). А модель при квантовании до 2 бит меняется катастрофически.


    1. galqiwi Автор
      09.12.2024 09:02

      Квантизация моделей по сути своей очень похожа на тот же jpeg. Всегда можно сжать в большее количество раз за счёт качества. Поэтому методы сжатия оцениваются с точки зрения парето оптимальности кривых качества против размера.

      При этом при сжатии модель меняется, но не катастрофически. Метрики сжатой в 2 бита ламы можно посмотреть тут. Сжатая Llama3.1-8B работает лучше оригинальной Llama2-7B.


  1. mitroshenkovaa
    09.12.2024 09:02

    Сейчас в современных процессорах практически всегда есть GPU, конечно, не везде это отдельный GPU nVidia. Сейчас в некоторых процессорах есть и NPU


  1. orbion
    09.12.2024 09:02

    Отличный проект, спасибо! В своё время впечатлил ещё whisper / whisper.cpp, у которого также есть веб-версия.
    Из предложений — вероятно, перед загрузкой стоит предупреждать о размере модели, т. к. не у всех безлимитный трафик.


  1. Proger3301
    09.12.2024 09:02

    Впервые такое вижу. Отличная работа! Помню интереса ради запускал LLMки на своем телефоне через termux. Довольно интересный опыт. Теперь, как я вижу, это можно делать через браузер.


  1. HLudens
    09.12.2024 09:02

    занятно, тут на днях опубликовали сеть Cotype-Nano так она весит меньше гигабайта (4бита квантование) и вполне вменяемо отвечает, интересно если ее вашим методом сжать что получится?


  1. ashorror
    09.12.2024 09:02

    Есть давно такая штука - Web-LLM, она так же давно 8b модели в браузере запускает.

    Я вернусь к вам с другой полезной информацией позже.


    1. galqiwi Автор
      09.12.2024 09:02

      Она не работает без webgpu и использует менее качественную квантизацию