针对投资者智能推荐金融产品是不是一个好主意?实际上许多互联网金融公司已经开始了这方面的尝试,陆金所的用户中心界面下方的‘为您推荐’栏目就是这方面的尝试,具体如下图:
这个就是典型的基于用户相似度做智能推荐的产品,现在我们就来揭开一种基于用户相似度做推荐算法的神秘面纱吧!!
本博客主要参考文献张丹在这篇博客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的相似度高低来做加权平均以获得更好的结果。
总之,上面只是推荐算法的简单实践,如果需要用它来解决实际问题,还需要在工作中多考虑实际情况,并根据情况调整自己的模型思路就行了。