原理:
基于邻域的协同过滤算法主要分为基于用户的协同过滤和基于项目的协同过滤。
基于用户的协同过滤的基本原理是找到与目标用户兴趣相似的其他用户,然后根据这些相似用户的评分来预测目标用户对未评分的项目的评分。具体步骤如下:
- 计算用户之间的相似度。这通常可以通过余弦相似度或皮尔逊相关系数来完成,比较两个用户评分的项目,并根据评分是否相似来计算相似度。
- 找到与目标用户最相似的K个用户。这可以通过查找相似度最高的K个用户来完成。
- 根据这些相似用户的评分来预测目标用户对未评分的项目的评分。这通常采用加权平均的方法,即根据相似度的大小给不同的用户分配不同的权重。
- 将预测评分与实际评分进行比较,以评估算法的准确性。这可以通过计算均方根误差(RMSE)、平均绝对误差(MAE)或准确率(Precision)等指标来完成。
基于项目的协同过滤的基本原理是找到与目标项目相似的一系列项目,然后根据这些相似项目的评分来预测目标项目被未评分用户所评分的概率。具体步骤如下: - 计算项目之间的相似度。这通常可以通过余弦相似度或皮尔逊相关系数来完成,比较两个项目所包含的元素(如关键词、主题等)的相似性。
- 找到与目标项目最相似的K个项目。这可以通过查找相似度最高的K个项目来完成。
- 根据这些相似项目的评分来预测目标项目被未评分用户所评分的概率。这通常采用加权平均的方法,即根据相似度的大小给不同的项目分配不同的权重。
- 将预测评分与实际评分进行比较,以评估算法的准确性。这可以通过计算均方根误差(RMSE)、平均绝对误差(MAE)或准确率(Precision)等指标来完成。
基于项目的协同过滤
import argparse
import random
import pandas as pd
import numpy as np
import math
from operator import itemgetter
from utils import evaluate
from utils import modelType
np.random.seed(1024)
class itemCF():
# 参数:
# K:近邻数目
# test_data:测试数据,二维字典。user->item->评分
# train_data:训练数据
# n_users:用户数目
# n_items:项目数目
# average_rating:每个项目的平均评分、字典。项目->平均评分
# item_sim:项目之间的相似度。二维字典。i->j->相似度
# similarityMeasure:相似度度量,cosine或pearson
def __init__(self, data_file, K=20,similarityMeasure="cosine"):
self.K = K # 近邻数
self.similarityMeasure=similarityMeasure
self.loadData(data_file) # 读取数据
self.initModel() # 初始化模型
def initModel(self):
# 计算每个物品被用户评分的个数
item_cnt = {}
for user, items in self.train_data.items():
for i in items:
# count item popularity
item_cnt.setdefault(i, 0)
item_cnt[i] += 1
# 计算每个项目的平均评分
self.average_rating = {}
for user, items in self.train_data.items():
for i in items:
self.average_rating.setdefault(i, 0)
self.average_rating[i] += self.train_data[user][i] / item_cnt[i]
# 计算用户的平均评分
self.user_average_rating={}
for user, items in self.train_data.items():
self.user_average_rating.setdefault(user, 0)
for i in items:
self.user_average_rating[user] += self.train_data[user][i] / len(self.train_data[user])
# 相似度的分子部分
C2 = dict()
C3 = dict()
C1 = dict()
for user, items in self.train_data.items():
for i in items:
for j in items:
if i == j:
continue
C1.setdefault(i, {})
C1[i].setdefault(j, 0)
C2.setdefault(i, {})
C2[i].setdefault(j, 0)
C3.setdefault(i, {})
C3[i].setdefault(j, 0)
if self.similarityMeasure=="cosine":
C1[i][j] += ((self.train_data[user][i] - self.user_average_rating[user]) * (
self.train_data[user][j] - self.user_average_rating[user]))
C2[i][j] += ((self.train_data[user][i] - self.user_average_rating[user]) * (
self.train_data[user][i] - self.user_average_rating[user]))
C3[i][j] += ((self.train_data[user][j] - self.user_average_rating[user]) * (
self.train_data[user][j] - self.user_average_rating[user]))
else:
C1[i][j] += ((self.train_data[user][i] - self.average_rating[i]) * (
self.train_data[user][j] - self.average_rating[j]))
C2[i][j] += ((self.train_data[user][i] - self.average_rating[i]) * (
self.train_data[user][i] - self.average_rating[i]))
C3[i][j] += ((self.train_data[user][j] - self.average_rating[j]) * (
self.train_data[user][j] - self.average_rating[j]))
# 计算最终的物品相似度矩阵
self.item_sim = dict()
for i, related_items in C1.items():
self.item_sim[i] = {}
for j, cuv in related_items.items():
if C1[i][j] == 0:
self.item_sim[i][j] = 0
else:
self.item_sim[i][j] = C1[i][j] / math.sqrt(C2[i][j] * C3[i][j])
#每个物品的邻域
self.item_nei={}
for item in self.item_sim.keys():
simi_items=sorted(self.item_sim[item].items(), key=itemgetter(1), reverse=True)[:self.K]
self.item_nei[item]=simi_items
def loadData(self, data_file):
data_fields = ['user_id', 'item_id', 'rating', 'timestamp']
data = pd.read_table(data_file, names=data_fields)
self.test_data = {}
self.train_data = {}
# 按照1:9划分数据集
for (user, item, record, timestamp) in data.itertuples(index=False):
if random.randint(0, 10) == 0:
self.test_data.setdefault(user, {})
self.test_data[user][item] = record
else:
self.train_data.setdefault(user, {})
self.train_data[user][item] = record
self.n_users = len(set(data['user_id'].values))
self.n_items = len(set(data['item_id'].values))
print("Initialize end.The user number is:%d,item number is:%d" % (self.n_users, self.n_items))
def predict(self, user, item):
rui = 0
# 分子和分母
C1 = 0
C2 = 0
if not item in self.item_sim:
return rui
for interacted_item in self.train_data[user]:
simi_items = self.item_nei[interacted_item]
for similar_item, similarity_factor in simi_items:
if item == similar_item:
C1 += similarity_factor * self.train_data[user][interacted_item]
C2 += math.fabs(similarity_factor)
#
# for similar_item, similarity_factor in sorted(self.item_sim[item].items(),
# key=itemgetter(1), reverse=True)[:self.K]:
# if similar_item not in self.train_data[user]:
# continue
# C1 += similarity_factor * self.train_data[user][similar_item]
# C2 += math.fabs(similarity_factor)
if not C1 == 0:
rui = (C1 / C2)
return rui
parser = argparse.ArgumentParser()
parser.add_argument('--K',type=int, default=20, help='近邻数')
parser.add_argument('--similarityMeasure', default="cosine", help='相似度度量,cosine或pearson')
opt = parser.parse_args()
if __name__ == '__main__':
model = itemCF("../data/ml-100k/u.data",K=opt.K,similarityMeasure=opt.similarityMeasure)
ev=evaluate(modelType.rating)
ev.evaluateModel(model)
print('done!')
基于用户的协同过滤
import random
import pandas as pd
import numpy as np
import math
from operator import itemgetter
from utils import evaluate
from utils import modelType
import argparse
np.random.seed(1024)
class userCF():
# 参数:
# K:近邻数目
# test_data:测试数据,二维字典。user->item->评分
# train_data:训练数据
# n_users:用户数目
# n_items:项目数目
# average_rating:每个用户的平均评分、字典。用户->平均评分
# user_sim:用户之间的相似度。二维字典。u->v->相似度
# similarityMeasure:相似度度量,cosine或pearson
def __init__(self, data_file, K=20,similarityMeasure="cosine"):
self.K = K # 近邻数
self.similarityMeasure = similarityMeasure
self.loadData(data_file) # 读取数据
self.initModel() # 初始化模型
def initModel(self):
# 计算每个用户的平均评分
self.average_rating = {}
for u, items in self.train_data.items():
self.average_rating.setdefault(u, 0)
for i in items:
self.average_rating[u] += self.train_data[u][i] / len(items)
# 建立item_user倒排表
# item->set
item_users = dict()
for u, items in self.train_data.items():
for i in items:
if i not in item_users:
item_users[i] = set()
item_users[i].add(u)
# 相似度的分子部分
C1 = dict()
C2 = dict()
C3 = dict()
for i, users in item_users.items():
for u in users:
for v in users:
if u == v:
continue
C1.setdefault(u, {})
C1[u].setdefault(v, 0)
C2.setdefault(u, {})
C2[u].setdefault(v, 0)
C3.setdefault(u, {})
C3[u].setdefault(v, 0)
if self.similarityMeasure == "cosine":
C1[u][v] += ((self.train_data[u][i]) * (
self.train_data[v][i] ))
C2[u][v] += ((self.train_data[u][i]) * (
self.train_data[u][i] ))
C3[u][v] += ((self.train_data[v][i] ) * (
self.train_data[v][i] ))
else:
C1[u][v] += ((self.train_data[u][i] - self.average_rating[u]) * (
self.train_data[v][i] - self.average_rating[v]))
C2[u][v] += ((self.train_data[u][i] - self.average_rating[u]) * (
self.train_data[u][i] - self.average_rating[u]))
C3[u][v] += ((self.train_data[v][i] - self.average_rating[v]) * (
self.train_data[v][i] - self.average_rating[v]))
# 计算最终的用户相似度矩阵
self.user_sim = dict()
for u, related_users in C1.items():
self.user_sim[u] = {}
for v, cuv in related_users.items():
# print(C1[u][v]," ",C2[u][v]," ",C3[u][v])
if C1[u][v]==0:
self.user_sim[u][v]=0
else:
self.user_sim[u][v] = C1[u][v] / math.sqrt(C2[u][v] * C3[u][v])
def loadData(self, data_file):
data_fields = ['user_id', 'item_id', 'rating', 'timestamp']
data = pd.read_table(data_file, names=data_fields)
# 二维字典
self.test_data = {}
self.train_data = {}
# 按照1:9划分数据集
for (user, item, record, timestamp) in data.itertuples(index=False):
if random.randint(0, 10) == 0:
self.test_data.setdefault(user, {})
self.test_data[user][item] = record
else:
self.train_data.setdefault(user, {})
self.train_data[user][item] = record
self.n_users = len(set(data['user_id'].values))
self.n_items = len(set(data['item_id'].values))
print("Initialize end.The user number is:%d,item number is:%d" % (self.n_users, self.n_items))
def predict(self, user, item):
rui = self.average_rating[user]
# 分子和分母
C1 = 0
C2 = 0
for similar_user, similarity_factor in sorted(self.user_sim[user].items(),
key=itemgetter(1), reverse=True)[0:self.K]:
if item not in self.train_data[similar_user]:
continue
C1 += similarity_factor * (self.train_data[similar_user][item] - self.average_rating[similar_user])
C2 += math.fabs(similarity_factor)
if not C1==0:
rui += (C1 / C2)
else :
rui=0
return rui
parser = argparse.ArgumentParser()
parser.add_argument('--K',type=int, default=20, help='近邻数')
parser.add_argument('--similarityMeasure', default="cosine", help='相似度度量,cosine或pearson')
opt = parser.parse_args()
if __name__ == '__main__':
model = userCF("../data/ml-100k/u.data",K=opt.K,similarityMeasure=opt.similarityMeasure)
ev = evaluate(modelType.rating)
ev.evaluateModel(model)
print('done!')
执行结果: