因为学习了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) 该电影的推荐值大于所有电影对该用户的推荐度的平均值
*
***********************************************************************************************************************
由于代码过长,博客中就不粘贴了,代码可见GitHub。
注意事项:
参考了两篇文章:
参考文章代码:
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