1. 引言
自然语言处理(NLP)在过去几年中迅速发展。其中,文本分类也是很重要的一环。今天,我们将介绍如何使用 PyTorch 和 TorchText 进行文本分类。
2. 数据集
在这个文本分类的实例中,我们将使用 IMDb 数据集。这个数据集包含了 50,000 条电影评论,其中 25,000 条是在训练集中,另外 25,000 条在测试集中。
在 PyTorch 中,我们可以使用 `torchtext` 轻松地对数据集进行处理。
2.1 安装 torchtext
我们可以使用以下命令来安装 `torchtext` 模块:
pip install torchtext
2.2 下载 IMDb 数据集
我们可以使用以下命令来下载 IMDb 数据集:
wget https://ai.stanford.edu/~amaas/data/sentiment/aclImdb_v1.tar.gz
3. 数据预处理
在 `torchtext` 中,我们可以使用 `Field` 对象进行数据预处理。
我们需要考虑的主要是文本的预处理。在这个例子中,我们需要对原始 IMDb 数据集进行以下处理:
- 移除 HTML 标签;
- 将所有字符转换为小写字母;
- 根据空格划分单词;
3.1 定义预处理规则
在这里,我们将定义 `Field` 类型的对象,以规定文本预处理的方式。我们将使用 `TEXT` 和 `LABEL` 两个类型的对象。`TEXT` 表示数据集中的文本数据,`LABEL` 表示数据集中的标签数据。
from torchtext.legacy.data import Field, TabularDataset
# 定义字段
TEXT = Field(sequential=True, lower=True, tokenize='spacy')
LABEL = Field(sequential=False, use_vocab=False)
# 构建数据集
train, test = TabularDataset.splits(
path = '.',
train = 'train.csv',
test = 'test.csv',
format = 'csv',
fields=[('label', LABEL), ('text', TEXT)],
skip_header = True)
3.2 创建数据迭代器
我们需要使用一个数据迭代器来遍历数据集中的样本。在这里,我们将使用 `BucketIterator` 迭代器。这个迭代器将返回数据集中的一个迭代器,其中每一批数据的长度都是相等的。
from torchtext.legacy.data import BucketIterator
# 创建数据迭代器
train_iter, test_iter = BucketIterator.splits((train, test), batch_size=32, sort_key=lambda x: len(x.text), repeat=False)
4. 构建模型
在这个实例中,我们将使用一个简单的卷积神经网络(CNN)。CNN 已经被广泛应用于文本分类任务。
在 PyTorch 中,我们可以通过继承 `nn.Module` 类来构建自己的模型。
4.1 定义模型
这里是我们模型的定义。CNN 由一个卷积层、一个最大池层和两个全连接层组成。
import torch.nn as nn
import torch.nn.functional as F
class CNN(nn.Module):
def __init__(self, vocab_size, embed_dim, num_filters, filter_sizes, output_dim, dropout):
super().__init__()
self.embedding = nn.Embedding(vocab_size, embed_dim)
self.convs = nn.ModuleList([
nn.Conv2d(in_channels=1,
out_channels=num_filters,
kernel_size=(fs, embed_dim))
for fs in filter_sizes
])
self.fc = nn.Linear(len(filter_sizes)*num_filters, output_dim)
self.dropout = nn.Dropout(dropout)
def forward(self, text):
embedded = self.embedding(text)
embedded = embedded.unsqueeze(1)
conved = [F.relu(conv(embedded)).squeeze(3) for conv in self.convs]
pooled = [F.max_pool1d(conv, conv.shape[2]).squeeze(2) for conv in conved]
cat = self.dropout(torch.cat(pooled, dim=1))
return self.fc(cat)
4.2 设置模型参数
接下来,我们设置模型的参数。在这里,我们需要指定词汇表的大小、嵌入层的维度、卷积层的数量及其大小、全连接层的输出维度和 droupout 的概率。
VOCAB_SIZE = len(TEXT.vocab)
EMBED_DIM = 100
NUM_FILTERS = 100
FILTER_SIZES = [3,4,5]
OUTPUT_DIM = 1
DROPOUT = 0.5
model = CNN(VOCAB_SIZE, EMBED_DIM, NUM_FILTERS, FILTER_SIZES, OUTPUT_DIM, DROPOUT)
4.3 查看模型结构
我们可以使用以下命令来查看模型的结构:
print(model)
5. 训练
在这个实例中,我们将使用二元交叉熵损失函数和随机梯度下降(SGD)来训练我们的模型。
5.1 定义损失函数和优化器
我们将使用二元交叉熵损失函数和随机梯度下降(SGD)作为我们的损失函数和优化器。
import torch.optim as optim
criterion = nn.BCEWithLogitsLoss()
optimizer = optim.SGD(model.parameters(), lr=0.01)
5.2 训练模型
我们定义了一个 `train` 函数,来训练我们的模型。在每一轮训练之后,我们会计算一次准确率,并将模型保存。
在每次训练迭代中,我们需要将模型的梯度清零,我们可以使用以下命令来实现:
optimizer.zero_grad()
在得到了模型的预测结果之后,我们需要计算预测结果与真实结果之间的误差。误差可以使用下面的命令来计算:
loss = criterion(predictions, batch.label.float())
在计算出误差后,我们可以通过以下命令来计算梯度并更新参数:
loss.backward()
optimizer.step()
from torch.utils.tensorboard import SummaryWriter
def train(model, iterator, optimizer, criterion):
writer = SummaryWriter('runs/imdb')
epoch_loss = 0
epoch_acc = 0
model.train()
for batch in iterator:
optimizer.zero_grad()
predictions = model(batch.text).squeeze(1)
loss = criterion(predictions, batch.label.float())
acc = binary_accuracy(predictions, batch.label.float())
loss.backward()
optimizer.step()
epoch_loss += loss.item()
epoch_acc += acc.item()
return epoch_loss / len(iterator), epoch_acc / len(iterator)
def binary_accuracy(preds, y):
rounded_preds = torch.round(torch.sigmoid(preds))
correct = (rounded_preds == y).float()
acc = correct.sum() / len(correct)
return acc
5.3 进行测试和评估
在训练完成后,我们需要测试我们的模型。我们将使用 `eval` 函数来使模型在测试模式下运行。
在每一轮中,我们将计算在测试数据集上的准确率和损失。
def evaluate(model, iterator, criterion):
epoch_loss = 0
epoch_acc = 0
model.eval()
with torch.no_grad():
for batch in iterator:
predictions = model(batch.text).squeeze(1)
loss = criterion(predictions, batch.label.float())
acc = binary_accuracy(predictions, batch.label.float())
epoch_loss += loss.item()
epoch_acc += acc.item()
return epoch_loss / len(iterator), epoch_acc / len(iterator)
6. 训练模型
我们使用上面定义的 `train` 函数来训练我们的模型。
我们将对模型进行 5 轮训练,并使用 `temperature=0.6` 来控制我们的预测。预测结果会传递给 `sigmoid` 函数,来获取处于 0 到 1 之间的概率值。
N_EPOCHS = 5
best_valid_loss = float('inf')
for epoch in range(N_EPOCHS):
train_loss, train_acc = train(model, train_iter, optimizer, criterion)
valid_loss, valid_acc = evaluate(model, test_iter, criterion)
if valid_loss < best_valid_loss:
best_valid_loss = valid_loss
torch.save(model.state_dict(), 'tut4-model.pt')
print(f'Epoch: {epoch+1:02}')
print(f'Train Loss: {train_loss:.3f} | Train Acc: {train_acc*100:.2f}%')
print(f'Val. Loss: {valid_loss:.3f} | Val. Acc: {valid_acc*100:.2f}%')
7. 结论
在这个实例中,我们介绍了如何使用 PyTorch 和 TorchText 来进行文本分类。我们使用了一个简单的卷积神经网络(CNN)模型,对 IMDb 数据集进行了训练,并在测试集上进行了评估。
我们还介绍了如何使用 `torchtext` 来对数据集进行处理,并使用 `BucketIterator` 迭代器来遍历数据集。
最后,我们通过在测试集上计算准确率和损失,来对我们的模型进行了评估。
以上是 PyTorch 和 TorchText 中的文本分类实例,希望能帮助大家更好地理解文本分类的技术原理。