В этой статье используется простой код для представления механизма алгоритма совместной фильтрации на основе соседства.
Чтобы сделать процесс описания более понятным, здесь мы используемсамодельные данные. В каждой строке записывается оценка пользователя для книги в диапазоне от 1 до 5.
import pandas as pd
data_url = 'https://gist.githubusercontent.com/guerbai/3f4964350678c84d359e3536a08f6d3a/raw/f62f26d9ac24d434b1a0be3b5aec57c8a08e7741/user_book_ratings.txt'
df = pd.read_csv(data_url,
sep = ',',
header = None,
names = ['user_id', 'book_id', 'rating'])
print (df.head())
print ('-----')
user_count = df['user_id'].unique().shape[0]
book_count = df['book_id'].unique().shape[0]
print ('user_count: ', user_count)
print ('book_count: ', book_count)
user_id book_id rating
0 user_001 book_001 4
1 user_001 book_002 3
2 user_001 book_005 5
3 user_002 book_001 5
4 user_002 book_003 4
-----
user_count: 6
book_count: 6
Создайте матрицу отношений пользователя и элемента
Теперь создайте матрицу отношений пользователя и элемента, которая имеет решающее значение в рекомендательной системе, на основе загруженных данных. Это можно понимать как таблицу в базе данных, книга - это столбец, строка соответствует пользователю, когда пользователь прочитал книгу и поставил ей оценку, заполните балл в соответствующей позиции, а другие позиции установлены на 0, что указывает на отсутствие Seen.
Следует отметить, что значения матрицы представлены нижними индексами, такими какmatrix[2][2]
Соответствует рейтингу третьего пользователя третьей книги, так что вотuser_id
, book_id
Соответствие координатам матрицы, выраженное с помощью pandas Series.
user_id_index_series = pd.Series(range(user_count), index=['user_001', 'user_002', 'user_003', 'user_004', 'user_005', 'user_006'])
book_id_index_series = pd.Series(range(book_count), index=['book_001', 'book_002', 'book_003', 'book_004', 'book_005', 'book_006'])
import numpy as np
def construct_user_item_matrix(df):
user_item_matrix = np.zeros((user_count, book_count), dtype=np.int8)
for row in df.itertuples():
user_id = row[1]
book_id = row[2]
rating = row[3]
user_item_matrix[user_id_index_series[user_id], book_id_index_series[book_id]] = rating
return user_item_matrix
user_book_matrix = construct_user_item_matrix(df)
print ('用户关系矩阵长这样:')
print ('-----')
print (user_book_matrix)
用户关系矩阵长这样:
-----
[[4 3 0 0 5 0]
[5 0 4 0 4 0]
[4 0 5 3 4 0]
[0 3 0 0 0 5]
[0 4 0 0 0 4]
[0 0 2 4 0 5]]
Вычислить матрицу подобия
Так называемое сходство, здесь мы используем косинусное сходство, а другие включают корреляцию Пирсона, евклидово расстояние, сходство Жаккара и т. д. Различия не перечислены. Формула расчета:
Получил это сейчасuser_book_matrix
, каждый пользователь и каждый элемент могут соответствовать вектору, напримерuser_book_matrix[2]
представлятьuser_003
вектор, равный[4, 0, 5, 3, 4, 0]
,иuser_book_matrix[:,2]
представляет собойbook_003
:[0, 4, 5, 0, 0, 2]
.
Таким образом, матрица сходства пользователей и матрица сходства элементов могут быть рассчитаны на основе пользователя и на основе элемента соответственно.
Взяв в качестве примера матрицу сходства пользователей, после расчета будет получена матрица формы (user_count, user_count), напримерuser_similarity_matrix[2][3]
0,5, значитuser_002
иuser_003
Косинусное сходство равно 0,5.
Эта матрица является симметричной матрицей, соответственно,user_similarity_matrix[3][2]
тоже 0.5, а пользователь естественно максимально похож на себя, так что естьuser_similarity_matrix[n][n]
всегда равен 1.
def cosine_similarity(vec1, vec2):
return round(vec1.dot(vec2)/(np.linalg.norm(vec1)*np.linalg.norm(vec2)), 2)
def construct_similarity_matrix(user_item_matrix, dim='user'):
if dim == 'user':
similarity_matrix = np.zeros((user_count, user_count))
count = user_count
else:
similarity_matrix = np.zeros((book_count, book_count))
count = book_count
get_vector = lambda i: user_item_matrix[i] if dim == 'user' else user_item_matrix[:,i]
for i in range(user_count):
i_vector = get_vector(i)
similarity_matrix[i][i] = cosine_similarity(i_vector, i_vector)
for j in range(i, count):
j_vector = get_vector(j)
similarity = cosine_similarity(i_vector, j_vector)
similarity_matrix[i][j] = similarity
similarity_matrix[j][i] = similarity
return similarity_matrix
user_similarity_matrix = construct_similarity_matrix(user_book_matrix)
book_similarity_matrix = construct_similarity_matrix(user_book_matrix, dim='book')
print ('user_similarity_matrix:')
print (user_similarity_matrix)
print ('book_similarity_matrix:')
print (book_similarity_matrix)
user_similarity_matrix:
[[1. 0.75 0.63 0.22 0.3 0. ]
[0.75 1. 0.91 0. 0. 0.16]
[0.63 0.91 1. 0. 0. 0.4 ]
[0.22 0. 0. 1. 0.97 0.64]
[0.3 0. 0. 0.97 1. 0.53]
[0. 0.16 0.4 0.64 0.53 1. ]]
book_similarity_matrix:
[[1. 0.27 0.79 0.32 0.98 0. ]
[0.27 1. 0. 0. 0.34 0.65]
[0.79 0. 1. 0.69 0.71 0.18]
[0.32 0. 0.69 1. 0.32 0.49]
[0.98 0.34 0.71 0.32 1. 0. ]
[0. 0.65 0.18 0.49 0. 1. ]]
рекомендовать
Имея в руках матрицу подобия, можно начинать рекомендации.
Прежде всего, пользователи могут рекомендовать список пользователей с такими же вкусами, как у них самих, что очень важно для продуктов с социальными атрибутами, таких как Zhihu, Douban и Netease Cloud Music.
Метод очень прост: порекомендовать пользователю А K пользователей со схожими вкусами (здесь K равно 3), тогдаuser_similarity_matrix
В порядке значений строки о A вы можете взять K битов от старшего к низшему.
def recommend_similar_users(user_id, n=3):
user_index = user_id_index_series[user_id]
similar_users_index = pd.Series(user_similarity_matrix[user_index]).drop(index=user_index).sort_values(ascending=False).index[:n]
return np.array(similar_users_index)
print ('recommend user_indexes %s to user_001' % recommend_similar_users('user_001'))
recommend user_indexes [1 2 4] to user_001
В то же время в измерении предмета подобные рекомендации также очень полезны: например, QQ Music рекомендует песни, похожие на музыку, которую слушает пользователь, а Amazon рекомендует похожие предметы на предмет, который пользователь только что купил.
Код тот же, что и для рекомендации похожих пользователей, и никакой дополнительной обработки не требуется.
def recommend_similar_items(item_id, n=3):
item_index = book_id_index_series[item_id]
similar_item_index = pd.Series(book_similarity_matrix[item_index]).drop(index=item_index).sort_values(ascending=False).index[:n]
return np.array(similar_item_index)
print ('recommend item_indexes %s to book_001' % recommend_similar_items('book_001'))
recommend item_indexes [4 2 3] to book_001
Следующим шагом является рекомендация книг для пользователей, сначала выберите K пользователей, которые наиболее похожи на пользователя, затем найдите набор книг, оцененных этими K пользователями, а затем удалите те части, которые оценил пользователь. В остальных книгах по следующей формуле вычислить ожидаемую оценку пользователя для определенной книги, а затем отсортировать оценки от высокой к низкой и вывести.
def recommend_item_to_user(user_id):
user_index = user_id_index_series[user_id]
similar_users = recommend_similar_users(user_id, 2)
recommend_set = set()
for similar_user in similar_users:
recommend_set = recommend_set.union(np.nonzero(user_book_matrix[similar_user])[0])
recommend_set = recommend_set.difference(np.nonzero(user_book_matrix[user_index])[0])
predict = pd.Series([0.0]*len(recommend_set), index=list(recommend_set))
for book_index in recommend_set:
fenzi = 0
fenmu = 0
for j in similar_users:
if user_book_matrix[j][book_index] == 0:
continue # 相似用户未看过该书则不计入统计.
fenzi += user_book_matrix[j][book_index] * user_similarity_matrix[j][user_index]
fenmu += user_similarity_matrix[j][user_index]
if fenmu == 0:
continue
predict[book_index] = round(fenzi/fenmu, 2)
return predict.sort_values(ascending=False)
recommend_item_to_user('user_005')
3 4.0
2 2.0
dtype: float64
Вышеупомянутое предназначено для использования матрицы сходства пользователей, чтобы рекомендовать элементы для пользователя, и то же самое можно изменить, чтобы использовать матрицу сходства элементов для рекомендации книг пользователю.
Метод заключается в том, чтобы найти все книги, которые читал пользователь, найти две книги, наиболее похожие на книгу для каждой книги, удалить из всех найденных книг те, которые пользователь прочитал, а затем предсказать книги, которые имеют рейтинг пользователем.очки.
Здесь действительно есть некоторая путаница, и ее легко спутать с вышеизложенным, поэтому пример следующий:
Напримерuser_001
читать книгуbook_001
, book_002
,book_005
, найденный набор книг, а затем удалить результат, который прочитал пользователь:{'book_003', 'book_006'}
, бытьbook_003
оценка прогноза, обратите внимание, что это такжеbook_001
иbook_005
узнать на их основеbook_001
иbook_005
Баллы и сходствоbook_003
Оценка:(4*0.79+5*0.71)/(0.79+0.71)=4.47
.
Тогда функция рекомендации элементов для пользователей на основе элементов:
def recommend_item_to_user_ib(user_id):
user_index = user_id_index_series[user_id]
user_read_books = np.nonzero(user_book_matrix[user_index])[0]
book_set = set()
book_relation = dict()
for book in user_read_books:
relative_books = recommend_similar_items(book, 2)
book_set = book_set.union(relative_books)
book_relation[book] = relative_books
book_set = book_set.difference(user_read_books)
predict = pd.Series([0.0]*len(book_set), index=list(book_set))
for book in book_set:
fenzi = 0
fenmu = 0
for similar_book, relative_books in book_relation.items():
if book in relative_books:
fenzi += book_similarity_matrix[book][similar_book] * user_book_matrix[user_index][similar_book]
fenmu += book_similarity_matrix[book][similar_book]
predict[book] = round(fenzi/fenmu, 2)
return predict.sort_values(ascending=False)
recommend_item_to_user_ib('user_001')
2 4.47
5 3.00
dtype: float64
Суммировать
Вышеизложенное представляет собой введение в механизм работы совместной фильтрации на основе предметной области.Используя всего две простые математические формулы и различную обработку кода, можно дать некоторые рекомендации для пользователей.
С точки зрения рекомендации элементов пользователям, на основе пользователей и на основе элементов есть свои особенности.
Основываясь на результатах рекомендаций, данных пользователями, он больше зависит от социального поведения групп пользователей, похожих на текущего пользователя.Учитывая вычислительные затраты, он подходит для случая небольшого числа пользователей.В то же время , это более удобно для проблемы холодного запуска новых добавленных элементов.Однако по сравнению со сходством элементов, интерпретируемость рекомендаций, основанных на сходстве между пользователями, относительно слаба, и с точки зрения реального времени поведение новых пользователей может не обязательно приведет к изменению результатов.
Результаты рекомендаций на основе элементов больше ориентированы на собственное индивидуальное поведение пользователя, что подходит для небольшого количества элементов.Обнаружение элементов с длинным хвостом лучше, чем на основе пользователей.В то же время могут быть рекомендованы вновь добавленные пользователи быстро, а ассоциации между элементами легче понять и интерпретировать, а новое поведение пользователей должно привести к изменениям в результатах.
Очевидно, что на основе элементов обычно лучше, чем на основе пользователей.Исторически верно, что сначала был изобретен на основе пользователей, а затем Amazon изобрел алгоритм на основе элементов, и теперь продуктов, основанных на пользователях, меньше.
Ссылаться на
Intro to Recommender Systems: Collaborative Filtering
[Рекомендовано соседями] Люди делятся на группы, кто бы вы ни были, вы можете увидеть мир
Специальный выпуск архитектора: рекомендательные системы (теория)
Практика алгоритма рекомендации Meituan