»Spotify Radio — это функция, которая создает список воспроизведения на основе любой выбранной вами песни, альбома, списка воспроизведения или исполнителя. Список воспроизведения также обновляется сверхурочно, чтобы оставаться свежим. Представьте, что вы не совсем уверены, что именно сейчас слушать, но знаете, в каком настроении вы находитесь. Просто выберите, скажем, песню, которая лучше всего передает это настроение, и запустите радио оттуда!»

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

Давайте сформулируем эту проблему.

Проблема

У Spotify 82 миллиона треков и 365 миллионов пользователей.

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

Учитывая пользователя, песню и контекст (время, место и время года), спрогнозируйте вероятность взаимодействия с каждым треком и упорядочите треки, используя эту оценку.

Общая архитектура:

С точки зрения архитектуры лучшим вариантом для нас является использование подхода с воронкой. Запуск сложного алгоритма ранжирования на более чем 82 миллионах треков был бы недостаточно эффективным по времени. Поэтому нам нужно запустить менее сложный алгоритм, чтобы отбросить нерелевантные элементы из списка треков. Давайте настроим очень простой алгоритм для поиска треков, которые выглядят близко к текущему треку (т.е. песня мы хотим сделать плейлист из).

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

Добавление шага фильтрации может быть интересным. Например, мы не хотим показывать плейлист, состоящий только из одного исполнителя или одного альбома. Добавление разнообразия в наши продукты может быть интересно для бизнеса, поскольку это может увеличить число открытий для пользователей, чего мы и ожидаем от Spotify Radio. Или, может быть, нет, обратитесь к части об AB-тестировании для этого.

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

Модель генерации кандидатов

На этом этапе мы хотим исключить из поиска нерелевантные треки. В обычной рекомендательной системе мы просто обучаем древовидную систему или логистическую регрессию, чтобы предсказывать вероятность взаимодействия и сохранять треки с самым высоким рейтингом (топ-500 или около того). Здесь нам нужно отобразить треки, которые как-то близки к исходному треку (тому, из которого мы строим радио).

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

Разработка функций:

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

Для этой простой модели давайте просто сосредоточимся на самой дорожке и некоторых метаданных.

Что касается самой дорожки, использование глубокой нейронной сети может быть способом создания вложений. Конечно, все вложения дорожек будут предварительно сгенерированы (помните, что мы делаем этот шаг простым).

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

Один из удивительных способов генерировать вложения из метаданных — использовать графики. В частности, графы свойств будут работать здесь очень хорошо. Вот пример очень упрощенного графика, связанного с треками Spotify. (Извините за недостающие края).

Отличным подходом к использованию этих связанных данных является встраивание узлов с помощью таких алгоритмов, как Node2Vec или GraphSAGE. Идея та же, что и в Word2vec, но здесь функции контекста генерируются случайным блужданием по графу от исходного узла (т. е. нашей дорожки).

Опять же, эти вложения должны регулярно предварительно вычисляться (например, с помощью AirFlow). Мы только хотим вычислить простую KNN в производственной среде.

Модель ранжирования

Наша ранее использовавшаяся модель сильно сократила набор кандидатов для нашего Радио. Это здорово, потому что это означает, что мы можем использовать более сложные модели, поскольку нам придется запускать их на меньшем количестве данных. Здесь мы хотим предсказать вероятность взаимодействия. Для этого мы можем использовать логистическую регрессию, решение на основе дерева или глубокую нейронную сеть.

Хороший способ продолжить — попробовать список моделей и оставить лучшие из них. Возможно, вы захотите сложить их позже или оставить только один из них.

Для этой задачи я бы выбрал несколько форсированных древовидных моделей (XGBoost, lightgbm, Catboost), модель логистической регрессии и DNN.

Разработка функций:

Здесь мы сможем использовать все данные нашего пользователя (при условии, что он вошел в систему, иначе мы можем создать другую модель, которая не будет зависеть от пользовательских данных), множество дополнительных разреженных данных, сгенерированные данные перекрестного отслеживания, пользовательский трек сгенерированные данные и, конечно, контекстные данные. Существует бесконечное количество дополнительных функций, которые мы можем добавить. Не стесняйтесь комментировать любые дополнительные идеи (так алгоритм ранжирования Medium ставит мою статью выше 👊).

Функции пользователя:

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

Пользователь для отслеживания функций:

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

Функции отслеживания:

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

Отслеживание функций:

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

Контекстные функции:

Лично мне очень нравится поп-панк, когда наступает лето. Я также интересовался бы музыкой кантри, если бы собирался в путешествие по США. Ну, вот почему функции контекста здесь. Чтобы придать контекст модели, она может порекомендовать нужные песни в нужный момент.

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

Генерация данных

Откровенный отзыв:

Явный отзыв соответствует отзыву, предоставленному непосредственно пользователем.

Мы можем визуализировать взаимодействие с контентом в виде бинарного вектора, где 1 соответствует треку, с которым взаимодействовал пользователь. Взаимодействием здесь может быть лайк трека или добавление трека в плейлист.

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

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

Использование неявной обратной связи:

Неявная обратная связь напрямую извлекается из взаимодействия пользователя с платформой. Здесь мы могли бы рассчитать оценку по нормализованному времени прослушивания трека. Добавление этого нового вида обратной связи позволит нам собрать гораздо больше данных и решить проблему MNAR (пользователь в основном взаимодействует с вещами, которые ему нравятся, и искажает набор данных). В этой области мы должны выбрать произвольные значения времени прослушивания для проверки и аннулирования дорожки. Вот пример сочетания явной и неявной обратной связи для генерации меток.

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

Конечно, не забудьте сбалансировать свой набор данных (с точки зрения меток) и разделить поезд, Вал и тестовые наборы стратифицированным образом, чтобы вы не тестировали период, который алгоритм никогда не видел. .

Показатели

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

Офлайн-показатели:

Наши офлайн-метрики будут довольно простыми.

Для нашей модели KNN может подойти RMSE. Мы будем следить за ним, чтобы проверить, очень ли далек наш текущий трек от чего-либо еще или нет. Это даст нам представление о том, как трек связан с другими треками в нашем наборе данных. Другой важной метрикой для этого компонента будет отзыв. Мы не хотим упустить ни одного возможного кандидата, который мог бы хорошо подойти для создаваемого нами плейлиста.

Для нашего ранкера мы будем выполнять бинарную классификацию. Мы хотим иметь как можно меньше плохих совпадений и как можно больше хороших. F-оценка выглядит здесь как хороший тип метрики. Мы хотим взвесить точность и отзыв в его выражении, чтобы максимально соответствовать потребностям бизнеса.

Онлайн-показатели:

Как упоминалось ранее, пользователь не так сильно взаимодействует с контентом на потоковых платформах. Нам нужно найти лучший способ узнать, ценит ли пользователь контент, который мы предлагаем. В противном случае мы можем измерить неявное взаимодействие, которое мы определили ранее, или добавить некоторые другие показатели, такие как среднее время сеанса (сколько времени пользователь проводит на нашей платформе в течение одного сеанса).

Фильтрация:

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

Если вы хорошо помните, говоря о компоненте фильтрации, мы упомянули, что добавление разнообразия в наши выходы — это то, что клиенты ожидают от Spotify Radio. Однако кто мы такие, чтобы говорить, чего на самом деле хотят клиенты? Поведение клиентов нелегко предсказать, а тем более просто предвидеть. В этом сценарии A/B-тестирование похоже на то, чем мы действительно хотим заниматься.

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

Если вы хотите узнать больше об A/B-тестировании, я могу только порекомендовать вам посмотреть эту статью из Netflix Tech Blog на Medium.

Заключение и следующие шаги:

Мы разработали пайплайн, который вполне подходит для рекомендательного механизма, основанного на похожих песнях. Мы не обсудили многие моменты дизайна. Некоторые другие моменты будут обсуждать параметры базы данных, варианты развертывания и множество других вопросов инфраструктуры. Если вы хотите конкретный пост о развертывании такого рода конвейера, просто дайте мне знать в комментариях 😄