EMAN

2025-12-07 0 430

EMAN

一个基于SSM框架与物品的协同过滤算法(ItemCF)的简单电子书推荐系统


界面截图

系统功能分析

推荐策略

因部分推荐算法需要使用用户的喜爱数据作为参数。若用户未登录采用游客的 推荐策略。若用户已登录就采用对登录用户的推荐策略。其中若登录用户在数据库中存 在感兴趣的分区记录的话就会增加一个来自你感兴趣的分区的推荐。
所以将推荐策略分为是否登录两种情况进行区别。

若用户未登录就采用对游客的用户评分显示策略。若用户已登录就采用对登录用户 的用户评分显示策略。其中若登录用户已经对当前详情页的电子书进行过评分,则显示 其评分记录。

�爬虫爬取策略

系统分析与设计

系统分析

如用例图所示,本系统中的基本用户分为 3 种。分别是游客、注册用户、管理员。
游客可以访问电子书推荐平台的首页、用户注册页面、查看电子书页面。注册用户比游 客多的功能在于可以对电子书进行评分与评论和由该用户预测兴趣度决定的电子书推 荐。而管理员则可以定期使用爬虫模块来更新电子书信息和使用统计模块更新分类统计 信息、电子书同现矩阵和电子书余弦相似度矩阵。

![User Case.png](/img/User Case.png)

推荐功能采用基于物品的协同过滤算法(ItemCF)

考虑到该算法存在冷启动问题。即对于新用户往往缺少评分数据从而导致根据用户预测兴趣度来进行推荐不能顺利进行。为解决该问题,我增加了使用余弦相似度矩阵 w 来直接进行相似电子书推荐的模块,方便未注册用户与新用户更好的获得推荐。而对于协同过滤算法存在的数据矩阵稀疏问题,即可能存在部分冷门电子书没有用户评分、电子书关联度不高、部分用户对电子书评分少等情况。为解决该问题,在首页上增加新书 推荐模块来更好的推荐没有人评分的电子书;在首页上增加根据用户选择感兴趣的分区 推荐模块以增加对具体的用户的兴趣来推荐电子书的拟合度。
值得注意的是,用户预测兴趣度推荐的电子书是在该算法所计算出来的余弦相似度矩阵 W 上使用公式计算而来。并且在计算的时候还需要用户喜爱的电子书作为输入参数。 而相似电子书推荐则是直接使用弦相似度矩阵 W 进行统计。这就意味着以上两个推荐需 要先完成电子书同现矩阵和电子书余弦相似度矩阵的计算才可以进行。因为以上两个矩阵计算量较大且每次计算都需要使用全部数据进行计算,所以在此设置成由管理员定期来执行。

系统设计

数据库表

  1. ebook:图书详情
  2. ratinglist:��评分评论列表
  3. subclassify:图书副分类
  4. user:用户信息
  5. classifymainstatistics:主分类统计
  6. recommendhomepage:�主页推荐书籍(待定)
  7. favorite:用户喜爱分类表
  8. matrixC:同现矩阵C与余弦相似度矩阵W表

类�和相关主要文件

符号 → 表示该类为定期手动运行模块

  1. com.controller
    1. EBookController:与图书相关的接口
    2. RatingListController:与用户评论、评分相关的接口
    3. StatisticsController:统计模块接口
    4. UserController:用户相关接口
    5. FavoriteController:用户喜爱分类接口
    6. ClassifyMainStatisticsController:分类统计相关接口
  2. com.dao
    1. DBAccess:用于获取SqlSession数据库连接
    2. EBookDao:提供图书表的访问
    3. RatingListDao:提供评分评论表的访问
    4. UserDao:提供用户表的访问
    5. FavoriteDao:用户喜爱分类表的访问
    6. ClassifyMainStatisticsDao:分类统计相关数据表的访问
    7. MatrixCDao:提供同现矩阵C与余弦相似度矩阵W的访问
    8. EBookMapper.xml:提供图书表的访问的sql语句
    9. RatingListMapper.xml:提供评分评论表的访问的sql语句
    10. UserMapper.xml:提供用户表的访问的sql语句
    11. ClassifyMainStatisticsMapper.xml:提供主分区统计表的访问的sql语句
    12. FavoriteMapper.xml:用户喜爱分类表的访问的sql语句
    13. ClassifyMainStatisticsMapper.xml:分类统计相关数据表的访问的sql语句
    14. MatrixCMapper.xml:提供同现矩阵C与余弦相似度矩阵W的访问的sql语句
  3. com.entity
    1. EBook:图书 -> ebook表
    2. RatingList:评论评分相 -> ratingList表
    3. ClassifyMainStatistics:主分区统计模块 -> classifyMainStatistics表
    4. User:用户 -> user表
    5. Favorite:用户喜爱分类实体 -> favorite表
    6. MatrixC:同现矩阵C与余弦相似度矩阵W -> matrixC表
  4. com.service
    1. EBookService:图书模块
    2. RatingListService:评论评分模块
    3. UserService:用户模块
    4. FavoriteService:用户喜爱分类模块
    5. ClassifyMainStatisticsService:分类统计模块
  5. com.statistics
    1. → ItemCollaborationFilter:用于计算基于物品的协同过滤推荐矩阵
    2. → StatisticsClassifyMain:统计主分类的各个分数的评分人数、平均评分、评分的方差
    3. → StatisticsRatingValue:图书评分信息统计:统计RatingValue表的数据并将统计结果写入到EBook表对应到图书上
  6. com.util
    1. ChartDataJsonCreater:提供将�数据转换为 Chart.js 插件专用的结构化 Json 数据格式
    2. JSONConverter:提供用于将 JavaBeans 对象直接转化为结构化 Json 数据
    3. RandomNumFactory:随机数生成工具类
  7. spider(爬虫包)
    1. BookInfoSpider:爬取电子书详情页并存入数据库
    2. → EBookListSpider:爬取全部电子书
    3. HttpURLConnectionUtil:
    4. → RatingValueListSpider:爬取豆瓣图书评分列表与用户评论(从数据库读取图书信息并爬取图书的评分列表)
  8. 配置文件
    1. SSM框架配置文件
      1. applicationContext.xml:Spring 框架配置文件
      2. jdbc.properties:jdbc 配置文件
      3. mybatis-config.xml:MyBatis 框架配置文件
      4. web.xml:项目配置文件
    2. 爬虫配置文件
      1. setting.properties:电子书爬虫(EBookListSpider)的配置文件
      2. user_spider_setting.properties:用户评论评分爬虫(RatingValueListSpider)的配置文件
      3. user_spider_exception.log:用户评论评分爬虫(RatingValueListSpider)的错误日志

页面列表

  1. index.jsp:主页
  2. head.jsp:导航栏
  3. error.jsp:错误信息页(待实现)
  4. success.jsp:成功操作信息跳转页
  5. /ebook(图书相关页面)
    1. list.jsp:主分类图书列表
    2. info.jsp:图书详情页
    3. serrchResult.jsp�:图书搜索结果页
  6. /user(用户相关页面)
    1. login.jsp:用户登录
    2. register.jsp:用户注册
    3. home.jsp:用户喜爱分类选择

系统实现

算法(基于物品的协同过滤算法(ItemCF))设计与实现

基于物品的协同过滤算法主要有两步:

  1. 计算物品之间的相似度。
  2. 根据物品的相似度和用户的历史行为给用户生成推荐列表。

设 N(i)是表示喜欢物品 i 的用户数。N(i)⋂N(j)表示同时喜欢物品 i 物品 j 的用户数。则物品 i 与物品 j 的相似度为:

但是上式有一个缺陷:当物品 j 是一个很热门的商品时,人人都喜欢,那么 wij
就会很接近于 1,即上式会让很多物品都和热门商品有一个很大的相似度,所以可以改进一下公式:

建立用户物品倒排表(设用大写字母表示用户,小写字母表示物品):

计算共现矩阵 C(共现矩阵 C 表示同时喜欢两个物品的用户数,是根据用户物品倒
排表计算出来的):

如图可知共现矩阵的对角线元素全为 0,且是实对称稀疏矩阵。 算法实现如下:

com.statistics.ItemCollaborationFilter

    /**
	 * 计算共现矩阵C
	 */
	private void computerMatrixC(){
		// 建立用户物品倒排表
		// 若用户对物品评分大于等于4则认为喜欢(出现)
		List<User> allUser = userDao.queryAllUser();
		for(int i = 0; i < allUser.size(); i++){ // 遍历全部用户
			// 获取一个用户的评分列表中>=4的评分记录
			List<RatingList> likeList = ratingListDao.selectRatingListByUidAndRatingValue(allUser.get(i).getUid(), 4);
			if(likeList.size() <= 1){ // 若用户只喜欢一本或不喜欢任何图书
				continue;
			}
			
			for(int j = 0; j < likeList.size(); j++){ // 计算likeList中两两出现的图书并写入同现矩阵C
				for(int k = j+1; k < likeList.size(); k++){
					int a = Integer.valueOf(likeList.get(j).getEid());
					int b = Integer.valueOf(likeList.get(k).getEid());
					// 生成key
					String key = null;
					if(a < b){
						key = a + \",\" + b;
					}else{
						key = b + \",\" + a;
					}
					// 检查key是否已经存在
					if(this.matrixC.get(key) != null){
						int value = this.matrixC.get(key);
						this.matrixC.put(key, value+1);
					}else{
						this.matrixC.put(key, 1);
					}
				}
			}
			System.out.println(\"[\"+df.format(new Date())+\"]\"+\"[已完成\"+i+\",共\"+allUser.size()+\"]:用户uid=\"+allUser.get(i).getUid()+\"的记录以计算完成,共\"+likeList.size()+\"本图书\"); 
		}
		
	}

统计可得每个物品出现的次数为:

计算余弦相似度矩阵 W:使用改进后的公式计算可得余弦相似度矩阵。

算法实现如下:

com.statistics.ItemCollaborationFilter

/**
	 * 计算余弦相似度矩阵W
	 * 计算方法:
	 * 使用矩阵C的每个value作为分子,key中的两个图书的喜欢人数的积开根号作为分母
	 */
	private Double computerMatrixW(String eida, String eidb, int value){
		DecimalFormat df = new DecimalFormat(\"#.##\");
		// 查询每个图书有多少人喜欢
        try {
            Statement statemenet = conn.createStatement();
            ResultSet rs = statemenet.executeQuery(\"select count(rid) from ratinglist where eid = \'\"+ eida +\"\' and ratingValue >= 4;\");
            rs.next();
            int likeANum = rs.getInt(\"count(rid)\");
            rs = statemenet.executeQuery(\"select count(rid) from ratinglist where eid = \'\"+ eidb +\"\' and ratingValue >= 4;\");
            rs.next();
            int likeBNum = rs.getInt(\"count(rid)\");
            if(likeANum == 0)
                likeANum = 1;
            if(likeBNum == 0)
                likeBNum = 1;
            // 开始计算
            Double answer = value*1.0/Math.sqrt(likeANum*likeBNum);
            // 精确到小数点后两位
            Double result = Double.parseDouble(df.format(answer));
            // 返回计算结果
            return result;
        } catch (SQLException e) {
            e.printStackTrace();
        }
		return null;
	}

最终推荐的是什么物品,是由预测兴趣度决定的。

物品j预测兴趣度 = 用户喜欢的物品i的兴趣度 × 物品i和物品j的相似度。

例如:某个用户喜欢物品 a、b 和 c。对其兴趣度分别为 1、2、2。那么物品 c、d、e、
f 的预测兴趣度分别为:

  • c:1×0.58+2×0 +2×0=0.58
  • d:1×1.0+2×1.44+2×0.45=4.78
  • e:1×0+2×0 +2×0=0.0
  • f:1×0+2×0.35+2×0=0.70

所以应当向该用户推荐物品 d。 算法实现如下:

	@Override
	public List<EBook> userRecommendedList(String uid) {
		// 获取用户喜爱图书列表
		List<RatingList> likeList = this.ratingListDao.selectRatingListByUidAndRatingValue(uid, 4);
		// debug
		System.out.println(\"uid=\"+uid+\"用户喜爱图书列表\");
		for(RatingList r : likeList){
			System.out.println(r.getEid()+\",\"+r.getRatingValue());
		}
		System.out.println(\"likeList.size=\"+likeList.size());
		// 定义计算用矩阵
		List<Item> matrix = new ArrayList<>();
		// 将用户喜爱的图书作为矩阵的列
		// 将与用户喜爱的图书同现的图书作为矩阵的行
		// 建立工作矩阵
		for(int i = 0; i < likeList.size(); i++){ // 遍历用户喜爱的图书
			RatingList temp = likeList.get(i);
			// 获取同现图书
			List<MatrixC> itemList = this.matrixCDao.selectMatrixCByEidAOrEidB(temp.getEid(), temp.getEid());
			for(int j = 0; j < itemList.size(); j++){
				MatrixC c = itemList.get(j);
				// 从matrixC的key中选出同现图书的eid
				String sEid = null;
				if(c.getEida().equals(temp.getEid())){
					sEid = c.getEidb();
				}else{
					sEid = c.getEida();
				}
				// 在行中查询同现图书是否存在
				if(matrix.indexOf(sEid) == -1){ // 若列中不存在
					double[] col = new double[likeList.size()];
					// 将同现图书所在行对应喜爱图书的数组值设为对应的余弦相似度*用户喜爱程度(4分为1,5分为2)
					col[likeList.indexOf(temp)] = c.getCos_similarity()*(temp.getRatingValue()-3);
					matrix.add(new Item(sEid, col)); // 增加行

				}else{ // 若列中存在
					// 则将同现图书所在行对应喜爱图书的数组值设为对应的余弦相似度*用户喜爱程度(4分为1,5分为2)
					matrix.get(matrix.indexOf(sEid)).col[likeList.indexOf(temp)] = c.getCos_similarity()*(temp.getRatingValue()-3);
				}
			}
		}
		// 计算预测兴趣度
		for(int i = 0; i < matrix.size(); i++){
			Item item = matrix.get(i);
			double interestValue = 0;
			for(int j =0; j < item.col.length; j++){
				interestValue += item.col[j];
			}
			matrix.get(i).interestValue = interestValue;
		}
		// 根据预测兴趣度进行排序
		Collections.sort(matrix);
		// 返回推荐图书列表
		List<EBook> resultList = new ArrayList<>();
		for(int i = 0; i < matrix.size() && i < 20; i++){ // 返回排前10的书
			if(matrix.get(i).interestValue > 0){
				EBook eBook = this.eBookDao.queryEBookByEid(matrix.get(i).eid);
				resultList.add(eBook);
				// debug
				System.out.println(matrix.get(i).eid+\",\"+eBook.getEname()+\",interestValue=\"+matrix.get(i).interestValue);
			}
		}
		return EBookServiceImpl.initEBookImgAddress(resultList);
	}

推荐模块使用爬虫爬取的数据作为输入,将计算结果输出到 matrixC 表中。整个计算过程分为 2 个阶段来进行。第一阶段计算出共现矩阵 C。第二阶段计算出两两出现的电子书的余弦相似度 w。而对于根据用户预测兴趣度来推荐这一功能因为用户喜爱电子书数据的实时性和总计算量太大,则采用用户在访问页面时实时进行计算。经过多次测试,用户平均等待时间在可以接受的范围。

附录:数据

因为豆瓣电影网页升级加入反爬措施,所以在此提供用于运行推荐算法的数据

  • 数据库数据
    • 见sql文件夹
  • 图片数据
    • 百度网盘
    • 提取码: ycqu

下载源码

通过命令行克隆项目:

git clone https://github.com/Sicmatr1x/EMAN.git

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

申明:本文由第三方发布,内容仅代表作者观点,与本网站无关。对本文以及其中全部或者部分内容的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。本网发布或转载文章出于传递更多信息之目的,并不意味着赞同其观点或证实其描述,也不代表本网对其真实性负责。

左子网 开发教程 EMAN https://www.zuozi.net/31420.html

常见问题
  • 1、自动:拍下后,点击(下载)链接即可下载;2、手动:拍下后,联系卖家发放即可或者联系官方找开发者发货。
查看详情
  • 1、源码默认交易周期:手动发货商品为1-3天,并且用户付款金额将会进入平台担保直到交易完成或者3-7天即可发放,如遇纠纷无限期延长收款金额直至纠纷解决或者退款!;
查看详情
  • 1、描述:源码描述(含标题)与实际源码不一致的(例:货不对板); 2、演示:有演示站时,与实际源码小于95%一致的(但描述中有”不保证完全一样、有变化的可能性”类似显著声明的除外); 3、发货:不发货可无理由退款; 4、安装:免费提供安装服务的源码但卖家不履行的; 5、收费:价格虚标,额外收取其他费用的(但描述中有显著声明或双方交易前有商定的除外); 6、其他:如质量方面的硬性常规问题BUG等。 注:经核实符合上述任一,均支持退款,但卖家予以积极解决问题则除外。
查看详情
  • 1、左子会对双方交易的过程及交易商品的快照进行永久存档,以确保交易的真实、有效、安全! 2、左子无法对如“永久包更新”、“永久技术支持”等类似交易之后的商家承诺做担保,请买家自行鉴别; 3、在源码同时有网站演示与图片演示,且站演与图演不一致时,默认按图演作为纠纷评判依据(特别声明或有商定除外); 4、在没有”无任何正当退款依据”的前提下,商品写有”一旦售出,概不支持退款”等类似的声明,视为无效声明; 5、在未拍下前,双方在QQ上所商定的交易内容,亦可成为纠纷评判依据(商定与描述冲突时,商定为准); 6、因聊天记录可作为纠纷评判依据,故双方联系时,只与对方在左子上所留的QQ、手机号沟通,以防对方不承认自我承诺。 7、虽然交易产生纠纷的几率很小,但一定要保留如聊天记录、手机短信等这样的重要信息,以防产生纠纷时便于左子介入快速处理。
查看详情

相关文章

猜你喜欢
发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务