您的位置:首页 > 编程语言

集体智慧编程 第二章 匹配商品

2017-01-05 14:54 357 查看
我们在前面学习了如何为指定人员寻找品味相近的人,以及如何向其推荐商品。但是如果我们想了解哪些商品是彼此相近的,应该如何做?

匹配商品

比如我们去淘宝,点击某个商品的时候,侧面总会给我们推荐一些类似商品。这是如何做到的呢?

首先我们要将之前的:{'Person':{'movie':score}} 的形式换成 {'Movie':{'person':score}}

用下列代码实现:

#这个函数就是将字典里面的人员和物品对调
def transformPrefs(prefs):
result = {}
for person in prefs:
for item in prefs[person]:
result.setdefault(item, {})
#将物品和人员对调
result[item][person] = prefs[person][item]
return result


然后运行代码中添加:

print '\n=========================================='
print 'topMatches-Superman Returns'
movies = recommendations.transformPrefs(recommendations.critics)
print recommendations.topMatches(movies, 'Superman Returns')
可以看出结果:



现在我们就得到了一组与《Superman Returns》最为相近的电影。但是有一些相关评价值为负数,这说明喜欢《Superman Returns》的人,存在不喜欢《Just My Luck》的倾向。我们还可以为影片推荐评论者。

添加运行代码:

print '\n=========================================='
print 'getRecommendations'
movies = recommendations.transformPrefs(recommendations.critics)
print recommendations.getRecommendations(movies, 'Just My Luck')




当然,书上也强调了对调人和物不一定总是有意义的。但是大多数情况下,这都有助于做出对比。

=============================================================================

构建一个基于 del.icio.us 的链接推荐系统

这一节我们将学习如何从在线书签网站上面获得数据,如何利用这些数据查找用户,并向他们推荐以前没看过的链接。

这个网站的网站是 http://del.icio.us
通过 API 访问 del.icio.us 网站获得的数据是以 XML 格式返回的,我们先要安装一些东西。

书上给出了两个链接,下载已经写好了的 API:

https://code.google.com/p/pydelicious/source

http://oreilly.com/catalog/9780596529321

当然我跟我一步一步来更简单:

1.进入网站 https://pypi.python.org/pypi/feedparser#downloads 下载 feedparser 5.2.1 这个库,注意是 feedparser-5.2.1.zip

2.用winRAR打开,解压到桌面,在 PowerShell 里面各种 cd 进入解压文件夹,输入

python setup.py install
feedparser安装完毕

3.进入网站 https://pypi.python.org/pypi/pydelicious 下载 pydelicious 0.6.1。下载文件为:pydelicious-0.6.1.tar.gz

4.用winRAR打开,解压到桌面,进去再次解压 pydelicious-0.6.1.tar ,在 PowerShell 里面各种 cd 进入解压文件夹,输入

python setup.py install
pydelicious安装完毕

好了,安装完毕我们继续下一步走起。

跟着教材走,建立一个 run2.py:

import pydelicious
print pydelicious.get_popular(tag = 'programming')


运行发现:



吃屎啊...怎么破?

书上还给了两个选择,这里选择用 Dorisa 用户为例:

print pydelicious.get_userposts('dorisa')
然后还是不行。
delicious.com/rss 破网站真的有毒,十有八九被墙了。

于是我决定死马当成活马医,继续跟书走吧。

============================================================

构造数据集

主要是下载一个子集信息。

新建一个 deliciousrec.py 文件:

# -*- coding:utf-8 -*-
# deliciousrec.pydelicous
from pydelicious import get_popular, get_userposts, get_urlposts

# 执行这个代码活的一个包含若干用户数据的字典,其中每一项都各自指向一个等待填入具体链接的空字典。
def initializeUserDict(tag, count = 5):
user_dict = {}
# 获取前 count 个最受欢迎的链接张贴记录
for p1 in get_popular(tag = tag)[0:count]:
for p2 in get_urlposts(p1['href']):
user = p2['user']
user_dict[user] = {}
return user_dict

# 只有两种评价值:
# 0:用户没有张贴这一链接
# 1:用户张贴了这一链接
def fillItem(user_dict):
all_items = {}
# 查找所有用户都提交过的链接
for user in user_dict:
for i in range(3):
try:
posts = get_userposts(user)
break
except:
print "Failed user '+user+', retrying"
time.sleep(4)
for post in posts:
url = post['herf']
user_dict[user][url] = 1.0
all_items[url] = 1
# 用 0 填充缺失的项目
for ratings in user_dict.values():
for item in all_items:
if tiem not in ratings:
ratings[item] = 0.0


利用这个函数构造一个数据集,类似之前的影评字典:

# -*- coding:utf-8 -*-
# run deliciousrec.py
from deliciousrec import *
delusers = initializeUserDict('programming')
delusers ['tsegaran'] = {} # 如果你也是用 delicious,则将自己也加入字典中
fillItems(delusers)


这里的第三行代码将用户 tsegaran 添加到了列表中。假如你也是使用 del.icio.us,则不妨以自己的名字来替换 tsegaran。

然后对于 fillItems 的调用要花费几分钟的时间来执行,因为要向网站发起数百个请求。

当然对于网站被墙来说并无卵用。

=============================================================================

推荐近邻与链接

为了随机选择一个用户,并且找出与其品味相近的其他用户,在 run2 里面添加代码:

import random
user = delusers.keys()[random.randint(0, len(delusers) - 1)]
print user
print recommendations.topMatches(delusers, user)


也可以通过调用 getRecommendations 函数为该用户获取推荐链接。

print recommendations.getRecommendations(delusers, user)[0:10]
也可以依据链接来搜索:

url = recommendations.getRecommendations(delusers,user)[0][1]
print recommendations.topMatches(recommendations.transformPrefs(delusers), url)
这样我们就给网站增加了一个推荐引擎。

=============================================================================

基于物品的过滤

刚刚的方法对于上千的用户和物品规模是没问题的,对于像淘宝亚马逊这样的网站,把单个用户和其他所有用户比较太慢了。之前我们采用的方法叫做基于用户的协作型过滤,此外,还有一种方法叫做基于物品的协作型过滤。在大数据情况下,这种方法能得出更好的结论。
总体思路就是为每件物品预先计算好最为相近的其他物品。区别在于物品间的比较不会像用户间的比较那么频繁变化。

=============================================================================
构造物品比较数据集
为了对物品比较,在 recommendations.py 里面加入:

def calculateSimilarItems(prefs, n = 10):
# 建立字典,以给出与这些物品最为相近的所有其他物品
result = {}
# 以物品为中心对偏好矩阵实施倒置处理
itemPrefs = transformPrefs(prefs)
c = 0
for item in temPrefs:
# 针对大数据集更新状态变量
c += 1
if c % 100 == 0:print "%d / %d" % (c, len(itemPrefs))
# 寻找最为相近的物品
scores = topMatches(itemPrefs, item, n = n, similarity = sim_distance)
result[item] = scores
return result # 返回一个包含物品及其最相近物品列表的字典


先对反映评价值的字典倒置处理,从而得到一个有关物品及其用户评价的列表。然后程序循环遍历每个物品,并且把转换了的字典传入 topMatches 函数中,求得最为相近的物品及其相似度评价值。

在 run1.py 添加:

print '\n=========================================='
print 'itemsim'
itemsim = recommendations.calculateSimilarItems(recommendations.critics)
print itemsim

这里结果很有意思,书上第一个是0.4,而我计算的则是0.44。仔细检查发现书上的示例代码中 sim_distance 函数的返回值如果不加 sqrt 的话就是一致的。归根结底应该是书上的代码有点问题。



以上是结果。

=============================================================================
获得推荐
现在我们可以在不遍历整个数据集的情况下,利用反映物品相似度的字典给出推荐。具体数学方法参见书上。
在 recommendations.py 里面加入:

def getRecommendedItems(prefs, itemMatch, user):
userRatings = prefs[user]
scores = {}
totalSim = {}
# 循环遍历由当前用户评分的物品
for (item, rating) in userRatings.items(): # dict.items() 此方法返回元组对的列表。
# 寻遍遍历与当前物品相机的物品
for (similarity, item2) in itemMatch[item]:
# 如果该用户已经对当前物品做过评价,则将其忽略
if item2 in userRatings: continue
# 评价值与相似度的加权之和
scores.setdefault(item2, 0) # setdefault 见前面注释
scores[item2] += similarity * rating
# 全部相似度之和
totalSim.setdefault(item2, 0)
totalSim[item2] += similarity
# 将每个合计值除以加权和,求出平均值
rankings = [(score / totalSim[item], item) for item, score in scores.items()]
# 按最高值到最低值的顺序,返回评分结果
rankings.sort()
rankings.reverse()
return rankings


run1.py:

print '\n=========================================='
print 'getRecommendedItems'
print recommendations.getRecommendedItems(recommendations.critics, itemsim, 'Toby')


结果:



可以看出推荐物品排行。

=============================================================================
使用 MovieLens 数据集
从 http://www.grouplens.org/node/73 下载 MovieLens 数据集。注意下载的是小的数据集,十万数据的那个。
解压打开文件,我们主要关心的是 movies.csv 和 ratings.csv ,前者包含了一组有关影片ID和片面的列表,后者是实际评价情况。

每位用户都对较多影片做出过评价。在 recommendations.py 中新建一个方法,取名 loadMovieLens,用以加载数据。

def loadMovieLens(path = 'MovieLens-latest-small/ml-latest-small'):
# 获取影片标题
movies = {}
for line in open(path + '/movies.csv'):
(id, title, genres) = line.split('|')[0:2] # 这里文件中第三列是影片类型,略作修改
movies[id] = title # 把 title 和 id对应
# 加载数据
prefs = {}
for line in open(path + '/ratings.csv'):
(user, movieid, rating, ts) = line.split('\t') # 分割
prefs.setdefault(user, {})
prefs[user][movies[movieid]] = float(rating)
return prefs
 

很遗憾,就卡在了这一步。这里读不出来。也不知道是哪里出错了。

虽然止步于最后一步,但是基于用户和基于物品的推荐大概方法已经过了一遍。

文末还指出,对于稀疏数据集,基于物品的过滤方法通常要优于基于用户的过滤方法,对于密集数据集则两者差不多。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息