Pytorch——BERT 预训练模型及文本分类 (5)

下载好数据集后,读取数据文件。

with open(\'./negdata.txt\', \'r\', encoding=\'utf-8\') as f: neg_data = f.read() with open(\'./posdata.txt\', \'r\', encoding=\'utf-8\') as f: pos_data = f.read() neg_datalist = neg_data.split(\'\n\') pos_datalist = pos_data.split(\'\n\') len(neg_datalist), len(pos_datalist)

在读取到数据后,我们将将数据存到一个列表中,并构建标签列表,用 1 表示正面的评论,用 0 表示负面的评论。

import numpy as np dataset = np.array(pos_datalist + neg_datalist) labels = np.array([1] * len(pos_datalist) + [0] * len(neg_datalist)) len(dataset) # 共 3000 条数据

利用 NumPy 库使样本数据随机排列。

np.random.seed(10) mix_index = np.random.choice(3000, 3000) dataset = dataset[mix_index] labels = labels[mix_index] len(dataset), len(labels)

然后以取 2500 条数据作为训练集,取 500 条数据作为验证集。

TRAINSET_SIZE = 2500 EVALSET_SIZE = 500 train_samples = dataset[:TRAINSET_SIZE] # 2500 条数据 train_labels = labels[:TRAINSET_SIZE] eval_samples = dataset[TRAINSET_SIZE:TRAINSET_SIZE+EVALSET_SIZE] # 500 条数据 eval_labels = labels[TRAINSET_SIZE:TRAINSET_SIZE+EVALSET_SIZE] len(train_samples), len(eval_samples)

构建函数 get_dummies ,作用是把标签转换成 one-hot 的表示形式,例如将 1 表示成 [0, 1],0 表示成 [1, 0] 的形式。

def get_dummies(l, size=2): res = list() for i in l: tmp = [0] * size tmp[i] = 1 res.append(tmp) return res

这里使用 PyTorch 提供的 DataLoader() 构建训练集数据集表示,使用 TensorDataset() 构建训练集数据迭代器。

from torch.utils.data import DataLoader, TensorDataset tokenized_text = [tokenizer.tokenize(i) for i in train_samples] input_ids = [tokenizer.convert_tokens_to_ids(i) for i in tokenized_text] input_labels = get_dummies(train_labels) # 使用 get_dummies 函数转换标签 for j in range(len(input_ids)): # 将样本数据填充至长度为 512 i = input_ids[j] if len(i) != 512: input_ids[j].extend([0]*(512 - len(i))) # 构建数据集和数据迭代器,设定 batch_size 大小为 4 train_set = TensorDataset(torch.LongTensor(input_ids), torch.FloatTensor(input_labels)) train_loader = DataLoader(dataset=train_set, batch_size=4, shuffle=True) train_loader

与构建训练集数据迭代器类似,构建验证集的数据迭代器。

tokenized_text = [tokenizer.tokenize(i) for i in eval_samples] input_ids = [tokenizer.convert_tokens_to_ids(i) for i in tokenized_text] input_labels = eval_labels for j in range(len(input_ids)): i = input_ids[j] if len(i) != 512: input_ids[j].extend([0]*(512 - len(i))) eval_set = TensorDataset(torch.LongTensor(input_ids), torch.FloatTensor(input_labels)) eval_loader = DataLoader(dataset=eval_set, batch_size=1, shuffle=True) eval_loader

检查是否机器有 GPU,如果有就在 GPU 运行,否则就在 CPU 运行。

device = torch.device("cuda" if torch.cuda.is_available() else "cpu") device

构建一个用于分类的类,加入 BERT 模型,在 BERT 模型下加入一个 Dropout 层用于防止过拟合,和一个 Linear 全连接层。

import torch.nn as nn import torch.nn.functional as F from pytorch_transformers import BertModel class fn_cls(nn.Module): def __init__(self): super(fn_cls, self).__init__() self.model = BertModel.from_pretrained(model_name, cache_dir="./") self.model.to(device) self.dropout = nn.Dropout(0.1) self.l1 = nn.Linear(768, 2) def forward(self, x, attention_mask=None): outputs = self.model(x, attention_mask=attention_mask) x = outputs[1] # 取池化后的结果 batch * 768 x = x.view(-1, 768) x = self.dropout(x) x = self.l1(x) return x

定义损失函数,建立优化器。

from torch import optim cls = fn_cls() cls.to(device) cls.train() criterion = nn.BCELoss() sigmoid = nn.Sigmoid() optimizer = optim.Adam(cls.parameters(), lr=1e-5)

构建预测函数,用于计算预测结果。

def predict(logits): res = torch.argmax(logits, 1) return res

构建训练函数并开始训练。这里需要说一下,因为 GPU 内存的限制,训练集的 batch_size 设为了 4,这样的 batch_size 过小,使得梯度下降方向不准,引起震荡,难以收敛。所以,在训练时使用了梯度积累的方法,即计算 8 个小批次的梯度的平均值来更新模型,从而达到了 32 个小批次的效果。

from torch.autograd import Variable import time pre = time.time() accumulation_steps = 8 epoch = 3 for i in range(epoch): for batch_idx, (data, target) in enumerate(train_loader): data, target = Variable(data).to(device), Variable( target.view(-1, 2)).to(device) mask = [] for sample in data: mask.append([1 if i != 0 else 0 for i in sample]) mask = torch.Tensor(mask).to(device) output = cls(data, attention_mask=mask) pred = predict(output) loss = criterion(sigmoid(output).view(-1, 2), target) # 梯度积累 loss = loss/accumulation_steps loss.backward() if((batch_idx+1) % accumulation_steps) == 0: # 每 8 次更新一下网络中的参数 optimizer.step() optimizer.zero_grad() if ((batch_idx+1) % accumulation_steps) == 1: print(\'Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss:{:.6f}\'.format( i+1, batch_idx, len(train_loader), 100. * batch_idx/len(train_loader), loss.item() )) if batch_idx == len(train_loader)-1: # 在每个 Epoch 的最后输出一下结果 print(\'labels:\', target) print(\'pred:\', pred) print(\'训练时间:\', time.time()-pre)

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zwxssf.html