March 15, 2023

Lifehacks for recsys

Курсы/видео, которые можно пройти:

Вообще на ютубе куча видео (даже на русском!) по рексисамБиблиотеки:

Библиотеки:

  • специализированные: implicit и LightFM (Используются для коллаборативной фильтрации и гибридных методов)
  • Градиентный бустинг ( catboost / XGBoost / LightGBM )
  • рандом, который есть на github:
    • surprise (аддон к scipy, есть knn, svd, pmf, nmf)
    • EasyRec (штука от алибабы с моделями их разработки)
    • подборка от Microsoft
    • turicreate от Apple. Легка в использовании: скармливаем данные от объектов, матрицу взаимодействий, получаем на выходе рекомендации для юзера/объекта

Pandas lifehacks

  1. Если ID пользователей или объектов - строки, то их лучше приводить к CategoricalDtype
  2. Колонку с пропущенными значениями pandas приводит ко float, если значения столбца не float, то лучше использовать IntegerDType

Библиотеки для recsys используют на входе разреженные матрицы, поэтому загружаем данные в scipy.sparse

В основном используются три вида разреженных матриц:

  1. coo_matrix (A sparse matrix in COOrdinate format) - используется для создания разреженной матрицы
  2. csr(Compressed Sparse Column matrix)/csc_matrix(CS Row matrix) - используется для оптимизированных операций над матрицами

TP - True Positive - модель рекомендовала объект, с которым пользователь провзаимодействовал

FP - False Positive - модель рекомендовала объект, с которым пользователь не провзаимодействовал

TN - True Negative - модель не рекомендовала объект, с которым пользователь не провзаимодействовал

FN - False Negative - модель не рекомендовала объект, с которым пользователь провзаимодействовал

Популярные метрики для recsys:

  • Precision@K
    • Формула: TP/ (TP+FP)
    • Можно заметить, что под positives мы понимаем рекомендованные объекты, то есть наш топ-K, значит TP+FP=K
    • Итоговая формула: TP/K
    • Интерпретируется как доля релевантных рекомендаций
  • Recall@K
    • Формула: TP/(TP+FN)
    • TP+FN это количество известных релевантных объектов для пользователя
    • Интерпретируется как доля релевантных объектов, попавших в рекомендации

Модели

SVD

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

Реализации:

  • from scipy.sparse.linalg import svds
  • from sklearn.utils.extmath import randomized_svd
  • from sklearn.decomposition import TruncatedSVD

implicit kNN

❗ВАЖНО: kNN медленно работает, если число объектов большое. Поэтому есть такое решение - Approximately Nearest Neighbour. По этой штуке на гитхабе есть бенчмарк, разные датасеты - разные топовые модели

Доступны 3 модели, все item-ориентированы(схожесть объектов, а не пользователей):

  • CosineRecommender (косинусное расстояние)
  • BM25Recommender
  • TFIDFRecomender

Последние две модели подходят для количественных признаков (количество прослушиваний, суммарная длительность просмотра и т.д.)

Гиперпараметр один для всех - K, число соседей, число элементов в нашем топе

Процесс обучения - поиск К ближайших соседей для каждого объекта и сохранение схожести с ними

Как реализуется построение рекомендаций:

  1. Для каждого объекта из матрицы взаимодействий создаём топ К похожих объектов, суммируем топы, повторяющиеся объекты имеют больший вес
  2. Получаем из суммированного топа наш топ К

Ключевые методы:

  • fit
    • item_user_matrix - разреженная матрица с взаимодействиями в ориентации item-user (matrix.T)
  • recommend
    • user_id - номер строки, ID пользователя, для которого строим рекомендации
    • user_item_matrix - разреженная матрица с нашими взаимодействиями
    • N - топ рекомендаций
    • filter_already_liked_items - флаг для исключения уже известных объектов (user_item_matrix) Принимает значения True/False
    • filter_items - список столбцов (объектов), которые нужно исключить
    • Возвращает список кортежей (tuple) - (столбец(item_id), схожесть)

implicit iALS (можно использовать на GPU!)

implicit - переход от предсказания значений к предсказанию релевантности, учитывая веса

ALS - метод оптимизации, в котором мы поочерёдно фиксируем одну матрицу и делаем оптимизационный шаг по другой

Функционал идентичен implicit kNN

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

Есть возможность сделать предсказания для юзеров, которых не было при обучении, но для которых известны несколько взаимодействий. Для этого в методе recommend используем параметр recalculate_user

Итоговые предсказания должны использоваться только для ранжирования

Ключевые гиперпараметры

  • factors - размерность вектора
    • обычно в районе 16-256
    • при использовании GPU должна быть вида 32*N
  • iterations - количество итераций (1 итерация - проход по P и Q)
    • 10-200
  • regularization - регуляризация
    • 0.00001 - 1
  • use_gpu
  • random_state

При повторном вызове fit, метод делает ещё итерации на основе уже имеющихся векторов

LightFM

Отличительная фишка - модель строит отдельные вектора для фичей юзера и объекта. В качестве их векторных представлений берётся сумма векторов их фичей.

Модель одна, отличаются loss:

  • Logistic
  • Bayesian Personalized Ranking
  • WARP
  • k-os WARP

Обучается с помощью SGD. Есть две вариации:

  • adagrad
  • adadelta

LightFM Dataset

Для начала собираем датасет в нужном виде. Для этого на вход классу lightfm.data.Dataset подаём следующие данные:

  • users - ID пользователей
  • items - ID объектов
  • user_features - имена фичей юзеров
  • item_features - имена фичей объектов

Методы LightFM Dataset:

build_interactions - построение матрицы взаимодействий на основе списка кортежей вида:

  • (user_id, item_id)
  • (user_id, item_id, weight)

build_user_features/build_item_features - построение матрицы фичей на основе кортежей вида:

  • (user_id, [user_feature_name1, ..., user_feature_nameN])
  • (user_id, {user_feature_name1: weight,...})

WARP

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

Основная идея - переход от проверки качества предсказания значения к предсказанию качества ранжирования.

Достигается это путём сэмплирования негативных примеров и построения функции ошибки на основе соотношения предсказанных значений

Алгоритм обучения:

  1. Берём пользователя u
  2. Выбираем объект i, с которым он взаимодействовал
  3. Выбираем случайный объект j, с которым он не взаимодействовал
  4. Считаем скалярные произведения p = (u, i) и n = (u, j)
  5. Сравниваем p и n
    1. Если p>n, то переходим на шаг 2 (или шаг 1)
    2. Иначе считаем ошибку, правим веса и возвращаемся на шаг 3

Количество сэмплирований - гиперпараметр max_sampled

Ключевые гиперпараметры

  • no_components - размерность вектора
  • learning_rate - лёрнинг рейт при обучении в SGD
  • user_alpha/item_alpha - регуляризация по векторам пользователей и объектов
  • random_state

Есть loss-зависимые параметры, например для WARP:

  • max_sampled - максимальное количество сэмплирований негативных примеров для одной позитивной пары (пользователь, объект). Увеличение приводит к росту качества, но обучение замедляется

Предсказания в lightfm сырые, метод model.predict возвращает массив скоров по заданным парам (пользователь, объект)

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

  • 1) Получить оценку для всех объектов
    • model.predict (передаём ID пользователя и ID всех объектов)
    • user_vector * item_embeddings_matrix (получаем почти то же самое. что и при model.predict)
  • 2) Получить индексы самых больших скоров
    • np.argpartition(scores, -np.arange(N))[-N:]
  • 3) Перевести индексы в item_id

Ноутбук для практики по моделям implicit и lightfm