这种算法称为协同过滤算法,协同过滤是指用户可以齐心协力,通过不断的和网站互动,使自己的推荐列表能够过滤掉自己不感兴趣的物品,从而越来越满足自己的需求。
用户行为数据最简单的存在形式是日志。很多互联网业务会把原始日志按照用户行为汇总成为会话日志(session log),其中每个会话代表一次用户行为和对应的服务,如展示日志,点击日志。
用户行为在个性化推荐系统中一般分为显性反馈行为(explicit feedback)和隐性反馈行为(implicit feedback)。explicit feedback包括用户明确表示对物品的喜好的行为。implicit feedback 是指那些不能明确反应用户喜好的行为。与explicit feedback相比,隐性反馈虽然不明确,但是数量庞大。
显性反馈数据 | 隐性反馈数据 | |
---|---|---|
用户兴趣 | 明确 | 不明确 |
数量 | 很少 | 庞大 |
存储 | 数据库 | 分布式文件系统 |
实时读取 | 实时 | 有延迟 |
正负反馈 | 都有 | 只有正反馈 |
在利用用户行为数据设计推荐算法之前,首先需要对用户行为数据进行分析,了解数据中蕴含的一般规律,这样才能对算法的设计起到指导作用。以下用户行为数据的普遍规律。
互联网上很多数据分布都满足power Law也称长尾分布。
f ( x ) = α x k f(x)=\alpha x^k f(x)=αxk
研究发现,用户行为数据也满足power law分布, f i ( k ) f_i (k) fi(k)为被k个用户产生行为的物品数, f u ( k ) f_u(k) fu(k)为对k个物品产生行为的用户数。即
f i ( k ) = α i k i β f_i(k)=\alpha_i k^\beta_i fi(k)=αikiβ f u ( k ) = α u k u β f_u(k)=\alpha_u k^\beta_u fu(k)=αukuβ
根据这个规律可以发现,用户越活跃,越倾向于冷门物品。
仅仅基于用户行为数据设计的推荐算法一般称为协同过滤算法。学术界对协同过滤算法提出了很多方法,如:基于领域的方法,隐语义模型、基于图的随机游走算法等。应用最广泛的是:
采用GroupLens提供的MovieLens数据集,该数据集包含3个不同版本,选用中等大小的数据集,包含6000多用户对4000多部电影的100万条评分。该数据集是一个评分数据集。着重研究隐反馈数据集中的TopN推荐问题,因此忽略数据集中的评分纪录。TopN的推荐任务是预测用户会不会给某部电影评分,而不是预测用户在准备对电影评分的前提下给电影评多少分。
首先,将用户行为数据集均匀随机分成M份(M=8),挑选一份作为测试集,其余的作为训练集。在训练集上建立用户兴趣模型,并在测试集上对用户行为进行预测,统计相应的评测指标。为了保证评测指标并不是过拟合的结果,需要进行M次试验,并且每次都使用不同的测试集。然后取M次试验的评测指标的平均值作为最终的评测指标。
下面代码为将用户评分文件中的数据分成训练集合测试集的过程。
def get_datas(input_file,M,k,seed) : #将用户评分文件中的数据分成训练集合测试集
"""
get rating information
Args:
input_file:user rating file
M: 数据分成的份数
k: 实验选取的测试集
seed: 随机种子
Return: two lists
one:train datas
another:test datas
"""
if not os.path.exists(input_file) :
return [],[]
linenum = 0
train_data = []
test_data = []
fp = open(input_file)
random.seed(seed)
for line in fp :
if linenum == 0 :
linenum += 1
continue
item = line.strip().split(',')
if len(item) < 4:
continue
if random.randint(0,M) == k :
test_data.append([item[0],item[1],item[2]])
else :
train_data.append([item[0],item[1],item[2]])
fp.close()
return train_data,test_data
这里,每次选取不同的k和相同的随机种子seed,进行M次实验就可以得到M个不同的训练集和测试集,然后分别进行实验,用M次实验的平均值作为最后的评测指标。这样做主要就是为了防止某次实验结果是过拟合的结果(overf itting)。
基于用户的协同过滤算法就是将兴趣相同的人喜欢的物品推荐给你。
包括两个步骤:
步骤1的关键就是计算两个用户的兴趣相似度。这里,主要利用行为的相似度计算兴趣的相似度。给定用户 u u u和 v v v,令 N ( u ) N(u) N(u)为用户 u u u曾经有过正反馈的物品集合,令 N ( v ) N(v) N(v)为用户 v v v曾经有过正反馈的物品集合。计算方法有两种:
import os
import math
import numpy as np
import random
NumOfMovies = 9000
NumOfUsers = 700
def get_data(file):
"""
读取数据
"""
if not os.path.exists(file):
return {}
fp = open(file)
data = {}
linenum = 0
for line in fp:
if linenum == 0:
linenum += 1
continue
line = line.split(',')
userid,itemid = int(line[0]),int(line[1])
if userid not in data:
data[userid] = []
data[userid].append(itemid)
fp.close()
return data
def split_data(data,M,k,seed):
#将数据划分成为测试集和训练集
test = {}
train = {}
random.seed(seed)
for user,items in data.items():
for i in items:
if random.randint(0,M) == k:
if user not in test:
test[user] =[]
test[user].append(i)
else:
if user not in train:
train[user] = []
train[user].append(i)
return train,test
def UserSimilarity(train):
#得到用户相似集合W
#建立电影用户倒排表
item_user = {}
for u,items in train.items():
for i in items:
if i not in item_user:
item_user[i] = []
item_user[i].append(u)
#计算C[u][v]即u和v共同观看的电影数
C = {}
N = np.zeros([NumOfUsers],dtype = np.int32)
user_related = {}
for i,users in item_user.items():
for u in users:
N[u] += 1
if u not in C:
C[u] = {}
for v in users:
if u == v:
continue
if v not in C[u]:
C[u][v] = 0
C[u][v] += (1/math.log(1+len(users)))
if u not in user_related:
user_related[u] = []
user_related[u].append(v)
#求用户相似矩阵W
W = np.zeros([NumOfUsers,NumOfUsers],dtype = np.float)
for u,users in C.items():
for v in users:
W[u][v] += C[u][v] / math.sqrt(N[u] * N[v])
return W ,user_related
def recommend(User,train,K,N,W,user_related):
#通过相似矩阵W给用户产生推荐record
k_user = {}
rank ={}
for v in user_related[User]:
k_user[v] = W[User][v]
k_user = sorted(k_user.items(),key = lambda x:x[1],reverse = True)[:K]
for v,w in k_user:
for item in train[v] :
if item in train[User]:
continue
if item not in rank:
rank[item] = 0
rank[item] += w
rank = sorted(rank.items(),key = lambda x:x[1],reverse = True)[:N]
return rank
def Recall(train,test,N,k,W,user_related):
#计算召回率
hit = 0
totla = 0
for user in train:
tu =test[user]
rank = recommend(user,train,k,N,W,user_related)
for item in rank :
if item[0] in tu :
hit += 1
totla += len(tu)
return hit/(totla*1.0)
def Precision(train,test,N,k,W,user_related):
#计算召回率
hit = 0
totla = 0
for user in train:
tu =test[user]
rank = recommend(user,train,k,N,W,user_related)
for item in rank :
if item[0] in tu :
hit += 1
totla += len(rank)
return hit/(totla*1.0)
if __name__ == "__main__":
data = get_data(r"F:\个性化推荐算法\UserCF\data\ratings.csv")
train,test = split_data(data,2,1,1)
del data
W,user_relatde = UserSimilarity(train)
recall = Recall(train,test,10,10,W,user_relatde)
precision = Precision(train,test,10,10,W,user_relatde)
print(recall,precision)
注:以上参考《推荐系统实践》一书