上一篇文章講解了Langchain,實現一個簡單的demo,結合利用 LangChain 和 BERT 用于命名實體識別。
一、命名實體識別模型訓練(bert+CRF)
bert作為我們的預訓練模型(用于將輸入文本轉換為特征向量),CRF作為我們的條件隨機場(將嵌入特征轉為標簽),既然要訓練,那么我們的損失函數采用CRF 損失。
注意區分 交叉熵損失和CRF損失
CRF本身也有學習參數,一起參與梯度更新,只是參數為一塊轉移矩陣實現標簽之間的關系建模。
實現代碼如下,
模型和 分詞器都是使用的bert base chinese
實現了一個結合BERT和CRF模型的命名實體識別(NER)任務。首先,定義了BertCRF
類,利用BERT進行特征提取,并通過CRF層進行序列標簽預測。數據預處理部分使用BertTokenizerFast
對輸入文本進行分詞,同時將標簽對齊到子詞級別,處理特殊token。在數據加載方面,使用Hugging Face的datasets
庫加載MSRA NER數據集,并利用DataCollatorForTokenClassification
動態填充批次。
import torch
import torch.nn as nn
from torch.utils.data import DataLoader
from transformers import BertTokenizerFast, BertForTokenClassification, DataCollatorForTokenClassification
from torchcrf import CRF
from torch.optim import AdamW
from datasets import load_dataset
from seqeval.metrics import classification_report, accuracy_score
from tqdm.auto import tqdm# 定義BERT + CRF模型
class BertCRF(nn.Module):def __init__(self, bert_model_name, num_labels):super(BertCRF, self).__init__()# 使用預訓練的BERT模型進行特征提取self.bert = BertForTokenClassification.from_pretrained(bert_model_name, num_labels=num_labels)# CRF層進行標簽序列建模self.crf = CRF(num_labels, batch_first=True)def forward(self, input_ids, attention_mask, labels=None):# BERT輸出outputs = self.bert(input_ids, attention_mask=attention_mask)emissions = outputs[0] # 獲取BERT的最后隱藏層輸出if labels is not None: # 訓練模式loss = -self.crf(emissions, labels, mask=attention_mask.bool())return losselse:predictions = self.crf.decode(emissions, mask=attention_mask.bool())return predictions# 數據預處理函數
def preprocess_data(examples):"""對批數據進行分詞并對齊標簽。HuggingFace 的 tokenizer 在 `is_split_into_words=True` 且 `batched=True` 時可以一次處理多句子。這里根據 `word_ids(batch_index=...)` 把原始詞級別標簽擴展到子詞級別;對特殊 token (CLS、SEP、PAD) 使用 -100,使其在計算 loss 時被忽略。`msra_ner` 數據集的 `ner_tags` 已經是整數 ID,因此無需 label2id 轉換。"""# 分詞tokenized = tokenizer(examples["tokens"],