利用IKAnalyzer中文分詞,計算句子相似度
IKAnalyzer中文分詞,計算句子相似度
一、簡介
IKAnalyzer是一個開源的,基于java語言開發的輕量級的中文分詞工具包。以開源項目Luence為應用主體的,結合詞典分詞和文法分析算法的中文分詞組件。獨立于Lucene項目,同時提供了對Lucene的默認優化實現。(簡介來源:百度百科)
二、準備
項目結構:
ext.dic
stopword.dic
IKAnalyzer.cfg.xml
三、分詞
分詞由于比較簡單,就直接粘貼代碼了
public static Vector<String> participle( String str ) { Vector<String> str1 = new Vector<String>() ;//對輸入進行分詞 try { StringReader reader = new StringReader( str ); IKSegmenter ik = new IKSegmenter(reader,true);//當為true時,分詞器進行最大詞長切分 Lexeme lexeme = null ; while( ( lexeme = ik.next() ) != null ) { str1.add( lexeme.getLexemeText() ); } if( str1.size() == 0 ) { return null ; } //分詞后 System.out.println( "str分詞后:" + str1 ); } catch ( IOException e1 ) { System.out.println(); } return str1; }
四、計算相似度
計算句子相似度,①常用方法有基于語義和詞序相似度計算方法,②基于關系向量模型
基于語義和詞序的句子相似度計算方法簡介
定義1:給定一個句子Ti,經過漢語分詞系統分詞后,得到的所有詞W1構成的向量稱為句子Ti的向量表示,表示為Ti = {w1,w2,.....wn}。
例子1:T1:這個中文分詞可不可以,用著方不方便。分詞后:T1=[這個, 中文分詞, 可不可以, 用著, 方, 不, 方便]。向量表示T1={這個, 中文分詞, 可不可以, 用著, 方, 不, 方便}
T2:這個中文分詞比較方便,用著方便還可以。分詞后:T2=[這個, 中文分詞, 比較方便, 用著, 方便, 還可以]。向量表示T2={這個, 中文分詞, 比較方便, 用著, 方便, 還可以}
定義2:給定一個句子Ti的向量表示,Ti中詞的個數稱為Ti的向量長度,表示為Len(Ti).
例子2:對于句子T1和T2的向量長度表示為:Len(T1)=7,Len(T1)=6。
定義3:給定兩個句子Ti、Tj的向量表示,將Ti、Tj中的所有詞Wi進行合并,并且對于重復初想的詞只保留一個,由此得到兩個向量之和,稱為Ti、Tj的并集,表示T=T1 U T2={這個, 中文分詞, 可不可以, 比較方便,用著, 方, 不, 方便,還可以},很顯然,并集長度Len(T)<=Len(T1)+Len(T2)。
定義4:給定一個句子Ti的向量表示Ti = {w1,w2,.....wn}和一個詞wi,依次計算wi和Ti中每一個詞的相似度(值為0到1之間),所以所有結果中的最大值稱為wi在Ti中的語義分數,表示為Ci。
定義5:給定兩個句子Ti、Tj的向量表示,Ti和Tj的集合T={w1、w2,....wn},對T中的每一個詞Wi,計算Wi在Ti中的語義分數Ci,T中每個分詞的語義分數組成的一個向量稱為Ti基于T的語義向量,表示為Si={C1,C2,...,Cn}。
在該算法中,基于T分別計算Ti和Tj的語義向量Si、Sj,以計算Si作為說明,過程如下:
1、對于T中的每一個詞wi,如果wi 在Ti 中出現,則在語義向量Si中將wi 的語義分數Ci設為1。
2、如果Ti中不包含wi,則計算wi 在 Ti 中的語義分數 Ci=a(a為預先設定的閾值 , 無閾值設為0 ,本文中閾值為0.2 ) 。
根據語義向量計算語義相似度方法如下圖:
詞序相似度計算法方法如下:
其中r1、r2 分別為T1、T2的詞序向量,以T1為例,其計算方法如下:
1、對于T中的每一個詞wi,如果T1中包含該次,則r1中該次的取值為該詞在T1中出現的詞序。否則在T1中找出與wi最相似的詞Wi。
2、如果wi 和 wi 的相似度大于一個給定的閾值(實驗取值為0.4),wi在r1中的取值設為wi在Ti中出現的詞序。
3、如果兩種情況均為發生,則wi在r1中的取值設為null。
然而,根據語義向量計算語義相似度方法中,想到了之前看過的一篇博客,通過余玄定理來實現,計算方法如下
//閾值 public static double YUZHI = 0.2 ; /** * 返回百分比 * @author: Administrator * @Date: 2015年1月22日 * @param T1 * @param T2 * @return */ public static double getSimilarity(Vector<String> T1, Vector<String> T2) throws Exception { int size = 0 , size2 = 0 ; if ( T1 != null && ( size = T1.size() ) > 0 && T2 != null && ( size2 = T2.size() ) > 0 ) { Map<String, double[]> T = new HashMap<String, double[]>(); //T1和T2的并集T String index = null ; for ( int i = 0 ; i < size ; i++ ) { index = T1.get(i) ; if( index != null){ double[] c = T.get(index); c = new double[2]; c[0] = 1; //T1的語義分數Ci c[1] = YUZHI;//T2的語義分數Ci T.put( index, c ); } } for ( int i = 0; i < size2 ; i++ ) { index = T2.get(i) ; if( index != null ){ double[] c = T.get( index ); if( c != null && c.length == 2 ){ c[1] = 1; //T2中也存在,T2的語義分數=1 }else { c = new double[2]; c[0] = YUZHI; //T1的語義分數Ci c[1] = 1; //T2的語義分數Ci T.put( index , c ); } } } //開始計算,百分比 Iterator<String> it = T.keySet().iterator(); double s1 = 0 , s2 = 0, Ssum = 0; //S1、S2 while( it.hasNext() ){ double[] c = T.get( it.next() ); Ssum += c[0]*c[1]; s1 += c[0]*c[0]; s2 += c[1]*c[1]; } //百分比 return Ssum / Math.sqrt( s1*s2 ); } else { throw new Exception("傳入參數有問題!"); } }
測試結果:
str分詞后:[這個, 中文分詞, 可不可以, 用著, 方, 不, 方便] str分詞后:[這個, 中文分詞, 比較方便, 用著, 方便, 還可以] 相似度:0.7595872466989299
代碼地址:twosnail源碼地址
原創作者:twosnail(兩只蝸牛)
來自:http://my.oschina.net/twosnail/blog/370744