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

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

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

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

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

Набор данных 10k Books
В этом руководстве я выбрал Набор данных Goodbooks-10k, который я нашел на Kaggle, чтобы начать работу. Я всегда боялся разочароваться в книге после прочтения увлекательной, поэтому я подумал, что это решит личную борьбу и, в общем, может быть просто забавным занятием, чтобы поговорить с друзьями, которые спрашивают меня, что мне читать дальше.
ZIP-файл содержит несколько наборов данных (book_tags, книги, рейтинги, теги). Мы будем использовать только книги и наборы данных с рейтингами, которые содержат столбцы, релевантные для нашего анализа.

Перво-наперво, давайте импортируем необходимые библиотеки.

import pandas as pd
import numpy as np
import sklearn
from sklearn.decomposition import TruncatedSVD
import warnings

Давайте загрузим наборы данных. Набор данных «книги» содержит 23 столбца. Мы разрежем данные и отбросим переменные, чтобы оставить только интересующие столбцы. Мы сохраним набор данных рейтингов как есть.
Затем мы объединим оба набора данных для book_id. Book_id более надежен, чем original_title, поскольку форматирование некоторых заголовков может отличаться. Прежде чем перейти к созданию матрицы, мы удалим дубликаты в попарных комбинациях user_id и book_id, а также user_id и original_title.

books = pd.read_csv('books.csv', sep=',')
books = books.iloc[:, :16]
books = books.drop(columns=['title', 'best_book_id', 'work_id', 'books_count', 'isbn', 'isbn13', 'original_publication_year','language_code','work_ratings_count','work_text_reviews_count'])
books.head(5)
ratings = pd.read_csv('ratings.csv', sep=',')
ratings.head(5)
df = pd.merge(ratings, books, on="book_id")
df.head(5)
df1= df.drop_duplicates(['user_id','original_title'])
df1= df.drop_duplicates(['user_id','book_id'])
df1.head(10) #went down from 79701 to 79531 
df1.shape #(79531, 8)

Метод матричной факторизации и SVD - интуиция

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

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

######################################
####MATRIX FACTORIZATION
######################################
books_matrix = df1.pivot_table(index = 'user_id', columns = 'original_title', values = 'rating').fillna(0)
books_matrix.shape #(28554, 794)
books_matrix.head()

Если вы посмотрите на графическое изображение ниже, вы поймете, что происходило за кулисами. Во-первых, мы создали матрицу A с формой (m * d) = (books * user_id) и матрицу B с shape (d * n) = (рейтинги * user_id).

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

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

X = books_matrix.values.T
X.shape
#Fitting the Model
SVD = TruncatedSVD(n_components=12, random_state=0)
matrix = SVD.fit_transform(X)
matrix.shape #(812, 12)

Вы заметите, что наша недавно созданная матрица очень разреженная. В зависимости от того, какое случайное состояние вы указали, количество столбцов состоит из 5 цифр, что означает 5-значное пространство. Вот тут и вмешивается метод СВД.

Подобно тому, как мы использовали бы метод извлечения функций PCA / Kernel PCA для других наборов данных, SVD - это еще один метод, который мы применяем к матрицам в рекомендательных приложениях. SVD может сократить наши размеры до меньшего числа, чтобы описать разброс данных. Что здесь происходит, так это то, что SVD будет искать скрытые функции и извлекать их из данных, чтобы снизить количество функций, скажем, с 10.000 до всего лишь 10, и сэкономит нам огромное количество вычислительной мощности, а также позволит избежать переобучения данных. Для этого упражнения я установил количество компонентов для нашего SVD равным 12. Теперь, что осталось для применения модели, это подгонка и преобразование обучающих данных X.

Примечание. Обычно после применения SVD обычно вводят термин регуляризации (термин справа внизу), чтобы избежать переобучения данных:

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

Создание коэффициента корреляции
Затем мы создаем функцию коэффициента корреляции для всех элементов в матрице с помощью функции numpy np_corrcoef. Мы назовем это «corr».
Как только мы применим «corr» к книге, которая действительно похожа, функция вычислит все коэффициенты корреляции с оставшимися книгами и вернет все книги, которые нам, скорее всего, понравятся, как хорошо.

import warnings
warnings.filterwarnings("ignore",category =RuntimeWarning)#to avoid RuntimeWarning #Base class for warnings about dubious runtime behavior.
corr = np.corrcoef(matrix)
corr.shape

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

Проверка результатов
Давайте теперь проверим результаты. Я создам вектор под названием «заголовки» и перечислю элементы. Я выберу книгу, которая мне нравится, в качестве указателя. «Воспоминания гейши» - одна из моих любимых художественных книг, так что давайте продолжим.

title = books_matrix.columns
title_list = list(title)
samia = title_list.index('Memoirs of a Geisha')
corr_samia  = corr[samia]
list(title[(corr_samia >= 0.9)])

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

Выглядит хорошо! Я уже прочитал много из вышеперечисленного и могу засвидетельствовать, что некоторые из них были в моем списке в какой-то момент (например, Персеполис, В диких условиях, Великий Гэтсби, 100 лет одиночества, Суть дела и т. Д.). Мне все еще любопытно, какие скрытые особенности здесь также используются в алгоритме для выбора «Думай и обогащайся», поскольку я бы отнес его к другой категории (научная литература + другие соображения), но опять же это обнаруживает ограничение в этом алгоритме. что может иметь отношение к весам независимых переменных, которые мы ему скармливали.

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

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

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

Следуйте за мной в Linkedin
Подключитесь в Twitter