数据挖掘:用户推荐系统技术深度揭秘

数据科学家需要具备专业领域知识并研究相应的算法以分析对应的问题,而数据挖掘是其必须掌握的重要技术。以帮助创建推动业务发展的相应大数据产品和大数据解决方案。EMC最近的一项调查也证实了这点。调查结果显示83%的人认为大数据浪潮所催生的新技术增加了数据科学家的需求。本文将为您展示如何基于一个简单的公式查找相关的项目。请注意,此项技术适用于所有的网站(如亚马逊),以个性化用户体验、提高转换效率。

查找相关项问题

要想为一个特定的项目查找相关项,就必须首先为这两个项目定义相关之处。而这些也正是你要解决的问题:

  • 在博客上,你可能想以标签的形式分享文章,或者对比查看同一个人阅读过的文章
  • 亚马逊站点被称为“购买此商品的客户还购买了”的部分
  • 一个类似于IMDB(Internet Movie Database)的服务,可以根据用户的评级,给出观影指南建议

不论是标签、购买的商品还是观看的电影,我们都要对其进行分门别类。这里我们将采用标签的形式,因为它很简单,而且其公式也适用于更复杂的情形。

以几何关系重定义问题

现在以我的博客为例,来列举一些标签:

 
 
  1. ["API""Algorithms""Amazon""Android""Books""Browser"]  

好,我们来看看在欧式空间几何学中如何表示这些标签。

我们要排序或比较的每个项目在空间中以点表示,坐标值(代表一个标签)为1(标记)或者0(未标记)。

因此,如果我们已经获取了一篇标签为“API”和“Browser”的文章,那么其关联点是:

 
 
  1. [ 1, 0, 0, 0, 0, 1 ] 

现在这些坐标可以表示其它含义。例如,他们可以代表用户。如果在你的系统中有6个用户,其中2个用户对一篇文章分别评了3星和5星,那么你就可以针对此文章查看相关联的点(请注意顺序):

 
 
  1. [ 0, 3, 0, 0, 5, 0 ] 

现在我们可以计算出相关矢量之间的夹角,以及这些点之间的距离。下面是它们在二维空间中的图像:

欧式几何空间距离

计算欧式几何空间两点之间距离的数学公式非常简单。考虑相关两点A、B之间的距离:

两点之间的距离越近,它们的相关性越大。下面是Ruby代码:

 
 
  1. # Returns the Euclidean distance between 2 points  
  2. #  
  3. # Params:  
  4. #  - a, b: list of coordinates (float or integer)  
  5. #  
  6. def euclidean_distance(a, b)  
  7.   sq = a.zip(b).map{|a,b| (a - b) ** 2}  
  8.   Math.sqrt(sq.inject(0) {|s,c| s + c})  
  9. end 
  10. # Returns the associated point of our tags_set, relative to our  
  11. # tags_space.  
  12. #  
  13. # Params:  
  14. #  - tags_set: list of tags  
  15. #  - tags_space: _ordered_ list of tags  
  16. def tags_to_point(tags_set, tags_space)  
  17.   tags_space.map{|c| tags_set.member?(c) ? 1 : 0}  
  18. end 
  19. # Returns other_items sorted by similarity to this_item   
  20. # (most relevant are first in the returned list)  
  21. #  
  22. # Params:  
  23. #  - items: list of hashes that have [:tags]  
  24. #  - by_these_tags: list of tags to compare with  
  25. def sort_by_similarity(items, by_these_tags)  
  26.   tags_space = by_these_tags + items.map{|x| x[:tags]}    
  27.   tags_space.flatten!.sort!.uniq!  
  28.   this_point = tags_to_point(by_these_tags, tags_space)  
  29.   other_points = items.map{|i|   
  30.     [i, tags_to_point(i[:tags], tags_space)]  
  31.   }  
  32.  
  33.   similarities = other_points.map{|item, that_point|  
  34.     [item, euclidean_distance(this_point, that_point)]  
  35.   }  
  36.   sorted = similarities.sort {|a,b| a[1] <=> b[1]}  
  37.   return sorted.map{|point,s| point}  
  38. End 

这是一些示例代码,你可以直接复制运行:

 
 
  1. # SAMPLE DATA  
  2.  
  3. all_articles = [  
  4.   {  
  5.    :article => "Data Mining: Finding Similar Items",   
  6.    :tags => ["Algorithms""Programming""Mining",   
  7.      "Python""Ruby"]  
  8.   },   
  9.   {  
  10.    :article => "Blogging Platform for Hackers",    
  11.    :tags => ["Publishing""Server""Cloud""Heroku",   
  12.      "Jekyll""GAE"]  
  13.   },   
  14.   {  
  15.    :article => "UX Tip: Don't Hurt Me On Sign-Up",   
  16.    :tags => ["Web""Design""UX"]  
  17.   },   
  18.   {  
  19.    :article => "Crawling the Android Marketplace",   
  20.    :tags => ["Python""Android""Mining",   
  21.      "Web""API"]  
  22.   }  
  23. ]  
  24.  
  25. # SORTING these articles by similarity with an article   
  26. # tagged with Publishing + Web + API  
  27. #  
  28. #  
  29. # The list is returned in this order:  
  30. #  
  31. # 1. article: Crawling the Android Marketplace  
  32. #    similarity: 2.0  
  33. #  
  34. # 2. article: "UX Tip: Don't Hurt Me On Sign-Up"  
  35. #    similarity: 2.0  
  36. #  
  37. # 3. article: Blogging Platform for Hackers  
  38. #    similarity: 2.645751  
  39. #  
  40. # 4. article: "Data Mining: Finding Similar Items"  
  41. #    similarity: 2.828427  
  42. #  
  43.  
  44. sorted = sort_by_similarity(  
  45.     all_articles, ['Publishing''Web''API'])  
  46.  
  47. require 'yaml' 
  48. puts YAML.dump(sorted) 

你是否留意到我们之前选择的数据存在一个缺陷?前两篇文章对于标签“["Publishing", "Web", "API"]”有着相同的欧氏几何空间距离。

为了更加形象化,我们来看看计算第一篇文章所用到的点:

 
 
  1. [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]  
  2. [1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1] 

只有四个坐标值不同,我们再来看看第二篇文章所用到的点:

 
 
  1. [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]  
  2. [0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1] 

与第一篇文章相同,也只有4个坐标值不同。欧氏空间距离的度量取决于点之间的差异。这也许不太好,因为相对平均值而言,有更多或更少标签的文章会处于不利地位。

余弦相似度

这种方法与之前的方法类似,但更关注相似性。下面是公式:

下面是Ruby代码:

 
 
  1. def dot_product(a, b)  
  2.   products = a.zip(b).map{|a, b| a * b}  
  3.   products.inject(0) {|s,p| s + p}  
  4. end 
  5.  
  6. def magnitude(point)  
  7.   squares = point.map{|x| x ** 2}  
  8.   Math.sqrt(squares.inject(0) {|s, c| s + c})  
  9. end 
  10.  
  11. # Returns the cosine of the angle between the vectors   
  12. #associated with 2 points  
  13. #  
  14. # Params:  
  15. #  - a, b: list of coordinates (float or integer)  
  16. #  
  17. def cosine_similarity(a, b)  
  18.   dot_product(a, b) / (magnitude(a) * magnitude(b))  
  19. end 

对于以上示例,我们对文章进行分类得到:

 
 
  1. - article: Crawling the Android Marketplace  
  2.   similarity: 0.5163977794943222  
  3. - article: "UX Tip: Don't Hurt Me On Sign-Up" 
  4.   similarity: 0.33333333333333337  
  5. - article: Blogging Platform for Hackers  
  6.   similarity: 0.23570226039551587  
  7. - article: "Data Mining: Finding Similar Items" 
  8.   similarity: 0.0 

这种方法有了很大改善,我们的代码可以很好地运行,但它依然存在问题。

示例中的问题:Tf-ldf权重

我们的数据很简单,可以轻松地计算并作为衡量的依据。如果不采用余弦相似度,很可能会出现相同的结果。

Tf-ldf权重是一种解决方案。Tf-ldf是一个静态统计量,用于权衡文本集合中的一个词在一个文档中的重要性。

根据Tf-ldff,我们可以为坐标值赋予独特的值,而并非局限于0和1.

对于我们刚才示例中的简单数据集,也许更简单的度量方法更适合,比如Jaccard index也许会更好。

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

使用皮尔逊相关系数(Pearson Correlation Coefficient)寻找两个项目之间的相似性略显复杂,也并不是非常适用于我们的数据集合。

例如,我们在IMDB中有2个用户。其中一个用户名为John,对五部电影做了评级:[1,2,3,4,5]。另一个用户名为Mary,对这五部电影也给出了评级:[4, 5, 6, 7, 8]。这两个用户非常相似,他们之间有一个完美的线性关系,Mary的评级都是在John的基础上加3。

计算公式如下:

 代码如下:

 
 
  1. def pearson_score(a, b)  
  2.   n = a.length  
  3.   return 0 unless n > 0  
  4.   # summing the preferences  
  5.   sum1 = a.inject(0) {|sum, c| sum + c}  
  6.   sum2 = b.inject(0) {|sum, c| sum + c}  
  7.   # summing up the squares  
  8.   sum1_sq = a.inject(0) {|sum, c| sum + c ** 2}  
  9.   sum2_sq = b.inject(0) {|sum, c| sum + c ** 2}  
  10.   # summing up the product  
  11.   prod_sum = a.zip(b).inject(0) {|sum, ab| sum + ab[0] * ab[1]}  
  12.   # calculating the Pearson score  
  13.   num = prod_sum - (sum1 *sum2 / n)    
  14.   den = Math.sqrt((sum1_sq - (sum1 ** 2) / n) * (sum2_sq - (sum2 ** 2) / n))  
  15.   return 0 if den == 0  
  16.   return num / den    
  17. end 
  18. puts pearson_score([1,2,3,4,5], [4,5,6,7,8])  
  19. # => 1.0  
  20. puts pearson_score([1,2,3,4,5], [4,5,0,7,8])  
  21. # => 0.5063696835418333  
  22. puts pearson_score([1,2,3,4,5], [4,5,0,7,7])  
  23. # => 0.4338609156373132  
  24. puts pearson_score([1,2,3,4,5], [8,7,6,5,4])  
  25. # => -1 

曼哈顿距离算法

没有放之四海而皆准的真理,我们所使用的公式取决于要处理的数据。下面我们简要介绍一下曼哈顿距离算法。

曼哈顿距离算法计算两点之间的网格距离,维基百科中的图形完美诠释了它与欧氏几何距离的不同:

红线、黄线和蓝线是具有相同长度的曼哈顿距离,绿线代表欧氏几何空间距离

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值