返回文章列表

自定義NER模型微調與Prodigy實戰

本文探討如何利用 spaCy 和 Prodigy 建立和微調命名實體辨識 (NER) 模型。文章涵蓋了使用 Prodigy 進行資料標註、選擇合適的 spaCy 預訓練模型、以及使用標註資料微調模型的完整流程。同時,文章比較了根據 Transformer 的 GPU 加速模型和根據 CPU

自然語言處理 機器學習

自然語言處理中的命名實體辨識 (NER) 技術,能有效識別文字中的關鍵實體。雖然預訓練模型如 spaCy 已具備一定的 NER 能力,但在特定領域的應用常需微調才能達到最佳效能。本文將逐步講解如何運用 Prodigy 進行資料標註,並使用 spaCy 訓練客製化 NER 模型,同時比較 Transformer GPU 加速模型與 CPU 模型的效能差異。透過實際案例與程式碼,示範如何提升 NER 模型在特定實體型別上的辨識能力,並探討自定義模型與原始模型的差異。

命名實體辨識(Named Entity Recognition, NER)的進階應用與自定義模型微調

前言

命名實體辨識(NER)是自然語言處理(NLP)中的一項基礎任務,主要用於識別文字中具有特定意義的實體,如人名、地名、組織機構名等。預訓練的模型如spaCy在許多NLP任務中表現出色,但在處理特定領域或任務時,可能需要對模型進行微調以提升效能。

利用預訓練模型進行NER

預訓練的spaCy模型已經在多個NLP任務上展現了其強大的能力。例如,在新聞文章資料集上,spaCy能夠有效地識別出日期、人名、組織機構名等實體。然而,當面對與訓練資料分佈不同的資料時,如技術檔案或金融報告,預訓練模型的表現可能會下降。

自定義NER模型的微調

為了提升模型在特定任務或資料集上的表現,可以透過以下步驟進行自定義微調:

  1. 資料標註:使用工具如Prodigy對資料進行標註。Prodigy是一款由Explosion開發的註解工具,能夠與spaCy模型無縫整合。透過Prodigy,可以方便地對文字資料進行實體標註。

  2. 模型選擇:選擇適合的預訓練模型作為基礎。目前,spaCy的transformer-based模型在多個NLP任務上表現出色,但需要注意版本相容性問題。

  3. 微調:使用標註好的資料對預訓練模型進行微調。這一步驟能夠使模型更好地適應特定的資料分佈或任務需求。

使用Prodigy進行資料標註

Prodigy是一款強大的資料標註工具,支援多種NLP任務的標註,包括NER。以下是使用Prodigy進行NER標註的基本步驟:

安裝Prodigy

  1. 購買Prodigy授權並下載對應的Python wheel檔案。
  2. 建立並啟用一個新的虛擬環境,以避免與其他專案的依賴衝突。
    $ conda create -n prodigy anaconda python=3.8
    $ conda activate prodigy
    
  3. 在虛擬環境中安裝Prodigy及所需的spaCy模型。
    $ pip install prodigy*.whl
    $ pip install -U spacy[cuda110]==2.3.5
    $ pip install -U spacy-lookups-data==1.0.0
    $ pip install cupy-cuda110==8.5.0
    $ python -m spacy download en_core_web_lg
    

準備資料並進行標註

  1. 將待標註的文字資料準備成CSV格式,並確保文字列的名稱為"text"。
    train_prodigy_ner = data.copy()
    train_prodigy_ner = train_prodigy_ner.description
    train_prodigy_ner.rename("text", inplace=True)
    train_prodigy_ner.to_csv(cwd + "data/ag_dataset/ner/raw/train_prodigy_ner.csv", index=False)
    
  2. 使用Prodigy的ner.manual配方進行NER標註。

微調spaCy模型

透過Prodigy標註的資料,可以用於微調spaCy模型,以提升其在特定任務或資料集上的表現。

內容解密:

  1. 準備標註資料:首先,需要將原始資料轉換為適合Prodigy處理的格式,即CSV檔案,並且確保文字資料儲存在名為"text"的列中。

  2. 使用Prodigy進行標註:啟動Prodigy後,透過其提供的UI介面對文字進行實體標註。對於NER任務,可以使用ner.manual配方來手動標註實體。

  3. 微調模型:將標註好的資料用於微調預訓練的spaCy模型。這一步驟能夠使模型更好地理解特定領域的文字,從而提高NER任務的準確率。

  4. 版本相容性:需要注意的是,Prodigy與spaCy的版本相容性問題。目前,Prodigy尚未完全支援spaCy 3.x版本,因此在安裝時需要指定相應的版本。

使用Prodigy進行命名實體識別(NER)註解

在命令列中,我們需要指定配方名稱(ner.manual)、要儲存註解的資料集名稱(例如,ag_data_ner_ticker)、一個spaCy模型(例如,en_core_web_lg或空白:en,如果我們想從空白模型開始)、文字來源(在我們的例子中,是train_prodigy_ner.csv的路徑),以及我們希望在Prodigy UI中可用的實體標籤來註解文字:

$ python -m prodigy ner.manual <dataset> <spacy_model> <source> \
--label ORG,PERSON,GPE,TICKER

內容解密:

  • python -m prodigy ner.manual:使用Prodigy的ner.manual配方進行命名實體識別註解。
  • <dataset>:指定儲存註解的資料集名稱。
  • <spacy_model>:指定使用的spaCy模型,例如en_core_web_lg或blank:en。
  • <source>:文字來源的路徑,例如train_prodigy_ner.csv。
  • --label ORG,PERSON,GPE,TICKER:指定在Prodigy UI中可用的實體標籤。

如果成功,您將在命令列中看到此訊息:

✨ Starting the web server at http://localhost:8080 … Open the app in your browser and start annotating!

將URL複製到您的網頁瀏覽器中,您應該會看到一個註解UI,如圖3-3所示。

圖3-3. Prodigy NER註解UI

現在,我們可以突出顯示範圍並為資料標記正確的實體,如圖3-4所示。點選大綠色勾選框以繼續下一個範例(或在鍵盤上按“a”鍵)。如果您不確定答案,可以按鍵盤上的空格鍵跳過該範例。

圖3-4. Prodigy NER註解UI:註解第一個範例

讓我們註解幾百個範例,然後透過點選UI左上角“prodigy”旁邊的軟碟圖示來儲存它們。幾百個註解應該足以建立一個不錯的微調模型,儘管,像往常一樣,註解越多,模型的效能就越好。

完成註解後,我們可以使用data-to-spaCy Prodigy配方將NER註解以spaCy的JSON格式輸出(見圖3-5)。

圖3-5. data-to-spaCy Prodigy配方

對於這個配方,我們需要指定輸出路徑(用於訓練模型)、評估輸出路徑(用於評估模型)、語言(在我們的例子中是“en”),以及使用–ner標籤的NER資料集:

$ python -m prodigy data-to-spacy <output> <eval_output> --lang en \
--ner ag_data_ner_ticker

內容解密:

  • python -m prodigy data-to-spacy:使用Prodigy的data-to-spacy配方將註解輸出為spaCy的JSON格式。
  • <output>:指定輸出路徑,用於訓練模型。
  • <eval_output>:指定評估輸出路徑,用於評估模型。
  • --lang en:指定語言為英語。
  • --ner ag_data_ner_ticker:指定NER資料集。

此命令以JSON格式輸出註解,但截至spaCy v3.0(2021年1月發布),spaCy的主要資料格式是二進位制格式。在使用spaCy進行訓練之前,我們需要將JSON格式轉換為二進位制。spaCy有一個轉換配方(見圖3-6),我們現在將使用它:

$ python -m spacy convert <path-to-json> <path-for-binary-output>

內容解密:

  • python -m spacy convert:使用spaCy的convert配方將JSON格式轉換為二進位制格式。
  • <path-to-json>:指定JSON檔案的路徑。
  • <path-for-binary-output>:指定二進位制輸出檔案的路徑。

使用spaCy訓練自定義NER模型

我們將訓練兩個獨立的NER模型。首先,我們將使用遷移學習訓練一個NER模型。為了進行遷移學習,我們將使用一個名為RoBERTa的變壓器模型,這是一個由Facebook在2019年發布的大型預訓練語言模型。其次,我們將在沒有變壓器模型和GPU的情況下訓練一個NER模型,而僅依賴於根據CPU的訓練流程。這將幫助我們比較根據變壓器的GPU啟用的效能與標準的根據CPU的效能。

讓我們首先訓練根據變壓器的模型。我們將使用spaCy中的train命令,如圖3-7所示。

圖3-7. spaCy train命令

對於這個命令,我們需要指定組態路徑、輸出路徑和GPU標籤以啟用GPU上的訓練。對訓練組態路徑的要求是spaCy v3.0的新增功能。訓練組態是設定模型開發的所有設定和超引數的檔案:

$ python -m spacy train <config_path> --output <output_path> \
--gpu-id 0

內容解密:

  • python -m spacy train:使用spaCy的train命令訓練模型。
  • <config_path>:指定組態檔案的路徑。
  • --output <output_path>:指定輸出模型的路徑。
  • --gpu-id 0:啟用GPU上的訓練。

首先,讓我們生成這個組態檔案。令人驚訝的是,有一個spaCy配方可以從頭開始建立組態檔案(見圖3-8)。對於這個命令,我們需要指定語言(en)、需要修改的流程元件(ner)、最佳化標籤(“efficiency”以獲得更快的推斷/更小的模型,或“accuracy”以獲得更高的準確度/更慢、更大的模型)、是否使用GPU,以及命令是否應該覆寫輸出檔案(如果存在)。

圖3-8. spaCy init config

$ python -m spacy init config <config_path> --lang --pipeline \
--optimize --gpu --force

內容解密:

  • python -m spacy init config:使用spaCy的init config命令建立組態檔案。
  • <config_path>:指定組態檔案的路徑。
  • --lang--pipeline--optimize--gpu--force:根據需求設定組態檔案的相關引數。

或者,可以使用spaCy官方網站上的訓練組態UI來生成NER的最佳實踐版本的組態檔案。我們將使用這個方法,並啟動一個空白的根據變壓器的範本(啟用GPU)。

我們需要使用另一個名為init fill-config的spaCy命令來自動填充這個基本的NER範本,如圖3-9所示。

圖3-9. spaCy init fill-config

$ python -m spacy init fill-config <config_path_original> \
<config_path_new>

內容解密:

  • python -m spacy init fill-config:使用spaCy的init fill-config命令自動填充組態檔案。
  • <config_path_original>:指定原始組態檔案的路徑。
  • <config_path_new>:指定新組態檔案的路徑。

執行完上述步驟後,我們就可以開始訓練模型了。我們將進行30個epoch的訓練,並觀察模型的效能。結果如範例3-1所示。正如您所見,模型在30個epoch內達到了超過95的F1分數。

名為實體辨識的自然語言處理任務:以spaCy為例

在自然語言處理(NLP)領域中,名為實體辨識(Named Entity Recognition, NER)是一項關鍵任務,旨在從文字中識別和分類別具有特定意義的實體,如人名、地名、組織名稱等。本文將探討如何使用spaCy,一個流行的NLP函式庫,來建立和訓練NER模型。

使用spaCy訓練NER模型

spaCy提供了一種高效的方式來訓練NER模型,無論是使用預訓練的transformer模型還是從頭開始訓練。以下範例展示瞭如何使用spaCy訓練兩個不同的NER模型:一個根據transformer且使用GPU加速,另一個則不使用transformer且根據CPU。

transformer GPU-based NER模型

首先,我們來看看使用transformer和GPU進行訓練的NER模型的結果。

========================= Training pipeline =============================
[i] Pipeline: ['transformer', 'ner']
[i] Initial learn rate: 0.0
E # LOSS TRANS... LOSS NER ENTS_F ENTS_P ENTS_R SCORE
---
 
---
---
- 
---
-
---
-
---
-- 
---
-
---
- 
---
---
 
---
---
 
---
---
 
---
--
0 0 866.56 1087.32 3.31 1.75 29.99 0.03
8 200 127924.74 63854.97 94.39 94.05 94.75 0.94
17 400 2339.89 2798.22 94.13 93.78 94.48 0.94
26 600 166.36 2400.21 95.40 95.00 95.81 0.95

#### 內容解密:

  • 訓練流程:此模型使用了包含transformer的訓練流程,這是一種預訓練語言模型,能夠提供豐富的語義資訊。
  • 損失函式:LOSS TRANS和LOSS NER分別代表transformer部分和NER部分的損失函式值,隨著訓練的進行,這些值逐漸降低,表明模型的表現越來越好。
  • 評估指標:ENTS_F、ENTS_P和ENTS_R分別代表F1分數、精確率和召回率,這些指標顯示了模型在NER任務上的表現。最終,模型的F1分數達到了0.95,表現出色。

非transformer CPU-based NER模型

接下來,我們看看不使用transformer且根據CPU訓練的NER模型的結果。

========================= Training pipeline =============================
[i] Pipeline: ['tok2vec', 'ner']
[i] Initial learn rate: 0.001
E # LOSS TOK2VEC LOSS NER ENTS_F ENTS_P ENTS_R SCORE
---
 
---
---
- 
---
-
---
-
---
-- 
---
-
---
- 
---
---
 
---
---
 
---
---
 
---
--
0 0 0.00 51.44 4.16 2.39 16.14 0.04
...
21 2200 179.27 60.26 89.19 89.91 88.48 0.89

#### 內容解密:

  • 訓練流程:此模型使用了’tok2vec’和’ner’的訓練流程,不依賴transformer。
  • 損失函式:LOSS TOK2VEC和LOSS NER顯示了模型在不同階段的損失。
  • 評估指標:最終,模型的F1分數達到了約0.89,雖然不如根據transformer的模型,但仍然是一個不錯的成績。

自定義NER模型與原始模型的比較

透過比較自定義訓練的NER模型與spaCy原始的en_core_web_trf模型,可以觀察到自定義模型在特定實體型別上的表現略有提升。

#### 載入自定義NER模型並檢視其後設資料

# 載入自定義NER模型
spacy.require_gpu()
custom_ner_model = spacy.load(cwd + '/models/ag_dataset/ner/ner-gpu-blank/model-best')

# 檢視模型的後設資料
import pprint
pp = pprint.PrettyPrinter(indent=4)
pp.pprint(custom_ner_model.meta)

#### 使用內建視覺化工具比較兩個模型的NER結果

# 對比原始模型與自定義模型的NER結果
from spacy import displacy
import random

base_model = spacy.load("en_core_web_trf")
options = {"ents": ["ORG","PERSON","GPE","TICKER"]}
for j in range(3):
    i = random.randint(0, len(data))
    print("Article",i)
    doc_base = base_model(data.loc[i,"description"])
    doc_custom = custom_ner_model(data.loc[i,"description"])
    print("Base Model NER:")
    displacy.render(doc_base, style="ent", options=options, jupyter=True)
    print("Custom Model NER:")
    displacy.render(doc_custom, style="ent", options=options, jupyter=True)
    print("\n")

#### 結果分析:

透過視覺化比較,可以發現自定義模型在某些樣本上表現更好,例如正確識別出「NewsFactor」和「Cingular Wireless」作為組織名稱。然而,兩者的表現並非總是存在顯著差異,自定義模型有時也會錯失某些實體。