机器学习之基于M-distance 的推荐:日撸Java三百行day54-55

一、什么是M-distance

M-distance是MBR(Memory-Based Recommenders)System中使用的一种评分预测机制。该算法来源于论文 Mei Zheng, Fan Min, Heng-Ru Zhang, Wen-Bin Chen, Fast recommendations with the M-distance, IEEE Access 4 (2016) 1464–1468。下载地址在此

二、M-distance的基本思想

在M-distance算法中同样也引入了“邻居”的概念。令项目j的平均分为r_{\cdot j},那么本算法第j个项目关于第i个用户的“邻居”的数学表达式为:

N_{i,j}=\left \{ 1\leq {j}'\leq m|{j}'\neq j,p_{i{j}'}\neq 0|\left | \overline{r_{\cdot j}}-\overline{r_{\cdot {j}'}} \right | < \epsilon \right \}

与KNN算法不同,该算法中的邻居不是由K控制,而是通过计算不同数据之间的差值来寻找邻居。即:距离小于radius(\epsilon)的都为邻居。radius一般人为设定。第i个用户对第j个项目的评分预测为:

p_{ij}=\frac{​{\textstyle \sum_{​{j}'\in N_{ij} }^{}} r_{i{j}' } }{\left | N_{ij} \right |}

 

机器学习之基于M-distance 的推荐:日撸Java三百行day54-55_第1张图片

如图所示,要预测用户u_{0}m_{2}数据,就需要找到其相应的邻居。利用与m_{2}数据相似数据进行推荐,得到一个预测值。首先利用相似度计算找到邻居,而相似度的计算,则是通过从m_{0}m_{5}每一个数据的平均值。找到与m_{2}的平均值相差小于radius的数据作为其邻居。 如:找到m_{1}m_{3}作为其邻居。将m_{1}m_{3}的值相加并求均值,就得到了m_{2}的预测值。

三、什么是leave-one-out

leave-one-out(LOO),亦称为留一法,是机器学习领域的一种交叉验证的方法。顾名思义,可以理解为:只留下一个。在这种验证方法中,我们可以将数据集分为10份,使用其中的9份进行训练,而将留下的那一份作为测试集。该过程可以重复10次,每次使用的测试数据都不同。这使得每次的测试和验证都可能会有不同的测试结果。

留一法的优点在于十分的公平,能够将算法运用于每一个数据上进行测试,并得到相应的结果。同时这种方法也具有确定性,有着明确的方向。

但是,留一法的缺点也显而易见。机器学习的数据量十分庞大,若在一个不是那么高效的算法中使用留一法,那么算法的时间开销很大。因此,留一法对算法效率有着较高的要求。一般在高效算法中使用这种方法,本文的M-distance算法可以使用。

四、算法的基本流程及操作

在介绍算法流程之前,先分析数据集。本次学习使用了电影评分表数据集。数据量为100000,下载地址在此。数据集共三种属性:user、movie、score。如:[0,64,4]表示用户0观看了64号电影,给出的评价为4分。

User Movie Score
0 64 4
0 65 4
... ... ...
942 1329 3

以电影评分表数据集处理为例,阐述M-distance算法的基本流程。

①初始化全局变量,并读取数据文件,将数据文件中的内容填充到全局变量或矩阵中。

//初始化全局变量
	public static final double DEFAULT_RATING=3.0;
	private int numUsers;//用户数量
	private int numItems;//项目数量
	private int numRating;//评分数量
	private double[] predictions;//预测
	private int[][] compressedRatingMatrix;//压缩的评分矩阵
	private double[] userAverageRatings;//用户平均评分
	private int[] userDegrees;//当前用户数量
	private int[] itemDegrees;//项目评分
	private double[] itemAverageRatings;//项目平均评分
	private int[] userStartingIndices;//开始下标
	private int numNonNeighbors;//不存在邻居的数量
	private double radius;//判断是否为邻居的关键变量,为人工设定
	public MBR(String paraFilename,int paraNumUsers,int paraNumItems,int paraNumRatings)throws Exception{
		//第一步 初始化
		numItems=paraNumItems;
		numUsers=paraNumUsers;
		numRating=paraNumRatings;
		
		userDegrees=new int[numUsers];
		userStartingIndices=new int[numUsers+1];
		userAverageRatings=new double[numUsers];
		itemDegrees=new int[numItems];
		compressedRatingMatrix=new int[numRating][3];
		itemAverageRatings=new double[numRating];
		
		System.out.println("Reading " + paraFilename);
		
		//第二步 读取数据文件
		File tempFile=new File(paraFilename);
		if(!tempFile.exists()) {//不存在
			System.out.println("File " + paraFilename + "does not exist.");
			System.exit(0);
		}//of if
		BufferedReader tempBufReader=new BufferedReader(new FileReader(tempFile));
		String tempString;
		String[] tempStrArray;
		int tempIndex=0;
		userStartingIndices[0]=0;
		userStartingIndices[numUsers]=numRating;
		while((tempString=tempBufReader.readLine())!=null) {//按行读取,行某行为空时停止
			//每一行有三个值
			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) {//选择下一行数据
				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

 ②设置radius,用于后续找邻居操作。

	public void setRadius(double paraRadius) {
		if(paraRadius>0) {
			radius=paraRadius;
		}
		else {
			radius=0.1;
		}
	}//of setRadius

 ③利用留一法(leave-one-out)进行预测,存储预测值。

	public void leaveOneOutPrediction() {//遮住一个数值,来进行预测
		double tempItemAverageRating;//评估均值
		int tempUser,tempItem,tempRating;
		System.out.println("\r\nleaveOneOutPrediction for radius " + radius);
		numNonNeighbors=0;//非邻居个数
		for(int i=0;i0) {//若存在邻居
				predictions[i]=tempTotal/tempNeighbors;//预测值为总分数除以邻居数目
			}
			else {
				predictions[i]=DEFAULT_RATING;//预测值为默认值
				numNonNeighbors++;
			}//of if
		}//of for i
	}

④计算MAE 与RSME这两个评估指标来反映出算法预测的准确度。

public double computeMAE()throws Exception{//MAE代表着绝对误差
		double tempTotalError=0;
		for(int i=0;i

⑤将radius从0.2到0.6依次递增,按tempRadius值寻找邻居并预测。最后输出在不同radius的情况下算法的预测准确度。

	public static void main(String args[]) {
		try {
			MBR tempRecommender=new MBR("D:/software/eclipse/eclipse-workspace/day51/movielens-943u1682m.txt",943,1682,100000);//读数据
			for(double tempRadius=0.2;tempRadius<0.6;tempRadius+=0.1) {
				tempRecommender.setRadius(tempRadius);//设置
				tempRecommender.leaveOneOutPrediction();//预测
				double tempMAE=tempRecommender.computeMAE();//计算MAE
				double tempRSME=tempRecommender.computeRSME();
				System.out.println("Radius = " + tempRadius + " , MAE = " + tempMAE + " , RSME = " + tempRSME );
			}
		}catch (Exception ee) {
			System.out.println(ee);
			// TODO: handle exception
		}
	}

你可能感兴趣的:(机器学习,算法)