概述
引言
本文我们来使用BERT进行单文本分类(Single Sentence Classification,SSC),对输入的文本分成不同的类别。
类似英文glue/sst2数据集,我们今天实现的是对中文情感分类数据集进行分类。
数据集
情感分类数据集是网上开源的数据集,下载地址→点此
但是无法直接通过datasets
去加载,有些记录有问题,博主已经处理好了。
需要处理好的数据集关注公众号:Hello丶Java
回复“SSC”即可。
安装所需的包
pip install transformers datasets
导入模块
import numpy as np
from datasets import load_dataset, load_metric
from transformers import BertTokenizerFast, BertForSequenceClassification, TrainingArguments, Trainer
加载数据集
把处理好的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 torch
device = 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)
probs
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=<SoftmaxBackward>)
嗯,是输出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模型得到句子间的相似度
最后
以上就是陶醉月光为你收集整理的【一天一个NLP任务】(Day 1)——BERT解决中文情绪分类任务引言数据集安装所需的包导入模块加载数据集加载分词器加载预训练模型加载评价指标处理数据集定义评价指标定义训练参数定义训练器开始训练测试推理Reference的全部内容,希望文章能够帮你解决【一天一个NLP任务】(Day 1)——BERT解决中文情绪分类任务引言数据集安装所需的包导入模块加载数据集加载分词器加载预训练模型加载评价指标处理数据集定义评价指标定义训练参数定义训练器开始训练测试推理Reference所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复