首页
视频
资源
登录
原
HugginFace 中文数据关系推断(学习笔记)
1498
人阅读
2023/10/30 15:56
总访问:
2993361
评论:
0
收藏:
0
手机
分类:
HuggingFace
![](https://img.tnblog.net/arcimg/hb/782c293bc3904ab0bb30af5ff454beae.png) >#HugginFace 中文数据关系推断(学习笔记) [TOC] ## 实现代码 ### 安装包 tn2>加载的环境可以通过如下命令进行安装。 ```python %pip install -q transformers==4.18 datasets==2.4.0 torchtext ``` ### 准备数据集 tn2>使用编码工具。 本章依然使用的是`bert-base-chinese`编码工具。 ```python from transformers import BertTokenizer token = BertTokenizer.from_pretrained('bert-base-chinese') token ``` ![](https://img.tnblog.net/arcimg/hb/6870101f6e8b490abf4301cd9aa5cd29.png) tn2>进行试算一次,以更清晰的观察输入和输出,代码如下: ```python #试编码句子 out = token.batch_encode_plus( batch_text_or_text_pairs=[('不是一切大树,', '都被风暴折断。'), ('不是一切种子,', '都找不到生根的土壤。')], truncation=True, padding='max_length', max_length=18, return_tensors='pt', return_length=True, ) #查看编码输出 for k, v in out.items(): print(k, v.shape) #把编码还原为句子 print(token.decode(out['input_ids'][0])) ``` tn2>与情感分类和填空任务不同,这里编码的是句子对,运行结果如下: ![](https://img.tnblog.net/arcimg/hb/165b9b876e7a4a3792c4f1d5fbd691ee.png) tn2>确定编码的长度为`18`个词的长度。 ### 定义数据集 tn2>定义本次任务所需要的数据集仍然是`ChnSentiCorp`数据集。 ```python # 定义数据集 import torch from datasets import load_dataset import random class Dataset(torch.utils.data.Dataset): def __init__(self, split): dataset = load_dataset('lansinuote/ChnSentiCorp')[split] def f(data): return len(data['text']) > 40 self.dataset = dataset.filter(f) def __len__(self): return len(self.dataset) def __getitem__(self, i): text = self.dataset[i]['text'] #切分一句话为前半句和后半句 sentence1 = text[:20] sentence2 = text[20:40] #随机整数,取值范围为0和1 label = random.randint(0, 1) #有一半的概率把后半句替换为一句无关的话 if label == 1: j = random.randint(0, len(self.dataset) - 1) sentence2 = self.dataset[j]['text'][20:40] return sentence1, sentence2, label dataset = Dataset('train') sentence1, sentence2, label = dataset[7] len(dataset), sentence1, sentence2, label ``` tn2>在这段代码中,加载了`ChnSentiCorp`数据集,并使用PyTorch的Dataset对象进行封装,由于本次任务是要判断两句话是否存在相连的关系,如果假设定义每句话的长度为20个字,则原句子最短不能少于40个字,否则不能被切割成两句话。 所以在`__init__()`函数中加载了`ChnSentiCorp`数据集后对数据集进行过滤,丢弃了数字少于40个字的句子。 在`__getitem__()`函数中把原句切割成了各20个字的两句话,并且有一半的概率把后半句替换为无关的句子,这样就形成了本次任务中需要的数据结构,即每条数据中包括两句话,并且这两句话分别有`50%`的概率是相联和无关的关系。 运行结果如下: ![](https://img.tnblog.net/arcimg/hb/90c4f1a695314b33ae1c6bb22cb52557.png) tn2>可见,训练数据集包括`8001`条数据,有两句话一个标识。 ### 定义计算设备 tn2>能不能支持gpu。 ```python import torch device = 'cpu' if torch.cuda.is_available(): device = 'cuda' device ``` ![](https://img.tnblog.net/arcimg/hb/47d2cf1407fe41f4a7585d40545c039d.png) ### 定义数据整理函数 tn2>定义一个数据整理函数,它具有批量编码一批文本数据的功能,代码如下: ```python #数据整理函数 def collate_fn(data): sents = [i[:2] for i in data] labels = [i[2] for i in data] #编码 data = token.batch_encode_plus(batch_text_or_text_pairs=sents, truncation=True, padding='max_length', max_length=45, return_tensors='pt', return_length=True, add_special_tokens=True) #input_ids:编码之后的数字 #attention_mask:是补零的位置是0,其他位置是1 #token_type_ids:第一个句子和特殊符号的位置是0,第二个句子的位置是1 input_ids = data['input_ids'].to(device) attention_mask = data['attention_mask'].to(device) token_type_ids = data['token_type_ids'].to(device) labels = torch.LongTensor(labels).to(device) return input_ids, attention_mask, token_type_ids, labels ``` tn2>在这段代码中,入参的data表示一批数据,取出其中的句子对和标识,分别为两个list,其中句子对的list中为一个一个tuple,每个tuple中包括两个句子,即一对句子。 在制作数据集时已经明确两个句子各有20个字,但在经历编码时每个字并不一定会被编码成一个词,此外在编码时还需要往句子中插入一些特殊福好,如标识句子开始的`[CLS]`标识一个句子的结束的`[SEP]`,所以编码的结果并不能确定为40个词,因此在编码时需要留下一定的容差,让编码结果中能囊括两个句子的所有信息,如果过有多余的位置,则可以以`[PAD]`填充。 综上所述,使用编码工具编码这一批句子对时,在参数中指定了编码后的结果为确定的45个词,超过45个词的句子将被截断,而不足45个词的句子将被补充`PAD`,直到45个词。 在编码时,通过参数`return_tensors='pt'`让编码结果为`PyTorch`的`Tensor`格式,这免去了后续转换数据格式的麻烦。 之后取出编码的结果,不妨假定一批数据,让数据整理函数进行试算,以观察数据整理的输入和输出,代码如下: ```python #数据整理函数试算 #模拟一批数据 data = [('酒店还是非常的不错,我预定的是套间,服务', '非常好,随叫随到,结帐非常快。', 0), ('外观很漂亮,性价比感觉还不错,功能简', '单,适合出差携带。蓝牙摄象头都有了。', 0), ('《穆斯林的葬礼》我已闻名很久,只是一直没', '怎能享受4星的服务,连空调都不能用的。', 1)] #试算 input_ids, attention_mask, token_type_ids, labels = collate_fn(data) #把编码还原为句子 print(token.decode(input_ids[0])) input_ids.shape, attention_mask.shape, token_type_ids.shape, labels ``` ![](https://img.tnblog.net/arcimg/hb/3016ab0fc30a43328c4bb775bec56481.png) tn2>编码之后的结果都是确定的45个词 ### 定义数据集加载器 ```python #数据加载器 loader = torch.utils.data.DataLoader(dataset=dataset, batch_size=8, collate_fn=collate_fn, shuffle=True, drop_last=True) len(loader) ``` ![](https://img.tnblog.net/arcimg/hb/6be2fc5f63ae4c85a142f78222eb2d72.png) tn2>可见,训练数据集加载器一共有1000个批次。 定义好了数据加载器之后,可以查看一批数据样本,代码如下: ```python #查看数据样例 for i, (input_ids, attention_mask, token_type_ids, labels) in enumerate(loader): break input_ids.shape, attention_mask.shape, token_type_ids.shape, labels ``` ![](https://img.tnblog.net/arcimg/hb/a4729074d99648fdbdbc9750bf842242.png) tn2>这个结果其实就是数据整理函数的计算结果,只是句子的数量更多。 ## 定义模型 ### 加载预训练模型 ```python #加载预训练模型 from transformers import BertModel pretrained = BertModel.from_pretrained('bert-base-chinese') #统计参数量 sum(i.numel() for i in pretrained.parameters()) / 10000 ``` tn2>在代码的最后,输出了模型的参数量,运行结果如下: ![](https://img.tnblog.net/arcimg/hb/bf43aafbde66446fa2e61b0890f69537.png) tn2>可见`bert-base-chinese`的参数量约为1亿个,在本次任务中选择不训练它,代码如下: ```python #不训练预训练模型,不需要计算梯度 for param in pretrained.parameters(): param.requires_grad_(False) ``` tn2>运行结果如下: ![](https://img.tnblog.net/arcimg/hb/b67cc0b5f3024930b247256d1aef60d0.png) tn2>样例数据为8句话的编码结果,从预训练模型的计算结果可以看出,这也是8句话的结果,每句话包括45个词,每个词被抽成了一个768维的向量。到此为止,通过预训练模型成功地把8句话转换为一个特征向量矩阵,可以接入下游任务模型做分类或者回归任务。 ### 定义下游任务模型 tn2>完成以上工作后,现在可以定义下游任务模型了,对于本章的任务来讲,需要计算一个二分类的结果,并且需要和数据集中真实的label保持一致,代码如下: ```python #定义下游任务模型 class Model(torch.nn.Module): def __init__(self): super().__init__() self.fc = torch.nn.Linear(768, 2) def forward(self, input_ids, attention_mask, token_type_ids): #使用预训练模型抽取数据特征 with torch.no_grad(): out = pretrained(input_ids=input_ids, attention_mask=attention_mask, token_type_ids=token_type_ids) #对抽取的特征只取第一个字的结果做分类即可 out = self.fc(out.last_hidden_state[:, 0]) out = out.softmax(dim=1) return out model = Model() #设定计算设备 model.to(device) #试算 model(input_ids=input_ids, attention_mask=attention_mask, token_type_ids=token_type_ids).shape ``` tn2>在这段代码中,定义了下游任务模型,该模型只包括一个全连接的线性神经网络,权重矩阵为768x2,所以它能够把一个768维度的向量转换到二维空间中。 下游任务模型丢弃了44个词的特征,只取得了第1个词(索引为0)的特征向量,对应了编码结果中的`[CLS]`,把特征向量矩阵变成了`16x768`。相当于把每句话变成了一个`768`维度的向量。 tn>之所以只取了第1个词的特征做后续的判断计算,这和预训练模型BERT的训练方法有关系。 tn2>之后再使用自己的全连接线性神经网络把`16x768`特征矩阵转换到`16x2`,即为要求的二分类结果。 ![](https://img.tnblog.net/arcimg/hb/d4dd1edb71614348a4eaabfcf4265081.png) tn2>可见,这就是要求的16句话的二分类的结果。 ## 训练和测试 ### 训练 ```python #第9章/训练 from transformers import AdamW from transformers.optimization import get_scheduler def train(): #定义优化器 optimizer = AdamW(model.parameters(), lr=5e-5) #定义loss函数 criterion = torch.nn.CrossEntropyLoss() #定义学习率调节器 scheduler = get_scheduler(name='linear', num_warmup_steps=0, num_training_steps=len(loader), optimizer=optimizer) #模型切换到训练模式 model.train() #按批次遍历训练集中的数据 for i, (input_ids, attention_mask, token_type_ids, labels) in enumerate(loader): #模型计算 out = model(input_ids=input_ids, attention_mask=attention_mask, token_type_ids=token_type_ids) #计算loss并使用梯度下降法优化模型参数 loss = criterion(out, labels) loss.backward() optimizer.step() scheduler.step() optimizer.zero_grad() #输出各项数据的情况,便于观察 if i % 20 == 0: out = out.argmax(dim=1) accuracy = (out == labels).sum().item() / len(labels) lr = optimizer.state_dict()['param_groups'][0]['lr'] print(i, loss.item(), lr, accuracy) train() ``` tn2>在这段代码中,首先定义了优化器、loss计算函数、学习率调节器。 训练完成后见下图所示: ![](https://img.tnblog.net/arcimg/hb/378b911d6d8e463a92f94d84cfb4063c.png) tn2>由于只是简单的一层全连接神经网络,所以训练的难度很低。 学习率在慢慢下降。 ### 测试 tn2>最后,对训练好的模型进行测试,以验证训练的有效性,代码如下图所示: ```python #第9章/测试 def test(): #定义测试数据集加载器 loader_test = torch.utils.data.DataLoader(dataset=Dataset('test'), batch_size=32, collate_fn=collate_fn, shuffle=True, drop_last=True) #下游任务模型切换到运行模式 model.eval() correct = 0 total = 0 #按批次遍历测试集中的数据 for i, (input_ids, attention_mask, token_type_ids, labels) in enumerate(loader_test): #计算5个批次即可,不需要全部遍历 if i == 5: break print(i) #计算 with torch.no_grad(): out = model(input_ids=input_ids, attention_mask=attention_mask, token_type_ids=token_type_ids) pred = out.argmax(dim=1) #统计正确率 correct += (pred == labels).sum().item() total += len(labels) print(correct / total) test() ``` tn2>定义了测试数据集和加载器,并取出5个批次的数据让模型进行预测,最后统计正确率并输出,运行结果如下: ![](https://img.tnblog.net/arcimg/hb/5436a8be626d4c079f4502b1bd146d92.png)
欢迎加群讨论技术,1群:677373950(满了,可以加,但通过不了),2群:656732739
👈{{preArticle.title}}
👉{{nextArticle.title}}
评价
{{titleitem}}
{{titleitem}}
{{item.content}}
{{titleitem}}
{{titleitem}}
{{item.content}}
尘叶心繁
这一世以无限游戏为使命!
博主信息
排名
6
文章
6
粉丝
16
评论
8
文章类别
.net后台框架
178篇
linux
18篇
linux中cve
1篇
windows中cve
0篇
资源分享
10篇
Win32
3篇
前端
28篇
传说中的c
5篇
Xamarin
9篇
docker
15篇
容器编排
101篇
grpc
4篇
Go
15篇
yaml模板
1篇
理论
2篇
更多
Sqlserver
4篇
云产品
39篇
git
3篇
Unity
1篇
考证
2篇
RabbitMq
23篇
Harbor
1篇
Ansible
8篇
Jenkins
17篇
Vue
1篇
Ids4
18篇
istio
1篇
架构
2篇
网络
7篇
windbg
4篇
AI
19篇
threejs
2篇
人物
1篇
嵌入式
20篇
python
20篇
HuggingFace
8篇
pytorch
10篇
opencv
6篇
Halcon
5篇
最新文章
最新评价
{{item.articleTitle}}
{{item.blogName}}
:
{{item.content}}
关于我们
ICP备案 :
渝ICP备18016597号-1
网站信息:
2018-2024
TNBLOG.NET
技术交流:
群号656732739
联系我们:
contact@tnblog.net
欢迎加群
欢迎加群交流技术