日撸代码300行:第54天(基于 M-distance 的推荐)

news2024/11/16 9:27:10

代码来自闵老师”日撸 Java 三百行(51-60天)“,链接:日撸 Java 三百行(51-60天,kNN 与 NB)_闵帆的博客-CSDN博客

算法是基于M-distance的推荐,通过用户评分矩阵对用户进行电影推荐。论文为Mei Zheng, Fan Min, Heng-Ru Zhang, Wen-Bin Chen, Fast recommendations with the M-distance, IEEE Access 4 (2016) 1464–1468。论文可点击进行下载。数据集也可以通过闵老师原文的链接进行下载。数据集的名称为movielens-943u1682m.txt.

代码在基于条目推荐的时候使用了一个技巧,降低了算法的复杂度。因为数据是按照用户存储的,所以对于同一用户,存储的空间就是从userStartingIndices[tempUser]开始,至userStartingIndices[tempUser + 1]结束。这样在推荐计算的时候,就不需要遍历所有数据集,只需便利该用户对应的数据条数。

通过今天的代码又学到了一点儿,算法的实际思路不一定与底层内存存储的方式一一对应。例如,评分表里评分为0的条目,在算法实现的时候不需要管。矩阵(如下图,原论文中的图片)中缺失的数据,压根就没有读入条目,所以算邻居个数的时候不用剔除该用户评分为0的条目。自己刚开始理解代码的时候就整迷糊了。

第一个构造函数里,while循环中的第一个for循环中compressedRatingMatrix[i][0]的值是用户名,所以相当于tempUserTotalScore[]的地址是用户名,里面存的是该用户的总分。刚开始理解成指的是每一条数据的index,后来仔细想了想,发现compressedRatingMatrix[i][0]存的是compressedRatingMatrix[i][0]。是一个值,是用户名,比如User1或者User2等。最终数组的返回值都指向这个用户名,有点儿寻址的味道。

整个算法实现的java代码如下:

package machinelearning.knn;

/**
 * Recommendation with M-distance.
 * @author WX873
 */
import java.io.*;
import java.util.PrimitiveIterator.OfDouble;


public class MBR {
	
	/**
	 * Default rating for 1-5 points.
	 */
	public static final double DEFAULT_RATING = 3.0;
	
	/**
	 * The total number of users.
	 */
	private int numUsers;
	
	/**
	 * The total number of items.
	 */
	private int numItems;
	
	/**
	 * The total number of ratings (non-zero values)
	 */
	private int numRatings;
	
	/**
	 * The predictions.
	 */
	private double[] predictions;
	
	/**
	 * Compressed rating matrix. User-item-rating triples.
	 */
	private int[][] compressedRatingMatrix;
	
	/**
	 * The degree of users (how many item he has rated).
	 */
	private int[] userDegrees;
	
	/**
	 * The average rating of the current user.
	 */
	private double[] userAverageRatings;
	
	/**
	 * The degree of items (how many users has rated this item).
	 */
	private int[] itemDegrees;
	
	/**
	 * The average rating of the current item.
	 */
	private double[] itemAverageRatings;
	
	/**
	 * The first user start from 0. Let the first user has x ratings, the second
	 * user will start from x. The start index x is for dataset's item.
	 */
	private int[] userStartingIndices;
	
	/**
	 * Number of non-neighbor objects.
	 */
	private int numNonNeighbors;
	
	/**
	 * The radius (delta) for determining the neighborhood.
	 */
	private double radius;
	
	/**
	 * Construct the rating matrix.
	 * 
	 * @param paraFilename   The rating filename.
	 * @param paraNumUsers   Number of users
	 * @param paraNumItems   Number of items
	 * @param paraNumRatings  Number of ratings
	 * @throws Exception
	 */
	public MBR(String paraFilename, int paraNumUsers, int paraNumItems, int paraNumRatings) throws Exception {
		// Step 1. Initialize these arrays
		numItems = paraNumItems;
		numUsers = paraNumUsers;
		numRatings = paraNumRatings;
		
		userDegrees = new int [numUsers];
		userStartingIndices = new int[numUsers + 1];
		userAverageRatings = new double[numUsers];
		itemDegrees = new int[numItems];
		compressedRatingMatrix = new int[numRatings][3];
		itemAverageRatings = new double[numItems];
		
		predictions = new double[numRatings];
		
		// Step 2. Read the data file.
		File tempfile = new File(paraFilename);
		if (!tempfile.exists()) {
			System.out.println("File " + paraFilename + " does not exists.");
			System.exit(0);
		}//of if
		BufferedReader tempBufReader = new BufferedReader(new FileReader(tempfile));
		String tempString;
		String[] tempStrArray;
		int tempIndex = 0;
		userStartingIndices[0] = 0;
		userStartingIndices[numUsers] = numRatings;
		while ((tempString = tempBufReader.readLine()) != null) {
			// Each line has three values
			tempStrArray = tempString.split(",");
			compressedRatingMatrix[tempIndex][0] = Integer.parseInt(tempStrArray[0]);
			compressedRatingMatrix[tempIndex][1] = Integer.parseInt(tempStrArray[1]);
			compressedRatingMatrix[tempIndex][2] = Integer.parseInt(tempStrArray[2]);
			
			userDegrees[compressedRatingMatrix[tempIndex][0]]++;
			itemDegrees[compressedRatingMatrix[tempIndex][1]]++;
			
			if (tempIndex > 0) {
				// Starting to read the data of a new user.
				if (compressedRatingMatrix[tempIndex][0] != compressedRatingMatrix[tempIndex - 1][0]) {
					userStartingIndices[compressedRatingMatrix[tempIndex][0]] = tempIndex;
				}//of if
			}//of if
			tempIndex++;
		}//of while
		tempBufReader.close();
		
		double[] tempUserTotalScore = new double[numUsers];
		double[] tempItemTotalScore = new double[numItems];
		for (int i = 0; i < numRatings; i++) {
			tempUserTotalScore[compressedRatingMatrix[i][0]] += compressedRatingMatrix[i][2];  //compressedRatingMatrix[i][0]的值是用户名,所以相当于tempUserTotalScore[]的地址是用户名,里面存的是该用户的总分
			tempItemTotalScore[compressedRatingMatrix[i][1]] += compressedRatingMatrix[i][2];  //同上
		}//of for i
		
		for (int i = 0; i < numUsers; i++) {
			userAverageRatings[i] = tempUserTotalScore[i]/userDegrees[i];
		}//of for i
		for (int i = 0; i < numItems; i++) {
			itemAverageRatings[i] = tempItemTotalScore[i]/itemDegrees[i];
		}//of for i
	}//of the first constructor
	
	/**
	 * *****************************************
	 * Set the radius (delta).
	 * @param paraRadius
	 * 		The given radius.
	 * *****************************************
	 */
	public void setRadius(double paraRadius) {
		if (paraRadius > 0) {
			radius = paraRadius;
		}else {
			radius = 0.1;
		}//of if
		
	}//of setRadius
	
	/*
	 * ***************************************************************************
	 * Leave-one-out prediction item based. The predicted values are stored in predictions.
	 * 
	 * @see predictions
	 * ***************************************************************************
	 */
	public void leaveOneOutPredictionBsaedItem() {
		double tempItemAverageRating;
		//Make each line of the code shorter.
		int tempUser, tempItem, tempRating;
		System.out.println("\r\nLeaveOneOutPrediction for radius " + radius);
		
		numNonNeighbors = 0;
		for (int i = 0; i < numRatings; i++) {
			tempUser = compressedRatingMatrix[i][0];
			tempItem = compressedRatingMatrix[i][1];
			tempRating = compressedRatingMatrix[i][2];
			
			//Step 1. Recompute average rating of the current item.
			tempItemAverageRating = (itemAverageRatings[tempItem] * itemDegrees[tempItem] - tempRating)/(itemDegrees[tempItem] - 1);
			
			// Step 2. Recompute neighbors, at the same time obtain the ratings of neighbors.
			int tempNeighbors = 0;
			double tempTotal = 0;
			int tempComparedItem;
			for (int j = userStartingIndices[tempUser]; j < userStartingIndices[tempUser + 1]; j++) {
				tempComparedItem = compressedRatingMatrix[j][1];
				if (tempItem == tempComparedItem) {
					continue; //Ignore itself.
				}//of if
				
				if (Math.abs(tempItemAverageRating - itemAverageRatings[tempComparedItem]) < radius) {
					//矩阵中缺失的数据,压根就没有读入条目,所以算邻居个数的时候不用剔除该用户评分为0的条目。
					tempTotal += compressedRatingMatrix[j][2];
					tempNeighbors++;
				}//of if 
			}//of for j
			
			//Step 3. Predict as the average value of neighbors.
			if (tempNeighbors > 0) {
				predictions[i] = tempTotal/tempNeighbors;
			}else {
				predictions[i] = DEFAULT_RATING;
				numNonNeighbors++;
			}//of if
		}//of for i
	}//of leaveOneOutPredictionBsaedItem
	
	
	/****************************************************************
	 * Compute the MAE based on the deviation of each leave-one-out.
	 * 
	 * @return
	 * @throws Exception
	 * **************************************************************
	 */
	public double computeMAE() throws Exception {
		double tempTotalError = 0;
		for (int i = 0; i < predictions.length; i++) {
			tempTotalError += Math.abs(predictions[i] - compressedRatingMatrix[i][2]);
		}//of for i
		
		double tempAverage = tempTotalError / predictions.length;
		return Math.sqrt(tempAverage);
	}//of computeRSME
	
	/****************************************************************
	 * Compute the RSME based on the deviation of each leave-one-out.
	 * 
	 * @return
	 * @throws Exception
	 * **************************************************************
	 */
	public double computeRSME() throws Exception {
		double tempTotalError = 0;
		for (int i = 0; i < predictions.length; i++) {
			tempTotalError += (predictions[i] - compressedRatingMatrix[i][2]) * (predictions[i] - compressedRatingMatrix[i][2]);
		}//of for i
		
		double tempAverage = tempTotalError / predictions.length;
		return Math.sqrt(tempAverage);
	}//of computeRSME
	
	/***
	 * **************************************************
	 * The entrance of the program.
	 * @param args
	 * **************************************************
	 */
	public static void main(String args[]) {
		try {
			MBR tempRecommender = new MBR("E:/Datasets/UCIdatasets/temp/movielens-943u1682m.txt", 943, 1682, 100000);
			
			for (double tempRadius = 0.2; tempRadius < 0.6; tempRadius += 0.1) {
				tempRecommender.setRadius(tempRadius);
				
				tempRecommender.leaveOneOutPredictionBsaedItem();
				
				double tempMAE = tempRecommender.computeMAE();
				double tempRSME = tempRecommender.computeRSME();
				
				System.out.println("Radius = " + tempRadius + ", MAE = " + tempMAE + ", RSME = " + tempRSME
						+ ", numNonNeighbors = " + tempRecommender.numNonNeighbors);
			}//of for tempRadius
		} catch (Exception e) {
			// TODO: handle exception
			System.out.println(e);
		}//of try
	}//of main

}//MBR

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如果你在选型低代码平台,可以从这5个角度去分析抉择

研究低代码平台已有3年&#xff0c;也算是个低代码资深用户了&#xff0c;很多企业面临低代码选型上的困难&#xff0c;选平台容易&#xff0c;换平台难。下面基于个人理解给大家做一份千字的注意事项&#xff01;希望对大家在选型低代码方面有一定帮助。最终&#xff0c;正确且…

[AWD靶场搭建]

文章目录 [AWD靶场搭建]前言AWD平台搭建靶机搭建Cadinal添加靶机 连接Asteroid大屏默认ssh账号密码参考 [AWD靶场搭建] 前言 觉得好玩搭建了一下AWD靶场&#xff0c;使用了vidar-team编写的 Cardinal AWD平台搭建 这里我是在kali搭建的&#xff0c;所以我下载了这个压缩包&…

centos7搭建k8s环境并部署springboot项目

之前看了很多文章&#xff0c;都是部署后一直报错&#xff0c;百度解决后下次又忘了&#xff0c;这次决定把从头到尾的过程记录下来方便下次再看&#xff0c;部署参考文章尚硅谷Kubernetes&#xff08;k8s&#xff09;视频学习笔记_尚硅谷k8s笔记_溯光旅者的博客-CSDN博客 1、…

13年测试老鸟,接口性能测试总结整理,据说这是全网最全的...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 性能测试按照不同…

支持多种通信方式和协议方便接入第三方服务器或云平台

2路RS485串口是一种常用的通信接口&#xff0c;可以支持Modbus Slave协议&#xff0c;并可接入SCADA、HMI、DSC、PLC等上位机。它还支持Modbus RTU Master协议&#xff0c;可用于扩展多达48个Modbus Slave设备&#xff0c;如Modbus RTU远程数据采集模块、电表、水表、柴油发电机…

GAN论文精读

标题:Generative Adversarial Nets 摘要: 简写:作者提出了一个framework通过一个对抗的过程&#xff0c;在这里面会同时训练两个模型。 第一个模型为生成模型G&#xff0c;是用来抓住整个数据的分布 第二个模型为辨别模型D&#xff0c;是用来估计一个样本是否从G中产生。 …

BD Biosciences通过使用Liquid UI优化SAP QM,节省了80%的处理时间,提高了 95% 的数据准确性

背景 BD 生物科学公司成立于 1897 年&#xff0c;致力于改善患者的治疗效果&#xff0c;并在一个多世纪的时间里始终坚持这一理念&#xff0c;现已涉足诊断、生物科学以及各种医疗设备和仪器系统。 挑战 手动验证数据 原因&#xff1a;使用非自动程序演示和验证数据&#xff0c…

FRR+VPP

安装 三者的结合&#xff0c;实际上编译安装好就行了&#xff0c;不需要做任何代码上的修改&#xff0c;只需要安装和配置&#xff0c;然后你就有了一台路由器。 FRRouting使用frr-8.5.2版本&#xff0c;VPP使用23.06版本&#xff0c;DPDK和lcpng是VPP的插件&#xff0c;安装…

【CAS6.6源码解析】源码构建时-默认service配置不生效解决方案

CAS6的源码提供了默认的HTTPSandIMAPS-10000001.json配置用于授权所有的https和imaps服务&#xff0c;但是当添加JsonServiceRegistry模块启动后&#xff0c;会发现service是没有被注册的&#xff0c;是由于json路径引起的错误&#xff0c;可以把路径修改为绝对路径以解决此问题…

在idea中添加try/catch的快捷键

在idea中添加try/catch的快捷键 在idea中添加try/catch的快捷键 ctrlaltt 选中想被try/catch包围的语句&#xff0c;同时按下ctrlaltt&#xff0c; 出现下图 选择try/catch即可。

QTDAY3

闹钟 头文件 #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QTimerEvent> //定时器事件处理函数 #include <QTime> //时间类 #include <QString> #include <QPushButton> #include <QTextToSpeech> #include …

spring中存储和获取bean对象

文章目录 1. 存储bean对象1.1 使用配置文件存储bean对象1.2 使用五大类注解存储bean对象1.2.1 类注解1.2.2 五大类注解的作用1.2.3 方法注解 2.获取bean对象2.1 属性注入2.2 构造器注入2.3 getter注入2.4 注入对象的时候有spring中有多个bean对象怎么办2.4.1 将类中属性的名字&…

ava版知识付费平台免费搭建 Spring Cloud+Spring Boot+Mybatis+uniapp+前后端分离实现知识付费平台

提供私有化部署&#xff0c;免费售后&#xff0c;专业技术指导&#xff0c;支持PC、APP、H5、小程序多终端同步&#xff0c;支持二次开发定制&#xff0c;源码交付。 Java版知识付费-轻松拥有知识付费平台 多种直播形式&#xff0c;全面满足直播场景需求 公开课、小班课、独…

数据结构基础:2.顺序表。

顺序表的介绍和实现 一.线性表1.基本概念&#xff1a; 二.顺序表&#xff1a;1.基本概念&#xff1a;分类&#xff1a;1.静态顺序表&#xff1a;分类&#xff1a;2.动态顺序表&#xff1a;2.动态顺序表的功能接口的实现&#xff1a;0.顺序表打印&#xff1a;1.初始化和删除&…

功率放大器在电光调制中的应用有哪些

电光调制是一种利用光电效应将电信号转化为光信号的技术。在实现电光调制的过程中&#xff0c;功率放大器作为一个重要的组件&#xff0c;具有对输入电信号进行放大和控制的功能。本文将介绍功率放大器的基本原理、特点以及在电光调制中的应用。 基本原理 功率放大器是一种能够…

新建Git仓库,将本地文件上传至仓库

1、新建仓库&#xff0c;勾选初始化仓库 2、复制仓库链接 3、打开本地文件目录 右键选择 Git Bash Here 打开命令窗口 4、依次按照下面的步骤&#xff08;*如果报错&#xff0c;看原目录下是否存在 .git 需要删除&#xff09; // 生成git文件 git init // 把文件加入暂存区 g…

DeepSpeed-MoE:训练更大及更复杂的混合专家网络

这是微软发布在2022 ICML的论文&#xff0c;MoE可以降低训练成本&#xff0c;但是快速的MoE模型推理仍然是一个未解决的问题。所以论文提出了一个端到端的MoE训练和推理解决方案DeepSpeed-MoE&#xff1a;它包括新颖的MoE架构设计和模型压缩技术&#xff0c;可将MoE模型大小减少…

Java基础篇

前言&#xff1a;此篇博客笔者参考了JavaGuide、三分恶等博主的八股文&#xff0c;结合Chat老师和自己的理解&#xff0c;整理了一篇关于Java基础的八股文。全篇图文并茂&#xff0c;每个知识点都有细致描述&#xff0c;详略得当&#xff0c;理解通透。希望对各位读者有所帮助&…

Python(四十二)流程控制语句——continue

❤️ 专栏简介&#xff1a;本专栏记录了我个人从零开始学习Python编程的过程。在这个专栏中&#xff0c;我将分享我在学习Python的过程中的学习笔记、学习路线以及各个知识点。 ☀️ 专栏适用人群 &#xff1a;本专栏适用于希望学习Python编程的初学者和有一定编程基础的人。无…

reids集群删除某个集群节点

由于服务期调整&#xff0c;已经配好的集群&#xff0c;要删除几个节点&#xff0c;利用命令redis-cli --cluster del-node ip:prot 结果删除失败了&#xff0c;报错&#xff1a; ERR Unknown subcommand or wrong number of arguments for del-node。help查了下&#xff0c;没…