转自:http://www.cnblogs.com/zhangchaoyang/articles/2664366.html
Collaborative Filtering Recommendation
向量之间的相似度
度量向量之间的相似度方法很多了,你可以用距离(各种距离)的倒数,向量夹角,Pearson相关系数等。
皮尔森相关系数计算公式如下:
分子是协方差,分子是两个变量标准差的乘积。显然要求X和Y的标准差都不能为0。
因为
当两个变量的线性关系增强时,相关系数趋于1或-1。
Pearson相关系数有个特点,它在计算两个数列的相似度时忽略其平均值的差异。比如说有的用户对商品评分普遍偏低,有的用户评分普遍偏高,而实际上他们具有相同的爱好,他们的Pearson相关系数会比较高。用户1对某一个商品的评分是X=(1,2,3),用户2对这三个商品的评分是Y=(4,5,6),则X和Y的Pearson相关系数是0.865,相关性还是挺高的。
iterm1 | ………… | itemn | |
user1 | R11 | R1n | |
…… | Rij | ||
userm | Rm1 | Rmn |
用户评分数据矩阵
基于用户的协同过滤
step1.如果用户i对项目j没有评过分,就找到与用户i最相似的K个邻居(采用Pearson相关系数)
step2.然后用这K个邻居对项目j的评分的加权平均来预测用户i对项目j的评分。
要预测用户u对商品i的评分
用户u对所有商品的平均得分为
用户x评分的商品集合为
采用Pearson相关系数用户x和y的相似度:
则
其中U是用户u的近邻,z是归一化因子,
这各预测方法充分考虑了用户一向的评分习惯是偏高还是偏低,因为用户u的近邻对u产生影响时已经减去了各自的平均值。
计算用户
基于物品的协同过滤
step1.如果用户i对项目j没有评过分,就把
step2.然后用这K个邻居对项目j的评分的加权平均来预测用户i对项目j的评分。
每一项减去各个用户评分的均值:
商品i和j之间的相似度采用余弦计算:
预测用户u对商品i的评分:
其中N是商品i的近邻。
由于物品之间的相似度比较稳定,可以离线先算好,定期更新即可。在电商行业这种算法用的比较多。
混合协同过滤
所谓的混合算法,主体思路还是基于用户的协同过滤,只是在计算两个用户的相似度时又嵌套了item-based CF思想。
度量用户i和用户j相似度更好的方法是:
1.用户i参与评分的项目集合为
2.在集合
3.这样用户i和j对
示例代码:
1 #include<iostream>
2 #include<queue>
3 #include<cmath>
4 #include<cassert>
5 #include<cstdlib>
6 #include<fstream>
7 #include<sstream>
8 #include<vector>
9 #include<algorithm>
10
11 using namespace std;
12
13 const int ITERM_SIZE=1682;
14 const int USER_SIZE=943;
15 const int V=15; //ITERM的最近邻居数
16 const int S=10; //USER的最近邻居数
17
18 struct MyPair{
19 int id;
20 double value;
21 MyPair(int i=0,double v=0):id(i),value(v){}
22 };
23
24 struct cmp{
25 bool operator() (const MyPair & obj1,const MyPair & obj2)const{
26 return obj1.value < obj2.value;
27 }
28 };
29
30 double rate[USER_SIZE][ITERM_SIZE]; //评分矩阵
31 MyPair nbi[ITERM_SIZE][V]; //存放每个ITERM的最近邻居
32 MyPair nbu[USER_SIZE][S]; //存放每个USER的最近邻居
33 double rate_avg[USER_SIZE]; //每个用户的平均评分
34
35 //从文件中读入评分矩阵
36 int readRate(string filename){
37 ifstream ifs;
38 ifs.open(filename.c_str());
39 if(!ifs){
40 cerr<<"error:unable to open input file "<<filename<<endl;
41 return -1;
42 }
43 string line;
44 while(getline(ifs,line)){
45 string str1,str2,str3;
46 istringstream strstm(line);
47 strstm>>str1>>str2>>str3;
48 int userid=atoi(str1.c_str());
49 int itermid=atoi(str2.c_str());
50 double rating=atof(str3.c_str());
51 rate[userid-1][itermid-1]=rating;
52 line.clear();
53 }
54 ifs.close();
55 return 0;
56 }
57
58 //计算每个用户的平均评分
59 void getAvgRate(){
60 for(int i=0;i<USER_SIZE;++i){
61 double sum=0;
62 for(int j=0;j<ITERM_SIZE;++j)
63 sum+=rate[i][j];
64 rate_avg[i]=sum/ITERM_SIZE;
65 }
66 }
67
68 //计算两个向量的皮尔森相关系数
69 double getSim(const vector<double> &vec1,const vector<double> &vec2){
70 int len=vec1.size();
71 assert(len==vec2.size());
72 double sum1=0;
73 double sum2=0;
74 double sum1_1=0;
75 double sum2_2=0;
76 double sum=0;
77 for(int i=0;i<len;i++){
78 sum+=vec1[i]*vec2[i];
79 sum1+=vec1[i];
80 sum2+=vec2[i];
81 sum1_1+=vec1[i]*vec1[i];
82 sum2_2+=vec2[i]*vec2[i];
83 }
84 double ex=sum1/len;
85 double ey=sum2/len;
86 double ex2=sum1_1/len;
87 double ey2=sum2_2/len;
88 double exy=sum/len;
89 double sdx=sqrt(ex2-ex*ex);
90 double sdy=sqrt(ey2-ey*ey);
91 assert(sdx!=0 && sdy!=0);
92 double sim=(exy-ex*ey)/(sdx*sdy);
93 return sim;
94 }
95
96 //计算每个ITERM的最近邻
97 void getNBI(){
98 for(int i=0;i<ITERM_SIZE;++i){
99 vector<double> vec1;
100 priority_queue<MyPair,vector<MyPair>,cmp> neighbour;
101 for(int k=0;k<USER_SIZE;k++)
102 vec1.push_back(rate[k][i]);
103 for(int j=0;j<ITERM_SIZE;j++){
104 if(i==j)
105 continue;
106 vector<double> vec2;
107 for(int k=0;k<USER_SIZE;k++)
108 vec2.push_back(rate[k][j]);
109 double sim=getSim(vec1,vec2);
110 MyPair p(j,sim);
111 neighbour.push(p);
112 }
113 for(int j=0;j<V;++j){
114 nbi[i][j]=neighbour.top();
115 neighbour.pop();
116 }
117 }
118 }
119
120 //预测用户对未评分项目的评分值
121 double getPredict(const vector<double> &user,int index){
122 double sum1=0;
123 double sum2=0;
124 for(int i=0;i<V;++i){
125 int neib_index=nbi[index][i].id;
126 double neib_sim=nbi[index][i].value;
127 sum1+=neib_sim*user[neib_index];
128 sum2+=fabs(neib_sim);
129 }
130 return sum1/sum2;
131 }
132
133 //计算两个用户的相似度
134 double getUserSim(const vector<double> &user1,const vector<double> &user2){
135 vector<double> vec1;
136 vector<double> vec2;
137 int len=user1.size();
138 assert(len==user2.size());
139 for(int i=0;i<len;++i){
140 if(user1[i]!=0 || user2[i]!=0){
141 if(user1[i]!=0)
142 vec1.push_back(user1[i]);
143 else
144 vec1.push_back(getPredict(user1,i));
145 if(user2[i]!=0)
146 vec2.push_back(user2[i]);
147 else
148 vec2.push_back(getPredict(user2,i));
149 }
150 }
151 return getSim(vec1,vec2);
152 }
153
154 //计算每个USER的最近邻
155 void getNBU(){
156 for(int i=0;i<USER_SIZE;++i){
157 vector<double> user1;
158 priority_queue<MyPair,vector<MyPair>,cmp> neighbour;
159 for(int k=0;k<ITERM_SIZE;++k)
160 user1.push_back(rate[i][k]);
161 for(int j=0;j<USER_SIZE;++j){
162 if(j==i)
163 continue;
164 vector<double> user2;
165 for(int k=0;k<ITERM_SIZE;++k)
166 user2.push_back(rate[j][k]);
167 double sim=getUserSim(user1,user2);
168 MyPair p(j,sim);
169 neighbour.push(p);
170 }
171 for(int j=0;j<S;++j){
172 nbu[i][j]=neighbour.top();
173 neighbour.pop();
174 }
175 }
176 }
177
178 //产生推荐,预测某用户对某项目的评分
179 double predictRate(int user,int iterm){
180 double sum1=0;
181 double sum2=0;
182 for(int i=0;i<S;++i){
183 int neib_index=nbu[user][i].id;
184 double neib_sim=nbu[user][i].value;
185 sum1+=neib_sim*(rate[neib_index][iterm]-rate_avg[neib_index]);
186 sum2+=fabs(neib_sim);
187 }
188 return rate_avg[user]+sum1/sum2;
189 }
190
191 //测试
192 int main(){
193 string file="/home/orisun/DataSet/movie-lens-100k/u.data";
194 if(readRate(file)!=0){
195 return -1;
196 }
197 getAvgRate();
198 getNBI();
199 getNBU();
200 while(1){
201 cout<<"please input user index and iterm index which you want predict"<<endl;
202 int user,iterm;
203 cin>>user>>iterm;
204 cout<<predictRate(user,iterm)<<endl;
205 }
206 return 0;
207 }
原文来自:博客园(华夏35度)http://www.cnblogs.com/zhangchaoyang 作者:Orisun