NLP标签不均衡的文本多分类

简介

自然语言处理中,文本多分类是最常见的需求之一。如果标注数据量大且样本均衡,任选一个bert模型都能达到非常好的准确度。但实际应用中往往面临的是数据量小,标签不均衡,标注错误等各种预想之外但又普遍存在的问题。如何根据实际情况解决问题,获得不错的效果才是我们需要研究的。

问题

现有一个多分类问题,供讨论研究可行的方法。一批标注数据有100个标签,标签为文本。标签不均衡问题非常严重,有的标签样本量数千条数据,有的标签样本量从个位数到数十个。如何使用现有数据训练模型,用于后续数据的标签预测。由于样本较少的类别本身很难达到较高的准确性,以下方法测试仅说明如何在给定数据条件下,找到一个相对较优的方法。示例数据如下:

句子 类别
This is the first test. label1
The description of ach sentence describe belong different subject. label2

方法

数据预处理

文本分类问题数据简单,统计标签类别和数目,根据实际需求尝试合并样本过于少的标签,清洗列表进行标准化。

1. 多分类模型

直接使用 simpletransformers中的多分类模型,标签数目设置为多分类的标签数目。以下为官方示例代码。

from simpletransformers.classification import ClassificationModel, ClassificationArgs
# 将标签类别 用数字标签代替
train_data = [
    ["Aragorn was the heir of Isildur", 1],
    ["Frodo was the heir of Isildur", 0],
    ["Pippin is stronger than Merry", 2],
]
train_df = pd.DataFrame(train_data)
train_df.columns = ["text", "labels"]
# 修改模型训练参数
model_args = ClassificationArgs(num_train_epochs=1)
# 初始化模型
model = ClassificationModel(
    'bert','bert-base-cased',
    num_labels=3, # 设置类别数目
    args=model_args)
# 训练模型
model.train_model(train_df)

经测试,此方法对本例中样本数目较少的类别准确度非常差。

2. 句子对分类

句子对分类本身适用于判断两个句子的关系是相似,蕴含还是相反。此处由于标签也是文本,可将分类问题转换认为是句子对的关系判断(虽然句子对一个是相对短的文本)。后续有新样本时,直接与已有的所有样本进行匹配,将得分最高的匹配样本的标签作为新样本的预测标签(与聚类相似),将分类问题转换为搜索问题,这样即使部分标签的训练数据非常少,仍然可以发挥作用。

(1)使用simpletransformers

train_data = [
  ["Aragorn was the heir of Isildur", "Gimli fought with a battle axe", 1,],
  ["Frodo was the heir of Isildur", "Legolas was an expert archer", 0,],
]
train_df = pd.DataFrame(train_data)
train_df.columns = ["text_a", "text_b", "labels"]
model_args = ClassificationArgs(num_train_epochs=1)
model = ClassificationModel("roberta", "roberta-base")
model.train_model(train_df)

对于我们的问题,将类别表示为”text_b“。构建正例使用配对的真实类别。构建负例,使用非配对的其他类别。每个正例可随机生成20个负例,构建训练集进行模型训练。总体准确率有明显提升。

(2)使用 Sentence-Transformer

sentence_transformers中提供了更多丰富的损失函数,能够更准确有效的训练模型,提升准确度。构建三元组训练数据,使用 BatchSemiHard 作为损失函数,训练模型,使得锚样本与正样本的距离远小于锚样本与负样本之间的距离。损失函数的原理和详细介绍可参考其他资料。
关于模型评价,由于本质是分类问题,使用BinaryClassificationEvaluator直接计算准确率 而不是EmbeddingSimilarityEvaluator计算的相关性。

model = SentenceTransformer(model_name)
train_loss = losses.BatchSemiHardTripletLoss(model=model)
num_epochs = 3
warmup_steps = math.ceil(len(train_dataloader) * num_epochs * 0.1)  # 10% of train data for warm-up
model.fit(train_objectives=[(train_dataloader, train_loss)],
          evaluator=dev_evaluator,
          epochs=num_epochs,
          evaluation_steps=int(len(train_dataloader) * 0.1),
          warmup_steps=warmup_steps,
          output_path=model_save_path,
          use_amp=True  ### Set to True, if your GPU supports FP16 operations
          )

预训练模型使用multilingual-mpnet-base-v2,经过微调训练,模型在测试数据上总体准确率为90.2%。

另外也尝试了其他损失函数,如 ProxyAnchorLoss ,此函数不是 sentence_transformers 中的损失函数,需要 在sentence_transformers安装包中存放损失函数的目录losses 中新建文件,封装该损失函数,之后可导入使用,使用方法与上述BatchSemiHardTripletLoss 相同。使用该损失函数训练模型,总体准确率提升1.1%至91.3%。

总结

  • simpletransformers的详细用法及更多功能可参考官方文档,提供了简洁的使用方法。
  • Sentence-Transformer则提供了更灵活的与语义,文本搜索相关的API,可根据类型,选择对应的损失函数等。损失函数的选择对模型的准确度影响非常大。
  • 针对标签不平衡的文本分类,通过转换为相似性搜索,可部分解决问题。此种三元组数据训练的模型,相比于常规分类,是更细粒度的分类,常用于人脸识别,身份鉴定等方面。当数据缺少的时候,也可以使用这种方法,效果需要更多实例才能判断。
  • 以上各种损失函数的详细差异以及与其他损失函数的比较还了解不足,需要进一步研究。

你可能感兴趣的:(NLP,自然语言处理,分类,机器学习)