基于用户的协同过滤算法(java)

杂言

什么是协同过滤算法,在我的理解中协同过滤算法分成两个部分,相信各位看官也能猜到,顾名思义就是协同与过滤,那什么是协同,什么过滤呢?下面基于用户视角给大家说说我的浅显之见(错误的地方还望各位看官不吝赐教)

协同

协同,在于找到不同用户之间相同的部分,比如用户A浏览了商品A,商品B,商品C,而用户B浏览了商品A,商品C。

用户A

商品A

商品B

商品C

用户B

商品A

商品C

如上图所示,那么商品A和商品C就是用户A和用户B的相同部分。

现在有用户C,浏览了商品B,商品D,商品E。

用户A

商品A

商品B

商品C

用户B

商品A

商品C

用户C

商品B

商品D

商品E

如上图所示,那么现在对于用户A来说,他与用户B以及用户C都有相同的部分,但是用户A与用户B相交的部分比用户A与用户C相交的部分更”大“,所以对于用户A来说相比于用户C,他更”贴近“于用户B,所以用户A与用户B的契合度高与用户C。对于用户B来说,他与用户A有相交的部分,但是与用户C没有相交,所以他们之间的契合度为0。这就是我所理解的”协同“。

过滤

过滤,在于找到相似用户之间的不同数据,主要做法就是过滤掉相同的部分,剩下的就是需要的部分。

用户A

商品A

商品B

商品C

用户B

商品A

商品C

还是用户A和用户B,他们已经患难与共,经历了”协同“的洗礼,到了过滤的步骤了,现在他们都浏览了商品A,商品C,经过过滤,用户A浏览剩下的商品B就会被推荐给用户B。

以上,就完成了协同过滤的全过程。

算法核心

本文参考了博主jf-lin的文字,再他的基础上作出符合我项目的改动

文章地址:基于用户的协同过滤算法(JAVA实现)_jf-lin的博客-CSDN博客

文本沿用了博主jf-lin的核心算法:皮尔逊相关系数( Pearson correlation coefficient),又称皮尔逊积矩相关系数(Pearson product-moment correlation coefficient,简称 PPMCCPCCs)以及沿用公式:

公式不做解释,想了解可自行百度,只说明一下计算的结果,最终计算结果会落在[-1,1]的区间上,整数为正相关,负数为负相关。

用这个公式的目的在于在多个相似用户中找到最接近的用户,以此来进行精准推荐,或对推荐进行排序。

代码实现

核心推荐代码解释

(下面有完整的)

首先,computeNearestNeighbor函数用于计算被计算用户与其他用户之间的皮尔逊相关系数,也就是起到协同的作用,由于博主jf-lin没有对被计算用户以及其他用户的数据集长度做限制,会导致pearsonDis函数计算出现数据获取不到的错误,所以我在计算皮尔逊相关系数之前先获取被计算用户以及其他用户的共同部分,然后才将数据送入pearsonDis函数中进行皮尔逊相关系数的计算。


/**
     * 计算皮尔逊积矩相关系数(Pearson product-moment correlation coefficient)
     * 协同
     * @param otherRecommendUsers   其他用户数据
     * @param currentRecommendUser  当前用户数据
     * @return  返回值为当前用户与其他计算的皮尔森相关系数结果集
     */
    private Map<Double, String> computeNearestNeighbor(
            List<RecommendUser> otherRecommendUsers, RecommendUser currentRecommendUser) {
        //计算皮尔逊积矩相关系数(Pearson product-moment correlation coefficient)
        Map<Double, String> distances = new TreeMap<>();
        Map<String, Integer> map = new HashMap<>();
        for (RecommendGood recommendGood1 : currentRecommendUser.recommendGoodList) {
            map.put(recommendGood1.goodName,recommendGood1.score);
        }
        for (RecommendUser otherRecommendUser : otherRecommendUsers) {
            List<RecommendGood> rating1 = new ArrayList<>();
            List<RecommendGood> rating2 = new ArrayList<>();
            //查找相同的部分
            for (RecommendGood recommendGood2 : otherRecommendUser.recommendGoodList) {
                if (map.containsKey(recommendGood2.goodName)){
                    rating1.add(new RecommendGood(recommendGood2.goodName,map.get(recommendGood2.goodName)));
                    rating2.add(recommendGood2);
                }
            }
            if (rating1.size() == 0){
                continue;
            }
            double distance = pearsonDis(rating1, rating2);
            distances.put(distance, otherRecommendUser.username);

        }
        return distances;
    }

pearsonDis函数负责计算两个用户的皮尔逊相关系数。由于之前在”协同“部分进行了相同物品的整理,所以在计算皮尔逊相关系数的时候就可以排除掉两个数据集合不一致的问题。而计算Exy的时候也是针对两个用户对同一物品的评分进行计算的,在浏览博主jf-lin的核心代码时,发现他的核心代码并没有基于这一条件进行计算。


/**
     * 计算2个打分序列间的皮尔逊积矩相关系数(Pearson product-moment correlation coefficient)
     * 选择公式四进行计算(百度可查)
     * @param rating1   被计算的用户1(当前用户)
     * @param rating2   被计算的用户2(其他用户之一)
     * @return  返回对应的皮尔森相关系数结果
     */
    private double pearsonDis(List<RecommendGood> rating1, List<RecommendGood> rating2) {
        int n = rating1.size();
        List<Integer> rating1ScoreCollect = rating1.stream().map(A -> A.score).collect(Collectors.toList());
        List<Integer> rating2ScoreCollect = rating2.stream().map(A -> A.score).collect(Collectors.toList());

        double Ex= rating1ScoreCollect.stream().mapToDouble(x->x).sum();
        double Ey= rating2ScoreCollect.stream().mapToDouble(y->y).sum();

        double Ex2=rating1ScoreCollect.stream().mapToDouble(x->Math.pow(x,2)).sum();
        double Ey2=rating2ScoreCollect.stream().mapToDouble(y->Math.pow(y,2)).sum();

        double Exy= IntStream.range(0,n).mapToDouble(i->rating1ScoreCollect.get(i)*rating2ScoreCollect.get(i)).sum();

        double numerator=Exy-Ex*Ey/n;
        double denominator=Math.sqrt((Ex2-Math.pow(Ex,2)/n)*(Ey2-Math.pow(Ey,2)/n));
        if (denominator==0){ return 0.0;}
        return numerator/denominator;
    }

filtering函数,用于排除被计算用户与被计算用户具有相关性的用户的共同商品,也就是起到过滤的作用。在过滤的时候,我特地把皮尔逊相关系数小于0的用户过滤掉,因为皮尔逊相关系数小于0就代表该用户与被计算用户处于负相关的关系,所浏览的物品没有相似性。


/**
     * 过滤
     * @param distances 计算后的皮尔森相关系数
     * @param otherRecommendUsers   其他用户的数据集
     * @param currentRecommendUser  被计算用户的数据
     * @return  返回过滤后的相似度高的结果
     */
    public List<RecommendGood> filtering(
            Map<Double, String> distances, List<RecommendUser> otherRecommendUsers, RecommendUser currentRecommendUser){
        List<String> nearList = new ArrayList<>(distances.values());
        List<Double> scores = new ArrayList<>(distances.keySet());
        List<List<Object>> nears= new ArrayList<>();
        List<RecommendGood> recommendationRecommendGoods = new ArrayList<>();
        for (int i = nearList.size() - 1; i >= 0; i--){
            if (scores.get(i) > 0){
                ArrayList<Object> objects = new ArrayList<>();
                objects.add(nearList.get(i));
                objects.add(scores.get(i));
                nears.add(objects);
            }
        }
        for (List<Object> near : nears) {
            //找到邻居看过的商品
            RecommendUser neighborRatings = new RecommendUser();
            for (RecommendUser recommendUser : otherRecommendUsers) {
                if (near.get(0).equals(recommendUser.username)) {
                    neighborRatings = recommendUser;
                }
            }

            //排除掉相同的商品
            boolean t;
            for (RecommendGood recommendNeighborGood : neighborRatings.recommendGoodList) {
                t = true;
                for (RecommendGood recommendUserGood : currentRecommendUser.recommendGoodList) {
                    if (recommendNeighborGood.goodName.equals(recommendUserGood.goodName)) {
                        t = false;
                        break;
                    }
                }
                if (t){
                    recommendationRecommendGoods.add(recommendNeighborGood);
                }
            }
        }
        Collections.sort(recommendationRecommendGoods);
        return recommendationRecommendGoods;
    }

完整代码

RecommendGood类

用于存放商品的实体类


package com.example.recommender_system_web.bean;

/**
 * @author 沐沐言又几
 * @time 2023/2/6
 */
public class RecommendGood implements Comparable<RecommendGood> {
    public String goodName;
    public int score;

    public RecommendGood(String goodName, int score) {
        this.goodName = goodName;
        this.score = score;
    }

    @Override
    public String toString() {
        return "Good{" +
                "goodName='" + goodName + '\'' +
                ", score=" + score +
                '}';
    }

    @Override
    public int compareTo(RecommendGood o) {
        return score > o.score ? -1 : 1;
    }

}

RecommendUser类

用于存放用户的实体类


package com.example.recommender_system_web.bean;

import java.util.ArrayList;
import java.util.List;

/**
 * @author 沐沐言又几
 * @time 2023/2/6
 */
public class RecommendUser {
    public String username;
    public List<RecommendGood> recommendGoodList = new ArrayList<>();

    public RecommendUser() {}

    public RecommendUser(String username) {
        this.username = username;
    }

    public RecommendUser set(String movieName, int score) {
        this.recommendGoodList.add(new RecommendGood(movieName, score));
        return this;
    }

    public RecommendGood find(String movieName) {
        for (RecommendGood recommendGood : recommendGoodList) {
            if (recommendGood.goodName.equals(username)) {
                return recommendGood;
            }
        }
        return null;
    }

    @Override
    public String toString() {
        return "RecommendUser{" +
                "username='" + username + '\'' +
                ", recommendGoodsList=" + recommendGoodList +
                '}';
    }
}

Recommend类

核心推荐代码


package com.example.recommender_system_web.recommender;

import com.example.recommender_system_web.bean.RecommendGood;
import com.example.recommender_system_web.bean.RecommendUser;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * @author 沐沐言又几
 * @time 2023/2/6
 */
public class Recommend {
    /**
     * 计算皮尔逊积矩相关系数(Pearson product-moment correlation coefficient)
     * 协同
     * @param otherRecommendUsers   其他用户数据
     * @param currentRecommendUser  当前用户数据
     * @return  返回值为当前用户与其他计算的皮尔森相关系数结果集
     */
    private Map<Double, String> computeNearestNeighbor(
            List<RecommendUser> otherRecommendUsers, RecommendUser currentRecommendUser) {
        //计算皮尔逊积矩相关系数(Pearson product-moment correlation coefficient)
        Map<Double, String> distances = new TreeMap<>();
        Map<String, Integer> map = new HashMap<>();
        for (RecommendGood recommendGood1 : currentRecommendUser.recommendGoodList) {
            map.put(recommendGood1.goodName,recommendGood1.score);
        }
        for (RecommendUser otherRecommendUser : otherRecommendUsers) {
            List<RecommendGood> rating1 = new ArrayList<>();
            List<RecommendGood> rating2 = new ArrayList<>();
            //查找相同的部分
            for (RecommendGood recommendGood2 : otherRecommendUser.recommendGoodList) {
                if (map.containsKey(recommendGood2.goodName)){
                    rating1.add(new RecommendGood(recommendGood2.goodName,map.get(recommendGood2.goodName)));
                    rating2.add(recommendGood2);
                }
            }
            if (rating1.size() == 0){
                continue;
            }
            double distance = pearsonDis(rating1, rating2);
            distances.put(distance, otherRecommendUser.username);

        }
        return distances;
    }


    /**
     * 计算2个打分序列间的皮尔逊积矩相关系数(Pearson product-moment correlation coefficient)
     * 选择公式四进行计算(百度可查)
     * @param rating1   被计算的用户1(当前用户)
     * @param rating2   被计算的用户2(其他用户之一)
     * @return  返回对应的皮尔森相关系数结果
     */
    private double pearsonDis(List<RecommendGood> rating1, List<RecommendGood> rating2) {
        int n = rating1.size();
        List<Integer> rating1ScoreCollect = rating1.stream().map(A -> A.score).collect(Collectors.toList());
        List<Integer> rating2ScoreCollect = rating2.stream().map(A -> A.score).collect(Collectors.toList());

        double Ex= rating1ScoreCollect.stream().mapToDouble(x->x).sum();
        double Ey= rating2ScoreCollect.stream().mapToDouble(y->y).sum();

        double Ex2=rating1ScoreCollect.stream().mapToDouble(x->Math.pow(x,2)).sum();
        double Ey2=rating2ScoreCollect.stream().mapToDouble(y->Math.pow(y,2)).sum();

        double Exy= IntStream.range(0,n).mapToDouble(i->rating1ScoreCollect.get(i)*rating2ScoreCollect.get(i)).sum();

        double numerator=Exy-Ex*Ey/n;
        double denominator=Math.sqrt((Ex2-Math.pow(Ex,2)/n)*(Ey2-Math.pow(Ey,2)/n));
        if (denominator==0){ return 0.0;}
        return numerator/denominator;
    }

    /**
     * 过滤
     * @param distances 计算后的皮尔森相关系数
     * @param otherRecommendUsers   其他用户的数据集
     * @param currentRecommendUser  被计算用户的数据
     * @return  返回过滤后的相似度高的结果
     */
    public List<RecommendGood> filtering(
            Map<Double, String> distances, List<RecommendUser> otherRecommendUsers, RecommendUser currentRecommendUser){
        List<String> nearList = new ArrayList<>(distances.values());
        List<Double> scores = new ArrayList<>(distances.keySet());
        List<List<Object>> nears= new ArrayList<>();
        List<RecommendGood> recommendationRecommendGoods = new ArrayList<>();
        for (int i = nearList.size() - 1; i >= 0; i--){
            if (scores.get(i) > 0){
                ArrayList<Object> objects = new ArrayList<>();
                objects.add(nearList.get(i));
                objects.add(scores.get(i));
                nears.add(objects);
            }
        }
        for (List<Object> near : nears) {
            //找到邻居看过的商品
            RecommendUser neighborRatings = new RecommendUser();
            for (RecommendUser recommendUser : otherRecommendUsers) {
                if (near.get(0).equals(recommendUser.username)) {
                    neighborRatings = recommendUser;
                }
            }

            //排除掉相同的商品
            boolean t;
            for (RecommendGood recommendNeighborGood : neighborRatings.recommendGoodList) {
                t = true;
                for (RecommendGood recommendUserGood : currentRecommendUser.recommendGoodList) {
                    if (recommendNeighborGood.goodName.equals(recommendUserGood.goodName)) {
                        t = false;
                        break;
                    }
                }
                if (t){
                    recommendationRecommendGoods.add(recommendNeighborGood);
                }
            }
        }
        Collections.sort(recommendationRecommendGoods);
        return recommendationRecommendGoods;
    }

    /**
     * 推荐函数入口
     * @param otherRecommendUsers   其他用户数据集
     * @param currentRecommendUser  当前用户数据
     * @return
     */
    public List<RecommendGood> recommend(
            List<RecommendUser> otherRecommendUsers, RecommendUser currentRecommendUser) {
        //找到皮尔逊积矩相关系数(Pearson product-moment correlation coefficient)大于零的用户
        Map<Double, String> distances = computeNearestNeighbor(otherRecommendUsers, currentRecommendUser);
        return filtering(distances, otherRecommendUsers, currentRecommendUser);
    }
}

结语

如果对以上内容有疑问,或有错误,还望不吝赐教,多多指出错误。

如果有更好的见解,也可以私聊我,一起深入谈论。

  • 7
    点赞
  • 81
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
协同过滤算法是一种推荐系统算法,它通过分析用户的历史行为,比如购买记录、评分记录等,来推荐用户可能感兴趣的产品或内容。其中基于用户点赞的协同过滤算法是一种常见的算法Java实现基于用户点赞的协同过滤算法可以分为以下几个步骤: 1. 数据准备 首先需要准备用户点赞数据,可以从数据库或文件中读取。数据格式应该包含用户ID和点赞的内容ID。 2. 数据处理 将用户点赞数据转换成用户-内容矩阵,矩阵的行表示用户,列表示内容,矩阵中的每个元素表示用户对内容的点赞情况。矩阵中未点赞的位置可以用0或其他值表示。 3. 相似度计算 计算用户之间的相似度,常见的计算方法有余弦相似度和皮尔逊相关系数。可以使用公式或Java库函数计算相似度。 4. 推荐生成 根据相似度矩阵和用户点赞数据,生成推荐结果。可以使用加权平均或其他推荐算法生成推荐结果。 以下是一个简单的Java实现示例: ```java import java.util.HashMap; import java.util.Map; public class CollaborativeFiltering { // 用户点赞数据 private static int[][] userLikes = { {1, 2}, {1, 3, 4}, {2, 3}, {2, 4}, {3, 5}, {4, 5} }; public static void main(String[] args) { // 计算用户-内容矩阵 int[][] userContentMatrix = getUserContentMatrix(userLikes); // 计算用户之间的相似度矩阵 double[][] similarityMatrix = getSimilarityMatrix(userContentMatrix); // 生成推荐结果 Map<Integer, Double> recommendations = getRecommendations(0, userLikes, similarityMatrix); System.out.println(recommendations); } // 将用户点赞数据转换成用户-内容矩阵 private static int[][] getUserContentMatrix(int[][] userLikes) { int maxContentId = 0; for (int[] likes : userLikes) { for (int contentId : likes) { if (contentId > maxContentId) { maxContentId = contentId; } } } int[][] userContentMatrix = new int[userLikes.length][maxContentId]; for (int i = 0; i < userLikes.length; i++) { for (int contentId : userLikes[i]) { userContentMatrix[i][contentId - 1] = 1; } } return userContentMatrix; } // 计算用户之间的相似度矩阵 private static double[][] getSimilarityMatrix(int[][] userContentMatrix) { int numUsers = userContentMatrix.length; double[][] similarityMatrix = new double[numUsers][numUsers]; for (int i = 0; i < numUsers; i++) { for (int j = 0; j < numUsers; j++) { if (i == j) { similarityMatrix[i][j] = 1.0; } else { double dotProduct = 0.0; double norm1 = 0.0; double norm2 = 0.0; for (int k = 0; k < userContentMatrix[i].length; k++) { dotProduct += userContentMatrix[i][k] * userContentMatrix[j][k]; norm1 += Math.pow(userContentMatrix[i][k], 2); norm2 += Math.pow(userContentMatrix[j][k], 2); } similarityMatrix[i][j] = dotProduct / (Math.sqrt(norm1) * Math.sqrt(norm2)); } } } return similarityMatrix; } // 生成推荐结果 private static Map<Integer, Double> getRecommendations(int userId, int[][] userLikes, double[][] similarityMatrix) { Map<Integer, Double> recommendations = new HashMap<>(); int[] userLikesArray = userLikes[userId]; for (int contentId = 1; contentId <= userLikesArray.length; contentId++) { if (userLikesArray[contentId - 1] == 0) { double weightedSum = 0.0; double weightSum = 0.0; for (int i = 0; i < userLikes.length; i++) { if (i != userId && userLikes[i][contentId - 1] == 1) { weightedSum += similarityMatrix[userId][i]; weightSum += similarityMatrix[userId][i]; } } if (weightSum > 0.0) { recommendations.put(contentId, weightedSum / weightSum); } } } return recommendations; } } ``` 该示例中,用户点赞数据用二维数组`userLikes`表示,计算用户-内容矩阵用函数`getUserContentMatrix`实现,计算用户之间的相似度矩阵用函数`getSimilarityMatrix`实现,生成推荐结果用函数`getRecommendations`实现。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值