Spacy v3及以上版本不平衡数据NER的自定义损失函数与数据增强方法
针对spaCy v3+ NER不平衡数据集的解决方案
一、数据增强实现方案
- 标签感知实体替换:针对标注量少的标签(如示例中的标签b),先从训练集中提取所有该标签的实体构建候选集,对包含该标签的样本,随机用候选集中的同标签实体替换原实体,替换时同步调整NER标注的起止偏移量,确保标注正确。该方式可以快速扩充少样本标签的有效样本量,不会引入标注错误。
- 非实体区域扰动:仅对包含少样本标签的样本做扰动操作,包括随机交换非实体区域的相邻词汇、替换非实体词汇为同义词、随机删除非实体区域的停用词,这类操作不会改变实体的位置和标签,同时能提升样本多样性,避免过拟合。
- 加权过采样:训练阶段对包含少样本标签的样本设置更高的采样权重,你可以直接在spaCy的训练配置中为少样本子集单独设置更高的训练轮次,也可以在自定义语料读取逻辑中重复加载少样本,注意要搭配前面的扰动操作后再重复采样,不要直接使用原始样本重复训练。
二、自定义损失函数实现
spaCy v3及以上版本支持通过注册自定义损失函数的方式适配不平衡场景,推荐使用加权交叉熵损失或者Focal Loss,前者按标签样本量的反比设置权重,后者可以进一步降低多数类易分样本的权重,让模型聚焦少样本的学习。
第一步:注册自定义损失函数
import spacy import torch import torch.nn.functional as F from spacy.training import Example from thinc.api import Ops @spacy.registry.losses("WeightedFocalNERLoss.v1") def create_weighted_focal_loss(class_weights: list[float], gamma: float = 2.0): def loss_func( ops: Ops, guesses: torch.Tensor, examples: list[Example], scores: torch.Tensor, ) -> tuple[torch.Tensor, torch.Tensor]: # 将类别权重转换为对应运算设备的张量 weight_tensor = torch.tensor(class_weights, device=guesses.device) # 计算基础交叉熵损失 ce_loss = F.cross_entropy(guesses, scores, weight=weight_tensor, reduction="none") # 计算Focal Loss的调制系数 p_t = torch.exp(-ce_loss) focal_loss = ((1 - p_t) ** gamma * ce_loss).mean() # 反向传播计算梯度 d_loss = ops.backprop(focal_loss, guesses) return focal_loss, d_loss return loss_func
第二步:修改训练配置文件引用自定义损失
在你的spaCy训练config文件中,修改NER损失的配置项,示例如下:
[training.loss.ner] @losses = "WeightedFocalNERLoss.v1" # 权重按标签样本量反比计算,示例中标签a样本量45000,标签b4000,权重比约为1:11.25,按你的标签顺序调整即可 class_weights = [1.0, 11.25] # gamma设为0时为普通加权交叉熵,设为2是常用的Focal Loss参数 gamma = 2.0
注意事项
- 类别权重不要设置过高,否则会导致模型过度偏向少样本标签,出现大量假阳性预测,建议先按样本反比设置初始值,再根据验证集少样本标签的F1值微调。
- 如果你使用的是BIO标注体系,要给B-前缀和I-前缀的同实体标签设置相同的权重,避免同实体的不同标注位权重不一致。
- 训练时建议开启早停策略,监控验证集上少样本标签的F1值,避免模型过拟合多数类标签。
内容的提问来源于stack exchange,提问作者sampath kumaran
相关产品推荐
相关产品推荐

