Transformers实战——使用本地数据进行AclImdb情感分类

本文使用 https://huggingface.co/models 中的bert-base-uncased预训练模型和aclImdb数据集进行实战。

部分库版本

  1. transformers==4.17.0
  2. torch==1.11.0
  3. datasets==2.0.0

数据介绍

AclImdb – v1 Dataset 是用于二进制情绪分类的大型电影评论数据集,其涵盖比基准数据集更多的数据,其中有 25,000 条电影评论用于训练,25,000 条用于测试,还有其他未经标记的数据可供使用。

本次只使用里面的训练数据和测试数据,使用的结构如下:

|- test
|-- neg
|-- pos
|- train
|-- neg
|-- pos

数据预处理

要想使用Trainer进行训练,需要将数据调整到一定的规范,以下展示使用预训练模型对应的文本标记器(tokenizer)和datasets库处理原始数据。

导入必要库

from datasets import Dataset
from transformers import BertTokenizer
import os

载入AclImdb数据,并转化为datasets.Dataset

# 载入原始数据
def load_data(base_path):
    paths = os.listdir(base_path)
    result = []
    for path in paths:
        with open(os.path.join(base_path, path), 'r', encoding='utf-8') as f:
            result.append(f.readline())
    return result

# 读入数据并转化为datasets.Dataset
def get_dataset(base_path):
		# 为了展示方便,这里只取前3个数据,真实使用需要删掉切片操作
    pos_data = load_data(os.path.join(base_path, 'pos'))[:3]
    neg_data = load_data(os.path.join(base_path, 'neg'))[:3]
    
		# 列表合并
    texts = pos_data + neg_data
		# 生成标签,其中使用 '1.' 和 '0.' 是因为需要转化为浮点数,要不然模型训练时会报错
    labels = [[1., 0.]]*len(pos_data) + [[0., 1.]] * len(neg_data)
    dataset = Dataset.from_dict({'texts':texts, 'labels':labels})
    return dataset

# 加载数据
train_dataset = get_dataset('../data/aclImdb/train/')
test_dataset = get_dataset('../data/aclImdb/test/')

此时输出train_dataset可以查看其结构:

print(train_dataset)

输出结果
Dataset({
    features: ['texts', 'labels'],
    num_rows: 6
})

可以看到,包含了textslabels,有6行数据。

通过字典的方式可以查看其中的元素:

print(train_dataset['labels'])

输出结果:
[[1.0, 0.0], [1.0, 0.0], [1.0, 0.0], [0.0, 1.0], [0.0, 1.0], [0.0, 1.0]]

通过.features可以查看特征:

print(train_dataset.features)

输出结果:
{
'texts': Value(dtype='string', id=None), 
'labels': Sequence(feature=Value(dtype='float64', id=None), length=-1, id=None)
}

载入文本标记器

# cache_dir是预训练模型的地址
cache_dir="../transformersModels/bert-base-uncased/"
tokenizer = BertTokenizer.from_pretrained(cache_dir)
  • 注意

    这个路径的模型要自己下载,不能是transformer包下的,要不会报错。

将数据转化为模型可以接受的格式

# 设置最大长度
MAX_LENGTH = 512

# 使用文本标记器对texts进行编码
train_dataset = train_dataset.map(lambda e: tokenizer(e['texts'], truncation=True, padding='max_length', max_length=MAX_LENGTH), batched=True)
test_dataset = test_dataset.map(lambda e: tokenizer(e['texts'], truncation=True, padding='max_length', max_length=MAX_LENGTH), batched=True)

运行完后,数据集会包含训练模型所需要的所有参数:

print(print(train_dataset.features))

输出结果:
{
'texts': Value(dtype='string', id=None),
 'labels': Sequence(feature=Value(dtype='float64', id=None), length=-1, id=None),
 'input_ids': Sequence(feature=Value(dtype='int32', id=None), length=-1, id=None),
 'token_type_ids': Sequence(feature=Value(dtype='int8', id=None), length=-1, id=None),
 'attention_mask': Sequence(feature=Value(dtype='int8', id=None), length=-1, id=None)
}

保存处理好的数据到本地

在数据量大的时候,处理数据需要很长的时间,为了不每次都重新处理数据,可以将数据先存到本地

train_dataset.save_to_disk('./data/train_dataset')
test_dataset.save_to_disk('./data/test_dataset')

训练模型

导入必要库

from transformers import BertForSequenceClassification, BertTokenizer, Trainer, TrainingArguments, BertConfig
import torch
from datasets import Dataset
import json
import os
# 设定使用的GPU编号,也可以不设置,但trainer会默认使用多GPU
os.environ["CUDA_VISIBLE_DEVICES"] = "1"

载入网络并设置自定义尾部

# 将num_labels设置为2,因为我们训练的任务为2分类
model = BertForSequenceClassification.from_pretrained('../transformersModels/bert-base-uncased/', num_labels=2)

加载处理好的数据

train_dataset = Dataset.load_from_disk('./data/train_dataset/')
test_dataset = Dataset.load_from_disk('./data/test_dataset/')

冻结BERT参数

for param in model.base_model.parameters():
    param.requires_grad = False

因为BERT是预训练模型,因此可以不再进行权重更新,只对尾部的分类器进行优化。与此同时,这个设置也会减少训练时使用的时间和显存。

创建trainer

# 训练超参配置
training_args = TrainingArguments(
    output_dir='./my_results',          # output directory 结果输出地址
    num_train_epochs=10,              # total # of training epochs 训练总批次
    per_device_train_batch_size=128,  # batch size per device during training 训练批大小
    per_device_eval_batch_size=128,   # batch size for evaluation 评估批大小
    logging_dir='./my_logs',            # directory for storing logs 日志存储位置
)

# 创建Trainer
trainer = Trainer(
    model=model.to('cuda'),              # the instantiated 🤗 Transformers model to be trained 需要训练的模型
    args=training_args,                  # training arguments, defined above 训练参数
    train_dataset=train_dataset,         # training dataset 训练集
    eval_dataset=test_dataset,           # evaluation dataset 测试集
)

训练、评估和保存模型

# 开始训练
trainer.train()

# 开始评估模型
trainer.evaluate()

# 保存模型 会保存到配置的output_dir处
trainer.save_model()

保存模型会生成三个文件:

# 模型配置文件
config.json

# 模型数据文件
pytorch-model.bin

# 训练配置文件
training_args.bin

查看日志

使用tensorboard查看配置的logging_dir即可看到训练时的日志。

加载模型

output_config_file = './my_results/config.json'
output_model_file = './my_results/pytorch_model.bin'

config = BertConfig.from_json_file(output_config_file)
model = BertForSequenceClassification(config)
state_dict = torch.load(output_model_file)
model.load_state_dict(state_dict)

载入成功后会显示:

<All keys matched successfully>

这个model就可以跟普通的pytorch模型一样使用了

如下:

cache_dir="../transformersModels/bert-base-uncased/"
tokenizer = BertTokenizer.from_pretrained(cache_dir)
data = tokenizer(['This is a good movie', 'This is a bad movie'], max_length=512, truncation=True, padding='max_length', return_tensors="pt")
print(model(**data))

输出结果:
SequenceClassifierOutput(
loss=None, logits=tensor([
[-0.2951,  0.5463],
[-0.4638,  0.6353]], 
grad_fn=<AddmmBackward0>), 
hidden_states=None, 
attentions=None)

由于只用3条数据训练了10轮,因此结果很差,正常训练结果可以变好了。

完整代码

处理数据

from datasets import Dataset
from transformers import BertTokenizer
import os

def load_data(base_path):
    paths = os.listdir(base_path)
    result = []
    for path in paths:
        with open(os.path.join(base_path, path), 'r', encoding='utf-8') as f:
            result.append(f.readline())
    return result

def get_dataset(base_path):
    pos_data = load_data(os.path.join(base_path, 'pos'))
    neg_data = load_data(os.path.join(base_path, 'neg'))
    
    texts = pos_data + neg_data
    labels = [[1., 0.]]*len(pos_data) + [[0., 1.]] * len(neg_data)
    dataset = Dataset.from_dict({'texts':texts, 'labels':labels})
    return dataset

# 加载数据

train_dataset = get_dataset('../data/aclImdb/train/')
test_dataset = get_dataset('../data/aclImdb/test/')

# 处理数据
cache_dir="../transformersModels/bert-base-uncased/"
tokenizer = BertTokenizer.from_pretrained(cache_dir)

MAX_LENGTH = 512
train_dataset = train_dataset.map(lambda e: tokenizer(e['texts'], truncation=True, padding='max_length', max_length=MAX_LENGTH), batched=True)
test_dataset = test_dataset.map(lambda e: tokenizer(e['texts'], truncation=True, padding='max_length', max_length=MAX_LENGTH), batched=True)

# 保存数据
train_dataset.save_to_disk('./data/train_dataset')
test_dataset.save_to_disk('./data/test_dataset')

训练模型

from transformers import BertForSequenceClassification, BertTokenizer, Trainer, TrainingArguments, BertConfig
import torch
from datasets import Dataset
import json
import os
os.environ["CUDA_VISIBLE_DEVICES"] = "1"

# 加载模型
model = BertForSequenceClassification.from_pretrained('../transformersModels/bert-base-uncased/', num_labels=2)

# 加载数据
train_dataset = Dataset.load_from_disk('./data/train_dataset/')
test_dataset = Dataset.load_from_disk('./data/test_dataset/')

# 冻结bert参数
for param in model.base_model.parameters():
    param.requires_grad = False

# 创建trianer
training_args = TrainingArguments(
    output_dir='./my_results',          # output directory 结果输出地址
    num_train_epochs=10,              # total # of training epochs 训练总批次
    per_device_train_batch_size=128,  # batch size per device during training 训练批大小
    per_device_eval_batch_size=128,   # batch size for evaluation 评估批大小
    logging_dir='./my_logs',            # directory for storing logs 日志存储位置
)

trainer = Trainer(
    model=model.to('cuda'),              # the instantiated 🤗 Transformers model to be trained 需要训练的模型
    args=training_args,                  # training arguments, defined above 训练参数
    train_dataset=train_dataset,         # training dataset 训练集
    eval_dataset=test_dataset,           # evaluation dataset 测试集
)

# 训练和保存
trainer.train()
trainer.save_model()

载入模型

output_config_file = './my_results/config.json'
output_model_file = './my_results/pytorch_model.bin'

config = BertConfig.from_json_file(output_config_file)
model = BertForSequenceClassification(config)
state_dict = torch.load(output_model_file)
model.load_state_dict(state_dict)
  • 8
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
嗨!对于BERT情感分类实战,您可以按照以下步骤进行: 1. 数据准备: - 收集和整理情感分类的训练数据集,一般包含文本和对应的情感类别(如正面、负面、中性等)。 - 划分数据集为训练集和测试集,确保数据集的均衡性和随机性。 2. BERT模型介绍: - BERT(Bidirectional Encoder Representations from Transformers)是一种预训练的自然语言处理模型,通过双向Transformer编码器学习语义表示。 - 可以选择使用基于TensorFlow或PyTorch实现的BERT模型,或者直接使用已经训练好的BERT模型进行微调。 3. 模型微调: - 使用训练集对BERT模型进行微调,即在预训练的BERT模型基础上,通过训练集进行进一步的学习。 - 这里的微调过程包括输入数据的预处理、构建分类任务的模型结构、定义损失函数和优化算法等。 4. 模型评估: - 使用测试集对训练好的BERT模型进行评估,计算分类准确率、精确率、召回率等指标,评估模型在情感分类任务上的性能。 5. 预测与应用: - 使用训练好的BERT模型对新的文本进行情感分类预测,得到情感类别的预测结果。 - 可以将该模型应用于各种情感分析任务,如舆情监测、评论情感分析等。 以上是BERT情感分类实战的一般流程,您可以根据具体需求和数据特点进行相应调整和优化。希望能对您有所帮助!如有更多问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值