Как построить рекомендательную систему с цензурированными данными
В отличие от распространенного мнения о том, что система рекомендаций, которая теперь является стандартной частью онлайн-сервисов, требует использования всех исторических взаимодействий между пользователями и элементами для обеспечения точности обслуживания. Здесь я представляю один подход, позволяющий точно рекомендовать пользователям хорошие элементы, в то же время на стороне пользователя подвергая цензуре историческое взаимодействие пользователей с элементами, заменяя их поддельными взаимодействиями (преднамеренный информационный шум).
Цель создания шума в данных заключается в том, что на стороне системы нельзя сказать, является ли взаимодействие между пользователями и элементами реальным или случайным шумом, таким образом защищая пользователей от полного раскрытия их конфиденциальности. И это позволяет пользователям контролировать уровень конфиденциальности, предоставляемой онлайн-сервисам, завинчивая уровень генератора шума на стороне пользователя. Это также подразумевает новый экономический подход, который позволяет пользователям договариваться о ценности их конфиденциальности при использовании онлайн-сервисов, а не текущий подход «конфиденциальность для удобства», используемый большинством современных онлайн-сервисов.
Современные системы рекомендаций предназначены для работы с ненаблюдаемыми данными, поскольку большая часть данных обратной связи относится только к элементам, с которыми взаимодействуют пользователи, а эти невзаимодействующие элементы остаются неявными обратными связями. Которые можно рассматривать как отрицательные отзывы путем случайной выборки. Путем выборки для имитации ненаблюдаемых данных алгоритмы уже способны справляться и с зашумленными наблюдениями.
Активно добавляя шум во взаимодействие с пользователем для цензуры реальных данных со стороны пользователя, я демонстрирую, что шум не влияет на точность моделей, даже если большой процент взаимодействий заменяется шумом.
Во-первых, я использую Spotlight для создания рекомендательной модели, работающей с наборами данных последовательного взаимодействия, Movielens, для рекомендации следующего фильма. Модель рекомендаций изучает представление элементов фильмов и пользователей с помощью сложенных причинно-следственных слоев свертки (в нашем эксперименте установлено значение 1 слой) и генерирует неявные отрицательные отзывы путем равномерной выборки.
import numpy as np user_ids, item_ids, ratings, timestamps = zip(*[i.strip().split('\t') for i in open("./ml-100k/u.data").readlines()]) user_ids = np.array([int(u) for u in list(user_ids)]) item_ids = np.array([int(i) for i in list(item_ids)]) timestamps = np.array([int(s) for s in list(timestamps)]) interactions = Interactions(user_ids=user_ids, item_ids=item_ids, timestamps=timestamps) train, test = random_train_test_split(interactions)
Набор данных 100k Movielens содержит 943 пользователя, 1682 фильма и 100 000 взаимодействий между пользователями и фильмами. Взаимодействия разделены в соотношении 80/20 для обучения и тестирования. Взаимодействия в поезде разделены на три последовательности: список идентификаторов пользователей, список идентификаторов элементов, с которыми взаимодействует пользователь, и отметка времени этого взаимодействия. В обычном сценарии все взаимодействия между пользователями и элементами фиксируются и сохраняются на стороне сервера. В нашей системе пользователям разрешено отправлять поддельные взаимодействия с условием, что они взяты из однородного шума.
Я готовлю три сценария шума: 25%, 50% и 75%, имитируя соответствующее количество данных о поездах, заменяемых шумом в наборе данных.
import random preserving_25_percent_items = [] preserving_50_percent_items = [] preserving_75_percent_items = [] vmin = train.item_ids.min() vmax = train.item_ids.max() for real_item_idx in train.item_ids: random_item_idx = random.randint(vmin, vmax) sampling_threshold = random.random() if sampling_threshold < .25: preserving_25_percent_items.append(real_item_idx) else: preserving_25_percent_items.append(random_item_idx) if sampling_threshold < .5: preserving_50_percent_items.append(real_item_idx) else: preserving_50_percent_items.append(random_item_idx) if sampling_threshold < .75: preserving_75_percent_items.append(real_item_idx) else: preserving_75_percent_items.append(random_item_idx)
Затем визуализируйте форму распределения каждого набора данных с помощью графика гистограммы, чтобы сравнить влияние шума на форму распределения на каждом уровне шума.
from matplotlib import pyplot plt = pyplot.figure(figsize=(16,10)) pyplot.subplot(221) pyplot.hist(item_ids, bins=50, alpha=0.7, label='100% item preserving', color='red') pyplot.legend(loc='upper right') pyplot.subplot(222) pyplot.hist(preserving_25_percent_items, bins=50, alpha=0.7, color='green', label='25% item preserving, 75% random noise' ) pyplot.legend(loc='upper right') pyplot.subplot(223) pyplot.hist(preserving_50_percent_items, bins=50, alpha=0.7, color='blue', label= '50% item preserving, 50% random noise') pyplot.legend(loc='upper right') pyplot.subplot(224) pyplot.hist(preserving_75_percent_items, bins=50, alpha=0.7, label='75% item preserving, 25% random noise') pyplot.legend(loc='upper right') pyplot.show()
Как показано на графиках гистограммы, по сравнению с формой распределения полных данных, другие формы распределения становятся более плоскими в соответствии с добавленным уровнем шума.
Затем создайте три дополнительных цензурированных набора данных поезда с одинаковыми идентификаторами пользователей и временными метками, отличающимися только идентификаторами элементов.
user_ids = train.user_ids timestamps = train.timestamps preserving_25_percent_train = Interactions( user_ids=user_ids, item_ids=np.asarray(preserving_25_percent_items), timestamps=timestamps) preserving_50_percent_train = Interactions( user_ids=user_ids, item_ids=np.asarray(preserving_50_percent_items), timestamps=timestamps) preserving_75_percent_train = Interactions( user_ids=user_ids, item_ids=np.asarray(preserving_75_percent_items), timestamps=timestamps)
И подготовьте четыре обучающие модели для каждого случая обучающего набора данных. Все гиперпараметры идентичны среди моделей.
from spotlight.sequence.implicit import ImplicitSequenceModel model = ImplicitSequenceModel(embedding_dim=128) preserving_25_percent_model = ImplicitSequenceModel(embedding_dim=128) preserving_50_percent_model = ImplicitSequenceModel(embedding_dim=128) preserving_75_percent_model = ImplicitSequenceModel(embedding_dim=128)
Теперь он готов к обучению четырех моделей, что может занять несколько минут. Потери каждой модели немного отличаются, но все же сходятся.
model.fit(train.to_sequence(), verbose=True) preserving_25_percent_model.fit(preserving_25_percent_train.to_sequence(), verbose=True) preserving_50_percent_model.fit(preserving_50_percent_train.to_sequence(), verbose=True) preserving_75_percent_model.fit(preserving_75_percent_train.to_sequence(), verbose=True)
После установки оцените модели, используя оценки среднего обратного ранга (MRR). Оценка подсчитывается на единицу каждый раз, когда пользователь взаимодействует с элементом в наборе тестов, а затем возвращается список средних обратных рангов всех элементов теста.
from spotlight.evaluation import mrr_score train_mrrs = mrr_score(model, train) preserving_25_train_mrrs = mrr_score(preserving_25_percent_model, preserving_25_percent_train) preserving_50_train_mrrs = mrr_score(preserving_50_percent_model, preserving_50_percent_train) preserving_75_train_mrrs = mrr_score(preserving_75_percent_model, preserving_75_percent_train) test_mrrs = mrr_score(model, test) preserving_25_test_mrrs = mrr_score(preserving_25_percent_model, test) preserving_50_test_mrrs = mrr_score(preserving_50_percent_model, test) preserving_75_test_mrrs = mrr_score(preserving_75_percent_model, test) print('For 100% preserving interactions') print('Train MRRS {:.3f}, test MRRS {:.3f}'.format(train_mrrs.sum(), test_mrrs.sum())) print('For 25% preserving interactions') print('Train MRRS {:.3f}, test MRRS {:.3f}'.format(preserving_25_train_mrrs.sum(), preserving_25_test_mrrs.sum())) print('For 50% preserving interactions') print('Train MRRS {:.3f}, test MRRS {:.3f}'.format(preserving_50_train_mrrs.sum(), preserving_50_test_mrrs.sum())) print('For 75% preserving interactions') print('Train MRRS {:.3f}, test MRRS {:.3f}'.format(preserving_75_train_mrrs.sum(), preserving_75_test_mrrs.sum()))
Для 100 % сохранения взаимодействий
Обучите MRRS 9.566, проверьте MRRS 10.132
Для 25 % сохранения взаимодействий
Обучите MRRS 6.064, проверьте MRRS 11.379
Для 50 % сохранения взаимодействий
Обучить MRRS 8.932, протестировать MRRS 12.749
Для 75% сохранения взаимодействий
Обучить MRRS 9.836, протестировать MRRS 12.488
И результаты довольно удивительны: с сохранением элементов на 25%, 50% и 75% эти модели работают лучше на тестовом наборе соответственно 11,379, 12,488 и 12,749 по сравнению с 10,132 для модели с полными данными. Даже если случайно получилось лучше, можно подтвердить, что рекомендательная модель все еще работает с зашумленными данными.
Нассим Николас Талеб, автор «Черного лебедя», однажды заявил, что большую часть продаж издателя внесла небольшая группа авторов. То же наблюдение можно применить и к этому набору данных о фильмах, когда некоторые известные фильмы вызывают наибольший интерес пользователей, а другие фильмы не обладают достаточной пропускной способностью для привлечения внимания пользователей. Здесь я не хочу заявлять, что это предвзятость в наборе данных, а затем предлагать другой вид обработки начальной загрузки, поскольку я не ожидаю справедливости в любых данных, основанных на эмоциях человека. Возможно, система рекомендаций должна сосредоточиться только на поиске хорошего контента среди пула контента.
Вот полный основной код, просто скачайте и извлеките набор данных в папку «./ml-100k», после чего результат можно будет проверить.
На следующем этапе я планирую сообщить о нашем подходе с комплексной проверкой с использованием различных типов и размеров наборов данных, а также продемонстрировать концепцию этой системы рекомендаций.
Наше решение для искусственного интеллекта имеет открытый исходный код и активно разрабатывается в рамках проекта Telar, а также несколько интересных статей от нас:
- Telar Social использует доменно-ориентированный дизайн (DDD) для микросервисов
- Как мы используем ИИ для обеспечения конфиденциальности в Telar Social
- Взаимодействие сервер-клиент в режиме реального времени с использованием сервера Websocket и Redux в Telar Social
- Внешний интерфейс Telar Social DI (внедрение зависимостей)
- Telar Social AIМодерация
- Рекомендация Telar Social AI с сохранением конфиденциальности
👋 Для получения дополнительной информации посетите официальный сайт Телар Пресс или команды Красное Золото.
Все виды обратной связи всегда приветствуются. Спасибо за ваше время!