轉載自:http://www.cnblogs.com/zhangchaoyang/articles/2631907.html
Collaborative Filtering Recommendation
向量之間的相似度
度量向量之間的相似度方法很多了,你可以用距離(各種距離)的倒數,向量夾角,Pearson相關系數等。
皮爾森相關系數計算公式如下:
分子是協方差,分子是兩個變量標準差的乘積。顯然要求X和Y的標準差都不能為0。
因為
,所以皮爾森相關系數計算公式還可以寫成:
當兩個變量的線性關系增強時,相關系數趨于1或-1。
用戶評分預測
用戶評分預測的基本原理是:
step1. 如果用戶i對項目j沒有評過分,就找到與用戶i最相似的K個鄰居(使用向量相似度度量方法)
step2. 然后用這K個鄰居對項目j的評分的加權平均來預測用戶i對項目j的評分。
| iterm 1 | ………… | item n | |
| user 1 | R 11 | R 1n | |
| …… | R ij | ||
| user m | R m1 | R mn |
用戶評分數據矩陣
step1.
用戶評分矩陣是個高度稀疏的矩陣,即用戶對很多項目都沒有評分。在高度稀疏的情況下用傳統的向量相似度度量方法來度量兩個用戶的相似度就會很不準確。一種簡單的處理辦法是對未評分的項都令其等于該用戶的平均評分值。
度量用戶i和用戶j相似度更好的方法是:
1.用戶i參與評分的項目集合為I
i
,用戶i參與評分的項目集合為I
j
,找到它們的并集
2.在集合
中用戶i未評分的項目是
,重新估計用戶i對
中每個項目的評分
。
2.1.取出評分矩陣的兩列,計算這兩列的相似度就是這兩個項目的相似度。
。找到與
最相似的V個項目構成集合
。
2.2.
3.這樣用戶i和j對
的評分就都是非0值了,在此情況下計算他們的相似度。
step2.
表示用戶u對所有評分過的項目的評分平均值。
完了,可以看到算法非常的簡單,核心就是計算向量的相似度。代碼量也很少。
#include<iostream>
#include
<queue>
#include
<cmath>
#include
<cassert>
#include
<cstdlib>
#include
<fstream>
#include
<sstream>
#include
<vector>
#include
<algorithm>
using
namespace
std;
const
int
ITERM_SIZE=
1682
;
const
int
USER_SIZE=
943
;
const
int
V=
15
;
//
ITERM的最近鄰居數
const
int
S=
10
;
//
USER的最近鄰居數
struct
MyPair{
int
id;
double
value;
MyPair(
int
i=
0
,
double
v=
0
):id(i),value(v){}
};
struct
cmp{
bool
operator
() (
const
MyPair & obj1,
const
MyPair & obj2)
const
{
return
obj1.value <
obj2.value;
}
};
double
rate[USER_SIZE][ITERM_SIZE];
//
評分矩陣
MyPair nbi[ITERM_SIZE][V];
//
存放每個ITERM的最近鄰居
MyPair nbu[USER_SIZE][S];
//
存放每個USER的最近鄰居
double
rate_avg[USER_SIZE];
//
每個用戶的平均評分
//
從文件中讀入評分矩陣
int
readRate(
string
filename){
ifstream ifs;
ifs.open(filename.c_str());
if
(!
ifs){
cerr
<<
"
error:unable to open input file
"
<<filename<<
endl;
return
-
1
;
}
string
line;
while
(getline(ifs,line)){
string
str1,str2,str3;
istringstream strstm(line);
strstm
>>str1>>str2>>
str3;
int
userid=
atoi(str1.c_str());
int
itermid=
atoi(str2.c_str());
double
rating=
atof(str3.c_str());
rate[userid
-
1
][itermid-
1
]=
rating;
line.clear();
}
ifs.close();
return
0
;
}
//
計算每個用戶的平均評分
void
getAvgRate(){
for
(
int
i=
0
;i<USER_SIZE;++
i){
double
sum=
0
;
for
(
int
j=
0
;j<ITERM_SIZE;++
j)
sum
+=
rate[i][j];
rate_avg[i]
=sum/
ITERM_SIZE;
}
}
//
計算兩個向量的皮爾森相關系數
double
getSim(
const
vector<
double
> &vec1,
const
vector<
double
> &
vec2){
int
len=
vec1.size();
assert(len
==
vec2.size());
double
sum1=
0
;
double
sum2=
0
;
double
sum1_1=
0
;
double
sum2_2=
0
;
double
sum=
0
;
for
(
int
i=
0
;i<len;i++
){
sum
+=vec1[i]*
vec2[i];
sum1
+=
vec1[i];
sum2
+=
vec2[i];
sum1_1
+=vec1[i]*
vec1[i];
sum2_2
+=vec2[i]*
vec2[i];
}
double
ex=sum1/
len;
double
ey=sum2/
len;
double
ex2=sum1_1/
len;
double
ey2=sum2_2/
len;
double
exy=sum/
len;
double
sdx=sqrt(ex2-ex*
ex);
double
sdy=sqrt(ey2-ey*
ey);
assert(sdx
!=
0
&& sdy!=
0
);
double
sim=(exy-ex*ey)/(sdx*
sdy);
return
sim;
}
//
計算每個ITERM的最近鄰
void
getNBI(){
for
(
int
i=
0
;i<ITERM_SIZE;++
i){
vector
<
double
>
vec1;
priority_queue
<MyPair,vector<MyPair>,cmp>
neighbour;
for
(
int
k=
0
;k<USER_SIZE;k++
)
vec1.push_back(rate[k][i]);
for
(
int
j=
0
;j<ITERM_SIZE;j++
){
if
(i==
j)
continue
;
vector
<
double
>
vec2;
for
(
int
k=
0
;k<USER_SIZE;k++
)
vec2.push_back(rate[k][j]);
double
sim=
getSim(vec1,vec2);
MyPair p(j,sim);
neighbour.push(p);
}
for
(
int
j=
0
;j<V;++
j){
nbi[i][j]
=
neighbour.top();
neighbour.pop();
}
}
}
//
預測用戶對未評分項目的評分值
double
getPredict(
const
vector<
double
> &user,
int
index){
double
sum1=
0
;
double
sum2=
0
;
for
(
int
i=
0
;i<V;++
i){
int
neib_index=
nbi[index][i].id;
double
neib_sim=
nbi[index][i].value;
sum1
+=neib_sim*
user[neib_index];
sum2
+=
fabs(neib_sim);
}
return
sum1/
sum2;
}
//
計算兩個用戶的相似度
double
getUserSim(
const
vector<
double
> &user1,
const
vector<
double
> &
user2){
vector
<
double
>
vec1;
vector
<
double
>
vec2;
int
len=
user1.size();
assert(len
==
user2.size());
for
(
int
i=
0
;i<len;++
i){
if
(user1[i]!=
0
|| user2[i]!=
0
){
if
(user1[i]!=
0
)
vec1.push_back(user1[i]);
else
vec1.push_back(getPredict(user1,i));
if
(user2[i]!=
0
)
vec2.push_back(user2[i]);
else
vec2.push_back(getPredict(user2,i));
}
}
return
getSim(vec1,vec2);
}
//
計算每個USER的最近鄰
void
getNBU(){
for
(
int
i=
0
;i<USER_SIZE;++
i){
vector
<
double
>
user1;
priority_queue
<MyPair,vector<MyPair>,cmp>
neighbour;
for
(
int
k=
0
;k<ITERM_SIZE;++
k)
user1.push_back(rate[i][k]);
for
(
int
j=
0
;j<USER_SIZE;++
j){
if
(j==
i)
continue
;
vector
<
double
>
user2;
for
(
int
k=
0
;k<ITERM_SIZE;++
k)
user2.push_back(rate[j][k]);
double
sim=
getUserSim(user1,user2);
MyPair p(j,sim);
neighbour.push(p);
}
for
(
int
j=
0
;j<S;++
j){
nbu[i][j]
=
neighbour.top();
neighbour.pop();
}
}
}
//
產生推薦,預測某用戶對某項目的評分
double
predictRate(
int
user,
int
iterm){
double
sum1=
0
;
double
sum2=
0
;
for
(
int
i=
0
;i<S;++
i){
int
neib_index=
nbu[user][i].id;
double
neib_sim=
nbu[user][i].value;
sum1
+=neib_sim*(rate[neib_index][iterm]-
rate_avg[neib_index]);
sum2
+=
fabs(neib_sim);
}
return
rate_avg[user]+sum1/
sum2;
}
//
測試
int
main(){
string
file=
"
/home/orisun/DataSet/movie-lens-100k/u.data
"
;
if
(readRate(file)!=
0
){
return
-
1
;
}
getAvgRate();
getNBI();
getNBU();
while
(
1
){
cout
<<
"
please input user index and iterm index which you want predict
"
<<
endl;
int
user,iterm;
cin
>>user>>
iterm;
cout
<<predictRate(user,iterm)<<
endl;
}
return
0
;
}
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061
微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元

