Полное руководство по рекомендательным системам, использующим LightFM в Azure ML.

Введение

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

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



Введение в рекомендательные системы
Обзор некоторых основных алгоритмов рекомендаций.towardsdatascience.com



В моем предыдущем посте (Как генерировать синтетические данные с помощью Faker в Python и Azure ML) я показал, как генерировать фальшивые данные для обучения рекомендательной системы, сегодня поговорим немного о рекомендательных системах, а более конкретно углубимся в код с реальным примером. С этого момента я сосредоточусь на практической стороне рекомендательной системы с использованием LightFM.

Для начала я буду использовать 3 набора данных:

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

Последнее - это связь между UserId и идентификатором обучения, в основном, чтобы узнать, какой пользователь прошел какое обучение. В рекомендательных системах у вас также могут быть веса, вес — это, по сути, рейтинг, например, в фильмах вы можете оценить их от 0 до 5, поэтому вам решать, нужны вам веса или нет для вашего бизнеса. Если вам нужны веса, вы, вероятно, поместите это поле в набор данных TrainingsTaken.

Я работаю с Azure ML, поэтому я зарегистрировал наборы данных в ML Studio, как я это сделал, вы можете увидеть в моем предыдущем посте (Как генерировать синтетические данные с помощью Faker в Python и Azure ML).

С помощью приведенного ниже кода я загружаю наборы данных, уже зарегистрированные из Azure ML, в память как кадр данных pandas, что позволит нам позже манипулировать форматом данных по мере необходимости.

Если вы хотите проверить содержимое каждого фрейма данных, вы можете использовать df.head(5), и результат будет похож на следующие изображения.

Теперь у нас есть 3 кадра данных pandas, которые мы можем использовать с алгоритмом LightFM.

ЛайтFM

LightFM — это реализация на Python ряда популярных алгоритмов рекомендаций как для неявной, так и для явной обратной связи, включая эффективную реализацию потерь при ранжировании BPR и WARP. Он прост в использовании, быстр (благодаря оценке многопоточной модели) и дает высококачественные результаты. (источник: https://github.com/lyst/lightfm)

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

Для начала нам нужно создать набор данных LightFM, этот набор данных позволит нам позже сопоставить модель с данными в желаемом формате.

Метод подгонки

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

Мы будем передавать три параметра методу подгонки: список пользователей, список элементов и пользовательские функции. Передача списка пользователей и элементов довольно проста — просто используйте «User-Id» и «Training-Id». ' столбцы из trainingsttaken кадра данных.

Когда дело доходит до передачи user_features, лучше передать список, в котором каждый элемент имеет формат 'feature_name:feature_value'..

Тогда нашuser_features должен выглядеть примерно так:
['name:Susana Johnson', 'Age:42', 'los:IFS', 'ou:development', 'gender:F', 'skills:azure', 'language:dutch'].

Этот список был создан путем рассмотрения всех возможных feature_name,feature_value пар, которые можно найти в обучающем наборе. Например, для feature_name, равного Gender, может быть два feature_values, а именно M и F.

Фрагмент кода выше генерирует нужный нам список в формате, описанном выше, со всеми возможными комбинациями. Это то, что LightFM ожидает.

Теперь нам нужно вызвать метод .fit, который принимает список User-Id, Training-ID и список всех пользовательских функций (список выше).

После вызова метода fit я преобразовал свои поля trainingdf в числовые, поскольку это ожидаемый тип, и, наконец, используя dataset1экземпляр, который я вызываю build_interactions, в этом методе я делаю следующее: для перебора всех trainingstakendataframe и передачи их в качестве параметров один за другим, User-Id и Training-Id, при желании вы также можете передайте веса (рейтинги), в моем случае я игнорирую этот столбец, потому что вначале я предполагал, что все строки со значением 10 будут означать, что пользователь прошел обучение, но в данном случае это не имеет значения.

В строке № 13 метод построения взаимодействий возвращает разреженную матрицу взаимодействий и весов. Если вам нужно более четкое представление, вы можете использовать метод .todense для просмотра сводки плотной матрицы.

Создание пользовательских функций

Метод build_user_features требует таких параметров:
[
(user1, [feature1, feature2, feature3, ….]),
(user2, [feature1, feature2, feature3, ….]) ,
]

Помните, что feature1 , feature2, feature3 и т. д. должны быть одними из элементов списка user_features, который мы передали методу fit ранее.

Еще раз повторюсь, вот как сейчас выглядит наш список user_features:
['name:Susana Johnson', 'Age:42', 'los:IFS', 'ou:development', 'gender:F', 'skills:azure', 'language:dutch'].

Для нашего конкретного примера это должно выглядеть так:

[
     ('1', ['name:Susana Johnson', 'Age:32', 'los:IFS', 'ou:development', 'gender:F', 'skills:azure', 'language:dutch']),
     ('2', .....
 ]

Следующий метод и некоторые пояснения в этом сообщении в блоге были взяты из статьи Как бы я объяснил создание «гибридных рекомендателей LightFM пятилетнему ребенку!»

Конкретный код для моего варианта использования приведен ниже:

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

Наконец, нам нужно добавить идентификатор пользователя к каждому элементу массива, что можно сделать с помощью следующей строки:

user_tuple = list(zip(usersdf['User-Id'], feature_list))
user_tuple
Output:
[(8361131,
  ['ou:development',
   'skills:azure',
   'language:dutch',
   'grade:Junior',
   'career interests:solutions design']),
 (2162101,
  ['ou:development',
   'skills:javascript',
   'language:french',
   'grade:Junior',
   'career interests:javascript']),
 (81727,
  ['ou:operations',
   'skills:pm',
   'language:german',
   'grade:Junior',
   'career interests:pm']),

Мы почти на месте.

Теперь у нас есть функция пользователя в нужном формате, мы можем вызвать метод build_user_features:

user_features = dataset1.build_user_features(user_tuple, normalize= False)

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

user_features.todense()
matrix([[1., 0., 0., ..., 0., 0., 0.],
        [0., 1., 0., ..., 0., 0., 0.],
        [0., 0., 1., ..., 0., 0., 0.],
        ...,
        [0., 0., 0., ..., 0., 0., 0.],
        [0., 0., 0., ..., 0., 0., 0.],
        [0., 0., 0., ..., 1., 0., 0.]], dtype=float32)

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

Если мы вызовем атрибут .shape в матрице user_features, мы получим:

user_features.shape
(9996, 10025)
user_id_map, user_feature_map, item_id_map, item_feature_map = dataset1.mapping()
user_feature_map

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

Давайте построим модель

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

model = LightFM(loss='warp')
model.fit(interactions,
user_features= user_features,
epochs=10)

После того, как мы обучим нашу модель, мы можем оценить ее с помощью AUC.

from lightfm.evaluation import auc_score
train_auc = auc_score(model,
interactions,
user_features=user_features
).mean()
print('Hybrid training set AUC: %s' % train_auc)

Hybrid training set AUC: 0.9402231

Помните, что это фиктивные данные, поэтому модель может переобучиться, не слишком увлекайтесь метрикой 94%.

Давайте делать прогнозы для известных пользователей

Метод предсказания принимает 2 параметра: отображение идентификатора пользователя и список идентификаторов элементов. Здесь мы будем использовать user_id_map из предыдущего шага, чтобы получить ссылку на конкретного пользователя (user_x),

import numpy as np
user_x = user_id_map[9212216] #just a random user
n_users, n_items = interactions.shape # no of users * no of items
model.predict(user_x, np.arange(n_items)) # means predict for all

Это вернет оценку для каждого элемента (обучения) в формате массива:

array([-0.49955484, -0.4502962 , -0.6466697 , -0.7361969 , -0.30803648,
        0.01278364, -0.37532082, -0.2221036 , -0.7242191 , -1.6705698 ,
       -0.01221651, -0.23012483, -0.89942145, -1.3498331 , -0.7373183 ,
       -0.20021401,  0.21310112, -0.9948864 ,  0.13983092, -0.7846861 ,
       -0.5542359 , -0.30498767,  1.0424366 , -0.29013318, -0.23596957,
        0.1327716 , -0.49574524, -1.5379183 , -0.7636943 , -0.12699573,
        0.14224172, -0.4512871 , -0.49226752,  0.01528413,  0.4442131 ],
      dtype=float32)

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

Конкретно следует ожидать, что lfm.predict([0, 1], [8, 9]) вернет массив np.float32, который может выглядеть примерно так [0,42 0,31], где 0,42 равно оценка, присвоенная паре пользователь-элемент (0, 8), и 0,31 оценка, присвоенная паре (1, 9) соответственно

Если вы проверите документацию LightFM, вы также можете использовать predict_rank, и он вернет элементы в отсортированном порядке, где первыми будут рекомендации для этого конкретного пользователя.

Регистрация модели в Azure

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

import pickle
with open('savefile.pickle', 'wb') as fle:
pickle.dump(model, fle, protocol=pickle.HIGHEST_PROTOCOL)

Это сохранит файл рассола в вашем текущем каталоге.

from azureml.core import Workspace
from azureml.core.model import Model
ws = Workspace(subscription_id="x",resource_group="y",workspace_name="z")
model = Model.register(ws, model_name="recommender", model_path="./savefile.pickle")

И, наконец, с помощью приведенного выше кода мы регистрируем модель в Azure.

После регистрации модели в Azure ML вы можете развернуть ее как веб-службу или загрузить из кода Python, чтобы повторно использовать в своих прогнозах.

Заключительные слова

Сегодня мы прошли все шаги, чтобы понять рекомендательные системы, мы сослались на сообщение в блоге с четким теоретическим введением в рекомендательные системы, затем мы сгенерировали поддельные данные и зарегистрировали их в Azure ML как наборы данных, и, наконец, мы шаг за шагом приступили к подготовке наборы данных в определенные форматы, требуемые LightFM.

Позже мы сделали некоторые прогнозы для случайного идентификатора пользователя и, наконец, смогли зарегистрировать модель LightFM, сначала экспортировав файл в формате pickle, а затем зарегистрировав этот файл в качестве модели для последующего использования в Azure ML.

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

Дополнительные материалы на PlainEnglish.io. Подпишитесь на нашу бесплатную еженедельную рассылку новостей. Подпишитесь на нас в Twitter и LinkedIn. Присоединяйтесь к нашему сообществу Discord.