CRF模型详解
條件隨機場(CRF)是自然語言處理中的基礎模型, 廣泛用于分詞, 實體識別和詞性標注等場景. 隨著深度學習的普及, BILSTM+CRF, BERT+CRF, TRANSFORMER+CRF等模型, 逐步亮相, 并在這些標注場景, 效果有顯著的提升.
下面是我學習CRF的學心總結, 看了多篇知乎, paper, 和CRF++的實現代碼后, 終于有了深刻的理解.
基礎概念
首先, 一起看一下隨機過程, 隨機場, 馬爾可夫隨機場的定義, 在最后請出條件隨機場.
隨機過程:
 設 ???????TTT是一無限實數集, 把依賴于參數t∈Tt\in Tt∈T的一族(無限多個)隨機變量稱為隨機過程, 記為 X(t),t∈T{X(t), t \in T}X(t),t∈T
隨機場: 從平面(隨機過程)到向量空間(隨機場)
 若TTT是nnn維空間的某個子集, 即ttt是一個nnn維向量, 此時隨機過程又稱為隨機場. 常見隨機場有: 馬爾可夫隨機場(MRF), 吉布斯隨機場(GRF), 條件隨機場(CRF)和高斯隨機場.
馬爾可夫隨機場:
 具有馬爾可夫性的隨機場.
 馬爾可夫性: P(Yv∣Yw,w≠v)=P(Yv∣Yw,w~v)P(Y_v|Y_w, w\neq v) = P(Y_v|Y_w, w \sim v)P(Yv?∣Yw?,w?=v)=P(Yv?∣Yw?,w~v)
- w~vw \sim vw~v表示在圖G=(V,E)G=(V, E)G=(V,E)中與頂點vvv有邊連接的所有頂點www
 - w≠vw \neq vw?=v表示頂點vvv以外的所有頂點
 - YvY_vYv?與YwY_wYw?為頂點vvv與www對應的隨機變量
 
那么, 條件隨機場是如何定義的呢?
條件隨機場:
設XXX與YYY是隨機變量, P(Y∣X)P(Y|X)P(Y∣X)是在給定XXX的條件下YYY的條件概率分布.
 若隨機變量YYY構成一個由無向圖G=(V,E)G=(V, E)G=(V,E)表示的馬爾可夫隨機場, 即
 P(Yv∣X,Yw,w≠v)=P(Yv∣X,Yw,w~v)P(Y_v|X, Y_w, w \neq v) = P(Y_v|X, Y_w, w \sim v)P(Yv?∣X,Yw?,w?=v)=P(Yv?∣X,Yw?,w~v)
 對任意頂點vvv成立, 則稱條件概率分布P(Y∣X)P(Y|X)P(Y∣X)為條件隨機場.
- w~vw \sim vw~v表示在圖G=(V,E)G=(V, E)G=(V,E)中與頂點vvv有邊連接的所有頂點www
 - w≠vw \neq vw?=v表示頂點vvv以外的所有頂點
 - YvY_vYv?與YwY_wYw?為頂點vvv與www對應的隨機變量
 
只基于Y序列做預測, 太單調了, 所以額外給出一個觀測序列X, 幫助你更好的做決策. 這就是從馬爾可夫隨機場變成條件隨機場的過程. 條件隨機場中, "條件"指的是給定觀測序列X的情況, 求狀態序列Y的概率, 即輸出的是條件概率分布. 而"隨機場"指的是狀態序列Y構成的隨機場.
線性鏈條件隨機場:
常見的標注場景, 如分詞, 實體識別和詞性標注等, 都是典型的線性鏈條件隨機場. 那么什么是線性鏈條件隨機場呢?
 XXX和YYY有相同結構(線性表示的隨機變量序列)的條件隨機場就構成了線性鏈條件隨機場. (這里指的線性, 指語言天然具有的先后順序,成線性特性.)
定義:
 設X=(X1,X2,...,Xn)X=(X_1, X_2, ..., X_n)X=(X1?,X2?,...,Xn?), Y=(Y1,Y2,...,Yn)Y=(Y_1, Y_2, ..., Y_n)Y=(Y1?,Y2?,...,Yn?)均為線性表示的隨機變量序列,
 若在給定隨機變量序列XXX的條件下,
 隨機變量序列YYY的條件概率分布P(Y∣X)P(Y|X)P(Y∣X)構成條件隨機場, 即滿足馬爾可夫性
 P(Yi∣X,Y1,...,Yi?1,Yi+1,...,Yn)=P(Yi∣X,Yi?1,Yi+1)P(Y_i|X, Y_1, ..., Y_{i-1}, Y_{i+1}, ..., Y_n) = P(Y_i|X, Y_{i-1}, Y_{i+1})P(Yi?∣X,Y1?,...,Yi?1?,Yi+1?,...,Yn?)=P(Yi?∣X,Yi?1?,Yi+1?)
 則稱P(Y∣X)P(Y|X)P(Y∣X)為線性鏈條件隨機場. 其中i=(1,2,..n)i=(1,2,..n)i=(1,2,..n), 在i=1i=1i=1和i=ni=ni=n時只考慮單邊.
兩種主要的線性鏈條件隨機場的圖結構如下:
 
 由于線性鏈條件隨機場應用非常廣泛, 所以習慣把"線性鏈條件隨機場"簡稱為條件隨機場(CRF).
CRF公式
根據Hammersley Clifford定理, 一個無向圖模型的概率, 可以表示為定義在圖上所有最大團上的勢函數的乘積.
 那么, CRF的條件概率可以在因子分解下表示為:
 
 線性鏈CRF的因子分解
 如上圖, 線性鏈CRF的因子分解, 根據函數類型, 可細化為:
 P(y∣x)=1Z(x)exp?(∑i,kλktk(yi?1,yi,x,i)+∑i,lμlsl(yi,x,i))P(y|x) = \frac{1}{Z(x)} \exp (\sum_{i,k}\lambda_k t_k(y_{i-1}, y_i, x, i) + \sum_{i, l}\mu_l s_l (y_i, x, i))P(y∣x)=Z(x)1?exp(∑i,k?λk?tk?(yi?1?,yi?,x,i)+∑i,l?μl?sl?(yi?,x,i))
 其中,
 Z(x)=∑yexp?(∑i,kλktk(yi?1,yi,x,i)+∑i,lμlsl(yi,x,i))Z(x) = \sum_y \exp (\sum_{i,k}\lambda_k t_k(y_{i-1}, y_i, x, i) + \sum_{i,l}\mu_l s_l(y_i, x, i))Z(x)=∑y?exp(∑i,k?λk?tk?(yi?1?,yi?,x,i)+∑i,l?μl?sl?(yi?,x,i))
- tkt_ktk?為轉移特征函數, 在CRF++中轉移特征由Bigram Template + 輸入字(詞)序列生成
 - sls_lsl?為狀態特征函數, 在CRF++中狀態特征由Uigram Template + 輸入字(詞)序列生成
 
CRF中, 通常有兩類特征函數, 分別為轉移特征和狀態特征. 狀態特征表示輸入序列與當前狀態之間的關系. 轉移特征表示前一個輸出狀態與當前輸出狀態之間的關系. CRF++中, 特征模板需要人工設定, 有一定的局限性. 若結合深度神經網絡, 如BERT+CRF, 則特征就可由模型自動學習得到, 具體講, BERT學習狀態特征, CRF學習轉移特征, 并用viterbi獲取最優路徑.
理論講了很多, 接下來講講CRF的實現代碼, 以牛逼的CRF++為例.
CRF++實現詳解
https://taku910.github.io/crfpp/ 是開源的CRF++的工具源代碼.
CRF++中, 核心功能分為模型訓練和預測兩部分.
模型訓練
整個訓練過程分為三步:
- 準備訓練數據集
 - 通過定義特征模板, 自動生成特征函數集
 - 通過LBFGS算法, 學習特征函數的權重
 
訓練數據集
訓練數據集, 按照文檔要求準備即可, 例子如下:
山 B_PROV
 東 I_PROV
 省 I_PROV
 菏 B_CITY
 澤 I_CITY
 市 I_CITY
 牡 B_DIST
 丹 I_DIST
 區 I_DIST
 重 B_ROAD
 慶 I_ROAD
 路 I_ROAD
特征函數定義
特征模板格式:%x[row,col]。x可取U或B,對應兩種類型。方括號里的編號用于標定特征來源,row表示相對當前位置的行,0即是當前行;col對應訓練文件中的列。這里只使用第1列(編號0),即文字。若值為1, 則使用標簽值.
定義特征模板:
# Unigram
 U00:%x[-2,0]
 U01:%x[-1,0]
 U02:%x[0,0]
 U03:%x[1,0]
 U04:%x[2,0]
 U05:%x[-2,0]/%x[-1,0]/%x[0,0]
 U06:%x[-1,0]/%x[0,0]/%x[1,0]
 U07:%x[0,0]/%x[1,0]/%x[2,0]
 U08:%x[-1,0]/%x[0,0]
 U09:%x[0,0]/%x[1,0]
 # Bigram
 B
特征函數, 前面提到過, 分為兩種, 轉移特征和狀態特征. 特征模板中的Unigram or Bigram, 是針對輸出序列YYY, 即標注label而言的. 如轉移特征tk(yi?1,yi,x,i)t_k(y_{i-1}, y_i, x, i)tk?(yi?1?,yi?,x,i), 涉及到yi?1y_{i-1}yi?1?和yiy_iyi?, 所以叫Bigram特征. 狀態特征sl(yi,x,i)s_l(y_i, x, i)sl?(yi?,x,i)只涉及到yiy_iyi?, 所以叫Unigram特征.
以訓練集中的第一行為例, 基于上面的特征模板, 生成10個特征 (此時還未與label進行結合):
U00:%x[-2, 0] =>“U00:_B-2” (這里的_B-2指的是beginning of sentence)
 U01:%x[-1, 0] => “U01:_B-1” (_B-1, _B-2均為指代)
 U02:%x[0, 0] => “U02:山”
 U05:%x[-2,0]/%x[-1,0]/%x[0,0] => “U05:_B-2/_B-1/山”
 U06:%x[-1,0]/%x[0,0]/%x[1,0] => “U06:_B-1/山/東”
 U07:%x[0,0]/%x[1,0]/%x[2,0] => “U07:山/東/省”
 U08:%x[-1,0]/%x[0,0] => “U08:_B-1/山”
 U09:%x[0,0]/%x[1,0] => “U09:山/東”
基于輸入序列生成的單邊特征, 再與標簽集合(Y集合)結合, 生成最終的特征函數.
 若特征類型為Unigram, 每行模板生成一組狀態特征函數,數量是L*N 個,L是標簽狀態數。N是此行模板在訓練集上展開后的去重后的樣本數, 如:
func0 = if (output = B_PROV and feature=“U02:山”) return 1 else return 0
 func1 = if (output = I_PROV and feature=“U02:山”) return 1 else return 0
 func1 = if (output = B_CITY and feature=“U02:山”) return 1 else return 0
 …
若特征類型為Bigram, 每行模板生成一組轉移特征函數, 數量是L*L*N 個。經過訓練后,這些函數的權值反映了上一個節點的標簽對當前節點的影響。例如對應 B00:%x[0, 0]:
func0 = if (prev_output = B_PROV and output = B_PROV and feature=“U02:山”) return 1 else return 0
 func1 = if (prev_output = B_PROV and output = I_PROV and feature=“U02:山”) return 1 else return 0
 func2 = if (prev_output = B_PROV and output = B_CITY and feature=“U02:山”) return 1 else return 0
 …
 funcN = if (prev_output = I_DIST and output = B_PROV and feature=“U02:山”) return 1 else return 0
 …
現實中, 若Bigram模板的定義中, 涉及輸入序列, 容易導致特征數巨大, 所以默認只使用簡單的B, 即簡化的轉移特征函數tk(yi?1,yi)t_k(y_{i-1}, y_i)tk?(yi?1?,yi?), 與輸入序列無關.
模型預測
HMM, MEMM vs. CRF對比
總結
                            
                        - 上一篇: 踩坑内核参数tcp_tw_recycle
 - 下一篇: 远程读取mysql_远程获取数据库和文件