Пошаговое руководство по реализации механизма рекомендаций по скрытым факторам с использованием библиотеки Surprise в Python.

Этот пост - последняя часть моей серии рекомендаций Python Surprise, в которой я представляю методы, которые я использовал в своем проекте механизма рекомендаций для настольных игр. (Смотрите мой репозиторий на GitHub для всего проекта.)

Предыдущие посты в серии:

Часть 1: Как создать систему рекомендаций на основе памяти с помощью Python Surprise: я рекомендую сначала прочитать этот пост, если вы не знакомы с темой, особенно с этапами импорта и подготовки данных, поскольку они идентичны для подхода на основе памяти и на основе модели.

Часть 2: Мой код Python для гибких рекомендаций: этот пост содержит дополнительный код, который я написал для структуры рекомендаций, который позволяет создавать прогнозы без повторного обучения всей модели. Однако этот подход работает только для более простых моделей в стиле KNN.

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

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

Системы рекомендаций на основе моделей

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

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

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

Математическая формула

В модели SVD оценочная оценка пользователя u по элементу i рассчитывается как:

где μ - общий средний рейтинг, а все остальные параметры рассчитываются на основе модели с помощью метода градиентного спуска. Таким образом, модель попытается подогнать этот оценочный рейтинг ко всем известным рейтингам, минимизировать MSE и получить наиболее близкое соответствие.

bᵤ и bᵢ - скаляры, они представляют предубеждения пользователя u или элемента i. Например, пользователь u имеет тенденцию bᵤ отклоняться от общего среднего рейтинга. Эти смещения можно отключить при подборе модели, что в основном и есть модель NMF.

pᵤ и qᵢ - векторы, а их длина - гиперпараметр модели, n. Они представляют собой фактическую часть модели матричной факторизации, в которой происходит волшебство. Каждый пользователь и элемент будут представлены своим вектором, который пытается отразить их сущность в n числах. И мы получаем рейтинг, умножая пары элемент - пользователь (и, конечно, добавляя средние значения и отклонения).

Может возникнуть соблазн подумать об этих n измерениях как о чем-то понятном для человека. Например, если мы имеем дело с настольными играми, первое измерение могло бы измерить, насколько сложен свод правил. Теперь пользователь, который уделяет большое внимание сложности (что означает, что у них большое число на qᵢ [1]) , будет выставлен высокий рейтинг в игру с высокой сложностью (что означает высокий pᵤ [1]). Однако, по моему опыту, это бывает редко, довольно сложно сопоставить значение с отдельными координатами, выходящими из модели.

Тренировка модели с удивлением

Предполагая, что у вас уже есть импортированная и настроенная база данных (еще раз, если вы не знаете, как это сделать, обратитесь к моему предыдущему посту), работа с SVD аналогична работе с другими моделями в Surprise. Во-первых, вам нужно импортировать модель:

from surprise import SVD

Затем вы можете поместить модель в набор и протестировать производительность модели, используя оценку RMSE (что означает среднеквадратичную ошибку, чем ниже, тем лучше):

SVD_model = SVD()
SVD_model.fit(trainset)
predictions = SVD_model.test(testset)
accuracy.rmse(predictions)

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

SVD_model.predict(uid = 'TestUser1', iid = '161936')

Анализ модели

Как вы видели, подобрать модель SVD просто, но проанализировать результат немного сложнее.

Используя методы pu, qi, bu и bi объекта SVD, вы можете получить соответствующие значения из математической формулы. В своем проекте я обнаружил, что qi является наиболее интересным: вызов метода qi на уже подходящей SVD модели вернет 2-мерный массив, где высота - это количество элементов, ширина - это параметр модели n_factor (мы будем обсудите параметры в следующем разделе). Каждая строка представляет элемент с n_factor количеством факторов, это так называемые скрытые факторы, найденные моделью, и они представляют элемент в расчетах рейтинга.

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

Каковы наши гиперпараметры?

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

Прежде чем мы начнем, обратите внимание, что оценка RMSE с параметрами по умолчанию для моего проекта составила 1,332, и после того, как моя виртуальная машина GCP работала в течение нескольких часов, мне удалось снизить RMSE до 1,3210. Это не так много улучшений ... Может быть, это просто моя база данных, может быть, потому что параметры по умолчанию в Surprise были настроены эффективно. Несмотря на это, я думаю, что гиперпараметры все же важно учитывать.

Я настроил четыре гиперпараметра:

  • n_factors: Мы кратко коснулись этого в предыдущем разделе, этот параметр определяет размер ваших векторов pᵤ и qᵢ. Это определяет, сколько скрытых факторов модель попытается найти. Чем выше число, тем больше мощности у модели, но также выше вероятность переобучения.
  • n_epochs: Этот коэффициент определяет, сколько раз повторяются вычисления градиентного спуска. Увеличение этого числа делает прогнозы более точными, но требует больше времени для расчета.
  • lr_all: коэффициент скорости обучения для всех параметров. Это размеры шага, которые модель будет использовать для минимизации функции стоимости, подробнее см. В Сюрпризной документации.
  • reg_all: Коэффициент регуляризации для всех параметров. Surprise использует регуляризацию L2, что примерно означает, что он попытается минимизировать различия между квадратами значений параметров. (Все параметры - это bᵤ, bᵢ, pᵤ и qᵢ.)

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

Настройка гиперпараметров

Мы собираемся использовать GridSearchCV для настройки гиперпараметров в Surprise. Он работает в основном так же, как и его аналог в scikit-learn, как следует из названия, он будет искать все возможные комбинации в сетке гиперпараметров, используя перекрестную проверку.

Во-первых, нам нужен словарь, в котором ключи - это имена гиперпараметров, а значения - это списки различных элементов, которые вы хотите проверить:

param_grid = {
    'n_factors':[5, 10,20],
    'n_epochs': [5, 10, 20], 
    'lr_all': [0.002, 0.005],
    'reg_all': [0.4, 0.6]}

Вы должны быть осторожны с тем, как вы устанавливаете эти параметры, так как каждая из возможных комбинаций будет проверена. В наших случаях это 3 * 3 * 2 * 2 = 36 различных комбинаций, и для каждой из них модель будет запускаться несколько раз, в зависимости от выбранной вами перекрестной проверки. Я оставил параметр cv равным 5-кратному, что означает, что всего будет выполнено 36 * 5 = 180 моделей.

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

Когда у вас есть сетка параметров, вы можете настроить объект GridSearchCV следующим образом:

gs_model = GridSearchCV(
    algo_class = SVD,
    param_grid = param_grid,
    n_jobs = -1,
    joblib_verbose = 5)

Первый параметр algo_class - это тип модели, которую вы хотите использовать. n_jobs = -1 просто сообщает модели, что она может использовать все доступные процессоры, что очень желательно, если у вас есть параллелизируемая операция. Еще один параметр, который вы, возможно, захотите изменить, - это cv, я просто оставил его по умолчанию, который выполняет 5-кратную перекрестную проверку.

Затем вы можете просто уместить данные:

gs_model.fit(data)

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

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

gs_model.best_params

Мой процесс

Мой процесс при работе с GridSearchCV был следующим:

  • Подобрать GridSearchCV модель для всех данных с сеткой параметров, охватывающей широкий диапазон
  • Рассчитан RMSE балл с перекрестной проверкой
  • Повторил процесс для другой сетки параметров, которая либо углублялась до более низкого уровня, если оптимальный параметр казался посередине, либо исследовала более высокие / более низкие параметры, если казалось, что оптимальный параметр находится на границе предыдущей сетки параметров.
  • Как только уменьшение RMSE оценки стало минимальным, сохраните лучшие гиперпараметры и примените их в SVD моделях на следующих этапах.
  • Поскольку я хотел сравнить результаты с моими предыдущими моделями типа KNN, я запустил модель SVD на trainset и рассчитал результат теста RMSE на testset

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

Вывод

На этом я завершаю серию статей о проекте системы рекомендаций.

Было интересно работать со всеми этими разными подходами, особенно насколько близкими оказались их результаты. Я потратил много времени на определение лучшей модели, но правда в том, что даже самые простые модели KNN работали относительно хорошо.

Я думаю, что отчасти это связано с тем, что мои данные были не так скудны, как это обычно бывает с системами рекомендаций. Были заполнены 10% возможных оценок пользователей, что считается чрезвычайно высоким показателем. Представьте, если бы люди в среднем купили 10% всех товаров на Amazon! Высокое соотношение объясняется тем, что я ограничил свое внимание 100 лучшими настольными играми всех времен, которые, естественно, являются популярными. В качестве дополнительного бонуса средние рейтинги были довольно высокими и близкими, поскольку все они считаются хорошими играми.

использованная литература

Сюрприз-документация:



Мои предыдущие посты в серии: