1. 程式人生 > >NLP之CRF++安裝及使用

NLP之CRF++安裝及使用

 

目錄

 

一、CRF簡介

CRF VS 詞典統計分詞

CRF VS HMM,MEMM

CRF分詞原理

二、CRF++工具包

CRF++的安裝(linux)

CRF++的使用


一、CRF簡介

Conditional Random Field:條件隨機場,一種機器學習技術(模型)

CRF由John Lafferty最早用於NLP技術領域,其在NLP技術領域中主要用於文字標註,並有多種應用場景,例如:

  • 分詞(標註字的詞位資訊,由字構詞)
  • 詞性標註(標註分詞的詞性,例如:名詞,動詞,助詞)
  • 命名實體識別(識別人名,地名,機構名,商品名等具有一定內在規律的實體名詞)

本文主要描述如何使用CRF技術來進行中文分詞。

CRF VS 詞典統計分詞

  • 基於詞典的分詞過度依賴詞典和規則庫,因此對於歧義詞和未登入詞的識別能力較低;其優點是速度快,效率高
  • CRF代表了新一代的機器學習技術分詞,其基本思路是對漢字進行標註即由字構詞(組詞),不僅考慮了文字詞語出現的頻率資訊,同時考慮上下文語境,具備較好的學習能力,因此其對歧義詞和未登入詞的識別都具有良好的效果;其不足之處是訓練週期較長,運營時計算量較大,效能不如詞典分詞

CRF VS HMM,MEMM

  • 首先,CRF,HMM(隱馬模型),MEMM(最大熵隱馬模型)都常用來做序列標註的建模,像分詞、詞性標註,以及命名實體標註
  • 隱馬模型一個最大的缺點就是由於其輸出獨立性假設,導致其不能考慮上下文的特徵,限制了特徵的選擇
  • 最大熵隱馬模型則解決了隱馬的問題,可以任意選擇特徵,但由於其在每一節點都要進行歸一化,所以只能找到區域性的最優值,同時也帶來了標記偏見的問題,即凡是訓練語料中未出現的情況全都忽略掉
  • 條件隨機場則很好的解決了這一問題,他並不在每一個節點進行歸一化,而是所有特徵進行全域性歸一化,因此可以求得全域性的最優值。

CRF分詞原理

1. CRF把分詞當做字的詞位分類問題,通常定義字的詞位資訊如下:

  • 詞首,常用B表示
  • 詞中,常用M表示
  • 詞尾,常用E表示
  • 單子詞,常用S表示

2. CRF分詞的過程就是對詞位標註後,將B和E之間的字,以及S單字構成分詞

3. CRF分詞例項:

  • 原始例句:我愛北京天安門
  • CRF標註後:我/S 愛/S 北/B 京/E 天/B 安/M 門/E
  • 分詞結果:我/愛/北京/天安門

CRF命名實體識別

基於CRF的命名實體識別實質上也是把它看做一個序列標註問題。

基本思路是:先進行分詞,然後對人名、簡單地名和簡單組織名進行識別,最後識別複合地名和複合組織名。

用CRF進行命名實體識別屬於有監督學習。需要大批已標註的語料對模型引數進行訓練。

二、CRF++工具包

上面介紹了CRF技術思想以及如何用於分詞,下面將介紹如何在實際開發中使用CRF進行分詞工作。目前常見的CRF工具包有pocket crf, flexcrf 和crf++,目前網上也有一些它們3者之間的對比報告,個人感覺crf++在易用性,穩定性和準確性等綜合方面的表現最好,同時在公司的專案開發中也在使用,因此下面將概述一下crf++的使用方法。

CRF++的安裝(linux)

安裝包官方下載地址:https://drive.google.com/drive/folders/0B4y35FiV1wh7fngteFhHQUN2Y1B5eUJBNHZUemJYQV9VWlBUb3JlX0xBdWVZTWtSbVBneU0?usp=drive_web#list(最新版0.58)

安裝要求:

  • C++ compiler (gcc 3.0 or higher)

安裝操作:
1.解壓安裝包:tar –xvzf 軟體包名
2.cd 進入解壓後的目錄,執行‘./configure’命令為編譯做好準備
3.執行“make”命令進行軟體編譯
4.執行“make install”完成安裝 (注意:需先執行“su”獲取root使用者許可權)
5.執行“make clean”刪除安裝時產生的臨時檔案(可不執行)

檔案主要內容:
    doc資料夾:就是官方主頁的內容。
    example資料夾:有四個任務的訓練資料、測試資料和模板檔案。
    sdk資料夾:CRF++的標頭檔案和靜態連結庫。
    crf_learn.exe:CRF++的訓練程式。
    crf_test.exe:CRF++的預測程式
    libcrfpp.dll:訓練程式和預測程式需要使用的靜態連結庫。

    實際上,需要使用的就是crf_learn.exe,crf_test.exe和libcrfpp.dll,這三個檔案。

CRF++的使用

和許多機器學習工具一樣,CRF++的使用分為兩個過程,一個是訓練過程,一個是測試過程,我們先來直接閱讀官方的使用教程http://taku910.github.io/crfpp/

1.訓練和測試的資料格式

訓練和測試檔案必須包含多個tokens,每個token又包含多個列。token的定義可根據具體的任務,如詞、詞性等。每個token必須寫在一行,且各列之間用空格或製表格間隔。一個token的序列可構成一個sentence,每個sentence之間用一個空行間隔。

注意最後一列將是被CRF用來訓練的最終標籤。

例子:He reckons the current account deficit will narrow to only #1.8 billion in September.

這個例子中”He reckons the current account deficit will narrow to only #1.8 billion in September .”代表一個訓練語句,CRF++要求將這樣的句子拆成每一個詞一行並且是相同固定列數的資料,其中列除了原始輸入,還可以包含一些其他資訊,比如例子每個token包含3列,分別為字本身、字型別和詞位標記,最後一列是Label資訊,也就是標準答案yy。而不同的訓練序列與序列之間的相隔,依靠一個空白行來區分。

通俗說法:訓練檔案由若干個句子組成(可以理解為若干個訓練樣例),不同句子之間通過換行符分隔,上圖中顯示出的有兩個句子。每個句子可以有若干組標籤,最後一組標籤是標註,上圖中有三列,即第一列和第二列都是已知的資料,第三列是要預測的標註,以上面例子為例是,根據第一列的詞語和和第二列的詞性,預測第三列的標註。 當然這裡有涉及到標註的問題,比如命名實體識別就有很多不同的標註集。

注意:每一行的列數必須相同一致,否則系統將報錯。

2.準備特徵模板

CRF++訓練的時候,要求我們自己提供特徵模板。

2.1 模板基礎

模板檔案中的每一行是一個模板。每個模板都是由%x[row,col]來指定輸入資料中的一個token。row指定到當前token的行偏移,col指定列位置。

由上圖可見,當前token是the這個單詞。%x[-2,1]就就是the的前兩行,1號列的元素(注意,列也是從0開始的),即為PRP。

2.2模板型別

模板有兩種型別,(模板型別)通過第一個字元指定。

 Unigram template:首字元: 'U'

當給出一個"U01:%x[0,1]"的模板時,CRF++會產生如下的一些特徵函式集合(func1 ... funcN) 。

這幾個函式說明一下,%x[0,1]這個特徵到前面的例子就是說,根據詞語(第1列)的詞性(第2列)來預測其標註(第3列),這些函式就是反應了訓練樣例的情況,func1反映了“訓練樣例中,詞性是DT且標註是B-NP的情況”,func2反映了“訓練樣例中,詞性是DT且標註是I-NP的情況”。    

模板函式的數量是L*N,其中L是標註集中類別的數量,N是從模板中擴充套件處理的字串種類數量。

Bigram template:首字元:'B'   

這個模板用來描述二元特徵。這個模板會自動產生當前output token和前一個output token的合併。注意,這種型別的模板會產生L * L * N種不同的特徵。當類別數很大的時候,這種型別會產生許多可區分的特徵,這將會導致訓練和測試的效率都很低下。

Unigram feature 和 Bigram feature有什麼區別呢? 
    unigram/bigram很容易混淆,因為通過unigram-features也可以寫出類似%x[-1,0]%x[0,0]這樣的單詞級別的bigram(二元特徵)。而這裡的unigram和bigram features指定是uni/bigrams的輸出標籤。
    這裡的一元/二元指的就是輸出標籤的情況,這個具體的例子我還沒看到,example資料夾中四個例子,也都是隻用了Unigram,沒有用Bigarm,因此感覺一般Unigram feature就夠了。

2.3使用識別符號區分相對位置

如果使用者需要區分token的相對位置時,可以使用識別符號。比如在下面的例子中,"%x[-2,0]"和"%x[1,0]"都代表“北”,但是它們又是不同的“北“。
北 CN B
京 CN E
的 CN S  >> 當前token
北 CN S
部 CN S
為了區分它們,可以在模型中加入一個唯一的識別符號(U01: 或 U02:),即:
U01:%x[-2,0]
U02:%x[1,0]
在這樣的條件下,兩種模型將被認為是不同的,因為他們將被擴充套件為”U01:北“和”U02:北”。只要你喜歡,你可以使用任何識別符號,但是使用數字序號區分更很有用,因為它們只需簡單的與特徵數相對應。

2.4  這是CoNLL 2000的Base-NP chunking任務的模板例子。只使用了一個bigram template ('B')。這意味著只有前一個output token和當前token被當作bigram features。“#”開始的行是註釋,空行沒有意義。

3.訓練(編碼)

命令列:
    % crf_learn template_file train_file model_file
其中,template_file和train_file需由使用者事先準備好。crf_learn將生成訓練後的模型並存放在model_file中。

一般的,crf_learn將在STDOUT上輸出下面的資訊。

  • iter: 迭代次數
  • terr: tags的錯誤率(錯誤的tag數/所有的tag數)
  • serr:sentence的錯誤率(錯誤的sentence數/所有的sentence數)
  • obj:當前物件的值。當這個值收斂到一個確定的值時,CRF模型將停止迭代
  • diff:與上一個物件值之間的相對差

這個訓練過程的時間、迭代次數等資訊會輸出到控制檯上(感覺上是crf_learn程式的輸出資訊到標準輸出流上了),如果想儲存這些資訊,我們可以將這些標準輸出流到檔案上,命令格式如下:
    % crf_learn template_file train_file model_file >> train_info_file

有四個主要的引數可以調整:
    -a CRF-L2 or CRF-L1     
    規範化演算法選擇。預設是CRF-L2。一般來說L2演算法效果要比L1演算法稍微好一點,雖然L1演算法中非零特徵的數值要比L2中大幅度的小。
    -c float
    這個引數設定CRF的hyper-parameter。c的數值越大,CRF擬合訓練資料的程度越高。這個引數可以調整過度擬合和不擬合之間的平衡度。這個引數可以通過交叉驗證等方法尋找較優的引數。
    -f NUM
    這個引數設定特徵的cut-off threshold。CRF++使用訓練資料中至少NUM次出現的特徵。預設值為1。當使用CRF++到大規模資料時,只出現一次的特徵可能會有幾百萬,這個選項就會在這樣的情況下起到作用。
    -p NUM
    如果電腦有多個CPU,那麼那麼可以通過多執行緒提升訓練速度。NUM是執行緒數量。

    帶兩個引數的命令列例子:
    % crf_learn -f  3 -c 1.5 template_file train_file model_file

4.測試(解碼)

     命令列:
     % crf_test -m model_file test_files

在測試過程中,使用者不需要指定template file,因為,mode file已經有了template的資訊。test_file是你想要標註序列標記的測試語料。

有兩個引數-v和-n都是顯示一些資訊的,-v可以顯示預測標籤的概率值,-n可以顯示不同可能序列的概率值,對於準確率,召回率,執行效率,沒有影響,這裡不說明了。

與crf_learn類似,輸出的結果放到了標準輸出流上,而這個輸出結果是最重要的預測結果資訊(測試檔案的內容+預測標註),同樣可以使用重定向,將結果儲存下來,命令列如下。
      % crf_test -m model_file test_files >> result_file

 

參考文章:

CRF++使用小結
CRF 及CRF++ 安裝與解釋
CRF++原始碼解讀
CRF++程式碼分析CRF++使用教程