關注公眾號,回復「spark nlp」獲取原始碼:
NER 是信息提取的一個子任務,旨在將非結構化文本中提到的命名實體定位和分類為預定義的類別,例如人名、組織、位置、醫療代碼、時間表達式、數量、貨幣價值、百分比等。
NER 用於自然語言處理 (NLP) 的許多領域,它可以幫助回答許多現實世界的問題,例如:
新聞文章中提到了哪些公司?
哪些測試適用於患者(臨床報告)?
推文中是否提到了產品名稱?
Spark NLP 庫中的臨床 NER 模型
NER系統NER 是 NLP 中的一個活躍話題,多年來研究工作不斷發展。我們可以在以下主題中總結這些努力:
經典方法(基於規則)
機器學習方法
3. 深度學習方法
雙向 LSTM-CRF
雙向 LSTM-CNN
雙向 LSTM-CNNS-CRF
預訓練語言模型(Elmo和BERT)
4.混合方法(DL + ML)
在 Spark NLP 中使用 BERT 的 NER在 本文中,我們將嘗試向您展示如何使用Spark NLP 庫中的 BERT 構建最先進的 NER 模型。我們將要實現的模型的靈感來自於 NER 以前最先進的模型:Chiu 和 Nicols,使用雙向 LSTM-CNN 命名實體識別, 並且它已經嵌入到 Spark NLP NerDL Annotator 中。這是一種新穎的神經網絡架構,可使用混合雙向 LSTM 和 CNN 架構自動檢測單詞和字符級特徵,無需進行大多數特徵工程。
在開發該模型時,還沒有類似 BERT 的語言模型,原始論文中使用的是 Glove 詞嵌入。但在本文中,我們將用 BERT 替換 Glove。
在 Spark NLP 中,有三種不同的預訓練詞嵌入模型(及其變體),您可以根據使用情況即插即用。
為了簡單起見並節省您一些時間,我們將跳過安裝步驟。我們已經有另一篇文章詳細介紹了 Spark NLP。而且我們會在Colab 中完成所有工作,讓您直接跳入 Spark NLP。
讓我們開始吧!
我們首先下載訓練和測試集。我們將使用官方的 CoNLL2003 數據集,這是一個幾乎所有 NER 論文中都使用過的基準數據集。您可以在此處下載此數據集。
讓我們簡要解釋一下什麼是 CoNLL。「 Conference on Natural Language Learning」的縮寫, CoNLL 也是訓練集中用於訓練 NER 模型的注釋的標準格式。這是它的外觀。您可以在 CONLL 中注釋自己的數據,然後在 Spark NLP 中訓練自定義 NER。還有一些免費的注釋工具可用於標記自己的數據。
下面是我們要一一解釋的流程。
我們導入相關的包,然後開始編碼。
from pyspark.sql import SparkSession
from pyspark.ml import pipelinefrom sparknlp.annotator import *
from sparknlp.common import *
from sparknlp.base import *import sparknlp
spark = sparknlp.start()print("Spark NLP 版本:", sparknlp.version())
print("Apache Spark 版本:", spark.version)>> Spark NLP 版本:2.4.1
>> Apache Spark 版本:2.4.4
如果您想在 GPU 上訓練您的模型,您只需使用spark = sparknlp(gpu=True).
然後我們將 CoNLL 文件轉換為 Spark 數據幀,並生成所有附加欄位以供稍後使用。
from sparknlp.training import CoNLL
training_data = CoNLL().readDataset(spark, './eng.train')
training_data.show()
如您所見,輸入數據集只有行和標籤,現在我們這裡有幾列。我們在 Spark NLP 中的 CoNLL 助手類將數據集作為輸入並準備我們將要使用的 NER 算法所需的標記、POS(詞性)和句子。您需要做的就是準備或下載 CoNLL 格式的數據集。
(注意:儘管原始論文建議使用 POS,但在 Spark NLP 訓練時不會使用它。由於 POS 標籤是 CONLL 格式,因此 CoNLL 助手類從作為輸入給出的 CoNLL 文件中提取它們。因此,我們甚至可以在該源 CoNLL 文件中使用 -X- 而不是 POS 標籤。)
下一步是通過BERT得到詞嵌入。我們將使用名為BertEmbeddings().
bert = BertEmbeddings.pretrained('bert_base_cased', 'en') \ .setInputCols(["sentence",'token'])\
.setOutputCol("bert")\
.setCaseSensitive(False)\
.setPoolingLayer(0) # default 0
在 Spark NLP 中,我們有四個預訓練的 BERT 變體:bert_base_uncased、bert_base_cased、bert_large_uncased、bert_large_cased。使用哪一個取決於您的用例、訓練集以及您嘗試建模的任務的複雜性。
在上面的代碼片段中,我們基本上是bert_base_cased從 Spark NLP 公共資源中加載版本並指向sentence和token列setInputCols().簡而言之,BertEmbeddings()注釋器將獲取sentence和token列並在bert列中填充 Bert 嵌入。一般來說,每個詞都被翻譯成一個 768 維的向量。所述參數setPoolingLayer()可以被設置為0作為第一層和最快的,-1作為最後的層和-2作為second-to-last-hidden layer。
正如官方BERT 論文的作者所解釋的,不同的 BERT 層捕獲不同的信息。最後一層在預訓練過程中與目標函數(即掩碼語言模型和下一句預測)過於接近,因此可能會偏向那些目標。如果您無論如何都想使用最後一個隱藏層,請隨意設置pooling_layer=-1. 直覺上,pooling_layer=-1接近於訓練輸出,所以可能會偏向於訓練目標。如果您不對模型進行微調,則可能會導致不良表示。也就是說,這是模型準確性和您擁有的計算資源之間的權衡問題。
然後我們導入NerDLApproach()annotator,它是負責訓練 NER 模型的主要模塊。
nerTagger = NerDLApproach()\
.setInputCols(["sentence", "token", "bert"])\
.setLabelColumn("label")\
.setOutputCol("ner")\
.setMaxEpochs(1)\
.setRandomSeed(0) )\
.setVerbose(1)\
.setValidationSplit(0.2)\
.setEvaluationLogExtended(True)\
.setEnableOutputLogs(True)\
.setIncludeConfidence(True)\
.setTestDataset("test_withEmbeds.parquet")
讓我們解釋一下每個參數:
.setInputCols([「sentence」, 「token」, 「bert」]) : NER 模型將用於生成特徵的列。
.setLabelColumn(「label」) : 目標列
.setOutputCol(「ner」):預測將寫入ner列
.setMaxEpochs(1) : 訓練的時期數
.setVerbose(1) : 訓練時的日誌級別
.setValidationSplit(0.2):要在每個 Epoch 上針對模型進行驗證的訓練數據集的比例。該值應介於 0.0 和 1.0 之間,默認情況下為 0.0 和關閉。
.setEvaluationLogExtended(True):是否要擴展驗證日誌:它顯示每個標籤的時間和評估。默認值為假。
.setEnableOutputLogs(True): 是否輸出到日誌文件夾。設置為 True 時,日誌和訓練指標將寫入主文件夾中的文件夾。
.setIncludeConfidence(True) :是否在注釋元數據中包含置信度分數。
.setTestDataset(「test_withEmbeds.parquet」): 測試數據集的路徑。如果設置,則用於在訓練期間計算其統計信息。這也是 CoNLL 格式,但嵌入添加並像以前一樣保存到磁碟。如果您不需要在 Spark 中看不見的測試集上評估您的模型,則不必設置此項。
test_data = CoNLL().readDataset(spark, './eng.testa')
test_data = bert.transform(test_data)
test_data.write.parquet("test_withEmbeds.parquet")
您還可以設置學習率 ( setLr)、學習率衰減係數 ( setPo)setBatchSize和setDropout速率。有關整個列表,請參閱官方存儲庫。
現在我們可以將這兩個注釋器附加到管道中。
ner_pipeline = Pipeline(stages = [bert, nerTagger])ner_model = ner_pipeline.fit(training_data)
根據數據集大小和您設置的時期數完成擬合後,就可以使用了。讓我們看看僅 1 個時期後日誌的樣子(annotators_log在您的主文件夾中的文件夾內)。
您可以在Colab實例的根目錄中找到該文件夾。
總之,我們只需在 Spark NLP 中使用幾行代碼就可以在 SOTA 算法中訓練自定義 NER。
並在另一個pipeline中使用
訓練pipeline 並預測
text = "Peter Parker is a nice man and lives in New York"prediction_data = spark.createDataFrame([[text]]).toDF("text")prediction_model = custom_ner_pipeline.fit(prediction_data)preds = prediction_model.transform(prediction_data)preds.show()
我們還可以編寫一些 PySpark 函數,在 NerConverter 的幫助下使輸出更漂亮和有用ner_span。
我們還可以將此管道轉換為 LightPipeline 以擺脫 Spark 開銷並進行閃電般的速度推理。將任何擬合的管道轉換為 LightPipeline 後,您可以輕鬆地更快地獲得字符串或字符串列表的預測。您可以在此處找到有關 LightPipelines 的更多詳細信息。
所有代碼都可以在Github 上找到。在我們通過 Github 分享的筆記本中,您甚至會看到其他各種嵌入的實現,以及如何準備自己的 CoNLL 文件來訓練自定義 NER 模型。
結論目前為止就這樣了。在本文中,我們討論了如何使用 NLP 鎮上最酷的孩子 BERT 在 Spark NLP 中訓練最先進的 NER 模型。我希望你喜歡!