PyTorch实现文本分类(非常详细)
要使用 PyTorch 实现文本分类,可以执行以下步骤:
本节将设计一个简单的网络来对文本文档进行分类。将使用 torchtext 模块提供的 AG NEWS 数据集。除此之外,还将使用 torchtext 中提供的词汇构建和其他实用程序。
首先,加载 torchtext 提供的 AG NEWS 数据集。该数据集包含 4 个不同新闻类别的文本文档。数据集分为训练数据集和测试数据集。
然后,用来自训练数据集和测试数据集的数据表征(Token)填充词汇表。通过数据 get_tokenizer() 方法从 torchtext.data 模块获得第一个初始化的分词器。初始化一个简单的分词器,用于分隔单词和标点符号。
初始化分词器后,使用 torchtext.vocab 模块中提供的 build_vocab_from_iterator() 函数填充词汇表。该函数以迭代器作为输入,每次调用迭代器时,迭代器都会返回一个表征列表。我们创建了一个迭代器作为一个简单的函数,它以数据集列表作为输入。然后,它循环遍历每个数据集及其文本示例,产生使用分词器为每个示例生成的表征列表。还要求函数使用 <UNK> 表征作为特殊表征,词汇表中不存在的表征将被映射到该表征。
接下来,创建将在训练过程中使用的训练和测试数据加载程序。创建批处理大小为256的数据加载程序。两个数据加载程序都接收可调用的 collate_fn 参数。此函数负责对一批文本文档进行向量化。实现代码如下:
该网络有 3 个线性层,分别具有 128、64 和 4 个输出单元。我们已经将 relu 激活,应用于前两个线性层的输出。该网络采用 PyTorch 的 Sequential API 进行设计。实现代码如下:
对于每个回合,它使用训练数据加载器分批循环训练数据,该加载器为每个批次返回矢量化数据及其标签。对于每个批次,我们执行前向传递网络以进行预测、计算损失(使用预测和实际目标标签)、计算梯度并更新网络参数。该函数还记录每个批次的损失,并在每个回合结束时打印平均训练损失。我们还创建了另一个辅助函数,它以输入模型、损失函数和验证数据加载器来计算验证损失和准确性。
将回合数初始化为 8,将学习率初始化为 0.0001。然后,初始化交叉熵损失、文本分类器网络和 Adam 优化器。最后,用必要的参数调用训练程序来进行训练。通过查看每个回合结束时的损失和准确性值,可以得出结论,我们的模型在文本分类任务中做得很好。可以执行各种超参数微调,以进一步提高网络的性能。
- 预处理文本数据:包括将文本标记为单个单词或子单词,将其转换为数字表示,并创建词汇表;
- 准备数据集:将数据集拆分为训练集、验证集和测试集。将文本数据转换为张量或数值表示,这些张量或数值表示可以输入模型中;
- 设计模型架构:定义用于文本分类的神经网络架构。通常包括使用词嵌入来表示单词,然后是一个或多个层,如卷积层或递归层,最后是用于实现分类的完全连接层;
- 训练模型:使用训练集来训练模型,包括正向传播、使用合适的损失函数(如交叉熵)计算损失,以及使用优化器(如 Adam 或 SGD)反向传播以更新模型的参数;
- 评估模型:使用验证集来评估训练模型的性能。计算准确性、精确度、召回率或 F1 分数等指标,以评估模型的性能;
- 测试模型:使用测试集评估模型在没见过的数据上的性能。这将使用户了解模型的泛化能力。
本节将设计一个简单的网络来对文本文档进行分类。将使用 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)