初学hadoop之一:相似度计算(余弦距离)


写这个博客的初衷还是想记录一下自己学hadoop的历程啦~,大部分的东西都是老师要我们下来写的作业,虽然自己做的确实不怎么样,但如果要转载的话,还是麻烦注明一下出处,谢谢啦~

 

(一)问题

   老师给我们留的作业就是求一个或多个txt文件中任意两行的距离(余弦、jacard、欧式距离都行),txt文件中每一行是中文英文还是数字都不限。

   下面代码的我做测试用的数据格式是这样的:1{s1 s2 s3 s4 s5 s6},前面的1为行号,后面“{”“}”里的内容为真正要计算距离的某一行至,每个值之间用空格隔开。

(二)余弦距离

   网上关于余弦距离的计算的也有很多讲解的例子,我同学就举了这样一个例子,比如有这样两行ABA他喜欢你B我喜欢你。要计算AB的相似度,需先进行中文分词,分次后,A变成了” “喜欢” “B” “喜欢” “,先求A,B的并集,为{   喜欢     },再将AB转化为向量来表示,如下图所示:


喜欢
A 1 1 1 0
B 0 1 1 1

 那么,A对应的向量为(1,1,1,0),B为(0,1,1,1),再根据cosAB=(A,B)/|A||B|求得余弦值。

下面的计算A和B的余弦值的函数cosAB是没有考虑任何的优化直接写的,大家就随便看看吧:


	//计算string1和string2的余弦值
	public static double calculate(String s1,String s2){
		String[] t1=s1.split(" ");
		String[] t2=s2.split(" ");
		
		//以键值形式存储m1和m2的值
		Map<String, Integer> m1=new HashMap<String, Integer>();
		Map<String, Integer> m2=new HashMap<String, Integer>();
	 
	   for(int i=0;i<t1.length;i++){
		   
		   if(m1.containsKey(t1[i]))
		    m1.put(t1[i], m1.get(t1[i])+1);
		   else
			 m1.put(t1[i], 1);
	   }
	   
	   for(int j=0;j<t2.length;j++){
		   if(m2.containsKey(t2[j]))
			    m2.put(t2[j], m2.get(t2[j])+1);
			   else
				 m2.put(t2[j], 1);
	   }

		
		Set temp=new HashSet(t1.length+t2.length);
		
		//set为两个行的并集的key值
		for(int i=0;i<t1.length;i++){
			for(int j=0;j<t2.length;j++){
				temp.add(t1[i]);
				temp.add(t2[j]);
				
				}		
			}
		
		//分别计数
		for(int i=0;i<temp.size();i++){
			if(! m1.containsKey(temp.toArray()[i]))
				m1.put(temp.toArray()[i].toString(), 0);
		}
		

		
		for(int j=0;j<temp.size();j++){
			if(! m2.containsKey(temp.toArray()[j]))		
				m2.put(temp.toArray()[j].toString(), 0);

		}
		
		int n1=0,n2=0,nfinal=0;
		for(int k=0;k<temp.size();k++){
			String st1=temp.toArray()[k].toString();//得到某一个具体的key值
			
			System.out.println("m1的"+st1+"值为:"+m1.get(st1)+"    m2的"+st1+"值为:"+m2.get(st1));
			
				n1+=m1.get(st1)*m1.get(st1);
				n2+=m2.get(st1)*m2.get(st1);
				nfinal+=m1.get(st1)*m2.get(st1);
				System.out.println(n1);
				System.out.println(n2);
				System.out.println(nfinal);
			}
		
		double dis=nfinal/Math.sqrt(n1*n2);
		
		return dis;				
     }

(三)相似度计算

   3.1 计算思想

          input:
       
  1 s1
          2 s2
 
       
  mapper
   
      1,1 s1
          1,2 s1
          1,1 s1
          2,1 s1
          
          2,1 s2
          2,2 s2
          1,2 s2
          2,2 s2
 
       
  reducer
       
  1,1 {s1 s1}
          1,2 {s1 s2}
          2,1 {s1 s2}
          2,2 {s2 s2}

input中“1”“2”代表行号。

   3.2 mapper函数

	public static class calMapper  extends Mapper<LongWritable, Text, Text, Text>{//后两个Text指的是CONTEXT的参数
          
        public void map(LongWritable ikey, Text ivalue, Context context) throws IOException, InterruptedException {
         
        	String[] temp=ivalue.toString().split(" ");
        	
        	 for(int i=1;i<=100;i++){//偷懒了,这边可以自己确定一下一共有多少行需要计算
        		String id1=temp[0]+","+i;
        		String id2=i+","+temp[0];
        		
        		System.out.println("比较"+id1+"和"+id2);
        	 
        	 context.write(new Text(id1), new Text(ivalue));//重要的一点就是context可以写多行
     		 context.write(new Text(id2), new Text(ivalue));
        	 }
        }
	}
 3.3 reducer函数

	  public static class calReducer extends Reducer<Text,Text,Text,Text> {
		    
		  public void reduce(Text key, Iterable<Text> values,  Context context ) throws IOException, InterruptedException {
	    	
	    	String[] data=new String[2];
	    	String[] k=new String[2];
	    	String[] id1=new String[2];
	    	String[] id2=new String[2];
	    	//将id1,id2的两值分别存于data[0]和data[1]中
	    	int i=0;
	    	for(Text line:values){   
	    		String[] temp=line.toString().split(" ");
	    		k[i]=temp[0];
	    		//从values中读取行号和后面的值  				
	    		data[i]=line.toString().substring(line.toString().indexOf("{")+1, line.toString().length()-1);
                /*若将 line.toString().length()-1改为line.toString().indexOf("}"),
				就会报index out of range:-1的错误,不是很理解				
                */

	    		System.out.println("第"+k[i]+"行的数据为:"+data[i]);
	    		
	    		if(i==0){
	    			id1=data[i].split(" ");
	    		}
	    		else{
	    			id2=data[i].split(" ");
	    		}    		
	    		i++;    		    		
	    	} 
	    	double dis=calculate(data[0],data[1]);
	    	System.out.println(k[0]+"与"+k[1]+"的相似度为:"+dis);
	    	String t="the similarity of "+k[0]+","+k[1]+"is:";
	    	System.out.println(t+":"+dis);
	    	context.write(new Text(t), new Text(String.valueOf(dis+"\r\n")));    	
	    	System.out.println("------------------------");
		  }
	  }
	  
(四)疑问
  假如有以下三句话:A{他特别喜欢你};B{他非常喜欢你};C{他不喜欢你}。按照上面的余弦算法,AB和BC的余弦值是一样的,但是直观上感受明显是A比C离B更近,这种情形会怎么处理?

---------感谢将这篇博客看完啦,这是一条结束的分割线:)---------





智能推荐

注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
© 2014-2019 ITdaan.com 粤ICP备14056181号  

赞助商广告