Совместная фильтрация на основе соседства

Большие данные
Совместная фильтрация на основе соседства

В этой статье используется простой код для представления механизма алгоритма совместной фильтрации на основе соседства.

Чтобы сделать процесс описания более понятным, здесь мы используемсамодельные данные. В каждой строке записывается оценка пользователя для книги в диапазоне от 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