快捷搜索:  汽车  科技

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

协同过滤推荐算法问题,基于用户的协同过滤推荐算法实现原理及实现代码图1 余弦算法公式表1 用户-项目评分矩阵注:用户A对项目1的评分是1分,用户A对项目2没有评分。2、根据用户-项目评分矩阵计算用户之间的相似度。计算相似度常用的方法有余弦算法、修正余弦算法、皮尔森算法等等(后期我们会将相似度算法展开讲解,这里以余弦算法为例)。余弦算法公式如图1所示:

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

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语言开发,单机版(本地测试),数据集使用movielens的ml-100k,943*1682,80000条数据。

第一种,自定义实现:

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

协同过滤推荐算法问题,基于用户的协同过滤推荐算法实现原理及实现代码(1)

图3 项目目录

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

/**

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

*/

public class Application implements Base {

public static void main(String[] args) {

// 输入userId,并获取

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

Scanner scanner = new Scanner(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("%s" "相似度");//格式化输出"%s"是占多少位

System.out.printf("0s\n" "推荐产品");

Map<Integer Double> map = new HashMap<Integer Double>();//存放每件商品的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("s\t" String.format("%.2f" similarity[i]*100) "%");//输出的同时格式化数据

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

}

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

//循环map集合的键

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

double s1 = 0;

double s2 = 0;

Set<Integer> 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、运行结果

协同过滤推荐算法问题,基于用户的协同过滤推荐算法实现原理及实现代码(2)

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

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

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

package com.baseduser;


import java.utilst;

import org.apache.mahout.cf.tastemon.TasteException;

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

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

import org.apache.mahout.cf.taste.implmon.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.mahoutmon.RandomUtils;

import 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<RecommendedItem> 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 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 与用户: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


欢迎留言、私信交流或关注博客blog.csdn/u011291472

猜您喜欢: