A Programmer's Guide to Data Mining 2:Get started with recommendation system(User based filtering)

来源:互联网 发布:js数组对象去重复 编辑:程序博客网 时间:2024/05/18 01:28

在这一章讲述的是在基于协同过滤的推荐方法中如何计算用户之间的相似度.


什么是协同过滤(Collaborative Filtering):

简单来说就是利用某兴趣相投和拥有共同经验之群体的喜好来推荐用户刚兴趣的资讯, 个人通过合作的机制给予资讯相当程度的回应(比如评分)并记录下来以达到过滤的目的进而帮助别人筛选资讯,回应不一定局限于特别感兴趣的,特别不感兴趣资讯的记录也相当重要. 协同过滤又可以分为评比(rating)或者群体过滤(social filtering).


如何来计算用户之间的相似度:

本节计算用户之间的相似度是基于评比来实现的, 所使用的方法有曼哈坦距离(Manhattan distance), 欧几里得距离(Euclidean distance), 皮尔逊相关系数(Pearson Correlation Coefficient) 和余弦相似度(Cosine similarity). 

曼哈坦距离(Manhattan distance):

曼哈坦距离是最简单的用于计算用户相似度的算法,能快速的计算出用户之间的相似度,在这里我们用一个2维的例子来说明.


用户Snow crashDragon tattooAmy55Bill25Jill14X42

Snow crash 和 Dragon tattoo  是两部电影,用Amy, Bill, Jill 和X 分别给出了自己的评分. 在2维的坐标系里,每个用户都可以用坐标点(x,y) 来表示, x 表示用户对snow crash 的评分而y表示用户对Dragon tattoo的评分.


那么Amy 和 X 的曼哈坦距离 = |5-4| + |5-2| = 4

        Bill   和 X 的曼哈坦距离 = |2-4| + |5-2| = 5

        Jill    和 X 的曼哈坦距离 = |1-4| + |4-2| = 5


所以我们得出 Amy 与 X 的用户相似度最高,那么如果我们发现 X 给了另外一部电影 5分的评分,那么我们就可以把这部电影推荐给Amy.


欧几里得距离(Euclidean distance):

欧几里得距离可以通过勾股定理来求出.


Amy 和 X 的欧几里得距离 = 3.16

Bill 和 X 的欧几里得距离 = 3.61

Jill 和 X 的欧几里得距离 = 3.61


所以我们得出 Amy 和 X 的用户相似度最高.


问题: 既然我们有曼哈坦距离了,那么为什么还需要欧几里得距离,从上面的例子看来曼哈坦距离 和 欧几里得距离得到的结论是一样的.


首先让我们来做一个扩展,我们把曼哈坦距离和欧几里得距离扩展成明科夫斯基距离(Minkowski Distance Metric):


r=1: 这个方程式就是曼哈坦距离

r=2: 这个方程式就是欧几里得距离

r=无穷大:极大距离(Supremum Distance)


r 越大, 那么在某一个维度上的大距离差对总的距离的影响就越大.

比如:

用户Snow crashDragon tattooA55B14C23


B 和A 的曼哈坦距离等于 C 和A 的曼哈坦距离,都是5, 但是 B和A 的欧几里得距离是 4.12 而 C和A 的欧几里得距离是 3.6.

通过欧几里得距离我们得出C和A 的相似度比 B 要高. 在 Snow crash 这个维度上, A 和B 的距离差是4, 是最大的一个距离差,所以对总的距离差也造成了大的影响.


扩展到多维度:

接下来我们看一个把曼哈坦距离扩展到多维度的一个例子.

 AngelicaBillChanDanHaileyJordynSamVeronicaBlues Traveler3.5253--53Broken Bells23.51444.52-Deadmau5-414.514--Norah Jones4.5-3-4535Phoenix5253-554Slightly Stoopid1.53.514.5-4.542.5The strokes2.5--44453Vampire Weekend23-214--
(横线 - 表示用户没有对这支乐队做评分)

Angelica 和Bill 之间的 曼哈坦距离(Manhattan distance) 是:

 AngelicaBillDifferenceBlues Traveler3.521.5Broken Bells23.51.5Deadmau5-4 Norah Jones4.5- Phoenix523Slightly Stoopid1.53.52The strokes2.5--Vampire Weekend231曼哈坦距离  9

距离测算的缺点:

不知道大家发现没有,这种测量距离的方法其实是有缺点的. 当我们测量 Hailey 和 Veronica 的距离时,他们共同评分的乐队只有两支.

但是当测量Hailey 和 Jordyn 的距离时, 他们共同评分的乐队达到了5支.

这个时候Hailey-Veronica 距离是2维而Hailey 和Jordyn 的距离确实5维的.

曼哈坦距离和欧几里得距离在没有残缺值的情况下工作得最好.

在后面我们会讲到怎样处理这个残缺值问题. 


代码例子(原创--基于用户评分来找出某个用户的最相近用户并给出推荐):

#!/usr/bin/env python2.7import mathusers_ratings = {"Angelica": {"Blues Traveler": 3.5, "Broken Bells": 2.0, "Norah Jones": 4.5, "Phoenix": 5.0, "Slightly Stoopid": 1.5, "The Strokes": 2.5, "Vampire Weekend": 2.0},         "Bill":{"Blues Traveler": 2.0, "Broken Bells": 3.5, "Deadmau5": 4.0, "Phoenix": 2.0, "Slightly Stoopid": 3.5, "Vampire Weekend": 3.0},         "Chan": {"Blues Traveler": 5.0, "Broken Bells": 1.0, "Deadmau5": 1.0, "Norah Jones": 3.0, "Phoenix": 5, "Slightly Stoopid": 1.0},         "Dan": {"Blues Traveler": 3.0, "Broken Bells": 4.0, "Deadmau5": 4.5, "Phoenix": 3.0, "Slightly Stoopid": 4.5, "The Strokes": 4.0, "Vampire Weekend": 2.0},         "Hailey": {"Broken Bells": 4.0, "Deadmau5": 1.0, "Norah Jones": 4.0, "The Strokes": 4.0, "Vampire Weekend": 1.0},         "Jordyn":  {"Broken Bells": 4.5, "Deadmau5": 4.0, "Norah Jones": 5.0, "Phoenix": 5.0, "Slightly Stoopid": 4.5, "The Strokes": 4.0, "Vampire Weekend": 4.0},         "Sam": {"Blues Traveler": 5.0, "Broken Bells": 2.0, "Norah Jones": 3.0, "Phoenix": 5.0, "Slightly Stoopid": 4.0, "The Strokes": 5.0},         "Veronica": {"Blues Traveler": 3.0, "Norah Jones": 5.0, "Phoenix": 4.0, "Slightly Stoopid": 2.5, "The Strokes": 3.0}        }class Recommender(object):    def __init__(self, user, users_ratings, metric='manhattan'):        self._metric = metric        self._user = user        self._users_ratings = users_ratings    def recommend(self):        recommendations = list()        nearest_neighbor = self._find_nearest_neighbor()           neighbor_rating = self._users_ratings[nearest_neighbor]        user_rating = self._users_ratings[self._user]        for band in neighbor_rating:            if band not in user_rating:                recommendations.append((band, neighbor_rating[band]))        sorted_recommendations = sorted(recommendations, key = lambda rating: rating[1], reverse = True)        print 'Recommendation made for user %s is:%s' % (self._user, sorted_recommendations)    def _find_nearest_neighbor(self):        distance_list = list()        compute_distance = getattr(self, '_%s' % self._metric)        for user in users_ratings:            if user != self._user:                distance = compute_distance(users_ratings[user])                distance_list.append((user, distance))        sorted_distance_list = sorted(distance_list, key = lambda distance: distance[1])        return sorted_distance_list[0][0]    def _manhattan(self, ratings):        distance = 0        user_rating = users_ratings[self._user]        for band in user_rating:            if band in ratings:                distance = distance + abs(user_rating[band] - ratings[band])        return distance    def _eculidean(self, ratings):        distance = 0        user_rating = users_ratings[self._user]        for band in user_rating:            if band in ratings:                distance = distance + math.pow(abs(user_rating[band] - ratings[band]), 2)        return math.sqrt(distance)recommender = Recommender('Hailey', users_ratings)recommender.recommend()recommender = Recommender('Hailey', users_ratings, metric='eculidean')recommender.recommend()


皮尔逊相关系数(Pearson Correlation Coefficient):

好了,问题又来了, 在上述用户的评分中,每个用户所持有的评判标准时不同的,3对于用户A 来讲是很不错的标准但是对于用户B 来讲很可能就是一般的标准.而且有些用户有时就喜欢给两个评分,不是1就是4. 皮尔逊相关系数可以解决这个问题.

皮尔逊相关系数是一种度量两个变量之间相关程度的方法,它的值介于-1 与1 之间,1表示变量完全正相关,0 表示无关,-1 表示完全负相关.


皮尔逊相关系数公式:



Python 代码实现:

def get_pearson_correlation_coefficient(user_1, user_2, user_ratings):    user_1_ratings = user_ratings[user_1]    user_2_ratings = user_ratings[user_2]    common_rated_items = list(set(user_1_ratings.keys()).intersection(user_2_ratings.keys()))    user_1_ratings_on_common_rated_items = [user_1_ratings[key] for key in common_rated_items]    user_2_ratings_on_common_rated_items = [user_2_ratings[key] for key in common_rated_items]    average_user_1_rating = sum(user_1_ratings_on_common_rated_items)/len(user_1_ratings_on_common_rated_items)    average_user_2_rating = sum(user_2_ratings_on_common_rated_items)/len(user_2_ratings_on_common_rated_items)    user_1_deviation_list = map(lambda x: x-average_user_1_rating, user_1_ratings_on_common_rated_items)    user_2_deviation_list = map(lambda y: y-average_user_2_rating, user_2_ratings_on_common_rated_items)    numerator = sum(map(lambda x,y: x*y, user_1_deviation_list, user_2_deviation_list))    denominator = sum(map(lambda x: x*x, user_1_deviation_list))*sum(map(lambda y: y*y, user_2_deviation_list))    pearson_correlation_coefficient = numerator/math.sqrt(denominator)    print 'Pearson correlation coefficient between %s and %s is:%s' % (user_1, user_2, pearson_correlation_coefficient)get_pearson_correlation_coefficient('Hailey', 'Angelica', users_ratings)



余弦相似性(Cosine Similarity):

余弦相似性也是用来测量相似度的,尤其在比较,文本相似度的时候用的比较多. 和前文谈到的距离公式把值映射到空间中的点不同,余弦相似度把值映射成空间中的向量,一般指方向上的差别,即夹角,余弦值越接近与1, 表示夹角越接近与0,即两个向量之间越相似.


余弦相似性的公式如下:



适用场景:体现方向的差异,对绝对数值不敏感,更多用于使用用户评分来来区分用户爱好的相似度或差异,同时修正了用户度量标准不同意的问题.


举个例子(转):

例如某T恤从100块降到了50块(A(100,50)),某西装从1000块降到了500块(B(1000,500))

那么T恤和西装都是降价了50%,两者的价格变动趋势一致,余弦相似度为最大值,即两者有很高的变化趋势相似度

但是从商品价格本身的角度来说,两者相差了好几百块的差距,欧氏距离较大,即两者有较低的价格相似度



=============本章结束===============



0 0