ПРИЛОЖЕНИЯ ДЛЯ МАШИННОГО ОБУЧЕНИЯ

Использование нейронной сети для рекомендации шоу на Netflix

Обучение модели прогнозированию вероятности попадания на крючок

Нет ничего мучительнее, чем начать что-то на Netflix и через 20 минут понять, что тебе это неинтересно. В этом проекте я попытался взять дело в свои руки, рассчитав вероятность того, что я закончу шоу с учетом определенных характеристик шоу.

5 месяцев карантина, и я был уверен, что посмотрел все, что хотел, на Netflix. Возможность заснуть в 2 часа ночи и встать с постели в 9 утра для работы не очень хорошо повлияла на мою продуктивность с активной точки зрения. Когда-то мне действительно нравилось ходить пешком 30 минут до станции Caltrain и обратно каждый будний день. Единственным утешением в моем медленном погружении в полную и крайнюю апатию является то, насколько продуктивно я проталкиваю оригиналы Netflix. Сочетание того, что мне нечего смотреть, и решения немного более продуктивно использовать свое время, побудило меня попробовать свои силы в использовании нейронных сетей, чтобы предсказать, что я должен смотреть дальше.

Шаг 1: Получение данных

Первым шагом в этом проекте было получение моей активности просмотра. Мне нравится, что Netflix упрощает доступ и даже загрузку в виде файла .csv.

С полным списком того, что я смотрел за последние 3 года на Netflix (если смотреть только на шоу с более чем 1 эпизодом, я посмотрел от 100 до 200 наименований на Netflix). Это позволило мне разработать более персонализированную систему рекомендаций. Если вы новый пользователь Netflix, платформа обходит недостаток данных о вашей активности просмотра, используя шаблоны просмотра людей, похожих на вас.

Затем я взял данные с IMDb.com, на котором есть множество характеристик шоу, таких как жанры, актеры, создатели, продолжительность эпизода и теги. Вот несколько кратких сводных таблиц, показывающих жанры, актеров и теги, которые были самыми популярными среди шоу, которые я смотрел на Netflix.

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

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

Шаг 2: Предварительная обработка и выбор функций

На этом этапе мы пытаемся привести наши данные к формату, который можно использовать для запуска нейронной сети. Каждое наблюдение в моем наборе данных представляло собой комбинацию телешоу и сезона. Например, 1 сезон сериала «13 причин почему». Функции, которые я добавил в свой набор данных, включают жанры, теги и номер сезона в качестве категориальных переменных, а также продолжительность эпизода в виде числовой переменной.

Чтобы создать что-то полезное, мне пришлось превратить набор данных в широкий набор данных с большим количеством фиктивных переменных. Например, для жанра «Драма» такие шоу, как «13 причин почему», «Неортодоксальное» и 18 других в этом жанре будут иметь значение 1, а все остальные шоу будут иметь значение 0.

Поскольку у меня было большое количество тегов, я получил широкий набор данных из более чем 2000 признаков всего для 200 с лишним наблюдений! Это привело к тому, что мне пришлось сократить количество функций, чтобы предотвратить серьезное переоснащение. Для этого я создал корреляционную матрицу, в которой рассматривалась корреляция между всеми функциями. Это позволило мне удалить сильно коррелированные функции, которые не давали бы никакой новой информации. Например, жанр «Преступление» был бы тесно связан с тегом «убийство», и поэтому «убийство» было бы удалено. Вот пример кода для этого:

X_corr = X.corr() 
columns = np.full((X_corr.shape[0],), True, dtype=bool) 
#Keep only columns with correlation less than 0.9 
for i in range(X_corr.shape[0]): 
    for j in range(i+1, X_corr.shape[0]): 
        if X_corr.iloc[i,j] >= 0.9: 
            if columns[j]: columns[j] = False 
selected_X = X[X.columns[columns]]

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

Шаг 3: Выбор модели

Я использовал Keras API для запуска TensorFlow. С целым рядом параметров, которые я мог указать, я решил попробовать пару разных параметров, чтобы увидеть, что приведет к максимальной точности, когда обученная модель будет применена к тестовому набору. Я решил сосредоточиться на следующих рычагах: количестве узлов, коэффициенте регуляризации и значении отсева. Вместе эти рычаги могли бы обеспечить модель, которая с наименьшей вероятностью не соответствовала бы модели или не соответствовала бы ей.

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

#Generate list of nodes to search over
nodes = np.arange(12, 61, 24)
#Generate list of dropout values to search over
dp = np.arange(0.2, 0.81, 0.3)
#Generate list of regularization coefficients to search over
reg = np.array([1e-4, 1e-5, 1e-6])
params = list(product(nodes, dp, reg))

Для полной реализации поиска по сетке перейдите по ссылке здесь!

Вот несколько уроков, которые я извлек из этого процесса поиска по сетке:

  • Попробуйте несколько моделей, прежде чем начать; понаблюдайте за функцией потерь: когда я впервые начал запускать пару тестовых моделей, я заметил, что моя функция потерь увеличивается для моего тестового набора, что было невероятно странно для модели, предназначенной для минимизации функции потерь. Это странное явление было решено путем указания меньшего размера шага, чтобы градиентный спуск не «перешагивал» минимум. Вот пример кода, как реализовать меньший размер шага:
from keras.callbacks import LearningRateScheduler
#define when you want to reduce your step size by setting n
n = 50
def lr_scheduler(epoch, lr):
    if (epoch > n):
        #define your step size here
        lr = 1e-5 
    return lr
...
model.fit(X_train, Y_train, epochs = 200, batch_size = 32,\                    callbacks = [LearningRateScheduler(lr_scheduler, verbose=1)])
  • Использование функции «Ранняя остановка»: это может показаться здравым смыслом, но команда «подгонка» по умолчанию в Keras не останавливает вашу нейронную сеть раньше, даже если следующая итерация не дает значительного улучшения. в минимизации вашей функции потерь. Кроме того, он не использует веса, которые привели к наименьшим потерям, если только вы не используете функцию keras.callbacks.EarlyStopping. Вот пример реализации:
from keras import callbacks
earlystopping = callbacks.EarlyStopping(monitor =”val_loss”,\ 
mode =”min”, patience = 30, restore_best_weights = True)

Как бы вы ни соблазнялись, вы не хотите указывать «val_accuracy» в качестве критерия остановки, так как это может привести к переобучению.

  • Важность k-кратной перекрестной проверки. Поскольку у меня был довольно небольшой обучающий и тестовый набор, опробование множества моделей только на одном типе обучающего и тестового набора, скорее всего, приведет к переоснащению. Таким образом, для каждой модели я попробовал ее на 10 различных комбинациях обучающих и тестовых наборов и выбрал среднюю точность для выбора модели. Для этого существует множество пакетов, но я реализовал простой цикл for и использовал разные начальные значения для каждой итерации разделения поезд-тест.
for i in range (0, 10): 
    X_train, X_test, Y_train, Y_test = train_test_split(X, Y,\ test_size = 0.2, random_state = 1234 + i, stratify = Y)
...
#Fit your model here
  • Опасность добавления слоев. В начале я добавил много слоев в свою модель, основываясь на своем интуитивном понимании того, что делает нейронная сеть. Поскольку нейронная сеть в основном пытается создать новые функции, которые лучше всего предсказывают результат с учетом существующих функций, я предположил, что лучше иметь больше слоев. Однако это привело к серьезному переоснащению. Поскольку я уже был в опасности переоснащения таким количеством функций, создание более сложной модели усугубило бы проблему.
  • Важность параллельной обработки.Когда я впервые начал поиск по сетке, чтобы найти оптимальное сочетание параметров, я замерил прогон и понял, что с примерно 729 различными моделями, которые я хотел попробовать, это было бы на последовательный запуск каждой модели ушло около 14 часов. С пакетом multiprocessing я смог запустить около 12 моделей одновременно, сократив общее время обработки до чуть более 2 часов.
#Always time your code first before running all your different models! Assume here that train_model is a function I created to train my model.
start = time.time()
train_model(params)
end = time.time()
print(f"Runtime of the program is {end - start}")
#Here is some simple code to perform parallel-processing for a grid search
import multiprocessing as mp
pool = mp.Pool(processes = 12)
test = pool.map(train_model, params)

Шаг 4. Прогнозирование того, что смотреть на Netflix

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

Чтобы получить полный набор доступных шоу на Netflix, я взял данные с Reelgood.com. Как оказалось, доступно около 1700 телешоу из всех (местных и зарубежных) версий Netflix. Затем я снова изучил данные IMDb.com, чтобы найти характеристики всех этих шоу.

Наконец, я применил свою модель и веса к своему полному списку названий Netflix, и в итоге я получил прогнозируемую вероятность того, что я закончу каждое название Netflix.

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

  • Ей это нужно (вероятность 97,3%), основанный на популярном фильме Спайка Ли о молодой чернокожей женщине, пытающейся разобраться в своей личной жизни. Звучало для меня как Дорогие белые люди, которые я любил!
  • Обычно я не люблю корейские дорамы, но Голос (вероятность 96,1%) — история о детективе, расследующем убийство, — прозвучал довольно интересно!
  • Рита (вероятность 94,5%) — история о откровенной и непокорной учительнице, которой необходимо взять под контроль свою личную жизнь? Это звучало немного похоже на персонажа Уильяма Х. Мэйси в Бесстыдниках, которого я просто обожаю. Кроме того, он из Дании, что делает его еще более увлекательным!
  • Струны души Долли Партон (вероятность 93,9 %) — Шоу о семье на музыку Долли Партон? Мне кажется, что Это мы сочетается с Маленькой Америкой, и я здесь ради этого.

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

В заключение, вот несколько вещей, которые я бы сделал, если бы сделал это снова:

  • Дата построения просмотра в модели:Люди меняются — и поэтому модели должны идти в ногу со временем! Понравилось бы мне то, что я смотрел 3 года назад? В этом смысле, возможно, имело смысл придавать значения сериалам, которые я недавно смотрел, выше или, возможно, ниже, если меня тошнит от определенного жанра? В общем, у меня есть небольшие навязчивые идеи, например. смотрел британские шоу только тогда, когда был в Лондоне на прошлый День Благодарения. Таким образом, взвешивание шоу, которое я только что закончил, может иметь смысл в этом случае.
  • Получение большего количества данных. Ничего не помогает модели, как больше данных. Тем более, что я всегда был в опасности переоснащения только из-за огромного количества функций, которые у меня были. Также повод посмотреть больше Netflix (хотя я только что получил подписку на Apple TV+ и поверьте мне, когда я говорю вам, что вам это нужно).
  • Извлечение более полезных функций. Были ли другие функции, которые я упустил из виду, которые были бы более описательными, чем жанры и теги? Например, счет на Rotten Tomatoes? (Что, как мне кажется, обычно достаточно хорошо предсказывает мою просмотровую активность, за некоторыми заметными исключениями, такими как 3-й сезон «13 причин почему» — у меня нет этому оправдания, и я понятия не имею, почему я так зависим).
  • Выполнить текстовую ассоциацию. Если бы у меня был некоторый опыт НЛП, я бы выбрал свои функции, используя более сложный алгоритм, чем корреляционные матрицы. Но эй, это идея для другого проекта, не так ли?
  • Выбор модели, основанной на высоком уровне отзыва. Я немного подумал об этом, но, учитывая характер задачи, не лучше ли выбрать модель с более высоким уровнем отзыва? Хотя это может создать много шума в рекомендуемых шоу, большое количество ложных срабатываний не стоит дорого.

Однако это все идеи для другого дня. Сегодня вечером, пожалуйста, извините меня, пока я возвращаюсь к завершению «13 причин почему», чтобы я мог продолжить свою жизнь.

Полный код этого проекта можно посмотреть на моем Github здесь: