針對投資者智能推薦金融產品是不是一個好主意?實際上許多互聯網金融公司已經開始了這方面的嘗試,陸金所的用戶中心界面下方的‘爲您推薦’欄目就是這方面的嘗試,具體如下圖:
這個就是典型的基於用戶相似度做智能推薦的產品,現在我們就來揭開一種基於用戶相似度做推薦算法的神祕面紗吧!!
本博客主要參考文獻張丹在這篇博客http://blog.fens.me/r-mahout-usercf/裏給出了協同過濾算法的R代碼。
我主要使用R做模型,具體會使用到Matrix、arules、proxy、recommenderlab包,具體如下:
library(Matrix)
library(arules)
library(proxy)
library(recommenderlab)
這些安裝包很容易在鏡像網站找到,我這裏就不列出具體地址了。
主要數據如下:
需要把它修改成如下格式:
我們需要把這種格式的數據轉化爲表現用戶和產品關係的二維矩陣,如下圖:
我是通過下面的函數做到的(by 張丹):
#將投資統計數據轉化爲用戶與投資品的購買矩陣
FileDataModel<-function(file){
data<-read.csv(file,na.string='NA',header=T)
names(data)<-c("uid","iid","pref")
user <- unique(data$uid)
item <- unique(sort(data$iid))
uidx <- match(data$uid, user)
#爲用戶名建立唯一索引
iidx <- match(data$iid, item)
#爲投資品建立唯一索引
M <- matrix(0, length(user), length(item))
#建立空矩陣以存放用戶與投資品關係數據
i <- cbind(uidx, iidx, pref=data$pref)
#匹配用戶名索引、投資品索引、投資喜好
for(n in 1:nrow(i)){
M[i[n,][1],i[n,][2]]<-i[n,][3]
}
#將喜好數據導入到關係矩陣
dimnames(M)[[2]]<-item
#修改矩陣列名
M
}
在轉化完關係矩陣之後,我們需要計算每個投資者之間的距離以確定與其他投資者的相似度,如下圖:
代碼如下:
#歐氏距離相似度算法,按行計算與其他用戶的相似度
EuclideanDistanceSimilarity<-function(M){
row<-nrow(M)
s<-matrix(0, row, row)
for(z1 in 1:row){
for(z2 in 1:row){
if(z1<z2){
num<-intersect(which(M[z1,]!=0),which(M[z2,]!=0))
#不同投資品只有不同投資者均有投資纔可計算該列的相似度
sum<-0
for(z3 in num){
sum<-sum+(M[z1,][z3]-M[z2,][z3])^2
}
s[z2,z1]<-length(num)/(1+sqrt(sum))
#將歐氏距離倒置用來計算相似度,距離越小,相似度越大
if(s[z2,z1]>1) s[z2,z1]<-1
#將過大的相似度標準化爲1
if(s[z2,z1]< -1) s[z2,z1]<- -1
#將過小的相似度標準化爲-1
#print(paste(z1,z2));print(num);print(sum)
}
}
}
#補全三角矩陣
ts<-t(s)
w<-which(upper.tri(ts))
s[w]<-ts[w]
s
}
計算完用戶之間的距離後,需要確認他們的近鄰關係以備後面計算推薦產品,具體如下:
代碼如下:
#最近鄰算法,按相似度高低組建相似度最高的兩個投資用戶的近鄰矩陣
NearestNUserNeighborhood<-function(S,n){
row<-nrow(S)
neighbor<-matrix(0, row, n)
for(z1 in 1:row){
for(z2 in 1:n){
m<-which.max(S[,z1])
# print(paste(z1,z2,m,'\n'))
neighbor[z1,][z2]<-m
S[,z1][m]=0
}
}
neighbor
}
注意:這裏出現了後續我所惱火的最大問題,就是給大部分投資者推薦的都是前面幾個用戶的組合投資產品!爲什麼呢,下一章給大家講一下原因。
然後開始基於近鄰的投資習慣,推薦合適的投資產品給該投資者,結果如下圖:
代碼如下:
#推薦算法,推薦相似度最高的兩用戶均投資過的產品,並依據喜好權重給出推薦評分
UserBasedRecommender<-function(uid,n,M,S,N){
row<-ncol(N)
col<-ncol(M)
r<-matrix(0, row, col)
N1<-N[uid,]
for(z1 in 1:length(N1)){
num<-intersect(which(M[uid,]==0),which(M[N1[z1],]!=0)) #可計算的列
# print(num)
for(z2 in num){
# print(paste("for:",z1,N1[z1],z2,M[N1[z1],z2],S[uid,N1[z1]]))
r[z1,z2]=M[N1[z1],z2]*S[uid,N1[z1]]
}
}
sum<-colSums(r)
s2<-matrix(0, 2, col)
for(z1 in 1:length(N1)){
num<-intersect(which(colSums(r)!=0),which(M[N1[z1],]!=0))
for(z2 in num){
s2[1,][z2]<-s2[1,][z2]+S[uid,N1[z1]]
s2[2,][z2]<-s2[2,][z2]+1
}
}
s2[,which(s2[2,]==1)]=10000
s2<-s2[-2,]
r2<-matrix(0, n, 2)
rr<-sum/s2
item <-dimnames(M)[[2]]
for(z1 in 1:n){
w<-which.max(rr)
if(rr[w]>0.5){
r2[z1,1]<-item[which.max(rr)]
r2[z1,2]<-as.double(rr[w])
rr[w]=0
}
}
r2
}
#執行程序
FILE<-'F:/Rdata/hnjb/hnjbxtgltj2.csv'
NEIGHBORHOOD_NUM<-2
RECOMMENDER_NUM<-3
M<-FileDataModel(FILE)
#構建模型基礎數據
S<-EuclideanDistanceSimilarity(M)
#構建相似度矩陣
N<-NearestNUserNeighborhood(S,NEIGHBORHOOD_NUM)
#構建近鄰矩陣
#部分可執行,部分用戶由於無相似數據沒辦法執行
R1<-UserBasedRecommender(1,RECOMMENDER_NUM,M,S,N);R1
R2<-UserBasedRecommender(2,RECOMMENDER_NUM,M,S,N);R2
R3<-UserBasedRecommender(3,RECOMMENDER_NUM,M,S,N);R3
R4<-UserBasedRecommender(4,RECOMMENDER_NUM,M,S,N);R4
R5<-UserBasedRecommender(5,RECOMMENDER_NUM,M,S,N);R5
做完這個推薦算法,我對結果極不滿意,主要是出的結果價值不大,都推薦投資者去購買新手標了?!爲什麼呢?
我總結了一下,有以下三個問題:
1、數據基礎方面的問題:
1)產品不多,只有五個,可選擇性太少,可能需要把產品拆得更細才ok——後續把明細產品投資數據拿過來做模型可能會更好;
2)投資者太多,導致相似的概率較大——後續基於相似投資者的統計數據做推薦;
3)另大部分投資者只投資一個產品,根本無法計算相似度——後續剔除部分單次投資數據嘗試做推薦;
2、R語言實現問題:
1)如張丹在博客中所說,for語句較多,執行起來較慢,我需要半個小時才能執行一次;
2)近鄰矩陣只有選擇相似度最高的兩人我覺得還是有點問題,我覺得如果投資者較多,可以根據投資者數量適當調整近鄰矩陣容量,方便後續根據相似度最高的TOP100做投資品的統計分析;
3、模型相似度選擇問題:
1)本模型基於歐氏距離計算相似度,可能使用皮爾森相似度等來計算更好;
2)後續的推薦模型是加權平均算法,也可以根據TOP100的相似度高低來做加權平均以獲得更好的結果。
總之,上面只是推薦算法的簡單實踐,如果需要用它來解決實際問題,還需要在工作中多考慮實際情況,並根據情況調整自己的模型思路就行了。