【一天一个NLP任务】(Day 1)——BERT解决中文情绪分类任务

网友投稿 305 2022-11-16

【一天一个NLP任务】(Day 1)——BERT解决中文情绪分类任务

引言

本文我们来使用BERT进行单文本分类(Single Sentence Classification,SSC),对输入的文本分成不同的类别。

类似英文glue/sst2数据集,我们今天实现的是对中文情感分类数据集进行分类。

数据集

情感分类数据集是网上开源的数据​

但是无法直接通过​​datasets​​去加载,有些记录有问题,博主已经处理好了。

回复“SSC”即可。

安装所需的包

pip install transformers datasets

导入模块

import numpy as npfrom datasets import load_dataset, load_metricfrom transformers import BertTokenizerFast, BertForSequenceClassification, TrainingArguments,

加载数据集

把处理好的​​train.csv​​​、​​test.csv​​​和​​dev.csv​​文件放入我们的数据集目录,然后执行加载操作:

# 加载数据集base_url = './datasets/ssc/'raw_datasets = load_dataset('csv', data_files={'train': base_url + 'train.csv', 'test': base_url + 'test.csv', 'dev': base_url + 'dev.csv'})

加载分词器

tokenizer = BertTokenizerFast.from_pretrained('hfl/chinese-bert-wwm')

使用的是哈工大开源的模型

加载预训练模型

model = BertForSequenceClassification.from_pretrained('hfl/chinese-bert-wwm', return_dict=True)

加载评价指标

metric = load_metric('glue','sst2')

处理数据集

对数据集中的文本进行分词

def tokenize_function(examples): return tokenizer(examples['text_a'], truncation=True, padding='max_length', max_length=512)tokenized_datasets = raw_datasets.map(tokenize_function, batched=True)

我们打印一下处理好的数据集看看:

tokenized_datasets

DatasetDict({ train: Dataset({ features: ['attention_mask', 'input_ids', 'label', 'text_a', 'token_type_ids'], num_rows: 9146 }) test: Dataset({ features: ['attention_mask', 'input_ids', 'label', 'text_a', 'token_type_ids'], num_rows: 1200 }) dev: Dataset({ features: ['attention_mask', 'input_ids', 'label', 'text_a', 'token_type_ids'], num_rows: 1200 })})

其中​​text_a​​​已经不需要了,并且​​label​​​需要改成​​labels​​:

tokenized_datasets = tokenized_datasets.remove_columns( ["text_a"])tokenized_datasets.rename_column('label', 'labels')

DatasetDict({ train: Dataset({ features: ['attention_mask', 'input_ids', 'labels', 'token_type_ids'], num_rows: 9146 }) test: Dataset({ features: ['attention_mask', 'input_ids', 'labels', 'token_type_ids'], num_rows: 1200 }) dev: Dataset({ features: ['attention_mask', 'input_ids', 'labels', 'token_type_ids'], num_rows: 1200 })})

定义评价指标

def compute_metrics(eval_preds): predictions, labels = eval_preds return metric.compute(predictions=np.argmax(predictions, axis=1), references=labels)

定义训练参数

args = TrainingArguments( './saved/', # 保存路径,存放检查点和其他输出文件 evaluation_strategy='epoch', # 每轮结束后进行评价 learning_rate=2e-5, # 初始学习率 per_device_train_batch_size=8, # 训练批次大小 per_device_eval_batch_size=8, # 测试批次大小 num_train_epochs=3, # 训练轮数)

默认使用AdamW优化器。

定义训练器

trainer = Trainer( model, args, train_dataset=tokenized_datasets['train'], eval_dataset=tokenized_datasets["dev"], tokenizer=tokenizer, compute_metrics=compute_metrics)

开始训练

trainer.train()

训练了将近半个小时,3轮完了之后在验证集上的准确率为:

然后我们在测试集上进行测试:

trainer.evaluate(eval_dataset=tokenized_datasets['test'])

{'epoch': 3.0, 'eval_accuracy': 0.9466666666666667, 'eval_loss': 0.2731056809425354, 'eval_runtime': 23.3378, 'eval_samples_per_second': 51.419, 'eval_steps_per_second': 6.427}

准确率还挺高,但是光看准确率有时还不够,更常见的方式是同时加上F1 Score和召回率等。

测试

from sklearn.metrics import accuracy_score, precision_recall_fscore_support# 重新定义评价指标def compute_metrics(eval_preds): logits, labels = eval_preds predictions = np.argmax(logits, axis=-1) precision, recall, f1, _ = precision_recall_fscore_support(labels, predictions, average='binary') acc = accuracy_score(labels, predictions) result = { 'accuracy': acc, 'f1': f1, 'precision': precision, 'recall': recall } return result # 修改训练器的评价指标trainer.compute_metrics = compute_metrics# 在测试集上进行测试trainer.evaluate(eval_dataset=tokenized_datasets['test'])

{'epoch': 3.0, 'eval_accuracy': 0.9466666666666667, 'eval_f1': 0.9471947194719471, 'eval_loss': 0.2731056809425354, 'eval_precision': 0.9503311258278145, 'eval_recall': 0.944078947368421, 'eval_runtime': 23.2695, 'eval_samples_per_second': 51.57, 'eval_steps_per_second': 6.446}

嗯,其他指标都还可以,说明结果还行。那我们就自己随便输入点什么进行测试吧。

推理

本小节我们自定义一些数据,进行情绪分类。

texts = [ '垃圾游戏,毁我青春', '嗯,这游戏真香', '我感谢你全家', '哇,真好吃,妈妈的味道', '大家好,我叫马牛逼,你们知道我有多牛逼吗,我拉屎不擦屁股,你们敢吗']# 首先还是用分词器进行预处理encoded = tokenizer(texts, truncation=True, padding='max_length', max_length=512, return_tensors='pt')

然后需要设置设备类型。

import torchdevice = torch.device("cuda:0")encoded = {k: v.to(device) for k, v in encoded.items()}

得到模型输出的logits,并且计算Softmax:

out = model(**encoded)probs = out.logits.softmax(dim=-1)

tensor([[9.9923e-01, 7.6577e-04], [5.6161e-03, 9.9438e-01], [5.6499e-04, 9.9944e-01], [8.2916e-04, 9.9917e-01], [9.7360e-01, 2.6399e-02]], device='cuda:0', grad_fn=)

嗯,是输出5个句子属于各个类别的概率,但是它们的对应关系是啥呢?

很简单,可以直接打印出来:

model.config.label2id

{'LABEL_0': 0, 'LABEL_1': 1}

我们数据集中的​​0​​​表示消极,​​1​​​表示积极,默认​​transformers​​​会为我们增加一个​​LABEL_​​前缀,我们也可以自己配置这个映射关系。

probs.argmax(dim=-1)

tensor([0, 1, 1, 1, 0], device='cuda:0')

知道了映射关系后,除了第三句话没有分辨出来外(这个话是真的损,不带一个脏字),其他都判断正确。

Reference

​​是时候彻底弄懂BERT模型了​​​​是时候彻底弄懂BERT模型了​​​​微调BERT模型得到句子间的相似度​​

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:关于Spring Boot项目的 log4j2 核弹漏洞问题(一行代码配置搞定)
下一篇:基于AT45DB161B存储器和PIC16LC73B单片机实现微型压力测量装置设计
相关文章

 发表评论

暂时没有评论,来抢沙发吧~