机器学习入门--协同过滤算法[推荐算法]

一、推荐算法前言

       大数据时代产生了海量的数据,数据对企业来说是一种隐形的资产,里面蕴含了丰富的价值。但是,大数据体量之大、种类之繁以及产生速率之快,海量的数据并不都是有价值的,用户从海量的数据中提取有用的、针对性的信息需要花费很大的时间成本。比如,当你面对如此多的电影列表,你想找到一部最符合自己兴趣的电影,因为电影数量之多,你不可能把所有的电影简介都看一遍。那么怎么解决这个问题呢?电影平台搜集你过去看过的全部电影,分析了解你对什么类型电影感兴趣,然后针对性的把你感兴趣的电影主动的罗列给你,你不用花费太多的精力便可快速找到满足自己需求的电影。推荐算法便是为解决这类实际需求而诞生了。

提示:机器学习与数据挖掘的区别?

数据挖掘是从已有的历史数据中挖掘、总结知识,这个知识是已经碎片化存在的,和你是否进行挖掘无关。比如你看了很多电影,你对什么类型电影感兴趣你自己心里很清楚,而我并不知道。但是当你一五一十的把你看过的全部电影都跟我讲述一遍后,我便从你看过的电影中了解到你的电影偏好。这个电影偏好便是知识,知识是需要去挖掘去总结,才能更直观的被人所了解。

机器学习同样是基于已有的历史数据,但是不同的是机器学习是从已有的知识中产生新的知识。这个知识可能是推荐、预判等。比如,我在了解了你的电影偏好后,我可以迅速给你推荐你没看过但是感兴趣的电影;了解了历年每个时间段机票的价格,我可以预判未来某个时刻机票的大致情况。

经常有人(包括我自己)会混淆数据挖掘与机器学习的内涵。现在看来,机器学习是在数据挖掘基础之上的。数据挖掘把碎片化的知识规整在一起,机器学习是从碎片化知识中产生新知识。

       推荐算法是机器学习算法的一种。推荐算法有很多,其中协同过滤算法便是其中最常用的一种。什么是协同过滤算法呢?直白点说,就是你不知道怎么选择,大家来帮你选择。这个大家可能是用户,和你有相同偏好的人帮你选择你没有而他们有的;也可能是物品本身,和你历史物品相似的物品把自己推荐给你。对应的算法分类也就是基于用户的协同过滤算法和基于物品的协同过滤算法。前述可知,基于用户的协同过滤算法的关键是找到相同偏好的用户,找到了偏好最近的几个用户,他们偏好的物品便是要给你推荐的目标。而基于物品的协同过滤算法的关键是计算其它物品和历史物品的相似度,相似度最近的几个物品便是要推荐的物品。(换句话说,协同过滤算法的关键是解决相似度问题)。

提示:如何理解用户偏好和用户相似度?

举个例子,四川人喜欢吃辣的食物就是一种关于饮食的用户偏好。在饮食方面,四川人A和四川人群的饮食偏好程度 > 四川人A和湖南人群的饮食偏好程度 > 四川人A和湖北人群的饮食偏好程度。某人和某XX在某方面的偏好程度,也可以描述为用户相似度。请四川人吃饭点辣的食物,这就是基于用户的协同过滤算法的生活场景之一。见下图,帮A推荐食物的不是湖南人、不是湖北人,而是四川人这个集体。因为A和四川人群在饮食方面的用户相似度最高。

提示:如何理解物品相似度?

理论上,两个物品在某方面的属性或表现相似,就可以说两个物品在某方面是相似的。换句话来说,判断两个物品是否相似,必须找到一个评判的依据。这个依据是随意的,可以从物品内在的性质、外在的表现或者社会化的属性。比如,小明喜欢A、B和C,小丽喜欢A、C和D,小张喜欢B、E和F。观察三人拥有的物品,可以知道拥有A的也拥有C,可知AC的关联程度很高,即AC相似度很高。如果此时我已经拥有A,请问给我推荐什么商品最合适,显然应该把C推荐给我。这种人们拥有商品的关联程度是一种社会化的属性,是由人们的行为而产生的社会化结果。

       下面看一下协同过滤算法的理论基础,相似度问题的解决方案便可明了。


二、协同过滤算法理论基础

       相似度计算主要有三个经典算法:余弦定理相似性度量、欧氏距离相似度度量和杰卡德相似性度量。下面分别进行说明:

  • 余弦定理相似性度量

       三角形余弦定理公式:,由该公式可知角A越小,bc两边越近。当A为0度时,bc两边完全重合。

       当bc两边为向量时,两个向量的余弦为:

       当存在多个向量时,多个向量的余弦为:
       

       由上求余弦的向量公式,可得知当两个向量的夹角越小,两个向量方向越相近。当夹角为0时,两个向量方向完全重合。由此原理可以计算两个事物的相似度。比如,把一篇篇新闻提取成有效词语的向量,每一组有效词语向量的角度越小,则两篇新闻的相似程度越高。如下图,为了便于理解举个特例,假设新闻ABC三篇文章提取的关键字都是诗歌、李白、杜甫、王勃,并且对应文章出现的数量分别为[(诗歌,1)(李白,1)(杜甫,1)(王勃,1)]、[(诗歌,2)(李白,2)(杜甫,2)(王勃,2)]和[(诗歌,1)(李白,2)(杜甫,3)(王勃,4)]。从坐标图可以看出AB的相似度大于AC或者BC的相似度,AB的各关键字比例一样,而C相比AB更侧重诗人王勃的话题。

       

       观察上图,你会发现新闻D和新闻A、B的相似度是一样的。这个就是余弦定理计算相似度的缺陷,因为余弦定理只关注向量的方向,并不关注向量的起始点。

  • 欧氏距离相似性度量

       与余弦定理通过方向度量相似度不同,欧氏距离是通过计算样本实际距离在度量相似度的。

       二维平面上两点a(x1,y1)与b(x2,y2)间的欧氏距离:

       三维空间两点a(x1,y1,z1)与b(x2,y2,z2)间的欧氏距离:

       两个n维向量a(x11,x12,…,x1n)与 b(x21,x22,…,x2n)间的欧氏距离:

       把上例的几个点代入以上公式可以算出:AB欧氏距离为√4,AC欧氏距离为√14,AD欧氏距离为√10,BC欧氏距离为√6,BD欧氏距离为√10,CD欧氏距离√30。欧氏距离越短,n微向量越“近”,向量化的物品的相似度越高。通过比较,依旧是AB的相似度最高。

提示:对比余弦定理相似度度量与欧氏距离相似度度量:

欧氏距离能够体现个体数值特征的绝对差异,所以更多的用于需要从维度的数值大小中体现差异的分析,如使用用户行为指标分析用户价值的相似度或差异。

余弦距离更多的是从方向上区分差异,而对绝对的数值不敏感,更多的用于使用用户对内容评分来区分兴趣的相似度和差异,同时修正了用户间可能存在的度量标准不统一的问题(因为余弦距离对绝对数值不敏感)。

  • 杰卡德相似性度量

       两个集合的交集在该两个集合的并集所占的比例来度量两个集合的相似度。举例,新闻A和新闻B提取出词语集合的交集在新闻A和新闻B提取出词语集合的并集所占的比例就是AB的相似度。

三、协同过滤算法实现

  • 基于用户的协同过滤算法[UserCF]

       案例背景:根据电影点评网站数据,给目标用户推荐电影。

       思路步骤:

  1. 计算其他用户和目标用户的相似度(使用欧氏距离算法);
  2. 根据相似度的高低找出K个目标用户最相似的邻居;
  3. 在这些邻居喜欢的电影中,根据邻居与你的远近程度算出每个电影的推荐度;
  4. 根据每一件物品的推荐度高低给你推荐物品。

       简单的代码模拟实现:

package ai;

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<List<Object>> similarityUsers=null;
    //推荐所有电影集合
    private static List<String> targetRecommendMovies=null;
    //点评过电影集合
    private static List<String> 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<allUserMovieStarList[targetUserIndex].length;i++){
            if(allUserMovieStarList[targetUserIndex][i]!=0){
                commentedMovies.add(movies[i]);
            }
        }
    }


    /**
     * 获取全部推荐电影,计算平均电影推荐度
     */
    private static void calcRecommendMovie(){
        targetRecommendMovies=new ArrayList<>();
        List<List<Object>> recommendMovies=new ArrayList<>();
        List<Object> 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<Object> 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<List<Object>> userSimilaritys=new ArrayList<>();
        for (int i=0;i<5;i++){
            if(i==targetUserIndex){
                continue;
            }
            List<Object> 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<users.length;i++){
            if(user.equals(users[i])){
                return i;
            }
        }

        return null;
    }

    /**
     * 集合排序
     * @param list
     * @param order  1正序 -1倒序
     */
    private static void sortCollection(List<List<Object>> list,int order){
        Collections.sort(list, new Comparator<List<Object>>() {
            @Override
            public int compare(List<Object> o1, List<Object> 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;
                }
            }
        });
    }
}

       运行结果:

     这里写图片描述

  • 基于物品的协同过滤算法[ItemCF]

       根据上面UserCF可知,判断相似用户是通过用户点评的所有电影之间的欧氏距离来度量的,距离越短,用户在电影方面相似度越高。现在换个思路通过计算每个用户对应电影之间的欧氏距离或杰卡德相似性度量来度量两个电影之间的相似度。

       案例背景:同样根据电影点评网站数据,给目标用户推荐电影。注意,一般来说用户是根据自己的兴趣选择电影的,所以在足够多用户基数的情况下,如果多数用户选择观看A电影的也观看了B电影,那么可以说电影AB是相似的。但是对电影的评分,则更多的是依据用户对电影的质量评价。所以,在此实现中把UserCF实现中非0的评分都置为1。

       思路步骤:

  1. 计算物品之间的相似度(同样使用杰卡德相似性度量算法);
  2. 根据物品之间的相似度以及用户历史行为给用户生成推荐列表。

       简单的代码模拟实现:

package ai;

import java.util.*;

/**
 * 描述:对电影点评过用1表示,0代表没点评过。
 * 给目标用户推荐相似度最高用户喜欢的电影
 */
public class ItemCFDemo {

    //系统用户
    private static String[] users={"小明","小花","小美","小张","小李"};
    //和这些用户相关的电影
    private static String[] movies={"电影1","电影2","电影3","电影4","电影5","电影6","电影7"};
    //用户点评电影情况
    private static Integer[][] allUserMovieCommentList={
            {1,1,1,0,1,0,0},
            {0,1,1,0,0,1,0},
            {1,0,1,1,1,1,1},
            {1,1,1,1,1,0,0},
            {1,1,0,1,0,1,1}
    };
    //用户点评电影情况,行转列
    private static Integer[][] allMovieCommentList=new Integer[allUserMovieCommentList[0].length][allUserMovieCommentList.length];
    //电影相似度
    private static HashMap<String,Double> movieABSimilaritys=null;
    //待推荐电影相似度列表
    private static HashMap<Integer,Object> movieSimilaritys=null;
    //用户所在的位置
    private static Integer targetUserIndex=null;
    //目标用户点评过的电影
    private static List<Integer> targetUserCommentedMovies=null;
    //推荐电影
    private static  List<Map.Entry<Integer, Object>> recommlist=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{
                //转换目标用户电影点评列表
                targetUserCommentedMovies=Arrays.asList(allUserMovieCommentList[targetUserIndex]);
                //计算电影相似度
                calcAllMovieSimilaritys();
                //获取全部待推荐电影
                calcRecommendMovie();
                //输出推荐电影
                System.out.print("推荐电影列表:");
                for (Map.Entry<Integer, Object> item:recommlist){
                    System.out.print(movies[item.getKey()]+"  ");
                }
                System.out.println();
            }

            user=scanner.nextLine();
        }

    }

    /**
     * 获取全部推荐电影
     */
    private static void calcRecommendMovie(){
        movieSimilaritys=new HashMap<>();
        for (int i=0;i<targetUserCommentedMovies.size()-1;i++){
            for (int j=i+1;j<targetUserCommentedMovies.size();j++){
                Object similarity=null;
                if(targetUserCommentedMovies.get(i)==1 &&  targetUserCommentedMovies.get(j)==0 && ( movieABSimilaritys.get(i+""+j)!=null || movieABSimilaritys.get(j+""+i)!=null)){
                    similarity=movieABSimilaritys.get(i+""+j)!=null?movieABSimilaritys.get(i+""+j):movieABSimilaritys.get(j+""+i);
                    movieSimilaritys.put(j,similarity);
                }else if(targetUserCommentedMovies.get(i)==0 &&  targetUserCommentedMovies.get(j)==1 && (movieABSimilaritys.get(i+""+j)!=null || movieABSimilaritys.get(j+""+i)!=null)){
                    similarity=movieABSimilaritys.get(i+""+j)!=null?movieABSimilaritys.get(i+""+j):movieABSimilaritys.get(j+""+i);
                    movieSimilaritys.put(i,similarity);
                }
            }
        }

        recommlist = new ArrayList<Map.Entry<Integer, Object>>(movieSimilaritys.entrySet());
        Collections.sort(recommlist, new Comparator<Map.Entry<Integer, Object>>() {
            @Override
            public int compare(Map.Entry<Integer, Object> o1, Map.Entry<Integer, Object> o2) {
                return o1.getValue().toString().compareTo(o2.getValue().toString());
            }
        });

        System.out.println("待推荐相似度电影列表:"+recommlist);
    }

    /**
     * 计算全部物品间的相似度
     */
    private static void calcAllMovieSimilaritys(){
        converRow2Col();
        movieABSimilaritys=new HashMap<>();
        for (int i=0;i<allMovieCommentList.length-1;i++){
            for (int j=i+1;j<allMovieCommentList.length;j++){
                movieABSimilaritys.put(i+""+j,calcTwoMovieSimilarity(allMovieCommentList[i],allMovieCommentList[j]));
            }
        }

        System.out.println("电影相似度:"+movieABSimilaritys);
    }


    /**
     * 根据电影全部点评数据,计算两个电影相似度
     * @param movie1Stars
     * @param movie2Starts
     * @return
     */
    private static double calcTwoMovieSimilarity(Integer[] movie1Stars,Integer[] movie2Starts){
        float sum=0;
        for(int i=0;i<movie1Stars.length;i++){
            sum+=Math.pow(movie1Stars[i]-movie2Starts[i],2);
        }
        return Math.sqrt(sum);
    }

    /**
     * 数组行转列
     */
    private static void converRow2Col(){
        for (int i=0;i<allUserMovieCommentList[0].length;i++){
            for(int j=0;j<allUserMovieCommentList.length;j++){
                allMovieCommentList[i][j]=allUserMovieCommentList[j][i];
            }
        }
        System.out.println("电影点评转行列:"+Arrays.deepToString(allMovieCommentList));
    }

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

        for(int i=0;i<users.length;i++){
            if(user.equals(users[i])){
                return i;
            }
        }

        return null;
    }
}

       运行结果:
这里写图片描述

四、使用Apache Mahout实现协同过滤算法

       三中自己实现推荐算法中的一种,这只是比较简单的实现。Apache Mahout为我们提供了聚类、分类、推荐过滤、频繁子项挖掘等一些可扩展的机器学习领域经典算法的实现。我们可以利用这些实现快捷的做出智能应用。其中Mahout实现协同过滤的基本组件如下:

这里写图片描述

       组件接口基本介绍可以看《使用Mahout实现协同过滤》《深入推荐引擎相关算法 - 协同过滤》和官方相关文档,在此不予赘述。

       以下两种实现所使用的依赖和数据分别如下:
       pom.xml

<dependencies>
        <dependency>
            <groupId>org.apache.mahout</groupId>
            <artifactId>mahout-core</artifactId>
            <version>${mahout.version}</version>
        </dependency>

        <dependency>
            <groupId>org.apache.mahout</groupId>
            <artifactId>mahout-integration</artifactId>
            <version>${mahout.version}</version>
        </dependency>

        <dependency>
            <groupId>org.apache.mahout</groupId>
            <artifactId>mahout-math</artifactId>
            <version>${mahout.version}</version>
        </dependency>

        <dependency>
            <groupId>org.apache.mahout</groupId>
            <artifactId>mahout-examples</artifactId>
            <version>${mahout.version}</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>${slf4j-log4j12.version}</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${slf4j-log4j12.version}</version>
        </dependency>

    </dependencies>

       数据文件data.csv内容如下,其中每行数据是以tab键相隔的。

11111   100001  3
11111   100002  2
11111   100003  5
11111   100004  1
22222   100005  3
22222   100007  4
22222   100002  4
22222   100003  1
22222   100006  5
33333   100001  4
33333   100003  4
33333   100005  3
44444   100008  1
55555   100009  2
55555   100002  4
55555   100001  3
55555   100010  4
55555   100011  3
  • 使用Mahout实现基于用户的协同过滤算法[UserCF]

       相关代码:

import org.apache.mahout.cf.taste.common.TasteException;
import org.apache.mahout.cf.taste.impl.common.LongPrimitiveIterator;
import org.apache.mahout.cf.taste.impl.model.file.FileDataModel;
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.EuclideanDistanceSimilarity;
import org.apache.mahout.cf.taste.model.DataModel;
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 java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.List;

/**
 * 基于用户的协同过滤算法
 */
public class UserCFDemo {

    final static int NEIGHBORHOOD_NUM = 2;   //用户邻居数量
    final static int RECOMMENDER_NUM = 3;    //推荐结果个数

    public static void main(String[] args) throws IOException, TasteException {
        URL url=UserCFDemo.class.getClassLoader().getResource("data.csv");   //数据集,其中第一列表示用户id;第二列表示商品id;第三列表示评分,评分是5分制
        DataModel model = new FileDataModel(new File(url.getFile()));  //基于文件的model,通过文件形式来读入,且此类型所需要读入的数据的格式要求很低,只需要满足每一行是用户id,物品id,用户偏好,且之间用tab或者是逗号隔开即可

        //基于用户的协同过滤算法,基于物品的协同过滤算法
        UserSimilarity user = new EuclideanDistanceSimilarity(model);  //计算欧式距离,欧式距离来定义相似性,用s=1/(1+d)来表示,范围在[0,1]之间,值越大,表明d越小,距离越近,则表示相似性越大
        NearestNUserNeighborhood  neighbor = new NearestNUserNeighborhood(NEIGHBORHOOD_NUM, user, model);
        //指定用户邻居数量

        //构建基于用户的推荐系统
        Recommender r = new GenericUserBasedRecommender(model, neighbor, user);


        //得到所有用户的id集合
        LongPrimitiveIterator iter = model.getUserIDs();

        while(iter.hasNext())
        {
            long uid = iter.nextLong();
            List<RecommendedItem> list = r.recommend(uid,RECOMMENDER_NUM);  //获取推荐结果
            System.out.printf("用户:%s",uid);
            //遍历推荐结果
            System.out.print("|| 推荐商品:");
            for(RecommendedItem ritem : list)
            {
                System.out.print(ritem.getItemID()+" ");
            }
            System.out.println();
        }
    }
}

       运行结果:
这里写图片描述

  • 使用Mahout实现基于物品的协同过滤算法[ItemCF]

       相关代码:

import org.apache.mahout.cf.taste.common.TasteException;
import org.apache.mahout.cf.taste.impl.common.LongPrimitiveIterator;
import org.apache.mahout.cf.taste.impl.model.file.FileDataModel;
import org.apache.mahout.cf.taste.impl.recommender.GenericItemBasedRecommender;
import org.apache.mahout.cf.taste.impl.similarity.EuclideanDistanceSimilarity;
import org.apache.mahout.cf.taste.model.DataModel;
import org.apache.mahout.cf.taste.recommender.RecommendedItem;
import org.apache.mahout.cf.taste.recommender.Recommender;
import org.apache.mahout.cf.taste.similarity.ItemSimilarity;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.List;

/**
 * Created by chao.du on 2018/1/23.
 */
public class ItemCFDemo {

    final static int NEIGHBORHOOD_NUM = 2;
    final static int RECOMMENDER_NUM = 3;

    public static void main(String[] args) throws IOException,TasteException
    {
        URL url=ItemCFDemo.class.getClassLoader().getResource("data.csv");

        DataModel model = new FileDataModel(new File(url.getFile()));

        ItemSimilarity item = new EuclideanDistanceSimilarity(model);

        Recommender r = new GenericItemBasedRecommender(model,item);

        LongPrimitiveIterator iter = model.getUserIDs();

        while(iter.hasNext())
        {
            long uid = iter.nextLong();
            List<RecommendedItem> list = r.recommend(uid,RECOMMENDER_NUM);  //获取推荐结果
            System.out.printf("用户:%s",uid);
            System.out.print("|| 推荐商品:");
            //遍历推荐结果
            for(RecommendedItem ritem : list)
            {
                System.out.printf(ritem.getItemID()+" ");
            }
            System.out.println();
        }

    }
}

       运行结果:
这里写图片描述


参考资料:

1、《深入推荐引擎相关算法 - 协同过滤》

2、《基于 Apache Mahout 构建社会化推荐引擎》

3、《Mahout实现基于用户的协同过滤算法》

4、《[推荐算法]基于用户的协同过滤算法》

5、《基于用户的协同过滤算法和基于物品的协同过滤算法之java实现》

6、《基于物品的协同过滤算法(ItemCF)》

7、《基于物品的协同过滤算法:理论说明,代码实现及应用》

8、《Mahout分步式程序开发 基于物品的协同过滤ItemCF》

9、《用Maven构建Mahout项目》

10、《推荐系统中的相似度计算与协同过滤》

11、《不同相关性度量方法的线上效果对比与分析》

12、《文本相似度计算-JaccardSimilarity和哈希签名函数》

13、《距离的总结(欧氏距离)》

14、《基于物品的协同过滤算法itemCF原理及python代码实现》

15、《使用Mahout实现协同过滤》

猜你喜欢

转载自blog.csdn.net/u012995888/article/details/79077681
今日推荐