2022-10-31-基于用户的协同过滤推荐算法实现+MAE+RMSE的求解+项目代码+运行结果图(二)

简介: 2022-10-31-基于用户的协同过滤推荐算法实现+MAE+RMSE的求解+项目代码+运行结果图

GetScore

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 < tempSimilarity.length; h++) {
                for (int j = 0; j < tempSimilarity.length - 1 - h; j++) {
                    if (tempSimilarity[j] < tempSimilarity[j + 1]) { //如果后一个数小于前一个数交换
                        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<Integer, Double> map = new HashMap<Integer, Double>();//存放每件商品的id和商品评分*相似度
            Map<Integer, Double> map2 = new HashMap<Integer, Double>();//存放每件商品的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<Integer> set = map.keySet();//循环所有推荐商品
            for (Integer key : set) {
                matrix[i][key] = map.get(key) / map2.get(key);
            }
        }
        return matrix;
    }
}

PearsonCorrelation

import java.util.List;
/**
 * 余弦算法计算相似度
 *
 * @author line
 */
public class PearsonCorrelation implements Base {
// 通过余弦求相邻值,对比两行数据,方法有很多,列举的是余弦方法,也可用皮尔森方法
    public double pearsonCorrelation(List<Integer> a, List<Integer> 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;//相似度值
    }
}

ProduceMAE

/**
 * 计算MSE平均绝对误差
 * @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<TESTROWCOUNT ;i++ ) {
            double sum_fencha= 0.0;
            int num=0;
            for(int j=0;j<PREFROWCOUNT;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;
    }
} 

ProduceMSE

import static java.lang.Math.sqrt;
/**
 * 计算MSE平均绝对误差
 * @author line
 *
 */
public class ProduceMSE implements Base{
    //求误差
    //该函数用于计算均方根误差,m为预测数据,test为真实数据
    //          RMSE : 浮点型
    //        均方根误差.
    public double produceMSE(double[][] m,int[][]test){
        //定义一个变量用于存储所有样本的平方误差之和
        double  the_sum_of_error = 0;
        double []mm=new double[TESTROWCOUNT ];
        for(int i=0;i<TESTROWCOUNT ;i++ ) {
            for(int j=0;j<test[i].length;j++){
                the_sum_of_error += ((double) test[i][j]-m[i][j])*((double) test[i][j]-m[i][j]);
            }
        }
        double RMSE = sqrt(the_sum_of_error/TESTROWCOUNT);
        return RMSE;
    }
} 

ProduceSimilarityMatrix

/**
 * 产生相似矩阵,通过一个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行和其他行对比
    }
} 

ReadFile

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;
    }
} 

运行结果图

RMSE计算过程示意图

运行结果示意图

数据集

MovieLens数据集

MovieLens数据集包含多个用户对多部电影的评级数据,也包括电影元数据信息和用户属性信息。

下载地址

http://files.grouplens.org/datasets/movielens/

介绍

下面以ml-100k数据集为例进行介绍:

最主要用的是u.data(评分) | u.item(电影信息) | u.user(用户信息)

下载之后各文件具体含义如下:
各文件含义如下:

allbut.pl --生成训练和测试集的脚本,其中除了n个用户评分之外,所有训练和测试集都在训练数据中。
mku.sh --从u.data数据集生成的所有用户的shell脚本。
u.data – 由943个用户对1682个电影的10000条评分组成。每个用户至少评分20部电影。用户和电影从1号开始连续编号。数据是随机排序的。
标签分隔列表:user id | item id | rating | timestamp
u.genre --类型列表。
u.info --u.data数据集中的用户数,电影数和评分数。
u.item --电影信息。
标签分隔列表:movie id | movie title | release date | video release date | IMDb URL | unknown | Action | Adventure | Animation | Children’s | Comedy | Crime | Documentary | Drama | Fantasy | Film-Noir | Horror | Musical | Mystery | Romance | Sci-Fi | Thriller | War | Western
最后19个字段是流派,1表示电影是该类型,0表示不是;电影可以同时使用几种流派。
电影id和u.data数据集中的id是一致的。
u.occupation --职业列表。
u.user --用户的人口统计信息。
标签分隔列表:user id | age | gender | occupation | zip code
用户id和u.data数据集中的id是一致的。
u1.base --数据集u1.base / u1.test到u5.base / u5.test都是将u.data数据集按照80% / 20%的比例分割的训练集和测试集。
u1.test u1,…,u5有互不相交的测试集;如果是5次交叉验证,那么你可以在每个训练和测试集中重复实验,平均结果。
u2.base 这些数据集可以通过mku.sh从u.data生成
u2.test
u3.base
u3.test
u4.base
u4.test
u5.base
u5.test
ua.base --数据集ua.base, ua.test, ub.base, ub.test将u.data数据集分为训练集和测试集,每个用户在测试集中具有10个评分。
ua.test ua.test和ub.test是不相交的。这些数据集可以通过mku.sh从u.data生成
目录
相关文章
|
6天前
|
存储 Rust 监控
Rust代码编写高性能屏幕监控软件的核心算法
本文介绍了使用Rust编写的高性能屏幕监控软件的实现方法。核心算法包括:1) 使用`image`和`winit`库捕获并转换屏幕图像;2) 对图像进行处理,检测特定对象或活动;3) 利用Rust的并发性并行处理多个帧以提高效率;4) 提取数据后,通过`reqwest`库自动提交到网站进行分析或存储。通过结合Rust的高性能和丰富的库,可构建满足各种需求的高效屏幕监控工具。
91 5
|
6天前
|
机器学习/深度学习 算法 API
【Paddle】PCA线性代数基础 + 领域应用:人脸识别算法(1.1w字超详细:附公式、代码)
【Paddle】PCA线性代数基础 + 领域应用:人脸识别算法(1.1w字超详细:附公式、代码)
9 0
|
6天前
|
算法 关系型数据库 C语言
卡尔曼滤波简介+ 算法实现代码(转)
卡尔曼滤波简介+ 算法实现代码(转)
20 4
|
6天前
|
运维 算法
基于改进遗传算法的配电网故障定位(matlab代码)
基于改进遗传算法的配电网故障定位(matlab代码)
|
6天前
|
算法 调度
基于多目标粒子群算法冷热电联供综合能源系统运行优化(matlab代码)
基于多目标粒子群算法冷热电联供综合能源系统运行优化(matlab代码)
|
6天前
|
算法
【免费】基于ADMM算法的多微网电能交互分布式运行策略(matlab代码)
【免费】基于ADMM算法的多微网电能交互分布式运行策略(matlab代码)
|
6天前
|
算法
基于蜣螂优化算法DBO的VMD-KELM光伏发电功率预测(matlab代码+可提供讲解)
基于蜣螂优化算法DBO的VMD-KELM光伏发电功率预测(matlab代码+可提供讲解)
|
6天前
|
算法
基于白鲸优化算法BWO的VMD-KELM光伏发电功率预测(matlab代码+可提供讲解)
基于白鲸优化算法BWO的VMD-KELM光伏发电功率预测(matlab代码+可提供讲解)
|
6天前
|
算法 调度 决策智能
基于元模型优化算法的主从博弈多虚拟电厂动态定价和能量管理(matlab代码)
基于元模型优化算法的主从博弈多虚拟电厂动态定价和能量管理(matlab代码)
|
6天前
|
机器学习/深度学习 算法 数据挖掘
基于改进ISODATA算法的负荷场景曲线聚类(matlab代码)
基于改进ISODATA算法的负荷场景曲线聚类(matlab代码)