当前位置: 首页 > article >正文

NLP:命名实体识别及案例(Bert微调)

1. 命名实体识别

1.1 序列标注

  序列标注(Sequence Labeling)是NLP中最基础的任务之一,其应用十分广泛。它指的是对给定的序列(如文本中的单词或字符)中的每个元素进行标注,以识别出该元素在序列中的特定角色或属性。

1.2 命名实体识别

  命名实体识别(Named Entity Recognition, NER)是序列标注任务中的一种,旨在从文本中识别并分类特定的实体。识别的实体通常包括人名、地名、机构名、时间、日期、货币等。这种识别在许多实际应用中非常重要,如信息提取、自动问答、机器翻译和文本摘要。

2. 利用Bert微调模型进行命名实体识别

2.1 BIO标记

  BIO标记法是命名实体识别中的一种常用数据标注方案,用于标记文本中每个单词的标签,明确它是属于实体的哪部分。BIO 标记法通过B-、I- 和O三个前缀来表示命名实体的边界和结构:

  • B-(Begin):表示命名实体的开头。例如,B-PER表示人名实体的第一个单词。
  • I-(Inside):表示命名实体的内部部分。例如,I-PER 表示人名实体中非首字的单词。
  • O(Outside):表示这个单词不属于任何命名实体。

BIO标记方法中通常包含:PER(人名)ORG(组织名)LOC(地名)MISC(事件、产品、国籍、语言)

2.2 数据集介绍

  这里使用NER任务中常用的数据集:CoNLL-2003,该数据集最早由 CoNLL(Conference on Computational Natural Language Learning)共享任务发布,广泛应用于自然语言处理中的 NER 任务。该数据集中训练集共14041条,验证集共3250条,测试集共3453。训练集中的数据如下:

{
    "chunk_tags": [11, 12, 12, 21, 13, 11, 11, 21, 13, 11, 12, 13, 11, 21, 22, 11, 12, 17, 11, 21, 17, 11, 12, 12, 21, 22, 22, 13, 11, 0],
    "id": "0",
    "ner_tags": [0, 3, 4, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    "pos_tags": [12, 22, 22, 38, 15, 22, 28, 38, 15, 16, 21, 35, 24, 35, 37, 16, 21, 15, 24, 41, 15, 16, 21, 21, 20, 37, 40, 35, 21, 7],
    "tokens": ["The", "European", "Commission", "said", "on", "Thursday", "it", "disagreed", "with", "German", "advice", "to", "consumers", "to", "shun", "British", "lamb", "until", "scientists", "determine", "whether", "mad", "cow", "disease", "can", "be", "transmitted", "to", "sheep", "."]
}

关于这个数据集,每个部分的具体含义如下:

  • tokens表示文本中的单词。所有的tokens组合起来就是句子的原始文本。NER任务中需要为数据集中的每个单词都会被标注相关的标签。
  • chunk_tags表示短语块标签。它标记出句子中的短语结构,帮助识别出名词短语(NP)、动词短语(VP)、介词短语(PP)等。
  • ner_tags表示命名实体识别标签,使用的是BIO标记法。在该数据集在hugging face上的主页上可以找到BIO标记与数字的对应方式。具体如下:
{'O': 0, 'B-PER': 1, 'I-PER': 2, 'B-ORG': 3, 'I-ORG': 4, 'B-LOC': 5, 'I-LOC': 6, 'B-MISC': 7, 'I-MISC': 8}

依据这个信息,European Commission被标记为ORG。

  • pos_tags为词性标签。
2.3 labels准备

  因为使用BertTokenizer处理数据的tokens时,使用WordPiece分词算法时可能会将一个完整的单词拆分成了多个,所有训练数据集中的ner_tags并不能直接作为后续分类器BertForTokenClassification的labels用于训练。在这种情况下,标签需要与每个子词对齐,可以采用如下规则:

  • 第一个子词保留原始标签,比如B-ORG,其余子词可以标注为I-ORG。举例如下:
tokens = ['Ap', '##ple', 'is', 'a', 'technology', 'company']
labels = ['B-ORG', 'I-ORG', 'O', 'O', 'O', 'O']
2.4 Bert微调
from datasets import load_dataset
from transformers import BertTokenizerFast, BertForTokenClassification
from transformers import TrainingArguments, Trainer

# 加载 CoNLL-2003 数据集
dataset = load_dataset("conll2003")
train_dataset = dataset["train"]
eval_dataset = dataset["validation"]
test_dataset = dataset["test"]
# 加载 BERT tokenizer 和模型
tokenizer = BertTokenizerFast.from_pretrained("bert-base-cased")
label_list = dataset["train"].features["ner_tags"].feature.names
print(train_dataset[0])
def tokenize_and_align_labels(examples):
    tokenized_inputs = tokenizer(examples["tokens"], truncation=True, padding="max_length",
                                 is_split_into_words=True)
    labels= []
    for i, label in enumerate(examples["ner_tags"]):
        word_ids = tokenized_inputs.word_ids(batch_index=i)
        previous_word_idx = None
        label_ids = []
        for word_idx in word_ids:
            if word_idx is None:
                label_ids.append(-100)
            elif word_idx != previous_word_idx:
                label_ids.append(label[word_idx])
            else:
                label_ids.append(label[word_idx])
            previous_word_idx = word_idx
        labels.append(label_ids)
    tokenized_inputs["labels"] = labels
    print(labels[0])
    print(len(tokenized_inputs.input_ids[0]),len(labels[0]))
    return tokenized_inputs

train_dataset = train_dataset.map(tokenize_and_align_labels, batched=True)
eval_dataset = eval_dataset.map(tokenize_and_align_labels, batched=True)
test_dataset = test_dataset.map(tokenize_and_align_labels, batched=True)

model= BertForTokenClassification.from_pretrained("bert-base-cased", 
                                                  num_labels=len(label_list))
training_args = TrainingArguments(
    output_dir='./results',          # 输出目录
    eval_strategy="epoch",     # 每个 epoch 进行评估
    learning_rate=2e-5,              # 学习率
    per_device_train_batch_size=16,  # 训练 batch size
    per_device_eval_batch_size=16,   # 评估 batch size
    num_train_epochs=3,              # 训练 epoch 数
    weight_decay=0.01,               # 权重衰减
)

# 使用 Trainer API
trainer = Trainer(
    model=model,                      # 待训练的模型
    args=training_args,               # 训练参数
    train_dataset=train_dataset,  # 训练数据集
    eval_dataset=eval_dataset,  # 验证数据集
    tokenizer=tokenizer               # 使用的 tokenizer
)

trainer.train()
eval_results = trainer.evaluate()
print(eval_results)
2.5 抽取结果

  正如嵌入所述,有些完整的单词投入Bert模型之后会被切成子词,我们在抽取模型预测的结果的时候也需要进行一个反操作,将子词及子词对应的预测结果合并到一起,具体代码如下:

def transform_labels_to_nertags(dataset,labels):
    new_labels=[]
    for i,label in enumerate(labels):
        label=[item for item in label if item != -100]
        words_id=dataset["word_ids"][i]
        idx_label=[]
        for i in range(len(words_id)):
            if i==0:
                idx_label.append(label[0])
            else:
                if words_id[i]==words_id[i-1]:
                    continue
                else:
                    idx_label.append(label[i])
        new_labels.append(idx_label)
    return new_labels

_, labels, _ = trainer.predict(test_dataset)

test_dataset= test_dataset.add_column("pred_ner_tags",transform_labels_to_nertags(test_dataset,labels))

print(test_dataset[0]["pred_ner_tags"],test_dataset[0]["ner_tags"])
print(test_dataset[1]["pred_ner_tags"],test_dataset[1]["ner_tags"])

http://www.kler.cn/a/320413.html

相关文章:

  • android 如何获取当前 Activity 的类名和包名
  • ggplot2-scale_x_continuous()
  • React教程第二节之虚拟DOM与Diffing算法理解
  • 深入理解Redis(七)----Redis实现分布式锁
  • 【MySql】实验十六 综合练习:图书管理系统数据库结构
  • Isaac Sim+SKRL机器人并行强化学习
  • Github 2024-09-22 php开源项目日报 Top10
  • 零基础入门ComfyUI(一)初识ComfyUI
  • 软件架构设计-系统架构师(七十二)
  • SQL_over_partition_by_order_by
  • go 安装三方库
  • ShiftAddAug:基于乘法算子训练的最新无乘法网络方案 | CVPR‘24
  • Wpf使用NLog将日志输出到LogViewer
  • 8.5 矢量图层点要素分级(Graduated)渲染使用
  • 用 CSS 动画记录宝宝0-280天的变化
  • 传输大咖46 | 还在为如何快速传输大文件困扰?镭速帮你解决
  • 数据集-目标检测系列-老虎检测数据集 tiger>> DataBall
  • 理解JVM中的死锁:原因及解决方案
  • 2015年国赛高教杯数学建模B题互联网+时代的出租车资源配置解题全过程文档及程序
  • 炉石传说辅助攻略—VMOS云手机助攻:国服回归任务要点,哪个辅助更好?
  • TypeScript 设计模式之【享元模式】
  • django项目添加测试数据的三种方式
  • 机器人时代的“触觉革命”:一块小传感器如何颠覆你的认知?
  • 2024最新Python Debugger工具pdb的用法(深度学习项目),了解输入输出的形状大小
  • 【每日一题】LeetCode 2306.公司命名(位运算、数组、哈希表、字符串、枚举)
  • Excel 设置自动换行