Привет! Меня зовут Савва Степурин, я старший разработчик в группе рекомендательных продуктов в Фантехе Яндекса. Сегодня расскажу вам про то, как мы сделали «Незнакомое» для Моей волны — специальный режим для активного поиска музыкальных открытий.
«Незнакомое» позволяет вам получать от Моей волны те треки, которые вы ещё не слушали (возможно, даже не знаете про их существование), но которые с большой долей вероятности могут попасть в ваши музыкальные предпочтения. Если Моя волна в чистом виде — это идеальный баланс между любимыми композициями и чем-то новым, то «Незнакомое» помогает выйти из музыкального информационного пузыря и послушать новые треки.
Под катом — техническая эволюция «Незнакомого» от фильтра до отдельного продукта, описание новой модели ранжирования и многое другое.
Становление «Незнакомого»
Что это такое
Режим «Незнакомое» – это рекомендации неизвестной для пользователя музыки. Хоть это треки, которые пользователь никогда не слышал, но подбираются они так же, как и в обычной Моей волне: персонализировано с учётом индивидуальных предпочтений. Он предназначен для активного поиска новых артистов, треков и музыкальных жанров. Главная его задача — расширять кругозор, помогать выходить из музыкального пузыря и находить что-то совершенно новое, что попадёт прямо в сердце.
С чего мы начали
Режим «Незнакомое» существует с момента запуска Моей волны в ноябре 2021. Сначала «Незнакомое» представляло собой простой фильтр для Моей волны, которая без дополнительных настроек была сбалансированным рекомендационным потоком. Он получался максимально универсальным — по вашим вкусам на все случаи жизни. В сформированном потоке были треки, которые вы давно слушали, треки, которым вы ставили лайки, и те, которые вы не раз переслушивали, но лайком не удостоили.
Вместе с этим в поток подмешивались и новые треки — новая песня знакомого исполнителя или что-то от незнакомого. Таким образом, рекомендации условно делятся на две части: exploitation — используем уже знакомые треки, и exploration — исследуем что-то новое. Для уравновешивания «смелости» подбора между ними у нас есть специальные механизмы. Иными словами, баланс подбирается так, чтобы вам попадалась не полностью незнакомая и непривычная музыка, но и при этом не делающая поток слишком скучным и предсказуемым.
Поэтому, когда пользователь хотел включить «Незнакомое», происходило вот что:
Брался базовый поток Моей волны, тот самый, который максимально универсальный и всем нравится.
На него сверху накладывался фильтр, который выкидывал из потока то, что пользователь раньше уже слышал (например, конкретный прослушанный трек или исполнителя, которого слушали более 5 раз).
В итоге после работы фильтра в поток попадали только совершенно незнакомые исполнители и принципиально незнакомые треки. И всё бы хорошо, но было немного скучновато чисто с эмоциональной точки зрения. Произошло так потому, что мы брали исходную Мою волну и просто её фильтровали, отбрасывая знакомое. Поток получался хоть и формально незнакомым, но всё равно довольно консервативным, так как наследовался от Моей волны.
Развиваем идею
Мы решили концептуально прийти к такому решению: сделать настройку «Незнакомое» более смелой и вывести её в отдельную самостоятельную сущность, а не просто в добавочный фильтр. Дать ей уникальные свойства и свой характер — более рискованный.
Мы стали обучать отдельную модель ранжирования (в Яндексе мы называем ее формулой), которая задействуется только в том случае, если пользователь чётко даёт понять, что хочет слушать «Незнакомое». Приоритет у формулы был такой: в первую очередь отлично ранжировать именно случаи, когда речь идёт о незнакомой музыке. То есть о треках, незнакомых пользователю, включая незнакомых исполнителей или тех, кого он слышал пару раз. А вот знакомую музыку формуле разрешили ранжировать хуже, почти не обращая на неё внимания. Главное — хорошо угадывать треки при включённом режиме «Незнакомое».
На самом деле новая формула обучается и при работе со знакомой музыкой, но мы сильнее задираем веса на обучающих примерах, в которых пользователь включал «Незнакомое». Для CatBoost, который мы используем, эти примеры имеют решающее значение. А вот другие стримы пользователя, которые видит формула (без настройки «Незнакомое»), имеют ощутимо меньшие веса.
Но и этого было мало для создания из «Незнакомого» полноценной сущности.
Чтобы наши дальнейшие шаги стали понятны, нужно сначала рассказать об обучающем пуле. В данном случае мы пытаемся определять, какой из двух треков больше нравится конкретному пользователю, поэтому формула учится на парах событий. Мы берём последовательные пары разных событий пользователя: Play, Skip, Like, Dislike. Важно, что события должны быть именно разными, то есть пара вида Play-Play в выборку не попадёт.
Так что формула получает на вход пары вида: Play-Skip, Skip-Play, Dislike-Like и так далее.
Задача формулы — угадать, какое из событий лучше. Например, в паре Play-Skip у нас Play лучше, чем Skip. А в паре Like-Play — Like лучше, чем Play.
Что именно лучше, мы описываем как раз с помощью специальных весов, задающих систему ценностей для формулы. Мы всегда можем сказать ей, что дизлайки — это очень плохо, и выкрутить у них вес. Если так сделать, то формула превратится в очень осторожную и будет избегать дизлайков любой ценой, подкидывая пользователю только то, что не вызовет раздражения. Как следствие — скорее всего, рекомендации будут скучными.
Сейчас мы переподобрали веса отдельно для настройки «Незнакомое», сказав формуле, что будем поощрять её за лайки. При этом мы оценили скипы и дизлайки как вполне себе допустимые события, а не что-то страшное. Благодаря такой приоритизации формула стала более смелой, и у нас получилось наращивать число лайков, даже иногда уходя в размен на скипы или дизлайки.
Почему именно в этом случае можно делать формулу такой рисковой?
Потому что мы всегда исходим из того, что запуск «Незнакомого» — это сознательный выбор пользователя, готового к новым музыкальным открытиям. Если бы мы выдали такую формулу для обычной Моей волны (когда человек в том числе хочет слушать любимую и знакомую музыку), то пользователей это явно бы опечалило. А здесь мы работаем с незнакомым контентом, повышаем количество лайков благодаря смелости формулы и при этом не просаживаем в процессе никакие метрики, связанные с продолжительностью слушания настройки «Незнакомое». Как раз из-за того, что пользователь готов к такой экспериментальной дерзости системы.
Архитектура решения
В нашем рекомендационном процессе есть пайплайн, который перезапускается с каждым новым треком. Например, вы послушали какой-то трек и дали по нему фидбэк (лайк, дизлайк, скип). Это сразу заливается в рекомендации, и пайплайн перестраивается. Точная частота перестройки зависит от клиента, но не реже, чем раз в 5 треков. Это в целом не самая распространённая история среди рекомендательных систем — почти на лету реагировать на действия пользователя и подстраиваться под них, но мы научились работать именно так.
Сам пайплайн состоит из нескольких стадий.
Кандидат-генерация
В нашем каталоге десятки миллионов треков. Из всего этого количества нам надо отобрать так называемых кандидатов — треки, которые могут оказаться для пользователя хоть немного релевантными. Для этого у нас есть набор селекторов.
Каждый селектор по своей сути — это мини-поиск, решающий определённую задачу. Из десятков миллионов треков селекторы отбирают несколько сотен тысяч кандидатов. К примеру, один селектор отбирает треки исполнителей, которых вы лайкали. Другой селектор тщательно выбирает треки, которые похожи на последние пять прослушанных. Причём тут без разницы, известные это вам треки или нет, главное — чтобы просто были похожи на последние пять треков.
И таких селекторов, у каждого из которых своя задача, у нас несколько.
Фильтрация
Итак, после первого этапа у нас есть множество кандидатов. Пришло время фильтрации, во время которой мы выкинем из этого списка то, что точно не хотим ставить пользователю в поток. К примеру, выкидываем треки исполнителей, которые недавно звучали (менее чем 25 треков назад).
На этом же этапе у нас работает фильтр «Незнакомого», который отбрасывает из списка знакомые треки.
Ранжирование
После фильтрации у нас остаётся набор треков, который мы ранжируем при помощи формулы. Благодаря CatBoost сортируем эти десятки тысяч треков, ранжируем, а затем лучшее ставим пользователю в поток.
В очень упрощённом виде это можно представить на такой схеме:
Куда же без метрик
Хорошие рекомендации — это очень многофакторная задача. Тут существует множество аспектов качества, причём некоторые из них вообще могут быть ортогональными друг другу.
Вот, скажем, есть точность — этот аспект просто оценивает, подходит ли вам трек или нет. Если вы любитель хард-рока, то, скорее всего, песни Киркорова будут вам не очень в тему. И если рекомендации, зная ваши вкусы, подкидывают вам Киркорова, значит, это так себе рекомендации — они не понимают, что вам нравится.
Это отчасти похоже на поисковую выдачу. У вас есть конкретный поисковый запрос, вы вводите его в поисковик, получаете результат. Затем оцениваете, релевантен он для вас или нет — довольно бинарная оценка. Вы просто берёте количество ответов, оцениваете релевантность каждого из них и выводите для себя оценку общей выдачи.
Но для создания системы рекомендаций этого мало, потому что поток, построенный просто на тех песнях, которые вам подходят, может быть поистине отвратительным. Мы проверяли это даже в том плане, что просто брали набор одних и тех же треков и меняли их порядок в потоке. Даже используя лишь разницу в порядке, можно собрать из одного и того же набора как отличный поток, который будут слушать, так и скучную волну, которая быстро надоест. Это что-то, отчасти завязанное на сиюминутное настроение пользователя и его эмоции. Поэтому с этим очень интересно работать.
В общем, как вы поняли, на одной точности хорошие рекомендации не построить.
Дополнительный аспект — это разнообразие. То, как часто мы повторяем какую-то песню. Если какой-то трек вам нравится и подходит по вкусам, он всё равно может надоесть, если будет выдаваться в потоке слишком часто.
Ещё один отличный аспект — серендипность, открытие нового. Это тот случай, когда вам в поток прилетает новая, незнакомая песня, вызывающая вау-эффект и становящаяся чуть ли не новым любимым треком на какое-то время. Причем ранее вы это не слышали, не знаете исполнителя — его просто подкинули рекомендации.
Я выделил три довольно важных аспекта хороших рекомендаций, но на самом деле их множество, как и внутренних метрик, оценивающих каждый аспект. Главное, что тут надо понимать, — задача качества рекомендаций многофакторная и все метрики объединены в чёткую иерархию.
Есть верхнеуровневые метрики, например возвращаемость пользователя. Ей подчиняется много метрик уровнем пониже. Из самых интересных могу выделить Like prob и Play prob. Play prob — это вероятность того, что вы дослушаете поставленный нами трек до конца. Like prob — вероятность того, что трек получит от вас лайк.
Вместе эти метрики выступают в роли сообщающихся сосудов, мы можем их разменивать друг на друга. Если будем ставить больше незнакомого, то лайков может стать больше. В том числе и потому, что вы не можете дважды пролайкать какой-то трек. К тому же не очень вероятно, что вы поставите лайк треку, который давно знаете, часто слушаете, но лайка от вас он пока не заслужил. Поэтому чем меньше знакомых треков, тем выше вероятность получить на трек лайк. То есть Like prob вырастет, а вот Play prob может просесть, так как какие-то незнакомые треки будут просто проматывать, не дослушивая.
Можно сдвинуть чашу весов в другую сторону — оставить слушателю только то, что он точно любит. Например, любите вы Queen, давно их слушаете, и если группа попадётся вам в потоке — наверняка дослушаете, повысив Play prob. А вот с Like prob всё будет грустнее, потому что на этих песнях у вас или лайк уже стоит (и нового не поставить), или за всё это время вы его так и не поставили, и скорее всего, уже не поставите.
Прямо сейчас самая интересная (и сложная) задача для нас — это не просто разменивать одну метрику на другую, а делать так, чтобы растить их обе, без просадок.
Это и есть главный показатель того, что рекомендации работают так, как надо. В случае настройки «Незнакомое», хоть мы и пожертвовали небольшой просадкой Play prob, но получили очень заметный прирост Like prob и в общем улучшили метрику возвращаемости пользователя в этот режим.
Сейчас с помощью режима «Незнакомое» пользователи стали на 20% чаще добавлять себе в медиатеку треки неизвестных ранее исполнителей. Если тоже хотите попробовать, это можно сделать в настройках Моей волны.