Open In Google Colab

文章目录

  • 本文涉及知识点
  • 本文内容
  • 环境配置
  • 全局配置
  • 数据处理
    • 加载数据集
    • Dataset and Dataloader
  • 构建模型
  • 训练模型
  • 模型使用

本文涉及知识点

  1. Hugging Face快速入门
  2. Pytorch中DataLoader和Dataset的基本用法

本文内容

这是Kaggle上NLP的一个入门题目(链接),任务是对文本进行二分类。内容描述:人们会在Twitter上发布一些内容,这些内容有些是灾难事件,例如“白宫着火了,火焰很大”,这就是一个灾难事件。而有一些虽然也带了相关词汇,却不是灾难事件,例如:”天上那朵云好像燃烧的火焰。“。所以本项目的任务就是区分这两种情况。

数据集可以到Kaggle上下载(链接),或者使用百度网盘下载(链接)

最终可以将你的预测结果上传到Kaggle上查看分数(链接)。

你可以在Github上找到本文的源码(链接)。你也可以直接使用Google Colab来运行代码(Open In Google Colab)

环境配置

本项目使用库版本如下

python==3.8.5
pandas==1.3.5
torch==1.11.0
transformers==4.21

导入本文要使用的所有依赖包:

import os
import pandas
import torch
from torch import nn
from torch.utils.data import Dataset, DataLoader
# 用于加载bert模型的分词器
from transformers import AutoTokenizer
# 用于加载bert模型
from transformers import AutoModel
from pathlib import Path
from tqdm.notebook import tqdm

全局配置

batch_size = 16
# 文本的最大长度
text_max_length = 128
# 总训练的epochs数,我只是随便定义了个数
epochs = 100
# 取多少训练集的数据作为验证集
validation_ratio = 0.1
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')# 每多少步,打印一次loss
log_per_step = 50# 数据集所在位置
dataset_dir = Path("./dataset")
os.makedirs(dataset_dir) if not os.path.exists(dataset_dir) else ''# 模型存储路径
model_dir = Path("./drive/MyDrive/model/bert_checkpoints")
# 如果模型目录不存在,则创建一个
os.makedirs(model_dir) if not os.path.exists(model_dir) else ''print("Device:", device)
Device: cuda

数据处理

加载数据集

请先下载数据集,并解压到dataset目录下,其中会有train.csv、test.csv和sample_submission.csv三个文件。

使用pandas来加载训练数据,对于训练数据,我们只需要text和target两行:

pd_data = pandas.read_csv(dataset_dir / 'train.csv')[['text', 'target']]

加载成功后,来看一下内容:

pd_data
text target
0 Our Deeds are the Reason of this #earthquake M... 1
1 Forest fire near La Ronge Sask. Canada 1
2 All residents asked to 'shelter in place' are ... 1
3 13,000 people receive #wildfires evacuation or... 1
4 Just got sent this photo from Ruby #Alaska as ... 1
... ... ...
7608 Two giant cranes holding a bridge collapse int... 1
7609 @aria_ahrary @TheTawniest The out of control w... 1
7610 M1.94 [01:04 UTC]?5km S of Volcano Hawaii. htt... 1
7611 Police investigating after an e-bike collided ... 1
7612 The Latest: More Homes Razed by Northern Calif... 1

text就是推文,target就是该推文是否是在描述一个灾难事件,1:是,0:否。

Dataset and Dataloader

我们将训练数据按比例随机分为训练集和验证集:

pd_validation_data = pd_data.sample(frac=validation_ratio)
pd_train_data = pd_data[~pd_data.index.isin(pd_validation_data.index)]

加载好数据集后,我们就可以开始构建Dataset了,我们这里Dataset就是返回推文和其target:

class MyDataset(Dataset):def __init__(self, mode='train'):super(MyDataset, self).__init__()self.mode = mode# 拿到对应的数据if mode == 'train':self.dataset = pd_train_dataelif mode == 'validation':self.dataset = pd_validation_dataelif mode == 'test':# 如果是测试模式,则返回推文和id。拿id做target主要是方便后面写入结果。self.dataset = pandas.read_csv(dataset_dir / 'test.csv')[['text', 'id']]else:raise Exception("Unknown mode {}".format(mode))def __getitem__(self, index):# 取第index条data = self.dataset.iloc[index]# 取其推文,做个简单的数据清理source = data['text'].replace("#", "").replace("@", "")# 取对应的推文if self.mode == 'test':# 如果是test,将id做为targettarget = data['id']else:target = data['target']# 返回推文和targetreturn source, targetdef __len__(self):return len(self.dataset)
train_dataset = MyDataset('train')
validation_dataset = MyDataset('validation')

我们来打印看一下;

train_dataset.__getitem__(0)
('Our Deeds are the Reason of this earthquake May ALLAH Forgive us all', 1)

构造好Dataset后,就可以来构造Dataloader了。在构造Dataloader前,我们需要先定义好分词器:

tokenizer = AutoTokenizer.from_pretrained("bert-base-uncased")

我们来尝试使用一下分词器:

tokenizer("I'm learning deep learning", return_tensors='pt')
{'input_ids': tensor([[ 101, 1045, 1005, 1049, 4083, 2784, 4083,  102]]), 'token_type_ids': tensor([[0, 0, 0, 0, 0, 0, 0, 0]]), 'attention_mask': tensor([[1, 1, 1, 1, 1, 1, 1, 1]])}

可以正常运行。其中101表示“开始”([CLS]),102表示句子结束([SEP])。

我们接着构造我们的Dataloader。我们需要定义一下collate_fn,在其中完成对句子进行编码、填充、组装batch等动作:

def collate_fn(batch):"""将一个batch的文本句子转成tensor,并组成batch。:param batch: 一个batch的句子,例如: [('推文', target), ('推文', target), ...]:return: 处理后的结果,例如:src: {'input_ids': tensor([[ 101, ..., 102, 0, 0, ...], ...]), 'attention_mask': tensor([[1, ..., 1, 0, ...], ...])}target:[1, 1, 0, ...]"""text, target = zip(*batch)text, target = list(text), list(target)# src是要送给bert的,所以不需要特殊处理,直接用tokenizer的结果即可# padding='max_length' 不够长度的进行填充# truncation=True 长度过长的进行裁剪src = tokenizer(text, padding='max_length', max_length=text_max_length, return_tensors='pt', truncation=True)return src, torch.LongTensor(target)
train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True, collate_fn=collate_fn)
validation_loader = DataLoader(validation_dataset, batch_size=batch_size, shuffle=False, collate_fn=collate_fn)

我们来看一眼train_loader的数据:

inputs, targets = next(iter(train_loader))
print("inputs:", inputs)
print("targets:", targets)
inputs: {'input_ids': tensor([[  101,  4911,  1024,  ...,     0,     0,     0],[  101, 19387, 11113,  ...,     0,     0,     0],[  101,  2317,  2111,  ...,     0,     0,     0],...,[  101, 25595, 10288,  ...,     0,     0,     0],[  101,  1037, 14700,  ...,     0,     0,     0],[  101, 12361,  2042,  ...,     0,     0,     0]]), 'token_type_ids': tensor([[0, 0, 0,  ..., 0, 0, 0],[0, 0, 0,  ..., 0, 0, 0],[0, 0, 0,  ..., 0, 0, 0],...,[0, 0, 0,  ..., 0, 0, 0],[0, 0, 0,  ..., 0, 0, 0],[0, 0, 0,  ..., 0, 0, 0]]), 'attention_mask': tensor([[1, 1, 1,  ..., 0, 0, 0],[1, 1, 1,  ..., 0, 0, 0],[1, 1, 1,  ..., 0, 0, 0],...,[1, 1, 1,  ..., 0, 0, 0],[1, 1, 1,  ..., 0, 0, 0],[1, 1, 1,  ..., 0, 0, 0]])}
targets: tensor([1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0])

构建模型

class MyModel(nn.Module):def __init__(self):super(MyModel, self).__init__()# 加载bert模型self.bert = AutoModel.from_pretrained("bert-base-uncased")# 最后的预测层self.predictor = nn.Sequential(nn.Linear(768, 256),nn.ReLU(),nn.Linear(256, 1),nn.Sigmoid())def forward(self, src):""":param src: 分词后的推文数据"""# 将src直接序列解包传入bert,因为bert和tokenizer是一套的,所以可以这么做。# 得到encoder的输出,用最前面[CLS]的输出作为最终线性层的输入outputs = self.bert(**src).last_hidden_state[:, 0, :]# 使用线性层来做最终的预测return self.predictor(outputs)
model = MyModel()
model = model.to(device)
model(inputs.to(device))
tensor([[0.5121],[0.5032],[0.5032],[0.4913],[0.4941],[0.4924],[0.5204],[0.4764],[0.5025],[0.5145],[0.4916],[0.4909],[0.4891],[0.5333],[0.4967],[0.4951]], device='cuda:0', grad_fn=<SigmoidBackward0>)

训练模型

接下来开始正式训练模型,首先定义出损失函数和优化器。因为是二分类问题,用Binary Cross Entropy就行:

criteria = nn.BCELoss()
optimizer = torch.optim.Adam(model.parameters(), lr=3e-5)

这个学习率是我测试出来的,之前用的3e-4,发现怎么都不收敛。看来学习率确实很重要。

# 由于inputs是字典类型的,定义一个辅助函数帮助to(device)
def to_device(dict_tensors):result_tensors = {}for key, value in dict_tensors.items():result_tensors[key] = value.to(device)return result_tensors

定义一个验证方法,获取到验证集的精准率和loss。

def validate():model.eval()total_loss = 0.total_correct = 0for inputs, targets in validation_loader:inputs, targets = to_device(inputs), targets.to(device)outputs = model(inputs)loss = criteria(outputs.view(-1), targets.float())total_loss += float(loss)correct_num = (((outputs >= 0.5).float() * 1).flatten() == targets).sum()total_correct += correct_numreturn total_correct / len(validation_dataset), total_loss / len(validation_dataset)

开始训练:

# 首先将模型调成训练模式
model.train()# 清空一下cuda缓存
if torch.cuda.is_available():torch.cuda.empty_cache()# 定义几个变量,帮助打印loss
total_loss = 0.
# 记录步数
step = 0# 记录在验证集上最好的准确率
best_accuracy = 0# 开始训练
for epoch in range(epochs):model.train()for i, (inputs, targets) in enumerate(train_loader):# 从batch中拿到训练数据inputs, targets = to_device(inputs), targets.to(device)# 传入模型进行前向传递outputs = model(inputs)# 计算损失loss = criteria(outputs.view(-1), targets.float())loss.backward()optimizer.step()optimizer.zero_grad()total_loss += float(loss)step += 1if step % log_per_step == 0:print("Epoch {}/{}, Step: {}/{}, total loss:{:.4f}".format(epoch+1, epochs, i, len(train_loader), total_loss))total_loss = 0del inputs, targets# 一个epoch后,使用过验证集进行验证accuracy, validation_loss = validate()print("Epoch {}, accuracy: {:.4f}, validation loss: {:.4f}".format(epoch+1, accuracy, validation_loss))torch.save(model, model_dir / f"model_{epoch}.pt")# 保存最好的模型if accuracy > best_accuracy:torch.save(model, model_dir / f"model_best.pt")best_accuracy = accuracy
Epoch 1/100, Step: 49/429, total loss:28.4544
Epoch 1/100, Step: 99/429, total loss:22.8545
Epoch 1/100, Step: 149/429, total loss:21.7493
。。。略
Epoch 10/100, Step: 288/429, total loss:3.1754
Epoch 10/100, Step: 338/429, total loss:3.3069
Epoch 10/100, Step: 388/429, total loss:1.8836
Epoch 10, accuracy: 0.8292, validation loss: 0.0561

模型使用

加载最好的模型,然后按照Kaggle的要求组装csv文件。

model = torch.load(model_dir / f"model_best.pt")
model = model.eval()

构造测试集的dataloader。测试集是不包含target的。

test_dataset = MyDataset('test')
test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False, collate_fn=collate_fn)

将测试数据送入模型,得到结果,最后组装成Kaggle要求数据结构:

results = []
for inputs, ids in tqdm(test_loader):outputs = model(inputs.to(device))outputs = (outputs >= 0.5).int().flatten().tolist()ids = ids.tolist()results = results + [(id, result) for result, id in zip(outputs, ids)]
with open(dataset_dir / 'results.csv', 'w', encoding='utf-8') as f:f.write('id,target\n')for id, result in results:f.write(f"{id},{result}\n")
print("Finished!")
Finished!

拿着结果去Kaggle上试一下吧,看看你能得多少分。我这边跑了10个Epoch,最终得到了0.83573的分数,还行。

Pytorch入门实战(7):基于BERT实现文本隐喻二分类(Kaggle入门题目)相关推荐

  1. 复盘:基于attention的多任务多模态情绪情感识别,基于BERT实现文本情感分类(pytorch实战)

    复盘:基于attention机制的多任务多模态情绪情感识别(pytorch实战),基于BERT实现文本情感分类 提示:系列被面试官问的问题,我自己当时不会,所以下来自己复盘一下,认真学习和总结,以应对 ...

  2. 自然语言处理NLP星空智能对话机器人系列:深入理解Transformer自然语言处理 基于BERT模型微调实现句子分类

    自然语言处理NLP星空智能对话机器人系列:深入理解Transformer自然语言处理 基于BERT模型微调实现句子分类 目录 基于BERT模型微调实现句子分类案例实战 Installing the H ...

  3. 阅读笔记——基于机器学习的文本情感多分类的学习与研究

    文章目录 1 文章简介 2 文本情感分类概述 3 文本情感多分类项目设计与实现 3.1 数据处理 3.2 特征选取 3.3 线性逻辑回归模型 3.4 朴素贝叶斯模型 4 项目结果与分析 5 总结 1 ...

  4. bert中文情感分析二分类任务详解

    查看GPU版本和使用情况 import torch if torch.cuda.is_available():device = torch.device("cuda")print( ...

  5. 基于Tensorflow的英文评论二分类CNN模型

    基于Tensorflow的英文评论二分类模型 前言 经过机器学习生成的模型,可以判断英语的肯定或否定含义,减轻了人的工作量,使得对大量意见进行归集,判断成为可能 ==>源代码Github下载 导 ...

  6. 我的实践:pytorch框架下基于BERT实现文本情感分类

    当前,在BERT等预训练模型的基础上进行微调已经成了NLP任务的一个定式了.为了了解BERT怎么用,在这次实践中,我实现了一个最简单的NLP任务,即文本情感分类. 文章目录 1.基于BERT进行情感分 ...

  7. 基于Bert的文本情感分类

    详细代码已上传到github: click me 摘  要:     情感分类是对带有感情色彩的主观性文本进行分析.推理的过程,即分析说话人的态度,推断其所包含的情感类别.传统机器学习在处理情感分类问 ...

  8. 二分类问题:基于BERT的文本分类实践!附完整代码

    Datawhale 作者:高宝丽,Datawhale优秀学习者 寄语:Bert天生适合做分类任务.文本分类有fasttext.textcnn等多种方法,但在Bert面前,就是小巫见大巫了. 推荐评论展 ...

  9. 搜出来的文本:基于BERT的文本采样

    ©PaperWeekly 原创 · 作者|苏剑林 单位|追一科技 研究方向|NLP.神经网络 从这一篇开始,我们就将前面所介绍的采样算法应用到具体的文本生成例子中.而作为第一个例子,我们将介绍如何利用 ...

  10. 基于sigmoid的文本多标签分类模型代码实现

    sigmoid一般是用来做二分类的,它是将一个标量的数字转换成[0,1]之间的一个概率值,如果概率值大于0.5, 则判定为是某个分类,否则则不是某个分类,公式如下: 本文基于sigmoid做了一个将一 ...

最新文章

  1. Android 源码编译相关
  2. 【我们都爱Paul Hegarty】斯坦福IOS8公开课个人笔记32 NSNotification
  3. mysql基础(一) 编译安装mysql5.5
  4. CNN中的卷积操作的参数数计算
  5. Java异常处理和常用类
  6. 领域模型命名规约【PO,VO,POJO,BO,DTO,DO,JavaBean】
  7. vscode编辑器 装JavaScript Standard Style 遇到不检测代码问题
  8. 【华为云技术分享】玩转华为物联网IoTDA服务系列三-自动售货机销售分析场景示例
  9. 每次编译要改名_华为突然在欧洲注册鸿蒙:正式改名方舟!与安卓竞争海外市场...
  10. html5回到顶部代码,JS返回顶部实例代码
  11. word中如何设置页码从任意页开始
  12. 6月刊精彩文章推荐:圆桌共话数据库
  13. js 对象拼接的方法 数组 Map Set
  14. The JSP specification requires that an attribute name is preceded by whitespace出现错误
  15. ubuntu 14.04 E450c 连不上网问题
  16. 专业的在线考试系统,快考题,全面聚集多场景考试业务
  17. Redis应用项目---抢红包功能(四)
  18. float与定位脱离文档流布局规则
  19. python太阳花代码_python太阳花绘制代码教程
  20. 【 FPGA 】超声波测距小实验(四):数码管显示测距结果

热门文章

  1. google jib容器打包工具
  2. 干货:学编程适合用什么配置的电脑?
  3. 为什么说 Mybatis 是半自动 ORM 映射工具?它与全自动的区别在哪里?
  4. 胡灵 c语言,清华作业们男女主角现身
  5. php打开后自动关闭,蜂窝数据打开了又自动关闭怎么办
  6. Electron 创建任务栏图标以及任务栏图标右键菜单
  7. Statistic模块管理统计功能,用于提供应用内统计的能力,支持统计和分析用户属性和用户行为数据。通过plus.statistic可获取统计管理对象
  8. Anaconda报错:Conda SSL Error: OpenSSL appears to be unavailable on this machine. OpenSSL is required t
  9. CF888G - Xor-MST(顺带学习Borůvka算法)
  10. 无线通信基础知识13:数字通信之信道编码