生活远不止眼前的苦劳与奔波,它还充满了无数值得我们去体验和珍惜的美好事物。在这个快节奏的世界中,我们往往容易陷入工作的漩涡,忘记了停下脚步,感受周围的世界。让我们一起提醒自己,要适时放慢脚步,欣赏生活中的每一道风景,享受与家人朋友的温馨时光,发现那些平凡日子里隐藏的幸福时刻。因为,这些点点滴滴汇聚起来的,才是构成我们丰富多彩生活的本质。希望每个人都能在繁忙的生活中找到自己的快乐之源,不仅仅为了生存而工作,更为了更好的生活而生活。
送你张美图!希望你开心!
简介
协同过滤算法(简称CF)
在早期,协同过滤几乎等同于推荐系统。主要的功能是预测和推荐。协同过滤推荐算法分为两类,分别是:
(英文userCF)
基于用户的协同过滤算法(相似的用户可能喜欢相同物品);这个一般适合推荐新闻和皮皮虾之类的,数据跟人有很大关系,而且信息是每日都是更新的。如果你推荐购物这种,因为一个新建的用户可能购买的商品不足全量商品万分之1,商品数据量大,人对商品购买少,很难找到相似的人;随着用户和物品数量的增加,计算复杂度增加,所以需要这种更适合第二种算法。
(英文itemCF)
基于物品的协同过滤算法(这种方法通过分析物品之间的相似性,根据用户喜欢的物品,,推荐最大相似度其他物品。比如用户喜欢物品A,然后通过算法的出物品C和A的相似度极高,那么用户有可能喜欢物品C)。当然也有缺点:需要足够的用户-物品交互数据来找出物品之间的相似性。
算法详解
如果想知道算法细节可以看我的皮尔森相关系数介绍,这边只是代码级别
推荐系统算法 协同过滤算法详解(二)皮尔森相关系数-CSDN博客
算法使用
下面例子都是基于电影推荐系统做的,向用户推荐喜欢的电影。核心有几个字段,1用户id,2电影id,3是评分。如果你不是电影推荐而是其他,其实代码都一样,逻辑也都大差不差。
核心协同过滤算法类,不管你是基于用户,还是基于商品电影都需要,使用的是皮尔森相关系数
package com.tarzan.recommend.core;
import com.tarzan.recommend.dto.RelateDTO;
import org.assertj.core.util.Lists;
import java.util.*;
import java.util.stream.IntStream;
/**
* 核心算法
*
*/
public class CoreMath {
/**
* 计算相关系数并排序
* @param key
* @param map
* @return Map<Integer,Double>
*/
public static Map<Integer,Double> computeNeighbor(Integer key, Map<Integer,List<RelateDTO>> map,int type) {
Map<Integer,Double> distMap = new TreeMap<>();
List<RelateDTO> userItems=map.get(key);
map.forEach((k,v)->{
//排除此用户
if(!k.equals(key)){
//关系系数
double coefficient = relateDist(v,userItems,type);
//关系距离
// double distance=Math.abs(coefficient);
distMap.put(k,coefficient);
}
});
return distMap;
}
/**
* 计算两个序列间的相关系数
*
* @param xList
* @param yList
* @param type 类型0基于用户推荐 1基于物品推荐
* @return double
*/
private static double relateDist(List<RelateDTO> xList, List<RelateDTO> yList,int type) {
List<Double> xs= Lists.newArrayList();
List<Double> ys= Lists.newArrayList();
xList.forEach(x->{
yList.forEach(y->{
if(type==0){
if(x.getItemId().equals(y.getItemId())){
xs.add(x.getIndex());
ys.add(y.getIndex());
}
}else{
if(x.getUseId().equals(y.getUseId())){
xs.add(x.getIndex());
ys.add(y.getIndex());
}
}
});
});
return getRelate(xs,ys);
}
/**
* 方法描述: 皮尔森(pearson)相关系数计算
*
* @param xs x集合
* @param ys y集合
*/
public static double getRelate(List<Double> xs, List<Double> ys){
int n=xs.size();
//至少有两个元素
if (n<2) {
return 0D;
}
double Ex= xs.stream().mapToDouble(x->x).sum();
double Ey=ys.stream().mapToDouble(y->y).sum();
double Ex2=xs.stream().mapToDouble(x->Math.pow(x,2)).sum();
double Ey2=ys.stream().mapToDouble(y->Math.pow(y,2)).sum();
double Exy= IntStream.range(0,n).mapToDouble(i->xs.get(i)*ys.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 0D;
}
return numerator/denominator;
}
}
实体类
package com.tarzan.recommend.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 关系数据
*
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class RelateDTO {
/** 用户id */
private Integer useId;
/** 电影id */
private Integer itemId;
/** 评分 */
private Double index;
}
基于用户
入参是你要给推荐具体电影的当前用户id,
package com.tarzan.recommend.service;
import com.tarzan.recommend.core.ItemCF;
import com.tarzan.recommend.core.UserCF;
import com.tarzan.recommend.dto.ItemDTO;
import com.tarzan.recommend.dto.RelateDTO;
import java.util.List;
import java.util.stream.Collectors;
/**
* 推荐服务
*
*/
public class Recommend{
/**
* 方法描述: 猜你喜欢
*
* @param userId 用户id
*/
public static List<Integer> userCfRecommend(int userId){
List<RelateDTO> data= Sql获取不同用户对不同电影评分关系;
// 获取到推荐的电影id
List<Integer> recommendations = UserCF.recommend(userId, data);
return recommendations ;
}
}
package com.tarzan.recommend.core;
import com.tarzan.recommend.dto.RelateDTO;
import java.util.*;
import java.util.stream.Collectors;
/**
* 核心算法
*
* @since JDK1.8
*/
public class ItemCF {
/**
* 方法描述: 推荐电影id列表
*
* @param itemId 当前电影id
* @param list 用户电影评分数据
* @return {@link List<Integer>}
*/
public static List<Integer> recommend(Integer itemId, List<RelateDTO> list) {
//按物品分组
Map<Integer, List<RelateDTO>> itemMap=list.stream().collect(Collectors.groupingBy(RelateDTO::getItemId));
//获取其他物品与当前物品的关系值
Map<Integer,Double> itemDisMap = CoreMath.computeNeighbor(itemId, itemMap,1);
//获取关系最近物品
double maxValue=Collections.max(itemDisMap.values());
return itemDisMap.entrySet().stream().filter(e->e.getValue()==maxValue).map(Map.Entry::getKey).collect(Collectors.toList());
}
}
基于物品
入参是当前用户曾经评过高分的电影id,以此通过算法推荐和此电影相似度高的电影
package com.tarzan.recommend.service;
import com.tarzan.recommend.core.ItemCF;
import com.tarzan.recommend.core.UserCF;
import com.tarzan.recommend.dto.ItemDTO;
import com.tarzan.recommend.dto.RelateDTO;
import java.util.List;
import java.util.stream.Collectors;
/**
* 推荐服务
*
*/
public class Recommend{
/**
* 方法描述: 猜你喜欢
*
* @param itemId 电影id
*/
public static List<Integer> userCfRecommend(int userId){
List<RelateDTO> data= Sql获取不同用户对不同电影评分关系;
// 获取到推荐的电影id
List<Integer> recommendations = ItemCF.recommend(itemId, data);
return recommendations ;
}
}
总结
建议使用基于物品的协同过滤算法,基于物品协同过滤可以预先计算好物品间的相似度,在线查询要比基于用户快的多,且基于物品实际效果质量一般比基于用户高,但这个也不是绝对的,像是抖音这中视频都是最新更新的还是基于用户靠谱。
系数计算要保证,分数值至少每行两个首要条件,其次对比值要多,少的话就没有那么理想结果
------------------------------------------与正文内容无关------------------------------------
如果觉的文章写对各位读者老爷们有帮助的话,麻烦点赞加关注呗!作者在这拜谢了!
混口饭吃了!如果你需要Java 、Python毕设、商务合作、技术交流、就业指导、技术支持度过试用期。请在关注私信我,本人看到一定马上回复!
这是我全部文章所在目录,看看是否有你需要的,如果遇到觉得不对地方请留言,看到后我会查阅进行改正。
A乐神-CSDN博客
关注在文章左上角,作者信息处。
版权归原作者 A乐神 所有, 如有侵权,请联系我们删除。