首页 > 编程笔记 > Python笔记 阅读:26

PyTorch实现文本分类(非常详细)

要使用 PyTorch 实现文本分类,可以执行以下步骤:
  1. 预处理文本数据:包括将文本标记为单个单词或子单词,将其转换为数字表示,并创建词汇表;
  2. 准备数据集:将数据集拆分为训练集、验证集和测试集。将文本数据转换为张量或数值表示,这些张量或数值表示可以输入模型中;
  3. 设计模型架构:定义用于文本分类的神经网络架构。通常包括使用词嵌入来表示单词,然后是一个或多个层,如卷积层或递归层,最后是用于实现分类的完全连接层;
  4. 训练模型:使用训练集来训练模型,包括正向传播、使用合适的损失函数(如交叉熵)计算损失,以及使用优化器(如 Adam 或 SGD)反向传播以更新模型的参数;
  5. 评估模型:使用验证集来评估训练模型的性能。计算准确性、精确度、召回率或 F1 分数等指标,以评估模型的性能;
  6. 测试模型:使用测试集评估模型在没见过的数据上的性能。这将使用户了解模型的泛化能力。

本节将设计一个简单的网络来对文本文档进行分类。将使用 torchtext 模块提供的 AG NEWS 数据集。除此之外,还将使用 torchtext 中提供的词汇构建和其他实用程序。

本节的主要目的是介绍如何使用 PyTorch 和 torchtext 模块设计文本分类网络。

准备数据集

接下来逐步对文本数据集进行向量化,为神经网络做好准备。将文本数据转换为神经网络所需的实数向量列表。为了做到这一点,首先用数据表征填充词汇表,然后创建数据加载程序,每次调用时返回向量化数据。使用词频方法来向量化数据。

首先,加载 torchtext 提供的 AG NEWS 数据集。该数据集包含 4 个不同新闻类别的文本文档。数据集分为训练数据集和测试数据集。
from torch.utils.data import DataLoader
train_dataset, test_dataset = torchtext.datasets.AG_NEWS()
target_classes = ["World", "Sports", "Business", "Sci/Tec"]

然后,用来自训练数据集和测试数据集的数据表征(Token)填充词汇表。通过数据 get_tokenizer() 方法从 torchtext.data 模块获得第一个初始化的分词器。初始化一个简单的分词器,用于分隔单词和标点符号。

初始化分词器后,使用 torchtext.vocab 模块中提供的 build_vocab_from_iterator() 函数填充词汇表。该函数以迭代器作为输入,每次调用迭代器时,迭代器都会返回一个表征列表。我们创建了一个迭代器作为一个简单的函数,它以数据集列表作为输入。然后,它循环遍历每个数据集及其文本示例,产生使用分词器为每个示例生成的表征列表。还要求函数使用 <UNK> 表征作为特殊表征,词汇表中不存在的表征将被映射到该表征。
from torchtext.data import get_tokenizer
from torchtext.vocab import build_vocab_from_iterator

tokenizer = get_tokenizer("basic_english")

def build_vocab(datasets):
    for dataset in datasets:
        for _, text in dataset:
            yield tokenizer(text)

vocab = build_vocab_from_iterator(
    build_vocab([train_dataset, test_dataset]),
    specials=["<UNK>"]
)
vocab.set_default_index(vocab["<UNK>"])

接下来,创建将在训练过程中使用的训练和测试数据加载程序。创建批处理大小为256的数据加载程序。两个数据加载程序都接收可调用的 collate_fn 参数。此函数负责对一批文本文档进行向量化。实现代码如下:
from sklearn.feature_extraction.text import TfidfVectorizer, CountVectorizer
from torch.utils.data import DataLoader
from torchtext.data.functional import to_map_style_dataset

vectorizer = CountVectorizer(
    vocabulary=vocab.get_itos(),
    tokenizer=tokenizer
)

def vectorize_batch(batch):
    Y, X = list(zip(*batch))
    X = vectorizer.transform(X).todense()
    return torch.tensor(X, dtype=torch.float32), torch.tensor(Y) - 1

train_dataset, test_dataset = torchtext.datasets.AG_NEWS()
train_dataset, test_dataset = to_map_style_dataset(train_dataset), to_map_style_dataset(test_dataset)

train_loader = DataLoader(
    train_dataset,
    batch_size=256,
    collate_fn=vectorize_batch
)
test_loader = DataLoader(
    test_dataset,
    batch_size=256,
    collate_fn=vectorize_batch
)

定义网络

接下来使用 PyTorch 设计一个简单的线性层神经网络,使用它对文本文档进行分类。该网络将矢量化数据作为输入并返回预测。

该网络有 3 个线性层,分别具有 128、64 和 4 个输出单元。我们已经将 relu 激活,应用于前两个线性层的输出。该网络采用 PyTorch 的 Sequential API 进行设计。实现代码如下:
from torch import nn
from torch.nn import functional as F

class TextClassifier(nn.Module):
    def __init__(self):
        super(TextClassifier, self).__init__()
        self.seq = nn.Sequential(
            nn.Linear(len(vocab), 128),
            nn.ReLU(),
            nn.Linear(128, 64),
            nn.ReLU(),
            nn.Linear(64, 4)
        )

    def forward(self, X_batch):
        return self.seq(X_batch)

text_classifier = TextClassifier()

for X, Y in train_loader:
    Y_preds = text_classifier(X)
    print(Y_preds.shape)
    break

训练网络

接下来将对前面定义的网络进行训练。为了训练网络,设计了一个简单的函数,该函数将在调用时执行训练。该函数将模型、损失函数、优化器、训练数据加载程序、验证数据加载程序和回合数作为输入,然后执行训练循环若干次。

对于每个回合,它使用训练数据加载器分批循环训练数据,该加载器为每个批次返回矢量化数据及其标签。对于每个批次,我们执行前向传递网络以进行预测、计算损失(使用预测和实际目标标签)、计算梯度并更新网络参数。该函数还记录每个批次的损失,并在每个回合结束时打印平均训练损失。我们还创建了另一个辅助函数,它以输入模型、损失函数和验证数据加载器来计算验证损失和准确性。
from tqdm import tqdm
from sklearn.metrics import accuracy_score
import gc

def CalcValLossAndAccuracy(model, loss_fn, val_loader):
    with torch.no_grad():
        Y_shuffled, Y_preds, losses = [], [], []
        for X, Y in val_loader:
            preds = model(X)
            loss = loss_fn(preds, Y)
            losses.append(loss.item())
            Y_shuffled.append(Y)
            Y_preds.append(preds.argmax(dim=-1))
        Y_shuffled = torch.cat(Y_shuffled)
        Y_preds = torch.cat(Y_preds)
        print("Valid Loss : {:.3f}".format(torch.tensor(losses).mean()))
        print("Valid Acc : {:.3f}".format(accuracy_score(Y_shuffled.detach().numpy(),
                                                          Y_preds.detach().numpy())))

def TrainModel(model, loss_fn, optimizer, train_loader, val_loader, epochs=10):
    for i in range(1, epochs + 1):
        losses = []
        for X, Y in tqdm(train_loader):
            Y_preds = model(X)
            loss = loss_fn(Y_preds, Y)
            losses.append(loss.item())
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()
        print("Train Loss : {:.3f}".format(torch.tensor(losses).mean()))
        CalcValLossAndAccuracy(model, loss_fn, val_loader)

将回合数初始化为 8,将学习率初始化为 0.0001。然后,初始化交叉熵损失、文本分类器网络和 Adam 优化器。最后,用必要的参数调用训练程序来进行训练。通过查看每个回合结束时的损失和准确性值,可以得出结论,我们的模型在文本分类任务中做得很好。可以执行各种超参数微调,以进一步提高网络的性能。
from torch.optim import Adam

epochs = 8
learning_rate = 1e-4
loss_fn = nn.CrossEntropyLoss()
text_classifier = TextClassifier()
optimizer = Adam(text_classifier.parameters(), lr=learning_rate)
TrainModel(text_classifier, loss_fn, optimizer, train_loader, test_loader, epochs)

相关文章