深度學習技術的進步推動了自然語言處理(NLP)領域的快速發展,使得機器能夠更好地理解和處理人類語言。預訓練語言模型的出現,結合遷移學習的應用,大幅降低了 NLP 任務的開發門檻,加速了 NLP 在各個領域的應用落地。本文從常見的 NLP 任務出發,探討如何利用 SpaCy 等工具,結合預訓練模型和遷移學習,高效地執行命名實體識別和文字分類別等任務。同時,也分析了模型在實際應用中的表現,並提供程式碼範例,方便讀者理解和實踐。
自然語言處理(NLP)任務與應用
機器學習是人工智慧的一個應用領域,允許機器透過學習資料來提高其在特定任務上的表現。自然語言處理(NLP)是機器學習的一個分支,涉及人類語言,如文字和語音。電腦視覺則是另一個分支,涉及視覺資料,如影像和影片,這些內容本文不會涉及。
機器可以從有標籤的資料或無標籤的資料中學習。涉及有標籤資料(例如,這是一張“貓”或“狗”的影像)的機器學習領域稱為監督式學習,而涉及無標籤資料(例如,你有貓和狗的影像,但沒有標籤)的領域稱為無監督式學習。第三個主要的機器學習領域稱為強化學習,涉及軟體代理在環境(物理或數位)中學習如何採取行動,以最大化其獲得的獎勵。
在機器學習中,機器從資料中學習(也稱為“訓練資料”)以提高其在特定任務上的表現,這個過程會產生一個模型。一旦機器的表現達到令人滿意的水平,模型就會以模型引數(例如權重)的形式儲存訓練過程中獲得的知識,這些引數用於機器學習中的微積分和線性代數運算。
模型使用這些儲存的知識(即模型引數)對新的或從未見過的資料進行推斷(即生成預測)。只要新資料與機器訓練過的資料相似,新資料上的表現就應該與機器在原始訓練資料集上達到的表現相似。
回到我們最初的主題,我們可以使用預訓練的語言模型來執行常見的NLP任務。當我們提到預訓練模型時,我們指的是之前已經在資料上訓練過的模型。我們不是從頭開始訓練機器來執行NLP任務,而是從已經在大量資料上訓練過的預訓練語言模型開始,並對其進行微調,以執行超出語言建模的特定NLP任務;這個過程稱為遷移學習。
遷移學習與微調
使用預訓練的語言模型是執行常見NLP任務的最快方法。相反,如果需要執行不常見的NLP任務,可能需要從頭開始訓練模型,包括為任務採集和標註相關的資料。
有些時候,你的任務與預訓練模型訓練的任務相似但不完全相同。在這些情況下,可以利用預訓練模型的一些先前學習成果,而不是從頭開始訓練一個全新的模型。你實際上是在將一個模型的學習成果“遷移”到另一個模型。
遷移學習之所以可能,是因為預訓練的語言模型是神經網路。神經網路是機器學習中的一類別模型,機器透過神經網路學習以一種能夠執行複雜任務(如資料分類別)的方式來表示資料。
神經網路通常涉及學習一系列表示,每一個後續表示使得機器更容易解釋來自前一個表示的資料。每個表示由神經網路中的一層學習;神經網路的層數越多,學習到的表示就越多。現代神經網路通常具有很多層,也就是說,它們非常深。這就是“深度學習”和“深度神經網路”術語的由來。
人工神經網路範例程式碼
import numpy as np
# 定義 sigmoid 函式
def sigmoid(x):
return 1 / (1 + np.exp(-x))
# 輸入資料
inputs = np.array([1, 2, 3, 4])
# 權重
weights = np.array([0.1, 0.2, 0.3, 0.4])
# 加權總和
weighted_sum = np.dot(inputs, weights)
# 輸出
output = sigmoid(weighted_sum)
print("輸出:", output)
內容解密:
import numpy as np:匯入 NumPy 函式庫,用於高效能數值運算。def sigmoid(x):定義 sigmoid 函式,用於將加權總和轉換為輸出機率。inputs和weights:定義輸入資料和權重,用於計算加權總和。np.dot(inputs, weights):計算輸入與權重的點積,得到加權總和。sigmoid(weighted_sum):將加權總和透過 sigmoid 函式,得到最終輸出。
在遷移學習中,我們借用預訓練語言模型的前幾層。這些前幾層已經學到了一些有用的資料表示,使得我們更容易為特定任務訓練神經網路的後續層。
例如,預訓練語言模型的前幾層可能已經發現了一種很好的方式來表示文字中的各種詞元。我們可以借用或遷移預訓練模型的知識,然後在我們的特定任務上對其進行進一步訓練(微調),而不需要從頭開始學習這些表示。
遷移學習和微調是當今NLP中非常常見的做法,並且有助於加速特定領域(如金融、法律等)中NLP應用的開發。如果每次從一個領域切換到另一個領域(例如,從分析金融檔案切換到法律檔案)都需要從頭開始訓練NLP模型,那麼構建NLP應用將是一個非常緩慢和艱苦的過程。
相反,我們可以利用在大規模網頁文字資料上訓練好的通用預訓練語言模型,並對其進行微調,以快速構建特定領域的語言模型,就像我們在第2章中對電影評論所做的那樣。遷移學習是近年來NLP在產業中蓬勃發展的原因之一。
NLP 任務
Hugging Face 對常見的NLP任務進行了很好的概述,我們將在這裡介紹。這些任務包括序列分類別、問答、語言建模、文字生成、命名實體識別、摘要和翻譯。這個列表並非詳盡無遺,但它突出了當今構建應用中最常見的NLP使用案例,是我們開始的好地方。
序列分類別
序列分類別就像聽起來一樣簡單;它涉及將序列分類別到給定的類別數量中。在文字上執行的序列分類別也稱為文字分類別,我們將在後面的章節中一起執行。序列分類別的一個例子是情感分析,就像我們在第2章中將IMDb電影評論分類別為正面、負面或中立一樣。另一個例子是蘊涵關係識別,它涉及將兩個陳述(分別稱為文字和假設)之間的關係標記為三個類別之一:正面蘊涵(假設陳述了關於文字中的情境或事件肯定正確的事情)、中立蘊涵(假設陳述了關於文字中的情境或事件可能正確的事情)或負面蘊涵(假設陳述了關於文字中的情境或事件肯定錯誤的事情)。
自然語言處理(NLP)任務與應用
常見的NLP任務
自然語言處理(NLP)是人工智慧領域的一個重要分支,旨在使機器能夠理解和處理人類語言。以下是一些常見的NLP任務:
文字分類別
文字分類別是指將文字分為預先定義的類別,例如垃圾郵件檢測、情感分析等。這項任務需要機器能夠理解文字的內容和語義,並將其歸類別到正確的類別中。GLUE(General Language Understanding Evaluation)基準是衡量序列分類別任務進展的最流行基準,而SuperGLUE則是更具挑戰性的基準。
問答系統
問答系統是指機器根據給定的問題,從一段文字或音訊中找出正確的答案。這項任務需要機器具備閱讀理解能力,能夠從文字中找出相關資訊並回答問題。SQuAD 2.0是衡量問答系統進展的最流行基準。
語言建模
語言建模是指預測給定文字序列下一個詞或詞序列的任務。這項任務分為因果語言建模和遮蔽語言建模兩種。因果語言建模常用於自然語言生成,而遮蔽語言建模則需要機器根據上下文預測被遮蔽的詞。
文字生成
文字生成是指根據給定的文字,生成一段連貫且相關的文字。這項任務比語言建模更開放,需要機器能夠生成更長的文字序列。OpenAI的GPT-2模型在2019年發布後,文字生成任務獲得了主流關注。
命名實體識別
命名實體識別(NER)是指將文字中的特定實體(如人名、組織名、地名等)識別出來的任務。CoNLL-2003是NER任務中最流行的資料集和基準。
文字摘要
文字摘要是指將長篇文字壓縮成短篇摘要的任務。這項任務需要機器能夠理解文字的主要內容並提取關鍵資訊。CNN/Daily Mail是文字摘要任務中最流行的資料集和基準。
翻譯
翻譯是指將文字從一種語言翻譯成另一種語言的任務。BLEU(Bilingual Evaluation Understudy)是衡量機器翻譯品質的最流行指標。
使用預訓練語言模型的NLP任務
在介紹完常見的NLP任務後,我們將使用預訓練語言模型來執行兩個NLP任務:命名實體識別和文字分類別。首先,我們需要一個自然語言資料集。
AG新聞分類別資料集
AG新聞分類別資料集是一個包含超過一百萬篇新聞文章的資料集,這些文章來自超過兩千個新聞來源。該資料集由學術界提供,用於研究目的。我們將使用Xiang Zhang構建的AG新聞分類別資料集版本,該版本在Kaggle上可用。
# 匯入函式庫
import pandas as pd
import os
# 取得當前工作目錄
cwd = os.getcwd()
# 匯入AG資料集
data = pd.read_csv(cwd+'/data/ag_dataset/train.csv')
data = pd.DataFrame(data=data)
# 將列名轉換為小寫並替換空格為下劃線
data.columns = data.columns.str.replace(" ","_")
data.columns = data.columns.str.lower()
# 新增一個新特徵“class_name”,將數值標籤對映到類別名稱
data["class_name"] = data["class_index"].map({1:"World", 2:"Sports", 3:"Business", 4:"Sci_Tech"})
內容解密:
- 匯入必要的函式庫:我們首先匯入了
pandas函式庫用於資料處理,以及os函式庫用於取得當前工作目錄。 - 載入資料集:使用
pd.read_csv函式載入AG新聞分類別資料集的訓練資料。 - 資料預處理:將資料集的列名轉換為小寫,並將空格替換為下劃線,以方便後續操作。
- 新增類別名稱:建立了一個新的列“class_name”,將數值標籤對映到對應的類別名稱(如1對映到"World"等),使得類別標籤更具可讀性。
自然語言資料集預覽與預處理
首先,我們來預覽資料集的內容。以下程式碼用於載入資料並進行初步檢視:
# 檢視資料
data
輸出結果顯示資料集包含 120,000 筆觀察值和四個特徵,分別是 class_index、title、description 和 class_name。進一步檢視每個類別的觀察值數量:
# 按類別統計觀察值數量
data.class_name.value_counts()
結果表明,四個類別(Sports、Sci_Tech、World 和 Business)各有 30,000 筆資料。接下來,我們檢視前 10 篇新聞文章的標題和描述,以更好地瞭解資料內容:
# 檢視前 10 篇文章的標題
for i in range(10):
print("第 {} 篇文章標題".format(i))
print(data.loc[i, "title"])
print("\n")
# 檢視前 10 篇文章的描述
for i in range(10):
print("第 {} 篇文章描述".format(i))
print(data.loc[i, "description"])
print("\n")
內容解密:
data.loc[i, "title"]用於存取第i筆資料的title欄位值。data.loc[i, "description"]用於存取第i筆資料的description欄位值。- 使用迴圈遍歷前 10 篇文章,並印出其標題和描述,以直觀瞭解資料內容。
資料預處理
為了提高資料品質,我們需要對文字進行預處理,去除不必要的字元和標記。以下程式碼實作了文字清理功能:
# 清理文字
cols = ["title", "description"]
data[cols] = data[cols].applymap(lambda x: x.replace("\\", " "))
data[cols] = data[cols].applymap(lambda x: x.replace("#36;", "$"))
data[cols] = data[cols].applymap(lambda x: x.replace(" ", " "))
data[cols] = data[cols].applymap(lambda x: x.strip())
# 將清理後的資料儲存到 CSV 檔案
data.to_csv(cwd + '/data/ag_dataset/prepared/train_prepared.csv', index=False)
內容解密:
- 使用
applymap對title和description欄位進行清理。 lambda x: x.replace("\\", " ")將反斜線替換為空白字元。lambda x: x.replace("#36;", "$")將#36;替換為美元符號$。lambda x: x.replace(" ", " ")將連續的空白字元替換為單一空白字元。x.strip()去除字串前後的空白字元。- 清理後的資料被儲存到新的 CSV 檔案中,以便後續使用。
命名實體辨識(NER)任務
命名實體辨識(NER)是自然語言處理中的一項重要任務,旨在識別文字中的命名實體並將其分類別。spaCy 提供了一個預訓練的語言模型,可用於執行 NER 任務。以下程式碼展示瞭如何使用 spaCy 的 transformer-based 模型進行 NER:
# 載入 spaCy 的 transformer-based 模型
import spacy
nlp = spacy.load("en_core_web_trf")
# 對文字進行 NER 處理
doc = nlp("Apple is looking to buy U.K. startup for $1 billion")
# 輸出 NER 結果
for ent in doc.ents:
print(ent.text, ent.label_)
內容解密:
- 載入 spaCy 的 transformer-based 模型
en_core_web_trf。 - 使用
nlp對輸入文字進行處理,得到doc物件。 - 遍歷
doc.ents,輸出每個命名實體的文字和對應的標籤。
命名實體辨識技術在資訊檢索、檔案摘要等領域具有廣泛的應用價值。在後續章節中,我們將進一步探討 NER 在實際場景中的應用。
實作spaCy於GPU上的命名實體辨識(NER)
安裝與設定
首先,我們需要在GPU上安裝spaCy,透過指定spacy[cuda110]來達成。你也可以根據你的CUDA版本進行調整。更多的細節可以參考spaCy的官方檔案。如果你不想使用GPU,可以直接使用pip install -U spacy來安裝spaCy。
pip install -U spacy[cuda110,transformers,lookups]==3.0.3
pip install -U spacy-lookups-data==1.0.0
pip install cupy-cuda110==8.5.0
python -m spacy download en_core_web_trf
內容解密:
pip install -U spacy[cuda110,transformers,lookups]==3.0.3:安裝spaCy並指定CUDA版本為11.0,同時包含transformers和lookups功能,版本固定為3.0.3。pip install -U spacy-lookups-data==1.0.0:安裝spaCy的額外查詢資料,版本固定為1.0.0。pip install cupy-cuda110==8.5.0:安裝支援CUDA 11.0的CuPy,版本固定為8.5.0,用於GPU加速。python -m spacy download en_core_web_trf:下載根據Transformer的英文語言模型en_core_web_trf。
載入模型與設定GPU
接下來,我們載入spaCy並啟用GPU加速。
import spacy
spacy.require_gpu()
print(spacy.require_gpu())
nlp = spacy.load("en_core_web_trf")
內容解密:
spacy.require_gpu():要求spaCy使用GPU,如果GPU不可用則會報錯。print(spacy.require_gpu()):輸出是否成功啟用GPU,成功則顯示True。nlp = spacy.load("en_core_web_trf"):載入根據Transformer的英文模型。
檢視模型的中繼資料
我們可以檢視模型的後設資料,以瞭解其底層元件和相關的準確度指標。
import pprint
pp = pprint.PrettyPrinter(indent=4)
pp.pprint(nlp.meta)
內容解密:
pprint.PrettyPrinter(indent=4):建立一個PrettyPrinter物件,用於美化輸出,縮排為4個空格。pp.pprint(nlp.meta):輸出模型的後設資料,包括其元件和效能指標。
分析命名實體辨識(NER)的結果
我們的模型支援多種實體型別,包括CARDINAL, DATE, EVENT, FAC, GPE, LANGUAGE, LAW, LOC, MONEY, NORP, ORDINAL, ORG, PERCENT, PERSON, PRODUCT, QUANTITY, TIME, 和 WORK OF ART。我們關注其中三種常見的實體型別:ORG(組織)、PERSON(人物)和GPE(地緣政治實體,如國家、城市、州)。
# 輸出前九篇文章的NER結果
for i in range(9):
print("Article", i)
print(data.loc[i, "description"])
print("Text Start End Label")
doc = nlp(data.loc[i, "description"])
for token in doc.ents:
print(token.text, token.start_char, token.end_char, token.label_)
print("\n")
內容解密:
doc = nlp(data.loc[i, "description"]):對每篇文章的描述套用spaCy模型進行NER分析。for token in doc.ents::遍歷所有被辨識出的實體。print(token.text, token.start_char, token.end_char, token.label_):輸出每個實體的文字、起始位置、結束位置和標籤。
結果分析
從輸出結果來看,模型對於多數實體的辨識效果不錯,但偶爾也會出現錯誤,如未能正確辨識AP為一個組織。整體而言,對於常見的實體型別如ORG、PERSON和GPE,模型的表現尚可,其中對於ORG的F1得分約為90,表現相對較弱。透過這些結果,我們可以進一步最佳化模型的效能,或是根據具體需求調整模型的設定。