目录

SST-2 二元文本分类与 XLM-RoBERTa 模型

作者: Parmeet Bhatia

概述

本教程演示了如何使用预训练的 XLM-RoBERTa(XLM-R)模型在 SST-2 二分类数据集上训练文本分类器。 我们将展示如何使用 torchtext 库来:

  1. 为 XLM-R 模型构建文本预处理管道

  2. 读取SST-2数据集,并使用文本和标签转换进行变换

  3. 使用预训练的XLM-R编码器实例化分类模型

常用导入

import torch
import torch.nn as nn

DEVICE = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu")

数据转换

像 XLM-R 这样的模型无法直接处理原始文本。训练这些模型的第一步是将输入文本转换为张量(数值)形式,以便模型能够进行预测。一种标准的文本处理方式是:

  1. 分词文本

  2. 将 token 转换为(整数)ID

  3. 添加任何特殊 token ID

XLM-R 使用 SentencePiece 模型进行文本分词。下面,我们使用预训练的 SentencePiece 模型以及相应的词汇表来构建文本预处理管道,使用 torchtext 的 transforms。 这些 transforms 是通过 torchtext.transforms.Sequential() 进行流水线化的,这与 torch.nn.Sequential() 类似, 但可以被 torchscript 化。请注意,transforms 支持批量和非批量文本输入,即可以传递单个句子或句子列表。

import torchtext.transforms as T
from torch.hub import load_state_dict_from_url

padding_idx = 1
bos_idx = 0
eos_idx = 2
max_seq_len = 256
xlmr_vocab_path = r"https://download.pytorch.org/models/text/xlmr.vocab.pt"
xlmr_spm_model_path = r"https://download.pytorch.org/models/text/xlmr.sentencepiece.bpe.model"

text_transform = T.Sequential(
    T.SentencePieceTokenizer(xlmr_spm_model_path),
    T.VocabTransform(load_state_dict_from_url(xlmr_vocab_path)),
    T.Truncate(max_seq_len - 2),
    T.AddToken(token=bos_idx, begin=True),
    T.AddToken(token=eos_idx, begin=False),
)


from torch.utils.data import DataLoader

或者我们可以使用与预训练模型一起提供的转换工具,该工具可以一键完成上述所有操作。

text_transform = XLMR_BASE_ENCODER.transform()

数据集

torchtext 提供了多个标准的自然语言处理数据集。完整列表请参阅https://pytorch.org/text/stable/datasets.html 文档。这些数据集使用可组合的 torchdata 数据管道构建,因此支持使用用户定义的函数和转换进行标准的流程控制和映射/转换。下面,我们演示如何使用文本和标签处理转换来预处理 SST-2 数据集。

注意

使用datapipes目前仍有一些注意事项。如果您希望将此示例扩展以包括洗牌、多进程或分布式学习,请参阅此说明以获取进一步的指示。

from torchtext.datasets import SST2

batch_size = 16

train_datapipe = SST2(split="train")
dev_datapipe = SST2(split="dev")


# Transform the raw dataset using non-batched API (i.e apply transformation line by line)
def apply_transform(x):
    return text_transform(x[0]), x[1]


train_datapipe = train_datapipe.map(apply_transform)
train_datapipe = train_datapipe.batch(batch_size)
train_datapipe = train_datapipe.rows2columnar(["token_ids", "target"])
train_dataloader = DataLoader(train_datapipe, batch_size=None)

dev_datapipe = dev_datapipe.map(apply_transform)
dev_datapipe = dev_datapipe.batch(batch_size)
dev_datapipe = dev_datapipe.rows2columnar(["token_ids", "target"])
dev_dataloader = DataLoader(dev_datapipe, batch_size=None)

或者我们也可以使用批处理 API(即对整个批次进行变换)。

def batch_transform(x):
    return {"token_ids": text_transform(x["text"]), "target": x["label"]}


train_datapipe = train_datapipe.batch(batch_size).rows2columnar(["text", "label"])
train_datapipe = train_datapipe.map(lambda x: batch_transform)
dev_datapipe = dev_datapipe.batch(batch_size).rows2columnar(["text", "label"])
dev_datapipe = dev_datapipe.map(lambda x: batch_transform)

模型准备

torchtext 提供了最先进的预训练模型,可以用于下游 NLP 任务的微调。 下面我们将使用标准基础架构的预训练 XLM-R 编码器,并附加一个分类头来进行 SST-2 二元分类任务的微调。 我们将使用库中的标准分类头,但用户可以定义自己的适当任务头并将其附加到预训练编码器上。 有关可用预训练模型的更多详细信息,请参阅https://pytorch.org/text/main/models.html 的文档。

num_classes = 2
input_dim = 768

from torchtext.models import RobertaClassificationHead, XLMR_BASE_ENCODER

classifier_head = RobertaClassificationHead(num_classes=num_classes, input_dim=input_dim)
model = XLMR_BASE_ENCODER.get_model(head=classifier_head)
model.to(DEVICE)

训练方法

现在我们定义标准优化器和训练准则,以及一些用于训练和评估的辅助函数。

import torchtext.functional as F
from torch.optim import AdamW

learning_rate = 1e-5
optim = AdamW(model.parameters(), lr=learning_rate)
criteria = nn.CrossEntropyLoss()


def train_step(input, target):
    output = model(input)
    loss = criteria(output, target)
    optim.zero_grad()
    loss.backward()
    optim.step()


def eval_step(input, target):
    output = model(input)
    loss = criteria(output, target).item()
    return float(loss), (output.argmax(1) == target).type(torch.float).sum().item()


def evaluate():
    model.eval()
    total_loss = 0
    correct_predictions = 0
    total_predictions = 0
    counter = 0
    with torch.no_grad():
        for batch in dev_dataloader:
            input = F.to_tensor(batch["token_ids"], padding_value=padding_idx).to(DEVICE)
            target = torch.tensor(batch["target"]).to(DEVICE)
            loss, predictions = eval_step(input, target)
            total_loss += loss
            correct_predictions += predictions
            total_predictions += len(target)
            counter += 1

    return total_loss / counter, correct_predictions / total_predictions

训练

现在我们已经具备了训练分类模型的所有要素。请注意,我们可以直接迭代数据集对象,而无需使用DataLoader。由于我们应用了批处理数据管道,预处理的数据集将自动生成批次数据。在分布式训练中,我们需要使用DataLoader来处理数据分片。

num_epochs = 1

for e in range(num_epochs):
    for batch in train_dataloader:
        input = F.to_tensor(batch["token_ids"], padding_value=padding_idx).to(DEVICE)
        target = torch.tensor(batch["target"]).to(DEVICE)
        train_step(input, target)

    loss, accuracy = evaluate()
    print("Epoch = [{}], loss = [{}], accuracy = [{}]".format(e, loss, accuracy))

输出

100%|██████████|5.07M/5.07M [00:00<00:00, 40.8MB/s]
Downloading: "https://download.pytorch.org/models/text/xlmr.vocab.pt" to /root/.cache/torch/hub/checkpoints/xlmr.vocab.pt
100%|██████████|4.85M/4.85M [00:00<00:00, 16.8MB/s]
Downloading: "https://download.pytorch.org/models/text/xlmr.base.encoder.pt" to /root/.cache/torch/hub/checkpoints/xlmr.base.encoder.pt
100%|██████████|1.03G/1.03G [00:26<00:00, 47.1MB/s]
Epoch = [0], loss = [0.2629831412637776], accuracy = [0.9105504587155964]

脚本的总运行时间: ( 0 分钟 0.000 秒)

通过 Sphinx-Gallery 生成的画廊

文档

访问 PyTorch 的全面开发人员文档

查看文档

教程

获取面向初学者和高级开发人员的深入教程

查看教程

资源

查找开发资源并解答您的问题

查看资源