用surprise实现SVD协同过滤推荐算法对本地数据做推荐

引入

surpriseSimple Python Recommendation System Engine的缩写,是一个为了实现推荐系统的框架。它自带了SVD,user-based,item-based协同过滤算法等多种推荐算法,接口简单,功能强大。但官方文档写的并不好,笔者花了不少时间,都没有找到如何预测某个user对某个item进行打分这样的基础用法,所以把摸索后得到的经验分享于此。

数据集

为了验证算法结果的正确性,自己定义了一个数据集,如下所示(保存为mydata.csv)

1,1,1
1,2,2
1,3,3
1,4,4
1,5,5
2,1,1
2,2,2
2,3,3
2,4,4
2,5,5
3,1,1
3,2,2
3,3,3
3,4,4
3,5,5
4,1,1
4,2,2
4,3,3
4,4,4
4,5,5
5,2,2
5,3,3

这个数据集中,每一行说明了user对item的评分,比如“2,1,1”,是指user-2对item-1的评分是1。
可见,这个数据集中一共有5位user,5个item。每一位user对第i个item的打分都是i。这样的简化是为了验证算法的准确性。user-5只对item-2、item-3进行了评分,根据数据集中体现出来的规律,user-5没打分的item,应该满足如下规律:

  • user-5对item-1打分是1分
  • user-5对item-4打分是4分
  • user-5对item-5打分是5分

如果我们的算法得到的打分和上面的一样,就说明算法的结果是100%正确的。如果算法得到的结果有所不同,但user-5对item-i的打分是依次升高的,也说明算法的结果是正确的。

某个user对某个item的评分

接下来我们讲解如何实现user-5对item-i打分的预测值。每段代码的讲解写在代码段后面。

from surprise import SVD
from surprise import Dataset, Reader
import os

首先,导入surprise中必须的class:SVD是SVD算法,Dataset是创建满足surprise需要的数据集所需的类,Reader是做数据读取,类似pandas

# 指定文件所在路径
file_path = os.path.expanduser('mydata.csv')
# 告诉文本阅读器,文本的格式是怎么样的
reader = Reader(line_format='user item rating', sep=',')
# 加载数据
data = Dataset.load_from_file(file_path, reader=reader)
trainset = data.build_full_trainset()

上面这一段代码读取之前构建的本地数据集’mydata.csv’,并用Reader读取数据,将数据加载为Dataset结构。

这里要注意的是,Dataset结构并不是surprise结构直接进行计算的最终结构,还需要通过data.build_full_trainset()将其转换为Trainset这样的数据结构,才能在后续过程中直接用于训练。

algo = SVD()
algo.fit(trainset)

这里用SVD对数据进行训练,也就是SVD推荐算法,将原始得分矩阵分解后,对未知得分进行重新计算的过程。

# we can now query for specific predicions
uid = str(5)  # raw user id
iid = str(1)  # raw item id

# get a prediction for specific users and items.
pred = algo.predict(uid, iid)
print('rating of user-{0} to item-{1} is '.format(uid, iid), pred.est)# rating of user-5 to item-1

接下来,我们就能计算user-5对item-1的打分(预测值)了。这里要注意,user-id和item-id,都要将id转换为string类型。然后调用algo.predict(uid, iid)就能得到预测值了,原始的pred变量内容如下所示

user: 5          item: 1          r_ui = None   est = 2.38   {'was_impossible': False}

这其中,est就是预测值,我们这里预测得到:

  • user-5对item-1打分是:2.3690
  • user-5对item-2打分是:2.3836
  • user-5对item-3打分是:3.0076
  • user-5对item-4打分是:3.44
  • user-5对item-5打分是:3.54

可见,user-5对item-i的打分是依次升高的,和预想的情况相同,说明算法的结果是正确的。

获取评分最高的TOP-N

可以迭代用algo.predict(uid, iid)对每个得分进行计算,然后遍历所有得分得到TOP-N。但这样做稍显笨拙,其实surprise作为一个优秀的推荐系统框架,已经给出了更好的接口,下文进行详述。每段代码的讲解写在代码段后面。

from collections import defaultdict
from surprise import SVD
from surprise import Dataset,Reader
import os

# 指定文件所在路径
file_path = os.path.expanduser('mydata.csv')
# 告诉文本阅读器,文本的格式是怎么样的
reader = Reader(line_format='user item rating', sep=',')
# 加载数据
data = Dataset.load_from_file(file_path, reader=reader)
trainset = data.build_full_trainset()

algo = SVD()
algo.fit(trainset)

首先是读入数据,转换为Trainset结构,并用SVD算法进行训练。这个过程和上一节的内容相同。

def get_top_n(predictions, n=10):

    # First map the predictions to each user.
    top_n = defaultdict(list)
    # uid: 用户ID
    # iid: item ID
    # true_r: 真实得分
    # est:估计得分
    for uid, iid, true_r, est, _ in predictions:
        top_n[uid].append((iid, est))

    # Then sort the predictions for each user and retrieve the k highest ones.
    # 为每一个用户都寻找K个得分最高的item
    for uid, user_ratings in top_n.items():
        user_ratings.sort(key=lambda x: x[1], reverse=True)
        top_n[uid] = user_ratings[:n]

    return top_n

接下来定义get_top_n()函数,它能根据predictions结果进行解析,获取top_n字典,该字典的key是user-id,value是该user打分(预测值)最高的n个item-id。

predictions的数据结构,是surprise中的算法自带接口algo.test()的输出值。

testset =  [
    ('5','1',0),# 想获取第5个用户对第1个item的得分
    ('5','4',0),# 0这个位置是真实得分,不知道时可以写0
    ('5','5',0),# 但写0后,就没法进行算法评估了,因为不知道真实值
]
predictions = algo.test(testset)

这里是将需要计算的user-id,item-id,true-rating-value构建成测试集。注意真实得分true-rating-value我们可能无法得到,那用0来代替也是可以的。但写0后,就没法进行算法评估了,因为不知道真实值,算法评估得到的值也是不准的。

algo.test()的输出值predictions中的数据如下所示:

[Prediction(uid='5', iid='1', r_ui=0, est=2.2154870570408325, details={'was_impossible': False}),
 Prediction(uid='5', iid='4', r_ui=0, est=3.037552499639195, details={'was_impossible': False}),
 Prediction(uid='5', iid='5', r_ui=0, est=3.434713829035328, details={'was_impossible': False})]

uid就是user-id,iid就是item-id,其中est是预测得分值。

top_n = get_top_n(predictions, n=2)

# Print the recommended items for each user
for uid, user_ratings in top_n.items():
    print(uid, [iid for (iid, _) in user_ratings])

使用get_top_n()函数,获取测试集中所有用户得分最高的n(等于2)个item-id,并将print出来,得到结果如下

5 ['5', '4']

可见,对user-5来说,得分最高的是item-5和item-4。这与数据集要体现出来的结果是一样的。

参考

  • [1] get top n: https://github.com/NicolasHug/Surprise/blob/master/examples/top_n_recommendations.py

你可能感兴趣的:(Machine,Learning,Python,推荐系统)