BiLSTM+CRF:
如果看了之後還看不懂,我自罰三杯!!!
參考的是國外一個很好的博客,原文鏈接:https://createmomo.github.io/2017/12/06/CRF-Layer-on-the-Top-of-BiLSTM-7/
現在抽空學習一下知識圖譜方面的知識
1、Introduction:
1.1 開始之前:
假設我們有兩個實體類別:person+location。在我們的數據集中,我們有五個標籤,B-person、I-person、B-location、I- location以及標籤O。在一個句子x(小明生活在北京)中,小明是一個person實體,北京是一個location實體。其他的元素如“生”“活”“在”都屬於O。
1.2 BiLSTM-CRF模型:
BilSTM-CRF模型輸入:wi是句子中每一個元素,轉換成字向量之後作爲輸入。
BilSTM-CRF模型輸出:每一個元素相對應的標籤。
BiLSTM模型輸出:每一個元素對每個標籤的概率,下圖中黃色部分。這些會作爲CRF的輸入。
1.3 如果沒有CRF層
如果沒有CRF層,我們貌似也可以BiLSTM層每個元素輸出的最大概率標籤當做最後結果,如下圖所示,w0的最大概率輸出標籤是B-person,那麼直接把這個元素最後結果當做B-person,相應的w1是I-person,等等。
上面說的貌似是正確的,但這種情況下元素之間的標籤不存在約束關係,即B-person之後絕對不能跟B-person,I-person之前絕對不能是I-organization,所以就會出現下面的錯誤。
1.4 CRF層能夠學習到上面所說的限制關係
CRF層能夠對最後的結果加上一些限制來確保結果是正確的,這些限制可以在訓練過程中通過訓練集自動學習得到。這些限制可能有:
- 句子中的第一個字可能是B-或者O,不能是I-
- B-label I-label2 I-label3中,label1、label2、label3應該是相同的實體label。也就是B-person後面不能跟I-location
- O後面不能跟I-label,這是明顯的。
2 、CRF層:
在CRF層,存在兩種類型的scores,這個兩種scores是crf層重要部分。
2.1 Emission score:
Emission scores主要來自於BiLSTM層,如下圖所示,第一個元素對於B-person的得分是1.5。
爲了方便觀察,給每個label加 一個index。
我們使用xiyj來代表emission score,其中i表示第幾個元素,yj表示label的index。例如上上個圖中,xi=1,yj=2= xw1,B-organization=0.1,代表的是第二個元素是B-organization的得分。
2.2 Transition score
tyiyj表示transition score,例如,tB-person,I-person=0.9意味着B-person到下一個字是I-person的概率是0.9,因此,transition包含所有標籤之間的轉移概率。爲了是transition矩陣更魯棒,我們新加兩個label,start和end,start表示一個句子的開始,並不是第一個字,end表示句子的結束。樣例如下圖所示:
顯然上述矩陣是存在一些限制關係的,如:transition矩陣從start到I-person和I-organization的概率是很低的,因爲第一個字應該以B-或者O-開始,而不是I-;B-person到I-organization的概率很低,因爲不能從一個label的開始到另一個標籤的中間;O-到I-的概率很低,因爲不可能呀!!!!那麼如何獲得這個矩陣呢?
實際上,transition矩陣是整個模型的一個參數,訓練之前,你可以隨機初始化這個矩陣,在訓練過程中,這個矩陣會不斷的更新,換言之,CRF層能夠學習到這個矩陣。
2.3 CRF lossfunction
這一層的損失包含真實路徑和所有路徑的得分和,真實路徑應該是最高得分。(真實路徑也就是每個元素的真是標籤,舉個例子,一個句子有5個元素,標籤數量是7個,那麼路徑個數是7^5,如下圖所示,每條路徑都有一個得分,也可以說是一個概率,真實路徑只有一個,使其最大就好了)
那麼總得分就是Ptotal=P1+ P2+……+ Pn=eS1+ eS2+……+ eSn,e是自然對數,下一節會介紹如何計算Si。損失計算公式如下:
LossFunction=PRealPath/( P1+ P2+……+ Pn)
在訓練期間,參數的更新使這個比重越來越大。
- 如何計算每個路徑的得分
- 如何計算所有路徑的總得分
- 計算總得分的時候,有必要列出來所有路徑嗎(顯然是NO)
2.4 真實路徑
在訓練過程中,lossFunction只需要兩個得分,真實路徑score+全部路徑的score。
真實路徑score的計算:句子有5個元素,7個label,真實路徑是START B-Person I-Person O B-Organization O END
Si=EmissionScore+TransitionScore
EmissionScore=(x0,START)+(x1,B−Person)+(x2,I−Person)+(x3,O)+(x4,B−Organization)+(x5,O)+(x6,END)
x1,B−Person是第一個字是B-person的概率,這就是BiLSTM這一層的輸出呀
x0,START和x6,END我們賦值爲0
TransitionScore=tSTART−>B−Person + tB−Person−>I−Person + tI−Person−>O + t0−>B−Organization + tB−Organization−>O + tO−>END
tB−Person−>I−Person是CRF層transition的參數,表示B-person到I-person的轉移概率
2.5 全路徑score
可以計算出所有路徑的score然後相加得到全路徑的得分,但這樣的話計算量是相當大的,訓練是很耗時的。
下面的例子會讓你瞭解如何快速計算全路徑的score,一步一步來,不着急,你會明白其中的奧妙。
Step1:LossFunction=PRealPath/( P1+ P2+……+ Pn)
LogLossFunction=log(PRealPath/( P1+ P2+……+ Pn))
最小化
Step2:爲了更好的描述,我們假設我們的句子長度只有3。 X=[w0, w1, w2];兩個label={ l1, l2}
假設emission score情況如下:xij表示wi是lj的概率,即BiLSTM層的輸出
Transition矩陣如下:tij表示label從i到j的概率
Step3: 我們的目標是計算log(eS1+ eS1+ ...+eSN)
整個計算過程和動態規劃想類似。所有可能路徑的總得分w0是已經計算好的,然後我們計算w0->w1的總得分,再計算 w0->w1->w2。在下面的步驟中,你會看到兩個變量,obs和previous。previous儲存了前一步的結果,obs表示當前字的 信息。
w0:
obs=[x01, x02]
previous=None
如果我們句子只有一個字,我們就不需要從previous中獲取結果了,因此previous是None。那麼很顯然,所有路徑的得分 就是log(ex01+ ex02)。
w0->w1:
obs=[ x11, x12]
previous=[x01, x02]
1.把previous和obs擴展成矩陣:
爲什麼要擴展呢?因爲擴展之後纔能有效計算所有路徑的總得分呀。
2.計算previous、obs和transition score的和:
舉個簡單的例子你就理解了,假設只有兩個元素,兩個label,那麼路徑是不是有4條,即上面scores裏面的東西, scores 裏面每個元素的得分就是一個路徑的得分,包括emission score和transition score,這也是爲什麼要把previous 和obs擴展成矩陣了,因爲這樣元素之間的相加可以羅列出所有的矩陣呀。那麼下一次迭代的previous是什麼呢,就是我 們的scores呀,只不過樣式需要變換一下:
爲什麼要這種樣式呢?我們分步計算一下就知道了:
最後這個結果和直接求e求和再求log的效果是一樣的。看着previous是兩個元素,實際上還有四條路徑。
w0->w1->w2:
接下來的迭代和上面的幾乎是一樣的了。
obs=[ x21, x22]
previous=[log(ex01+x11+t11+ ex02+x11+t21), log(ex01+x12+t12+ ex02+x12+t22)]
1.把previous和obs擴展成矩陣:
2.計算previous、obs和transition score的和:
下一次迭代的previous就是:看着複雜,實際很簡單。
分步計算可以知道,三個元素時,一共有8條路徑,
發現沒?完整的8條路徑,就是全路徑的得分了!!!
2.6 如果預測一個句子的label
Step1:emission score和transition score
假設句子還是三個字:X=[w0, w1, w2],且已經得到相應的emission score和transition score。
Step2:如果你瞭解Viterbi算法(之前瞭解過,貌似忘了,之前的博客有介紹HMM算法,和Viterbi差不多)。α0是歷史最好 得分 。α1是相應標籤的indexs。
w0:
obs=[x01, x02]
previous=None
現在我們只觀察到第一個字w0,如果obs=[ x01=0.2, x02=0.8],顯然,w0最好的結果就是l2。
w0->w1:
obs=[ x11, x12]
previous=[x01, x02]
(1)老規矩,把previous和obs擴展成矩陣:、
(2)計算previous、obs和transition矩陣的和:
到這裏你會不會覺得這和之前求總得分的步驟是一樣呀,不要着急,慢慢看。
previous=[max(scores[00], scores[10]), max(scores[01], scores[11])]
例如,如果我的得分是
那麼我們下一次迭代的previous就會變成
previous=[max(scores[00], scores[10]), max(scores[01], scores[11])] = [0.5, 0.4]
這裏previous存儲的是前一個字的每一個label的最大得分,和HMM很像啊啊啊啊啊!
Example Start:
上述例子中,我們只有兩個label,這兩個label的index是0和1,。previous[0]表示到達前一個字是label1的最大 值,同理,previous[1]表示到達前一個字是label2的最大值。每一次迭代中,變量previous存儲到達每一個 label的最大得分值。換言之,每一次迭代中,我們都存儲了到達每一個label的最優路徑。
Example End:
我們上面提到過,我們還有兩個變量去存儲歷史信息α0和α1。
迭代時,我們把最優得分信息放入α0:
α0 = [(scores[10], scores[11])] = [(0.5, 0.4)]
α1中存入相應的列座標:
α1 = [(ColumnIndex(scores[10]), ColumnIndex(scores[11]))] = [(1, 1)]
如何解釋呢,l1的下標是0,l2的下標是1,所以(1, 1)=(l2, l2)表示當前字wi和li。具體來說就是對於w1 時,到達l1(這個l1是(l2, l2)中第一個元素l2所以在位置是0)的最大值的上一個標籤是l2,即到達0.5的路 徑是l(i-1) = l2 –> l(i) = l1。
到達l2(這個l2是(l2, l2)中第二個元素l2所以在位置是1)的最大值的上一個標籤是l2,即到達0.4的路徑是
l(i-1) = l2 –> l(i) = l2。
l(i-1)指的是字w(i-1)的標籤。相當於保存了路徑。
w0->w1->w2:
接下來的迭代和上面的幾乎是一樣的了。
obs=[ x21, x22]
previous=[0.5, 0.4]
1.把previous、obs擴展成矩陣:
- 然後還是求和:
- 然後計算找到最大值,更新previous:
previous=[max(scores[00], scores[10]), max(scores[01], scores[11])]
假設這個得分是:
那麼previous=[0.8, 0.9],實際上previous[0]和previous[1]中最大的一個就是最優路徑的得分
同時,我們需要更新α0和α1:
α0 = [(0,5, 0.4), (scores[10], scores[01])] = [(0,5, 0.4), (0,8 0.9)]
α1 = [(1, 1), (1, 0)]
我覺得我有必要再強調一下α1,現在到達w2的l1和l2的最優路徑分別是l2 –>l1 ->l2和l2 –>l2 ->l1
Step3:找到最高得分的最優路徑:
實際上面已經說過了,α1中保存的就是最優路徑,只要復現出來就ok了。算了,還是再說一遍吧。
w1->w2:
α0 = [(0,5, 0.4), (0,8 0.9)]
α1 = [(1, 1), (1, 0)]
最大得分是0.9呀,相當於w2的 標籤是l2;再看α1,到達l2的上一個字w1的標籤是l1,所以最後一條連線就是 l1- l2
w0->w1:
上一個w1的標籤是l1,那麼到達l1的上一個字w0的標籤是l2,所以整個路徑就是l2 –>l1 ->l2。
3、Chainer Implementation
終於到代碼實現了,但作者使用chainer實現的,所以不寫了吧,畢竟沒用過,改天貼上自己的代碼,整個算法過程肯定理解了吧。