协同过滤介绍和简单推荐系统的实现

本文介绍推荐系统、协同过滤思想,两种基本的相似度衡量,并用python实现。最后就MovieLens数据集上作出简单的推荐。

一、相关知识

(1)推荐系统
如今,推荐系统已经在多方面得到应用,例如淘宝、当当、亚马逊等网站的商品推荐。而个性化推荐系统则是通过发掘用户的兴趣爱好,作出针对性的推荐。个性化推荐的方法较多,最常用的是协同过滤方法,而本文主要讲的也是基于协同过滤的个性化推荐。

(2)协同过滤
协同过滤技术出现于20世纪70年代,到90年代形成较为成熟的理论框架。协同过滤的基本假设是如果用户x和y对n个项目的评价或行为是相似的,那么他们对其他项目所持有的观点也是相似的,即行为相似的用户兴趣也可能相似。

一个协同过滤算法的基本方法是对大批用户进行搜索,从中找出兴趣相似的用户群。算法会对这些人的所偏爱的内容进行考察,然后构造出推荐列表,推荐给该群体的用户。

(3)Python语言
Python语言一种是一种面向对象的解释型语言,具有跨平台、简单实用、扩展性强等特性,数据结构也非常灵活。Python俗称胶水语言,其语法简洁,类库很多,适合快速开发程序。在做爬虫抓取数据和社交网络分析时学的,此处用的版本是python3.2。

二、相似度测量方法

主要有两种:欧几里得距离和皮尔逊相关度(pearson)

(1)数据集
Prefer以字典形式存放用户看过的电影和评分。

[AppleScript]  纯文本查看  复制代码
?
1
2
3
4
5
Prefer = { "tommy" : { 'War' : 2.3 , 'The lord of wings' : 3.0 , 'Kongfu' : 5.0 } ,
        "lily" : { 'War' : 2.0 , 'The lord of wings' : 3.6 , 'Kongfu' : 4.1 } ,
        "jim" : { 'War' : 1.9 , 'The lord of wings' : 4.0 , 'Beautiful America' : 4.7 , ' the big bang' : 1.0 } ,
        'jack' : { 'War' : 2.8 , 'The lord of wings' : 3.5 , 'Kongfu' : 5.5 }
        }


(2)、寻找有相似兴趣的用户,方式是比较各用户的评价数据,计算用户间的相似度。主要方法有2种即欧几里得距离和皮尔逊相关度。

2-1欧几里得距离

   使用欧几里得距离计算用户的相似度时,将用户评价的物品作为坐标轴,用户填充到坐标体系中。以二维为例,
 
上图中,Snakes和Dupree为电影,Toby,Lasclle等人对应的点根据其评分情况被画在相应的位置。这样两者在偏好空间中的距离越近,他们的兴趣就越相似。而此模型可以推广至多维情况。

2-2 Pearson相关度评价

欧几里得距离评价法是一种比较简单的方法。但是由于存在一些用户总是倾向于评分过高或过低(相对平均值),这是兴趣相似的用户并不能通过此方法计算出来。Pearson相关系数是根据两组数据与某一直线的拟合程度来衡量的。
 

此坐标系以用户为坐标轴,用户所评分的电影显示在对应位置。本方法可修正结果,增强准确性。例如TOM, Lily对电影A,B,C的评分为(2, 4.1, 4), (3, 5, 5 ),则用pearson方法得到两者相似度仍然较高,而欧几里得距离法得到的相似度则偏低。实际上是用户Lily倾向于评分更高。

(3)、编程并测试
#---------------欧几里得空间距离方法---------------
[AppleScript]  纯文本查看  复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
设原先距离L,L越小,越相似。本处采用改进的测量方法:sim = 1 / L + 1 ,sim越大,越相似
def sim_distance ( prefer , person 1 , person 2 ) :  
     sim = { }
     for item in prefer[person 1 ] :
         if item in prefer[person 2 ] :
             sim[ item ] = 1       #添加共同项到字典中
     #无共同项,返回0
     if len ( sim ) = = 0 :        
         return 0
     #计算所有共有项目的差值的平方和
     sum_all = sum ( [pow ( prefer[person 1 ][ item ] - prefer[person 2 ][ item ] , 2 )
                    for item in sim] )
     #返回改进的相似度函数
     return 1 / ( 1 + sqrt ( sum_all ) )
#测试
print ( "\n测试计算欧几里得距离的方法sim_distance()...." )
print ( "sim_distance(dic,'lily','jim') = " , sim_distance ( dic , 'lily' , 'jim' ) )
print ( "sim_distance(dic,'tommy','jim') = " , sim_distance ( dic , 'tommy' , 'jim' ) )
print ( "sim_distance(dic,'tommy','lily') = " , sim_distance ( dic , 'tommy' , 'lily' ) )
print ( "sim_distance(dic,'tommy','jack') = " , sim_distance ( dic , 'tommy' , 'jack' ) )
"" "
结果:
   0.7080595631785951
   0.4814560089181300
   0.471143138585317
   0.5358983848622454
" ""



#-----------------------pearson相关度系数------------------------

[AppleScript]  纯文本查看  复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
def sim_pearson ( prefer , person 1 , person 2 ) :
     sim = { }
     #查找双方都评价过的项
     for item in prefer[person 1 ] :
         if item in prefer[person 2 ] :
             sim[ item ] = 1           #将相同项添加到字典sim中
     #元素个数
     n = len ( sim )
     if len ( sim ) = = 0 :
         return -1
     # 所有偏好之和
     sum 1 = sum ( [prefer[person 1 ][ item ] for item in sim] )  #1.sum([1,4,5,,,])  2.list的灵活生成方式!(Python灵活)
     sum 2 = sum ( [prefer[person 2 ][ item ] for item in sim] )  #!!!!写成person1,导致程序一直runtime error!!!!
     #求平方和
     sum 1 Sq = sum ( [pow ( prefer[person 1 ][ item ] , 2 ) for item in sim] )
     sum 2 Sq = sum ( [pow ( prefer[person 2 ][ item ] , 2 ) for item in sim] )
     #求乘积之和 ∑XiYi
     sumMulti = sum ( [prefer[person 1 ][ item ] * prefer[person 2 ][ item ] for item in sim] )
     #Pearson系数计算 [url]http://baike.baidu.com/view/3891263.htm[/url],计算错误
     #num1 = n*sumMulti - sum1*sum2                                 #分子
     #num2 = sqrt(n*sum1Sq-pow(sum1,2))*sqrt(n*sum2Sq-pow(sum2,2))
     num 1 = sumMulti - ( sum 1 * sum 2 / n )
     num 2 = sqrt ( ( sum 1 Sq - pow ( sum 1 , 2 ) / n ) * ( sum 2 Sq - pow ( sum 2 , 2 ) / n ) )
     if num 2 = = 0 :
         return 0
    
     return num 1 / num 2
#测试
print ( "\n测试计算Pearson系数的方法sim_pearson()...." )
print ( "sim_pearson(dic,'lily','jim') = " , sim_pearson ( dic , 'lily' , 'jim' ) )
print ( "sim_pearson(dic,'tommy','jim') = " , sim_pearson ( dic , 'tommy' , 'jim' ) )
print ( "sim_pearson(dic,'tommy','lily') = " , sim_pearson ( dic , 'tommy' , 'lily' ) )
print ( "sim_pearson(dic,'tommy','jack') = " , sim_pearson ( dic , 'tommy' , 'jack' ) )
"" "
结果:
0.9999999999999991
0.9999999999999988
0.8446877845160871
0.9999999999999973       **[比较tommy和jack,皮尔逊系数和欧几里得距离差别很大,原因是jack一直评分偏高]**
" ""


三、获取推荐列表依然以上面的prefer数据集为基础,根据计算结果将兴趣相似的用户评分较高的电影推荐给用户。

(1)获取相似用户
#从用户评价字典中返回Top-K匹配者
#K,相似度函数 为可选参数
[AppleScript]  纯文本查看  复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
def topMatches ( prefer , person , n = 2 , similarity = sim_pearson ) :
     scores = [ ( similarity ( prefer , person , other ) , other ) for other in prefer if other! = person ]
     #对scores列表排序,从高到底
     scores.sort ( )  
     scores. reverse ( )
     return scores[ 0 : n]               #返回排序列表, 注意[0:n],仅返回前n项;
#测试
print ( "\n测试topMatches()方法......" )
print ( topMatches ( dic , 'tommy' ) )
"" "
结果:[(0.9999999999999988, 'jim'), (0.9999999999999973, 'jack')]   (相似度,姓名)
" ""


(2)、获取推荐
[AppleScript]  纯文本查看  复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 提供推荐,利用所有人评价的加权均值。  相似度高,影响因子越大。
def getRecommendations ( prefer , person , similarity = sim_pearson ) :
     totals = { }
     simSums = { }
     for other in prefer :
         if other = = person :
             continue
         else :
             sim = similarity ( prefer , person , other )    #计算比较其他用户的相似度
         #相似度>0
         if sim < = 0 : continue
         for item in prefer[other] :
             if item not in prefer[ person ] :
                 #加权评价值:相似度*评价值
                 totals.setdefault ( item , 0 )  #每轮循环开始时初始化为0
                 totals[ item ] + = prefer[other][ item ] * sim
                 #相似度之和
                 simSums.setdefault ( item , 0 )
                 simSums[ item ] + = sim
     #建立归一化列表
     ranks = [ ( total / simSums[ item ] , item ) for item , total in totals. items ( ) ]
     #返回经排序后的列表
     ranks.sort ( )
     ranks. reverse ( )
     return ranks
 
#测试
print ( "\n测试推荐方法getRecommendations(prefer, person, similarity=sim_pearson)......" )
print ( getRecommendations ( dic , 'tommy' ) )
"" "
结果: [(4.7, 'Beautiful America')]
" ""


四、利用MovieLens公开数据集做推荐
(1)、文件一loadMovieLens.py,加载测试集数据
[AppleScript]  纯文本查看  复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
def loadMovieLens ( ) :
     str 1 = ' / data / movielens / u. item '
     #获取影片的id和标题(其他项类似)
     movies = { }
     for line in open ( str 1 , 'r' ) :
         ( id , title ) = line.split ( '|' ) [ 0 : 2 ]   #将返回列表的前2元素赋给元组
         movies[ id ] = title
         #print(movies[id])       [测试输出正常,但是遇到UnicodeEncodeError?]
 
     # 加载数据
     prefer = { }
     for line in open ( ' / data / movielens / u. data ' ) :
         ( user , movieid , rating , ts ) = line.split ( '\t' )   #数据集中每行有4项
         prefer.setdefault ( user , { } )    #设置字典的默认格式,元素是user:{}字典
         prefer[user][movies[movieid]] = float ( rating )
     # 返回字典:user以及评价过的电影
return prefer


(2)、文件二:recommendation.py 做推荐
[AppleScript]  纯文本查看  复制代码
?
1
2
3
4
5
6
7
8
9
和三中介绍的方法放置于同一文件recommendation.py下。
from loadMovieLens import loadMovieLens
print ( "\n-------------------MovieLens 测试数据集的推荐系统--------------------" )
prefers =  loadMovieLens ( )
print ( "\n1.*********基于用户的推荐**********\n" )
print ( "用户87的评价列表为:" , prefers[' 87 '] )
print ( "\n**********推荐影片*********\n" )
tuijian = getRecommendations ( prefers , ' 87 ' ) [ 0 : 20 ]
print ( tuijian )


(3)利用MovieLens做测试是出现编码错误提示,后来发现时数据集中某行格式不规范,用软件打开u.item,修改下即可。

参考书籍:programing collective intelligence.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值