Опыт Звука: как реализовать рекомендательную систему аудиокниг с использованием больших языковых моделей (LLM)
Всем привет! На связи Дмитрий Берестнев, Chief Data Scientist в HiFi-стриминге Звук. Сегодня я расскажу о том, как мы реализовали систему подбора аудиокниг и зачем это вообще было сделано. В статье мы фокусируемся на принципе рекомендации похожих книг (а подходы для авторов в нашем случае были сделаны аналогично).
С чего начинали
Исторически сложилось так, что в Звуке много музыкального контента, а в один прекрасный момент к нему добавились еще и аудиокниги. Естественно, перед командой встала задача рассказать о новом контенте пользователям так, чтобы это было понятно и удобно. Каноническим решением этого вопроса являлось создание отдельной рекомендательной системы, чем мы и занялись.
Стоит отметить, что в процессе работы нам нужно было решить сразу две задачи: разобраться с рекомендацией похожих книг, и с персонализированными рекомендациями книг. В первом случае было важно подбирать книги и авторов, похожих на другие книги и авторов, а во второй — ключевым стал подбор релевантных книг и авторов для конкретного пользователя.
Как мы выявили и решали задачу
Может показаться, что решить первую нашу задачу с похожими книгами проще, однако, это не совсем так. Если мы хотим уйти от пользовательских предпочтений и считать похожими не те книги, которые читают друг за другом, а отталкиваться от их внутреннего содержания, то подход на основе пользовательских интеракций будет не совсем уместным. Следовательно, нам нужно анализировать непосредственно содержимое книги. Если текст книги мы превратим в вектор-эмбеддинг, то по полученной косинусной мере близости созданных векторов уже можем находить похожие книги, выбирая их с максимальной мерой близости.
Первая проблема, с которой мы столкнулись, — книги довольно большие и не всегда у них есть краткое описание. Следовательно, нужно было учиться анализировать весь текст книги (в тех случаях, когда он был). Поскольку текст большой, его не очень удобно анализировать с помощью классических языковых моделей типа BERTa, так как они позволяют за раз анализировать не более нескольких тысяч символов, что соответствует примерно 1–1,5 страницам текста. А если разделить книгу на части (и таких частей будет более 100), то не очень очевидно, как собрать эффективный вектор книги, кроме простого усреднения векторов ее частей. А в случае большого количества частей такое действие вообще будет неэффективно, поскольку мы можем столкнуться с эффектом так называемой «средней температуры по больнице», которая в нашем случае может приводить к тому что все длинные книги будут похожими. Для решения этой проблемы мы использовали подход на основании больших языковых моделей (LLM), которые могут суммаризовать большой текст (выделить наиболее важные смысловые части). На основании полученного саммари можно построить вектора через BERT-подобные модели или сразу превратить исходный текст напрямую в вектор.
Для получения саммари по книгам использовались опенсорсные LLM модели Qwen-2/2.5 от Alibaba. Они имеют очень большую длину входного контекста в примерно 128 тысяч токенов, что в 70% случаев позволяет загрузить книгу полностью, а в оставшихся 30% — большую ее часть.
Вторая проблема возникала, если у нас не было ни кратких описаний книг, ни их текстов. Когда текст книги нельзя найти в интернете в свободном доступе, то единственным решением является извлечение текста непосредственно из аудио. Для этого мы использовали собственную разработку speech2text, построенную на базе модели Whisper3, но с применением VAD (voice activity detector) и обработчика ошибок после генерации, извлекающего текст из аудио. Далее извлеченный текст обрабатывался с помощью LLM для получения саммари книги.
Поскольку перечисленные задачи достаточно большие и сложные, а для решения каждой нужна специализация, они были поделены внутри на 3 различные ML-команды: ML Recsys, ML Research и ML Audio соответственно.
Как отсортировывался похожий контент/книги?
В случае с рекомендацией похожих книг мы использовали контентные векторы, полученные на основании каскада моделей по извлечению текста, суммаризации, создания векторов и отбора кандидатов по косинусной мере близости векторов (чем больше мера, тем книги релевантнее).
В случае с персонализированными рекомендациями мы использовали векторы предпочтений user-item, полученные из матрицы взаимодействия между пользователями и просмотренными ими книгами. Мы знали, что векторы пользователей и книг получены в одном векторном пространстве, поэтому искали для вектора первых наиболее близкий по косинусной мере вектор книги. И так получали релевантные персонализированные рекомендации.
Основные инструменты для работы
Весь сервис писался на Python.
Для хранения всех векторов мы использовали векторную базу (в нашем случае Qdrant). Ее устройство позволяет индексировать данные таким образом, чтобы алгоритмическая сложность поиска была o (N) операций, вместо o (N^2) как для классической реляционной БД.
Для батчевого исполнения LLM моделей (в нашем случае это собственная SFT модель на базе e5-mistral-7b-instruct для формирования векторов из саммари или краткого описания книги и Qwen/Qwen2.5–7B-Instruct-AWQ для формирования саммари из текста книги) и моделей speech2text (Whisper 3 версии large) использовалась наша внутренняя ML платформа. В ней в AirFlow был развернут пайплайн подготовки данных с использованием GPU (для управления очередью расчетов и оптимизации использования видеопамяти мы применяли Clear ML сервер и технологии разбиения GPU-MIG). В части хранения исходных данных — мы использовали hadoop и S3. В части обработки и подготовки данных — presto и pyspark.
Как связали все в единое целое
Наш итоговый пайплайн решения для рекомендаций похожих книг выглядит следующим образом (см. рисунок):
Если у нас есть краткое описание книги, мы подаем его на вход в модель LLM-encoder e5-mistral-7b-instruct и получаем вектор краткого описания книги.
Если краткого описания нет, но есть сам текст книги, тогда создаем саммари с помощью Qwen2.5–7B-Instruct-AWQ, после чего прогоняем его через e5-mistral-7b-instruct и получаем нужный нам вектор.
Если нет ни краткого описания, ни текста книги, то сперва работаем со связкой Whisper3+VAD+spelling checker для извлечения текста, а после применяем подход из предыдущего пункта.
Поскольку финальная модель для генерации вектора одна и та же (это e5-mistral-7b-instruct), то все векторы получаются в одном векторном пространстве и их можно сравнивать.
Загружаем полученные векторы контента книг в векторную базу, после чего формируем список похожих друг на друга книг на основании векторной меры близости векторов.
Отдельно хочу отметить, что ради эксперимента мы пробовали спрашивать напрямую у нейросетей саммари книг по их названию. Для книг, которые появились до момента выхода самой Chat-GPT4, результат получался очень хорошим. Но по такому же запросу для новинок сразу было видно, что модель начинает сильно галлюцинировать и выдает неадекватный саммари. Поэтому в нашем пайплайне мы не использовали генерацию содержимого книги по названию из-за потенциальной ненадежности такого метода.
В качестве пайплайна рекомендаций персонализированных книг мы выбрали довольно простой вариант:
Каждые X дней обновляли матрицу интеракций пользователей с книгами. Причем использовали как сам факт прослушивания, так и параметры (длительность, частота и тд). В процессе формирования матрицы проводили фильтрацию по аномалиям, связанными с прослушиванием.
На основании полученной матрицы формировали, применяя ALS разложение, векторы для пользователей и книг (в едином векторном пространстве).
Загружали полученные векторы пользователей и книг в векторную базу и формировали персонализированные рекомендации на основании векторной меры близости вектора пользователя и полученных векторов книг (естественно, при этом человеку не рекомендуется уже просмотренное).
Проводилась ли верификация извлеченных саммари
При создании саммари на основании LLM мы подавали на вход или исходный или извлеченный текст, при этом качество саммари могло варьироваться. Для оценки вариативности качества суммаризации мы формировали случайным образом выборку для верификации нашими музыкальными/литературными экспертами, в рамках которой они давали свои заключения по соответствию саммари исходному тексту, а мы считали потенциальные ошибки.
В целом процент генерации неадекватного саммари для последних версий LLM моделей (а пользуемся мы именно последними) очень низкий (менее 5%).
Итог работы
В итоге у нас получился многоступенчатый пайплайн, справляющийся даже в самых сложных условиях (отработка только на основании исходной аудиозаписи) и формирующий векторы контента книг для рекомендаций похожих книг и векторы предпочтений пользователь-книга для персонализированных рекомендаций.
Качество полученных рекомендаций мы оценивали через месяц после запуска как с помощью специальных экспертов, так и с учетом действий пользователей.
Вот такие метрики у нас получились через месяц после запуска для сервиса.
«Похожие»:
92% — рекомендаций оценены экспертами как релевантные и очень релевантные.
Количество активных пользователей в неделю (WAU — week active users) выросло на +33,2%.
Количество активных дней в неделю, когда пользователь читает книгу (Active days count weekly) выросло на +8,2%.
«Для Вас»:
Количество активных пользователей в неделю (WAU — week active users) выросло на +11,6%
Количество активных сессий пользователя с персональными рекомендациями выросло на +25,7%.
Вот я и рассказал о процессе реализации системы подбора похожих аудиокниг в Звуке. Спасибо, что прочитали статью! Буду рад ответить на ваши вопросы в комментариях.
PS: Отдельное большое спасибо команде ML RecSys (Кудренок Любе и Здорову Филиппу), команде ML Research (Ринату Муллахметову и Ивану Сухареву) и команде ML Audio (Антону Нестеренко) за проделанную работу и помощь со статьей.