Bert 在 OCNLI 训练微调

0 资料

【数据集微调】

阿里天池比赛 微调BERT的数据集(“任务1:OCNLI–中文原版自然语言推理”)

数据集地址:https://tianchi.aliyun.com/competition/entrance/531841/information

由于这个比赛已经结束,原地址提交不了榜单看测试结果,请参照下面的信息,下载数据集、提交榜单测试。

15.4. 自然语言推断与数据集:https://zh-v2.d2l.ai/chapter_natural-language-processing-applications/natural-language-inference-and-dataset.html

15.7. 自然语言推断:微调BERT:https://zh-v2.d2l.ai/chapter_natural-language-processing-applications/natural-language-inference-bert.html#id3

保姆级教程,用PyTorch和BERT进行文本分类:https://zhuanlan.zhihu.com/p/524487313

b站视频:
https://www.bilibili.com/video/BV1rz421279G/

1 预训练权重

在国内,一般是手动下载预训练权重,而非网络自动下载。

我们将用到 chinese-macbert-base 这个预训练文件,下载网址如下:

https://huggingface.co/hfl/chinese-macbert-base/tree/main

除了叉掉的,其余都要下载。
在这里插入图片描述

2 wandb

pip install wandb

WandB 是一个用于实验跟踪、版本控制和结果可视化的工具,主要用于机器学习项目。
wandb使用教程(一):基础用法:https://zhuanlan.zhihu.com/p/493093033

3 Bert-OCNLI

3.1 目录结构

在这里插入图片描述

3.2 导入的库

import os
import torch
from torch import nn
import pandas as pd
from transformers import BertModel, BertTokenizer
from torch.optim import Adam
from tqdm import tqdm

3.3 数据集

自然语言推断

自然语言推断(natural language inference)主要研究 假设(hypothesis)是否可以从前提(premise)中推断出来, 其中两者都是文本序列。 换言之,自然语言推断决定了一对文本序列之间的逻辑关系。这类关系通常分为三种类型:

蕴涵(entailment):假设可以从前提中推断出来。

矛盾(contradiction):假设的否定可以从前提中推断出来。

中性(neutral):所有其他情况。

自然语言推断也被称为识别文本蕴涵任务。 例如,下面的一个文本对将被贴上“蕴涵”的标签,因为假设中的“表白”可以从前提中的“拥抱”中推断出来。

前提:两个女人拥抱在一起。

假设:两个女人在示爱。

下面是一个“矛盾”的例子,因为“运行编码示例”表示“不睡觉”,而不是“睡觉”。

前提:一名男子正在运行Dive Into Deep Learning的编码示例。

假设:该男子正在睡觉。

第三个例子显示了一种“中性”关系,因为“正在为我们表演”这一事实无法推断出“出名”或“不出名”。

前提:音乐家们正在为我们表演。

假设:音乐家很有名。

自然语言推断一直是理解自然语言的中心话题。它有着广泛的应用,从信息检索到开放领域的问答。为了研究这个问题,我们将首先研究一个流行的自然语言推断基准数据集。

数据集路径

# 数据集路径
data_dir = 'OCNLI/data/ocnli'

读取数据集

# 读ocnli,两个参数,data_dir是数据集的路径,is_train为bool类型,True代表训练,False代表验证
def read_ocnli(data_dir, is_train):
    # 将ocnli解析为前提、假设、标签
    # labels_map是标签映射,0、1、2代表三类,3代表无法分类(或者应该去除的数据)。
    labels_map = {'entailment':0, 'neutral':1, 'contradiction':2, '-': 3}
    file_name = os.path.join(data_dir, 'train.3k.json' if is_train else 'dev.json')
    rows = pd.read_json(file_name, lines=True)
    
    premises = [sentence1 for sentence1 in rows['sentence1'] ]  # 前提
    hypotheses = [sentence2 for sentence2 in rows['sentence2'] ] # 假设
    # if label != '-' 是为了去除无法分类的标签
    labels = [labels_map[label] for label in rows['label'] if label != '-'] # 标签
    return premises, hypotheses, labels
    

数据集样例展示

# 样例展示
train_data = read_ocnli(data_dir, is_train=True)
for x0, x1, y in zip(train_data[0][:3], train_data[1][:3], train_data[2][:3]):
    print("前提:", x0)
    print("假设:", x1)
    print("标签:", y)

结果:

前提: 现在,我代表国务院,向大会报告政府工作,请予审议,并请全国政协委员提出意见
假设: 全国政协委员无权提出建议
标签: 2
前提: 不过以后呢,两年增加一次工资.
假设: 多年之后工资很高
标签: 1
前提: 一万块,嗯那头盔要八千.
假设: 说话的人很有钱
标签: 1

数据集类别统计

# 类别数据统计
val_data = read_ocnli(data_dir, is_train=False)

label_set = [0, 1, 2]

for data in [train_data, val_data]:
    print([
        [
            row for row in data[2]
        ].count(i) for i in label_set
    ])

结果:

[974, 1054, 966]
[947, 1103, 900]

数据集类

tokenizer = BertTokenizer.from_pretrained('bert-base-chinese')
class OCNLI_Dataset(torch.utils.data.Dataset):
    def __init__(self, dataset):
        sentence1 = [sentence1 for sentence1 in dataset[0]]
        sentence2 = [sentence2 for sentence2 in dataset[1]]
        # 用 _ 将前提和假设拼接在一起,但这应该不是好的做法
        sentence1_2 = ['{}_{}'.format(a, b) for a, b in zip(sentence1, sentence2)]
        self.texts = [tokenizer(
            sentence, 
            padding='max_length', 
            # bert最大可以设置到512,对OCNLI的统计计算中,
            # 发现所有数据没有超过128,max_length越大,计算量越大
            max_length = 128, 
            truncation=True,
            return_tensors="pt"
        ) for sentence in sentence1_2 ] 
        self.labels = torch.tensor(dataset[2])
    
    def __len__(self):
        return len(self.labels)
    
    def __getitem__(self, idx):
        return self.texts[idx], self.labels[idx]

加载数据

train_set = OCNLI_Dataset(read_ocnli(data_dir, True))
test_set = OCNLI_Dataset(read_ocnli(data_dir, False))
print(len(train_set))
# for train_input, train_label in train_set:
#     print(train_input)
#     print(train_label)
#     input()

结果:

3000

3.4 Bert

class BertClassifier(nn.Module):
    def __init__(self, dropout=0.5):
        super(BertClassifier, self).__init__()
        self.bert = BertModel.from_pretrained('bert-base-chinese')
        self.dropout = nn.Dropout(dropout)
        self.linear = nn.Linear(768, 3) # 这里的3代表输出的类别
        self.relu = nn.ReLU()

    def forward(self, input_id, mask):
        _, pooled_output = self.bert(input_ids= input_id, attention_mask=mask,return_dict=False)
        dropout_output = self.dropout(pooled_output)
        linear_output = self.linear(dropout_output)
        final_layer = self.relu(linear_output)
        return final_layer

3.4 训练

def train(model, train_data, val_data, learning_rate, epochs):
    # 通过Dataset类获取训练和验证集
    train, val = OCNLI_Dataset(train_data), OCNLI_Dataset(val_data)
    # DataLoader根据batch_size获取数据,训练时选择打乱样本
    train_dataloader = torch.utils.data.DataLoader(train, batch_size=32, shuffle=True)
    val_dataloader = torch.utils.data.DataLoader(val, batch_size=32)
    # 判断是否使用GPU
    use_cuda = torch.cuda.is_available()
    device = torch.device("cuda" if use_cuda else "cpu")
    # 定义损失函数和优化器
    criterion = nn.CrossEntropyLoss()
    optimizer = Adam(model.parameters(), lr=learning_rate)

    if use_cuda:
        model = model.cuda()
        criterion = criterion.cuda()
    # 开始进入训练循环
    for epoch_num in range(epochs):
        # 定义两个变量,用于存储训练集的准确率和损失
        total_acc_train = 0
        total_loss_train = 0
        # 进度条函数tqdm
        for train_input, train_label in tqdm(train_dataloader):
            train_label = train_label.to(device)
            mask = train_input['attention_mask'].to(device)
            input_id = train_input['input_ids'].squeeze(1).to(device)
            # 通过模型得到输出
            output = model(input_id, mask)
            # 计算损失
            batch_loss = criterion(output, train_label)
            # input()
            total_loss_train += batch_loss.item()
            # print("total_loss_train:",total_loss_train)
            # 计算精度
            acc = (output.argmax(dim=1) == train_label).sum().item()
            total_acc_train += acc
            # 模型更新
            model.zero_grad()
            batch_loss.backward()
            optimizer.step()
            # ------ 验证模型 -----------
            # 定义两个变量,用于存储验证集的准确率和损失
            total_acc_val = 0
            total_loss_val = 0
            # 不需要计算梯度
            with torch.no_grad():
                # 循环获取数据集,并用训练好的模型进行验证
                for val_input, val_label in val_dataloader:
                    # 如果有GPU,则使用GPU,接下来的操作同训练
                    val_label = val_label.to(device)
                    mask = val_input['attention_mask'].to(device)
                    input_id = val_input['input_ids'].squeeze(1).to(device)
  
                    output = model(input_id, mask)

                    batch_loss = criterion(output, val_label)
                    total_loss_val += batch_loss.item()
                    
                    acc = (output.argmax(dim=1) == val_label).sum().item()
                    total_acc_val += acc

    
        print(
            f'''Epochs: {epoch_num + 1} 
          | Train Loss: {total_loss_train / len(train): .3f} 
          | Train Accuracy: {total_acc_train / len(train): .3f} 
          | Val Loss: {total_loss_val / len(train): .3f} 
          | Val Accuracy: {total_acc_val / len(train): .3f}''')     
        print("total_loss_train:",total_loss_train)
        print("total_acc_train:",total_acc_train)
        print("total_loss_val:",total_loss_val)
        print("total_acc_val:",total_acc_val)
        print("len(train_data):",len(train))          
EPOCHS = 50
model = BertClassifier()
LR = 1e-6
train(model, read_ocnli(data_dir, True), read_ocnli(data_dir, False), LR, EPOCHS)

在这里插入图片描述

4 训练微调结果

3k

10k

50k

  • 27
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
BERT(Bidirectional Encoder Representations from Transformers)是一种预训练语言模型,可以用于各种自然语言处理任务的微调。下面是BERT训练微调的简要介绍: BERT训练BERT的预训练是指在大规模文本上训练出一个通用的语言模型,然后将这个模型微调到各种具体的NLP任务上。预训练BERT包含两个阶段:Masked Language Model(MLM)和Next Sentence Prediction(NSP)。在MLM阶段,部分输入单词被随机替换成[Mask]标记,模型需要预测这些[Mask]标记对应的原始单词;在NSP阶段,模型需要判断两个句子是否是连续的。BERT的预训练代码可以使用谷歌官方实现的TensorFlow版本或者Hugging Face开发的PyTorch版本。 BERT微调BERT微调是指在任务特定的数据集上微调训练好的BERT模型微调步骤包括数据处理、模型配置、模型微调模型评估。在数据处理阶段,需要将任务特定数据集转换成适合BERT输入的格式;在模型配置阶段,需要根据任务需求选择合适的模型结构、超参数和优化器;在模型微调阶段,需要用微调数据集对模型进行训练;在模型评估阶段,需要使用测试集对微调后的模型进行评估。BERT微调代码可以使用各种框架实现,如PyTorch、TensorFlow等。 如果您想学习更多关于BERT训练微调的内容,我建议您查看谷歌官方论文和Hugging Face官方文档。此外,您也可以在GitHub上找到各种不同的BERT实现和教程。如果您需要具体的代码示例,可以告诉我您所使用的框架和任务类型,我可以为您提供相关参考。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值