返回文章列表

生物醫學命名實體識別模型微調與評估

本文介紹如何使用預訓練的 BERT 模型微調,實作生物醫學命名實體識別(NER)任務,並詳細說明資料準備、BIO 標記、模型訓練、評估與視覺化等關鍵步驟。透過 Python 程式碼範例和說明,展示如何處理 ADE 資料集、建立 BIO 標記函式、使用 Hugging Face Transformers

自然語言處理 機器學習

生物醫學命名實體識別技術在從文字中提取關鍵資訊方面扮演著重要角色。本文以不良藥物反應(ADE)資料集為例,示範如何利用預訓練的 BERT 模型,構建一個高效的生物醫學 NER 模型。首先,將 ADE 資料集的藥物和不良反應資訊合併整理,並轉換為 Pandas DataFrame 格式,方便後續處理。接著,利用 BIO 標記方案,建立一個標記函式,將資料集中的藥物和不良反應實體進行標記。利用 Hugging Face Transformers 函式庫載入預訓練的 BioBERT 模型,並根據 ADE 資料集進行微調。訓練過程中,使用 seqeval 評估指標監控模型效能,並記錄精確率、召回率和 F1 分數等指標。最後,透過視覺化工具 displacy,將模型的預測結果以圖形化方式呈現,更直觀地展現模型的實體識別能力。

實體命名識別(NER)任務的資料準備與標記

在進行實體命名識別(NER)任務時,資料的準備和標記是至關重要的步驟。本章節將詳細介紹如何處理ADE(不良藥物反應)資料集,並對藥物和不良反應實體進行標記。

資料合併與整理

首先,我們需要將訓練資料集中的資訊合併到一個字典(merged_dataset)中。對於每筆記錄,我們檢查文字是否已經存在於字典中。如果不存在,我們新增文字以及相關的藥物和不良反應資訊,包括它們的起始和結束索引。如果文字已經存在,我們將新的藥物和不良反應資訊附加到現有的記錄中。這樣可以處理同一文字中提及多個藥物和不良反應的情況,確保每個例項都被唯一地捕捉。

程式碼範例:資料合併

merged_dataset = {}
for record in dataset:
    text = record["text"]
    if text not in merged_dataset:
        merged_dataset[text] = {
            "text": text,
            "drugs": [record["drug"]],
            "effects": [record["effect"]],
            "drug_starts": [record["drug_start"]],
            "drug_ends": [record["drug_end"]],
            "effect_starts": [record["effect_start"]],
            "effect_ends": [record["effect_end"]],
        }
    else:
        merged_dataset[text]["drugs"].append(record["drug"])
        merged_dataset[text]["effects"].append(record["effect"])
        merged_dataset[text]["drug_starts"].append(record["drug_start"])
        merged_dataset[text]["drug_ends"].append(record["drug_end"])
        merged_dataset[text]["effect_starts"].append(record["effect_start"])
        merged_dataset[text]["effect_ends"].append(record["effect_end"])

內容解密:

  1. 建立一個空字典 merged_dataset 用於儲存合併後的資料。
  2. 遍歷資料集中的每筆記錄,檢查文字是否已存在於 merged_dataset 中。
  3. 如果文字不存在,則新增該文字及其相關資訊到字典中。
  4. 如果文字已存在,則將新的藥物和不良反應資訊附加到現有的記錄中。
  5. 這樣可以確保每個文字例項都被唯一地捕捉,並且相關的藥物和不良反應資訊都被正確地合併。

資料轉換與排序

接下來,我們將合併後的資料轉換為Pandas DataFrame,並對藥物和不良反應的起始和結束索引進行排序,以確保它們的順序正確。

程式碼範例:資料轉換與排序

df_ade = pd.DataFrame(list(merged_dataset.values()))
df_ade["drug_starts"] = df_ade["drug_starts"].apply(list).apply(sorted)
df_ade["drug_ends"] = df_ade["drug_ends"].apply(list).apply(sorted)
df_ade["effect_starts"] = df_ade["effect_starts"].apply(list).apply(sorted)
df_ade["effect_ends"] = df_ade["effect_ends"].apply(list).apply(sorted)

內容解密:

  1. merged_dataset 的值轉換為列表,並建立一個Pandas DataFrame df_ade
  2. df_ade 中的藥物和不良反應的起始和結束索引進行排序,以確保它們的順序正確。
  3. 這樣可以保證後續的NER任務能夠正確地處理這些索引。

資料儲存與載入

然後,我們將處理後的資料儲存為JSON格式,以便後續載入到Dataset物件中。

程式碼範例:資料儲存與載入

df_ade.to_json("dataset.jsonl", orient="records", lines=True)
ade_dataset = load_dataset("json", data_files="dataset.jsonl")

內容解密:

  1. df_ade 儲存為JSON格式的檔案 dataset.jsonl
  2. 使用 load_dataset 函式載入JSON格式的資料到 ade_dataset 中。
  3. 這樣可以方便地將資料用於後續的NER任務。

資料分割

接下來,我們將資料集分割為訓練集和測試集。

程式碼範例:資料分割

ade_train_test = ade_dataset["train"].train_test_split()

內容解密:

  1. 使用 train_test_split 方法將 ade_dataset 分割為訓練集和測試集。
  2. 這樣可以評估NER模型的效能。

BIO標記方案

我們採用BIO(Beginning, Inside, Outside)標記方案對藥物和不良反應實體進行標記。

程式碼範例:BIO標記

entity_label_list = ['O', 'B-DRUG', 'I-DRUG', 'B-EFFECT', 'I-EFFECT']
custom_sequence = Sequence(feature=ClassLabel(num_classes=5, names=entity_label_list), length=-1)
ade_train_test["train"].features["ner_tags"] = custom_sequence
ade_train_test["test"].features["ner_tags"] = custom_sequence

內容解密:

  1. 定義 entity_label_list,包含BIO標記方案中的五個標籤。
  2. 建立一個 custom_sequence,指定NER標籤的類別和順序。
  3. custom_sequence 分別應用於訓練集和測試集的 ner_tags 特徵中。
  4. 這樣可以確保資料集被正確地格式化,以便用於訓練和評估NER模型。

建立BIO標記函式

我們建立一個函式 create_bio_tags_for_entities,用於對藥物和不良反應實體進行BIO標記。

程式碼範例:BIO標記函式

def create_bio_tags_for_entities(data_row, display_log=False):
    # 對句子進行分詞並遍歷每個詞元
    sentence = data_row["text"]
    bio_tags = []
    # ...
    return tokenized_data

內容解密:

  1. 定義函式 create_bio_tags_for_entities,接受一個資料行和一個顯示日誌的引數。
  2. 對句子進行分詞,並遍歷每個詞元,根據藥物和不良反應實體的起始和結束索引進行BIO標記。
  3. 如果啟用顯示日誌,則列印原始詞元及其對應的BIO標籤。
  4. 傳回包含BIO標籤的分詞資料。

測試BIO標記函式

最後,我們測試 create_bio_tags_for_entities 函式在一個樣本記錄上的表現。

程式碼範例:測試BIO標記函式

create_bio_tags_for_entities(ade_train_test["train"][2], display_log=True)

內容解密:

  1. 呼叫 create_bio_tags_for_entities 函式,傳入訓練集中的第二個記錄和 display_log=True
  2. 列印該記錄的原始詞元及其對應的BIO標籤,以驗證標記的正確性。

生物醫學命名實體識別(NER)模型的微調與評估

在生物醫學領域中,命名實體識別(NER)是一項至關重要的任務,主要用於識別文字中提到的實體,如藥物名稱、疾病名稱等。本篇文章將介紹如何利用預訓練的BERT模型進行微調,以實作生物醫學NER任務的最佳效能。

步驟1:資料準備

首先,我們需要準備一個包含生物醫學文字及其對應實體標註的資料集。這個資料集將被用於訓練和評估我們的NER模型。

步驟2:建立BIO標籤

為了讓模型能夠正確地識別實體,我們需要為每個實體建立BIO(Beginning-Inside-Outside)標籤。這個過程可以透過自定義函式create_bio_tags_for_entities來實作。

labeled_dataset = ade_train_test.map(create_bio_tags_for_entities)

程式碼解密:

  • ade_train_test.map(create_bio_tags_for_entities):這行程式碼將create_bio_tags_for_entities函式應用於資料集ade_train_test中的每個條目,生成對應的BIO標籤。
  • create_bio_tags_for_entities函式的作用是為資料集中的每個實體建立BIO標籤,這些標籤對於訓練NER模型至關重要。

步驟3:模型訓練

接下來,我們將載入預訓練的BERT模型,並對其進行微調,以適應我們的NER任務。

model_checkpoint = "dmis-lab/biobert-v1.1"
batch_size = 16
model = AutoModelForTokenClassification.from_pretrained(model_checkpoint, num_labels=len(entity_label_list))
args = TrainingArguments(
    f"{model_name}-finetuned-{task}",
    evaluation_strategy="epoch",
    learning_rate=1e-5,
    per_device_train_batch_size=batch_size,
    per_device_eval_batch_size=batch_size,
    num_train_epochs=5,
    weight_decay=0.05,
    logging_steps=1
)
data_collator = DataCollatorForTokenClassification(tokenizer)

程式碼解密:

  • AutoModelForTokenClassification.from_pretrained(model_checkpoint, num_labels=len(entity_label_list)):這行程式碼初始化了一個用於Token分類別的模型,使用了預訓練的model_checkpoint,並指定了標籤數量。
  • TrainingArguments定義了訓練過程中的超引數,如學習率、批次大小、訓練輪數等。
  • DataCollatorForTokenClassification(tokenizer)建立了一個資料收集器,用於處理和編碼訓練和評估資料。

步驟4:模型評估

在訓練過程中,我們已經組態了模型評估機制,透過evaluate_model_performance函式來計算模型的準確率和F1分數。

metric = load_metric("seqeval")

def evaluate_model_performance(prediction_data):
    predicted_values, actual_labels = prediction_data
    predicted_values = np.argmax(predicted_values, axis=2)
    # 省略部分程式碼...
    return {
        "precision_score": metric_results["overall_precision"],
        # 省略其他傳回指標...
    }

程式碼解密:

  • load_metric("seqeval"):載入用於評估序列標註任務的指標。
  • evaluate_model_performance函式計算模型的預測結果和實際標籤之間的精確率、召回率和F1分數等指標。

命名實體辨識(NER)模型的評估與視覺化

在前面的程式碼區塊中,我們準備評估NER模型的效能。首先載入seqeval評估指標,這是序列評估任務的標準。然後定義一個名為evaluate_model_performance的函式來處理模型的預測結果。

NER模型效能評估函式

該函式接收預測標籤和實際標籤,並對每個預測選擇具有最高機率的標籤(使用np.argmax)。接著,過濾掉標記為-100的特殊符號索引。然後,將數字預測和標籤轉換為entity_label_list中定義的對應文字標籤。最後,使用seqeval指標計算評估指標,包括精確度、召回率、F1分數和準確率。

程式碼範例:

metric_results = metric.compute(predictions=processed_predictions, references=processed_labels)
evaluation_results = {
    "precision_score": metric_results["overall_precision"],
    "recall_score": metric_results["overall_recall"],
    "f1_score": metric_results["overall_f1"],
    "accuracy_score": metric_results["overall_accuracy"],
}

內容解密:

  • metric.compute用於計算預測結果和實際標籤之間的評估指標。
  • evaluation_results字典儲存了整體的精確度、召回率、F1分數和準確率。

執行模型預測與評估

使用訓練好的模型對保留的測試資料集進行預測,並評估結果。利用trainer.predict方法獲得預測結果,並處理這些結果以找出每個標記最可能的標籤。過濾掉特殊符號(如[CLS]和[SEP]),並將數字索引轉換為文字標籤。

程式碼範例:

predicted_results, actual_labels, _ = trainer.predict(labeled_dataset["test"])
predicted_results = np.argmax(predicted_results, axis=2)
processed_predictions = [
    [entity_label_list[each_pred] for each_pred, each_label in zip(single_prediction, single_label) if each_label != -100]
    for single_prediction, single_label in zip(predicted_results, actual_labels)
]
processed_labels = [
    [entity_label_list[each_label] for each_pred, each_label in zip(single_prediction, single_label) if each_label != -100]
    for single_prediction, single_label in zip(predicted_results, actual_labels)
]

內容解密:

  • np.argmax用於找出每個標記最可能的標籤。
  • 過濾掉標記為-100的特殊符號索引,以專注於有意義的預測。
  • 將數字索引轉換為文字標籤,以便於理解。

評估結果

評估結果顯示,即使只進行了五個訓練週期,模型的F1分數仍可達到0.86,表現出良好的命名實體辨識能力。

視覺化實體註解

定義了一個名為display_entity_annotations的函式,用於視覺化模型對給定文字的實體註解。該函式處理模型識別出的標記,並根據其實體型別分配標籤。然後,利用SpaCy的displacy工具建立一個HTML表示形式,以突出顯示實體。

程式碼範例:

def display_entity_annotations(text):
    annotated_tokens = ade_ner_model(text)
    entity_annotations = []
    for token in annotated_tokens:
        entity_type = int(token["entity"][-1])
        if entity_type != 0:
            token["label"] = entity_label_list[entity_type]
            entity_annotations.append(token)
    render_params = [{"text": text, "ents": entity_annotations, "title": None}]
    rendered_html = displacy.render(render_params, style="ent", manual=True, options={
        "colors": {
            "B-DRUG": "#00FF00",
            "I-DRUG": "#00FF00",
            "B-EFFECT": "#ff0000",
            "I-EFFECT": "#ff0000",
        },
    })
    display(HTML(rendered_html))

內容解密:

  • ade_ner_model用於對文字進行實體註解。
  • displacy.render建立一個HTML表示形式,以突出顯示實體。
  • 使用不同的顏色來區分不同的實體型別(例如,藥物和效果)。

視覺化範例

使用一些來自Wikipedia的不良反應例子來視覺化模型的預測結果。

程式碼範例:

examples = [
    "Rhabdomyolysis associated with statins (anticholesterol drugs)",
    "Seizures caused by withdrawal from benzodiazepines",
    # ...
]
for sample in examples:
    display_entity_annotations(sample)
    print(f"{'=' * 50}\n")

內容解密:

  • 使用display_entity_annotations函式來視覺化模型的預測結果。
  • 列印分隔線以區分不同的例子。

圖表翻譯:

此圖示顯示了微調後的模型對一些未見資料的預測結果。模型能夠正確地識別出文字中的藥物和效果實體,並以不同的顏色突出顯示。

透過這些步驟,我們能夠全面評估和視覺化NER模型的效能,從而更好地理解其在命名實體辨識任務中的能力。