首页 > 其他分享 >多标签文本分类

多标签文本分类

时间:2022-10-18 17:34:55浏览次数:75  
标签:labels ln 标签 分类 batch model 文本 data

多标签文本分类是指一个输入文本样本对应有多种标签。本文是一个训练多标签文本分类任务的实例:

训练过程

main.py

导入相关的库:

# coding=utf-8
import os
# os.environ["CUDA_VISIBLE_DEVICES"] = "2"
os.environ["WANDB_DISABLED"] = "true"  # 禁用wandb
'''
from huggingface_hub import snapshot_download
# 将预训练模型下载到指定目录(cache_dir参数指定保存目录)
snapshot_download(repo_id='bert-base-chinese', cache_dir='.')
'''

import json
import torch
from sklearn.preprocessing import MultiLabelBinarizer
from transformers import AutoModelForSequenceClassification, AutoTokenizer, DataCollatorWithPadding, TrainingArguments
from custom_trainer import CustomTrainer
from datasets import load_dataset

tokenizer及数据加载:

tokenizer = AutoTokenizer.from_pretrained('models--bert-base-chinese')
data = load_dataset("json", data_files='preprocessed_data/labeled_data.json')

利用MultiLabelBinarizer处理标签,利用MultiLabelBinarizer处理标签的好处是:在后续利用MultiLabelBinarizer相关方法向模型传入label时,模型会自动识别为多标签模型(在模型的全连接层使用sigmoid函数而不是softmax函数)

label_encoder = MultiLabelBinarizer()
label_encoder = label_encoder.fit(data['train']['labels'])

保存一个标签ID到标签名的映射文件,便于后续得到测试文本对应的标签名

save_batch = 64  # batch size
epochs = 100     # epoch num
if not os.path.exists(f"model_save_epochs{epochs}_batch{save_batch}"):
    os.mkdir(f"model_save_epochs{epochs}_batch{save_batch}")
    
with open(f"model_save_epochs{epochs}_batch{save_batch}/labelmap.json", 'w', encoding='utf-8') as fw:
    json.dump({'LABEL_' + str(ind): label_name for ind, label_name in enumerate(label_encoder.classes_)}, fw,
              ensure_ascii=False, indent=1)

将加载到的数据集分割成训练集和验证集

data = data['train'].train_test_split(test_size=0.1, shuffle=True)

构造模型的输入输出数据

def preprocess_function(examples):
    model_inputs = tokenizer(examples['text'], max_length=300, truncation=True) 
    labels = [labels for labels in examples['labels']]
    model_inputs['labels'] = label_encoder.transform(labels).astype(float)  # 将标签名转换成标签id
    return model_inputs


data = data.map(preprocess_function, batched=True, batch_size=100)

加载预训练模型

model = AutoModelForSequenceClassification.from_pretrained('models--bert-base-chinese',
                                                           num_labels=label_encoder.classes_.size, # 标签类别数
                                                           # problem_type 指明模型训练类型为多标签文本分类,但在实际测试时发现:
                                                           # 即使不设置此参数,模型也仍使用的是多标签文本分类方式训练
                                                           # 可能是因为前面用MultiLabelBinarizer处理标签后,模型识别出要训练多标签文本分类
                                                           problem_type='multi_label_classification'
                                                           )

使用huggingface/Datasets方式加载数据时,可以用DataCollator达到批处理的效果。这里可能会有读者好奇,数据并没有做填充处理,后面如何以batch输入模型进行训练。事实上,在tokenizer时做填充处理是没有问题的。但是如果在这个阶段中做填充,会将所有数据的长度都填充到300,如果训练时一个batch中的数据中都是短文本,那数据中将有大量的填充值,影响计算效率。该如何解决这一问题呢?

我们可以让其在取出一个batch的数据之后再根据batch内数据的最大长度进行填充,例如用Dataloader加载数据,我们可以指定collate_fn,但是这个函数需要我们自行实现。在transformers中,我们则可以使用DataCollatorWithPadding,实例化该类并在Trainer中指定即可。

data_collator = DataCollatorWithPadding(tokenizer=tokenizer)

获取模型训练参数:

args = TrainingArguments(
    learning_rate=2e-5,
    per_device_train_batch_size=save_batch,
    per_device_eval_batch_size=128,
    num_train_epochs=epochs,
    weight_decay=0.01,
    output_dir=f"model_save_epochs{epochs}_batch{save_batch}",
    logging_steps=10,
    evaluation_strategy="epoch",
    save_strategy="epoch",
    save_total_limit=3,
    load_best_model_at_end=True,
    metric_for_best_model="eval_loss",
    fp16=True,
    disable_tqdm=False,
)

自定义验证

def compute_metrics(eval_pred):
    predictions, labels = eval_pred
    # BCEWithLogitsLoss 用于计算多标签损失,后文将具体介绍
    loss = torch.nn.BCEWithLogitsLoss()(torch.Tensor(predictions), torch.tensor(labels))
    return {'eval_loss': loss.cpu().numpy()}

进行训练:

 # CustomTrainer中重写了compute_loss()函数,目的是检验模型训练过程中的损失计算结果是否与BCEWithLogitsLoss计算结果一致
 # 经测试,结果是一致的,大家可以直接使用Trainer进行训练即可
trainer = CustomTrainer( 
  model,
  args=args,
  train_dataset=data["train"],
  eval_dataset=data["test"],
  tokenizer=tokenizer,
  compute_metrics=compute_metrics,
  data_collator=DataCollatorWithPadding(tokenizer=tokenizer)
)

trainer.train()

custom_trainer.py

from transformers import Trainer
import torch
class CustomTrainer(Trainer):
    def compute_loss(self, model, inputs, return_outputs=False):
        """
        How the loss is computed by Trainer. By default, all models return the loss in the first element.

        Subclass and override for custom behavior.
        """
        labels = inputs.get('labels')
        outputs = model(**inputs)
        logits = outputs.get('logits')
        loss = torch.nn.BCEWithLogitsLoss()(logits, labels)
        return (loss, outputs) if return_outputs else loss

Pytorch之BCELoss和BCEWithLogitsLoss

BCELoss是
$$
-\frac{1}{n}\sum (y_{n} \times ln X_{n} + (1-y_{n})\times ln(1-X_{n}))
$$
其中y是标签(target),x是模型输出的值(logits)。

所以对于给定

x = [[0.3992 , 0.2232, 0.6435 ],
	[0.3800 , 0.3044, 0.3241], 
	[0.6281, 0.4689, 0.3834]]
	
y = [[0, 1, 1],
    [0, 0, 1], 
    [1, 0, 1]]

所以其计算过程

第一行:
第一列 0 × ln ⁡ 0.3992 + ( 1 − 0 ) × ln ⁡ ( 1 − 0.3992 ) = − 0.5095
第二列 1 × ln ⁡ 0.2232 + ( 1 − 1 ) × ln ⁡ ( 1 − 0.2232 ) = − 1.4997
第三列 1 × ln ⁡ 0.6435 + ( 1 − 1 ) × ln ⁡ ( 1 − 0.6435 ) = − 0.4408
第二行:
第一列 0 × ln ⁡ 0.3800 + ( 1 − 0 ) × ln ⁡ ( 1 − 0.3800 ) = − 0.4780
第二列 0 × ln ⁡ 0.3044 + ( 1 − 0 ) × ln ⁡ ( 1 − 0.3044 ) = − 0.3630
第三列 1 × ln ⁡ 0.3241 + ( 1 − 1 ) × ln ⁡ ( 1 − 0.3241 ) = − 1.1267
第三行:
第一列 1 × ln ⁡ 0.6281 + ( 1 − 1 ) × ln ⁡ ( 1 − 0.6281 ) = − 0.4651
第二列 0 × ln ⁡ 0.4689 + ( 1 − 0 ) × ln ⁡ ( 1 − 0.4689 ) = − 0.6328
第三列 1 × ln ⁡ 0.3834 + ( 1 − 1 ) × ln ⁡ ( 1 − 0.3834 ) = − 0.9587

$$
-\frac{-0.5095 - 1.4997 - 0.4408}{3} = 0.8167
$$
$$
-\frac{-0.4780 - 0.3630 - 1.1267 }{3} = 0.6559
$$

$$
-\frac{-0.4651 - 0.6328 - 0.9587}{3} = 0.6855
$$

故而BCE损失为:
$$
BCELoss \approx \frac{-0.8167 - 0.6559- 0.9587}{3} = 0.7194
$$

利用BCELoss代码实现如下:

from torch import nn
import torch
m = nn.Sigmoid()
loss = nn.BCELoss()
logits = torch.Tensor([[-0.4089, -1.2471, 0.5907], 
                       [-0.4897, -0.8267, -0.7349], 
                       [0.5241, -0.1246, -0.4751]])
target = torch.Tensor([[0, 1, 1], 
                       [0, 0, 1], 
                       [1, 0, 1]])
pre = m(logits)
output = loss(pre, target)
print(output)

# 输出为:0.7193

利用BCEWithLogitsLoss实现:

print(nn.BCEWithLogitsLoss()(logits, target))
# 输出为:0.7193

BCEWithLogitsLoss() 中包含了sigmoid过程

BCELoss参考链接:https://blog.csdn.net/qq_22210253/article/details/85222093

标签:labels,ln,标签,分类,batch,model,文本,data
From: https://www.cnblogs.com/teanon/p/16803324.html

相关文章

  • 用心维护自己的职场标签
    前几天有个朋友推了某乎上的一个问题,问我怎么看,题目是“有些测试人员BUG产出较少,复制其他组员N个版本以前的BUG进行提交,这种行为你怎么看? 怎么处理?”看完了我不知道该说什......
  • antdv 实现表格中多个标签 分开显示
    如下图,实现效果:  {title:'商品标签',dataIndex:'labelnames',width:260,customRender:({record})=>{constlabelnames=recor......
  • jq 点击文本复制
    <atitle="<?=$u['did'];?>"id="article"><divclass="td-text"id="copy-text"><?=$u['did'];?></div></a>$(document).on('click','#art......
  • 中文文本分类实例
    中文文本分类【参考https://github.com/lijqhs/text-classification-cn】文本分类(TextClassification)是自然语言处理中的一个重要应用技术,根据文档的内容或主题,自动识别......
  • 10. CSS文本格式化
    1.前言通过CSS中的文本属性您可以像操作Word文档那样定义网页中文本的字符间距、对齐方式、缩进等等,CSS中常用的文本属性如下所示:text-align:设置文本的水平对齐方......
  • 实战 | OpenCV实现扫描文本矫正应用与实现详解(附源码)
    导读本文主要介绍使用OpenCV对扫描文本矫正的应用实例及详细实现步骤。背景介绍在使用打印机或扫描仪扫描文档时,由于摆放位置差异难免造成扫描文档的倾斜。本文将使用Open......
  • 一文带你了解软件测试分类——概念篇
    前言测试小伙伴在谈论软件测试分类,五花八门的分类,眼花缭乱。因为将各个维度划分的内容都整到一块了,在加上各自不同的见解与补充,各种冲突......经过多年测试总结基本定为4类......
  • tinymce富文本编辑器的简单使用及OSS直传
    1.安装tinymce依赖我的是vue2项目,指定版本"@tinymce/tinymce-vue":"^2.0.0","tinymce":"^5.0.3",npminstall@tinymce/[email protected]......
  • 音频转文本
    1.背景最近被朋友推荐播客,只有音频,没有文本,不太适应篇幅长,耗时高不同嘉宾声音截然不同因此想将音频转换为文本,快速阅读。2.体验2.1准备电脑上下载了音乐林俊杰......
  • Linux基础_7_文本显示
    注:实质是针对标准输出文本的各种骚操作!简单查看注:初略加工后进行显示。cat-n文件名#查看文件内容并显示行号tac文件名#逆序查看more文件名less......