Efficient GlobalPointer:少點引數,多點效果

語言: CN / TW / HK

《GlobalPointer:用統一的方式處理巢狀和非巢狀NER》 中,我們提出了名為“GlobalPointer”的token-pair識別模組,當它用於NER時,能統一處理巢狀和非巢狀任務,並在非巢狀場景有著比CRF更快的速度和不遜色於CRF的效果。換言之,就目前的實驗結果來看,至少在NER場景,我們可以放心地將CRF替換為GlobalPointer,而不用擔心效果和速度上的損失。

在這篇文章中,我們提出GlobalPointer的一個改進版——Efficient GlobalPointer,它主要針對原GlobalPointer引數利用率不高的問題進行改進,明顯降低了GlobalPointer的引數量。更有趣的是,多個任務的實驗結果顯示,引數量更少的Efficient GlobalPointer反而還取得更好的效果。

這裡簡單回顧一下GlobalPointer,詳細介紹則請讀者閱讀 《GlobalPointer:用統一的方式處理巢狀和非巢狀NER》 。簡單來說,GlobalPointer是基於內積的token-pair識別模組,它可以用於NER場景,因為對於NER來說我們只需要把每一類實體的“(首, 尾)”這樣的token-pair識別出來就行了。

設長度為$n$的輸入$t$經過編碼後得到向量序列$[\boldsymbol{h}_1,\boldsymbol{h}_2,\cdots,\boldsymbol{h}_n]$,原始GlobalPointer通過變換$\boldsymbol{q}_{i,\alpha}=\boldsymbol{W}_{q,\alpha}\boldsymbol{h}_i$和$\boldsymbol{k}_{i,\alpha}=\boldsymbol{W}_{k,\alpha}\boldsymbol{h}_i$我們得到序列向量序列$[\boldsymbol{q}_{1,\alpha},\boldsymbol{q}_{2,\alpha},\cdots,\boldsymbol{q}_{n,\alpha}]$和$[\boldsymbol{k}_{1,\alpha},\boldsymbol{k}_{2,\alpha},\cdots,\boldsymbol{k}_{n,\alpha}]$,然後定義

\begin{equation}s_{\alpha}(i,j) = \boldsymbol{q}_{i,\alpha}^{\top}\boldsymbol{k}_{j,\alpha}\end{equation}

作為從$i$到$j$的連續片段是一個型別為$\alpha$的實體的打分。這裡我們暫時省略了偏置項,如果覺得有必要,自行加上就好。

這樣一來,有多少種類型的實體,就有多少個$\boldsymbol{W}_{q,\alpha}$和$\boldsymbol{W}_{k,\alpha}$。不妨設$\boldsymbol{W}_{q,\alpha},\boldsymbol{W}_{k,\alpha}\in\mathbb{R}^{D\times d}$,那麼每新增一種實體型別,我們就要新增$2Dd$個引數;而如果用CRF+BIO標註的話,每新增一種實體型別,我們只需要增加$2D$的引數(轉移矩陣引數較少,忽略不計)。對於BERT base來說,常見的選擇是$D=768,d=64$,可見GlobalPointer的引數量遠遠大於CRF。

事實上,不難想象對於任意型別$\alpha$,其打分矩陣$s_{\alpha}(i,j)$必然有很多相似之處,因為對於大多數token-pair而言,它們代表的都是“非實體”,這些非實體的正確打分都是負的。這也就意味著,我們沒必要為每種實體型別都設計獨立的$s_{\alpha}(i,j)$,它們應當包含更多的共性。

怎麼突出$s_{\alpha}(i,j)$的共性呢?以NER為例,我們知道NER實際上可以分解為“識別”和“分類”兩個步驟,“識別”就是識別出哪些片段是實體,“分類”就是確定每個實體的型別。這樣一來,“識別”這一步相當於只有一種實體型別的NER,我們可以用一個打分矩陣就可以完成,即$(\boldsymbol{W}_q\boldsymbol{h}_i)^{\top}(\boldsymbol{W}_k\boldsymbol{h}_j)$,而“分類”這一步,我們則可以用“特徵拼接+Dense層”來完成,即$\boldsymbol{w}_{q,\alpha}^{\top}\boldsymbol{h}_i + \boldsymbol{w}_{k,\alpha}^{\top}\boldsymbol{h}_j$。於是我們可以將兩項組合起來,作為新的打分函式:

\begin{equation}s_{\alpha}(i,j) = (\boldsymbol{W}_q\boldsymbol{h}_i)^{\top}(\boldsymbol{W}_k\boldsymbol{h}_j) + \boldsymbol{w}_{q,\alpha}^{\top}\boldsymbol{h}_i + \boldsymbol{w}_{k,\alpha}^{\top}\boldsymbol{h}_j\label{eq:EGP}\end{equation}

這就是Efficient GlobalPointer的打分函式。這樣一來,“識別”這部分的引數對所有實體型別都是共享的,因此每新增一種實體型別,我們只需要新增對應的$\boldsymbol{w}_{q,\alpha},\boldsymbol{w}_{k,\alpha}$就行了,即新增一種實體型別增加的引數量也只是$2D$。

Efficient GlobalPointer已經內建在 bert4keras>=0.10.9 中,讀者只需要更改一行程式碼,就可以切換Efficient GlobalPointer了。

# from bert4keras.layers import GlobalPointer
from bert4keras.layers import EfficientGlobalPointer as GlobalPointer

下面我們來對比一下GlobalPointer和Efficient GlobalPointer的結果:

\begin{array}{c}

\text{人民日報NER實驗結果} \\

{\begin{array}{c|cc}

\hline

& \text{驗證集F1} & \text{測試集F1}\\

\hline

\text{CRF} & 96.39\% & 95.46\% \\

\text{GlobalPointer} & \textbf{96.25%} & \textbf{95.51%} \\

\text{Efficient GlobalPointer} & 96.10\% & 95.36\%\\

\hline

\end{array}} \\ \\

\text{CLUENER實驗結果} \\

{\begin{array}{c|cc}

\hline

& \text{驗證集F1} & \text{測試集F1} \\

\hline

\text{CRF} & 79.51\% & 78.70\% \\

\text{GlobalPointer} & 80.03\% & 79.44\%\\

\text{Efficient GlobalPointer} & \textbf{80.66%} & \textbf{80.04%} \\

\hline

\end{array}} \\ \\

\text{CMeEE實驗結果} \\

{\begin{array}{c|cc}

\hline

& \text{驗證集F1} & \text{測試集F1} \\

\hline

\text{CRF} & 63.81\% & 64.39\% \\

\text{GlobalPointer} & 64.84\% & 65.98\%\\

\text{Efficient GlobalPointer} & \textbf{65.16%} & \textbf{66.54%} \\

\hline

\end{array}}

\end{array}

可以看到,Efficient GlobalPointer的實驗結果還是很不錯的,除了在人民日報任務上有輕微下降外,其他兩個任務都獲得了一定提升,並且整體而言提升的幅度大於下降的幅度,所以Efficient GlobalPointer不單單是節省了引數量,還提升了效果。而在速度上,Efficient GlobalPointer與原始的GlobalPointer幾乎沒有差別。

考慮到人民日報NER只有3種實體型別,CLUENER和CMeEE分別有10種和9種實體型別,從分數來看也是人民日報比其他兩種要高,這說明CLUENER和CMeEE的難度更大。另一方面,在CLUENER和CMeEE上Efficient GlobalPointer都取得了提升,所以我們可以初步推斷:實體類別越多、任務越難時,Efficient GlobalPointer越有效。

這也不難理解,原版GlobalPointer引數過大,那麼平均起來每個引數更新越稀疏,相對來說也越容易過擬合;而Efficient GlobalPointer共享了“識別”這一部分引數,僅通過“分類”引數區分不同的實體型別,那麼實體識別這一步的學習就會比較充分,而實體分類這一步由於引數比較少,學起來也比較容易。反過來,Efficient GlobalPointer的實驗效果好也間接證明了式$\eqref{eq:EGP}$的分解是合理的。

當然,不排除在訓練資料足夠多的時候,原版GlobalPointer會取得更好的效果。但即便如此,在類別數目較多時,原版GlobalPointer可能會佔用較多視訊記憶體以至於難以使用,還是以base版$D=768,d=64$為例,如果類別數有100個,那麼原版GlobalPointer的引數量為$2\times 768\times 64\times 100$,接近千萬,不得不說確實是不夠友好了。

本文指出了原版GlobalPointer的引數利用率不高問題,並提出了相應的改進版Efficient GlobalPointer。實驗結果顯示,Efficient GlobalPointer在降低引數量的同時,基本不會損失效能,甚至還可能獲得提升。

轉載到請包括本文地址: https://kexue.fm/archives/8877

更詳細的轉載事宜請參考: 《科學空間FAQ》

如果您還有什麼疑惑或建議,歡迎在下方評論區繼續討論。

如果您覺得本文還不錯,歡迎/本文。打賞並非要從中獲得收益,而是希望知道科學空間獲得了多少讀者的真心關注。當然,如果你無視它,也不會影響你的閱讀。再次表示歡迎和感謝!

如果您需要引用本文,請參考:

蘇劍林. (Jan. 25, 2022). 《Efficient GlobalPointer:少點引數,多點效果 》[Blog post]. Retrieved from https://kexue.fm/archives/8877

「其他文章」