协同过滤算法基于用户--使用MapReduce框架实现为用户推荐电影

1. 协同过滤算法

因为学习了MapReduce后,老师推荐可以自学协同过滤算法。因此,在网上学习了下,实现了该算法。
本次编写,使用了MapReduce框架、基于用户的关联实现电影推荐。
因为第一次使用代码实现高级算法,编写的可能过于复杂,下面给出实现代码实现的原理和详细注释:

********************************************************************************************************************
 * 代码分析:
 * main函数:
 * 	共执行三个函数:
 * 	1. generateSourceData(locaFilePath) 
 * 		根据已有的用户名、电影名生成用户对电影的随机评分,按照 用户	电影1$评分;电影2$评分;....格式写入本地磁盘
 * 	
 * 	2. putFile2HDFS(locaFilePath)
 * 		将generateSourceData函数写入本地的磁盘文件上传到 HDFS 
 * 
 * 	3. startMapReduce(inputOutputPath)
 * 		启动MapReduce,为用户推荐电影
 * 		MapReduce程序共分为:ColFilterMapper、ColFilterCombiner、ColFilterReducer
 * 		1) ColFilterMapper
 * 			执行次数:行数
 * 			输入:读取HDFS文件,每次按行读入
 * 			输出:key=电影名,value=用户1$评分;用户2$评分.....
 * 			内部功能:
 * 				1. 按照读取的顺序,将用户名赋值给users一维数组,其下标作为用户ID
 * 				2. 将用户的对所有电影的评分写入usersInfo变量(key=用户名,value=map类型,map类型中:key=电影名,value=评分)
 * 
 * 
 * 		2) ColFilterCombiner
 * 			执行次数:Mapper输出的不同key值数量(相同key将合并)
 * 			输入:Mapper的输出
 * 			输出:key=ID&用户 value=电影$评分;电影$评分;...(value值这里并没有实际意义,因为在Reducer并没有使用到)
 * 			内部功能:
 * 				1. 将输入数据的key值即电影名,赋值到一维数组movies
 * 				2. 将各用户对同一部电影的评分做差值,并将 5-该差值 作为用户与用户之间的相似度并写入二维数组(维度:用户数×用户数),
 * 					并且二维数组的下标与用户ID一一对应。该值越大,用户越相似。按照key=电影名,value=各用户之间的
 * 					相似度二维数组的格式put到 userSimilarityMap变量。
 * 				3. 按照用户数量输出(因为如果输出有重复,Reducer最后写入文件会出现重复)
 * 
 *
 * 		3) ColFilterReducer
 * 			输入:Combier的输出:key=ID&用户 value=电影$评分;电影$评分;...
 * 			输出:key=用户名	value=推荐电影列表
 * 			内部功能:
 * 				1. userSimilarityMap变量记录了不同电影下用户与用户之间的相似度值,将其求和,即将userSimilarityMap变量
 * 					的所有value值(二维数组)对应位置求和。得到总的用户相似度二维数组 userSimilarityAll(维度:用户数×用户数)
 * 				2. userSimilarityAll二维数组中记录了用户与用户之间的总相似度,其值越高越相似。根据Combiner传过来的key=ID&用户,
 * 					按照key值中的ID确定userSimilarityAll数组中的行,然后遍历该行,找出相似度值最大的3个用户(修改变量值即可确定
 * 					寻找前几个最相似的用户),符合条件的用户所在位置的列下标值即为用户的ID。根据此ID可通过users一维数组确定用户名。
 * 					根据用户名可通过usersInfo集合确定用户对所有电影的评分。获取到最相似的3各用户的所有评分后,
 * 						按照:电影推荐度 = 用户1对该电影评分×用户与目标用户的总相似度值 
 * 						+ 用户2对该电影评分×用户与目标用户的总相似度值
 * 						+ 用户3对该电影评分×用户与目标用户的总相似度值。
 * 					最后按照以下条件,确定某电影是否推荐给目标用户:
 * 						1) 目标用户对该电影评分为0.0,即为看过该电影
 * 						2) 该电影的推荐值大于所有电影对该用户的推荐度的平均值
 * 
 ***********************************************************************************************************************

2. 运行结果示例

[ ]内数值为电影对该用户的推荐值。
协同过滤算法基于用户--使用MapReduce框架实现为用户推荐电影_第1张图片

3.完整项目

由于代码过长,博客中就不粘贴了,代码可见GitHub。

GitHub:https://github.com/GYT0313/CollaborativeFiltering

注意事项:

  1. 注意代码中所有的地址,若有需要请更改。
  2. 在/home/hadoop/file/collaborativeFiltering目录下应上传Users.txt、Movies.txt文件,可以在generateSourceData函数中修改相应路径。
  3. 运行前确保以启动HDFS

4.参考

参考了两篇文章:

  • https://www.cnblogs.com/pinard/p/6349233.html
    主要简述了很多实现协同过滤的算法,原理。
  • https://blog.csdn.net/u012995888/article/details/79077681
    有部分原理,含有代码,博主的这篇代码就是参考这篇文件的第一个程序代码(纯Java基础知识实现)。这篇文章的原文代码注释比较少,博主在其基础上加上了自己对该代码理解的注释:

参考文章代码:

package com.gyt.collaborativeFiltering;

import java.util.*;

/**
 * 描述:对电影打星1~5,最低打1星,0代表没打星过。大于平均推荐度代表喜欢。
 * 给目标用户推荐相似度最高用户喜欢的电影
 */
public class UserCFDemo {

    //系统用户
    private static String[] users={"小明","小花","小美","小张","小李"};
    //和这些用户相关的电影
    private static String[] movies={"电影1","电影2","电影3","电影4","电影5","电影6","电影7"};
    //用户点评电影打星数据,是users对应用户针对movies对应电影的评分
    private static int[][] allUserMovieStarList={
            {3,1,4,4,1,0,0},
            {0,5,1,0,0,4,0},
            {1,0,5,4,3,5,2},
            {3,1,4,3,5,0,0},
            {5,2,0,1,0,5,5}
    };
    //相似用户集合
    private static List> similarityUsers=null;
    //推荐所有电影集合
    private static List targetRecommendMovies=null;
    //点评过电影集合
    private static List commentedMovies=null;
    //用户在电影打星集合中的位置
    private static Integer targetUserIndex=null;

    public static void main(String[] args) {
        Scanner scanner=new Scanner(System.in);
        String user=scanner.nextLine();
        while (user!=null && !"exit".equals(user)){
            targetUserIndex=getUserIndex(user);	// 计算用户位置
            if(targetUserIndex==null){
                System.out.println("没有搜索到此用户,请重新输入:");
            }else{
                //计算用户相似度
                calcUserSimilarity();
                
                //计算电影推荐度,排序
                calcRecommendMovie();
                
                //处理推荐电影列表
                handleRecommendMovies();	// 将已评分的电影加入列表
                
                //输出推荐电影
                System.out.print("推荐电影列表:");
                for (String item:targetRecommendMovies){
                    if(!commentedMovies.contains(item)){
                        System.out.print(item+"  ");
                    }
                }
                System.out.println();
            }

            user=scanner.nextLine();
            targetRecommendMovies=null;
        }

    }

    /**
     * 把推荐列表中用户已经点评过的电影剔除
     */
    private static void handleRecommendMovies(){
        commentedMovies=new ArrayList<>();
        for (int i=0;i();
        List> recommendMovies=new ArrayList<>();
        List recommendMovie=null;
        double recommdRate=0,sumRate=0;
        
        for (int i=0;i<7;i++){
            recommendMovie=new ArrayList<>();
            recommendMovie.add(i);

            recommdRate=allUserMovieStarList[Integer.parseInt(similarityUsers.get(0).get(0).toString())][i]*Double.parseDouble(similarityUsers.get(0).get(1).toString())
                    +allUserMovieStarList[Integer.parseInt(similarityUsers.get(1).get(0).toString())][i]*Double.parseDouble(similarityUsers.get(1).get(1).toString());
            recommendMovie.add(recommdRate);
            recommendMovies.add(recommendMovie);
            sumRate+=recommdRate;
        }

        sortCollection(recommendMovies,-1);

        for (List item:recommendMovies){
            if(Double.parseDouble(item.get(1).toString()) > sumRate/7){ //大于平均推荐度的商品才有可能被推荐
                targetRecommendMovies.add(movies[Integer.parseInt(item.get(0).toString())]);
            }
        }
    }

    /**
     * 获取两个最相似的用户
     */
    private static void calcUserSimilarity(){
        similarityUsers=new ArrayList<>();	// 相似用户集合
        List> userSimilaritys=new ArrayList<>();
        for (int i=0;i<5;i++){	// 计算目标用户与每个用户的相似度
            if(i==targetUserIndex){	// 用户本身跳过
                continue;
            }
            List userSimilarity=new ArrayList<>();
            userSimilarity.add(i);
            userSimilarity.add(calcTwoUserSimilarity(allUserMovieStarList[i],allUserMovieStarList[targetUserIndex]));	// 根据打分计算相似度
            userSimilaritys.add(userSimilarity);
        }

        sortCollection(userSimilaritys,1);	// 按照降序,将用户相似度列表排序

        similarityUsers.add(userSimilaritys.get(0));	// 应该是推荐两个相似度最高的
        similarityUsers.add(userSimilaritys.get(1));
    }

    /**
     * 根据用户数据,计算用户相似度
     * @param user1Stars
     * @param user2Starts
     * @return
     */
    private static double calcTwoUserSimilarity(int[] user1Stars,int[] user2Starts){
        float sum=0;
        for(int i=0;i<7;i++){	// 计算每部电影的打分之差
            sum += Math.pow(user1Stars[i]-user2Starts[i],2);
        }
        return Math.sqrt(sum);	// 返回所有评分差值的和
    }

    /**
     * 查找用户所在的位置
     * @param user
     * @return
     */
    private static Integer getUserIndex(String user){
        if(user==null || "".contains(user)){
            return null;
        }

        for(int i=0;i> list,int order){
        Collections.sort(list, new Comparator>() {
            @Override
            public int compare(List o1, List o2) {
                if(Double.valueOf(o1.get(1).toString()) > Double.valueOf(o2.get(1).toString())){
                    return order;
                }else if(Double.valueOf(o1.get(1).toString()) < Double.valueOf(o2.get(1).toString())){
                    return -order;
                }else{
                    return 0;
                }
            }
        });
    }
}
 
  

完!

你可能感兴趣的:(Hadoop,大数据算法,MapReduce,Hadoop生态圈学习)