You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何高效标注100k+N2C2数据集用于BERT-NER?求实现资源

针对N2C2数据集的100k+规模BERT-NER高效标注方案

一、高效标注核心策略

  • 半监督预标注先行:用N2C2已标注子集(比如20k规模)训练基础BERT-NER模型,对剩余未标注数据批量预标注。过滤置信度>0.9的结果直接保留,仅对低置信度(<0.7)或边界模糊的样本做人工修正,可减少70%以上的纯人工标注量。
  • 主动学习迭代优化:优先筛选模型最不确定的样本(如实体置信度接近0.5、边界歧义的文本)进行标注,每完成一批标注就重新训练模型,再迭代筛选下一批高价值样本,最大化标注效率。
  • 医疗规则辅助过滤:针对N2C2的医疗文本特性,编写正则表达式匹配常见实体(如ICD疾病代码、药物通用名、诊疗操作术语),先做规则预标注,再结合模型结果交叉验证,降低人工标注成本。
  • 本地批量标注工具:使用LabelStudio(本地部署)或Prodigy,配置自定义NER标注模板,支持快捷键批量确认/修正预标注结果,避免在线工具的数据安全风险。

二、核心实现代码示例

1. 基于N2C2子集训练基础BERT-NER模型

from transformers import BertTokenizer, BertForTokenClassification, Trainer, TrainingArguments
import datasets

# 加载N2C2标注子集(需转换为JSON格式,包含text和labels字段)
dataset = datasets.load_dataset("json", data_files="n2c2_labeled_subset.json")
# 定义N2C2实体对应的BIO标签列表
label_list = ["O", "B-DISEASE", "I-DISEASE", "B-DRUG", "I-DRUG", "B-TREATMENT", "I-TREATMENT"]

tokenizer = BertTokenizer.from_pretrained("bert-base-uncased")
def tokenize_and_align_labels(examples):
    tokenized_inputs = tokenizer(examples["text"], truncation=True, padding="max_length", max_length=128)
    labels = []
    for i, label in enumerate(examples["labels"]):
        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)  # padding标签忽略
            elif word_idx != previous_word_idx:
                label_ids.append(label_list.index(label[word_idx]))
            else:
                # 非首词的实体标签需保持I-前缀
                current_label = label_list.index(label[word_idx])
                label_ids.append(current_label if label_list[current_label].startswith("I-") else -100)
            previous_word_idx = word_idx
        labels.append(label_ids)
    tokenized_inputs["labels"] = labels
    return tokenized_inputs

tokenized_datasets = dataset.map(tokenize_and_align_labels, batched=True)

model = BertForTokenClassification.from_pretrained("bert-base-uncased", num_labels=len(label_list))

training_args = TrainingArguments(
    output_dir="./n2c2_bert_ner",
    per_device_train_batch_size=16,
    num_train_epochs=3,
    logging_dir="./logs",
    logging_steps=100,
    evaluation_strategy="epoch",
    save_strategy="epoch"
)

trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=tokenized_datasets["train"],
    eval_dataset=tokenized_datasets["validation"]
)

trainer.train()

2. 批量预标注未标注数据

import torch
import json
from transformers import pipeline

# 加载训练好的模型和分词器
ner_pipeline = pipeline("ner", model="./n2c2_bert_ner/checkpoint-xxx", tokenizer=tokenizer, aggregation_strategy="simple")

# 读取未标注文本
with open("n2c2_unlabeled_data.txt", "r", encoding="utf-8") as f:
    unlabeled_texts = [line.strip() for line in f if line.strip()]

prelabeled_results = []
for text in unlabeled_texts:
    ner_results = ner_pipeline(text)
    # 转换为BIO格式标签(按词拆分)
    words = text.split()
    bio_labels = ["O"] * len(words)
    for res in ner_results:
        # 匹配实体对应的词位置(简化处理,可根据实际文本优化)
        char_start, char_end = res["start"], res["end"]
        word_start = len(text[:char_start].split())
        word_end = len(text[:char_end].split())
        entity_type = res["entity_group"]
        bio_labels[word_start] = f"B-{entity_type}"
        for idx in range(word_start + 1, word_end):
            bio_labels[idx] = f"I-{entity_type}"
    prelabeled_results.append({"text": text, "labels": bio_labels})

# 保存预标注结果
with open("n2c2_prelabeled_data.json", "w", encoding="utf-8") as f:
    json.dump(prelabeled_results, f, indent=2, ensure_ascii=False)

3. 主动学习筛选高不确定性样本

import numpy as np

def calculate_uncertainty(model, tokenizer, texts):
    model.eval()
    uncertainty_scores = []
    with torch.no_grad():
        for text in texts:
            inputs = tokenizer(text, return_tensors="pt", truncation=True, padding="max_length", max_length=128)
            outputs = model(**inputs)
            logits = outputs.logits
            # 计算每个token的熵,取平均值作为样本不确定性
            probs = torch.softmax(logits, dim=-1)
            entropy = -torch.sum(probs * torch.log(probs + 1e-10), dim=-1)
            # 忽略padding部分的熵
            valid_entropy = entropy[inputs["attention_mask"].squeeze() == 1]
            avg_entropy = torch.mean(valid_entropy).item()
            uncertainty_scores.append(avg_entropy)
    return uncertainty_scores

# 获取所有未标注样本的不确定性分数
uncertainty_scores = calculate_uncertainty(model, tokenizer, unlabeled_texts)

# 筛选top 1000个最不确定的样本用于人工标注
top_k = 1000
top_uncertain_indices = np.argsort(uncertainty_scores)[-top_k:]
top_uncertain_texts = [unlabeled_texts[i] for i in top_uncertain_indices]

# 保存待标注样本
with open("n2c2_high_priority_texts.txt", "w", encoding="utf-8") as f:
    for text in top_uncertain_texts:
        f.write(text + "\n")

三、实用教程与资源

  • N2C2数据格式适配:参考N2C2官方数据说明,将原始XML/CSV格式转换为BIO格式,重点处理医疗文本中的长实体、嵌套实体标注逻辑。
  • Hugging Face NER流程:学习官方TokenClassification任务教程,掌握数据预处理、模型微调、评估的完整流程,直接适配N2C2的实体类型即可。
  • 主动学习落地:使用modAL库实现主动学习的自动化样本筛选,结合模型迭代训练,进一步提升标注效率。
  • 标注工具使用:参考LabelStudio官方NER标注模块教程,配置自定义实体类型,导入预标注结果进行批量修正。

内容的提问来源于stack exchange,提问作者Muh. Rayi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 02:17:12