协同过滤推荐算法java_基于用户的协同过滤推荐算法 实现原理及实现代码

基于用户的协同过滤推荐算法

实现原理及实现代码

基于用户的协同过滤推荐算法实现原理

传统的基于用户(User-Based)的协同过滤推荐算法实现原理分四个步骤:

1、根据用户历史行为信息构建用户-项目评分矩阵,用户历史行为信息包括项目评分、浏览历史、收藏历史、喜好标签等,本文以单一的项目评分为例,后期介绍其他行为信息和混合行为信息,用户-项目评分矩阵如表1所示:

项目1

项目2

项目3

用户A

1

0

5

用户B

3

4

0

用户C

0

3

2

表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、结论。以上步骤是最简单,最传统的基于用户的协同过滤推荐算法的实现原理,但是在实现过程中还是有很多注意细节。

基于用户的协同过滤推荐算法实现代码

本文我们介绍两种实现代码,都是java语言开发,单机版(本地测试),数据集使用movielens的ml-100k,943*1682,80000条数据。

第一种,自定义实现:

1、项目目录,如图3所示:

图3 项目目录

2、Application.java文件,算法主运行方法

import java.util.Arrays;

import java.util.HashMap;

import java.util.Map;

import java.util.Scanner;

import java.util.Set;

/**

* 协同过滤推荐算法运行主方法

* @author line

*

*/

public class Application implements Base {

public static void main(String[] args) {

// 输入userId,并获取

System.out.println("请输入一个用户Id(1、2、3……943)");

Scanner scanner = new Scanner(http://System.in);

//获取得到输入的userId

int userId = scanner.nextInt();

// 从文件中读取数据

int[][] user_movie_base = new int[PREFROWCOUNT][COLUMNCOUNT];

//读取文件中的数据

user_movie_base = new ReadFile().readFile(BASE);

//产生相似度矩阵

double[] similarityMatrix = new ProduceSimilarityMatrix().produceSimilarityMatrix(user_movie_base, userId);

// 知道每个用户之间的相似度值之后,开始获取每隔相似值对应的userId,然后和相似值关联,再根据相似值排序,即得到相似爱好的userId,然后再输出相似推荐的商品

int[] id = new int[KNEIGHBOUR];//存放K个最近邻userId

//产生一个临时相似度矩阵变量,是为了相似度排序时和userid对应

double[] tempSimilarity = new double[similarityMatrix.length];

for (int j = 0; j < tempSimilarity.length; j++) {

tempSimilarity[j] = similarityMatrix[j];

}

Arrays.sort(tempSimilarity);//排序,升序

int flag = 0;//临时变量

double[] similarity = new double[KNEIGHBOUR];//保存前K个相似度,从大到小

for (int m = tempSimilarity.length - 1; m >= tempSimilarity.length - KNEIGHBOUR; m--) {

for(int j = 0; j < similarityMatrix.length; j++) {

if (similarityMatrix[j] == tempSimilarity[m] && similarityMatrix[j] != 0.0){

similarity[flag] = tempSimilarity[m];

id[flag]=j;//保存前K个相似度的userid

flag++;

}

}

}

System.out.println("相似度最近的" + KNEIGHBOUR + "个用户是:");

System.out.print("近邻用户");

System.out.printf("%25s","相似度");//格式化输出"%25s"是占多少位

System.out.printf("%30s\n","推荐产品");

Map map = new HashMap();//存放每件商品的id和期望值,是键值对关系,即一对一

for (int i = 0; i < KNEIGHBOUR; i++) {//按照k值得大小来循环

// 前k个近邻用户的推荐产品

int user_id = id[i];//数组id中的userid根据相似度大小顺序已经排好,从大到小

int[] items = user_movie_base[user_id];// 获取源数据K个邻近用户userid的所有评分

String str = "";

for (int j = 0; j < COLUMNCOUNT; j++) {//循环每件商品,如果相邻用户对某件商品的评分不为0,而目标用户的评分为0,该商品就为推荐商品

if ((items[j] != 0) && (user_movie_base[userId - 1][j] == 0)){

str += " " + (j + 1);//将推荐商品的id保存在一个字符串中,可以直接输出

//此时,可以通过循环计算某一件推荐商品的评分用户的相似度期望

//开始计算期望,将相同商品的相似度相加,并保存在map集合中

if(map.containsKey(j + 1)){//如果一件商品的值,已经保存在map集合的键中(键是唯一的,即不会和其他的数值一样),那么键对应的值,就会改变,加上该商品不用用户的相似度

double d = map.get(j+1);

d+=similarity[i];

map.put(j+1,d);//修改map中的值

}else{

map.put(j+1, similarity[i]);//如果没有保存一件商品的id,那么开始保存

}

}

}

System.out.print(id[i] + 1);

System.out.printf("%16s\t" ,String.format("%.2f",similarity[i]*100)+"%");//输出的同时格式化数据

System.out.println(str);//输出每个用户的推荐商品

}

//选择最好的推荐商品,期望加权

//循环map集合的键

Map map2 = new HashMap(); //保存商品id和加权期望,因为还要对加权期望排序,要和商品id对应

double s1 = 0;

double s2 = 0;

Set set = map.keySet();//获取map集合中的所有键,输出是一个set集合

for(int key : set){//循环map中的所有键

for (int i = 0; i < KNEIGHBOUR; i++) {

int score = user_movie_base[id[i]][key-1];//map中的键是商品id,i是userid,获取评分

s1+=score*map.get(key);

s2+=score;

}

map2.put(key, s1/s2);//保存加权期望值,和商品id对应

}

Object[] arr = map2.values().toArray();//获取map2中所有的值,也就是每件商品的加权期望

Arrays.sort(arr);//升序排列,调用系统数据包中的函数,自动排列数组

set = map2.keySet();//获取商品id

int max=0;//最佳推荐项目id

for(int key : set){//循环商品id,根据最大的加权期望,找到商品id

if(map2.get(key)==arr[arr.length-1]){

max = key;

break;

}

}

System.out.println("最值得推荐的商品是:"+max);

// 误差率

int[][] test = new ReadFile().readFile(TEST); // 462个用户的实际评分

double[][] similarityMatrix2 = new ProduceSimilarityMatrix()

.produceSimilarityMatrix(user_movie_base);//获取任意两行之间的相似度矩阵

double[][] matrix = new GetScore().getScore(user_movie_base,similarityMatrix2);

double[] mae = new ProduceMAE().produceMAE(matrix, test);

double Mae = 0.0, MAE = 0.0;//平均绝对误差,通过两大组数据的相似度矩阵对比而来

for (int k = 0; k < mae.length; k++) {

Mae += mae[k];

}

MAE = Mae / TESTROWCOUNT;

System.out.println("MAE=:" + MAE);

}

}

3、Base.java文件,基础常量接口

/**

* 基础静态文件数据

* @author line

*

*/

public interface Base {

public static final int KNEIGHBOUR = 10; //number of neighbors最近邻个数

public static final int COLUMNCOUNT = 1682; //number of items 项目总数

public static final int PREFROWCOUNT = 943; //number of users in base训练集上的用户数目

public static final int TESTROWCOUNT = 462; //number of users in test测试集上的用户数目

public static final String BASE = "u1.base";//训练集

public static final int BASE_LINE = 80000;//base数据集的行数

public static final String TEST = "u1.test";//测试集

public static final int TEST_LINE = 20000;//test数据集的行数

public static final String BASE_GENRE = "u.user";//用户属性集

public static final String BASE_ITEMS_GENRE = "u.item";//用户属性集

public static final int ITEMS_GENRE_LINE = 19;//test数据集的行数

}

4、ComputeSimilarity.java文件,比较相似度

import java.util.ArrayList;

import java.util.List;

/**

* 从两行数据中,获取需要对比的要求数据

* @author line

*

*/

public class ComputeSimilarity {

public double computeSimilarity(int[] item1,int[] item2) {

List list1 = new ArrayList();//因为不知道两行userid的评分是否有效即都不为0,所以定义集合来储存不知道的有效评分

List list2 = new ArrayList();

for (int i = 0; i < item1.length; i++) {

if(item1[i] != 0 || item2[i] !=0) {//如果相同列上有0就舍去

list1.add(new Integer(item1[i]));//因为合格数据个数不确定,所以用集合表示

list2.add(new Integer(item2[i]));

}

}

return new PearsonCorrelation().pearsonCorrelation(list1,list2);//返回相似度值

}

}

5、GetScore.java文件,获取预测评分

import java.util.ArrayList;

import java.util.Arrays;

import java.util.HashMap;

import java.util.List;

import java.util.Map;

import java.util.Set;

public class GetScore implements Base {

//方法参数,一个是源数据,一个是通过源数据得到的相似度矩阵

//得到预测评分矩阵,先循环行userid,得到每一个userid的K个近邻用户和相似度,再得到目标用户的预测项目

public double[][] getScore(int[][] user_movie_base,double[][] combineMatrix ){

double[][] matrix = new double[PREFROWCOUNT][COLUMNCOUNT];//保存每个用户对评分为0的项目的预测值

//循环userid

for (int i = 0; i < PREFROWCOUNT; i++) {//KNEIGHBOUR

//得到每一个userid的K个邻近相似度极其userid

int[] id = new int[KNEIGHBOUR];//存放K个最近邻userId

double[] tempSimilarity = new double[combineMatrix[i].length];//产生一个临时相似度矩阵变量,是为了相似度排序时和userid对应

for (int j = 0; j < tempSimilarity.length; j++) {

tempSimilarity[j] = combineMatrix[i][j];

}

//Arrays.sort(tempSimilarity);//排序,升序

//int flag = 0;//临时变量

double[] similarity = new double[KNEIGHBOUR];//保存前K个相似度,从大到小

// for (int m = tempSimilarity.length - 1; m >= tempSimilarity.length - KNEIGHBOUR; m--) {

// for(int j = 0; j < combineMatrix[i].length; j++) {

// if (combineMatrix[i][j] == tempSimilarity[m] && combineMatrix[i][j] != 0.0){

// similarity[flag] = tempSimilarity[m];

// id[flag]=j;//保存前K个相似度的userid

// flag++;

// }

// }

// }

int[] ids = new int[PREFROWCOUNT];//存放K个邻近项目id

for (int h = 0; h < PREFROWCOUNT; h++) {

ids[h] = h;

}

for(int h=0;h

for(int j=0;j

if(tempSimilarity[j]

double tmp=tempSimilarity[j];

tempSimilarity[j]=tempSimilarity[j+1];

tempSimilarity[j+1]=tmp;

int temp = ids[j];

ids[j] = ids[j+1];

ids[j+1] = temp;

}

}

}

for (int h = 0;h < KNEIGHBOUR; h++) {

similarity[h]=tempSimilarity[h];

}

for (int h = 0; h < KNEIGHBOUR; h++) {

id[h]=ids[h];

}

//以上代码已经得到一个目标用户的K个相似度userid和相似度结束,并且已经排好顺序,分别是:数组id,和数组similarity

//开始计算一个目标用户的推荐产品的预测评分,方法,K个邻近用户的相同商品的加权平均数

Map map = new HashMap();//存放每件商品的id和商品评分*相似度

Map map2 = new HashMap();//存放每件商品的id和相似度之和

for (int k = 0; k < KNEIGHBOUR; k++) {//按照k值得大小来循环

// 前k个近邻用户的推荐产品

int user_id = id[k];//数组id中的userid根据相似度大小顺序已经排好,从大到小

int[] items = user_movie_base[user_id];// 获取源数据K个邻近用户userid的所有评分

for (int j = 0; j < COLUMNCOUNT; j++) {//循环每件商品,如果相邻用户对某件商品的评分不为0,而目标用户的评分为0,该商品就为推荐商品

if ((items[j] != 0) && (user_movie_base[i][j] == 0)){

if(map.containsKey(j)){//如果一件商品的值,已经保存在map集合的键中(键是唯一的,即不会和其他的数值一样),那么键对应的值,就会改变,加上该商品不用用户的相似度

double d = map.get(j);

d+=similarity[k]*items[j];

map.put(j,d);//修改map中的值

double dd = map2.get(j);

dd+=similarity[k];

map2.put(j, dd);

}else{

map.put(j, similarity[k]*items[j]);//如果没有保存一件商品的id,那么开始保存

map2.put(j, similarity[k]);

}

}

}

}

Set set = map.keySet();//循环所有推荐商品

for(Integer key:set){

matrix[i][key] = map.get(key)/map2.get(key);

}

}

return matrix;

}

}

6、PearsonCorrelation.java文件,余弦算法/皮尔森算法

import java.util.List;

/**

* 余弦算法计算相似度

* @author line

*

*/

public class PearsonCorrelation implements Base {

// 通过余弦求相邻值,对比两行数据,方法有很多,列举的是余弦方法,也可用皮尔森方法

public double pearsonCorrelation(List a, List b) {// 返回某两行的相似度值

double sum1 = 0;

double sum2 = 0;

Object[] a2 = a.toArray();

Object[] b2 = b.toArray();

int aimcha;

int usercha;

double wei = 0;

for (int j = 0; j < a.size(); j++) {

aimcha = (Integer) a2[j];

usercha = (Integer) b2[j];

sum1 += aimcha * aimcha;

sum2 += usercha * usercha;

}

for (int i = 0; i < a.size(); i++) {

double light = 0;

double right = 0;

aimcha = (Integer) a2[i];

usercha = (Integer) b2[i];

light = aimcha / Math.sqrt(sum1);

right = usercha / Math.sqrt(sum2);

wei += light * right;

}

return wei;//相似度值

}

}

7、ProduceMAE.java文件,计算平均绝对误差MAE

/**

* 计算MAE平均绝对误差

* @author line

*

*/

public class ProduceMAE implements Base{

//求误差

public double[] produceMAE(double[][] m,int[][]test){

double mae= 0.0;

double []mm=new double[TESTROWCOUNT ];

for(int i=0;i

double sum_fencha= 0.0;

int num=0;

for(int j=0;j

if(test[i][j]!=0&& m[i][j]!=0){

sum_fencha+=Math.abs(m[i][j]-(double)test[i][j]);//相差取绝对值

num++;

}

}if (num==0) mae=0;else mae= sum_fencha/num;

mm[i]=mae;

}

return mm;

}

}

8、ProduceSimilarityMatrix.java文件,产生相似度矩阵

/**

* 产生相似矩阵,通过一个userId找其最近邻userId喜欢的产品,则相似度矩阵为一行n列矩阵,

* 若是全部比较一个矩阵所有userId的相关度产生一个n行n列矩阵

* @author line

*

*/

public class ProduceSimilarityMatrix implements Base{

//在计算MAE会用到

public double[][] produceSimilarityMatrix(int[][] preference) {

double[][] similarityMatrix = new double[PREFROWCOUNT][PREFROWCOUNT];//行和列都是所有的用户,因为是每一行和每一行相比,所以得到的相似矩阵为正方形

for (int i = 0; i < PREFROWCOUNT; i++) {

for (int j = 0; j < PREFROWCOUNT; j++) {

if (i == j) {

continue;

}

//数据是两行之间对比,其实只需要填满相似度矩阵的左下方或者右上方即可(减少重复运算)

similarityMatrix[i][j] =

new ComputeSimilarity().computeSimilarity(preference[i], preference[j]);//参数是从第一行开始,和其他每一行比较相似度

}

}

return similarityMatrix;//返回相似度矩阵

}

//计算某个userId的相似度矩阵,用户之间的相似度是每个用户的每件商品评分的相似度,也就是说相似度矩阵是行是用户列也是用户,是正方形矩阵,对角线上的值都为1

//参数i是输入的userid

public double[] produceSimilarityMatrix(int[][] preference,int i) {

double[] similarityMatrix = new double[PREFROWCOUNT];//定义一个相似度矩阵,行和列都是所有的用户,因为是每一行和每一行相比,所以得到的相似矩阵为正方形

for (int j = 0; j < PREFROWCOUNT; j++) {//循环和其他userId对比其所有商品

if(j==(i-1)){//不比较同行,i-1是因为数组索引比userid小1

continue;//跳出循环,继续下一次循环

}

similarityMatrix[j] =

new ComputeSimilarity().computeSimilarity(preference[i-1], preference[j]);//参数是从第一行开始,和其他每一行比较相似度

}

return similarityMatrix;//返回相似度矩阵,只有在userid-1行有数据,其他行列数据都为0,因为只是userid-1行和其他行对比

}

//根据性别属性,产生用户性别属性相似度

public double[] produceSimilarityMatrixGener(int[] preference,int userId) {

double[] similarityMatrix = new double[PREFROWCOUNT];//定义一个相似度矩阵,行和列都是所有的用户,因为是每一行和每一行相比,所以得到的相似矩阵为正方形

for (int j = 0; j < PREFROWCOUNT; j++) {//循环和其他userId对比其所有商品

if(j==(userId-1)){//不比较同行,i-1是因为数组索引比userid小1

continue;//跳出循环,继续下一次循环

}

if(preference[j]==preference[userId-1])

similarityMatrix[j] = 1;

else

similarityMatrix[j] = 0;

}

return similarityMatrix;//返回相似度矩阵,只有在userid-1行有数据,其他行列数据都为0,因为只是userid-1行和其他行对比

}

//基于项目

public double[] produceSimilarityMatrixItems(int[][] preference,int i) {

double[] similarityMatrix = new double[COLUMNCOUNT];

for (int j = 0; j < COLUMNCOUNT; j++) {

if(j==(i-1)){//不比较同行

continue;//跳出循环,继续下一次循环

}

similarityMatrix[j] =

new ComputeSimilarity().computeSimilarity(preference[i-1], preference[j]);//参数是从第一行开始,和其他每一行比较相似度

}

return similarityMatrix;//返回相似度矩阵,只有在userid-1行有数据,其他行列数据都为0,因为只是userid-1行和其他行对比

}

}

9、ReadFile.java文件,读取movielens数据集数据

import java.io.BufferedReader;

import java.io.File;

import java.io.FileReader;

/**

* 读取数据集中的数据

* @author line

*

*/

public class ReadFile implements Base {

//从文件中读取数据,以“ ”划分

public int[][] readFile( String fileName) {

int[][] user_movie = new int[PREFROWCOUNT][COLUMNCOUNT];//存放数据

try {

File file = new File(fileName);

FileReader fr = new FileReader(file);

BufferedReader br = new BufferedReader(fr);

String line = "";

while (br.ready()) {

line = br.readLine();//按行获取数据

String[] data = line.split("\t");//以“TAB”符来分割每行的四个数据数据获取userid,score,product

int[] ddd = new int[4];

for (int j = 0; j < data.length; j++) {

ddd[j] = Integer.parseInt(data[j]);

}

user_movie[ddd[0] - 1][ddd[1] - 1] = ddd[2];//因为数组的索引是从0开始,而商品和用户id是从1开始,故减去1

}

} catch (Exception ex) {

ex.printStackTrace();//如果方法出现错误,会被抓住,在控制台输出错误原因

}

return user_movie;

}

//从文件中读取数据,以“|”划分

public int[] readFileGener(String fileName) {

int[] user_genre_base = new int[PREFROWCOUNT];//存放数据

try {

File file = new File(fileName);

FileReader fr = new FileReader(file);

BufferedReader br = new BufferedReader(fr);

String line = "";

int i=0;

while (br.ready()) {

line = br.readLine();//按行获取数据

String[] data = line.split("\\|");

if(data[2].equals("M")){//男性设为1

user_genre_base[i]= 1;

}else

user_genre_base[i]= 0;//女性

i++;

}

} catch (Exception ex) {

ex.printStackTrace();//如果方法出现错误,会被抓住,在控制台输出错误原因

}

return user_genre_base;

}

//获取items-user矩阵

public int[][] readFileItems( String fileName) {

int[][] items_movie = new int[COLUMNCOUNT][PREFROWCOUNT];//存放数据

try {

File file = new File(fileName);

FileReader fr = new FileReader(file);

BufferedReader br = new BufferedReader(fr);

String line = "";

while (br.ready()) {//矩阵中循环列

line = br.readLine();

String[] data = line.split("\t");

int itemsId = Integer.parseInt(data[1]);

int userId = Integer.parseInt(data[0]);

items_movie[itemsId-1][userId-1] = Integer.parseInt(data[2]);

}

} catch (Exception ex) {

ex.printStackTrace();//如果方法出现错误,会被抓住,在控制台输出错误原因

}

return items_movie;

}

//获取items-gener矩阵

public int[][] readFileItemsGener( String fileName) {

int[][] items_movie = new int[COLUMNCOUNT][ITEMS_GENRE_LINE];//存放数据

try {

File file = new File(fileName);

FileReader fr = new FileReader(file);

BufferedReader br = new BufferedReader(fr);

String line = "";

while (br.ready()) {//矩阵中循环列

line = br.readLine();

String[] data = line.split("\\|");

int itemsId = Integer.parseInt(data[0]);

int j = 0;

for (int i = data.length-ITEMS_GENRE_LINE; i < data.length; i++) {

items_movie[itemsId-1][j] =Integer.parseInt(data[i]);

j++;

}

}

} catch (Exception ex) {

ex.printStackTrace();//如果方法出现错误,会被抓住,在控制台输出错误原因

}

return items_movie;

}

}

10、运行结果

第二种,使用mahout api接口实现

mahout是一个算法包,实现了很多协同过滤推荐算法接口,本文只讲解调用过程,后期详解mahout。接口调用实现如下:

1、CFBasedUser.java文件,算法实现主方法

package com.baseduser;

import java.util.List;

import org.apache.mahout.cf.taste.common.TasteException;

import org.apache.mahout.cf.taste.eval.RecommenderBuilder;

import org.apache.mahout.cf.taste.eval.RecommenderEvaluator;

import org.apache.mahout.cf.taste.impl.common.LongPrimitiveIterator;

import org.apache.mahout.cf.taste.impl.eval.AverageAbsoluteDifferenceRecommenderEvaluator;

import org.apache.mahout.cf.taste.impl.neighborhood.NearestNUserNeighborhood;

import org.apache.mahout.cf.taste.impl.recommender.GenericUserBasedRecommender;

import org.apache.mahout.cf.taste.impl.similarity.UncenteredCosineSimilarity;

import org.apache.mahout.cf.taste.model.DataModel;

import org.apache.mahout.cf.taste.neighborhood.UserNeighborhood;

import org.apache.mahout.cf.taste.recommender.RecommendedItem;

import org.apache.mahout.cf.taste.recommender.Recommender;

import org.apache.mahout.cf.taste.similarity.UserSimilarity;

import org.apache.mahout.common.RandomUtils;

import com.common.Constant;

/**

* 协同过滤算法主方法(使用mahout的协同过滤推荐算法)

*/

public class CFBasedUser {

public static void main(String[] args) throws Exception {

userCF(10, 1);

getMae();

}

/**

* 基于用户的协同过滤算法

* @param knn 近邻数量

* @param userid 目标用户id

* @return

*/

private static void userCF(int knn,int userid){

RandomUtils.useTestSeed();

UserSimilarity userSimilarity;

try {

DataModel model = Constant.model;//获取评分数据

userSimilarity = new UncenteredCosineSimilarity(model);//使用余弦相似度计算方法

LongPrimitiveIterator iterator = model.getUserIDs();

while(iterator.hasNext()){

long id = iterator.next();

double sim = userSimilarity.userSimilarity(userid, id);

System.out.println("目标用户:"+userid+" 与用户:"+id+" 相似度="+sim);

}

//定义最近邻对象

UserNeighborhood neighborhood = new NearestNUserNeighborhood(knn, userSimilarity, model);

long[] tempKnn = neighborhood.getUserNeighborhood(userid);

System.out.println("最近邻:");

for(long l:tempKnn){

System.out.print(l+" ");

}

System.out.println("");

//定义推荐器

Recommender recommender = new GenericUserBasedRecommender(model, neighborhood, userSimilarity);

//进行推荐

List recommendations = recommender.recommend(userid, Constant.cfCount);

for(RecommendedItem ri:recommendations){//循环得到推荐项目详细信息

int itemid = (int) ri.getItemID();//推荐电影id

float score = ri.getValue();//预测评分

System.out.println("推荐电影:"+itemid+",预测评分:"+score);

}

} catch (TasteException e) {

e.printStackTrace();

}

}

/**

* 计算MAE

*/

private static void getMae() throws Exception{

//这个是产生唯一的种子使得在划分训练和测试数据的时候具有唯一性=

RandomUtils.useTestSeed();

//推荐评估,使用平均差值

RecommenderEvaluator evaluator = new AverageAbsoluteDifferenceRecommenderEvaluator();

//定义推荐器

RecommenderBuilder builder = new RecommenderBuilder() {

public Recommender buildRecommender(DataModel dataModel)

throws TasteException {

//采用余弦相似度算法

UserSimilarity similarity = new UncenteredCosineSimilarity(

dataModel);

//定义最近邻

UserNeighborhood neighborhood = new NearestNUserNeighborhood(10,

similarity, dataModel);

//返回推荐

return new GenericUserBasedRecommender(dataModel, neighborhood,

similarity);

}

};

//1.0表示待评估的数据集与总数据集的占比,1.0表示100%。 trainCount表示训练数据集在评估数据集的占比

double score = evaluator.evaluate(builder, null, Constant.model, 0.7f, 1.0);

System.out.println("基于用户的协同过滤算法MAE="+score);

}

}

2、Constant.java文件,常量文件

package com.common;

import java.io.File;

import java.io.IOException;

import org.apache.mahout.cf.taste.impl.model.file.FileDataModel;

import org.apache.mahout.cf.taste.model.DataModel;

/**

* 数据常量类

*/

public class Constant {

//数据文件目录

public static String realPath = "D://movielens//";

//评分数据文件名

public static String rateData = "u1.base";

//用户数据文件名

public static String userData = "u.user";

//电影数据文件名

public static String movieData = "u.item";

//数据源

public static DataModel model = null;

//推荐个数

public static int cfCount = 10;

static {

try {

model = new FileDataModel(new File(realPath+rateData));//实例化数据源

} catch (IOException e) {

e.printStackTrace();

}

}

}

3、运行结果

目标用户:1 与用户:1 相似度=1.0

目标用户:1 与用户:2 相似度=0.9710491789484753

目标用户:1 与用户:3 相似度=0.9568580574191399

目标用户:1 与用户:4 相似度=0.9999999999999998

目标用户:1 与用户:5 相似度=0.8940685750227884

目标用户:1 与用户:6 相似度=0.9503390016714864

目标用户:1 与用户:7 相似度=0.9561043914210211

目标用户:1 与用户:8 相似度=0.989476145352144

目标用户:1 与用户:9 相似度=1.0

目标用户:1 与用户:10 相似度=0.9574305119597658

目标用户:1 与用户:11 相似度=0.9207809439266476

目标用户:1 与用户:12 相似度=0.9710982658959537

目标用户:1 与用户:13 相似度=0.9438798074485389

目标用户:1 与用户:14 相似度=0.9526944450625402

目标用户:1 与用户:15 相似度=0.9208873346422858

目标用户:1 与用户:16 相似度=0.96542997738676

目标用户:1 与用户:17 相似度=0.9486452982788919

目标用户:1 与用户:18 相似度=0.9413434984843785

目标用户:1 与用户:19 相似度=0.680336051416609

目标用户:1 与用户:20 相似度=0.9654364352708692

目标用户:1 与用户:21 相似度=0.9666815847074658

目标用户:1 与用户:22 相似度=0.9595548606619673

目标用户:1 与用户:23 相似度=0.9485750073525052

目标用户:1 与用户:24 相似度=0.9343429895883113

目标用户:1 与用户:25 相似度=0.9605057528447042

目标用户:1 与用户:26 相似度=0.9401021481358628

目标用户:1 与用户:27 相似度=0.9491936172364526

目标用户:1 与用户:28 相似度=0.9691123561673313

目标用户:1 与用户:29 相似度=1.0

目标用户:1 与用户:30 相似度=0.944157692026077

目标用户:1 与用户:31 相似度=0.9377955495619765

目标用户:1 与用户:32 相似度=0.971914544658059

目标用户:1 与用户:33 相似度=0.9899494936611665

目标用户:1 与用户:34 相似度=NaN

目标用户:1 与用户:35 相似度=1.0

目标用户:1 与用户:36 相似度=1.0

目标用户:1 与用户:37 相似度=0.9829420139420947

目标用户:1 与用户:38 相似度=0.7088667191498986

目标用户:1 与用户:39 相似度=0.9999999999999998

目标用户:1 与用户:40 相似度=0.8117077033708013

目标用户:1 与用户:41 相似度=0.9802730677999772

目标用户:1 与用户:42 相似度=0.9446894041981059

目标用户:1 与用户:43 相似度=0.9382828952005916

目标用户:1 与用户:44 相似度=0.9764042722915194

……

目标用户:1 与用户:943 相似度=0.9424243561696607

最近邻:

9 29 35 36 47 93 107 139 140 143

推荐电影:303,预测评分:4.3333335

推荐电影:333,预测评分:4.3333335

推荐电影: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.9433980646006359

欢迎留言、私信交流

你可能感兴趣的:(协同过滤推荐算法java)