Rekko challenge


Сегодня мы запускаем Rekko Challenge 2019 — соревнование по машинному обучению от онлайн-кинотеатра Okko.


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


Подробнее про Okko, задачу, данные, призы и правила — ниже.


Задача


Вам доступны данные обо всех просмотрах, рейтингах и добавлениях в «Запомненное» фильмов и сериалов пользователем за некоторый период в N дней (N > 60), а также вся метаинформация о контенте. Необходимо предсказать, какие фильмы и сериалы пользователь купит или посмотрит по подписке за следующие 60 дней.


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


О нашем сервисе


Если пользователь хочет легально смотреть кино в интернете, у него есть три основных пути.


Первый путь — смотреть бесплатно, постоянно прерываясь на рекламные ролики (AVOD, Advertising Video On Demand). Второй — купить фильм к себе в коллекцию или арендовать (TVOD, Transactional Video On Demand). Третий — оформить подписку на определённый период (SVOD, Subscription Video On Demand).


Okko работает только по моделям TVOD и SVOD. В нашем сервисе совсем нет рекламы.


Всего в сервисе чуть больше 10 тысяч фильмов и сериалов, около 6 тысяч из них доступны по подписке, остальные только для покупки или аренды. При этом, почти любой подписной контент можно купить. Исключение составляют, например, сериалы Amediateka, их можно смотреть только по подписке.


Распределение количества контента в зависимости от модели потребления


По какой модели будет доступен фильм, во многом зависит от студии, владеющей правами. Они заключают с онлайн-кинотеатрами контракт, в котором оговорено, в какие сроки и по каким правам фильм будет доступен. Как правило, условия одинаковы для всех игроков рынка, но иногда студии идут на уступки некоторым кинотеатрам или предлагают более выгодные условия за бо?льшие деньги. Так появляются эксклюзивы.


Например, крупные мировые новинки попадают в подписку не сразу, а только спустя 2-3 месяца после появления в сервисе. Более того, в первые несколько недель их нельзя даже взять в аренду, доступна только возможность покупки навсегда. А вот российские фильмы могут быть доступны по подписке сразу после выхода и иногда даже одновременно со стартом проката в оффлайн-кинотеатрах.


Когда контракт истекает, фильм становится недоступен — до пролонгирования истекшего контракта или заключения нового.


Пример карточки недоступного фильма


Периоды отсутствия прав на контент хорошо заметны на графиках количества просмотров. Ниже, например, представлен такой график для фильма «Джон Уик 2». Первым делом может показаться, что хадуп прилёг отдохнуть на пару месяцев, но нет: закончились права.


График числа просмотров фильма "Джон Уик 2"


Самый высокий пик на графике выше (отмечен вертикальной линией) совпадает с датой добавления фильма в подписку: это весьма характерное поведение для громких новинок. В нашем сервисе 12 подписок:


  • Восемь тематических,
  • Сериалы Amediateka,
  • Сериалы ABC,
  • Российские фильмы и сериалы от сервиса «START»,
  • Фильмы в 4K.

И два пакета подписок: «Оптимальный», который включает все тематические подписки, и «Оптимальный + Amediateka».


Интерфейс подписок


Самыми популярными, естественно, являются мета-пакеты. Из тематических подписок пользователи предпочитают «Мировое кино» и «Наше кино».


Динамика числа просмотров по подпискам


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


Чаще всего пользователи выбирают для покупки новинки текущего проката и крупные премьеры прошлого года.


Самый популярный источник покупок в приложении — раздел «Рекомендации», следом за ним идут «Поиск», «Новинки» и «Каталог». Часть фильмов пользователи покупают из «Похожих» и «Запомненных».


Распределение источников покупок


Одна из главных проблем, с которыми мы в Okko активно боремся, — проблема выбора пользователями контента. Если посмотреть на график вероятности совершения покупки от времени нахождения в сервисе (данные за прошлый год), станет видно, что пользователи готовы выбрать и купить фильм в течении первых 10 минут, затем вероятность покупки стремительно падает. При этом остаётся достаточно большая часть пользователей, которые проводят в сервисе от получаса до часа и не могут выбрать подходящий для себя контент.


Вероятность покупки от времени


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


Тут в дело вступает Rekko — внутренняя рекомендательная система онлайн-кинотеатра Okko. Rekko в данный момент работает в двух разделах сервиса — «Рекомендации» и «Похожие».


Раздел рекомендации на TV


Похожие на TV


Чтобы оценить удовлетворенность пользователя контентом, мы анализируем факт покупки, просмотры по подписке, время просмотра, добавление в «Запомненные» и пользовательский рейтинг.


Шкала оценок в Okko представлена пятью звездочками с половинными делениями: она принимает целые значения от 0 до 10.


Интерфейс постановки рейтинга


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


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


Интерфейс запоминания


Запомненные фильмы в профиле


Работы над Rekko начались ровно год назад и на данный момент по данным A/B тестов она позволила нам увеличить среднее число покупок на 4%, транзакционную выручку на 3%, конверсию в подписку на 5%, а пользователи стали выбирать фильмы на 18% быстрее.


Конвертация в покупку в контрольной группе и группе с Rekko


Данные


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


transactions.csv


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


element_uid user_uid consumption_mode ts watched_time device_type device_manufacturer
3336 5177 S 44305181.2180206 4282 0 50
481 593316 S 44305180.606027626 2989 0 11
4128 262355 S 44305180.41444582 833 0 50

  • element_uid — идентификатор элемента
  • user_uid — идентификатор пользователя
  • consumption_mode — тип потребления (P — покупка, R — аренда, S — просмотр по подписке)
  • ts — время совершения транзакции
  • watched_time — число просмотренных пользователем по данной транзакции секунд
  • device_type — анонимизированный тип устройства, с которого была совершена транзакция
  • device_manufacturer — анонимизированный производитель устройства, с которого была совершена транзакция

Распределение watched_time


ratings.csv


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


user_uid element_uid rating ts
571252 1364 10 44305174.26309871
63140 3037 10 44305139.28281821
443817 4363 8 44305136.20584908

  • element_uid — идентификатор элемента
  • user_uid — идентификатор пользователя
  • rating — поставленный пользователем рейтинг (от 0 до 10)
  • ts — время постановки рейтинга

Распределение рейтингов


bookmarks.csv


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


user_uid element_uid ts
301135 7185 44305161.30743926
301135 4083 44305160.01187332
301135 10158 44305157.74463292

  • element_uid — идентификатор элемента
  • user_uid — идентификатор пользователя
  • ts — время добавления фильма в «запомненные»

catalogue.json


Метаинформация обо всех рекомендуемых элементах: фильмах, сериалах и многосерийных фильмах.


{
  "1983": {
    "type": "movie",
    "availability": ["purchase", "rent", "subscription"],
    "duration": 140,
    "feature_1": 1657223.396513469,
    "feature_2": 0.7536096584,
    "feature_3": 39,
    "feature_4": 1.1194091265,
    "feature_5": 0.0,
    "attributes": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ...]
  },
  "2166": {
    "type": "movie",
    "availability": ["purchase", "rent"],
    "duration": 110,
    "feature_1": 36764165.87817783,
    "feature_2": 0.7360206399,
    "feature_3": 11,
    "feature_4": 1.1386044027,
    "feature_5": 0.6547073468,
    "attributes": [16738, 13697, 1066, 1089, 7, 5318, 308, 54, 170, 33, ...]
  },
...
}

  • type — принимает значения movie, multipart_movie или series
  • duration — длительность в минутах, округлённая до десятков (продолжительность серии для сериалов и многосерийных фильмов)
  • availability — доступные права на контент (может содержать значения purchase, rent и subscription)
  • attributes — мешок некоторых анонимизированных атрибутов
  • feature_1..5 — пять анонимизированных вещественных и порядковых признаков

Доступные права указаны на момент окончания тренировочного периода и начала тестового.


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


Распределение признаков в каталоге


Метрика


В качестве метрики мы используем Mean Average Precision (MAP) для 20 элементов, но немного модифицированный. За тестовый период пользователь мог потребить меньше 20 фильмов. Если в таком случае считать честный MAP, верхняя граница метрики будет меньше единицы, а значения маленькие. Поэтому, если пользователь потребил меньше 20 элементов, нормируем мы на их количество, а не на 20.


$\mbox{MNAP@20} = \frac{1}{\lvert U \rvert} \sum_{u \in U} \frac{1}{\min(n_u, 20)} \sum_{i=1}^{20} r_u(i) p_u@i$


$p_u@k = \frac{1}{k} \sum_{i=1}^{k}r_u(i)$


$r_u(i)$ — находится ли $i$-ый предсказанный элемент в множестве потреблённых за тестовый период элементов пользователем $u$, $n_u$ — размер этого множества. Если вдруг подзабыли метрики качества ранжирования, на хабре про них есть отличная статья.


Код метрики на Cython
def average_precision(
        dict data_true,
        dict data_predicted,
        const unsigned long int k
) -> float:
    cdef:
        unsigned long int n_items_predicted
        unsigned long int n_items_true
        unsigned long int n_correct_items
        unsigned long int item_idx

        double average_precision_sum
        double precision

        set items_true
        list items_predicted

    if not data_true:
        raise ValueError('data_true is empty')

    average_precision_sum = 0.0

    for key, items_true in data_true.items():
        items_predicted = data_predicted.get(key, [])

        n_items_true = len(items_true)
        n_items_predicted = min(len(items_predicted), k)

        if n_items_true == 0 or n_items_predicted == 0:
            continue

        n_correct_items = 0
        precision = 0.0

        for item_idx in range(n_items_predicted):
            if items_predicted[item_idx] in items_true:
                n_correct_items += 1
                precision += <double>n_correct_items / <double>(item_idx + 1)

        average_precision_sum += <double>precision / <double>min(n_items_true, k)

    return average_precision_sum / <double>len(data_true)

def metric(true_data, predicted_data, k=20):
    true_data_set = {k: set(v) for k, v in true_data.items()}

    return average_precision(true_data_set, predicted_data, k=k)

Призы и правила


Призовой фонд составляет 600 тысяч рублей:


  • 300 тысяч получит победитель,
  • 200 тысяч — участник на втором месте,
  • 100 тысяч — участник на третьем месте.

Правила стандартные: не нарушать работу платформы, использовать только один аккаунт, избегать приватного обмена кодом с другими участниками и не быть сотрудником Okko и Rambler.


Как начать


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


Надеемся, что в данной статье мы смогли погрузить вас в тематику онлайн-кинотеатра и достаточно подробно описать данные. В архиве с задачей вы найдёте файл baseline.ipynb, который содержит код для загрузки данных и пример простого решения с помощью алгоритма K ближайших соседей.


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


Итак, как начать:


  1. Зарегистрируйтесь на boosters.pro и вступите в @boosterspro;
  2. Скачайте данные на странице соревнования или здесь;
  3. Откройте baseline.ipynb, установите нужные пакеты, исполните весь код и загрузите ваше первое решение;
  4. Попытайтесь изменить baseline, чтобы улучшить показатели;
  5. Экспериментируйте!

Rekko Challenge стартует сегодня, 18 февраля. Решения принимаются до 18 апреля 23:59:59 по московскому времени.


Ждём всех и желаем удачи!


Кстати, мы ищем сотрудников. В том числе и разработчика рекомендательных систем.

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


  1. Lidus
    18.02.2019 19:39
    +1

    Самый информативный график, который я видел:
    image


    1. stanislavnikitin
      19.02.2019 08:52

      А что с ним не так?


      1. abbath0767
        20.02.2019 15:04

        Что то так, что то не так
        image


        1. Vinchi
          20.02.2019 19:40

          с количество было лучше