1、 基于用户的协同过滤推荐算法实现原理
传统的基于用户(User-Based)的协同过滤推荐算法实现原理分四个步骤:
1、根据用户历史行为信息构建用户-项目评分矩阵,用户历史行为信息包括项目评分、浏览历史、收藏历史、喜好标签等,本文以单一的项目评分为例,后期介绍其他行为信息和混合行为信息,用户-项目评分矩阵如表1所示:
表1 用户-项目评分矩阵
注:用户A对项目1的评分是1分,用户A对项目2没有评分。
2、根据用户-项目评分矩阵计算用户之间的相似度。计算相似度常用的方法有余弦算法、修正余弦算法、皮尔森算法等等(后期我们会将相似度算法展开讲解,这里以余弦算法为例)。余弦算法公式如图1所示:
图1 余弦算法公式
注:表示用户u的评分集合(也就是矩阵中的一行评分数据),表示用户v的评分集合,i表示项目,表示用户u对项目1的评分乘以用户v对项目1的评分加上用户u对项目2的评分乘以用户v对项目2的评分……先相加再相乘直到最后一个项目,表示用户u对项目1的评分的平方加上用户u对项目2的评分的平方加上……先平方再相加直到最后一个项目然后得到的值取平方根,平方根乘以用户v的平方根。
3、根据用户之间的相似度得到目标用户的最近邻居KNN。KNN的筛选常用的有两种方式,一种是设置相似度阀值(给定一个相似度的下限,大于下限的相似度为最近邻居),一种是根据与目标用户相似度的高低来选择前N个最近邻居(本次以前N个为例,后期会详细对比讲解两者)。相似度排序可用经典冒泡排序法。
4、预测项目评分并进行推荐。最常用的预测公式如图2所示:
图2 预测评分公式
注:该公式实际上是相似度和评分的加权平均数。表示用户u对项目i的预测评分,n是最近邻集合,v是任意一个最近邻居,表示最近邻v和目标用户u的相似度乘以最近邻v对项目i的评分。得到预测评分后按照评分高低进行降序推荐。
5、结论。以上步骤是最简单,最传统的基于用户的协同过滤推荐算法的实现原理,但是在实现过程中还是有很多注意细节。
2、 基于用户的协同过滤推荐算法实现代码
本文我们介绍两种实现代码,都是java语言开发,单机版(本地测试),数据集使用的ml-100k,943*1682,80000条数据。
第一种,自定义实现:
1、项目目录,如图3所示:
图3 项目目录
2、.java文件,算法主运行方法
/**
* 协同过滤推荐算法运行主方法
*/
class Base {
void main([] args) {
// 输入,并获取
.out.("请输入一个用户Id(1、2、3……943)");
= new (.in);
//获取得到输入的
int = .();
// 从文件中读取数据
int[][] = new int[][];
//读取文件中的数据
= new ().(BASE);
//产生相似度矩阵
[] = new rix().rix(, );
// 知道每个用户之间的相似度值之后,开始获取每隔相似值对应的,然后和相似值关联,再根据相似值排序,即得到相似爱好的,然后再输出相似推荐的商品
int[] id = new int[];//存放K个最近邻
//产生一个临时相似度矩阵变量,是为了相似度排序时和对应
[] = new [.];
for (int j = 0; j < .; j++) {
[j] = [j];
}
.sort();//排序,升序
int flag = 0;//临时变量
[] = new [];//保存前K个相似度,从大到小
for (int m = . - 1; m >= . - ; m--) {
for(int j = 0; j < .; j++) {
if ([j] == [m] && [j] != 0.0){
[flag] = [m];
id[flag]=j;//保存前K个相似度的
flag++;
}
}
}
.out.("相似度最近的" + + "个用户是:");
.out.print("近邻用户");
.out.("%25s","相似度");//格式化输出"%25s"是占多少位
.out.("%30sn","推荐产品");
Map map = new ();//存放每件商品的id和期望值,是键值对关系,即一对一
for (int i = 0; i < ; i++) {//按照k值得大小来循环
// 前k个近邻用户的推荐产品
int = id[i];//数组id中的根据相似度大小顺序已经排好,从大到小
int[] items = [];// 获取源数据K个邻近用户的所有评分
str = "";
for (int j = 0; j < ; j++) {//循环每件商品,如果相邻用户对某件商品的评分不为0,而目标用户的评分为0,该商品就为推荐商品
if ((items[j] != 0) && ( - 1 == 0)){
str += " " + (j + 1);//将推荐商品的id保存在一个字符串中,可以直接输出
//此时,可以通过循环计算某一件推荐商品的评分用户的相似度期望
//开始计算期望,将相同商品的相似度相加,并保存在map集合中
if(map.(j + 1)){//如果一件商品的值,已经保存在map集合的键中(键是唯一的协同过滤推荐算法java,即不会和其他的数值一样),那么键对应的值,就会改变,加上该商品不用用户的相似度
d = map.get(j+1);
d+=[i];
map.put(j+1,d);//修改map中的值
}else{
map.put(j+1, [i]);//如果没有保存一件商品的id,那么开始保存
}
}
}
.out.print(id[i] + 1);
.out.("st" ,.("%.2f",[i]*100)+"%");//输出的同时格式化数据
.out.(str);//输出每个用户的推荐商品
}
//选择最好的推荐商品,期望加权
//循环map集合的键
Map map2 = new (); //保存商品id和加权期望,因为还要对加权期望排序,要和商品id对应
s1 = 0;
s2 = 0;
Set set = map.();//获取map集合中的所有键,输出是一个set集合
for(int key : set){//循环map中的所有键
for (int i = 0; i < ; i++) {
int score = [id[i]][key-1];//map中的键是商品id,i是,获取评分
s1+=score*map.get(key);
s2+=score;
}
map2.put(key, s1/s2);//保存加权期望值,和商品id对应
}
[] arr = map2.().();//获取map2中所有的值,也就是每件商品的加权期望
.sort(arr);//升序排列,调用系统数据包中的函数,自动排列数组
set = map2.();//获取商品id
int max=0;//最佳推荐项目id
for(int key : set){//循环商品id,根据最大的加权期望协同过滤推荐算法java,找到商品id
if(map2.get(key)==arr[arr.-1]){
max = key;
break;
}
}
.out.("最值得推荐的商品是:"+max);
// 误差率
int[][] test = new ().(TEST); // 462个用户的实际评分
[][] = new rix()
.rix();//获取任意两行之间的相似度矩阵
[][] = new ().(,);
[] mae = new ().(, test);
Mae = 0.0, MAE = 0.0;//平均绝对误差协同过滤推荐算法java 基于用户的协同过滤推荐算法实现原理及实现代码,通过两大组数据的相似度矩阵对比而来
for (int k = 0; k < mae.; k++) {
Mae += mae[k];
}
MAE = Mae / ;
.out.("MAE=:" + MAE);
}
}
3、运行结果
第二种,使用 api接口实现
是一个算法包,实现了很多协同过滤推荐算法接口,本文只讲解调用过程,后期详解。接口调用实现如下:
1、.java文件,算法实现主方法
com.;
java.util.List;
org.mon.;
org...cf.taste.eval.;
org...cf.taste.eval.;
org..mon.r;
org...cf.taste.impl.eval.uator;
org...cf.taste.impl..hood;
org...cf.taste.impl..;
org...cf.taste.impl..;
org...cf.taste.model.;
org...cf.taste..;
org...cf.taste..;
org...cf.taste..;
org...cf.taste..;
mon.;
mon.;
/**
* 协同过滤算法主方法(使用的协同过滤推荐算法)
*/
class {
void main([] args) {
(10, 1);
();
}
/**
* 基于用户的协同过滤算法
* @param knn 近邻数量
* @param 目标用户id
* @
*/
void (int knn,int ){
.();
;
try {
model = .model;//获取评分数据
= new (model);//使用余弦相似度计算方法
r = model.();
while(.()){
long id = .next();
sim = .(, id);
.out.("目标用户:"++" 与用户:"+id+" 相似度="+sim);
}
//定义最近邻对象
= new hood(knn, , model);
long[] = .();
.out.("最近邻:");
for(long l:){
.out.print(l+" ");
}
.out.("");
//定义推荐器
= new (model, , );
//进行推荐
List = .(, .);
for( ri:){//循环得到推荐项目详细信息
int = (int) ri.();//推荐电影id
float score = ri.();//预测评分
.out.("推荐电影:"++",预测评分:"+score);
}
} catch ( e) {
e.();
}
}
/**
* 计算MAE
*/
void () {
//这个是产生唯一的种子使得在划分训练和测试数据的时候具有唯一性=
.();
//推荐评估,使用平均差值
= new uator();
//定义推荐器
= new () {
( )
{
//采用余弦相似度算法
= new (
);
//定义最近邻
= new hood(10,
, );
//返回推荐
new (, ,
);
}
};
//1.0表示待评估的数据集与总数据集的占比,1.0表示100%。 表示训练数据集在评估数据集的占比
score = .(, null, .model, 0.7f, 1.0);
.out.("基于用户的协同过滤算法MAE="+score);
}
}
2、.java文件,常量文件
mon;
java.io.File;
java.io.;
org...cf.taste.impl.model.file.;
org...cf.taste.model.;
/**
* 数据常量类
*/
class {
//数据文件目录
= "D:////";
//评分数据文件名
= "u1.base";
//用户数据文件名
= "u.user";
//电影数据文件名
= "u.item";
//数据源
model = null;
//推荐个数
int = 10;
{
try {
model = new (new File(+));//实例化数据源
} catch ( e) {
e.();
}
}
}
3、运行结果
目标用户:1 与用户:1 相似度=1.0
目标用户:1 与用户:2 相似度=0.97753
目标用户:1 与用户:3 相似度=0.
目标用户:1 与用户:4 相似度=0.
目标用户:1 与用户:5 相似度=0.89484
目标用户:1 与用户:6 相似度=0.95864
目标用户:1 与用户:7 相似度=0.95611
目标用户:1 与用户:8 相似度=0.
目标用户:1 与用户:9 相似度=1.0
目标用户:1 与用户:10 相似度=0.95743
目标用户:1 与用户:11 相似度=0.92476
目标用户:1 与用户:12 相似度=0.97537
目标用户:1 与用户:13 相似度=0.
目标用户:1 与用户:14 相似度=0.
目标用户:1 与用户:15 相似度=0.92858
目标用户:1 与用户:16 相似度=0.
目标用户:1 与用户:17 相似度=0.
目标用户:1 与用户:18 相似度=0.94785
目标用户:1 与用户:19 相似度=0.6809
目标用户:1 与用户:20 相似度=0.
目标用户:1 与用户:21 相似度=0.96668
目标用户:1 与用户:22 相似度=0.
目标用户:1 与用户:23 相似度=0.
目标用户:1 与用户:24 相似度=0.
目标用户:1 与用户:25 相似度=0.96042
目标用户:1 与用户:26 相似度=0.94628
目标用户:1 与用户:27 相似度=0.94926
目标用户:1 与用户:28 相似度=0.96913
……
目标用户:1 与用户:943 相似度=0.
最近邻:
9 29 35 36 47 93 107 139 140 143
推荐电影:303,预测评分:4.
推荐电影:333,预测评分:4.
推荐电影:307,预测评分:4.0
推荐电影:312,预测评分:4.0
推荐电影:300,预测评分:4.0
推荐电影:302,预测评分:3.5
推荐电影:242,预测评分:3.5
推荐电影:321,预测评分:3.5
推荐电影:288,预测评分:3.5
推荐电影:304,预测评分:3.5
基于用户的协同过滤算法MAE=0.
欢迎留言、私信交流或关注博客