company_banner

Как мы учились рекомендовать фильмы и почему не стоит полагаться только на оценки



    Представьте, что вы хотите провести вечер за просмотром фильма, но не знаете, какой выбрать. Пользователи Яндекса часто оказываются в такой же ситуации, поэтому наша команда разрабатывает рекомендации, которые можно встретить в Поиске и Эфире. Казалось бы, что тут сложного: берём оценки пользователей, с их помощью обучаем машину находить фильмы, которым с высокой вероятностью поставят 5 баллов, получаем готовый список фильмов. Но этот подход не работает. Почему? Вот об этом я сегодня и расскажу вам.

    Немного истории. В далёком 2006 году Netflix запустила конкурс по машинному обучению Netflix Prize. Если вы вдруг забыли, тогда компания ещё не занималась стримингом в интернете, а сдавала в прокат фильмы на DVD. Но уже тогда ей было важно предугадывать оценку пользователя, чтобы что-то рекомендовать ему. Итак, суть конкурса: предсказать оценки зрителей на 10% лучше (по метрике RMSE), чем Cinematch, рекомендательная система Netflix. Это было одно из первых массовых соревнований такого рода. Интерес подогревал огромный датасет (больше 100 миллионов оценок), а также приз в 1 млн долларов.

    Конкурс закончился в 2009 году. Команды BellKor’s Pragmatic Chaos и The Ensemble пришли к финишу с одинаковым результатом (RMSE = 0,8567), но The Ensemble заняла второе место, потому что отправила решение на 20 минут позже конкурентов. Результаты и работы можно найти тут. Но самое интересное в другом. Если верить неоднократным рассказам на профильных конференциях, алгоритмы-победители так и не оказались в продакшене в запущенном вскоре сервисе видеостриминга. Я не могу говорить о причинах чужих решений, но расскажу, почему мы поступили аналогичным образом.

    Персональный рейтинг


    Уже достаточно давно пользователи Яндекса могут оценивать просмотренные фильмы. Причём не только на КиноПоиске, но и в результатах поиска. Со временем у нас накопились сотни миллионов оценок десятков миллионов людей. В какой-то момент мы решили воспользоваться этими данными, чтобы помочь пользователям понять, насколько тот или иной фильм им понравится. По сути, мы решали ту же задачу, что и на конкурсе Netflix Prize, то есть предсказывали, какую оценку пользователь поставит фильму. На тот момент уже существовали рейтинги КиноПоиска и IMDB, которые строились на основе оценок людей. Мы же строили персональный рейтинг, поэтому решили использовать отдельную шкалу в процентах, чтобы избежать визуального сходства и путаницы.



    Кстати, необходимость соотнести балльную шкалу и процентную — это отдельная неочевидная головная боль, поэтому коротко расскажу и о ней. Казалось бы, для 10-балльной шкалы возьмите по 10% на каждый балл — и дело в шляпе. Но нет. Причина в психологии и привычках. Например, с точки зрения пользователей, оценка 8/10 — это сильно лучше, чем 80%. Как такое соотнести? С помощью краудсорсинга! Так мы и поступили: запустили задание в Толоке, в котором толокеры описывали ожидания от фильмов с определённым баллом или процентом персонального рейтинга. На основе такой разметки мы подобрали функцию, которая переводит предсказание оценки из балльной в процентную так, чтобы при этом сохранялись ожидания пользователей.

    Пример заданий в Толоке


    Предсказывать ожидания от фильма полезно, но хорошо бы ещё и рекомендации строить. То есть сразу показывать человеку список хороших фильмов. В этот момент многие из вас могли подумать: «А давайте просто отсортируем по персональному рейтингу те фильмы, которые пользователь ещё не смотрел». Мы тоже так подумали сначала. Но затем пришлось решать две проблемы.

    Проблема инструмента


    Когда пользователь ищет определённый фильм (или хочет взять конкретный DVD напрокат), то сервис должен предсказать оценку именно этого фильма. Ровно эта задача и решалась на конкурсе Netflix Prize, где использовалась метрика RMSE. Но в рекомендациях решается другая задача: нужно не угадать оценку, а найти фильм, который и будет в итоге просмотрен. И метрика RMSE плохо справляется с этой задачей. Например, штраф за предсказание оценки 2 вместо 1 точно такой же, как за 5 вместо 4. Но наша система вообще никогда не должна рекомендовать фильмы, которым пользователь поставит 2! Для решения этой задачи гораздо лучше подходят метрики на основе списка, например Precision@K, Recall@K, MRR или NDCG. Не могу не рассказать о них чуть подробнее (но если метрики вам неинтересны, то просто пропустите следующий абзац).

    Начнём с метрики MRR (mean reciprocal rank). Будем смотреть, на какой позиции в ранжировании окажется фильм, с которым пользователь взаимодействовал (например, посмотрел или высоко оценил) в тестовом периоде. Метрика MRR — это усреднённая по пользователям обратная позиция такого фильма. То есть $MRR = \frac{1}{|U|} \sum_{u=1}^{|U|} \frac{1}{rank_u}$. Такая метрика, в отличие от RMSE, оценивает список целиком, но, к сожалению, смотрит только на первый угаданный элемент. Впрочем, легко модифицировать метрику, чтобы избавиться от этого недостатка. Мы можем посчитать сумму обратных позиций всех фильмов, с которыми взаимодействовал пользователь. Такая метрика называется Average Reciprocal Hit Rank. Такая метрика учитывает все угаданные фильмы в выдаче. Заметим, что позиция k в выдаче получает вес 1/k за угаданный фильм и вес 0 за другой фильм. Часто вместо 1/k используют 1/log(k): это лучше соответствует вероятности, что пользователь доскроллит выдачу до k позиции. Получится метрика DCG (discounted cumulative gain) $DCG = \frac{1}{|U|} \sum_{u=1}^{|U|} \sum_{pos=1}^{N} \frac{gain_{pos}}{\max(1, \log(pos))}$. Но вклад разных пользователей в метрику разный: для кого-то мы угадали все фильмы, для кого-то не угадали ничего. Поэтому, как правило, эту метрику нормируют. Поделим DCG каждого пользователя на DCG наилучшего ранжирования для этого пользователя. Полученная метрика называется NDCG (normalized discounted cumulative gain). Она широко используется для оценки качества ранжирования.

    Итак, каждой задаче — своя метрика. А вот следующая проблема уже не такая очевидная.

    Проблема выбора


    Её достаточно трудно описать, но я попробую. Оказывается, люди ставят высокие оценки не тем фильмам, которые обычно смотрят. Высшие оценки получают редкие киношедевры, классика, артхаус, но это не мешает людям вечером после работы с удовольствием посмотреть неплохую комедию, новый боевичок или эффектную космооперу. Добавьте к этому, что пользователи оценили в Яндексе далеко не все фильмы, которые когда-то и где-то уже посмотрели. И если ориентироваться только на высшие оценки, то мы рискуем получить ленту рекомендованных фильмов, которая будет выглядеть логично, пользователи могут даже признать её качество, но смотреть в итоге ничего не станут.

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



    Получается, что в условиях разрозненности оценок и дефицита фильмов с высоким рейтингом стоит смотреть не только на рейтинг. Хорошо, тогда обучим машину предсказывать просмотр рекомендованного фильма, а не оценку. Казалось бы, логично, ведь пользователь этого и хочет. Что же может пойти не так? Проблема в том, что лента заполнится фильмами, каждый из которых вполне подойдёт для лёгкого времяпровождения вечером, но их персональный рейтинг будет невысок. Пользователи, конечно же, обратят внимание на то, что в ленте нет «шедевров», а значит, доверие к рекомендациям будет подорвано, они не станут смотреть то, что в иных условиях посмотрели бы.

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

    Как работают наши рекомендации


    Наша система — часть Поиска, так что нам нужно строить рекомендации очень быстро: время ответа сервиса должно быть меньше 100 миллисекунд. Поэтому мы стараемся как можно больше тяжёлых операций выполнять в офлайне, на этапе подготовки данных. Все фильмы и пользователи в рекомендательной системе представлены профилями (важно не путать с аккаунтом), которые включают в себя ключи объекта, счётчики и эмбеддинги (проще говоря, векторы в некотором пространстве). Профили фильмов каждый день готовятся на YT (читается как «Ыть») и загружаются в оперативную память машин, которые отвечают на запросы. А вот с пользователями всё немного сложнее.

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

    Это происходит в офлайне (то есть заранее), а теперь переходим непосредственно к рантайму. Здесь рекомендательная система состоит из двух шагов. Ранжировать всю базу фильмов слишком долго, поэтому на первом шаге мы просто отбираем несколько сотен кандидатов, то есть находим фильмы, которые могут быть интересны зрителю. Сюда попадают как популярные картины, так и близкие к пользователю по некоторым эмбеддингам. Существует несколько алгоритмов для быстрого нахождения ближайших соседей, мы используем HNSW (Hierarchical Navigable Small World). С его помощью мы находим ближайшие к пользователю фильмы всего за несколько миллисекунд.

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

    Чтобы находить кандидатов, мы используем эмбеддинги из нескольких матричных разложений: от классического варианта ALS, который предсказывает оценку, до более сложных вариаций на базе SVD++. В качестве фич для ранжирования используются как простые счётчики событий пользователя и фильмы, CTR’ы по разным событиям, так и более сложные предобученные модели. Например, предсказание ALS тоже выступает в роли фичи. Одна из наиболее полезных моделей — нейросеть Recommender DSSM, о которой я, пожалуй, расскажу чуть подробнее.

    Recommender DSSM


    DSSM — это нейросеть из двух башен. Каждая башня строит свой эмбеддинг, затем между эмбеддингами считается косинусное расстояние, это число — выход сети. То есть сеть учится оценивать близость объектов в левой и правой башне. Подобные нейросети используются, например, в веб-поиске, чтобы находить релевантные запросу документы. Для задачи поиска в одну из башен подаётся запрос, в другую — документ. Для нашей сети роль запроса играет пользователь, а в качестве документов выступают фильмы.

    Башня фильма строит эмбеддинг на основе данных о фильме: это заголовок, описание, жанр, страна, актёры и т. д. Эта часть сети достаточно сильно похожа на поисковую. Однако для зрителя мы хотим использовать его историю. Чтобы это сделать, мы агрегируем эмбеддинги фильмов из истории с затуханием по времени с момента события. Затем поверх суммарного эмбеддинга применяем несколько слоёв сети и в итоге получаем эмбеддинг размера 400.



    Если учитывать сразу всю историю пользователя в эмбеддинге, то это сильно замедлит обучение. Поэтому идём на хитрость и учим сеть в два этапа. Сначала учится более простой InnerDSSM. На вход он получает только последние 50 событий из истории пользователя без разделения на типы событий (просмотры, оценки...). Затем мы переобучаем полученный InnerDSSM на всей истории пользователя, но уже с разбиением на типы событий. Так получаем OuterDSSM, который и используется в рантайме.

    Применение сети в рантайме в лоб требует довольно много вычислительных ресурсов. Поэтому мы сохраняем эмбеддинги из башни фильмов в базе, а эмбеддинги по истории пользователя обновляем near real-time. Таким образом, во время обработки запроса нужно применить только небольшую часть OuterDSSM и посчитать косинусы, это не занимает много времени.

    Заключение


    Сейчас наши рекомендации уже доступны в нашем поиске (например, по запросу [что посмотреть]), в сервисе Яндекс.Эфир, а ещё адаптированная версия этой технологии применяется в Яндекс.Станции. Но это не значит, что нам можно расслабиться. Мы постоянно обучаем новые модели, применяем всё больше данных, пробуем новые подходы к обучению и новые метрики качества. На мой взгляд, чем старше область, тем сложнее её развивать. Но в этом и заключается главный интерес для специалистов.
    Яндекс
    Как мы делаем Яндекс

    Комментарии 26

      +10
      Теперь бы еще дойти до системы, которая реально полезна, и было бы вообще замечательно. А так, сколько я подобных вещей не видел (от игр до аниме до фильмов до музыки) — они более-менее работают только на очень неискушенного человека, для которого, на самом деле, единственный критерий рекомендаций — не рекомендовать ему совсем уж шлак, который вызовет негатив.

      Если же человек немного повидал/послушал сам, полезность систем рекомендаций резко уходит в ноль. Например, что-то вменяемое в рекомендациях нетфликса для меня закончилось на третий день залипания в сериальчики, и дальше нужно было уже банально сидеть и перетряхивать всё вручную (а нетфликс интерфейсно довольно убог в этом разрезе, например).
        +2
        Яростно плюсую! Некое усреднение и обыдлячивание идёт выдачи повсюду. Как-то спикер из Одноклассников даже сказал, мол наши пользователи подписываются на что-попало, и если мы сделаем в настройках переключатель «я не лох», и будем после его включения просто давать в ленту то, на что человек подписался, то его будут случайно нажимать идиоты, делать себе из ленты УГ, и уходить с ОК.

        Но, блин, неужели нельзя в случае что-то типа переключателя сделать, мол «хочу расслабиться/хочу шедевры/дайте что-то эпичное»? Или пару ползунков, которые можно сбрасывать в дефолт при каждой сессии?

        Например, лично я с удовольствием бы увидел топ всех времен и народов на кинопоиске, но очищенный от слезопускательных поучительных зеленых миль и списков шиндлера, и эпичных блокбастеров, снятых для всех и сразу. Но как?
          0
          Но как?
          Частично может помочь, если в расширенном поиске ограничить бюджет/сборы некоторой суммой. Чаще всего фильмы с большим бюджетом как раз и снимают для «расширенной» аудитории. Кроме того, эти фильмы и так на слуху.
        0
        Немного не в тему статьи, но: когда сделаете приложение «Кинопоиск HD» для Sony PlayStation? Очень не хватает, а в изоляции тем более. У Окко, Ivi, и Netflix уже давно есть нативные аппы для Плойки.
          +3
          Уточнил у коллег. Уже работают над этим.
            0
            Супер!!! Если требуется бета-тестер, могу порепортить :)
              0
              А ещё бы хотелось бы возможность шаринга видео на chromecast.
            +5

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

              0
              Если это список для выбора кино на вечер, то зачем в рекомендациях ещё не вышедшие фильмы?
                +1
                Мы можем показывать в том числе невышедший фильм в списке, если такой фильм релевантен пользователю. Да, прямо сейчас он не сможет его посмотреть, но возможно посмотрит трейлер и решит смотреть, когда фильм выйдет.
                В целом в рекомендациях таких фильмов немного.
                +1

                На кинопоиске мне очень помогает средний рейтинг фильма среди друзей по интересам. Если этот рейтинг сильно выше общего рейтинга фильма — скорее всего фильм достойный.

                  0
                  Проблема предсказания желаний и предпочтений пользователя не в том что они не известны, а в том что они меняются.
                    +2
                    А планируется ли когда-нибудь некая кнопка на фильме — «Не рекомендовать мне его никогда вообще»?
                      0
                      Можно поставить фильму оценку «не интересно» и мы его больше не будем рекомендовать
                      0
                      Отличная статья, спасибо! Есть несколько вопросов:

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

                      2) Только catboost использовался в ранжировании? «Мы пришли к комбинации нескольких моделей CatBoost» — то есть stacking?

                      3) Делаете что-нибудь пытаясь неожиданно удивить пользователя, то есть улучшить такой фактор, как Serendipity
                        +1
                        1) есть разные генераторы кандидатов, в том числе из свежего контента. У меня скорее обратное ощущение — довольно большая часть аудитории склонна выбирать советскую классику
                        2) сейчас у нас работает линейная комбинация нескольких катбустов с подобранными в экспериментах весами.
                        3) да, разнообразие и serendipity — это супер важно! Активно работаем над увеличением разнообразия как раз сейчас.
                        +2
                        Да, с трудом верится, что еще одна попытка изобрести рекомендательную систему к фильмам приведет к успеху. На сегодняшний день единственная рекомендательная система вкусов, которая действительно удивляет — это spotify, при этом есть слух, что они анализируют сам контент песни, что в случае с фильмами почти нереализуемо.

                        Что на самом деле ХОТЕЛОСЬ БЫ УВИДЕТЬ ХОТЬ ГДЕ-НИБУДЬ

                        Разбить весь фильм на список критериев и оценивать фильмы по ним ОТДЕЛЬНО. Представьте, как киноману было бы интересно повыставлять эти ползунки и выбрать действительно фильм под настроение? Причем, можно разбить критерии на первостепенные и advanced для совсем уж искушённых. Примеры:
                        — Хочется ли пересмотреть второй раз
                        — Необычные идеи в сюжете (чтобы отсечь марвел)
                        — Качество диалогов
                        — Юмор
                        — Есть ли неожиданные повороты
                        — Насколько фильм «классический» (от артхауса до иронии судьбы, чтобы можно было исключать властелин колец и титаник)
                        — симпатичные актеры\актрисы
                        — социальные вопросы\воспитание
                        — уровень фантастичности\выдуманности мира, в котором происходит действие (многие не любят смотреть про сказочных тварей, а кому то наоборот надоела повседневность)
                        — эмоциональность
                        — слезливость
                        — скорость повествования
                        — качество съемки\уровень спецэффектов (если я ищу фильм для проектора)
                        — …
                        — …
                          +1
                          Частично такая разметка фильмов есть в данных на Кинопоиске, мы сейчас пробуем учитывать подобные данные в нашем dssm. В идеале рекомендации должны сами понимать, что вам сейчас больше понравится.
                          Про ползунки позволю себе пофантазировать, это мог бы быть диалог с ассистентом вида: «Алиса, хочу посмотреть фильм. <СПИСОК> Убери классику. <СПИСОК> Хочу больше эмоций. <СПИСОК> И неожиданный сюжет. <СПИСОК> Включи ...»
                          Надеюсь, мы к чему-то подобному придем :)
                            0
                            В идеале рекомендации должны сами понимать, что вам сейчас больше понравится
                            И где они для энтого информацию возьмут? Сегодня мне хочется посмотреть боевик, завтра фэнтези, послезавтра что-то для семейного просмотра.

                            Это реальные случаи, но так явно на жанры не ложились и формулировались, скорее, так: какой-нибудь экшен, что-то максимально отвлеченное от реальности с хэппиендом и т.д.
                            0
                            Я пока пользуюсь kinonavigator.ru, где имеющихся ползунков вполне для меня достаточно, и рекомендашка хорошая. Есть даже подбор для совместного просмотра. Попробуйте и вы.
                              0
                              На сегодняшний день единственная рекомендательная система вкусов, которая действительно удивляет — это spotify, при этом есть слух, что они анализируют сам контент песни, что в случае с фильмами почти нереализуемо.

                              Spotify как-то по личному опыту работает даже хуже last.fm, которому сто лет в обед.


                              Видимо, это все очень субъективно.

                              0

                              На Кинопоиске эта система не будет работать?

                                0
                                На кинопоиске мы проводим эксперименты с нашими рекомендациями, надеюсь, что система будет общей
                                  0

                                  Раз уж мы тут: почему на кинопоиске не пишутся языки? И нет возможности искать по языку. Эти данные легко взять с Википедии. Язык — ведь одна из самых ключевых характеристик произведения. Я не спорю, что иногда можно определить по стране, но это в основном актуально для Японии, Кореи, Китая, России и т. д. (но не для США, т. к. часто бывают мультиязычные произведения, Украины, Индии и т. д.). И то, даже когда язык определить можно, ты можешь просто не знать точно, какие языки используются в этой стране, а также используются ли там другие языки. И даже в указанных странах язык не всегда совпадает, например, есть китайский фильм, в котором равномерно использовались китайский, русский и японский.


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

                                    0
                                    Недавно сделали новый интерфейс для сериалов, количество серий есть справа от описания.
                                0
                                В статье сказано, что вы собрали краудсорсингом кривую соответствия баллов и процентов. Уж очень любопытно было бы подивиться на её нелинейный вид и через неё понять психологию людей. Не поделитесь?

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

                                Самое читаемое