Transformers实战02-BERT预训练模型微调

news2024/11/25 15:30:19

文章目录

  • 简介
  • BERT的基本原理
    • 分词
    • 模型输出
  • BERT预训练的方法
  • BERT模型微调
    • 加载数据集
      • Dataset
      • DataLoader
    • 训练模型
      • 构建模型
      • tqdm使用
      • 训练模型
      • 模型预测

简介

BERT(Bidirectional Encoder Representations from Transformers)是一种基于 Transformer 模型的预训练语言表示方法,由Google研究团队于2018年提出。BERT 通过在大规模文本语料上进行无监督的预训练,学习了通用的语言表示,并且在各种自然语言处理任务中取得了显著的性能提升。

BERT仅使用了Transformer架构的Encoder部分。BERT自2018年由谷歌发布后,在多种NLP任务中(例如QA、文本生成、情感分析等等)都实现了更好的结果。

“Word2vec与GloVe都有一个特点,就是它们是上下文无关(context-free)的词嵌入。所以它们没有解决:一个单词在不同上下文中代表不同的含义的问题。例如,对于单词bank,它在不同的上下文中,有银行、河畔这种差别非常大的含义。BERT的出现,解决了这个问题。

BERT 的主要特点包括:

  1. 双向性:BERT 使用双向 Transformer 模型来处理输入序列,从而能够同时考虑上下文的信息,而不仅仅是单向的上下文信息。这种双向性使得 BERT 能够更好地理解句子中的语义和语境。

  2. 预训练-微调框架:BERT 使用了预训练-微调的方法。首先,在大规模文本语料上进行无监督的预训练,通过 Masked Language Model(MLM)和 Next Sentence Prediction(NSP)任务学习语言表示;然后,在特定的下游任务上微调模型参数,使其适应于特定的任务,如文本分类、命名实体识别等。

  3. Transformer 模型:BERT 基于 Transformer 模型结构,其中包括多层的编码器,每个编码器由自注意力机制和前馈神经网络组成。这种结构能够有效地捕获输入序列中的长距离依赖关系,有助于提高模型在各种自然语言处理任务中的性能。

  4. 多层表示:BERT 提供了多层的语言表示,使得用户可以根据具体任务选择不同层的表示进行应用。较底层的表示通常更加接近原始输入,而较高层的表示则更加抽象,包含了更多的语义信息。

  5. 开放源代码:BERT 的源代码和预训练模型已经在 GitHub 上开放,使得研究人员和开发者可以基于 BERT 进行进一步的研究和应用开发。

BERT 通过预训练大规模文本语料上的通用语言表示,以及在各种下游任务上的微调,有效地提高了自然语言处理任务的性能,并且成为了当前领域内最具影响力的预训练模型之一。

BERT的基本原理

BERT基于的是Transformer模型,并且仅使用Transformer模型的Encoder部分。在Transformer模型中,Encoder的输入是一串序列,输出的是对序列中每个字符的表示。同样,在BERT中,输入的是一串序列,输出的是也是对应序列中每个单词的编码。
以“He got bit by Python”为例,BERT的输入输出如下图所示:
在这里插入图片描述
其中输入为序列“He got bit by Python”,输出的是对每个单词的编码 R w o r d R_{word} Rword。这样在经过了BERT处理后,即得到了对每个单词包含的上下文表示 R w o r d R_{word} Rword

分词

from transformers import AutoModel, BertTokenizer
model_name="bert-base-chinese" #bert-base-uncased
model=AutoModel.from_pretrained(model_name)
tokenizer=BertTokenizer.from_pretrained(model_name)
print(type(model),type(tokenizer))
sequence = ["我出生在湖南A阳,我得家在深圳.","我得儿子是廖X谦"]
#输出中包含两个键 input_ids 和 attention_mask,其中 input_ids 对应分词之后的 tokens 映射到的数字编号列表,而 attention_mask 则是用来标记哪些 tokens #是被填充的(这里“1”表示是原文,“0”表示是填充字符)。
print(tokenizer(sequence, padding=True, truncation=True, return_tensors="pt",pair=True))
#将输入切分为词语、子词或者符号(例如标点符号),统称为 tokens;
print(tokenizer.tokenize(sequence[0]),len(tokenizer.tokenize(sequence[0])))
#我们通过 convert_tokens_to_ids() 将切分出的 tokens 转换为对应的 token IDs:
print(tokenizer.convert_tokens_to_ids(tokenizer.tokenize(sequence[0])))
#可以通过 encode() 函数将这两个步骤合并,并且 encode() 会自动添加模型需要的特殊 token,例如 BERT 分词器会分别在序列的首尾添加[CLS] 和 [SEP]
print(tokenizer.encode(sequence[0]))
#解码还原文字,可以看到encode前后加了[CLS] 和 [SEP]
print(tokenizer.decode(tokenizer.encode(sequence[1])))

输出

<class 'transformers.models.bert.modeling_bert.BertModel'> <class 'transformers.models.bert.tokenization_bert.BertTokenizer'>
{'input_ids': tensor([[ 101, 2769, 1139, 4495, 1762, 3959, 1298, 2277, 7345,  117, 2769, 2533,
         2157, 1762, 3918, 1766,  119,  102],
        [ 101, 2769, 2533, 1036, 2094, 3221, 2445, 3813, 6472,  102,    0,    0,
            0,    0,    0,    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, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
        [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0]])}
['我', '出', '生', '在', '湖', '南', 'A', '阳', ',', '我', '得', '家', '在', '深', '圳', '.'] 16
[2769, 1139, 4495, 1762, 3959, 1298, 2277, 7345, 117, 2769, 2533, 2157, 1762, 3918, 1766, 119]
[101, 2769, 1139, 4495, 1762, 3959, 1298, 2277, 7345, 117, 2769, 2533, 2157, 1762, 3918, 1766, 119, 102]
[CLS] 我 得 儿 子 是 廖 X 谦 [SEP]

模型输出

#这里演示最终输出隐藏状态得输出
from transformers import AutoModel,AutoTokenizer
model_name="bert-base-chinese" #bert-base-uncased
model=AutoModel.from_pretrained(model_name)
tokenizer=BertTokenizer.from_pretrained(model_name)
raw_inputs = [
    "I've been waiting for a HuggingFace course my whole life.",
    "I hate this so much!",
]
inputs = tokenizer(raw_inputs, padding=True, truncation=True, return_tensors="pt")
outputs = model(**inputs)
print("词个数",len(tokenizer.encode(raw_inputs[0])))
"""
在BERT模型中,last_hidden_state 的形状是 [batch_size, sequence_length, hidden_size],其中:
 batch_size 表示批量大小,即输入的样本数量。在你的例子中,batch_size 是 2,表示你有两个句子。
 sequence_length 表示序列长度,即输入文本中词元的数量。在你的例子中,sequence_length 是 19,表示每个句子包含 19 个词元,我爱中国,我就是一个词元,爱也是一个词元。
 hidden_size 表示隐藏状态的维度,通常是模型的隐藏层的大小。在BERT-base模型中,hidden_size 是 768,表示每个词元的隐藏状态是一个包含 768 个值的向量。
"""
print(outputs.last_hidden_state.shape)

输出:torch.Size([2, 19, 768])

BERT预训练的方法

BERT的预训练语料库使用的是Toronto BookCorpus和Wikipedia数据集。在准备训练数据时,首先从语料库中采样2条句子,例如Sentence-A与Sentence-B。这里需要注意的是:2条句子的单词之和不能超过512个。对于采集的这些句子,50%为两个句子是相邻句子,另50%为两个句子毫无关系。

假设采集了以下2条句子:

Beijing is a beautiful city
I love Beijing

对这2条句子先做分词:

Tokens = [ [CLS], Beijing, is, a, beautiful, city, [SEP], I, love, Beijing, [SEP] ]

然后,以15%的概率遮挡单词,并遵循80%-10%-10%的规则。假设遮挡的单词为city,则:

Tokens = [ [CLS], Beijing, is, a, beautiful, [MASK], [SEP], I, love, Beijing, [SEP] ]

接下来将Tokens送入到BERT中,并训练BERT预测被遮挡的单词,同时也要预测这2条句子是否为相邻(句子2是句子1的下一条句子)。也就是说,BERT是同时训练Masked Language Modeling和NSP任务。

BERT的训练参数是:1000000个step,每个batch包含256条序列(256 * 512个单词 = 128000单词/batch)。使用的是Adam,learning rate为1e-4、β1 = 0.9、β2 = 0.999。L2正则权重的衰减参数为0.01。对于learning rete,前10000个steps使用了rate warmup,之后开始线性衰减learning rate(简单地说,就是前期训练使用一个较大的learning rate,后期开始线性减少)。对所有layer使用0.1概率的dropout。使用的激活函数为gelu,而非relu。
验证使用两条句子。

checkpoint = "bert-base-chinese"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
raw_inputs = [
    "拼多多得货物真是差劲.",
    "我喜欢天猫,天猫货物都很好",
]
raw_inputs1 = [
    "拼多多买了一件掉色衣服.",
    "我在天猫买的衣服颜色还行",
]
#允许传入两个数组,相同索引会自动通过[SEP]拼接。
inputs = tokenizer(raw_inputs,raw_inputs1, padding=True, truncation=True, return_tensors="pt")
print(tokenizer.decode(inputs.input_ids[0]))
print(tokenizer.decode(inputs.input_ids[1]))

输出

[CLS] 拼 多 多 得 货 物 真 是 差 劲. [SEP] 拼 多 多 买 了 一 件 掉 色 衣 服. [SEP] [PAD] [PAD]
[CLS] 我 喜 欢 天 猫 , 天 猫 货 物 都 很 好 [SEP] 我 在 天 猫 买 的 衣 服 颜 色 还 行 [SEP]

预测的整个过程

#演示预测的整个过程。
import torch
from transformers import AutoModelForSequenceClassification
#情感分析任务
checkpoint = "bert-base-chinese"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
model = AutoModelForSequenceClassification.from_pretrained(checkpoint)
print(type(model))
raw_inputs = [
    "拼多多得货物真是差劲.",
    "我喜欢天猫,天猫货物都很好",
]
inputs = tokenizer(raw_inputs, padding=True, truncation=True, return_tensors="pt")
outputs = model(**inputs)
#将分词的词反编码出来
print(tokenizer.decode(inputs.input_ids[0]),tokenizer.decode(inputs.input_ids[1]))
#"Logits" 是指模型在分类问题中输出的未经过 softmax 或 sigmoid 函数处理的原始预测值。
print("分类输出形状:",outputs.logits.shape)
print("分类输出:",outputs.logits)
#经过softmax就是预测的结果了
predictions = torch.nn.functional.softmax(outputs.logits, dim=-1)
#预测的每一行是一个句子,第一列表示积极的概率,第二列表示不积极的概率
print("预测结果:",predictions)
#有两种分类0表示积极,1表示不积极
print("label和索引:",print(model.config.id2label))

输出

[CLS] 拼 多 多 得 货 物 真 是 差 劲. [SEP] [PAD] [PAD] [CLS] 我 喜 欢 天 猫 , 天 猫 货 物 都 很 好 [SEP]
分类输出形状: torch.Size([2, 2])
分类输出: tensor([[0.4789, 1.0043],
        [0.2907, 0.7432]], grad_fn=<AddmmBackward0>)
预测结果: tensor([[0.3716, 0.6284],
        [0.3888, 0.6112]], grad_fn=<SoftmaxBackward0>)
{0: 'LABEL_0', 1: 'LABEL_1'}

BERT模型微调

加载数据集

我们以同义句判断任务为例(每次输入两个句子,判断它们是否为同义句),带大家构建我们的第一个 Transformers 模型。我们选择蚂蚁金融语义相似度数据集 AFQMC 作为语料,它提供了官方的数据划分,训练集(train.json) / 验证集(dev.json) / 测试集(test.json)分别包含 34334 / 4316 / 3861 个句子对,标签 0 表示非同义句,1 表示同义句:

{"sentence1": "还款还清了,为什么花呗账单显示还要还款", "sentence2": "花呗全额还清怎么显示没有还款", "label": "1"}

训练集用于训练模型,验证集用于每次epoch后训练集的正确率,测试集用于验证最后生成模型的准确率。

Dataset

Pytorch 通过 Dataset 类和 DataLoader 类处理数据集和加载样本。同样地,这里我们首先继承 Dataset 类构造自定义数据集,以组织样本和标签。AFQMC 样本以 json 格式存储,因此我们使用 json 库按行读取样本,并且以行号作为索引构建数据集。

class MyDataSet(Dataset):
    def __init__(self,filePath):
        self.data={}
        current_directory = os.getcwd()
        with open(current_directory+"/dataset/"+filePath,"rt", encoding="utf-8") as f:
            for idx,line in enumerate(f):
                self.data[idx]=json.loads(line.strip())
    def __getitem__(self, item):
        return self.data[item]

    def __len__(self):
        return len(self.data)
        
train_data=MyDataSet("train.json")
dev_data=MyDataSet("dev.json")
print(dev_data[1])

输出:

{'id': 1, 'sentence1': '网商贷怎么转变成借呗', 'sentence2': '如何将网商贷切换为借呗'}

可以看到,我们编写的 AFQMC 类成功读取了数据集,每一个样本都以字典形式保存,分别以 sentence1、sentence2 和 label 为键存储句子对和标签。

如果数据集非常巨大,难以一次性加载到内存中,我们也可以继承 IterableDataset 类构建迭代型数据集:

class MyDataSetIter(IterableDataset):
    def __init__(self,filePath):
        self.filePath=filePath
    def __iter__(self):
        current_directory = os.getcwd()
        with open(current_directory+"/dataset/"+self.filePath,"rt", encoding="utf-8") as f:
            for _,line in enumerate(f):
                data=json.loads(line.strip())
                yield data
print(next(iter(MyDataSetIter("dev.json"))))       

输出:

{'sentence1': '双十一花呗提额在哪', 'sentence2': '里可以提花呗额度', 'label': '0'}

DataLoader

接下来就需要通过 DataLoader 库按批 (batch) 加载数据,并且将样本转换成模型可以接受的输入格式。对于 NLP 任务,这个环节就是将每个 batch 中的文本按照预训练模型的格式进行编码(包括 Padding、截断等操作)。

我们通过手工编写 DataLoader 的批处理函数 collate_fn 来实现。首先加载分词器,然后对每个 batch 中的所有句子对进行编码,同时把标签转换为张量格式:

#DataLoader处理数据为seq1 [SEP] seq2
from transformers import AutoTokenizer
import torch
from torch.utils.data import DataLoader
checkpoint = "bert-base-chinese"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
def collote_fn(batch_samples):
    batch_sentence_1, batch_sentence_2 = [], []
    batch_label = []
    for sample in batch_samples:
        batch_sentence_1.append(sample['sentence1'])
        batch_sentence_2.append(sample['sentence2'])
        batch_label.append(int(sample['label']))
    X = tokenizer(
        batch_sentence_1,
        batch_sentence_2,
        padding=True,
        truncation=True,
        return_tensors="pt"
    )
    y = torch.tensor(batch_label)
    return X, y
train_loader=DataLoader(train_data,batch_size=4,shuffle=False,collate_fn=collote_fn)
X,y=next(iter(train_loader))
print("label的维度",y.shape)
print("s1,s2合并的维度",X.input_ids.shape)
for idx,d in enumerate(X.input_ids):
    print("第一批次4个元素中的第{}个:{},label={}".format(idx,tokenizer.decode(X.input_ids[idx]),y[idx]))

输出

label的维度 torch.Size([4])
s1,s2合并的维度 torch.Size([4, 30])
第一批次4个元素中的第0个:[CLS] 蚂 蚁 借 呗 等 额 还 款 可 以 换 成 先 息 后 本 吗 [SEP] 借 呗 有 先 息 到 期 还 本 吗 [SEP],label=0
第一批次4个元素中的第1个:[CLS] 蚂 蚁 花 呗 说 我 违 约 一 次 [SEP] 蚂 蚁 花 呗 违 约 行 为 是 什 么 [SEP] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD],label=0
第一批次4个元素中的第2个:[CLS] 帮 我 看 一 下 本 月 花 呗 账 单 有 没 有 结 清 [SEP] 下 月 花 呗 账 单 [SEP] [PAD] [PAD] [PAD] [PAD] [PAD],label=0
第一批次4个元素中的第3个:[CLS] 蚂 蚁 借 呗 多 长 时 间 综 合 评 估 一 次 [SEP] 借 呗 得 评 估 多 久 [SEP] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD],label=0

可以看到,DataLoader 按照我们设置的 batch size 每次对 4 个样本进行编码,并且通过设置 padding=True 和 truncation=True 来自动对每个 batch 中的样本进行补全和截断。这里我们选择 BERT 模型作为 checkpoint,所以每个样本都被处理成了“了“[CLS] sen1 [SEP] sen2 [SEP]”的形式。

这种只在一个 batch 内进行补全的操作被称为动态补全 (Dynamic padding),Hugging Face 也提供了 DataCollatorWithPadding 类来进行,如果感兴趣可以自行了解。

训练模型

构建模型

常见的写法是继承 Transformers 库中的预训练模型来创建自己的模型。例如这里我们可以继承 BERT 模型(BertPreTrainedModel 类)来创建一个与上面模型结构完全相同的分类器:

#构建模型
from transformers import BertPreTrainedModel,BertModel,AutoConfig
from torch import nn
class BertForPartwiseCLs(BertPreTrainedModel):
    """
    定义模型继承自BertPreTrainedModel
    """
    def __init__(self,config):
        """
        传入config,原始镜像的config
        """
        super().__init__(config)
        #定义BertModel
        self.model=BertModel(config, add_pooling_layer=False)
        #丢弃10%
        self.dropout=nn.Dropout(config.hidden_dropout_prob)
        #全连接为2分类
        self.classifier=nn.Linear(768,2)
        #初始化权重参数
        self.post_init()
    def forward(self,input):
        #执行模型产生一个(批次,词元,隐藏神经元)的输出
        bert_output=self.model(**input)
        #输出的数据有多个词元,取第一个[CLS]词元,因为每个词元通过注意力机制都包含了和其他词的语义信息,所以只需要一个即可
        #这里句子的维度编程了[批次,1,768]
        vector_data=bert_output.last_hidden_state[:,0,:]
        vector_data=self.dropout(vector_data)
        logits=self.classifier(vector_data)
        return logits
checkpoint = "bert-base-chinese"
config=AutoConfig.from_pretrained(checkpoint)
model=BertForPartwiseCLs.from_pretrained(checkpoint,config=config)
print(model)
X,y=next(iter(train_loader))
print(model(X).shape)

输出

D:\python\evn311\Lib\site-packages\huggingface_hub\file_download.py:1132: FutureWarning: `resume_download` is deprecated and will be removed in version 1.0.0. Downloads always resume when possible. If you want to force a new download, use `force_download=True`.
  warnings.warn(
Some weights of BertForPartwiseCLs were not initialized from the model checkpoint at bert-base-chinese and are newly initialized: ['bert.classifier.bias', 'bert.classifier.weight', 'bert.model.embeddings.LayerNorm.bias', 'bert.model.embeddings.LayerNorm.weight', 'bert.model.embeddings.position_embeddings.weight', 'bert.model.embeddings.token_type_embeddings.weight', 'bert.model.embeddings.word_embeddings.weight', 'bert.model.encoder.layer.0.attention.output.LayerNorm.bias', 'bert.model.encoder.layer.0.attention.output.LayerNorm.weight', 'bert.model.encoder.layer.0.attention.output.dense.bias', 'bert.model.encoder.layer.0.attention.output.dense.weight', 'bert.model.encoder.layer.0.attention.self.key.bias', 'bert.model.encoder.layer.0.attention.self.key.weight', 'bert.model.encoder.layer.0.attention.self.query.bias', 'bert.model.encoder.layer.0.attention.self.query.weight', 'bert.model.encoder.layer.0.attention.self.value.bias', 'bert.model.encoder.layer.0.attention.self.value.weight', 'bert.model.encoder.layer.0.intermediate.dense.bias', 'bert.model.encoder.layer.0.intermediate.dense.weight', 'bert.model.encoder.layer.0.output.LayerNorm.bias', 'bert.model.encoder.layer.0.output.LayerNorm.weight', 'bert.model.encoder.layer.0.output.dense.bias', 'bert.model.encoder.layer.0.output.dense.weight', 'bert.model.encoder.layer.1.attention.output.LayerNorm.bias', 'bert.model.encoder.layer.1.attention.output.LayerNorm.weight', 'bert.model.encoder.layer.1.attention.output.dense.bias', 'bert.model.encoder.layer.1.attention.output.dense.weight', 'bert.model.encoder.layer.1.attention.self.key.bias', 'bert.model.encoder.layer.1.attention.self.key.weight', 'bert.model.encoder.layer.1.attention.self.query.bias', 'bert.model.encoder.layer.1.attention.self.query.weight', 'bert.model.encoder.layer.1.attention.self.value.bias', 'bert.model.encoder.layer.1.attention.self.value.weight', 'bert.model.encoder.layer.1.intermediate.dense.bias', 'bert.model.encoder.layer.1.intermediate.dense.weight', 'bert.model.encoder.layer.1.output.LayerNorm.bias', 'bert.model.encoder.layer.1.output.LayerNorm.weight', 'bert.model.encoder.layer.1.output.dense.bias', 'bert.model.encoder.layer.1.output.dense.weight', 'bert.model.encoder.layer.10.attention.output.LayerNorm.bias', 'bert.model.encoder.layer.10.attention.output.LayerNorm.weight', 'bert.model.encoder.layer.10.attention.output.dense.bias', 'bert.model.encoder.layer.10.attention.output.dense.weight', 'bert.model.encoder.layer.10.attention.self.key.bias', 'bert.model.encoder.layer.10.attention.self.key.weight', 'bert.model.encoder.layer.10.attention.self.query.bias', 'bert.model.encoder.layer.10.attention.self.query.weight', 'bert.model.encoder.layer.10.attention.self.value.bias', 'bert.model.encoder.layer.10.attention.self.value.weight', 'bert.model.encoder.layer.10.intermediate.dense.bias', 'bert.model.encoder.layer.10.intermediate.dense.weight', 'bert.model.encoder.layer.10.output.LayerNorm.bias', 'bert.model.encoder.layer.10.output.LayerNorm.weight', 'bert.model.encoder.layer.10.output.dense.bias', 'bert.model.encoder.layer.10.output.dense.weight', 'bert.model.encoder.layer.11.attention.output.LayerNorm.bias', 'bert.model.encoder.layer.11.attention.output.LayerNorm.weight', 'bert.model.encoder.layer.11.attention.output.dense.bias', 'bert.model.encoder.layer.11.attention.output.dense.weight', 'bert.model.encoder.layer.11.attention.self.key.bias', 'bert.model.encoder.layer.11.attention.self.key.weight', 'bert.model.encoder.layer.11.attention.self.query.bias', 'bert.model.encoder.layer.11.attention.self.query.weight', 'bert.model.encoder.layer.11.attention.self.value.bias', 'bert.model.encoder.layer.11.attention.self.value.weight', 'bert.model.encoder.layer.11.intermediate.dense.bias', 'bert.model.encoder.layer.11.intermediate.dense.weight', 'bert.model.encoder.layer.11.output.LayerNorm.bias', 'bert.model.encoder.layer.11.output.LayerNorm.weight', 'bert.model.encoder.layer.11.output.dense.bias', 'bert.model.encoder.layer.11.output.dense.weight', 'bert.model.encoder.layer.2.attention.output.LayerNorm.bias', 'bert.model.encoder.layer.2.attention.output.LayerNorm.weight', 'bert.model.encoder.layer.2.attention.output.dense.bias', 'bert.model.encoder.layer.2.attention.output.dense.weight', 'bert.model.encoder.layer.2.attention.self.key.bias', 'bert.model.encoder.layer.2.attention.self.key.weight', 'bert.model.encoder.layer.2.attention.self.query.bias', 'bert.model.encoder.layer.2.attention.self.query.weight', 'bert.model.encoder.layer.2.attention.self.value.bias', 'bert.model.encoder.layer.2.attention.self.value.weight', 'bert.model.encoder.layer.2.intermediate.dense.bias', 'bert.model.encoder.layer.2.intermediate.dense.weight', 'bert.model.encoder.layer.2.output.LayerNorm.bias', 'bert.model.encoder.layer.2.output.LayerNorm.weight', 'bert.model.encoder.layer.2.output.dense.bias', 'bert.model.encoder.layer.2.output.dense.weight', 'bert.model.encoder.layer.3.attention.output.LayerNorm.bias', 'bert.model.encoder.layer.3.attention.output.LayerNorm.weight', 'bert.model.encoder.layer.3.attention.output.dense.bias', 'bert.model.encoder.layer.3.attention.output.dense.weight', 'bert.model.encoder.layer.3.attention.self.key.bias', 'bert.model.encoder.layer.3.attention.self.key.weight', 'bert.model.encoder.layer.3.attention.self.query.bias', 'bert.model.encoder.layer.3.attention.self.query.weight', 'bert.model.encoder.layer.3.attention.self.value.bias', 'bert.model.encoder.layer.3.attention.self.value.weight', 'bert.model.encoder.layer.3.intermediate.dense.bias', 'bert.model.encoder.layer.3.intermediate.dense.weight', 'bert.model.encoder.layer.3.output.LayerNorm.bias', 'bert.model.encoder.layer.3.output.LayerNorm.weight', 'bert.model.encoder.layer.3.output.dense.bias', 'bert.model.encoder.layer.3.output.dense.weight', 'bert.model.encoder.layer.4.attention.output.LayerNorm.bias', 'bert.model.encoder.layer.4.attention.output.LayerNorm.weight', 'bert.model.encoder.layer.4.attention.output.dense.bias', 'bert.model.encoder.layer.4.attention.output.dense.weight', 'bert.model.encoder.layer.4.attention.self.key.bias', 'bert.model.encoder.layer.4.attention.self.key.weight', 'bert.model.encoder.layer.4.attention.self.query.bias', 'bert.model.encoder.layer.4.attention.self.query.weight', 'bert.model.encoder.layer.4.attention.self.value.bias', 'bert.model.encoder.layer.4.attention.self.value.weight', 'bert.model.encoder.layer.4.intermediate.dense.bias', 'bert.model.encoder.layer.4.intermediate.dense.weight', 'bert.model.encoder.layer.4.output.LayerNorm.bias', 'bert.model.encoder.layer.4.output.LayerNorm.weight', 'bert.model.encoder.layer.4.output.dense.bias', 'bert.model.encoder.layer.4.output.dense.weight', 'bert.model.encoder.layer.5.attention.output.LayerNorm.bias', 'bert.model.encoder.layer.5.attention.output.LayerNorm.weight', 'bert.model.encoder.layer.5.attention.output.dense.bias', 'bert.model.encoder.layer.5.attention.output.dense.weight', 'bert.model.encoder.layer.5.attention.self.key.bias', 'bert.model.encoder.layer.5.attention.self.key.weight', 'bert.model.encoder.layer.5.attention.self.query.bias', 'bert.model.encoder.layer.5.attention.self.query.weight', 'bert.model.encoder.layer.5.attention.self.value.bias', 'bert.model.encoder.layer.5.attention.self.value.weight', 'bert.model.encoder.layer.5.intermediate.dense.bias', 'bert.model.encoder.layer.5.intermediate.dense.weight', 'bert.model.encoder.layer.5.output.LayerNorm.bias', 'bert.model.encoder.layer.5.output.LayerNorm.weight', 'bert.model.encoder.layer.5.output.dense.bias', 'bert.model.encoder.layer.5.output.dense.weight', 'bert.model.encoder.layer.6.attention.output.LayerNorm.bias', 'bert.model.encoder.layer.6.attention.output.LayerNorm.weight', 'bert.model.encoder.layer.6.attention.output.dense.bias', 'bert.model.encoder.layer.6.attention.output.dense.weight', 'bert.model.encoder.layer.6.attention.self.key.bias', 'bert.model.encoder.layer.6.attention.self.key.weight', 'bert.model.encoder.layer.6.attention.self.query.bias', 'bert.model.encoder.layer.6.attention.self.query.weight', 'bert.model.encoder.layer.6.attention.self.value.bias', 'bert.model.encoder.layer.6.attention.self.value.weight', 'bert.model.encoder.layer.6.intermediate.dense.bias', 'bert.model.encoder.layer.6.intermediate.dense.weight', 'bert.model.encoder.layer.6.output.LayerNorm.bias', 'bert.model.encoder.layer.6.output.LayerNorm.weight', 'bert.model.encoder.layer.6.output.dense.bias', 'bert.model.encoder.layer.6.output.dense.weight', 'bert.model.encoder.layer.7.attention.output.LayerNorm.bias', 'bert.model.encoder.layer.7.attention.output.LayerNorm.weight', 'bert.model.encoder.layer.7.attention.output.dense.bias', 'bert.model.encoder.layer.7.attention.output.dense.weight', 'bert.model.encoder.layer.7.attention.self.key.bias', 'bert.model.encoder.layer.7.attention.self.key.weight', 'bert.model.encoder.layer.7.attention.self.query.bias', 'bert.model.encoder.layer.7.attention.self.query.weight', 'bert.model.encoder.layer.7.attention.self.value.bias', 'bert.model.encoder.layer.7.attention.self.value.weight', 'bert.model.encoder.layer.7.intermediate.dense.bias', 'bert.model.encoder.layer.7.intermediate.dense.weight', 'bert.model.encoder.layer.7.output.LayerNorm.bias', 'bert.model.encoder.layer.7.output.LayerNorm.weight', 'bert.model.encoder.layer.7.output.dense.bias', 'bert.model.encoder.layer.7.output.dense.weight', 'bert.model.encoder.layer.8.attention.output.LayerNorm.bias', 'bert.model.encoder.layer.8.attention.output.LayerNorm.weight', 'bert.model.encoder.layer.8.attention.output.dense.bias', 'bert.model.encoder.layer.8.attention.output.dense.weight', 'bert.model.encoder.layer.8.attention.self.key.bias', 'bert.model.encoder.layer.8.attention.self.key.weight', 'bert.model.encoder.layer.8.attention.self.query.bias', 'bert.model.encoder.layer.8.attention.self.query.weight', 'bert.model.encoder.layer.8.attention.self.value.bias', 'bert.model.encoder.layer.8.attention.self.value.weight', 'bert.model.encoder.layer.8.intermediate.dense.bias', 'bert.model.encoder.layer.8.intermediate.dense.weight', 'bert.model.encoder.layer.8.output.LayerNorm.bias', 'bert.model.encoder.layer.8.output.LayerNorm.weight', 'bert.model.encoder.layer.8.output.dense.bias', 'bert.model.encoder.layer.8.output.dense.weight', 'bert.model.encoder.layer.9.attention.output.LayerNorm.bias', 'bert.model.encoder.layer.9.attention.output.LayerNorm.weight', 'bert.model.encoder.layer.9.attention.output.dense.bias', 'bert.model.encoder.layer.9.attention.output.dense.weight', 'bert.model.encoder.layer.9.attention.self.key.bias', 'bert.model.encoder.layer.9.attention.self.key.weight', 'bert.model.encoder.layer.9.attention.self.query.bias', 'bert.model.encoder.layer.9.attention.self.query.weight', 'bert.model.encoder.layer.9.attention.self.value.bias', 'bert.model.encoder.layer.9.attention.self.value.weight', 'bert.model.encoder.layer.9.intermediate.dense.bias', 'bert.model.encoder.layer.9.intermediate.dense.weight', 'bert.model.encoder.layer.9.output.LayerNorm.bias', 'bert.model.encoder.layer.9.output.LayerNorm.weight', 'bert.model.encoder.layer.9.output.dense.bias', 'bert.model.encoder.layer.9.output.dense.weight']
You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.

BertForPartwiseCLs(
  (model): BertModel(
    (embeddings): BertEmbeddings(
      (word_embeddings): Embedding(21128, 768, padding_idx=0)
      (position_embeddings): Embedding(512, 768)
      (token_type_embeddings): Embedding(2, 768)
      (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
      (dropout): Dropout(p=0.1, inplace=False)
    )
    (encoder): BertEncoder(
      (layer): ModuleList(
        (0-11): 12 x BertLayer(
          (attention): BertAttention(
            (self): BertSelfAttention(
              (query): Linear(in_features=768, out_features=768, bias=True)
              (key): Linear(in_features=768, out_features=768, bias=True)
              (value): Linear(in_features=768, out_features=768, bias=True)
              (dropout): Dropout(p=0.1, inplace=False)
            )
            (output): BertSelfOutput(
              (dense): Linear(in_features=768, out_features=768, bias=True)
              (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
              (dropout): Dropout(p=0.1, inplace=False)
            )
          )
          (intermediate): BertIntermediate(
            (dense): Linear(in_features=768, out_features=3072, bias=True)
            (intermediate_act_fn): GELUActivation()
          )
          (output): BertOutput(
            (dense): Linear(in_features=3072, out_features=768, bias=True)
            (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
            (dropout): Dropout(p=0.1, inplace=False)
          )
        )
      )
    )
  )
  (dropout): Dropout(p=0.1, inplace=False)
  (classifier): Linear(in_features=768, out_features=2, bias=True)
)
torch.Size([4, 2])

可以看到模型输出了一个 4×2 的张量,符合我们的预期(每个样本输出 2 维的 logits 值分别表示两个类别的预测分数,batch 内共 4 个样本)。

tqdm使用

tqdm是一个Python库,用于在终端中显示进度条。它广泛应用于各种数据处理任务中,如循环、迭代器、pandas数据帧等。以下是对tqdm的简要介绍:

  • 简单易用: tqdm提供了简单直观的API,可以快速集成到代码中,只需要几行代码即可实现进度条显示。
  • 丰富的功能: tqdm不仅可以显示进度条,还可以显示预估的剩余时间、完成百分比、已处理的数据量等信息。
  • 自动检测环境: tqdm可以自动检测运行环境,在支持ANSI转义码的终端中使用动态进度条,在不支持的环境中使用静态进度条。
  • 支持各种迭代器: tqdm支持各种Python内置迭代器,如list、range、enumerate等,也支持自定义迭代器。
  • 可定制性强: tqdm提供了丰富的参数供用户自定义进度条的样式和行为,如颜色、宽度、刷新间隔等。

代码

#tqdm进度条使用
from tqdm.auto import tqdm
import time

# 创建一个迭代对象,比如一个列表
items = range(10)

# 使用tqdm来迭代这个对象,并显示进度条
for item in tqdm(items, desc='Processing'):
    # 在这里执行你的任务
    time.sleep(0.1)  # 模拟一些长时间运行的任务
# range(10) 其实就是0-9
print([i for i in range(10)])
#创建一个tqdm对象,传入得必须是range对象,range(10) 其实就是0-9
print(range(10),len(range(10)))
tdm=tqdm(range(10), desc='Processing')
for item in range(10):
     time.sleep(1)  # 模拟一些长时间运行的任务
     #更新一次,其实就是进度条加上: 1/len(range(10))
     tdm.update(1)

效果
在这里插入图片描述

训练模型

在训练模型时,我们将每一轮 Epoch 分为训练循环和验证/测试循环。在训练循环中计算损失、优化模型的参数,在验证/测试循环中评估模型的性能,与 Pytorch 类似,Transformers 库同样实现了很多的优化器,并且相比 Pytorch 固定学习率,Transformers 库的优化器会随着训练过程逐步减小学习率(通常会产生更好的效果)。例如我们前面使用过的 AdamW 优化器
完整的训练过程,可与使用colab来进行训练。

#训练模型和验证测试
#定义损失函数
from torch.nn import CrossEntropyLoss
from transformers import get_scheduler
#定义优化函数,from torch.optim import AdamW
from transformers import AdamW
from tqdm.auto import tqdm
#定义epoch训练次数
epochs = 3
#默认学习率
learning_rate = 1e-5
# batchsize
batch_size=4
#AdamW是Adam优化器的一种变体,它在Adam的基础上进行了一些改进,旨在解决Adam优化器可能引入的权重衰减问题。
optimizer=AdamW(model.parameters(),lr=1e-5)
#定义交叉熵损失函数
loss_fn=CrossEntropyLoss()
#重新初始化数据集
train_loader=DataLoader(train_data,batch_size=batch_size,shuffle=False,collate_fn=collote_fn)
dev_loader=DataLoader(MyDataSet("dev.json"),batch_size=batch_size,shuffle=False,collate_fn=collote_fn)
#总步数=epoch*批次数(总记录数train_data/一批次多少条数据batch_size)
num_training_steps = epochs * len(train_loader)
#默认情况下,优化器会线性衰减学习率,对于上面的例子,学习率会线性地从le-5 降到0
#。为了正确地定义学习率调度器,我们需要知道总的训练步数 (step),它等于训练轮数 (Epoch number) 乘以每一轮中的步数(也就是训练 dataloader 的大小)
lr_scheduler = get_scheduler(
    "linear",
    optimizer=optimizer,
    num_warmup_steps=0,
    num_training_steps=num_training_steps,
)
#初始化模型
device = 'cuda' if torch.cuda.is_available() else 'cpu'
checkpoint = "bert-base-chinese"
config=AutoConfig.from_pretrained(checkpoint)
model=BertForPartwiseCLs.from_pretrained(checkpoint,config=config).to(device)
#定义总损失
total_loss=0
#完成总batch
complete_batch_count=0
#最好的正确率
best_acc = 0.
current_directory = os.getcwd()
for step in range(epochs):
    #进入训练模式
    model.train()
    print(f"Epoch {step+1}/{epochs}\n-------------------------------")
    progress_bar=tqdm(range(len(train_loader)))
    for batch,(X,y) in enumerate(train_loader):
        X,y=X.to(device),y.to(device)
        #获取预测结果
        pred=model(X)
        #计算损失函数
        loss=loss_fn(pred,y)
        #清空梯度
        optimizer.zero_grad()
        #前向传播
        loss.backward();
        #更新模型参数
        optimizer.step();
        #学习率线性下降,必须是更新模型参数之后,函数根据设定的规则来调整学习率。这个调整需要基于当前的模型状态,包括参数、损失函数值等,所以要放在optimizer.step()之后。
        lr_scheduler.step()
        total_loss+=loss.item()
        complete_batch_count+=1
        avg_loss=total_loss/complete_batch_count
        progress_bar.set_description("loss:{}".format(avg_loss))
        progress_bar.update(1)
    #使用验证集验证模型正确性。
    #进入预测模式,当前这一次epoch训练数据的正确率
    model.eval()
    correct=0
    #加载验证集的数据
    for batch,(X,y) in enumerate(dev_loader):
        #获取预测结果
        pred=model(X)
        #因为是[[0.9,0.1],[0.3,0.4]]所以取dim=1维度上最大值的索引,概率大的索引就是预测的类别,如果和label值y相等就加起来,算个数
        correct += (pred.argmax(dim=1) == y).type(torch.float).sum().item()
    #正确/总数就是争取率
    valid_acc=correct/len(dev_loader.dataset)
    print(f"{step+1} Accuracy: {(100*valid_acc):>0.1f}%\n")
    if valid_acc > best_acc:
        best_acc = valid_acc
        print('saving new weights...\n')
        torch.save(model.state_dict(), current_directory+f'/epoch_{step+1}_valid_acc_{(100*valid_acc):0.1f}_model_weights.bin')
print("Done!")

模型预测

最后,我们加载验证集上最优的模型权重,汇报其在测试集上的性能。由于 AFQMC 公布的测试集上并没有标签,无法评估性能,这里我们暂且用验证集代替进行演示:

current_directory = os.getcwd()
model.load_state_dict(torch.load(current_directory+'/model_weights.bin'))
model.eval()
test_loader=DataLoader(test_data,batch_size=4,shuffle=False,collate_fn=collote_fn)
X,y=next(iter(test_loader))
X, y = X.to(device), y.to(device)
pred = model(X)
print(pred.argmax(1) == y)

文章部分文字引用:https://transformers.run/c2/2021-12-17-transformers-note-4/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1694111.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Redis - 优惠卷秒杀

场景分析 为了避免对数据库造成压力&#xff0c;我们在新增优惠卷的时候&#xff0c;可以将优惠卷的信息储存在Redis中&#xff0c;这样用户抢购的时候访问优惠卷信息&#xff0c;通过Redis读取信息。 抢购流程&#xff1a; 业务分析 既然在新增优惠卷的时候&#xff0c;我…

CS 下载安装详解

目录 CS简介&#xff1a; CS下载地址&#xff1a; CS的安装&#xff1a; CS简介&#xff1a; CS为目前渗透中常用的一款工具&#xff0c;它的强大在于控制windows木马&#xff0c;CS主要控制windows木马。 CS下载地址&#xff1a; 链接&#xff1a;https://pan.baidu.com/…

并发编程笔记7--并发编程基础

1、线程简介 1.1、什么是线程 现代操作系统中运行一个程序&#xff0c;会为他创建一个进程。而每一个进程中又可以创建许多个线程。现代操作系统中线程是最小的调度单元。 两者关系&#xff1a;一个线程只属于一个进程&#xff0c;而一个进程可以拥有多个线程。线程是一个轻量…

循环进阶-素数回文数的个数c++

题目描述 计算鸭请你帮忙求1到n之间&#xff08;包括 n&#xff09;&#xff0c;既是素数又是回文数的整数有多少个。 输入 一个大于1小于1000的整数n。 输出 1到n之间的素数回文数个数。 样例输入 23 样例输出 5 分析 这道题就是怎样判断素数和怎样判断回文数的结合…

clion读取文件设置为读取当前目录下的文件

1.问题 使用vs读取文件时一切正常 但是同样的代码在clion中无法正常执行 原因 原因&#xff1a;clion的源文件找不到input.txt文件的位置 需要设置工作目录&#xff0c;例如此时input.txt在当前目录下&#xff0c;那么就设置 设置当前文件的工作目录为$FileDir$即可&am…

Unity面试八股文之基础篇

文章目录 前言1. Unity的生命周期加载第一个场景Editor在第一次帧更新之前帧之间更新顺序协程销毁对象时退出时 2. Unity 协程和线程,进程的区别3. 本地坐标系 世界坐标系4. 碰撞器和触发器的区别后话 前言 开设这个栏目的博文会写一些有关unity的面试题目&#xff0c;在面试的…

清理mysql binglog文件

mysql随着使用时间的推移&#xff0c;binglog文件会越来越大&#xff0c;比如我们的oa系统&#xff0c;上线4年多了&#xff0c;最近总有磁盘空间满影响系统正常使用的情况出现。检查后发现binglog是罪归祸首。 binglog文件最好不要采用应删除的方式清理&#xff0c;如下方式可…

【数据结构与算法】之堆的应用——堆排序及Top_K问题!

目录 1、堆排序 2、Top_K问题 3、完结散花 个人主页&#xff1a;秋风起&#xff0c;再归来~ 数据结构与算法 个人格言&#xff1a;悟已往之不谏&#xff0c;知来者犹可追 克心守己&#xff0c;律己则安&#xff01; 1、堆排序 对一个无序的数组…

使用OpenCV dnn c++加载YOLOv8生成的onnx文件进行目标检测

在网上下载了60多幅包含西瓜和冬瓜的图像组成melon数据集&#xff0c;使用 LabelMe 工具进行标注&#xff0c;然后使用 labelme2yolov8 脚本将json文件转换成YOLOv8支持的.txt文件&#xff0c;并自动生成YOLOv8支持的目录结构&#xff0c;包括melon.yaml文件&#xff0c;其内容…

HACL-Net:基于MRI的胎盘植入谱诊断的分层注意力和对比学习网络

文章目录 HACL-Net: Hierarchical Attention and Contrastive Learning Network for MRI-Based Placenta Accreta Spectrum Diagnosis摘要方法实验结果 HACL-Net: Hierarchical Attention and Contrastive Learning Network for MRI-Based Placenta Accreta Spectrum Diagnosis…

世界上首位AI程序员诞生,AI将成为人类的对手吗?

3月13日&#xff0c;世界上第一位AI程序员Devin诞生&#xff0c;不仅能自主学习新技术&#xff0c;自己改Bug&#xff0c;甚至还能训练和微调自己的AI模型&#xff0c;表现已然远超GPT-4等“顶流选手”。 AI的学习速度如此之快&#xff0c;人类的教育能否跟上“机器学习”的速…

2、xss-labs之level2

1、打开页面 2、传入xss代码 payload&#xff1a;<script>alert(xss)</script>&#xff0c;发现返回<script>alert(xss)</script> 3、分析原因 打开f12&#xff0c;没什么发现 看后端源码&#xff0c;在这form表单通过get获取keyword的值赋给$str&am…

《拯救大学生课设不挂科第三期之Windows下安装Dev C++(VC 6.0上位替代)与跑通Hello World程序C/C++版教程》【官方笔记】

背景&#xff1a; 大学老师使用的VC6.0(VC 6.0)太老了&#xff0c;老师为什么用VC6.0&#xff0c;象漂亮认为是高校缺乏鲶鱼刺激。很大一部分高校已经与市场脱节&#xff0c;这也是为什么很多同学毕业后想要入行计算机基本都得自己重新回炉打造一次&#xff0c;这和高校的老旧…

OpenStack平台Keystone组件的使用

1. 规划节点 安装基础服务的服务器规划 IP地址 主机名 节点 192.168.100.10 controller Openstack控制节点 2. 基础准备 使用机电云共享的单节点的openstack系统&#xff0c;自行修改虚拟网络编辑器、网络适配器&#xff0c;系统用户名&#xff1a;root&#xff0c;密…

在Github上寻找安装ROS软件包

1、创建一个功能包 并下载git sudo apt install git 2、找到自己想在github上要克隆的包 复制此链接 3、克隆到本地 git clone 链接 4.scripts目录用于放置脚本文件和python程序 使用脚本安装编译需要的依赖库 5、下载完成后&#xff0c;在~catkin_ws目录下运行catkin_make进…

电脑键盘如何练习盲打?

电脑键盘如何练习盲打&#xff1f;盲打很简单&#xff0c;跟着我做&#xff0c;今天教会你。 请看【图1】&#xff1a; 【图1】中&#xff0c;红色方框就是8个基准键位&#xff0c;打字时我们左右手的8个手指就是放在这8个基准键位上&#xff0c;F键和J键上各有一个小突起&…

FTP协议——BFTPD安装(Linux)

1、简介 BFTPD&#xff0c;全称为 Brutal File Transfer Protocol Daemon&#xff0c;是一个用于Unix和类Unix系统的轻量级FTP服务器软件。它的设计理念是提供一个简单、快速、安全的FTP服务器解决方案&#xff0c;特别适用于需要低资源占用的环境。 2、步骤 环境&#xff1…

网络爬虫原理及其应用

你是否想知道Google 和 Bing 等搜索引擎如何收集搜索结果中显示的所有数据。这是因为搜索引擎对其档案中的所有页面建立索引&#xff0c;以便它们可以根据查询返回最相关的结果。网络爬虫使搜索引擎能够处理这个过程。 本文重点介绍了网络爬虫的重要方面、网络爬虫为何重要、其…

卷积神经网络CNN动态演示和输出特征图计算公式

目录 一、卷积运算 1、卷积&#xff08;Convolution&#xff09; 2、填充&#xff08;Padding&#xff09; &#xff08;1&#xff09;Valid Padding &#xff08;2&#xff09;Same Padding 3、步长 4、卷积核大小为什么一般为奇数奇数&#xff1f; 5、卷积核kernel和…

蓝桥杯杨辉三角

PREV-282 杨辉三角形【第十二届】【蓝桥杯省赛】【B组】 &#xff08;二分查找 递推&#xff09;&#xff1a; 解析&#xff1a; 1.杨辉三角具有对称性&#xff1a; 2.杨辉三角具有一定规律 通过观察发现&#xff0c;第一次出现的地方一定在左部靠右的位置&#xff0c;所以从…