Привет, Хабр! На связи Фёдор Пахуров и Настя Изюмова. Мы команда внутренних инициативных сервисов ecom.tech. Занимаемся разработкой продуктов для внутренних нужПривет, Хабр! На связи Фёдор Пахуров и Настя Изюмова. Мы команда внутренних инициативных сервисов ecom.tech. Занимаемся разработкой продуктов для внутренних нуж

Генерируем SQL–запросы на локальных моделях

9м. чтение

Привет, Хабр! На связи Фёдор Пахуров и Настя Изюмова. Мы команда внутренних инициативных сервисов ecom.tech. Занимаемся разработкой продуктов для внутренних нужд компании, экспериментируем с внутренними AI-продуктами для сотрудников: от забавных и полезных ботов до автоматизации повседневных задач. Сегодня мы расскажем, как помогли разгрузить аналитиков от их типовых задач с помощью искусственного интеллекта – внутри реальные кейсы, гайды и пошаговые инструкции на примере ecom.tech. Поехали!

5cf8776d68038b2c082032df4dfc6568.jpg

Для успешной автоматизации понадобится простой

У аналитиков есть много рутинных задач на однообразную ad hoc аналитику, которая отнимает их ценный ресурс. Решением проблемы является бот-аналитик. Это продукт на основе LLM, который получает вопрос на естественном языке (Например: Сколько сырков заказали в Питере?), сам ходит в базу данных и возвращает в ответ красивую цифру. Всё это происходит за считанные секунды. Ситуация win-win: аналитики делегировали рутину и могут заниматься интересными задачами, а менеджмент мгновенно получает релевантную статистику.

На этапе PoC бот работает следующим образом: мы получаем вопрос пользователя текстом, переводим его в SQL запрос с помощью нейросети, затем выполняем его в изолированной базе данных с синтетическими данными, после чего получаем понятный результат исполнения запроса, написанный на русском языке с пояснениями. Казалось бы, все здорово, работает хорошо, давайте развернем на настоящей базе данных и запустим продукт.

828ca61ecde829f227c9a2eacc943cbb.jpg

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

Ни одна служба безопасности не пропустит подобное решение для работы на настоящих данных. Но выход есть: переезд на open-weight модели. Это позволит иметь свою собственную модель прямо во внутреннем контуре. Вы будете полностью контролировать ее, а ваши данные будут в безопасности. И мы начали изучать, что есть в открытом доступе, подходящего под нашу задачу.

Нам нужна была такая модель, чтобы она одновременно умела хорошо понимать текстовые вопросы пользователя, генерировать связный и интересный текст ответа и SQL-запросы по тексту. Среди помещающихся на одну видеокарту моделей, которые выдавали бы качество, сравнимое с популярными нейросетями, найти одну было невозможно, поэтому мы выбрали две: первая для генерации текста, вторая для генерации SQL-запросов.

Генерация SQL

Для задачи генерации SQL-скрипта по текстовому запросу мы сначала изучили бенчмарки под задачу генерации кода: MMLU – на логику, HumanEval, MBPP – на написание кода, и, самое важное, BIRD, SPIDER, PAUQ – task-specific бенчмарк для задачи написания SQL-запросов на разных схемах.

В открытых источниках публикуют только метрики, на которых их модель обходит конкурентов, и те зачастую немного завышенные (а значительная часть результатов вообще скрыта). Также по каждой модели можно найти несколько разных результатов на одном и том же бенчмарке, поэтому мы отталкивались от наименьшего найденного нами скора. Преимущественно обращали внимание на HumanEval, как на самый свежий и еще не сильно утекший (на момент июня 2024 года, занятные детали тут) в обучающие выборки, и на SPIDER – оригинальный бенчмарк, проверяющий SQL-навыки модели.

Для проверки качества моделей на наших схемах мы собрали вручную около 50 вопросов пользователей по данным и корректных ответов на них продукта.

Вопрос

Идеальный
запрос

Запрос кандидата

Ответ идеального запроса

Ответ кандидата

Флаг корректности ответа

GMV 2 мая

SELECT

SUM(gmv_field) AS gmv

FROM db.gmv

WHERE order_datetime = date '2024-05-02'

SELECT
SUM(gmv_field) AS gmv
FROM db.gmv
WHERE order_datetime = date '2024-05-02'

12345678900

1.23456789e+10

TRUE

Затем прогнали модели на этой выборке и проверили руками результаты исполнения SQL-запросов моделей. Такой метод валидации был выбран потому, что на один и тот же вопрос можно правильно ответить разным количеством по-разному названных колонок с разными форматами данных, но при этом ответ будет содержательно верным.

Например, для вопроса «В какой день было выполнено минимальное количество доставок за апрель» справедливы следующие SQL запросы:

SELECT

order_datetime_end,

COUNT(DISTINCT delivery_id) AS delivery_count

FROM db.gmv

WHERE order_datetime_end >= '2024-04-01'

AND order_datetime_end < '2024-05-01'

AND completion_status = 1

GROUP BY order_datetime_end

ORDER BY delivery_count ASC

LIMIT 1

SELECT

order_datetime_start

FROM db.gmv

WHERE EXTRACT(MONTH FROM order_datetime_start) = 4

AND EXTRACT(YEAR FROM order_datetime_start) = 2024

GROUP BY order_datetime_start

ORDER BY COUNT(DISTINCT delivery_id)

LIMIT 1

WITH deliveries AS (SELECT order_datetime_start, COUNT(DISTINCT delivery_id) AS delivery_count FROM db.gmv WHERE EXTRACT(MONTH FROM order_datetime_start) = 4 AND EXTRACT(YEAR FROM order_datetime_start) = 2024 GROUP BY order_datetime_start) SELECT order_datetime_start FROM deliveries ORDER BY delivery_count ASC LIMIT 1;

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

Но когда мы стали проверять время работы модели, возникла проблема. При тестировании мы использовали transformers и bitsandbytes int8 квантизацию. Работало это решение очень долго: 2 минуты против 2 секунд. Мы не могли позволить, чтобы пользователь ждал так долго. Нам хотелось сократить время ожидания генерации SQL запроса до 10 секунд.

Решение было найдено быстро. Нам помогла библиотека для инференса больших языковых моделей vLLM с оптимизированными CUDA-ядрами, continuous batching’ом и механизмом Paged Attention. Это позволило нам сократить время ожидания генерации SQL запроса до 3 секунд, что было чистой победой.

Деплоили в тесной кооперации с коллегами из MLOps. В продуктив пошла связка vLLM + кастом обертка Triton (почему именно он – хорошо объяснили коллеги-продуктивизаторы: ссылка на пост).

Квантизация

Наши выбранные модели были изначально довольно большими и не помещались в одну карту А100. Из вариантов решения мы видели дистилляцию знаний и квантизацию. Для дистилляции необходимо запускать модель в полном разрешении весов, что было невозможно с нашими доступными ресурсами. Единственным выходом для нас стала квантизация. Давайте разберем, как это работает.

Модели выше мы тестировали в режиме динамической квантизации 8bit с помощью конфигурации bitsandbytes, что сильно сказывалось на скорости инференса, а выкатывали уже статические квантизации FP8_E4M3. Но как это работает и причем тут Game Boy?

На картинке мы видим довольно стандартный FP32, который можно обозначить как E8M23, то есть, на экспоненту отводится 8 бит, а на мантиссу – 23. Из того же примера видно, что точность мантиссы избыточна. Да и экспоненту, на самом-то деле, можно практически без потерь подрезать (по крайней мере, мы значимых изменений в качестве не обнаружили). Поздравляю, мы изобрели квантизацию :)

Схожий трюк можно проделать и для получения целочисленных весов: для этого нужно отобразить веса из множества действительных чисел в целочисленные представления (осуществить rescaling весов). Есть несколько вариантов, какие диапазоны и во что отображать, но нам достаточно найти максимум для возможных принимаемых значений нашего веса и преобразовать его в диапазон [-max, max], но уже в целых числах. Полученный формат хранения весов чуть менее точен, но занимает кратно меньше драгоценной памяти GPU.

Также стоит отметить, что квантовать можно online и offline. Онлайн (динамическая) квантизация считает максимальное принимаемое значение веса во время инференса, что требует значительных дополнительных затрат по времени. Решение – offline (статическая) квантизация, в которой диапазоны определяются на калибровочном датасете, и в инференсе сразу используются полученные параметры квантизации без дополнительных runtime-затрат.

Обычно квантизуются веса полносвязных слоев, но не байесы/нормализации/активации, чтобы не терять точность на чувствительных этапах, добавляющих нелинейность или тонко настраивающих смещение данных. А Game Boy тут при том, что он тоже 8-битный :)

источник
источник
источник
источник
c413b0ed1c03eee3e10aa621a78ffbe0.jpg

Генерация текста

Возвращаемся из квантовой теории к практическому выбору LLM. Для подбора модели для задачи генерации текста мы смотрели на бенчмарки на русском языке, такие как MERA, RuLLMArena и ru-arena-general.

Брали топ моделей на general-knowledge бенчмарках, потому что кандидату предстояло решать довольно разноплановые задачи от категоризации исходного вопроса пользователя до выделения фильтров в SQL-запросе. Дополнительно мы обращали пристальное внимание на размер модели, так как у нас было дополнительное ограничение от бизнеса: модель должна была помещаться и работать на одной карте А100.

После ресерча мы отобрали 20 моделей – первичных кандидатов, которые проверяли на синтетически сгенерированных выборках. Всего было порядка семи выборок по сто вопросов в каждой. Данные выборки покрывали случаи ответа на вопрос, определения тегов и другие подзадачи, которые были нам нужны для успешной работы пайплайна ответа на вопрос, не считая саму генерацию SQL запроса.

d2135911361424e20fcea184234ff2aa.jpg

Кроме этого, был написан валидационный пайплайн, в котором можно запустить валидацию сразу по многим моделям и проанализировать метрики. Это позволяет одной командой автоматически запустить валидацию на нужных данных, если мы захотим в будущем проверить еще какие-то модели.

Мы написали промпт для каждой из задач, проверили 20 моделей-первичных кандидатов на них и подсчитали метрики качества. В качестве метрик мы использовали F1, Precision, Recall для задач классификации (например, определения тэгов в вопросе), а также LLM-as-Judge, AnswerRelevancy и кастомная метрика определения корректности для задач генерации текста и понимания вопросов пользователя.

Для последних двух мы воспользовались библиотекой deepeval. Это библиотека для оценки генерации LLM моделей с помощью LLM-судьи. Она использует reasoning для того, чтобы решить, совпадает ли ответ модели с образцовым ответом, в зависимости от описания метрики, и выдает оценку точности и описание, почему она так считает (не путать с reasoning в OpenAI моделях).

По итогам первого отбора от 20 кандидатов у нас осталось 8 самых качественных. Чтобы повысить получаемые метрики, мы занимались prompt-инженерингом. Так как подбирать промпты руками чаще всего больно, долго и не очень эффективно, мы использовали LLM арену в режиме Battle. Как работали с ареной: описывали задачу, давали промпт, просили улучшить какую-то часть или метрику. Например:

Есть промпт, который генерирует ответ для пользователя:
<ТУТ САМ ПРОМПТ>
Улучши промпт, потому что текущий недостаточно харизматично отвечает и обрезает ссылки.

Таким образом, нам удалось повысить метрики на всех 8 моделях. Но у нас все еще не было ситуации, когда одна модель побеждает все остальные во всех задачах. Поэтому, посмотрев дополнительно на время инференса через vLLM (это фреймворк, на котором мы запускали все модели), и проранжировав модели по всем задачам, мы выбрали победителя. Победившей оказалась модель из семейства Gemma. Более подробно про подбор промптов можно прочитать в канале коллег.

63f1a13772ee09a4d1546d42d4a95b06.png

По итогам исследования, определились две модели, которые мы можем использовать, как замену популярной нейросети и продолжить развивать наш продукт. Модели на замену:

  • для задачи понимания и генерациии текста: модель из семейства Gemma

  • для задачи text2SQL: модель из семейства Mistral.

Пара слов о безопасности

В ходе проекта мы выяснили несколько вещей:

  1. Можно найти open-weight модели, которые решают задачу хорошо, но в разы безопаснее.

  2. Подбор промптов реально работает.

  3. Бенчмарки удивительно быстро утекают. Им нельзя верить. Всегда проверяйте качество моделей на своих данных.

  4. Всё получится, особенно, если у вас есть примеры для модели и 1-2 карточки.

Не бойтесь переходить на локальные LLM – это не так страшно, как кажется. Особенно сейчас. Переход на open-weight модели сегодня позволит избежать утечки информации сегодня вечером :)
В дальнейшем мы планируем развивать тему агентов и прикладывать решение к новым доменам. Например, сейчас мы активно помогаем HR быстро "подбивать" нужную прямо сейчас аналитику.

Источник

Отказ от ответственности: Статьи, размещенные на этом веб-сайте, взяты из общедоступных источников и предоставляются исключительно в информационных целях. Они не обязательно отражают точку зрения MEXC. Все права принадлежат первоисточникам. Если вы считаете, что какой-либо контент нарушает права третьих лиц, пожалуйста, обратитесь по адресу service@support.mexc.com для его удаления. MEXC не дает никаких гарантий в отношении точности, полноты или своевременности контента и не несет ответственности за любые действия, предпринятые на основе предоставленной информации. Контент не является финансовой, юридической или иной профессиональной консультацией и не должен рассматриваться как рекомендация или одобрение со стороны MEXC.