【NLP实战】基于Bert和双向LSTM的情感分类【中篇】( 五 )


可以看到损失在逐渐下降,并且速度提升了很多倍 。并且显示使用的cuda:0 。
第三部分:!改造结构
本来在这部分,我想完善和batch的训练过程,并且记录loss和验证集的acc用于绘制图像,但在我写到一半是萌生了一个想法:有没有?于是我去问了chat 。它告诉我有个东西叫- 。里面就有,可以便于我们管理我们的训练过程等 。于是,我改变了这部分的目标 。接下来,我们将学习如何使用-改善我们结构,管理整理训练过程 。
这个呢,最好是先去看看视频,然后跟着官方文档学 。
参考教程

B站视频教学:使用 简化代码_哔哩哔哩
官方文档(超级有用):Basic—2.1.0dev
改造结构
现在我们新创建一个文件,copy已经写好的代码,准备用-去改造它,避免改造过程中失误 。这里只放最终改造结果,其实博主看着文档和教程一步步改造,用的时间很多 。但是也越来越熟练 。
注意:改造时加入了交叉验证,便于后续观察过拟合 。
import torchfrom datasets import load_dataset# hugging-face datasetfrom torch.utils.data import Datasetfrom torch.utils.data import DataLoaderimport torch.nn as nnfrom transformers import BertTokenizer, BertModelimport torch.optim as optimfrom torch.nn.functional import one_hotimport pytorch_lightning as plfrom pytorch_lightning import Trainer?# todo:定义超参数batch_size = 128epochs = 30dropout = 0.4rnn_hidden = 768rnn_layer = 1class_num = 3lr = 0.001??# todo:自定义数据集class MydataSet(Dataset):def __init__(self, path, split):self.dataset = load_dataset('csv', data_files=path, split=split)?def __getitem__(self, item):text = self.dataset[item]['text']label = self.dataset[item]['label']return text, label?def __len__(self):return len(self.dataset)??# todo: 定义批处理函数def collate_fn(data):sents = [i[0] for i in data]labels = [i[1] for i in data]?# 分词并编码data = http://www.kingceram.com/post/token.batch_encode_plus(batch_text_or_text_pairs=sents,# 单个句子参与编码truncation=True,# 当句子长度大于max_length时,截断padding='max_length',# 一律补pad到max_length长度max_length=200,return_tensors='pt',# 以pytorch的形式返回,可取值tf,pt,np,默认为返回listreturn_length=True,)?# input_ids:编码之后的数字# attention_mask:是补零的位置是0,其他位置是1input_ids = data['input_ids']# input_ids 就是编码后的词attention_mask = data['attention_mask']# pad的位置是0,其他位置是1token_type_ids = data['token_type_ids']# (如果是一对句子)第一个句子和特殊符号的位置是0,第二个句子的位置是1labels = torch.LongTensor(labels)# 该批次的labels?# print(data['length'], data['length'].max())return input_ids, attention_mask, token_type_ids, labels??# todo: 定义模型,上游使用bert预训练,下游任务选择双向LSTM模型,最后加一个全连接层class BiLSTMClassifier(nn.Module):def __init__(self, drop, hidden_dim, output_dim):super(BiLSTMClassifier, self).__init__()self.drop = dropself.hidden_dim = hidden_dimself.output_dim = output_dim?# 加载bert中文模型,生成embedding层self.embedding = BertModel.from_pretrained('bert-base-chinese')# 去掉移至gpu# 冻结上游模型参数(不进行预训练模型参数学习)for param in self.embedding.parameters():param.requires_grad_(False)# 生成下游RNN层以及全连接层self.lstm = nn.LSTM(input_size=768, hidden_size=self.hidden_dim, num_layers=2, batch_first=True,bidirectional=True, dropout=self.drop)self.fc = nn.Linear(self.hidden_dim * 2, self.output_dim)# 使用CrossEntropyLoss作为损失函数时,不需要激活 。因为实际上CrossEntropyLoss将softmax-log-NLLLoss一并实现的 。?def forward(self, input_ids, attention_mask, token_type_ids):embedded = self.embedding(input_ids=input_ids, attention_mask=attention_mask, token_type_ids=token_type_ids)embedded = embedded.last_hidden_state# 第0维才是我们需要的embedding,embedding.last_hidden_state = embedding[0]out, (h_n, c_n) = self.lstm(embedded)output = torch.cat((h_n[-2, :, :], h_n[-1, :, :]), dim=1)output = self.fc(output)return output??# todo: 定义pytorch lightningclass BiLSTMLighting(pl.LightningModule):def __init__(self, drop, hidden_dim, output_dim):super(BiLSTMLighting, self).__init__()self.model = BiLSTMClassifier(drop, hidden_dim, output_dim)# 设置modelself.criterion = nn.CrossEntropyLoss()# 设置损失函数self.train_dataset = MydataSet('./data/archive/train_clean.csv', 'train')self.val_dataset = MydataSet('./data/archive/val_clean.csv', 'train')?def configure_optimizers(self):optimizer = optim.AdamW(self.parameters(), lr=lr)return optimizer?def forward(self, input_ids, attention_mask, token_type_ids):# forward(self,x)return self.model(input_ids, attention_mask, token_type_ids)?def training_step(self, batch, batch_idx):input_ids, attention_mask, token_type_ids, labels = batch# x, y = batchy = one_hot(labels + 1, num_classes=3)# 将one_hot_labels类型转换成floaty = y.to(dtype=torch.float)# forward passy_hat = self.model(input_ids, attention_mask, token_type_ids)# y_hat = y_hat.squeeze()# 将[128, 1, 3]挤压为[128,3]loss = self.criterion(y_hat, y)# criterion(input, target)self.log('train_loss', loss, prog_bar=True, logger=True, on_step=True, on_epoch=True)# 将loss输出在控制台return loss?def train_dataloader(self):train_loader = DataLoader(dataset=self.train_dataset, batch_size=batch_size, collate_fn=collate_fn, shuffle=True)return train_loader?def validation_step(self, batch, batch_idx):input_ids, attention_mask, token_type_ids, labels = batchy = one_hot(labels + 1, num_classes=3)y = y.to(dtype=torch.float)# forward passy_hat = self.model(input_ids, attention_mask, token_type_ids)# y_hat = y_hat.squeeze()loss = self.criterion(y_hat, y)self.log('val_loss', loss, prog_bar=True, logger=True, on_step=True, on_epoch=True)return loss?def val_dataloader(self):val_loader = DataLoader(dataset=self.val_dataset, batch_size=batch_size, collate_fn=collate_fn, shuffle=False)return val_loader??if __name__ == '__main__':token = BertTokenizer.from_pretrained('bert-base-chinese')# Trainer可以帮助调试,比如快速运行、只使用一小部分数据进行测试、完整性检查等,# 详情请见官方文档https://lightning.ai/docs/pytorch/latest/debug/debugging_basic.html# auto自适应gpu数量trainer = Trainer(max_epochs=2, limit_train_batches=10, limit_val_batches=5, log_every_n_steps=1,accelerator='gpu', devices="auto")model = BiLSTMLighting(drop=dropout, hidden_dim=rnn_hidden, output_dim=class_num)trainer.fit(model)