协同推荐SlopeOne 算法

Slope One 推荐算法是 2005 年在香港的 WWW 会议上提出来的。可以Google检索会议论文集看原著,不过还是建议看原著之前,看完本文!

Slope One是一个基于Item的个性化推荐算法,股票上有个说法是平均值可以掩盖一切异常波动,所以股票上的各个技术指标都是不同时间段的平均值的曲线图或者柱状图等。同样的,Slope one算法也认为:平均值也可以代替某两个未知个体之间的打分差异。

Slope One算法的核心思想非常的简单,举例如下:

用户对商品的打分,或者从购买记录中,通过某些算法,折合成打分,得到矩阵如下:

User 二胡 现在购物车里买了索爱手机和大宝,或者 二胡同学以前买过这两个商品,当二胡再次来到网站购物,我们要推荐什么给他呢?亚都加湿器和阿凡达DVD 二胡同学喜欢吗?或者喜欢的程度有多大呢?

来看看Slope One的做法:从二胡同学已经打过分的商品出发

索爱手机对亚都加湿器的平均差是: ((4-4) + (3-4) + (2-5)) / 3 = -1.3 (这说明什么?)
大宝SOD蜜对亚都加湿器的平均差是:((3-4) + (2-4) + (3-5)) / 3 = -1.67(这又说明什么?)

那么二胡同学对亚都加湿器的可能打分是:(3*(5+1.3) + 3*(4+1.67)) / (3+3) = '''5.985'''

二胡同学对阿凡达的打分计算方法雷同,省去不写了。

够清楚吗?给出公式如下:

有 n 个人对商品 A 和商品 B 打分了,R(A->B)表示这 n 个人对 A 和对 B 打分的平均差,有 m 个人对商品 B 和商品 C 打分了,R(C->B)表示这 m 个人对C和对B打分的平均差,注意都是平均差而不是平方差,现在某个用户对 A 的打分是 ra,对 C 的打分是 rc,那么 A 对 B 的打分 rb 是:

rb = (n * (ra - R(A->B)) + m * (rc - R(C->B)))/(m+n)





          相信大家对如下的Category都很熟悉,很多网站都有类似如下的功能,“商品推荐”,"猜你喜欢“,在实体店中我们有导购来为我们服务,在网络上

我们需要同样的一种替代物,如果简简单单的在数据库里面去捞,去比较,几乎是完成不了的,这时我们就需要一种协同推荐算法,来高效的推荐浏览者喜

欢的商品。

一:概念

     SlopeOne的思想很简单,就是用均值化的思想来掩盖个体的打分差异,举个例子说明一下:

在这个图中,系统该如何计算“王五“对”电冰箱“的打分值呢?刚才我们也说了,slopeone是采用均值化的思想,也就是:R王五 =4-{[(5-10)+(4-5)]/2}=7 。

下面我们看看多于两项的商品,如何计算打分值。

rb = (n * (ra - R(A->B)) + m * (rc - R(C->B)))/(m+n)

注意: a,b,c 代表“商品”。

         ra 代表“商品的打分值”。

        ra->b  代表“A组到B组的平均差(均值化)”。

       m,n 代表人数。

根据公式,我们来算一下。

r王五 = (2 * (4 - R(洗衣机->彩电)) + 2 * (10 - R(电冰箱->彩电))+ 2 * (5 - R(空调->彩电)))/(2+2+2)=6.8

是的,slopeOne就是这么简单,实战效果非常不错。

 

二:实现

1:定义一个评分类Rating。

复制代码
 1     /// <summary>
 2     /// 评分实体类
 3     /// </summary>
 4     public class Rating
 5     {
 6         /// <summary>
 7         /// 记录差值
 8         /// </summary>
 9         public float Value { get; set; }
10 
11         /// <summary>
12         /// 记录评分人数,方便公式中的 m 和 n 的值
13         /// </summary>
14         public int Freq { get; set; }
15 
16         /// <summary>
17         /// 记录打分用户的ID
18         /// </summary>
19         public HashSet<int> hash_user = new HashSet<int>();
20 
21         /// <summary>
22         /// 平均值
23         /// </summary>
24         public float AverageValue
25         {
26             get { return Value / Freq; }
27         }
28     }
复制代码

2: 定义一个产品类

复制代码
 1     /// <summary>
 2     /// 产品类
 3     /// </summary>
 4     public class Product
 5     {
 6         public int ProductID { get; set; }
 7 
 8         public string ProductName { get; set; }
 9 
10         /// <summary>
11         /// 对产品的打分
12         /// </summary>
13         public float Score { get; set; }
14     }
复制代码

3:SlopeOne类

     参考了网络上的例子,将二维矩阵做成线性表,有效的降低了空间复杂度。

复制代码
  1 using System;
  2 using System.Collections.Generic;
  3 using System.Linq;
  4 using System.Text;
  5 
  6 namespace SupportCenter.Test
  7 {
  8     #region Slope One 算法
  9     /// <summary>
 10     /// Slope One 算法
 11     /// </summary>
 12     public class SlopeOne
 13     {
 14         /// <summary>
 15         /// 评分系统
 16         /// </summary>
 17         public static Dictionary<int, Product> dicRatingSystem = new Dictionary<int, Product>();
 18 
 19         public Dictionary<string, Rating> dic_Martix = new Dictionary<string, Rating>();
 20 
 21         public HashSet<int> hash_items = new HashSet<int>();
 22 
 23         #region 接收一个用户的打分记录
 24         /// <summary>
 25         /// 接收一个用户的打分记录
 26         /// </summary>
 27         /// <param name="userRatings"></param>
 28         public void AddUserRatings(IDictionary<int, List<Product>> userRatings)
 29         {
 30             foreach (var user1 in userRatings)
 31             {
 32                 //遍历所有的Item
 33                 foreach (var item1 in user1.Value)
 34                 {
 35                     //该产品的编号(具有唯一性)
 36                     int item1Id = item1.ProductID;
 37 
 38                     //该项目的评分
 39                     float item1Rating = item1.Score;
 40 
 41                     //将产品编号字存放在hash表中
 42                     hash_items.Add(item1.ProductID);
 43 
 44                     foreach (var user2 in userRatings)
 45                     {
 46                         //再次遍历item,用于计算俩俩 Item 之间的差值
 47                         foreach (var item2 in user2.Value)
 48                         {
 49                             //过滤掉同名的项目
 50                             if (item2.ProductID <= item1Id)
 51                                 continue;
 52 
 53                             //该产品的名字
 54                             int item2Id = item2.ProductID;
 55 
 56                             //该项目的评分
 57                             float item2Rating = item2.Score;
 58 
 59                             Rating ratingDiff;
 60 
 61                             //用表的形式构建矩阵
 62                             var key = Tools.GetKey(item1Id, item2Id);
 63 
 64                             //将俩俩 Item 的差值 存放到 Rating 中
 65                             if (dic_Martix.Keys.Contains(key))
 66                                 ratingDiff = dic_Martix[key];
 67                             else
 68                             {
 69                                 ratingDiff = new Rating();
 70                                 dic_Martix[key] = ratingDiff;
 71                             }
 72 
 73                             //方便以后以后userrating的编辑操作,(add)
 74                             if (!ratingDiff.hash_user.Contains(user1.Key))
 75                             {
 76                                 //value保存差值
 77                                 ratingDiff.Value += item1Rating - item2Rating;
 78 
 79                                 //说明计算过一次
 80                                 ratingDiff.Freq += 1;
 81                             }
 82 
 83                             //记录操作人的ID,方便以后再次添加评分
 84                             ratingDiff.hash_user.Add(user1.Key);
 85                         }
 86                     }
 87                 }
 88             }
 89         }
 90         #endregion
 91 
 92         #region 根据矩阵的值,预测出该Rating中的值
 93         /// <summary>
 94         /// 根据矩阵的值,预测出该Rating中的值
 95         /// </summary>
 96         /// <param name="userRatings"></param>
 97         /// <returns></returns>
 98         public IDictionary<int, float> Predict(List<Product> userRatings)
 99         {
100             Dictionary<int, float> predictions = new Dictionary<int, float>();
101 
102             var productIDs = userRatings.Select(i => i.ProductID).ToList();
103 
104             //循环遍历_Items中所有的Items
105             foreach (var itemId in this.hash_items)
106             {
107                 //过滤掉不需要计算的产品编号
108                 if (productIDs.Contains(itemId))
109                     continue;
110 
111                 Rating itemRating = new Rating();
112 
113                 // 内层遍历userRatings
114                 foreach (var userRating in userRatings)
115                 {
116                     if (userRating.ProductID == itemId)
117                         continue;
118 
119                     int inputItemId = userRating.ProductID;
120 
121                     //获取该key对应项目的两组AVG的值
122                     var key = Tools.GetKey(itemId, inputItemId);
123 
124                     if (dic_Martix.Keys.Contains(key))
125                     {
126                         Rating diff = dic_Martix[key];
127 
128                         //关键点:运用公式求解(这边为了节省空间,对角线两侧的值呈现奇函数的特性)
129                         itemRating.Value += diff.Freq * (userRating.Score + diff.AverageValue * ((itemId < inputItemId) ? 1 : -1));
130 
131                         //关键点:运用公式求解 累计每两组的人数
132                         itemRating.Freq += diff.Freq;
133                     }
134                 }
135 
136                 predictions.Add(itemId, itemRating.AverageValue);
137             }
138 
139             return predictions;
140         }
141         #endregion
142     }
143     #endregion
144 
145     #region 工具类
146     /// <summary>
147     /// 工具类
148     /// </summary>
149     public class Tools
150     {
151         public static string GetKey(int Item1Id, int Item2Id)
152         {
153             return (Item1Id < Item2Id) ? Item1Id + "->" + Item2Id : Item2Id + "->" + Item1Id;
154         }
155     }
156     #endregion
157 }
复制代码

 

4: 测试类Program

    这里我们灌入了userid=1000,2000,3000的这三个人,然后我们预测userID=3000这个人对 “彩电” 的打分会是多少?

复制代码
 1     public class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             SlopeOne test = new SlopeOne();
 6 
 7             Dictionary<int, List<Product>> userRating = new Dictionary<int, List<Product>>();
 8 
 9             //第一位用户
10             List<Product> list = new List<Product>()
11             {
12                 new Product(){ ProductID=1, ProductName="洗衣机",Score=5},
13                 new Product(){ ProductID=2, ProductName="电冰箱", Score=10},
14                 new Product(){ ProductID=3, ProductName="彩电", Score=10},
15                 new Product(){ ProductID=4, ProductName="空调", Score=5},
16             };
17 
18             userRating.Add(1000, list);
19 
20             test.AddUserRatings(userRating);
21 
22             userRating.Clear();
23             userRating.Add(1000, list);
24 
25             test.AddUserRatings(userRating);
26 
27             //第二位用户
28             list = new List<Product>()
29             {
30                 new Product(){ ProductID=1, ProductName="洗衣机",Score=4},
31                 new Product(){ ProductID=2, ProductName="电冰箱", Score=5},
32                 new Product(){ ProductID=3, ProductName="彩电", Score=4},
33                  new Product(){ ProductID=4, ProductName="空调", Score=10},
34             };
35 
36             userRating.Clear();
37             userRating.Add(2000, list);
38 
39             test.AddUserRatings(userRating);
40 
41             //第三位用户
42             list = new List<Product>()
43             {
44                 new Product(){ ProductID=1, ProductName="洗衣机", Score=4},
45                 new Product(){ ProductID=2, ProductName="电冰箱", Score=10},
46                 new Product(){ ProductID=4, ProductName="空调", Score=5},
47             };
48 
49             userRating.Clear();
50             userRating.Add(3000, list);
51 
52             test.AddUserRatings(userRating);
53 
54             //那么我们预测userID=3000这个人对 “彩电” 的打分会是多少?
55             var userID = userRating.Keys.FirstOrDefault();
56             var result = userRating[userID];
57 
58             var predictions = test.Predict(result);
59 
60             foreach (var rating in predictions)
61                 Console.WriteLine("ProductID= " + rating.Key + " Rating: " + rating.Value);
62         }
63     }
复制代码








http://www.cnblogs.com/huangxincheng/archive/2012/11/22/2782647.html

          相信大家对如下的Category都很熟悉,很多网站都有类似如下的功能,“商品推荐”,"猜你喜欢“,在实体店中我们有导购来为我们服务,在网络上

我们需要同样的一种替代物,如果简简单单的在数据库里面去捞,去比较,几乎是完成不了的,这时我们就需要一种协同推荐算法,来高效的推荐浏览者喜

欢的商品。

一:概念

     SlopeOne的思想很简单,就是用均值化的思想来掩盖个体的打分差异,举个例子说明一下:

在这个图中,系统该如何计算“王五“对”电冰箱“的打分值呢?刚才我们也说了,slopeone是采用均值化的思想,也就是:R王五 =4-{[(5-10)+(4-5)]/2}=7 。

下面我们看看多于两项的商品,如何计算打分值。

rb = (n * (ra - R(A->B)) + m * (rc - R(C->B)))/(m+n)

注意: a,b,c 代表“商品”。

         ra 代表“商品的打分值”。

        ra->b  代表“A组到B组的平均差(均值化)”。

       m,n 代表人数。

根据公式,我们来算一下。

r王五 = (2 * (4 - R(洗衣机->彩电)) + 2 * (10 - R(电冰箱->彩电))+ 2 * (5 - R(空调->彩电)))/(2+2+2)=6.8

是的,slopeOne就是这么简单,实战效果非常不错。

 

二:实现

1:定义一个评分类Rating。

复制代码
 1     /// <summary>
 2     /// 评分实体类
 3     /// </summary>
 4     public class Rating
 5     {
 6         /// <summary>
 7         /// 记录差值
 8         /// </summary>
 9         public float Value { get; set; }
10 
11         /// <summary>
12         /// 记录评分人数,方便公式中的 m 和 n 的值
13         /// </summary>
14         public int Freq { get; set; }
15 
16         /// <summary>
17         /// 记录打分用户的ID
18         /// </summary>
19         public HashSet<int> hash_user = new HashSet<int>();
20 
21         /// <summary>
22         /// 平均值
23         /// </summary>
24         public float AverageValue
25         {
26             get { return Value / Freq; }
27         }
28     }
复制代码

2: 定义一个产品类

复制代码
 1     /// <summary>
 2     /// 产品类
 3     /// </summary>
 4     public class Product
 5     {
 6         public int ProductID { get; set; }
 7 
 8         public string ProductName { get; set; }
 9 
10         /// <summary>
11         /// 对产品的打分
12         /// </summary>
13         public float Score { get; set; }
14     }
复制代码

3:SlopeOne类

     参考了网络上的例子,将二维矩阵做成线性表,有效的降低了空间复杂度。

复制代码
  1 using System;
  2 using System.Collections.Generic;
  3 using System.Linq;
  4 using System.Text;
  5 
  6 namespace SupportCenter.Test
  7 {
  8     #region Slope One 算法
  9     /// <summary>
 10     /// Slope One 算法
 11     /// </summary>
 12     public class SlopeOne
 13     {
 14         /// <summary>
 15         /// 评分系统
 16         /// </summary>
 17         public static Dictionary<int, Product> dicRatingSystem = new Dictionary<int, Product>();
 18 
 19         public Dictionary<string, Rating> dic_Martix = new Dictionary<string, Rating>();
 20 
 21         public HashSet<int> hash_items = new HashSet<int>();
 22 
 23         #region 接收一个用户的打分记录
 24         /// <summary>
 25         /// 接收一个用户的打分记录
 26         /// </summary>
 27         /// <param name="userRatings"></param>
 28         public void AddUserRatings(IDictionary<int, List<Product>> userRatings)
 29         {
 30             foreach (var user1 in userRatings)
 31             {
 32                 //遍历所有的Item
 33                 foreach (var item1 in user1.Value)
 34                 {
 35                     //该产品的编号(具有唯一性)
 36                     int item1Id = item1.ProductID;
 37 
 38                     //该项目的评分
 39                     float item1Rating = item1.Score;
 40 
 41                     //将产品编号字存放在hash表中
 42                     hash_items.Add(item1.ProductID);
 43 
 44                     foreach (var user2 in userRatings)
 45                     {
 46                         //再次遍历item,用于计算俩俩 Item 之间的差值
 47                         foreach (var item2 in user2.Value)
 48                         {
 49                             //过滤掉同名的项目
 50                             if (item2.ProductID <= item1Id)
 51                                 continue;
 52 
 53                             //该产品的名字
 54                             int item2Id = item2.ProductID;
 55 
 56                             //该项目的评分
 57                             float item2Rating = item2.Score;
 58 
 59                             Rating ratingDiff;
 60 
 61                             //用表的形式构建矩阵
 62                             var key = Tools.GetKey(item1Id, item2Id);
 63 
 64                             //将俩俩 Item 的差值 存放到 Rating 中
 65                             if (dic_Martix.Keys.Contains(key))
 66                                 ratingDiff = dic_Martix[key];
 67                             else
 68                             {
 69                                 ratingDiff = new Rating();
 70                                 dic_Martix[key] = ratingDiff;
 71                             }
 72 
 73                             //方便以后以后userrating的编辑操作,(add)
 74                             if (!ratingDiff.hash_user.Contains(user1.Key))
 75                             {
 76                                 //value保存差值
 77                                 ratingDiff.Value += item1Rating - item2Rating;
 78 
 79                                 //说明计算过一次
 80                                 ratingDiff.Freq += 1;
 81                             }
 82 
 83                             //记录操作人的ID,方便以后再次添加评分
 84                             ratingDiff.hash_user.Add(user1.Key);
 85                         }
 86                     }
 87                 }
 88             }
 89         }
 90         #endregion
 91 
 92         #region 根据矩阵的值,预测出该Rating中的值
 93         /// <summary>
 94         /// 根据矩阵的值,预测出该Rating中的值
 95         /// </summary>
 96         /// <param name="userRatings"></param>
 97         /// <returns></returns>
 98         public IDictionary<int, float> Predict(List<Product> userRatings)
 99         {
100             Dictionary<int, float> predictions = new Dictionary<int, float>();
101 
102             var productIDs = userRatings.Select(i => i.ProductID).ToList();
103 
104             //循环遍历_Items中所有的Items
105             foreach (var itemId in this.hash_items)
106             {
107                 //过滤掉不需要计算的产品编号
108                 if (productIDs.Contains(itemId))
109                     continue;
110 
111                 Rating itemRating = new Rating();
112 
113                 // 内层遍历userRatings
114                 foreach (var userRating in userRatings)
115                 {
116                     if (userRating.ProductID == itemId)
117                         continue;
118 
119                     int inputItemId = userRating.ProductID;
120 
121                     //获取该key对应项目的两组AVG的值
122                     var key = Tools.GetKey(itemId, inputItemId);
123 
124                     if (dic_Martix.Keys.Contains(key))
125                     {
126                         Rating diff = dic_Martix[key];
127 
128                         //关键点:运用公式求解(这边为了节省空间,对角线两侧的值呈现奇函数的特性)
129                         itemRating.Value += diff.Freq * (userRating.Score + diff.AverageValue * ((itemId < inputItemId) ? 1 : -1));
130 
131                         //关键点:运用公式求解 累计每两组的人数
132                         itemRating.Freq += diff.Freq;
133                     }
134                 }
135 
136                 predictions.Add(itemId, itemRating.AverageValue);
137             }
138 
139             return predictions;
140         }
141         #endregion
142     }
143     #endregion
144 
145     #region 工具类
146     /// <summary>
147     /// 工具类
148     /// </summary>
149     public class Tools
150     {
151         public static string GetKey(int Item1Id, int Item2Id)
152         {
153             return (Item1Id < Item2Id) ? Item1Id + "->" + Item2Id : Item2Id + "->" + Item1Id;
154         }
155     }
156     #endregion
157 }
复制代码

 

4: 测试类Program

    这里我们灌入了userid=1000,2000,3000的这三个人,然后我们预测userID=3000这个人对 “彩电” 的打分会是多少?

复制代码
 1     public class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             SlopeOne test = new SlopeOne();
 6 
 7             Dictionary<int, List<Product>> userRating = new Dictionary<int, List<Product>>();
 8 
 9             //第一位用户
10             List<Product> list = new List<Product>()
11             {
12                 new Product(){ ProductID=1, ProductName="洗衣机",Score=5},
13                 new Product(){ ProductID=2, ProductName="电冰箱", Score=10},
14                 new Product(){ ProductID=3, ProductName="彩电", Score=10},
15                 new Product(){ ProductID=4, ProductName="空调", Score=5},
16             };
17 
18             userRating.Add(1000, list);
19 
20             test.AddUserRatings(userRating);
21 
22             userRating.Clear();
23             userRating.Add(1000, list);
24 
25             test.AddUserRatings(userRating);
26 
27             //第二位用户
28             list = new List<Product>()
29             {
30                 new Product(){ ProductID=1, ProductName="洗衣机",Score=4},
31                 new Product(){ ProductID=2, ProductName="电冰箱", Score=5},
32                 new Product(){ ProductID=3, ProductName="彩电", Score=4},
33                  new Product(){ ProductID=4, ProductName="空调", Score=10},
34             };
35 
36             userRating.Clear();
37             userRating.Add(2000, list);
38 
39             test.AddUserRatings(userRating);
40 
41             //第三位用户
42             list = new List<Product>()
43             {
44                 new Product(){ ProductID=1, ProductName="洗衣机", Score=4},
45                 new Product(){ ProductID=2, ProductName="电冰箱", Score=10},
46                 new Product(){ ProductID=4, ProductName="空调", Score=5},
47             };
48 
49             userRating.Clear();
50             userRating.Add(3000, list);
51 
52             test.AddUserRatings(userRating);
53 
54             //那么我们预测userID=3000这个人对 “彩电” 的打分会是多少?
55             var userID = userRating.Keys.FirstOrDefault();
56             var result = userRating[userID];
57 
58             var predictions = test.Predict(result);
59 
60             foreach (var rating in predictions)
61                 Console.WriteLine("ProductID= " + rating.Key + " Rating: " + rating.Value);
62         }
63     }
复制代码

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值