欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

协同过滤

程序员文章站 2022-07-14 22:10:48
...

1,什么是协同过滤

推荐系统应用数据分析技术,找出用户最可能喜欢的东西推荐给用户,现在很多电子商务网站都应用这个算法。

2,协同过滤的种类

协同过滤推荐分为三种类型。第一种是基于用户(user-based)的协同过滤,第二种是基于项目(item-based)的协同过滤,第三种是基于模型(model based)的协同过滤。

3.协同过滤的相似度计算有哪几种

\1. 基于余弦(Cosine-based)的相似度计算,通过计算两个向量之间的夹角余弦值来计算物品之间的相似性,公式如下:

​ 其中分子为两个向量的内积,即两个向量相同位置的数字相乘。

协同过滤

​ \2. 基于关联(Correlation-based)的相似度计算,计算两个向量之间的Pearson-r关联度,公式如下:

协同过滤

​ 其中表示用户u对物品i的打分,表示第i个物品打分的平均值。

​ \3. 调整的余弦(Adjusted Cosine)相似度计算,由于基于余弦的相似度计算没有考虑不同用户的打分情况,可能有的用户偏向于给高分,而有的用户偏向于给低分,该方法通过减去用户打分的平均值消除不同用户打分习惯的影响,公式如下:

协同过滤

​ 其中表示用户u打分的平均值。

协同过滤

4.关于物品和用户(https://www.cnblogs.com/baihuaxiu/p/6617389.html)

基于用户的 CF 的基本思想相当简单,基于用户对物品的偏好找到相邻邻居用户,然后将邻居用户喜欢的推荐给当前用户。计算上,就是将一个用户对所有物品的偏好作为一个向量来计算用户之间的相似度,找到 K 邻居后,根据邻居的相似度权重以及他们对物品的偏好,预测当前用户没有偏好的未涉及物品,计算得到一个排序的物品列表作为推荐。 下图给出了一个例子,对于用户 A,根据用户的历史偏好,这里只计算得到一个邻居 - 用户 C,然后将用户 C 喜欢的物品 D 推荐给用户 A。

缺点:1. 数据稀疏性。一个大型的电子商务推荐系统一般有非常多的物品,用户可能买的其中不到1%的物品,不同用户之间买的物品重叠性较低,导致算法无法找到一个用户的邻居,即偏好相似的用户。

    2. 算法扩展性。最近邻居算法的计算量随着用户和物品数量的增加而增加,不适合数据量大的情况使用。

协同过滤
基于物品的 CF 的原理和基于用户的 CF 类似,只是在计算邻居时采用物品本身,而不是从用户的角度,即基于用户对物品的偏好找到相似的物品,然后根据用户的历史偏好,推荐相似的物品给他。从计算的角度看,就是将所有用户对某个物品的偏好作为一个向量来计算物品之间的相似度,得到物品的相似物品后,根据用户历史的偏好预测当前用户还没有表示偏好的物品,计算得到一个排序的物品列表作为推荐。下图给出了一个例子,对于物品 A,根据所有用户的历史偏好,喜欢物品 A 的用户都喜欢物品 C,得出物品 A 和物品 C 比较相似,而用户 C 喜欢物品 A,那么可以推断出用户 C 可能也喜欢物品 C。
协同过滤

5.py及例子(https://blog.csdn.net/u012995888/article/details/79077681)

1)用户

根据电影点评网站数据,给目标用户推荐电影。

   思路步骤:

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

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


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BU4hH3da-1595682802024)(C:\Users\Administrator\Pictures\QQ浏览器截图\20180119114407243.png)]

2)物品

思路步骤:

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

​ 简单的代码模拟实现:


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


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-M8AaNmN6-1595682802025)(C:\Users\Administrator\Pictures\QQ浏览器截图\20180119182344930.png)]