Java進行語義相似度分析
阿新 • • 發佈:2019-02-15
這是發的第二篇部落格,之前那篇還沒有通過稽核呢,無所謂。
想說說這個題目,還在上大四,自然語言處理和資訊檢索的知識在我們學校是研究生的課程,而且這個實驗室很NB哦,老那我不能保研,只能遠遠看著實驗室門牌號拿著紙巾……擦眼淚了。好在是良心學院,大四沒有什麼基礎課程了,卻開了還好多專業限選課,就是各個實驗室都能拿出來一些入門課程,派博士生或者直接教授上陣,拿到本科課堂來。
不多說了,這是課上老師留的作業:給定文字input.txt ,其中有750對英文句子,以" 句子1 + Tab + 句子2 +Enter "形式給出。現在要求用餘弦向量法,求每對英文句子的相似度,並且輸出到output.txt。 完成上一個任務後,老師還會給出一個針對上述750對句子,人工給出的相似度評分檔案standardAnalysis.txt(750個數,人工寫上去的?老師唬我,說,用了什麼高階技術),現在又要求利用Pearson相關係數
以上就是題目要求,聽說,這應該是這項課程最入門的程式和思想了吧,思想去百度 “紅字兒”,剩下的就是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,計算頻數時,去掉常用詞即可。