1. 程式人生 > >Java進行語義相似度分析

Java進行語義相似度分析

這是發的第二篇部落格,之前那篇還沒有通過稽核呢,無所謂。

想說說這個題目,還在上大四,自然語言處理和資訊檢索的知識在我們學校是研究生的課程,而且這個實驗室很NB哦,老那我不能保研,只能遠遠看著實驗室門牌號拿著紙巾……擦眼淚了。好在是良心學院,大四沒有什麼基礎課程了,卻開了還好多專業限選課,就是各個實驗室都能拿出來一些入門課程,派博士生或者直接教授上陣,拿到本科課堂來。

不多說了,這是課上老師留的作業:給定文字input.txt ,其中有750對英文句子,以"  句子1  +  Tab  + 句子2  +Enter   "形式給出。現在要求用餘弦向量法,求每對英文句子的相似度,並且輸出到output.txt。       完成上一個任務後,老師還會給出一個針對上述750對句子,人工給出的相似度評分檔案standardAnalysis.txt(750個數,人工寫上去的?老師唬我,說,用了什麼高階技術),現在又要求利用Pearson相關係數

法,分析output.txt與standardAnalysis.txt中得到的語義相似度的相關性如何。

以上就是題目要求,聽說,這應該是這項課程最入門的程式和思想了吧,思想去百度 “紅字兒”,剩下的就是java的基本操作了,作為一個對java還沒有入門我來說,這才是我的難題,好在兩個下午給弄出來了,有些地方寫的可能很可笑,可我還發現 不了,以後回來看的時候用來當茶餘飯後吧。。。

廢話少說,貼程式碼,註釋寫的還挺清楚的

/*
Author:Na
Data :2014/11/29
*/
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


class Similarity{
	String sentence="";
	String []part=new String[2];
	String input,output,standardAnalysis;
	List <String> comDataList=new ArrayList<String>();
	List <String> userDataList=new ArrayList<String>();
	static float[] comData=new float[1000];
	static float[] userData=new float[1000];
	public Similarity(String input,String output,String standardAnalysis){
		this.input=input;
		this.output=output;
		this.standardAnalysis=standardAnalysis;
	}
	public static void main(String args[]) throws IOException{
		Similarity s=new Similarity("E://input.txt","E://output.txt","E://standardAnalysis.txt");
		s.fileOperation(s.input,s.output);
		System.out.println("語義的向量相似度分析完畢"+"\n"+"請檢視檔案:"+s.output);
		System.out.println("......"+"\n"+"Pearson correlation分析如下");
		System.out.println("程式計算結果與人工打分結果之間的相關度是:");
		s.PearsonFileOperation(s.output,s.standardAnalysis);
		float pearson=s.Pearson(comData,userData);
		System.out.println(pearson);
	
		//System.out.println(s.Sum(comData));
		//System.out.println(s.Sum(userData));
		
	}
	
	//Chapter1:餘弦向量法
	public void fileOperation(String inputPath,String outputPath) throws IOException{
		//讀檔案
		File inputFile=new File(inputPath);
		BufferedReader reader=null;
        if(!inputFile.exists()||inputFile.isDirectory())
				throw new FileNotFoundException();
        reader=new BufferedReader(new FileReader(inputFile));
        
        //寫檔案
        File outputFile=new File(outputPath);
        FileWriter writer=null;
        if(!outputFile.exists())
        	if(!outputFile.createNewFile())
        		System.out.println("輸出檔案建立失敗");
        writer=new FileWriter(outputFile);
        
        //按行得到句子對兒
        int line=1;
        float result=(float) 0.0;
        String tmpToWrite="";
        while((sentence=reader.readLine())!=null){
            part=sentence.split("\t");//按"tab"將每對兒句子分成兩部分part[0],part[1]
            line++; 
            result=cosVector(part[0],part[1]); //餘弦向量法分析相似度   
            
            // 按照       相似度d+"\tab"+part[0]+"\tab"+part[1]+"\n"  
            tmpToWrite=result+"\t"+part[0]+"\t"+part[1]+"\r\n";
            writer.write(tmpToWrite);
            writer.flush();
        }
        if(reader!=null){
        	try{
        		reader.close();
        	}catch(Exception e){
        		e.printStackTrace();
        	}
        }
        if(writer!=null){
        	try{
        		writer.close();
        	}catch(Exception e){
        		e.printStackTrace();
        	}
        }
	}
	
	//判斷指定字串str是否在Map的索引集當中
	public boolean isIn(Map<String,int[]> wordWeight,String str){
		for (String key : wordWeight.keySet()) {//遍歷map的所有key
			if(key.equals(str))
				return true;
		}
		return false;
	}
	
	//計算餘弦向量
	public float cosVector(String sentence1,String sentence2){
		String []wordsOfSen1=new String[64];//第一句的單詞集
		String []wordsOfSen2=new String[64];//第二句的單詞集
		wordsOfSen1=sentence1.split(" ");
	    wordsOfSen2=sentence2.split(" ");
	    //單詞的出現頻數,例:wordWeight[word][0]單詞"word"在第一句中出現的頻數
		Map <String,int[]> wordWeight=new HashMap<String ,int[]>();
		
		//兩句話的單詞頻數統計
	    for(int i=0;i<wordsOfSen1.length;i++){
	    	if(!isIn(wordWeight,wordsOfSen1[i]))
	    		wordWeight.put(wordsOfSen1[i], new int[]{1,0});
	    	else
	    		wordWeight.get(wordsOfSen1[i])[0]+=1;
	    }
	    for(int i=0;i<wordsOfSen2.length;i++){
	    	if(!isIn(wordWeight,wordsOfSen2[i]))
	    		wordWeight.put(wordsOfSen2[i], new int[]{0,1});
	    	else
	    		wordWeight.get(wordsOfSen2[i])[1]+=1;
	    }
	    //上面已經將各個單詞的頻數按照向量(即句子向量)的形式表示出來了
	    //wordWeight.size就是向量的維數
	    //wordWeight[word][0]就是單詞"word"在第一句中出現的頻數
	    //下面利用該向量計算餘弦
	    float neiji=(float) 0.0;//兩個句子向量的內積
	    float modeOfSen1=(float)0.0;//句子1的向量模de平方
	    float modeOfSen2=(float)0.0;//句子2的向量模de平方
	    for(String key:wordWeight.keySet()){
	    	neiji+=wordWeight.get(key)[0]*wordWeight.get(key)[1];
	    	modeOfSen1+=Math.pow(wordWeight.get(key)[0], 2);
	    	modeOfSen2+=Math.pow(wordWeight.get(key)[1], 2);
	    	
	    }
		return (float) (neiji/(Math.sqrt(modeOfSen1)*Math.sqrt(modeOfSen2)));
	}
	
	
	
	//Chapter2:Pearson迴歸分析
	//Pearson公式
	public float Pearson(float[] x,float[] y){
		int lenx=x.length;
		int leny=y.length;
		int len=lenx;//小容錯
		if(lenx<leny) len=lenx;
		else len=leny;
		
		float sumX=Sum(x);
		float sumY=Sum(y);
		float sumXX=Mutipl(x,x,len);
		float sumYY=Mutipl(y,y,len);
		float sumXY=Mutipl(x,y,len);
		float upside=sumXY-sumX*sumY/len;
		float downside=(float) Math.sqrt((sumXX-(Math.pow(sumX, 2))/len)*(sumYY-(Math.pow(sumY, 2))/len));
		System.out.println(len+" "+sumX+" "+sumY+" "+sumXX+" "+sumYY+" "+sumXY);
		return upside/downside;
	}
	public float Sum(float[] arr){
		float total=(float)0.0;
		for(float ele:arr)
			total+=ele;
		return total;
	}
	public float Mutipl(float[] arr1,float[] arr2,int len){
		float total=(float)0.0;
		for(int i=0;i<len;i++)
			total+=arr1[i]*arr2[i];
		return total;
	}
	//String陣列轉為float陣列
	public float[] strToFloat(List<String> str){
		int len=str.size();
		float[] floatArr=new float[len];
		for(int i=0;i<len;i++){
			floatArr[i]=Float.parseFloat(str.get(i));
		}
		
		return floatArr;
	}
	public float PearsonFileOperation(String outputPath,String standardAnalysisPath) throws FileNotFoundException  {
		//讀檔案
		File outputFile=new File(outputPath);
		BufferedReader reader1=null;
		if(!outputFile.exists()||outputFile.isDirectory())
				throw new FileNotFoundException();
		reader1=new BufferedReader(new FileReader(outputFile));
		
		File standardAnalysisFile=new File(standardAnalysisPath);
		BufferedReader reader2=null;
		if(!standardAnalysisFile.exists()||standardAnalysisFile.isDirectory())
				throw new FileNotFoundException();
		reader2=new BufferedReader(new FileReader(standardAnalysisFile));
		
		//分段
		String tmpSen="";
		String []tmpPart=new String[6];
		try {
			while((tmpSen=reader1.readLine())!=null){
				tmpPart=tmpSen.split("\t");
				comDataList.add(tmpPart[0]);
			}
			while((tmpSen=reader2.readLine())!=null){
				tmpPart=tmpSen.split("\n");
				userDataList.add(tmpPart[0]);
			}
			//將list轉換為float陣列
			comData= strToFloat(comDataList);
			userData= strToFloat(userDataList);
			
		} catch (IOException e) {
			System.out.println("錯誤");
			e.printStackTrace();
		}
		return 0;
	}
	
}


再語義相似度分析時,上面的程式碼漏掉了一個很重要的點,就是要把平時常用詞彙去除掉,這是後來想起來的,懶得去加了,其實很簡單,只需要用再建立儲存常用詞彙的map,計算頻數時,去掉常用詞即可。