您的位置:首页 > 运维架构

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

2016-05-28 12:40 405 查看
写这个博客的初衷还是想记录一下自己学hadoop的历程啦~,大部分的东西都是老师要我们下来写的作业,虽然自己做的确实不怎么样,但如果要转载的话,还是麻烦注明一下出处,谢谢啦~
 
(一)问题
   老师给我们留的作业就是求一个或多个txt文件中任意两行的距离(余弦、jacard、欧式距离都行),txt文件中每一行是中文英文还是数字都不限。
   下面代码的我做测试用的数据格式是这样的:1{s1 s2 s3 s4 s5 s6},前面的1为行号,后面“{”“}”里的内容为真正要计算距离的某一行至,每个值之间用空格隔开。
(二)余弦距离
   网上关于余弦距离的计算的也有很多讲解的例子,我同学就举了这样一个例子,比如有这样两行A和B;A为“他喜欢你”,B为“我喜欢你”。要计算A,B的相似度,需先进行中文分词,分次后,A变成了“他”
“喜欢” “你”,B为“我”
“喜欢” “你”,先求A,B的并集,为{他
  喜欢  你  我 },再将A,B转化为向量来表示,如下图所示:

喜欢
A1110
B0111
 那么,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”代表行号。
[b]   3.2 mapper函数
[/b]

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更近,这种情形会怎么处理?

---------感谢将这篇博客看完啦,这是一条结束的分割线:)---------
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: