1. 前言
本文将介绍如何使用PyTorch实现用CNN和LSTM对文本进行分类的方法。这个任务通常被认为是自然语言处理(NLP)领域中的一个经典问题,它可以用于许多不同的应用程序,例如电子邮件分类、情感分析和垃圾信息过滤等。
2. 数据预处理
2.1 数据集介绍
我们选择了一个已有的情感分析数据集IMDB影评数据集。该数据集包含50,000个来自Internet Movie Database(IMDB)的影评。对于所有的样本,我们需要将它们转换成数字形式,并将其用于训练我们的模型。
2.2 文本预处理
我们首先需要将影评转换成数字形式。这个过程被称为文本预处理。我们将使用Python的NLTK库来实现它。
2.3 分词
首先,我们将每个影评文本分成单词。我们可以使用NLTK的word_tokenize()函数来实现它:
import nltk
nltk.download('punkt')
from nltk.tokenize import word_tokenize
text = 'This is a sentence.'
tokens = word_tokenize(text)
print(tokens)
# ['This', 'is', 'a', 'sentence', '.']
2.4 移除停用词
接下来,我们需要移除停用词。停用词是在文本中频繁出现但通常不包含足够信息的单词。例如,“the”、“a”和“an”都是停用词。
我们将使用NLTK的stopwords列表来移除停用词。
nltk.download('stopwords')
from nltk.corpus import stopwords
stop_words = set(stopwords.words('english'))
filtered_tokens = []
for token in tokens:
if token.lower() not in stop_words:
filtered_tokens.append(token)
print(filtered_tokens)
# ['sentence', '.']
在这个例子中,“This”、“is”和“a”都被移除了,因为它们是停用词。
2.5 将单词转换为数字
现在我们需要将每个单词转换成数字形式,以便我们可以将它们用于训练我们的模型。我们将使用PyTorch的torchtext库来实现它。
首先,我们需要创建一个词汇表。词汇表将单词映射到数字。我们使用Field类来定义每个文本列的字段类型:
import torchtext
tokenizer = lambda x: x.split()
TEXT = torchtext.data.Field(sequential=True, tokenize=tokenizer, lower=True)
LABEL = torchtext.data.Field(sequential=False, use_vocab=False)
train, test = torchtext.datasets.IMDB(split=('train', 'test'), fields=(('text', TEXT), ('label', LABEL)))
TEXT.build_vocab(train, vectors="glove.6B.100d")
在这个例子中,我们使用了预训练的GloVe词向量来初始化词汇表。这个过程将创建一个包含训练数据中所有单词的词汇表,其中每个单词都映射到一个独一无二的数字。
2.6 创建迭代器
最后,我们需要创建迭代器,以便我们可以在训练期间从数据集中获取数据批次。我们将使用BucketIterator类来实现它。
BATCH_SIZE = 32
train_iterator, test_iterator = torchtext.data.BucketIterator.splits(
(train, test),
batch_size=BATCH_SIZE,
sort_key=lambda x: len(x.text),
device=torch.device('cuda' if torch.cuda.is_available() else 'cpu'))
在这个例子中,我们创建了两个迭代器:一个训练迭代器和一个测试迭代器。这些迭代器将采样我们的训练集和测试集,并将它们分成一批。
3. 模型架构
3.1 网络结构
我们使用一个简单的网络结构,它由CNN和LSTM组成。这个结构可以在自然语言处理(NLP)领域中得到广泛的应用,特别是在情感分析中。
我们首先使用一个卷积层来提取文本特征。这个卷积层的输出被馈送到一个LSTM层,以学习文本序列的时序信息。最后,我们将LSTM的输出馈送到一个全连接层,以获得最终的分类结果。
我们使用PyTorch建立该网络结构。具体代码如下:
import torch.nn as nn
class TextCNNLSTM(nn.Module):
def __init__(self, input_dim, embedding_dim, num_filters, filter_sizes, hidden_dim, output_dim, dropout):
super().__init__()
self.embedding = nn.Embedding(input_dim, embedding_dim)
self.convs = nn.ModuleList([
nn.Conv2d(in_channels=1, out_channels=num_filters, kernel_size=(fs, embedding_dim)) for fs in filter_sizes
])
self.lstm = nn.LSTM(embedding_dim, hidden_dim, bidirectional=True)
self.fc = nn.Linear(hidden_dim * 2, output_dim)
self.dropout = nn.Dropout(dropout)
def forward(self, text):
embedded = self.embedding(text)
embedded = embedded.unsqueeze(1)
# [batch_size, num_filters, max_sent_len - filter_sizes[n] + 1, 1]
conved = [nn.functional.relu(conv(embedded)).squeeze(3) for conv in self.convs]
# [batch_size, num_filters, max_sent_len - filter_sizes[n] + 1]
pooled = [nn.functional.max_pool1d(conv, conv.shape[2]).squeeze(2) for conv in conved]
# [batch_size, embedding_dim, max_sent_len]
lstm_input = embedded.permute(2, 0, 1)
_, (final_hidden_state, _) = self.lstm(lstm_input)
hidden = self.dropout(torch.cat((final_hidden_state[-2, :, :], final_hidden_state[-1, :, :]), dim=1))
return self.fc(hidden)
在这个例子中,我们定义了一个名为TextCNNLSTM的类。这个类继承了nn.Module类,它是所有神经网络模型的基类。我们使用__init__()方法定义网络结构。我们在该方法中定义了一个嵌入层、卷积层、LSTM层和全连接层。我们还定义了一个dropout层,用于减少过拟合。
我们在forward()方法中实现了网络结构的前向传递。我们首先需要将输入序列转换成嵌入。接下来,我们将卷积层的输出馈送到LSTM层 中,最后,我们将LSTM的输出馈送到全连接层中,以获得最终的分类结果。
3.2 模型训练
我们将使用PyTorch来训练我们的模型。具体代码如下:
import torch.optim as optim
INPUT_DIM = len(TEXT.vocab)
EMBEDDING_DIM = 100
NUM_FILTERS = 100
FILTER_SIZES = [3, 4, 5]
HIDDEN_DIM = 256
OUTPUT_DIM = 1
DROPOUT = 0.5
model = TextCNNLSTM(INPUT_DIM, EMBEDDING_DIM, NUM_FILTERS, FILTER_SIZES, HIDDEN_DIM, OUTPUT_DIM, DROPOUT)
optimizer = optim.Adam(model.parameters(), lr=1e-4)
criterion = nn.BCEWithLogitsLoss()
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model = model.to(device)
criterion = criterion.to(device)
def binary_accuracy(preds, y):
rounded_preds = torch.round(torch.sigmoid(preds))
correct = (rounded_preds == y).float()
accuracy = correct.sum() / len(correct)
return accuracy
def train(model, iterator, optimizer, criterion):
epoch_loss = 0
epoch_acc = 0
model.train()
for batch in iterator:
optimizer.zero_grad()
text, labels = batch.text, batch.label
text = text.permute(1, 0)
predictions = model(text).squeeze(1)
loss = criterion(predictions, labels.float())
acc = binary_accuracy(predictions, labels.float())
loss.backward()
optimizer.step()
epoch_loss += loss.item()
epoch_acc += acc.item()
return epoch_loss / len(iterator), epoch_acc / len(iterator)
def evaluate(model, iterator, criterion):
epoch_loss = 0
epoch_acc = 0
model.eval()
with torch.no_grad():
for batch in iterator:
text, labels = batch.text, batch.label
text = text.permute(1, 0)
predictions = model(text).squeeze(1)
loss = criterion(predictions, labels.float())
acc = binary_accuracy(predictions, labels.float())
epoch_loss += loss.item()
epoch_acc += acc.item()
return epoch_loss / len(iterator), epoch_acc / len(iterator)
在训练前,我们需要定义一些重要的函数。首先,我们需要定义binary_accuracy()函数来计算分类的准确性。其次,我们需要定义train()函数和evaluate()函数来分别训练模型和评估模型的性能。
在train()函数中,我们使用实例迭代器中的每个批次来进行训练。对于每个批次,我们将其输入到模型中,并计算预测值。然后,我们计算损失和准确性,并对模型参数进行梯度更新。
在evaluate()函数中,我们使用与train()函数相同的步骤来评估模型的性能。不过,在这个函数中,我们不会对模型参数进行梯度更新。
3.3 模型评估
在训练模型后,我们需要评估其性能。我们可以使用二元交叉熵损失函数来度量模型的性能。具体代码如下:
N_EPOCHS = 10
best_test_loss = float('inf')
for epoch in range(N_EPOCHS):
train_loss, train_acc = train(model, train_iterator, optimizer, criterion)
test_loss, test_acc = evaluate(model, test_iterator, criterion)
if test_loss < best_test_loss:
best_test_loss = test_loss
torch.save(model.state_dict(), 'text_cnn_lstm.pt')
print(f'Epoch: {epoch+1:02}')
print(f'\tTrain Loss: {train_loss:.3f} | Train Acc: {train_acc*100:.2f}%')
print(f'\t Val. Loss: {test_loss:.3f} | Val. Acc: {test_acc*100:.2f}%')
在这个例子中,我们训练模型10次,并在每个周期结束时记录训练期间的性能。我们使用if语句来检查模型的表现是否优于先前的最佳表现,并在测试集上保存模型的权重。
4. 总结
在本文中,我们介绍了如何使用PyTorch实现用CNN和LSTM对文本进行分类的方法。我们首先介绍了数据集、文本预处理和数据迭代器。接下来,我们定义了一个CNN和LSTM结构的模型,并使用PyTorch训练和评估了它。最后,我们讨论了如何使用训练好的模型进行预测。
需要注意的是,本文仅介绍了如何使用PyTorch实现用CNN和LSTM对文本进行分类的方法。在实际应用中,我们可能需要更复杂的模型、更多的数据预处理步骤和更长的训练周期。本文提供的代码只是一个示例,在实际应用中可能需要进一步进行优化。