選單

基於BERT預訓練模型的中文文字分類任務

由於公眾號改版不再按照作者的釋出時間進行推送,為防止各位朋友錯過月來客棧推送的最新文章,大家可以手動將公眾號設定為“星標 ”以第一時間獲得推送內容,感謝各位~

1 引言

各位朋友大家好,歡迎來到

月來客棧

,我是掌櫃空字元。

點選進入討論區「NO. 0003」

總的來說,

基於BERT的文字分類模型就是在原始的BERT模型後再加上一個分類層即可

,這種類似的結構掌櫃在文章

基於Transformer的分類模型[7]

中也介紹過,大家可以去看一下。同時,對於分類層的輸入(也就是原始BERT的輸出),預設情況下取BERT輸出結果中位置對於的向量即可,當然也可以修改為其它方式,例如所有位置向量的均值等(有實驗表面取均值的效果往往更好)。因此,對於基於BERT的文字分類模型來說其輸入就是BERT的輸入,輸出則是每個類別對應的logits值。接下來,掌櫃首先就來介紹如何構造文字分類的資料集。

以下所有完整示例程式碼均可從倉庫https://github。com/moon-hotel/BertWithPretrained中獲取!

2 資料預處理

2。1 輸入介紹

在構建資料集之前,我們首先需要知道的是模型到底應該接收什麼樣的輸入,然後才能構建出正確的資料形式。在上面我們說到,基於BERT的文字分類模型的輸入就等價於BERT模型的輸入,同時根據文章

[2]

的介紹可以知道BERT模型的輸入如圖1所示:

圖 1。 BERT模型輸入圖

因此,對於文字分類這個場景來說,只需要構造原始文字對應的Token序列,並在首位分別再加上一個符和符作為輸入即可。

2。2 語料介紹

在這裡,我們使用到的資料集是今日頭條開放的一個新聞分類資料集[3],一共包含有382688條資料,15個類別。同時掌櫃已近將其進行了格式化處理,以7:2:1的比例劃分成了訓練集、驗證集和測試集三個部分。如下所示便是部分示例資料:

其中左邊為新聞標題,也就是後面需要用到的分類文字,右邊為類別標籤。

2。3 資料集預覽

同樣,在正式介紹如何構建資料集之前我們先透過一張圖來了解一下整個構建的流程,以便做到心中有數,不會迷路。假如我們現在有兩個樣本構成了一個batch,那麼其整個資料的處理過程則如圖2所示。

基於BERT預訓練模型的中文文字分類任務

圖 2。 文字分類資料集處理流程圖

如圖2所示,第1步需要將原始的資料樣本進行分字(tokenize)處理;第2步再根據tokenize後的結果構造一個字典,不過在使用BERT預訓練時並不需要我們自己來構造這個字典,直接載入谷歌開源的檔案構造字典即可,因為只有中每個字的索引順序才與開源模型中每個字的Embedding向量一一對應的。第3步則是根據字典將tokenize後的文字序列轉換為Token序列,同時在Token序列的首尾分別加上和符號,並進行Padding。第4步則是根據第3步處理後的結果生成對應的Padding Mask向量。

最後,在模型訓練時只需要將第3步和第4步處理後的結果一起餵給模型即可。

2。4 資料集構建

第1步:定義tokenize

第1步需要完成的就是將輸入進來的文字序列tokenize到字元級別。對於中文語料來說就是將每個字和標點符號都給切分開。在這裡,我們可以借用包中的方法來完成,如下所示:

在上述程式碼中,第2-3行就是根據指定的路徑(BERT預訓練模型的路徑)來載入一個分字模型;第7-8行便是tokenize後的結果。

第2步:建立詞表

由於BERT預訓練模型中已經有了一個給定的詞表(),因此我們並不需要根據自己的語料來建立一個詞表。當然,也不能夠根據自己的語料來建立詞表,因為相同的字在我們自己構建的詞表中和中的索引順序肯定會不一樣,而這就會導致後面根據token id 取出來的向量是錯誤的。

進一步,我們只需要將中的內容讀取進來形成一個詞表即可,程式碼如下:

接著便可以定義一個方法來例項化一個詞表:

在經過上述程式碼處理後,我們便能夠透過得到一個列表,返回詞表中的每一個詞;透過返回得到詞表中對應索引位置上的詞;透過得到一個字典,返回詞表中每個詞的索引;透過返回得到詞表中對應詞的索引;透過來返回詞表的長度。如下便是建立後的詞表:

此時,我們就需要定義一個類,並在類的初始化過程中根據訓練語料完成字典的構建等工作,程式碼如下:

在上述程式碼中,表示本地詞表的路徑。表示最大樣本長度,當時,即以每個batch中最長樣本長度為標準,對其它進行padding;當時,以整個資料集中最長樣本為標準,對其它進行padding;當, 表示以某個固定長度符樣本進行padding,多餘的截掉。表示樣本與標籤之間的分隔符。表示是否打亂資料集。第14-15行為建立詞表並取對應特殊字元的索引;第18行中為最大樣本長度,最大為512。第19-21行則是用來判斷傳入的最大樣本長度。

第3步:轉換為Token序列

在得到構建的字典後,便可以透過如下函式來將訓練集、驗證集和測試集轉換成Token序列:

在上述程式碼中,第6-7行便是用來取得文字和標籤;第8行則是首先對序列進行tokenize,然後轉換成Token序列並在最前面加上分類標誌位。第9-11行則是用來對Token序列進行擷取,最長為個字元即512,並同時在末尾加上符號。不過掌櫃認為其實不加應該也不會有影響,因為這本來是單個序列的分類。第14行則是用來儲存最長序列的長度。在處理完成後,2。2節中的4個樣本將會被轉換成如下形式:

從上面的輸出結果可以看出,101就是在詞表中的索引位置,102則是在詞表中的索引;其它非0值就是tokenize後的文字序列轉換成的Token序列。同時可以看出,這裡的結果是以第3個樣本的長度39對其它樣本進行padding的,並且padding的Token ID為0。因此,下面我們就來介紹樣本的padding處理。

第4步:padding處理與mask

從第3步的輸出結果看出,在對原始文字序列tokenize轉換為Token ID後還需要對其進行padding處理。對於這一處理過程可以透過如下程式碼來完成:

在上述程式碼中,為待padding的序列所構成的列表,其中的每一個元素為一個樣本的Token序列;表示是否將這個維度放在第1個;表示指定最大序列長度,當時,表示以某個固定長度對樣本進行padding多餘的截掉,當時表示以當前batch中最長樣本的長度對其它進行padding。第2-3行用來獲取padding的長度;第5-11行則是遍歷每一個Token序列,根據來進行padding。

進一步,我們需要定義一個函式來對每個batch的Token序列進行padding處理:

上述程式碼的作用就是對每個batch的Token序列進行padding處理。

最後,對於每一序列的向量,我們只需要判斷其是否等於便可以得到這一結果,可見第5步中的使用示例。

第5步:構造與使用示例

經過前面4步的操作,整個資料集的構建就算是已經基本完成了,只需要再構造一個迭代器即可,程式碼如下:

在上述程式碼中,第5-7行用來得到預處理後的資料並構造對應的DataLoader,其中將作為一個引數傳入來對每個batch的樣本進行處理;第8-9行則判斷是否只返回測試集;同理,第10-18行則是用來構造相應的訓練集和驗證集。在完成類所有的編碼過程後,便可以透過如下形式進行使用:

執行完上述程式碼後便可以得到如下所示的結果:

到此,對於整個資料集構建部分的內容就算是介紹完了,接下來我們再來看如何載入預訓練模型進行微調。

3 載入預訓練模型

在介紹模型微調之前,我們先來看看當我們拿到一個開源的模型引數後怎麼讀取以及分析。下面掌櫃就以huggingface開源的PyTorch訓練的bert-base-chinese模型引數

[4]

為例進行介紹。

3。1 檢視模型引數

前一篇文章[5]

中,儘管掌櫃已經大致介紹瞭如何透過PyTorch來讀取和載入模型引數,但是這裡仍舊有必要以引數為例再進行一次詳細的介紹。通常,對於一個透過PyTorch框架儲存的模型引數,我們可以透過如下方式來進行載入:

執行完上述程式碼後,便可以得到如下輸出結果:

從上面的輸出結果可以看到,引數被載入後變成了一個有序的字典,並且其中一共有207個引數,其名字分別就是列表中的各個元素。進一步,我們還可以將各個引數的形狀打印出來看一看:

同樣,我們還可以直接打印出某個引數具體的值。

到此,對於本地的模型引數我們就算是看明白了。不過想要將它遷移到自己所搭建的模型上還要進一步的來分析自己所搭建的模型。

3。2 載入模型配置

在繼續往下介紹之前,我們先來看看如何載入本地的BERT配置引數這個檔案。通常情況下我們都會定義一個配置類,然將這些引數從載入後來例項化這個配置類,程式碼如下所示:

在上述程式碼中,第5-29行是透過指定引數值來例項化這個類;第40行的則是透過指定的路徑來載入本地的配置檔案(如下所示),其中的表示未經例項化的類,是Python語法中的用法。

定義完成後便可以透過如下的方式來載入配置檔案。

接下里便可以以訪問類成員的方式來使用這些引數。

3。3 載入並初始化

上一篇文章[2]

中,掌櫃已經詳細地介紹瞭如何實現整個BERT模型,但是對於如何載入已有引數來初始化網路中的引數還並未介紹。在將本地引數遷移到一個新的模型之前,除了像2。1節那樣分析本地引數之外,我們還需要將網路的引數資訊也打印出來看一下,以便將兩者一一對應上。

在執行完上述程式碼後,便可以得到如下輸出結果:

從上面的輸出結果可以發現,一共有200個引數,而一共有207個引數。這裡需要注意的是模型中的這個引數並不是模型中需要訓練的引數,只是一個預設的初始值。最後,經分析(兩者一一進行對比)後發現中除了最後的8個引數以外,其餘的199個引數和模型中的199個引數一樣且順序也一樣。

因此,最後我們可以透過在類(檔案中)中再加入一個如下所示的函式來用中的引數初始化中的引數:

在上述程式碼中,第4-5行用來載入本地的引數;第6行用來複製一份中的網路引數,這是因為我們無法直接修改裡面的值;第7-10行則是根據我們上面的分析,將中的引數賦值到中;第12行是用中的引數來初始化中的引數。

最後,我們只需要透過如下方式便可以返回一個透過初始化的BERT模型:

當然,如果你需要凍結其中某些層的引數不參與模型訓練,那麼可以透過類似如下所示的程式碼來進行設定:

到此,對於整個預訓練模型的載入過程就介紹完了,接下來讓我們正式進入到基於BERT預訓練模型的文字分類場景中。

4 文字分類

4。1 工程結構

為了使得大家對於整個工程有著清晰的認識,掌櫃這裡先來介紹一下整個專案的目錄結構。

基於BERT預訓練模型的中文文字分類任務

圖 3。 工程目錄結構圖

如圖3所示,就是我們這個實戰專案的工程結構,其中:

目錄裡面是原始中文BERT的配置檔案和模型引數;

目錄用來儲存微調後儲存的模型;

裡面存放了各類下游任務需要用到的資料集;

目錄用來儲存日誌;

目錄裡面是實現的模型程式碼:

目錄裡面是實現整個BERT模型,包括載入預訓練引數所用到的程式碼;

目錄裡面實現的是各類基於BERT結構所實現的模型程式碼,例如中實現的便是基於BERT的文字分類模型,後續我們還會在同級目錄中來新建其它場景下基於BERT的模型程式碼;

目錄裡面實現的則是目錄中對應下游場景任務的訓練程式碼,例如實現的則是基於單文字進行分類的訓練程式碼,後續也會新增其它場景下的訓練程式碼;

目錄中是各個類的測試檔案;

目錄中是一些工具類檔案,例如資料集載入和日誌列印工具等。

4。2 文字分類前向傳播

在介紹完如何將中的引數賦值到BERT模型中以及整個工程的目錄結構後,接下來我們首先要做的就是實現文字分類的前向傳播過程。在圖3中的檔案中,我們透過定義如下一個類來完成整個前向傳播的過程:

在上述程式碼中,第4行程式碼分別就是用來指定模型配置、分類的標籤數量以及預訓練模型的路徑;第7-10行程式碼則是用來定義一個BERT模型,可以看到如果預訓練模型的路徑存在則會返回一個由引數初始化後的BERT模型,否則則會返回一個隨機初始化引數的BERT模型;第12行則是定義最後的分類層。

最後,整個前向傳播的實現程式碼如下所示:

在上述程式碼中,第6-9行返回的就是原始BERT網路的輸出,其中為BERT第一個位置的向量經過一個全連線層後的結果,第二個引數是BERT中所有位置的向量(具體可以參見文章

[2]

的第2。6節內容);第10-11行便是用來進行文字分類的分類層;第12-17行則是用來判斷返回損失值還是返回值。

4。3 模型訓練

如圖3所示,我們將在目錄下新建一個名為的模組來完成分類模型的微調訓練任務。

首先,我們需要定義一個類來對分類模型中的超引數進行管理,程式碼如下所示:

在上述程式碼中,第2-23行則是分別用來定義模型中的一些資料集目錄、超引數和初始化日誌列印類等;第25-29行則是將原始配置檔案,即中的引數也匯入到類中;第31-33行則是將所有的超引數配置情況一同列印到日誌檔案中方便後續分析,更多關於日誌的內容可以參加文章

訓練模型時如何便捷儲存訓練日誌[8]

最後,我們只需要再定義一個函式來完成模型的訓練即可,程式碼如下:

在上述程式碼中,第2-3行用來初始化一個基於BERT的文字分類模型;第9-19行則是載入相應的資料集;第20-39行則是整個模型的訓練過程,完整示例程式碼可參見

[6]

,掌櫃也在程式碼中進行了詳細的註釋。

如下便是網路的訓練結果:

5 總結

在這篇文章中,掌櫃首先從總體上介紹了建立基於BERT模型的文字分類模型的大致思路;然後介紹了模型的輸入部分,以及如何從零開始來構建資料集;接著詳細介紹瞭如何分析模型的引數,並將其載入到相應的模型中;最後介紹瞭如何在BERT網路模型的基礎之上來新增一個分類層,來完成最後的文字分類任務。在下一篇文章中,掌櫃將會介紹如何在

文字蘊含任務

,即輸入兩個句子來進行分類的場景下進行BERT預訓練模型的微調。

引用

[3]https://github。com/aceimnorstuvwxz/toutiao-text-classfication-dataset

[4]https://huggingface。co/bert-base-chinese/tree/main

[6] 示例程式碼 https://github。com/moon-hotel/BertWithPretrained

都滑到這裡了,給掌櫃點個贊再走吧!