使用Bert模型优化Padding策略:加速文本分类训练

摘要

本文探讨了如何通过优化Padding策略,提高基于Bert的文本分类模型的训练速度。我们比较了两种不同的Padding方式:一种是将输入数据统一填充到最大长度512,另一种是只将每个Batch中的数据填充到Batch中最长的样本长度。

通过实验结果证明,后者显著减少了训练时间,且不影响模型的性能。

介绍

在本节中,我们研究 🤗 Transformers 中标记器的功能。分析 batch 数据 的padding。
下述 tokenizer 代码,会把每一条数据,padding 到最大长度 512。

tokenized_inputs = tokenizer(
    item["text"],
    max_length=512,
    padding="max_length",
    truncation=True,
)

下述代码仅仅进行 tokenize 化,只进行截断,但暂时还 不进行padding。把 padding 操作留到 后续的 batch 数据处理上,这样只需要padding 到 batch 数据里最长的数据长度,而无需 padding 到512。由于减少了input_ids的数据长度,所以在一定程度上可以加快模型的训练和推理速度。

tokenized_inputs = tokenizer(
    item["text"],
    max_length=512,
    truncation=True,
)

导包

import os
import evaluate
import numpy as np
from datasets import load_dataset
from transformers import (
    Trainer,
    TrainingArguments,
    AutoModelForSequenceClassification,
    AutoTokenizer,
    DataCollatorWithPadding,
)
from dataclasses import dataclass

如果无法连接huggingface,利用c l a s h 的代理。

os.environ['HTTP_PROXY'] = 'http://127.0.0.1:7890'
os.environ['HTTPS_PROXY'] = 'http://127.0.0.1:7890'

模型

使用 Bert 模型,进行实验。

model_name = "bert-base-chinese"

bert = AutoModelForSequenceClassification.from_pretrained(
    model_name,
    trust_remote_code=True,
    num_labels=2,
)

tokenizer = AutoTokenizer.from_pretrained(model_name)

dataset

使用 huggingface 平台的 中文二分类数据集。

ds = load_dataset("lansinuote/ChnSentiCorp")

使用 transformers 的 DataCollatorWithPadding。

data_collator = DataCollatorWithPadding(tokenizer=tokenizer)
def tokenize_func_pad(item):
    global tokenizer
    tokenized_inputs = tokenizer(
        item["text"],
        max_length=512,
        truncation=True,
        padding="max_length"
    )
    return tokenized_inputs
batch_dataset_pad = ds["train"].map(
    tokenize_func_pad, remove_columns=["text"]
)
batch_dataset_pad.set_format("torch")
batch_dataset_pad["input_ids"].shape

输出结果:

torch.Size([9600, 512])

batch_dataset_pad["input_ids"].shapetorch.Size([9600, 512]),每一条数据都padding 到512长度。这里面会包含大量的<pad>。

只需要在每一个 batch 数据中,把其中所有的样本 padding 到这一个batch最长的样本长度即可。无需把所有的样本都 padding 到 512。

def tokenize_func(item):
    global tokenizer
    tokenized_inputs = tokenizer(
        item["text"],
        max_length=512,
        truncation=True,
    )
    return tokenized_inputs
batch_dataset = ds["train"].map(
	tokenize_func, 
	remove_columns=["text"]
)

data = [batch_dataset[i] for i in range(16)]
data_collator(data)["input_ids"].shape

输出结果:

torch.Size([16, 126])

上述结果展示了,把当前batch里的数据,padding到当前batch的最大长度126。

data_collator 可传递给 DataLoader 和 Trainer

BertCLS

下述是 Bert 封装好的一个工具类。方便读者使用,训练模型、评估、预测。

@dataclass
class BertCLS:
    def __init__(
        self,
        model,
        tokenizer,
        train_dataset=None,
        eval_dataset=None,
        output_dir="output",
        epoch=3,
    ):
        self.model = model
        self.tokenizer = tokenizer
        self.train_dataset = train_dataset
        self.eval_dataset = eval_dataset

        self.data_collator = DataCollatorWithPadding(tokenizer=tokenizer)

        self.args = self.get_args(output_dir, epoch)

        self.trainer = Trainer(
            model=self.model,
            args=self.args,
            train_dataset=self.train_dataset,
            eval_dataset=self.eval_dataset,
            data_collator=self.data_collator,
            # compute_metrics=compute_metrics,
            tokenizer=tokenizer,
        )

    def get_args(self, output_dir, epoch):
        if self.eval_dataset:
            args = TrainingArguments(
                output_dir=output_dir,
                evaluation_strategy="epoch",
                save_strategy="epoch",
                save_total_limit=3,
                learning_rate=2e-5,
                num_train_epochs=epoch,
                weight_decay=0.01,
                per_device_train_batch_size=32,
                per_device_eval_batch_size=16,
                # logging_steps=16,
                save_safetensors=True,
                overwrite_output_dir=True,
                load_best_model_at_end=True,
            )
        else:
            args = TrainingArguments(
                output_dir=output_dir,
                evaluation_strategy="no",
                save_strategy="epoch",
                save_total_limit=3,
                learning_rate=2e-5,
                num_train_epochs=epoch,
                weight_decay=0.01,
                per_device_train_batch_size=32,
                per_device_eval_batch_size=16,
                # logging_steps=16,
                save_safetensors=True,
                overwrite_output_dir=True,
                # load_best_model_at_end=True,
            )
        return args

    def set_args(self, args):
        """
        从外部重新设置 TrainingArguments,args 更新后,trainer也进行更新
        """
        self.args = args

        self.trainer = Trainer(
            model=self.model,
            args=self.args,
            train_dataset=self.train_dataset,
            eval_dataset=self.eval_dataset,
            data_collator=self.data_collator,
            # compute_metrics=compute_metrics,
            tokenizer=self.tokenizer,
        )

    def train(self, epoch=None, over_write=False):
        if epoch:
            self.args.num_train_epochs = epoch
            
        best_model_path = os.path.join(self.args.output_dir, "best_model")

        if over_write or not os.path.exists(best_model_path):
            self.trainer.train()
            self.trainer.save_model(best_model_path)
        else:
            print(
                f"预训练权重 {best_model_path} 已存在,且over_write={over_write}。不启动模型训练!"
            )

    def eval(self, eval_dataset):
        predictions = self.trainer.predict(eval_dataset)
        preds = np.argmax(predictions.predictions, axis=-1)
        metric = evaluate.load("glue", "mrpc")
        return metric.compute(predictions=preds, references=predictions.label_ids)

    def pred(self, pred_dataset):
        predictions = self.trainer.predict(pred_dataset)
        preds = np.argmax(predictions.predictions, axis=-1)
        return pred_dataset.add_column("pred", preds)

速度对比

只给 BertCLS 训练数据集,开始训练模型。如果要看模型训练的评估结果,输入评估数据集即可。

padding 到最大长度:

BertCLS(bert, tokenizer, batch_dataset_pad, epoch=1).train()
 [300/300 03:09, Epoch 1/1]

padding 到每个batch的最大长度:

BertCLS(bert, tokenizer, batch_dataset, epoch=1).train()
 [300/300 02:19, Epoch 1/1]

padding 到 512个长度,训练3分9秒结束。如果只padding到每个batch的最大长度,训练2分19秒结束。

代码开源地址

https://github.com/JieShenAI/csdn/blob/main/24/09/tokenizer_pad/pad_vs.ipynb

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
使用BERT训练模型进行中文文本分类是一种常见的应用。下面我将以基于PyTorch的方式进行介绍。 步骤一:准备数据 首先,我们需要准备用于训练和测试的文本数据集。可以选择一个适合任务的中文文本分类数据集,例如THUCNews等。将数据集划分为训练集和测试集。 步骤二:安装和导入依赖 在使用PyTorch进行BERT模型文本分类之前,需要安装相关的软件包。首先安装transformers库,该库提供了许多预训练BERT模型。另外,还需要安装torch和tqdm库用于训练和进度条显示。 步骤三:加载预训练模型 使用transformers库加载预训练的中文BERT模型,例如'bert-base-chinese'。可以通过以下代码进行加载: ``` from transformers import BertTokenizer, BertForSequenceClassification # 加载预训练模型 tokenizer = BertTokenizer.from_pretrained('bert-base-chinese') model = BertForSequenceClassification.from_pretrained('bert-base-chinese', num_labels=num_labels) ``` 这里需要根据具体的文本分类任务设置num_labels参数,表示分类的类别数。 步骤四:预处理数据 对训练集和测试集的文本进行预处理。这包括将文本转换为BERT模型所需的输入格式,即将文本转化为token ids,并将文本序列padding到相同的长度。 步骤五:定义训练和评估循环 定义训练和评估模型的循环。在每个训练batch中,将输入传递给BERT模型,获取模型的预测结果。然后计算损失并进行反向传播优化模型参数。 步骤六:训练模型 使用准备好的训练集对模型进行训练。根据任务需要选择合适的优化器和学习率,设置训练的epochs和batch size等参数。在训练过程中,可以通过打印损失和精度等指标来监控训练进展。 步骤七:评估模型 使用准备好的测试集对训练好的模型进行评估。计算模型在测试集上的准确率、精确率、召回率等指标,以评估模型的性能。 步骤八:保存和加载模型 可以选择保存训练好的模型以备后续使用。可以使用torch.save方法保存模型,并使用torch.load方法加载模型使用BERT训练模型进行中文文本分类可以较好地捕捉词义和语义的特征,提升分类任务的性能。通过以上步骤,可以实现一个简单的中文文本分类模型。当然,根据实际需求,还可以进行模型调优、模型融合等进一步的优化操作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jieshenai

为了遇见更好的文章

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值