當前位置:成語大全網 - 新華字典 - 查找java中文分類實現過程代碼

查找java中文分類實現過程代碼

前幾天看了google研究員吳軍的壹系列關於數學美的文章,挺有感觸的。前段時間剛做了壹個基於統計語言模型的中文分詞系統的課程項目,所以貼上來跟大家學習。

分詞技術在搜索引擎、信息抽取、機器翻譯等領域的重要地位和應用就不多說了。言歸正傳:)

& lt!-【如果!支持列表]-& gt;愛達荷(Idaho的縮寫)

這個切分系統的統計語料庫就是我們學校開放的部分。妳可以在這裏下載。漢字約為1.84萬。當然,這已經被分割了,所以妳可以用它來建立壹個相對較小的語料庫。該系統主要由以下四個步驟完成:

& lt!-【如果!支持列表]-& gt;1 、& lt!-[endif]-& gt;語料庫預處理

& lt!-【如果!支持列表]-& gt;2 、& lt!-[endif]-& gt;建立二元模型(統計二元模型)

& lt!-【如果!支持列表]-& gt;3 、& lt!-[endif]-& gt;實現完全細分

& lt!-【如果!支持列表]-& gt;4 、& lt!-[endif]-& gt;評價試驗

下面我就這四個方面逐壹來說。

& lt!-【如果!支持列表]-& gt;1 、& lt!-[endif]-& gt;語料庫預處理

下載的分段語料庫都是“19980131-04-012-001/mReality/n/u Epiphany/VN Que/d Bei/p Description/v Chu/v shape/Ng Lai/”/w”,其中有壹部分前面是日期數字,因為這些分段語料庫的來源是人民日報。預處理主要是根據標點符號劃分句子,句子簡單定義為(。?!: ;)對於這五個標點符號末尾的單詞串,添加

出;//輸出流

在;//輸入流

string buffer s 1 = new string buffer();//緩沖區

char a = in . read();

而(a!= -1) //判斷是否到達流的末尾。

{

if ((a == ' .|| a == '?'|| a == '!'| | a = = ':' | | a = = ';'))//句子結束

{

字符串s2 =新字符串(s 1);

out . write(" & lt;BOS >;");//添加

out . write(S2);

out . write(" & lt;EOS >;);//添加

out . write('/n ');//換行

s 1 = new string buffer();

}

else if ( a == '/')

s 1 = s 1 . append((char)32);//分詞位置空間

else if(a & gt;256 )

s 1 = s 1 . append((char)a);

a = in . read();

}

out . close();

in . close();

& lt!-【如果!支持列表]-& gt;2 、& lt!-[endif]-& gt;建立二元模型(統計二元模型)

這裏首先簡單介紹壹下n元模型和2元模型。

從語言樣本估計的概率分布p稱為語言l的語言模型,對於給定的句子s = w1w2…wn,(數字,n,I都是下標,wi是句子s的壹個詞)。根據鏈式法則,p(s)= p(w 1)p(w2 | w 1)p(w3 | w 1 w2)...p (wn | w1w2w3...w(n-65438n-gram模型是考慮前n-1個單詞的歷史的模型。N越大,提供的上下文信息越多,但是成本越大,需要的訓練語料也越多。當n較小時,提供的信息較少,但計算成本較小,訓練語料不多。

設c(w1,…,wi)表示單詞串w1,w2…wi在訓練語料中出現的次數,則用最大似然估計,p (wn | w1,…,w(n-1)) = c (w65438則2-gram為p(wn | w(n-1))= c(w(n-1),wn)/c(n

想了解更多,請找相關資料,拿出大學時的概率統計教材。數學真的是個好東西:)

回歸項目:)訓練語料庫1 * * *有5萬多個不同的詞。在建立2-gram統計模型時,需要不斷統計每個詞在訓練語料中出現的頻率,還要統計每個詞組成的2-gram及其後面的詞在訓練語料中出現的頻率。因為在分割期間將在所建立的2-gram模型中頻繁地搜索相關數據,所以用於存儲該2-gram模型數據的數據結構必須能夠提供高效的搜索。所以選擇哈希表,可以提供常量時間搜索。Java類庫中提供了HashMap類,基於數據不是很大,可以直接使用。存儲時,每個鍵值對應壹個在訓練語料庫中出現過的詞,每個鍵值對應的value值就是壹個HashMap。暫且稱之為子hashmap吧。這種結構有點類似於文件結構中的二級索引。相關代碼如下:

如何把預處理文件中的單詞單獨讀出來並不繁瑣。方法:讀取每壹行,用空格分割成字符串數組,匹配壹個正則表達式得到。

//此方法傳入的兩個單詞組成壹個2-gram,prewd為前壹個單詞,currwd為後壹個單詞。

public static void add(String prewd,String currwd){

String key = prewd

String curr = currwd

boolean bb = hmap . contains key(key);//Hmap是現有的HashMap,用於存儲2-gram統計模型。這裏判斷preword是否在主圖中。

If (bb == false) {//如果不在主圖中,則添加。

HashMap hm = new HashMap();//首先,構造壹個新的子地圖。

hm.put(key,新整數(1));//存儲主鍵的頻率hm.put(curr,new Integer(1));//存儲緊接在主關鍵字之後的單詞的頻率。

HMap.put(key,hm);//將主鍵和對應的子映射放到主映射中。

}

Else //如果該單詞包含在主地圖中,

{

HashMap temp =(HashMap)hmap . get(key);//返回主鍵對應的子映射來修改值。

int count =((Integer)temp . get(key))。int value()+1;//子映射中的主鍵數加1。

temp.put(key,new Integer(count));

If (temp.containsKey(curr)) //確定子映射是否包含單詞。

{

int value =((Integer)temp . get(curr))。int value()+1;temp.put(curr,新整數(值));

}

其他

temp.put(curr,新整數(1));//如果沒有,則存儲在子映射中。

HMap.put(key,temp);//修改完子地圖後,放回主地圖。

}

}

}

因為語言中的詞大多屬於低頻詞,所以壹定存在稀疏性的問題。然而,MLE(最大似然估計)給沒有出現在訓練語料庫中的2-gram 0概率。因此,為了得到更好的參數,有必要對2-gram模型的數據進行平滑處理。目前有許多平滑技術,如加壹、加增量、威滕-貝爾、保持平滑等。該系統主要采用兩種平滑方法:增量平滑法和保持平滑法。讓我們以Add-delta平滑技術為例來平滑2-gram。對於2克模型,平滑公式為:

p(wn | w(n-1))=[c(w(n-1),wn)+delta]/(n+delta * v),其中delta為0.5。

其中n是訓練語料庫中所有2-gram的數量。

v:所有可能的不同2-gram的數目。

流暢思維:1。生成主hashmap的叠代器,依次讀取key

2.對於每個鍵,再次讀取其值,即壹個子hashmap

3.然後根據平滑公式計算並修改submap中的值。

算法框架:

叠代器it = primary hashmap.keySet()。叠代器();

While(it.hasNext())

{

primary key = it . next();

子HashMap =(HashMap)primary HashMap . get(primary key);

iterator itr = child hashmap . keyset()。叠代器();

While(itr.hasNext())

{

根據平滑公式,依次計算修改量。

}

}

關註:1。因為計算出來的概率值壹般都比較小,為了防止下溢,可以先取對數再取逆。

2.每個主密鑰對應的所有不存在的2-gram,即出現頻率為零的2-gram,都可以用壹個鍵-值對統壹存儲在對應的子hashmap中。

完成,對象序列化。在使用這個系統的時候,lazy load把它加載到內存中,然後它就可以壹直住在內存中,這樣會大大加快速度。

至此,2-gram模型已經建立。

& lt!-【如果!支持列表]-& gt;3 、& lt!-[endif]-& gt;完全分段實現

分詞壹般有最大匹配法(MM,RMM),基於規則的方法,統計的方法。前兩個我就不贅述了。所謂全切分,就是根據詞典得到所有可能的切分形式。歧義識別的方法主要包括基於規則的方法和統計的方法。當然這裏采用的是基於2-gram統計模型的方法:)為了避免切分後歧義分析的時間浪費。而這裏我們用的是邊分段邊評價的方法,也就是邊分段邊評價的方法。

對壹個句子進行全切分的結果,即所有可能的組合,可以形成壹棵解空間樹。

因此,可以用回溯法搜索最優解。

如果先把所有的全切分組合都搜索出來,然後按照2-gram選取最好的,顯然會浪費時間,因為過程中可能會有很多次重復搜索,回溯搜索的時間復雜度是指數級的時間。

所以在搜索過程中要結合剪枝,避免無效搜索,這樣可以大大提高效率。

采用樹的深度優先規則。可以找到最優解

具體算法如下:

Stack.push(BOS) //樹節點

而堆棧不為空。

x=stack.pop()

位置:=x .位置,w = x.w舊值:= x .值前字:= x .前字

如果m & gto那麽//m是前綴字符串的個數。

forj:=1到m do

FWj是fwc的第j個元素l。

如果length(w+FWj) =length(c)和最大概率則輸出w+FWjl並設置最新句子的最大概率值。

其他

posl:= pos+長度(FWj)l

if probability(w+FWj,posl,newsate)>最大值(pos1)

stack.push(x)

endif

結束

endif

結束時間

結束。

算法實現中需要考慮壹些問題,如樹節點保存、前綴字符串處理等。

評估測試

環境:windows xp2,amd速龍1800+,內存768m,JDK1.5。

Delta平滑:Delta的值越小,精度越高,0.5,0.01,0.0005438+0。

召回率:0.9756 0.9826 0.9928

精確度:0.9638 0.9710 0.9883

保持平滑

召回率:0.9946

精確度:0.9902

壹般而言,保留平滑仍應優於增量平滑。

所有建模和平滑過程可以在1分鐘內完成。

分段時間和效率:

& lt!-【如果!支持列表]-& gt;n & lt!-[endif]-& gt;測試語料庫,17455字,(中文17287),平均句子長度41字,時間340ms,平均切分速度5.10000/s。

& lt!-【如果!支持列表]-& gt;n & lt!-[endif]-& gt;20.5萬測試語料(來自笑傲江湖),預處理後,174600,時間110 ms,句子行數24945,平均句子長度7,切分時間134600 ms,平均134600/s

& lt!-【如果!支持列表]-& gt;n & lt!-[endif]-& gt;205000個測試語料(來自笑傲江湖),無預處理,平均句長239,切分時間40S,平均每秒5000字。

回溯算法是時間開銷為O(N!),所以句子的長度直接決定了分詞的速度,因為句子越長,單詞就越多。

預處理後句子短,平均句子長度7,回溯短,所以速度快很多。

至此,系統基本完成,告壹段落。感覺寫的挺亂的,呵呵

現在在做另壹個功課,做壹個簡單的搜索引擎,我準備把這個東西和搜索引擎結合起來,實現分詞功能:)