鍍金池/ 教程/ 人工智能/ 語義角色標(biāo)注
識(shí)別數(shù)字
線性回歸
情感分析
詞向量
個(gè)性化推薦
圖像分類
語義角色標(biāo)注
機(jī)器翻譯

語義角色標(biāo)注

本教程源代碼目錄在book/label_semantic_roles, 初次使用請(qǐng)參考PaddlePaddle安裝教程。

背景介紹

自然語言分析技術(shù)大致分為三個(gè)層面:詞法分析、句法分析和語義分析。語義角色標(biāo)注是實(shí)現(xiàn)淺層語義分析的一種方式。在一個(gè)句子中,謂詞是對(duì)主語的陳述或說明,指出“做什么”、“是什么”或“怎么樣,代表了一個(gè)事件的核心,跟謂詞搭配的名詞稱為論元。語義角色是指論元在動(dòng)詞所指事件中擔(dān)任的角色。主要有:施事者(Agent)、受事者(Patient)、客體(Theme)、經(jīng)驗(yàn)者(Experiencer)、受益者(Beneficiary)、工具(Instrument)、處所(Location)、目標(biāo)(Goal)和來源(Source)等。

請(qǐng)看下面的例子,“遇到” 是謂詞(Predicate,通常簡(jiǎn)寫為“Pred”),“小明”是施事者(Agent),“小紅”是受事者(Patient),“昨天” 是事件發(fā)生的時(shí)間(Time),“公園”是事情發(fā)生的地點(diǎn)(Location)。

$$\mbox{[小明]}_{\mbox{Agent}}\mbox{[昨天]}_{\mbox{Time}}\mbox{[晚上]}_\mbox{Time}\mbox{在[公園]}_{\mbox{Location}}\mbox{[遇到]}_{\mbox{Predicate}}\mbox{了[小紅]}_{\mbox{Patient}}\mbox{。}$$

語義角色標(biāo)注(Semantic Role Labeling,SRL)以句子的謂詞為中心,不對(duì)句子所包含的語義信息進(jìn)行深入分析,只分析句子中各成分與謂詞之間的關(guān)系,即句子的謂詞(Predicate)- 論元(Argument)結(jié)構(gòu),并用語義角色來描述這些結(jié)構(gòu)關(guān)系,是許多自然語言理解任務(wù)(如信息抽取,篇章分析,深度問答等)的一個(gè)重要中間步驟。在研究中一般都假定謂詞是給定的,所要做的就是找出給定謂詞的各個(gè)論元和它們的語義角色。

傳統(tǒng)的SRL系統(tǒng)大多建立在句法分析基礎(chǔ)之上,通常包括5個(gè)流程:

  1.構(gòu)建一棵句法分析樹,例如,圖1是對(duì)上面例子進(jìn)行依存句法分析得到的一棵句法樹。
  2.從句法樹上識(shí)別出給定謂詞的候選論元。
  3.候選論元剪除;一個(gè)句子中的候選論元可能很多,候選論元剪除就是從大量的候選項(xiàng)中剪除那些最不可能成為論元的候選項(xiàng)。
  4.論元識(shí)別:這個(gè)過程是從上一步剪除之后的候選中判斷哪些是真正的論元,通常當(dāng)做一個(gè)二分類問題來解決。
  5.對(duì)第4步的結(jié)果,通過多分類得到論元的語義角色標(biāo)簽??梢钥吹?,句法分析是基礎(chǔ),并且后續(xù)步驟常常會(huì)構(gòu)造的一些人工特征,這些特征往往也來自句法分析。

http://wiki.jikexueyuan.com/project/deep-learning/images/07-01.png" alt="png" />
圖1. 依存句法分析句法樹示例

然而,完全句法分析需要確定句子所包含的全部句法信息,并確定句子各成分之間的關(guān)系,是一個(gè)非常困難的任務(wù),目前技術(shù)下的句法分析準(zhǔn)確率并不高,句法分析的細(xì)微錯(cuò)誤都會(huì)導(dǎo)致SRL的錯(cuò)誤。為了降低問題的復(fù)雜度,同時(shí)獲得一定的句法結(jié)構(gòu)信息,“淺層句法分析”的思想應(yīng)運(yùn)而生。淺層句法分析也稱為部分句法分析(partial parsing)或語塊劃分(chunking)。和完全句法分析得到一顆完整的句法樹不同,淺層句法分析只需要識(shí)別句子中某些結(jié)構(gòu)相對(duì)簡(jiǎn)單的獨(dú)立成分,例如:動(dòng)詞短語,這些被識(shí)別出來的結(jié)構(gòu)稱為語塊。為了回避 “無法獲得準(zhǔn)確率較高的句法樹” 所帶來的困難,一些研究[1]也提出了基于語塊(chunk)的SRL方法。基于語塊的SRL方法將SRL作為一個(gè)序列標(biāo)注問題來解決。序列標(biāo)注任務(wù)一般都會(huì)采用BIO表示方式來定義序列標(biāo)注的標(biāo)簽集,我們先來介紹這種表示方法。在BIO表示法中,B代表語塊的開始,I代表語塊的中間,O代表語塊結(jié)束。通過B、I、O 三種標(biāo)記將不同的語塊賦予不同的標(biāo)簽,例如:對(duì)于一個(gè)角色為A的論元,將它所包含的第一個(gè)語塊賦予標(biāo)簽B-A,將它所包含的其它語塊賦予標(biāo)簽I-A,不屬于任何論元的語塊賦予標(biāo)簽O。

我們繼續(xù)以上面的這句話為例,圖1展示了BIO表示方法。

http://wiki.jikexueyuan.com/project/deep-learning/images/07-02.png" alt="png" />
圖2. BIO標(biāo)注方法示例

從上面的例子可以看到,根據(jù)序列標(biāo)注結(jié)果可以直接得到論元的語義角色標(biāo)注結(jié)果,是一個(gè)相對(duì)簡(jiǎn)單的過程。這種簡(jiǎn)單性體現(xiàn)在:
  (1)依賴淺層句法分析,降低了句法分析的要求和難度;
  (2)沒有了候選論元剪除這一步驟;
  (3)論元的識(shí)別和論元標(biāo)注是同時(shí)實(shí)現(xiàn)的。
這種一體化處理論元識(shí)別和論元標(biāo)注的方法,簡(jiǎn)化了流程,降低了錯(cuò)誤累積的風(fēng)險(xiǎn),往往能夠取得更好的結(jié)果。

與基于語塊的SRL方法類似,在本教程中我們也將SRL看作一個(gè)序列標(biāo)注問題,不同的是,我們只依賴輸入文本序列,不依賴任何額外的語法解析結(jié)果或是復(fù)雜的人造特征,利用深度神經(jīng)網(wǎng)絡(luò)構(gòu)建一個(gè)端到端學(xué)習(xí)的SRL系統(tǒng)。我們以CoNLL-2004 and CoNLL-2005 Shared Tasks任務(wù)中SRL任務(wù)的公開數(shù)據(jù)集為例,實(shí)踐下面的任務(wù):給定一句話和這句話里的一個(gè)謂詞,通過序列標(biāo)注的方式,從句子中找到謂詞對(duì)應(yīng)的論元,同時(shí)標(biāo)注它們的語義角色。

模型概覽

循環(huán)神經(jīng)網(wǎng)絡(luò)(Recurrent Neural Network)是一種對(duì)序列建模的重要模型,在自然語言處理任務(wù)中有著廣泛地應(yīng)用。不同于前饋神經(jīng)網(wǎng)絡(luò)(Feed-forward Neural Network),RNN能夠處理輸入之間前后關(guān)聯(lián)的問題。LSTM是RNN的一種重要變種,常用來學(xué)習(xí)長(zhǎng)序列中蘊(yùn)含的長(zhǎng)程依賴關(guān)系,我們?cè)?a rel="nofollow" >情感分析一篇中已經(jīng)介紹過,這一篇中我們依然利用LSTM來解決SRL問題。

棧式循環(huán)神經(jīng)網(wǎng)絡(luò)(Stacked Recurrent Neural Network)

深層網(wǎng)絡(luò)有助于形成層次化特征,網(wǎng)絡(luò)上層在下層已經(jīng)學(xué)習(xí)到的初級(jí)特征基礎(chǔ)上,形成更復(fù)雜的高級(jí)特征。盡管LSTM沿時(shí)間軸展開后等價(jià)于一個(gè)非?!吧睢钡那梆伨W(wǎng)絡(luò),但由于LSTM各個(gè)時(shí)間步參數(shù)共享,$t-1$時(shí)刻狀態(tài)到$t$時(shí)刻的映射,始終只經(jīng)過了一次非線性映射,也就是說單層LSTM對(duì)狀態(tài)轉(zhuǎn)移的建模是 “淺” 的。堆疊多個(gè)LSTM單元,令前一個(gè)LSTM$t$時(shí)刻的輸出,成為下一個(gè)LSTM單元$t$時(shí)刻的輸入,幫助我們構(gòu)建起一個(gè)深層網(wǎng)絡(luò),我們把它稱為第一個(gè)版本的棧式循環(huán)神經(jīng)網(wǎng)絡(luò)。深層網(wǎng)絡(luò)提高了模型擬合復(fù)雜模式的能力,能夠更好地建模跨不同時(shí)間步的模式[2]。

然而,訓(xùn)練一個(gè)深層LSTM網(wǎng)絡(luò)并非易事??v向堆疊多個(gè)LSTM單元可能遇到梯度在縱向深度上傳播受阻的問題。通常,堆疊4層LSTM單元可以正常訓(xùn)練,當(dāng)層數(shù)達(dá)到4~8層時(shí),會(huì)出現(xiàn)性能衰減,這時(shí)必須考慮一些新的結(jié)構(gòu)以保證梯度縱向順暢傳播,這是訓(xùn)練深層LSTM網(wǎng)絡(luò)必須解決的問題。我們可以借鑒LSTM解決 “梯度消失梯度爆炸” 問題的智慧之一:在記憶單元(Memory Cell)這條信息傳播的路線上沒有非線性映射,當(dāng)梯度反向傳播時(shí)既不會(huì)衰減、也不會(huì)爆炸。因此,深層LSTM模型也可以在縱向上添加一條保證梯度順暢傳播的路徑。

一個(gè)LSTM單元完成的運(yùn)算可以被分為三部分:
  (1)輸入到隱層的映射(input-to-hidden) :每個(gè)時(shí)間步輸入信息$x$會(huì)首先經(jīng)過一個(gè)矩陣映射,再作為遺忘門,輸入門,記憶單元,輸出門的輸入,注意,這一次映射沒有引入非線性激活;
  (2)隱層到隱層的映射(hidden-to-hidden):這一步是LSTM計(jì)算的主體,包括遺忘門,輸入門,記憶單元更新,輸出門的計(jì)算;
  (3)隱層到輸出的映射(hidden-to-output):通常是簡(jiǎn)單的對(duì)隱層向量進(jìn)行激活。我們?cè)诘谝粋€(gè)版本的棧式網(wǎng)絡(luò)的基礎(chǔ)上,加入一條新的路徑:除上一層LSTM輸出之外,將前層LSTM的輸入到隱層的映射作為的一個(gè)新的輸入,同時(shí)加入一個(gè)線性映射去學(xué)習(xí)一個(gè)新的變換。

圖3是最終得到的棧式循環(huán)神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu)示意圖。

http://wiki.jikexueyuan.com/project/deep-learning/images/07-03.png" alt="png" />
圖3. 基于LSTM的棧式循環(huán)神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu)示意圖

雙向循環(huán)神經(jīng)網(wǎng)絡(luò)(Bidirectional Recurrent Neural Network)

在LSTM中,$t$時(shí)刻的隱藏層向量編碼了到$t$時(shí)刻為止所有輸入的信息,但$t$時(shí)刻的LSTM可以看到歷史,卻無法看到未來。在絕大多數(shù)自然語言處理任務(wù)中,我們幾乎總是能拿到整個(gè)句子。這種情況下,如果能夠像獲取歷史信息一樣,得到未來的信息,對(duì)序列學(xué)習(xí)任務(wù)會(huì)有很大的幫助。

為了克服這一缺陷,我們可以設(shè)計(jì)一種雙向循環(huán)網(wǎng)絡(luò)單元,它的思想簡(jiǎn)單且直接:對(duì)上一節(jié)的棧式循環(huán)神經(jīng)網(wǎng)絡(luò)進(jìn)行一個(gè)小小的修改,堆疊多個(gè)LSTM單元,讓每一層LSTM單元分別以:正向、反向、正向 …… 的順序?qū)W習(xí)上一層的輸出序列。于是,從第2層開始,$t$時(shí)刻我們的LSTM單元便總是可以看到歷史和未來的信息。圖4是基于LSTM的雙向循環(huán)神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu)示意圖。

http://wiki.jikexueyuan.com/project/deep-learning/images/07-04.png" alt="png" />
圖4. 基于LSTM的雙向循環(huán)神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu)示意圖

需要說明的是,這種雙向RNN結(jié)構(gòu)和Bengio等人在機(jī)器翻譯任務(wù)中使用的雙向RNN結(jié)構(gòu)[3, 4] 并不相同,我們會(huì)在后續(xù)機(jī)器翻譯任務(wù)中,介紹另一種雙向循環(huán)神經(jīng)網(wǎng)絡(luò)。

條件隨機(jī)場(chǎng) (Conditional Random Field)

使用神經(jīng)網(wǎng)絡(luò)模型解決問題的思路通常是:前層網(wǎng)絡(luò)學(xué)習(xí)輸入的特征表示,網(wǎng)絡(luò)的最后一層在特征基礎(chǔ)上完成最終的任務(wù)。在SRL任務(wù)中,深層LSTM網(wǎng)絡(luò)學(xué)習(xí)輸入的特征表示,條件隨機(jī)場(chǎng)(Conditional Random Filed, CRF)在特征的基礎(chǔ)上完成序列標(biāo)注,處于整個(gè)網(wǎng)絡(luò)的末端。

CRF是一種概率化結(jié)構(gòu)模型,可以看作是一個(gè)概率無向圖模型,結(jié)點(diǎn)表示隨機(jī)變量,邊表示隨機(jī)變量之間的概率依賴關(guān)系。簡(jiǎn)單來講,CRF學(xué)習(xí)條件概率$P(X|Y)$,其中 $X = (x_1, x_2, ... , x_n)$ 是輸入序列,$Y = (y_1, y_2, ... , y_n)$ 是標(biāo)記序列;解碼過程是給定 $X$序列求解令$P(Y|X)$最大的$Y$序列,即$Y^* = \mbox{arg max}_{Y} P(Y | X)$。

序列標(biāo)注任務(wù)只需要考慮輸入和輸出都是一個(gè)線性序列,并且由于我們只是將輸入序列作為條件,不做任何條件獨(dú)立假設(shè),因此輸入序列的元素之間并不存在圖結(jié)構(gòu)。綜上,在序列標(biāo)注任務(wù)中使用的是如圖5所示的定義在鏈?zhǔn)綀D上的CRF,稱之為線性鏈條件隨機(jī)場(chǎng)(Linear Chain Conditional Random Field)。

http://wiki.jikexueyuan.com/project/deep-learning/images/07-05.png" alt="png" />
圖5. 序列標(biāo)注任務(wù)中使用的線性鏈條件隨機(jī)場(chǎng)

根據(jù)線性鏈條件隨機(jī)場(chǎng)上的因子分解定理[5],在給定觀測(cè)序列$X$時(shí),一個(gè)特定標(biāo)記序列$Y$的概率可以定義為:

$$p(Y | X) = \frac{1}{Z(X)} \text{exp}\left(\sum_{i=1}^{n}\left(\sum_{j}\lambda_{j}t_{j} (y_{i - 1}, y_{i}, X, i) + \sum_{k} \mu_k s_k (y_i, X, i)\right)\right)$$

其中$Z(X)$是歸一化因子,$t_j$ 是定義在邊上的特征函數(shù),依賴于當(dāng)前和前一個(gè)位置,稱為轉(zhuǎn)移特征,表示對(duì)于輸入序列$X$及其標(biāo)注序列在 $i$及$i - 1$位置上標(biāo)記的轉(zhuǎn)移概率。$s_k$是定義在結(jié)點(diǎn)上的特征函數(shù),稱為狀態(tài)特征,依賴于當(dāng)前位置,表示對(duì)于觀察序列$X$及其$i$位置的標(biāo)記概率。$\lambda_j$ 和 $\mu_k$ 分別是轉(zhuǎn)移特征函數(shù)和狀態(tài)特征函數(shù)對(duì)應(yīng)的權(quán)值。實(shí)際上,$t$和$s$可以用相同的數(shù)學(xué)形式表示,再對(duì)轉(zhuǎn)移特征和狀態(tài)特在各個(gè)位置$i$求和有:$f_{k}(Y, X) = \sum_{i=1}^{n}f_k({y_{i - 1}, y_i, X, i})$,把$f$統(tǒng)稱為特征函數(shù),于是$P(Y|X)$可表示為:

$$p(Y|X, W) = \frac{1}{Z(X)}\text{exp}\sum_{k}\omega_{k}f_{k}(Y, X)$$

$\omega$是特征函數(shù)對(duì)應(yīng)的權(quán)值,是CRF模型要學(xué)習(xí)的參數(shù)。訓(xùn)練時(shí),對(duì)于給定的輸入序列和對(duì)應(yīng)的標(biāo)記序列集合$D = \left[(X_1, Y_1), (X_2 , Y_2) , ... , (X_N, Y_N)\right]$ ,通過正則化的極大似然估計(jì),求解如下優(yōu)化目標(biāo):

$$\DeclareMathOperator*{\argmax}{arg\,max} L(\lambda, D) = - \text{log}\left(\prod_{m=1}^{N}p(Y_m|X_m, W)\right) + C \frac{1}{2}\lVert W\rVert^{2}$$

這個(gè)優(yōu)化目標(biāo)可以通過反向傳播算法和整個(gè)神經(jīng)網(wǎng)絡(luò)一起求解。解碼時(shí),對(duì)于給定的輸入序列$X$,通過解碼算法(通常有:維特比算法、Beam Search)求令出條件概率$\bar{P}(Y|X)$最大的輸出序列 $\bar{Y}$。

深度雙向LSTM(DB-LSTM)SRL模型

在SRL任務(wù)中,輸入是 “謂詞” 和 “一句話”,目標(biāo)是從這句話中找到謂詞的論元,并標(biāo)注論元的語義角色。如果一個(gè)句子含有$n$個(gè)謂詞,這個(gè)句子會(huì)被處理$n$次。一個(gè)最為直接的模型是下面這樣:

  構(gòu)造輸入;
    輸入1是謂詞,輸入2是句子
    將輸入1擴(kuò)展成和輸入2一樣長(zhǎng)的序列,用one-hot方式表示;
  one-hot方式的謂詞序列和句子序列通過詞表,轉(zhuǎn)換為實(shí)向量表示的詞向量序列;
  將步驟2中的2個(gè)詞向量序列作為雙向LSTM的輸入,學(xué)習(xí)輸入序列的特征表示;
  CRF以步驟3中模型學(xué)習(xí)到的特征為輸入,以標(biāo)記序列為監(jiān)督信號(hào),實(shí)現(xiàn)序列標(biāo)注;

大家可以嘗試上面這種方法。這里,我們提出一些改進(jìn),引入兩個(gè)簡(jiǎn)單但對(duì)提高系統(tǒng)性能非常有效的特征:

  謂詞上下文:上面的方法中,只用到了謂詞的詞向量表達(dá)謂詞相關(guān)的所有信息,這種方法始終是非常弱的,特別是如果謂詞在句子中出現(xiàn)多次,有可能引起一定的歧義。從經(jīng)驗(yàn)出發(fā),謂詞前后若干個(gè)詞的一個(gè)小片段,能夠提供更豐富的信息,幫助消解歧義。于是,我們把這樣的經(jīng)驗(yàn)也添加到模型中,為每個(gè)謂詞同時(shí)抽取一個(gè)“謂詞上下文” 片段,也就是從這個(gè)謂詞前后各取$n$個(gè)詞構(gòu)成的一個(gè)窗口片段;
  謂詞上下文區(qū)域標(biāo)記:為句子中的每一個(gè)詞引入一個(gè)0-1二值變量,表示它們是否在“謂詞上下文”片段中;

修改后的模型如下(圖6是一個(gè)深度為4的模型結(jié)構(gòu)示意圖):

  構(gòu)造輸入
    輸入1是句子序列,輸入2是謂詞序列,輸入3是謂詞上下文,從句子中抽取這個(gè)謂詞前后各$n$個(gè)詞,構(gòu)成謂詞上下文,用one-hot方式表示,輸入4是謂詞上下文區(qū)域標(biāo)記,標(biāo)記了句子中每一個(gè)詞是否在謂詞上下文中;
    將輸入2~3均擴(kuò)展為和輸入1一樣長(zhǎng)的序列;   輸入1~4均通過詞表取詞向量轉(zhuǎn)換為實(shí)向量表示的詞向量序列;其中輸入1、3共享同一個(gè)詞表,輸入2和4各自獨(dú)有詞表;
  第2步的4個(gè)詞向量序列作為雙向LSTM模型的輸入;LSTM模型學(xué)習(xí)輸入序列的特征表示,得到新的特性表示序列;
  CRF以第3步中LSTM學(xué)習(xí)到的特征為輸入,以標(biāo)記序列為監(jiān)督信號(hào),完成序列標(biāo)注;

http://wiki.jikexueyuan.com/project/deep-learning/images/07-06.png" alt="png" />
圖6. SRL任務(wù)上的深層雙向LSTM模型

數(shù)據(jù)介紹

在此教程中,我們選用CoNLL 2005SRL任務(wù)開放出的數(shù)據(jù)集作為示例。需要特別說明的是,CoNLL 2005 SRL任務(wù)的訓(xùn)練數(shù)集和開發(fā)集在比賽之后并非免費(fèi)進(jìn)行公開,目前,能夠獲取到的只有測(cè)試集,包括Wall Street Journal的23節(jié)和Brown語料集中的3節(jié)。在本教程中,我們以測(cè)試集中的WSJ數(shù)據(jù)為訓(xùn)練集來講解模型。但是,由于測(cè)試集中樣本的數(shù)量遠(yuǎn)遠(yuǎn)不夠,如果希望訓(xùn)練一個(gè)可用的神經(jīng)網(wǎng)絡(luò)SRL系統(tǒng),請(qǐng)考慮付費(fèi)獲取全量數(shù)據(jù)。

原始數(shù)據(jù)中同時(shí)包括了詞性標(biāo)注、命名實(shí)體識(shí)別、語法解析樹等多種信息。本教程中,我們使用test.wsj文件夾中的數(shù)據(jù)進(jìn)行訓(xùn)練和測(cè)試,并只會(huì)用到words文件夾(文本序列)和props文件夾(標(biāo)注結(jié)果)下的數(shù)據(jù)。本教程使用的數(shù)據(jù)目錄如下:

conll05st-release/
└── test.wsj
    ├── props  # 標(biāo)注結(jié)果
    └── words  # 輸入文本序列

標(biāo)注信息源自Penn TreeBank[7]和PropBank[8]的標(biāo)注結(jié)果。PropBank標(biāo)注結(jié)果的標(biāo)簽和我們?cè)谖恼乱婚_始示例中使用的標(biāo)注結(jié)果標(biāo)簽不同,但原理是相同的,關(guān)于標(biāo)注結(jié)果標(biāo)簽含義的說明,請(qǐng)參考論文[9]。

原始數(shù)據(jù)需要進(jìn)行數(shù)據(jù)預(yù)處理才能被PaddlePaddle處理,預(yù)處理包括下面幾個(gè)步驟:

  1.將文本序列和標(biāo)記序列其合并到一條記錄中;
  2.一個(gè)句子如果含有$n$個(gè)謂詞,這個(gè)句子會(huì)被處理$n$次,變成$n$條獨(dú)立的訓(xùn)練樣本,每個(gè)樣本一個(gè)不同的謂詞;
  3.抽取謂詞上下文和構(gòu)造謂詞上下文區(qū)域標(biāo)記;
  4.構(gòu)造以BIO法表示的標(biāo)記;
  6.依據(jù)詞典獲取詞對(duì)應(yīng)的整數(shù)索引。

# import paddle.v2.dataset.conll05 as conll05
# conll05.corpus_reader函數(shù)完成上面第1步和第2步.
# conll05.reader_creator函數(shù)完成上面第3步到第5步.
# conll05.test函數(shù)可以獲取處理之后的每條樣本來供PaddlePaddle訓(xùn)練.

預(yù)處理完成之后一條訓(xùn)練樣本包含9個(gè)特征,分別是:句子序列、謂詞、謂詞上下文(占 5 列)、謂詞上下區(qū)域標(biāo)志、標(biāo)注序列。下表是一條訓(xùn)練樣本的示例。

句子序列 謂詞 謂詞上下文(窗口 = 5) 謂詞上下文區(qū)域標(biāo)記 標(biāo)注序列
A set n't been set . × 0 B-A1
record set n't been set . × 0 I-A1
date set n't been set . × 0 I-A1
has set n't been set . × 0 O
n't set n't been set . × 1 B-AM-NEG
been set n't been set . × 1 O
set set n't been set . × 1 B-V
. set n't been set . × 1 O

除數(shù)據(jù)之外,我們同時(shí)提供了以下資源:

文件名稱 說明
word_dict 輸入句子的詞典,共計(jì)44068個(gè)詞
label_dict 標(biāo)記的詞典,共計(jì)106個(gè)標(biāo)記
predicate_dict 謂詞的詞典,共計(jì)3162個(gè)詞
emb 一個(gè)訓(xùn)練好的詞表,32維

我們?cè)谟⑽木S基百科上訓(xùn)練語言模型得到了一份詞向量用來初始化SRL模型。在SRL模型訓(xùn)練過程中,詞向量不再被更新。關(guān)于語言模型和詞向量可以參考詞向量 這篇教程。我們訓(xùn)練語言模型的語料共有995,000,000個(gè)token,詞典大小控制為4900,000詞。CoNLL 2005訓(xùn)練語料中有5%的詞不在這4900,000個(gè)詞中,我們將它們?nèi)靠醋魑吹卿浽~,用<unk>表示。

獲取詞典,打印詞典大?。?/p>

import math
import numpy as np
import gzip
import paddle.v2 as paddle
import paddle.v2.dataset.conll05 as conll05
import paddle.v2.evaluator as evaluator

paddle.init(use_gpu=False, trainer_count=1)

word_dict, verb_dict, label_dict = conll05.get_dict()
word_dict_len = len(word_dict)
label_dict_len = len(label_dict)
pred_len = len(verb_dict)

print word_dict_len
print label_dict_len
print pred_len

模型配置說明

  定義輸入數(shù)據(jù)維度及模型超參數(shù)。

mark_dict_len = 2    # 謂上下文區(qū)域標(biāo)志的維度,是一個(gè)0-1 2值特征,因此維度為2
word_dim = 32        # 詞向量維度
mark_dim = 5         # 謂詞上下文區(qū)域通過詞表被映射為一個(gè)實(shí)向量,這個(gè)是相鄰的維度
hidden_dim = 512     # LSTM隱層向量的維度 : 512 / 4
depth = 8            # 棧式LSTM的深度

# 一條樣本總共9個(gè)特征,下面定義了9個(gè)data層,每個(gè)層類型為integer_value_sequence,表示整數(shù)ID的序列類型.
def d_type(size):
    return paddle.data_type.integer_value_sequence(size)

# 句子序列
word = paddle.layer.data(name='word_data', type=d_type(word_dict_len))
# 謂詞
predicate = paddle.layer.data(name='verb_data', type=d_type(pred_len))

# 謂詞上下文5個(gè)特征
ctx_n2 = paddle.layer.data(name='ctx_n2_data', type=d_type(word_dict_len))
ctx_n1 = paddle.layer.data(name='ctx_n1_data', type=d_type(word_dict_len))
ctx_0 = paddle.layer.data(name='ctx_0_data', type=d_type(word_dict_len))
ctx_p1 = paddle.layer.data(name='ctx_p1_data', type=d_type(word_dict_len))
ctx_p2 = paddle.layer.data(name='ctx_p2_data', type=d_type(word_dict_len))

# 謂詞上下區(qū)域標(biāo)志
mark = paddle.layer.data(name='mark_data', type=d_type(mark_dict_len))

# 標(biāo)注序列
target = paddle.layer.data(name='target', type=d_type(label_dict_len))

這里需要特別說明的是hidden_dim = 512指定了LSTM隱層向量的維度為128維,關(guān)于這一點(diǎn)請(qǐng)參考PaddlePaddle官方文檔中lstmemory的說明。

  將句子序列、謂詞、謂詞上下文、謂詞上下文區(qū)域標(biāo)記通過詞表,轉(zhuǎn)換為實(shí)向量表示的詞向量序列。


# 在本教程中,我們加載了預(yù)訓(xùn)練的詞向量,這里設(shè)置了:is_static=True
# is_static 為 True 時(shí)保證了在訓(xùn)練 SRL 模型過程中,詞表不再更新
emb_para = paddle.attr.Param(name='emb', initial_std=0., is_static=True)
# 設(shè)置超參數(shù)
default_std = 1 / math.sqrt(hidden_dim) / 3.0
std_default = paddle.attr.Param(initial_std=default_std)
std_0 = paddle.attr.Param(initial_std=0.)

predicate_embedding = paddle.layer.embedding(
    size=word_dim,
    input=predicate,
    param_attr=paddle.attr.Param(
        name='vemb', initial_std=default_std))
mark_embedding = paddle.layer.embedding(
    size=mark_dim, input=mark, param_attr=std_0)

word_input = [word, ctx_n2, ctx_n1, ctx_0, ctx_p1, ctx_p2]
emb_layers = [
    paddle.layer.embedding(
        size=word_dim, input=x, param_attr=emb_para) for x in word_input
]
emb_layers.append(predicate_embedding)
emb_layers.append(mark_embedding)

  8個(gè)LSTM單元以“正向/反向”的順序?qū)λ休斎胄蛄羞M(jìn)行學(xué)習(xí)。

hidden_0 = paddle.layer.mixed(
    size=hidden_dim,
    bias_attr=std_default,
    input=[
        paddle.layer.full_matrix_projection(
            input=emb, param_attr=std_default) for emb in emb_layers
    ])

mix_hidden_lr = 1e-3
lstm_para_attr = paddle.attr.Param(initial_std=0.0, learning_rate=1.0)
hidden_para_attr = paddle.attr.Param(
    initial_std=default_std, learning_rate=mix_hidden_lr)

lstm_0 = paddle.layer.lstmemory(
    input=hidden_0,
    act=paddle.activation.Relu(),
    gate_act=paddle.activation.Sigmoid(),
    state_act=paddle.activation.Sigmoid(),
    bias_attr=std_0,
    param_attr=lstm_para_attr)

#stack L-LSTM and R-LSTM with direct edges
input_tmp = [hidden_0, lstm_0]

for i in range(1, depth):
    mix_hidden = paddle.layer.mixed(
        size=hidden_dim,
        bias_attr=std_default,
        input=[
            paddle.layer.full_matrix_projection(
                input=input_tmp[0], param_attr=hidden_para_attr),
            paddle.layer.full_matrix_projection(
                input=input_tmp[1], param_attr=lstm_para_attr)
        ])

    lstm = paddle.layer.lstmemory(
        input=mix_hidden,
        act=paddle.activation.Relu(),
        gate_act=paddle.activation.Sigmoid(),
        state_act=paddle.activation.Sigmoid(),
        reverse=((i % 2) == 1),
        bias_attr=std_0,
        param_attr=lstm_para_attr)

    input_tmp = [mix_hidden, lstm]

  在PaddlePaddle中,CRF的狀態(tài)特征和轉(zhuǎn)移特征分別由一個(gè)全連接層和一個(gè)PaddlePaddle中的CRF層分別學(xué)習(xí)。在這個(gè)例子中,我們用線性激活的paddle.layer.mixed 來學(xué)習(xí)CRF的狀態(tài)特征(也可以使用paddle.layer.fc),而 paddle.layer.crf只學(xué)習(xí)轉(zhuǎn)移特征。paddle.layer.crf層是一個(gè) cost 層,處于整個(gè)網(wǎng)絡(luò)的末端,輸出給定輸入序列下,標(biāo)記序列的log probability作為代價(jià)。訓(xùn)練階段,該層需要輸入正確的標(biāo)記序列作為學(xué)習(xí)目標(biāo)。


# 取最后一個(gè)棧式LSTM的輸出和這個(gè)LSTM單元的輸入到隱層映射,
# 經(jīng)過一個(gè)全連接層映射到標(biāo)記字典的維度,來學(xué)習(xí) CRF 的狀態(tài)特征

feature_out = paddle.layer.mixed(
    size=label_dict_len,
    bias_attr=std_default,
    input=[
        paddle.layer.full_matrix_projection(
            input=input_tmp[0], param_attr=hidden_para_attr),
        paddle.layer.full_matrix_projection(
            input=input_tmp[1], param_attr=lstm_para_attr)
    ], )

# 學(xué)習(xí) CRF 的轉(zhuǎn)移特征
crf_cost = paddle.layer.crf(
    size=label_dict_len,
    input=feature_out,
    label=target,
    param_attr=paddle.attr.Param(
        name='crfw',
        initial_std=default_std,
        learning_rate=mix_hidden_lr))

  CRF解碼和CRF層參數(shù)名字相同,即:加載了paddle.layer.crf層學(xué)習(xí)到的參數(shù)。在訓(xùn)練階段,為paddle.layer.crf_decoding 輸入了正確的標(biāo)記序列(target),這一層會(huì)輸出是否正確標(biāo)記,evaluator.sum 用來計(jì)算序列上的標(biāo)記錯(cuò)誤率,可以用來評(píng)估模型。解碼階段,沒有輸入正確的數(shù)據(jù)標(biāo)簽,該層通過尋找概率最高的標(biāo)記序列,解碼出標(biāo)記結(jié)果。

crf_dec = paddle.layer.crf_decoding(
   size=label_dict_len,
   input=feature_out,
   label=target,
   param_attr=paddle.attr.Param(name='crfw'))
evaluator.sum(input=crf_dec)

訓(xùn)練模型

定義參數(shù)

首先依據(jù)模型配置的crf_cost定義模型參數(shù)。

# create parameters
parameters = paddle.parameters.create(crf_cost)

可以打印參數(shù)名字,如果在網(wǎng)絡(luò)配置中沒有指定名字,則默認(rèn)生成。

print parameters.keys()

如上文提到,我們用基于英文維基百科訓(xùn)練好的詞向量來初始化序列輸入、謂詞上下文總共6個(gè)特征的embedding層參數(shù),在訓(xùn)練中不更新。

# 這里加載PaddlePaddle上版保存的二進(jìn)制模型
def load_parameter(file_name, h, w):
    with open(file_name, 'rb') as f:
        f.read(16)
        return np.fromfile(f, dtype=np.float32).reshape(h, w)
parameters.set('emb', load_parameter(conll05.get_embedding(), 44068, 32))

構(gòu)造訓(xùn)練(Trainer)

然后根據(jù)網(wǎng)絡(luò)拓?fù)浣Y(jié)構(gòu)和模型參數(shù)來構(gòu)造出trainer用來訓(xùn)練,在構(gòu)造時(shí)還需指定優(yōu)化方法,這里使用最基本的SGD方法(momentum設(shè)置為0),同時(shí)設(shè)定了學(xué)習(xí)率、正則等。

# create optimizer
optimizer = paddle.optimizer.Momentum(
    momentum=0,
    learning_rate=1e-3,
    regularization=paddle.optimizer.L2Regularization(rate=8e-4),
    model_average=paddle.optimizer.ModelAverage(
        average_window=0.5, max_average_window=10000), )

trainer = paddle.trainer.SGD(cost=crf_cost,
                             parameters=parameters,
                             update_equation=optimizer,
                             extra_layers=crf_dec)

訓(xùn)練

數(shù)據(jù)介紹部分提到CoNLL 2005訓(xùn)練集付費(fèi),這里我們使用測(cè)試集訓(xùn)練供大家學(xué)習(xí)。conll05.test()每次產(chǎn)生一條樣本,包含9個(gè)特征,shuffle和組完batch后作為訓(xùn)練的輸入。

reader = paddle.batch(
    paddle.reader.shuffle(
        conll05.test(), buf_size=8192), batch_size=2)

通過feeding來指定每一個(gè)數(shù)據(jù)和data_layer的對(duì)應(yīng)關(guān)系。 例如 下面feeding表示: conll05.test()產(chǎn)生數(shù)據(jù)的第0列對(duì)應(yīng)word_data層的特征。

feeding = {
    'word_data': 0,
    'ctx_n2_data': 1,
    'ctx_n1_data': 2,
    'ctx_0_data': 3,
    'ctx_p1_data': 4,
    'ctx_p2_data': 5,
    'verb_data': 6,
    'mark_data': 7,
    'target': 8
}

可以使用event_handler回調(diào)函數(shù)來觀察訓(xùn)練過程,或進(jìn)行測(cè)試等。這里我們打印了訓(xùn)練過程的cost,該回調(diào)函數(shù)是trainer.train函數(shù)里設(shè)定。

def event_handler(event):
    if isinstance(event, paddle.event.EndIteration):
        if event.batch_id and event.batch_id % 10 == 0:
            print "Pass %d, Batch %d, Cost %f, %s" % (
                event.pass_id, event.batch_id, event.cost, event.metrics)
        if event.batch_id % 400 == 0:
            result = trainer.test(reader=reader, feeding=feeding)
            print "\nTest with Pass %d, Batch %d, %s" % (event.pass_id, event.batch_id, result.metrics)

    if isinstance(event, paddle.event.EndPass):
        # save parameters
        with gzip.open('params_pass_%d.tar.gz' % event.pass_id, 'w') as f:
            parameters.to_tar(f)

        result = trainer.test(reader=reader, feeding=feeding)
        print "\nTest with Pass %d, %s" % (event.pass_id, result.metrics)

通過trainer.train函數(shù)訓(xùn)練:

trainer.train(
    reader=reader,
    event_handler=event_handler,
    num_passes=1,
    feeding=feeding)

應(yīng)用模型

訓(xùn)練完成之后,需要依據(jù)某個(gè)我們關(guān)心的性能指標(biāo)選擇最優(yōu)的模型進(jìn)行預(yù)測(cè),可以簡(jiǎn)單的選擇測(cè)試集上標(biāo)記錯(cuò)誤最少的那個(gè)模型。預(yù)測(cè)時(shí)使用 paddle.layer.crf_decoding,和訓(xùn)練不同的是,該層沒有正確的標(biāo)簽層作為輸入。如下所示:

predict = paddle.layer.crf_decoding(
    size=label_dict_len,
    input=feature_out,
    param_attr=paddle.attr.Param(name='crfw'))

這里選用測(cè)試集的一條數(shù)據(jù)作為示例。

test_creator = paddle.dataset.conll05.test()
test_data = []
for item in test_creator():
    test_data.append(item[0:8])
    if len(test_data) == 1:
        break

推斷接口paddle.infer返回標(biāo)簽的索引,并查詢?cè)~典labels_reverse,打印出標(biāo)記的結(jié)果。

labs = paddle.infer(
    output_layer=predict, parameters=parameters, input=test_data, field='id')
assert len(labs) == len(test_data[0][0])
labels_reverse={}
for (k,v) in label_dict.items():
    labels_reverse[v]=k
pre_lab = [labels_reverse[i] for i in labs]
print pre_lab

總結(jié)

語義角色標(biāo)注是許多自然語言理解任務(wù)的重要中間步驟。這篇教程中我們以語義角色標(biāo)注任務(wù)為例,介紹如何利用PaddlePaddle進(jìn)行序列標(biāo)注任務(wù)。教程中所介紹的模型來自我們發(fā)表的論文[10]。由于 CoNLL 2005 SRL任務(wù)的訓(xùn)練數(shù)據(jù)目前并非完全開放,教程中只使用測(cè)試數(shù)據(jù)作為示例。在這個(gè)過程中,我們希望減少對(duì)其它自然語言處理工具的依賴,利用神經(jīng)網(wǎng)絡(luò)數(shù)據(jù)驅(qū)動(dòng)、端到端學(xué)習(xí)的能力,得到一個(gè)和傳統(tǒng)方法可比、甚至更好的模型。在論文中我們證實(shí)了這種可能性。關(guān)于模型更多的信息和討論可以在論文中找到。

參考文獻(xiàn)

  1. Sun W, Sui Z, Wang M, et al. Chinese semantic role labeling with shallow parsing[C]//Proceedings of the 2009 Conference on Empirical Methods in Natural Language Processing: Volume 3-Volume 3. Association for Computational Linguistics, 2009: 1475-1483.
  2. Pascanu R, Gulcehre C, Cho K, et al. How to construct deep recurrent neural networks[J]. arXiv preprint arXiv:1312.6026, 2013.
  3. Cho K, Van Merri?nboer B, Gulcehre C, et al. Learning phrase representations using RNN encoder-decoder for statistical machine translation[J]. arXiv preprint arXiv:1406.1078, 2014.
  4. Bahdanau D, Cho K, Bengio Y. Neural machine translation by jointly learning to align and translate[J]. arXiv preprint arXiv:1409.0473, 2014.
  5. Lafferty J, McCallum A, Pereira F. Conditional random fields: Probabilistic models for segmenting and labeling sequence data[C]//Proceedings of the eighteenth international conference on machine learning, ICML. 2001, 1: 282-289.
  6. 李航. 統(tǒng)計(jì)學(xué)習(xí)方法[J]. 清華大學(xué)出版社, 北京, 2012.
  7. Marcus M P, Marcinkiewicz M A, Santorini B. Building a large annotated corpus of English: The Penn Treebank[J]. Computational linguistics, 1993, 19(2): 313-330.
  8. Palmer M, Gildea D, Kingsbury P. The proposition bank: An annotated corpus of semantic roles[J]. Computational linguistics, 2005, 31(1): 71-106.
  9. Carreras X, Màrquez L. Introduction to the CoNLL-2005 shared task: Semantic role labeling[C]//Proceedings of the Ninth Conference on Computational Natural Language Learning. Association for Computational Linguistics, 2005: 152-164.
  10. Zhou J, Xu W. End-to-end learning of semantic role labeling using recurrent neural networks[C]//Proceedings of the Annual Meeting of the Association for Computational Linguistics. 2015.


知識(shí)共享許可協(xié)議
本教程PaddlePaddle 創(chuàng)作,采用 知識(shí)共享 署名-相同方式共享 4.0 國(guó)際 許可協(xié)議進(jìn)行許可。

上一篇:情感分析下一篇:圖像分類