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

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


匹配商品

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

首先我们要将之前的:{'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
 

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

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

文末还指出,对于稀疏数据集,基于物品的过滤方法通常要优于基于用户的过滤方法,对于密集数据集则两者差不多。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值