Привет! Я Настя Рысьмятова, руковожу командой LLM в Авито. Эта статья — про то, какие задачи мы решаем с помощью языковых моделей и как адаптируем их под себя. Мой опыт будет интересен прежде всего тем, кто тоже занимается большими языковыми моделями в крупных продуктовых компаниях. А всем остальным любопытно будет узнать, как модели учатся и решают конкретные задачи Авито — например, помогают пользователям писать тексты объявлений.

Что внутри статьи:

Какие задачи мы решаем с помощью LLM

Как мы обучаем свою языковую модель

Три причины, почему мы решили сделать свой претрейн

Что в итоге

Выводы и планы

Какие задачи мы решаем с помощью LLM

Некоторые решения мы уже внедрили, другие проходят A/B-тесты.

Генерация описания объявления. Когда вы подаете объявление на Авито, сначала нужно написать заголовок, загрузить фото и указать параметры товара, а потом — придумать описание. Многие пользователи не знают, что в нём написать, и в итоге там оказывается что-то не очень информативное. 

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

Модель уже работает в нескольких категориях сервиса:

  • «Одежда, обувь и аксессуары»;

  • «Велосипеды»;

  • «Охота и рыбалка»;

  • «Музыкальные инструменты»;

  • «Спорт и отдых».

Мы провели A/B-тест, а ещё спрашивали пользователей, понравилось ли им сгенерированное описание. Вот результат:

Удачные описания помогают продавать товары. А 60% продавцов отметили, что сгенерированные тексты им понравились
Удачные описания помогают продавать товары. А 60% продавцов отметили, что сгенерированные тексты им понравились

Извлечение параметров объявления. На Авито появились мультиобъявления. Они работают так: если у продавца несколько предложений с разными вариациями одного и того же товара, их теперь можно посмотреть внутри одного объявления. Например, если человек продаёт пять конфигураций одного ноутбука, то теперь не нужно переключаться между разными объявлениями — в каждом будет видно весь ассортимент. Это удобно для покупателей. 

Так выглядит мультиобъявление: все вариации товара остаются отдельными объявлениями, но переключаться между ними не нужно, весь ассортимент видно сразу
Так выглядит мультиобъявление: все вариации товара остаются отдельными объявлениями, но переключаться между ними не нужно, весь ассортимент видно сразу

Наши алгоритмы автоматически группируют объявления в мультиобъявления. Для этого нам нужно знать модель товара, но в объявлении этот параметр «модель» необязательный — пользователи не всегда его заполняют.

Мы обучили LLM-модель, которая достает модель товара из заголовка и описания объявления. Это не простая задача классификации: например, продавец может придумать свою модель товара, если сам её производит.

Допустим, пользователь продает футболку Nike — и модель нашла, что это за товар.

Моделька поняла, что перед нами объявление с футболкой Travis Scott Cactus Jack
Моделька поняла, что перед нами объявление с футболкой Travis Scott Cactus Jack

А вот пользователь продает комод «Мальта» — и модель понимает, что это комод «Мальта».

Модель справляется не только с одеждой, но и с мебелью
Модель справляется не только с одеждой, но и с мебелью

Есть более интересные случаи. Например, пользователь продает костюм Adidas — LLM тоже находит модель этого костюма.

Ещё один пример работы LLM
Ещё один пример работы LLM

Другие задачи, над которыми работаем:

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

Суммаризации отзывов в Услугах. На Авито пользователи, размещающие товары имеют рейтинг и иногда тысячи отзывов. Чтобы их прочитать, нужно потратить много времени. Мы хотим сделать модель, которая будет суммаризировать отзывы и выдавать пользователям самое важное.

Подсказки пользователям в мессенджере Авито. В некоторых ситуациях можно подсказывать продавцу или покупателю, что лучше написать.

Сейчас выходит много опенсорсных моделей, которые и так хорошо справляются с задачами. И каждая новая модель бьёт все предыдущие. А вот создать свой претрейн очень дорого: нужно взять большую модель, терабайты данных и обучить модель на задачу next token prediction. Это может занять несколько дней или даже недель — в зависимости от количества данных, размера модели и количества GPU-карт. 

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

Три причины, почему мы решили сделать свой претрейн

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

  • Токенизатор опенсорсных моделей хуже адаптирован для русского языка. Такие модели будут выдавать значительно больше токенов, чем токенизатор, который вы обучите на русскоязычных текстах. Это влияет на скорость: модель с вашим токенизатором будет работать в 1,5-2 раза быстрее.

  • Опенсорсные модели плохо работают в домене Авито и плохо справляются с нашими задачами. 

Метрики опенсорсной модели на наших задачах. Мы взяли популярные опенсорсные модели Mistral-7B-v0.1 и Mistral-7B-Instruct-v0.1 и исследовали их качество на наших бенчмарках. Далее опишу часть метрик из них.

MMLU — это общепринятая метрика, её всегда упоминают, когда говорят о больших языковых моделях. MMLU содержит 16 тысяч вопросов по 57 темам: алгебре, геометрии, физике, химии, менеджменту, биологии и так далее. Встречаются непростые вопросы — даже если сами захотите на них ответить, не факт, что получится сделать это во всех случаях.

К каждому вопросу есть четыре варианта, а дальше написано «Ответ:». Модели необходимо сгенерировать следующий токен — ответить на этот вопрос. Мы считаем долю правильных ответов. MMLU мы замеряем на русском и английском языках.

Пример вопроса по менеджменту
Пример вопроса по менеджменту
Вопрос по абстрактной алгебре
Вопрос по абстрактной алгебре

Мы решили сделать подобные задачи в домене Авито:

Метрика Moderation. У нас есть большая выборка объявлений с известными причинами нарушений или, наоборот, совсем без них. Мы перечисляем модели возможные причины нарушений и спрашиваем, есть ли они в тексте объявления.

Другая похожая метрика — Moderation review, где проверяются не объявления, а отзывы пользователей.

Задачка на поиск нарушений в тексте объявления
Задачка на поиск нарушений в тексте объявления

Метрика Relevance. У нас есть данные поисковых запросов в Авито, и данные о заголовках, которые показывались по этому запросу. Модели необходимо определить, релевантен ли заголовок объявления поисковому запросу.

Задачка на релевантность
Задачка на релевантность

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

Результаты на русском языке оказались не очень
Результаты на русском языке оказались не очень

Как мы обучаем свою языковую модель

Мы думали, с чего начать, чтобы сделать свою базовую модель. Исследовали много материалов: наткнулись на статью про continual pre-training, и решили попробовать что-то подобное. 

Добавили в модель русскоязычные данные. Мы собрали, как нам тогда казалось, очень много данных. В основном – русскоязычные данные из опенсорсных корпусов. Также мы добавили данные Авито. Всего получилось 1,5 терабайта данных, которые после дедубликации и фильтрации превратились в 1,1 терабайта.

Структура наших данных
Структура наших данных

Дальше мы взяли Mistral-7B-v0.1 и дообучили её на этих данных на задачу next token prediction. Нам было доступно 72 GPU A100 80GB на ML Space. Одна эпоха обучения длилась 15 дней.

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

Взрывы видны на графике
Взрывы видны на графике

После всего этого получилась моделька, которую мы назвали Adaptive Mistral 7B. У неё метрики MMLU на русском языке лучше. При этом MMLU на английском языке немного просело. Но у нас русскоязычный домен, поэтому мы решили, что ничего страшного в этом нет.

Результаты на русском стали заметно лучше
Результаты на русском стали заметно лучше

Дальше мы проделали SFT-этап — так обычно делают после обучения претрейна. В SFT-этапе много данных в формате задания к модели: вопрос и ответ. Модель дообучается на этих вопросах-ответах. 

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

Заменили токенизатор. Выше я писала, что у опенсорсных моделей токенизатор лучше адаптирован к английскому языку, чем к русскому. Как можно в этом убедиться? Мы взяли токенизатор Mistral и обучили свой на большом корпусе русскоязычных данных. После обучения среднее число символов в токене для русских текстов увеличилось с 2,1 до 3,3. То есть, перейдя на новый токенизатор, мы бы смогли ускорить инференс в среднем в 1,5 раза.

Это очень здорово, но непонятно, как заменить токенизатор. Ведь сначала обучается он, а потом уже вся модель, и его нельзя просто взять и выкинуть. Так мы думали, пока не прочитали несколько статей об этом. Например, ребята из МГУ адаптировали разные опенсорсные модели под русский язык: поменяли токенизатор и показали, что качество не проседает, а скорость увеличивается. 

Мы решили сделать что-то подобное. Основная идея упомянутой выше статьи в том, что нужно обучить новый токенизатор, а дальше подставить его к сети и переделать embedding-слой. То есть инициализировать его по-новому: берём токены от нового токенизатора, токенизируем их старым токенизатором, получаем их эмбеддинги и усредняем их. И это будет эмбеддинг в новой сети. Потом нужно заморозить всю сеть, кроме embedding-слоев, и дообучить на большом корпусе данных — примерно в 100 ГБ.

Мы это проделали, но решили не останавливаться: разморозили все веса сети и дообучили ещё раз на этих же 100 ГБ данных. Получились модели, которые я назвала Adaptive Mistral 7b New Tokenizer и Adaptive Mistral 7b New Tokenizer Adaptive. Слово Adaptive в конце — потому что мы снова разморозили все веса и снова дообучили на русских данных.

В финале мы ещё проделали SFT-этап на тех же данных, на которых делали раньше, просто с адаптированной моделью.

Что в итоге

Мы получили практически те же самые метрики, которые были у обычной адаптированной Мистрали. При этом у нашей модели новый токенизатор — и скорость инференcа выросла в среднем в 1,5 раза. На данных Авито даже больше, потому что при обучении токенизатора использовали много наших данных.

Новые модели появляются всё время. Например, недавно вышла Lama 3-8B, у которой метрики уже получше, чем у Mistral-7B-v0.1. И мы можем также адаптировать эту модель под наш домен.

Результаты с новым токенизатором
Результаты с новым токенизатором

Выводы и планы

Оказывается, можно довольно дешево получать адаптированный LLM под ваш домен — использовать подход continual pre-training и прикручивать новый токенизатор. Это позволяет растить метрики языковой модели внутри вашего домена. 

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

Бывает, выходит модель, вокруг которой много шума: говорят, что MMLU практически единица. Но мы измеряем наши метрики и понимаем, что модель использовать не стоит. У нас огромная таблица с практически всеми опенсорсными моделями. 

В будущем планируем провести ещё несколько экспериментов с адаптированием токенизатора — возможно, расскажем о них в следующих публикациях.

Спасибо вам за уделенное статье время! На вопросы готова ответить в комментариях, там же можно поделиться вашим собственным опытом доработки LLM.

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


  1. azzas
    24.10.2024 08:20

    Вы бы лучше точный адрес продавца вернули в объявление, сделали чтобы область поиска не сбрасывалась после ввода поискового запроса, откровенно мошеннические и спамерские объявы блокировали, аккаунты не блочили на ровном месте.

    А вот эта балалайка для сайта с объявлениями нафиг не нужна.

    Стойкое ощущение что толпе разработчиков просто надо давать какие то задачи вот и появляется такой "функционал", чтобы не сидели без дела.

    С каждым обновлением что приложения что сайта становится все менее удобно пользоваться.


    1. troublehabr
      24.10.2024 08:20

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

      А про то, что «разработчикам надо делать задачи» — мне оч нравится автозаполнение, удобная штука, особенно когда много объявлений выкладываешь.


  1. alexxxdevelop
    24.10.2024 08:20

    Спасибо за полезную статью. Ничего не понятно конечно, но очень интересно. Это из-за отсутствия опыта у меня по нейронным сетям. А в качестве отправной точки статья вполне годится как инструкция.

    Вопрос: для обучения модели вы использовали свои наработки на питоне или какие-то готовые инструменты?


    1. Anastasiya_Rysmyatova Автор
      24.10.2024 08:20

      Спасибо! Для обучения моделей мы используем различные OS библиотеки, например transformers. Часть кода дописываем, переопределяем некоторые методы.


  1. DSkorinkin
    24.10.2024 08:20

    Например, пользователь продает костюм Adidas — LLM тоже находит модель этого костюма.

    У вас на картинке при этом написано "Модель нашла **Слово пацана** " :) Это такая переменная для артикула этого конкретного костюма или что? Она точно нашла?


    1. Anastasiya_Rysmyatova Автор
      24.10.2024 08:20

      LLM решила, что модель данного костюма "Слово пацана".

      LLM научилась вытаскивать название товара, которое указал пользователь. В данном объявлении с костюмом Adidas не была указана модель товара, но пользователь сам придумал название этому товару и LLM смогла его определить :)


  1. d00m911
    24.10.2024 08:20

    А настолько ли критично было дообучать и использовать 7b модели вместо, скажем, новых моделей qwen 2.5, которые гораздо лучше владеют русским языком?


    1. Anastasiya_Rysmyatova Автор
      24.10.2024 08:20

      Когда дообучали qwen 2.5 еще не вышла. А так постоянно выходят новые модели, но их тоже можно адаптировать под наш домен и подменить токенизатор.

      Даже если модель хорошо владеет русским языком, подмена токенизатора позволяет ускорить инференс. А это очень важно в нашем случае.


  1. ENick
    24.10.2024 08:20

    На каком железе модели запускали? Какие временные затраты?


    1. Advisory
      24.10.2024 08:20

      Так написано же: "Дальше мы взяли Mistral-7B-v0.1 и дообучили её на этих данных на задачу next token prediction. Нам было доступно 72 GPU A100 80GB на ML Space. Одна эпоха обучения длилась 15 дней."

      Не знаю сколько стоит для внешних клиентов аренда GPU на платформе ML Space (это облачная платформа для машинного обучения от Сбера), но средние коммерческие расценки $2-4 в час в зависимости от провайдера.

      Если сделать приблизительный расчет:
      72 GPU × 24 часа × 15 дней × $3/час (среднее) = около $77 760 за одну эпоху обучения по коммерческим расценкам облачных провайдеров.

      "Оказывается, можно довольно дешево получать адаптированный LLM под ваш домен — использовать подход continual pre-training и прикручивать новый токенизатор. Это позволяет растить метрики языковой модели внутри вашего домена."


  1. Graid
    24.10.2024 08:20

    А экономику кто-то считал? Ну вот инференс в полтора раза дороже и медленнее, но модели сейчас клепают как пирожки, и через месяц выйдет модель, бьющая все усилия разработчиков и компьют на претрейн. Успело вложенное окупиться за этот месяц или стоило потратить больше на более крупные и "умные" модели, а потом свичнуться на новую модель, не повторяя все заново и не отставая всегда на поколение?