tnblog
首页
视频
资源
登录

HugginFace 中文情感分类(学习笔记)

3336人阅读 2023/10/27 17:22 总访问:3511525 评论:0 收藏:0 手机
分类: HuggingFace

HugginFace 中文情感分类(学习笔记)

数据集介绍


本章使用的是lansinuote/ChnSentiCorp数据集,这是一个情感分类数据集,每条数据中包括一句购物评价和标识(好评或差评)。

模型架构


RNN的主要功能是能把自然语言的句子抽取成特征向量,有了特征向量之后入全连接神经网络做分类或者回归就水到渠成了。
RNN把一个自然语言处理的任务换成了全连接神经网络任务。
对于类似RNN能把抽象数据类型转换成具体的特征向量的网络层,被统称为backbone,中文一般译为特征抽取层。
本章的情感分类任务中也将使用BERT中文模型作为backbone层。
相对于backbone的网络,后续的处理神经网络被称为下游任务模型,它往往会对backbone输出的特征向量进行再计算,得到业务上需要的计算结果,这往往是分类或者回归的结果。整合backbone和下游任务模型的架构如下图所示。


网络的计算过程先把一句自然语言输入backbone网络中进行特征抽取,特征是一个向量,再把特征向量输入下游任务模型中进行计算,得出最终业务需要的结果。
对于应用了预训练的backbone的网络,训练时可以选择继续训练backbone层,也可以不训练backbone层,因为backbone的参数量往往非常巨大。如果要对backbone进行再训练,则往往会消耗掉更多的计算资源;如果不对backbone进行再训练二模型的性能已经达到业务需求,也可以选择节省这些计算资源,接下来将进行演示。

实现代码

环境设置

  1. %pip install torch torchvision torchaudio -f https://download.pytorch.org/whl/cpu/torch_stable.html
  2. %pip install -q transformers==4.18 datasets==2.4.0 torchtext

准备数据集

使用编码工具


加载编码工具,把文字转数字。

  1. from transformers import BertTokenizer
  2. token = BertTokenizer.from_pretrained('bert-base-chinese')
  3. token


这里加载的编码工具为bert-base-chinese,编码工具和预训练模型往往是成对使用的,后续啊讲使用同名的预训练模型作为backbone,运行结果如下:


从输出可以看出,bert-base-chinese模型中的字典有21128个,编码器编码句子的最大长度为512个词,并且能看到bert-base-chinese模型所使用的一些特殊符号。
加载编码工具之后,不妨进行一次试算,以便更清晰地观察到编码工具的输入和输出,代码如下:

  1. out = token.batch_encode_plus(
  2. batch_text_or_text_pairs=['从明天起,做一个幸福的人。', '喂马,劈柴,周游世界。'],
  3. truncation=True,
  4. padding='max_length',
  5. max_length=17,
  6. return_tensors='pt',
  7. return_length=True)
  8. #查看编码输出
  9. for k, v in out.items():
  10. print(k, v.shape)
  11. #把编码还原为句子
  12. print(token.decode(out['input_ids'][0]))


从上面的代码中的参数max_length=17的说明可以看出,超过长度的会被夹断。

定义数据集


我们将一个情感分类数据集进行模型的训练和测试,这里我们加载lansinuote/ChnSentiCorp数据集。

  1. import torch
  2. from datasets import load_dataset
  3. class Dataset(torch.utils.data.Dataset):
  4. def __init__(self,split):
  5. self.dataset = load_dataset('lansinuote/ChnSentiCorp')[split]
  6. def __len__(self):
  7. return len(self.dataset)
  8. def __getitem__(self, i):
  9. text = self.dataset[i]['text']
  10. label = self.dataset[i]['label']
  11. return text, label
  12. dataset = Dataset('train')
  13. len(dataset), dataset[20]


定义了一个Dataset类初始化时加载lansinuote/ChnSentiCorptrain训练数据集。
__len__获取长度。
__getitem__定义了每条数据,包括textlabel两个字段,最后初始化训练数据集,并查看训练集的长度和一条数据采样。
结果如下:


可见训练数据集包括9600条数据。

定义数据整理函数


再CUDA计算平台上进行计算比在CPU上要快。
但不一定准,我们可以通过如下代码来确认系统是否支持GPU。

  1. device = 'cpu'
  2. if torch.cuda.is_available():
  3. device = 'cuda'
  4. device


这里我使用的显卡是V100,所以输出的是GUDA

定义数据整理函数


接下来我们需要定义个数据整理函数,它具有批量编码一批文本数据的功能。
代码如下:

  1. def collate_fn(data):
  2. sents = [i[0] for i in data]
  3. labels = [i[1] for i in data]
  4. #编码
  5. data = token.batch_encode_plus(batch_text_or_text_pairs=sents,
  6. truncation=True,
  7. padding='max_length',
  8. max_length=500,
  9. return_tensors='pt',
  10. return_length=True)
  11. #input_ids:编码之后的数字
  12. #attention_mask:是补零的位置是0,其他位置是1
  13. input_ids = data['input_ids']
  14. attention_mask = data['attention_mask']
  15. token_type_ids = data['token_type_ids']
  16. labels = torch.LongTensor(labels)
  17. #把数据移动到计算设备上
  18. input_ids = input_ids.to(device)
  19. attention_mask = attention_mask.to(device)
  20. token_type_ids = token_type_ids.to(device)
  21. labels = labels.to(device)
  22. return input_ids, attention_mask, token_type_ids, labels


入参的data表示每一批数据,在参数中将编码后的结果指定为确定的500个词,超过500个词的句子将被截断,而不足500个词的句子将被补充PAD,直到500个词。
取出的编码也转换为PyTorchTensor格式。
接下来我们将模拟一组数据进行试算。

  1. #第7章/数据整理函数试算
  2. #模拟一批数据
  3. data = [
  4. ('你站在桥上看风景', 1),
  5. ('看风景的人在楼上看你', 0),
  6. ('明月装饰了你的窗子', 1),
  7. ('你装饰了别人的梦', 0),
  8. ]
  9. #试算
  10. input_ids, attention_mask, token_type_ids, labels = collate_fn(data)
  11. input_ids.shape, attention_mask.shape, token_type_ids.shape, labels


输出到结果4个句子每个500个词。

定义数据集加载器


数据集加载器可以将数据整理函数来成批地处理数据集中的数据,代码如下:

  1. loader = torch.utils.data.DataLoader(dataset=dataset,
  2. batch_size=16,
  3. collate_fn=collate_fn,
  4. shuffle=True,
  5. drop_last=True)
  6. len(loader)
参数 描述
dataset=dataset 表示要加载的数据集。
batch_size=16 表示每个批次中包括16条数据
collate_fn=collate_fn 表示要使用的数据整理函数,这里使用了之前定义好的的数据整理函数。
shuffle=True 表示打乱各个批次之间的顺序,让数据更加随机。
drop_last=True 表示当剩余的数据不足16条时,丢弃这些尾数。


运行的结果获取加载器一共有多少个批次,运行结果如下:


我们来进行一次数据样本的示例,代码如下:

  1. for i, (input_ids, attention_mask, token_type_ids,
  2. labels) in enumerate(loader):
  3. break
  4. input_ids.shape, attention_mask.shape, token_type_ids.shape, labels

定义模型

加载预训练模型


完成以上准备工作,现在数据数据的机构已经准备好,可以输入模型进行计算了,即可加载预训练了,代码如下:

  1. from transformers import BertModel
  2. pretrained = BertModel.from_pretrained('bert-base-chinese')
  3. #统计参数量
  4. sum(i.numel() for i in pretrained.parameters()) / 10000


此处加载的模型为bert-base-chinese模型,和编码工具的名字一致,注意模型和其编码工具往往配套使用。
模型的参数量,运行结果如下:


可见bert-base-chinese模型的参数量约为1亿个。
由于bert-base-chinese的体量比较大所以我们不需要对于本次的二分类任务进行训练。
代码如下:

  1. # 不训练预训练模型,不需要计算梯度
  2. for param in pretrained.parameters():
  3. param.requires_grad_(False)


定义好预训练模型之后,可以进行一次试算,观察模型的输入和输出,代码如下:

  1. #设定计算设备
  2. pretrained.to(device)
  3. #模型试算
  4. out = pretrained(input_ids=input_ids,
  5. attention_mask=attention_mask,
  6. token_type_ids=token_type_ids)
  7. out.last_hidden_state.shape


在这段代码中,首先把预训练模型移动到计算设备上,如果模型和数据不在同一个设备上,则无法计算。
之后把之前得到的样例数据输入预训练模型中,得到的计算结果为一个BaseModelOutputWithPoolingAndCrossAttentions对象,其中包括last_hidden_statepooler_output两个字段,此处只关心last_hidden_state字段,取出该字段并输出其形状,运行结果如下:


16句话,每句话包括500个词,每个词被抽象成768维的向量。
接着我们接入下游任务模型做分类或者回归任务。

定义下游任务类型


下游任务模型的任务是对backbone抽取的特征进行进一步计算,得到符合业务需求的计算结果。
代码如下:

  1. #第7章/定义下游任务模型
  2. class Model(torch.nn.Module):
  3. def __init__(self):
  4. super().__init__()
  5. self.fc = torch.nn.Linear(in_features=768, out_features=2)
  6. def forward(self, input_ids, attention_mask, token_type_ids):
  7. #使用预训练模型抽取数据特征
  8. with torch.no_grad():
  9. out = pretrained(input_ids=input_ids,
  10. attention_mask=attention_mask,
  11. token_type_ids=token_type_ids)
  12. #对抽取的特征只取第一个字的结果做分类即可
  13. out = self.fc(out.last_hidden_state[:, 0])
  14. out = out.softmax(dim=1)
  15. return out
  16. model = Model()
  17. #设定计算设备
  18. model.to(device)
  19. #试算
  20. model(input_ids=input_ids,
  21. attention_mask=attention_mask,
  22. token_type_ids=token_type_ids).shape


在这段代码中,定义了下游任务模型,该模型只包括一个全连接的神经网络,权重矩阵768x2,所以它能够把一个768维度的向量转换到二维空间中。
下游任务模型的计算过程为,获取了一批数据之后,使用backbone将这批数据抽取成特征矩阵,抽取的特征矩阵的形状应该是16x500x768,这在之前预训练模型的试算中已经看到。这三个维度分别代表了16句话、500个词、768维度的特征向量。
之后下游任务模型丢弃了499个词的特征,只取得第一个词(索引为0)的特征向量,对应编码结果中的[CLS],把特征向量矩阵变成了16x768。相当于把每句话变成了一个768维度的向量。

注意:之所以只取了第0个词的特征做后续的判断计算,这和预训练模型BERT的训练方法有关系.


之后再使用自己的全连接线性神经网络把16x768特征矩阵转换到16x2,即为要求的分类结果。
运行结果如下:


这是16句话的二分类结果。

训练预测

训练

  1. from transformers import AdamW
  2. from transformers.optimization import get_scheduler
  3. def train():
  4. #定义优化器
  5. optimizer = AdamW(model.parameters(), lr=5e-4)
  6. #定义loss函数
  7. criterion = torch.nn.CrossEntropyLoss()
  8. #定义学习率调节器
  9. scheduler = get_scheduler(name='linear',
  10. num_warmup_steps=0,
  11. num_training_steps=len(loader),
  12. optimizer=optimizer)
  13. #模型切换到训练模式
  14. model.train()
  15. #按批次遍历训练集中的数据
  16. for i, (input_ids, attention_mask, token_type_ids,
  17. labels) in enumerate(loader):
  18. #模型计算
  19. out = model(input_ids=input_ids,
  20. attention_mask=attention_mask,
  21. token_type_ids=token_type_ids)
  22. #计算loss并使用梯度下降法优化模型参数
  23. loss = criterion(out, labels)
  24. loss.backward()
  25. optimizer.step()
  26. scheduler.step()
  27. optimizer.zero_grad()
  28. #输出各项数据的情况,便于观察
  29. if i % 10 == 0:
  30. out = out.argmax(dim=1)
  31. accuracy = (out == labels).sum().item() / len(labels)
  32. lr = optimizer.state_dict()['param_groups'][0]['lr']
  33. print(i, loss.item(), lr, accuracy)
  34. train()


首先定于了优化器、loss计算函数、学习率调节器,其中优化器使用了HuggingFace提供的AdamW优化,这是传统的Adam优化器改进版本,在自然语言处理任务中,该优化器往往能取得比Adam优化器更好的成绩,并且计算效率更高。
学习率调节器也使用了HuggingFace提供的线性学习率调节器,它能在训练的过程中,让学习率缓慢地下降,而不是使用始终如一的学习率,因为在训练的后期阶段,需要更小的学习率来微调参数,这有利于loss 下降到更低的点。
由于本章的任务为分类任务,所以使用的loss计算函数为CrossEntropyLoss,即交叉熵计算函数。
之后把下游任务模型切换到训练模式,即可开始训练。训练的过程为不断地从数据集加载器中获取一批一批的数据,让模型进行计算,用模型计算的结果和真实的labels 计算 loss,根据 loss计算模型中所有参数的梯度,并执行梯度下降优化参数。
最后,每优化10次模型参数,就计算一次当前模型预测结果的正确率,并输出模型的 loss 和优化器的学习率,最终训练完毕后,输出的观察数据图表。
从图表可以看出,在训练到大约580个steps时,模型已经能够达到大约87%100%的正确率,并且能够观察到loss是随着训练的进程在不断地下降,学习率也如预期的一样,也在缓慢地下降

测试

  1. def test():
  2. #定义测试数据集加载器
  3. loader_test = torch.utils.data.DataLoader(dataset=Dataset('test'),
  4. batch_size=32,
  5. collate_fn=collate_fn,
  6. shuffle=True,
  7. drop_last=True)
  8. #下游任务模型切换到运行模式
  9. model.eval()
  10. correct = 0
  11. total = 0
  12. #按批次遍历测试集中的数据
  13. for i, (input_ids, attention_mask, token_type_ids,
  14. labels) in enumerate(loader_test):
  15. #计算5个批次即可,不需要全部遍历
  16. if i == 5:
  17. break
  18. print(i)
  19. #计算
  20. with torch.no_grad():
  21. out = model(input_ids=input_ids,
  22. attention_mask=attention_mask,
  23. token_type_ids=token_type_ids)
  24. #统计正确率
  25. out = out.argmax(dim=1)
  26. correct += (out == labels).sum().item()
  27. total += len(labels)
  28. print(correct / total)
  29. test()


首先定义了测试数据集和加载器,并取出5个批次的数据让模型进行预测,最后统计正确率并输出,如下图所示:


欢迎加群讨论技术,1群:677373950(满了,可以加,但通过不了),2群:656732739

评价

HugginFace 初探

HugginFace 初探[TOC] 安装环境python环境是3.6。import sys sys.version 安装torch,简单起见,避免环境问题,并且计...

HugginFace 使用编码工具(学习笔记)

HugginFace 使用编码工具(学习笔记)[TOC] 安装环境# 我这里的python是3.11 %pip install -q transformers==4.18 datasets...

HugginFace 使用数据集(学习笔记)

HugginFace 使用数据集(学习笔记)[TOC] 数据集工具介绍HuggingFace 提供了统一的数据集处理工具,让不同的数据集通过统一...

HugginFace 使用评价指标工具(学习笔记)

HugginFace 使用评价指标工具(学习笔记)[TOC] 评价指标工具介绍在训练和测试一个模型时往往需要计算不同的评价指标,如正...

HugginFace 使用管道工具(学习笔记)

HugginFace 使用管道工具(学习笔记)[TOC] 管道工具介绍HuggingFace 有一个巨大的模型库,其中一些是已经非常成熟的经典模...

HugginFace 使用训练工具(学习笔记)

HugginFace 使用训练工具(学习笔记)[TOC] 训练工具介绍HuggingFace提供了巨大的模型库,但我们往往还需要对特定的数据集进...

HugginFace 中文填空(学习笔记)

HugginFace 中文填空(学习笔记)[TOC] 数据集介绍本章使用的仍然是情感分类数据集,每条包括一句购物评价一集以及是不是好...

HugginFace 中文数据关系推断(学习笔记)

HugginFace 中文数据关系推断(学习笔记)[TOC] 实现代码安装包加载的环境可以通过如下命令进行安装。%pip install -q trans...

HugginFace 中文命名实体识别(学习笔记)

HugginFace 中文命名实体识别(学习笔记)[TOC] 任务简介简单来说就是的识别人名、机构名、地名。数据集的介绍本章所使用的...
这一世以无限游戏为使命!
排名
2
文章
642
粉丝
44
评论
93
docker中Sware集群与service
尘叶心繁 : 想学呀!我教你呀
一个bug让程序员走上法庭 索赔金额达400亿日元
叼着奶瓶逛酒吧 : 所以说做程序员也要懂点法律知识
.net core 塑形资源
剑轩 : 收藏收藏
映射AutoMapper
剑轩 : 好是好,这个对效率影响大不大哇,效率高不高
ASP.NET Core 服务注册生命周期
剑轩 : http://www.tnblog.net/aojiancc2/article/details/167
ICP备案 :渝ICP备18016597号-1
网站信息:2018-2025TNBLOG.NET
技术交流:群号656732739
联系我们:contact@tnblog.net
公网安备:50010702506256
欢迎加群交流技术