传统推荐算法库使用--mahout初体验

文章目录

  • 前言
  • 环境准备
  • 调用
  • 混合
  • 总结

前言

郑重声明:本博文做法仅限毕设糊弄老师使用,不建议生产环境使用!!!
老项目缝缝补补又是三年,本来是打算直接重写写个社区然后给毕设使用的。但是怎么说呢,毕竟毕设的主角不是xx社区,这个社区是为我的编译器服务的,为了推广这个编译器,然后我才做了这个社区。然而不幸的是,开题答辩的时候,各位“专家”叫我以xx社区为主,听起来高级。于是没有办法,我只能强行做个社区,怎么做呢,照着以前写的社区抄,换个主题呗。但是重新写的成本太高了(一开始我是嫌弃白洞这个项目的部署成本比较高,因为里面确实集成了很多模块,有AI模块有传统微服务模块,当然开源的版本是没有这些东西的,毕竟还是要留点底裤的),但是重写实在难受,找了一圈想要找个开源的,结果都没有找到满意的,没办法,只能把白洞项目拿出来,然后做减法,加一个推荐系统。

推荐系统本来也是打算直接基于Java重写手写一个的,直接写个基于协同滤波的传统推荐算法。但是感谢开源,发现了个牛逼的框架mahout。这不就齐活了,我们直接糊弄糊弄毕设过去了就行了。借用某位大哥的话:你要搞清楚你的目的是什么,没有效益的事情少干。于是鄙人放弃了手写推荐系统,放弃了对netty重新封装。咱们有技术积累,但是没有能够产生实际效益的项目,所以不干,糊弄老师得了。

环境准备

这里的话,因为是糊弄毕设,所以我们是直接冷启动。用的是ItemCF,直接推荐博客。然后呢从100个用户里面数据里面推荐就行了,然后结果缓存起来,一天一推。多了没有,反正我用了这个玩意儿,现场查代码也没事,况且数据量根本就不够。

<!--        mahout推荐系统--><dependency><groupId>org.apache.mahout</groupId><artifactId>mahout-mr</artifactId><version>0.12.2</version></dependency>

导入依赖先。

创建记录表:

CREATE TABLE `user_article_operation` (`id` BIGINT(20) NOT NULL AUTO_INCREMENT,`create_time` DATETIME NOT NULL COMMENT '操作时间,我们默认抓取比较新的数据来进行统计',`userid` BIGINT(20) NULL DEFAULT NULL,`article_id` BIGINT(20) NULL DEFAULT NULL,`operation` INT(11) NULL DEFAULT NULL COMMENT '0-点赞,1-收藏,2-fork(不同的类型,不同的评分)',PRIMARY KEY (`id`) USING BTREE,INDEX `key` (`userid`, `article_id`, `operation`) USING BTREE
)
COMMENT='用户对文章的操作表'
COLLATE='utf8_unicode_ci'
ENGINE=InnoDB
;

这里的话,我使用的是mybatis-plus创建对应的dao和mapper(这里会使用到比较复杂的sql,得手写)


@Data
@TableName("user_article_operation")
public class BlogRe {@TableId(value = "id",type = IdType.AUTO)private Long id;private Long userid;private Long articleId;private Integer operation;private Date createTime;@TableField(exist = false)private Integer value;}

对应的xml文件是:


<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.huterox.whitehole.whiteholeblog.dao.BlogReDao"><!--sql--><select id="getAllUserPreference" resultType="com.huterox.whitehole.whiteholeblog.entity.surface.blogRe.BlogRe">SELECTuserid,article_id,SUM(CASE operation_typeWHEN 0 THEN 2WHEN 1 THEN 3WHEN 2 THEN 5else 0 END) AS "value"FROMuser_article_operationGROUP BY user_id,article_idlimit 100</select></mapper>

调用

基本的环境准备好了,我们就得调用了。
这里的我的逻辑是,当用户登录了有数据,那么我就直接推荐,如果没有那就继续走默认,也就是按照热度进行推荐。
在我的项目里面最终是定位到了这里:
在这里插入图片描述
具体的推荐逻辑是这里:
在这里插入图片描述
所以我们在这里重点关注这里的实现就可以:

@Service
public class BlogReServiceImpl implements BlogReService {@AutowiredBlogReDao blogReDao;@AutowiredBlogReUserIdDao blogReUserIdDao;@Overridepublic List<Long> recommend(String userId) throws TasteException {//注意这里我们限制了100个,我们从100个数据里面去拿到,然后做推荐List<BlogRe> userList = blogReDao.getAllUserPreference();//创建数据模型DataModel dataModel = this.createDataModel(userList);//获取用户相似程度UserSimilarity similarity = new UncenteredCosineSimilarity(dataModel);//获取用户邻居UserNeighborhood userNeighborhood = new NearestNUserNeighborhood(2, similarity, dataModel);//构建推荐器Recommender recommender = new GenericUserBasedRecommender(dataModel, userNeighborhood, similarity);//推荐2个BlogReUserId userMap = blogReUserIdDao.selectOne(new QueryWrapper<BlogReUserId>().eq("userid", userId));List<RecommendedItem> recommendedItems = recommender.recommend(userMap.getId(), 2);List<Long> itemIds = recommendedItems.stream().map(RecommendedItem::getItemID).collect(Collectors.toList());return itemIds;}private DataModel createDataModel(List<BlogRe> userArticleOperations) {FastByIDMap<PreferenceArray> fastByIdMap = new FastByIDMap<>();Map<Long, List<BlogRe>> map = userArticleOperations.stream().collect(Collectors.groupingBy(BlogRe::getUserid));Collection<List<BlogRe>> list = map.values();for (List<BlogRe> userPreferences : list) {GenericPreference[] array = new GenericPreference[userPreferences.size()];for (int i = 0; i < userPreferences.size(); i++) {BlogRe userPreference = userPreferences.get(i);GenericPreference item = new GenericPreference(userPreference.getUserid(), userPreference.getArticleId(), userPreference.getValue());array[i] = item;}fastByIdMap.put(array[0].getUserID(), new GenericUserPreferenceArray(Arrays.asList(array)));}return new GenericDataModel(fastByIdMap);}}

这里写得很清楚了,当然具体的算法原理也不难,可以去翻翻我往期的博文。有Python手撸的版本。加上几个数据源设配器也能直接用了。核心算法原理很简单,不会就问GPT,只要数学没啥问题就懂,不懂,那就直接调用API也挺好。

这里注意的是:
这里要求用户ID是Long类型。
在这里插入图片描述
所以如果你和我的项目一样用户ID用的不是雪花这种算法,而是UUID,那么你得搞个中间的转换表。我这里没辙,所以只能强行加一个转换表:
在这里插入图片描述
当然我们这里还得记录操作。

        //记录一下操作BlogReUserId userMap = blogReUserIdDao.selectOne(new QueryWrapper<BlogReUserId>().eq("userid", userid));if(userMap==null){BlogReUserId blogReUserId = new BlogReUserId();blogReUserId.setUserid(userid);blogReUserIdDao.insert(blogReUserId);}BlogRe blogRe = new BlogRe();assert userMap != null;blogRe.setUserid(userMap.getId());blogRe.setArticleId(blogid);blogRe.setCreateTime(new Date());blogRe.setOperation(0);blogReDao.insert(blogRe);

这里看实际情况,反正我这就先这样操作了。

混合

之后的话就是做混合了
在我这里是直接这样了:

    @Overridepublic PageUtils queryPageWithRem(Map<String, Object> params) throws Exception {//这里是携带推荐系统的PageUtils pageUtils = this.queryPage(params);if(params.get("rem").equals("1")){//触发满足使用推荐系统条件使用推荐系统if (params.get("userid")!=null){List<Long> blogIds = blogReService.recommend((String) params.get("userid"));List<BlogEntity> blogEntityList = this.list(new QueryWrapper<BlogEntity>().in("blogid", blogIds));//这个是按照热度推荐的List<BlogEntity> list = (List<BlogEntity>) pageUtils.getList();//将两者混合list.addAll(blogEntityList);pageUtils.setPageSize(list.size());pageUtils.setTotalCount(list.size());}}return pageUtils;}

数据不够的话可能推荐的数据是空的,所以得混合。之后缓存的话,是我直接在这个项目当中使用了SpringCache。当然最近搞项目的时候,我自己直接基于SpringAop写了个缓存注解实现,项目要求比较灵活,直接手写一个快。

总结

新年快乐~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/467003.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

网络安全的今年:量子、生成人工智能以及 LLM 和密码

尽管世界总是难以预测&#xff0c;但网络安全的几个强劲趋势表明未来几个月的发展充满希望和令人担忧。有一点是肯定的&#xff1a;2024 年将是非常重要且有趣的一年。 近年来&#xff0c;人工智能&#xff08;AI&#xff09;以令人难以置信的速度发展&#xff0c;其在网络安全…

Pandas自定义函数的多面手应用(pipe、apply、map、applymap、agg)【第76篇—Pandas自定义函数】

Pandas自定义函数的多面手应用&#xff08;pipe、apply、map、applymap、agg&#xff09; Pandas是Python中用于数据分析和处理的强大库&#xff0c;提供了丰富的功能和灵活性。在实际数据处理中&#xff0c;经常会遇到需要自定义函数来进行特定的操作。本文将深入探讨Pandas中…

真、开源LLM-OLMo

论文&#xff1a;https://arxiv.org/pdf/2402.00838.pdf Weights https://huggingface.co/allenai/OLMo-7B Code https://github.com/allenai/OLMo Data https://huggingface.co/datasets/allenai/dolma Evaluation https://github.com/allenai/OLMo-Eval Adaptation http…

ubuntu快速安装miniconda

ubuntu快速安装miniconda 环境 ubuntu.22.04 显卡 RTX 3050 关于选择Miniconda还是Anaconda的问题&#xff0c;Anaconda安装包比较大&#xff0c;耗时比较长&#xff0c;如果你是绝对的初学者&#xff0c;选择Anaconda会比较稳妥一些&#xff1b;否则建议你还是选择Miniconda安…

P1928 外星密码

网址如下&#xff1a; P1928 外星密码 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) C的string真的是太好用辣&#xff01; 思路就是用一个函数来递归翻译 代码如下&#xff1a; #include<iostream> #include<string> #include<cctype> using namespace…

python使用 sqlalchemy连接数据库帮助类

import mysql.connectorclass MySqlHelper(object):"""操作数据库帮助类"""def __init__(self):#self.host "localhost"#self.user "root"#self.password "xinshiyun123"#self.database "deliverunion_c…

【Java程序设计】【C00261】基于Springboot的休闲娱乐代理售票系统(有论文)

基于Springboot的休闲娱乐代理售票系统&#xff08;有论文&#xff09; 项目简介项目获取开发环境项目技术运行截图 项目简介 这是一个基于Springboot的休闲娱乐代理售票系统 本系统分为系统功能模块、管理员功能模块以及用户功能模块。 系统功能模块&#xff1a;休闲娱乐代理…

【蓝桥杯嵌入式】新建工程 | 点亮LED | LCD配置

目录 源代码 硬件资源 产品图片 硬件布局 资源配置表 跳线 下载方式 新建工程 点亮LED code 函数调用 LED初始化 Delay点灯 流水灯 积累流水灯 整合效果 LCD移植 lcd.c lcd.h fonts.h LCD初始化 main.c预览 闲话 源代码 网址&#xff1a;后续会上传…

Stable Diffusion 模型下载:majicMIX sombre 麦橘唯美

本文收录于《AI绘画从入门到精通》专栏,专栏总目录:点这里。 文章目录 模型介绍生成案例案例一案例二案例三案例四案例五案例六案例七案例八案例九案例十

大数据工具之Trino

大数据工具之Trino 简介 不少人没有听说过Trino&#xff0c;但绝大多数人都听说过Presto&#xff0c;一个基于JVM的MPP计算引擎&#xff0c;Presto是一个高性能的、分布式的大数据SQL查询引擎。 诞生于Facebook(脸书)&#xff0c;扬名于Linux基金会&#xff01; 官网&#…

海里定理例题

1. lim ⁡ x − > 0 s i n ( 1 x ) \lim\limits_{x ->0}sin(\frac{1}{x}) x−>0lim​sin(x1​)的极限不存在 取数列f(x), x n 1 2 n Π − Π 2 x_n\frac{1}{2nΠ-\frac{Π}{2}} xn​2nΠ−2Π​1​和 y n 1 2 n Π Π 2 y_n\frac{1}{2nΠ\frac{Π}{2}} yn​2nΠ2…

算法学习——LeetCode力扣二叉树篇5

算法学习——LeetCode力扣二叉树篇5 513. 找树左下角的值 513. 找树左下角的值 - 力扣&#xff08;LeetCode&#xff09; 描述 给定一个二叉树的 根节点 root&#xff0c;请找出该二叉树的 最底层 最左边 节点的值。 假设二叉树中至少有一个节点。 示例 示例 1: 输入: r…