在前一篇文章中,我们展示了如何使用TorchText和RNN进行新闻分类。在这篇文章中,我们将改进之前的模型,通过使用预训练词嵌入、优化器的更改、正交初始化以及打包填充序列的技巧,提升模型的学习效率和效果。
1. 改进方向
提高模型学习效果:
- 使用预训练词嵌入:使用FastText嵌入预训练的词向量,预计准确率提升约20%。
- 更换优化器为Adam:提升学习速度。
- 正交初始化:用于RNN/LSTM或CNN的初始化方法,尽管改善有限,但是一种优秀的实践。
提高模型计算效率:
- 打包填充序列:通过打包填充序列,RNN将忽略填充部分,节省计算量,大幅提高模型的准确率。
- Embedding层的
padding_idx
:让Embedding层忽略填充标记,减少无用的计算,提升效率。
2. 数据加载与预处理
首先,我们使用TorchText加载AG_NEWS数据集,并进行预处理。我们使用spacy
进行文本的标记化,并创建词汇表,将文本转化为对应的整数表示。
from torchtext.datasets import AG_NEWS
from torchtext.data.utils import get_tokenizer
from torchtext.vocab import build_vocab_from_iterator
# 加载数据集
train, test = AG_NEWS()
# 使用spacy进行标记化
tokenizer = get_tokenizer('spacy', language='en_core_web_sm')
# 构建词汇表
def yield_tokens(data_iter):
for _, text in data_iter:
yield tokenizer(text)
vocab = build_vocab_from_iterator(yield_tokens(train), specials=['<unk>', '<pad>'])
vocab.set_default_index(vocab["<unk>"])
为了节省计算资源,我们对数据集进行了随机拆分,仅保留20%的训练数据和10%的验证数据:
train_size = len(list(iter(train)))
too_much, train, valid = train.random_split(total_length=train_size, weights = {"too_much": 0.7, "train": 0.2, "valid": 0.1}, seed=999)
train_size = len(list(iter(train)))
val_size = len(list(iter(valid)))
print(f"训练集大小: {train_size}, 验证集大小: {val_size}")
3. 使用FastText嵌入
接下来,我们将使用FastText的预训练词嵌入,将其应用到我们的词汇表中。FastText能够处理OOV(Out Of Vocabulary,超出词汇表)的词汇,并考虑到词汇的子词信息,因此在许多应用中具有较好的表现。
from torchtext.vocab import FastText
# 下载FastText预训练向量
fast_vectors = FastText(language='simple')
# 获取FastText嵌入矩阵
fast_embedding = fast_vectors.get_vecs_by_tokens(vocab.get_itos()).to(device)
4. 打包填充序列与模型设计
为了提高RNN的计算效率,我们将使用打包填充序列(Packed Padded Sequences),让RNN忽略填充部分,节省不必要的计算。
from torch.nn.utils.rnn import pad_sequence, pack_padded_sequence, pad_packed_sequence
# 定义collate_fn函数,创建批次数据
def collate_batch(batch):
label_list, text_list, length_list = [], [], []
for (_label, _text) in batch:
label_list.append(label_pipeline(_label))
processed_text = torch.tensor(text_pipeline(_text), dtype=torch.int64)
text_list.append(processed_text)
length_list.append(processed_text.size(0))
return torch.tensor(label_list, dtype=torch.int64), pad_sequence(text_list, padding_value=vocab['<pad>'], batch_first=True), torch.tensor(length_list, dtype=torch.int64)
我们将在RNN模型中应用打包填充序列,以提高计算效率:
class PaddedRNN(nn.Module):
def __init__(self, input_dim, emb_dim, hid_dim, output_dim):
super().__init__()
# 通过设置padding_idx让Embedding层忽略填充
self.embedding = nn.Embedding(input_dim, emb_dim, padding_idx=vocab['<pad>'])
self.rnn = nn.RNN(emb_dim, hid_dim, batch_first=True)
self.fc = nn.Linear(hid_dim, output_dim)
def forward(self, text, text_lengths):
# 嵌入
embedded = self.embedding(text)
# 打包嵌入
packed_embedded = pack_padded_sequence(embedded, text_lengths.to('cpu'), batch_first=True, enforce_sorted=False)
# 通过RNN层
packed_output, hn = self.rnn(packed_embedded)
# 解包
output, _ = pad_packed_sequence(packed_output, batch_first=True)
return self.fc(hn.squeeze(0))
5. 模型训练与优化
为了加速训练,我们使用Adam优化器,并在RNN权重上应用正交初始化,以应对梯度消失和爆炸问题。
import torch.optim as optim
# 定义优化器和损失函数
optimizer = optim.Adam(model.parameters(), lr=1e-3)
criterion = nn.CrossEntropyLoss()
# 正交初始化
def initialize_weights(m):
if isinstance(m, nn.Linear):
nn.init.xavier_normal_(m.weight)
elif isinstance(m, nn.RNN):
for name, param in m.named_parameters():
if 'weight' in name:
nn.init.orthogonal_(param)
6. 模型训练与评估
接下来,我们定义模型的训练和评估函数。训练函数将模型设置为训练模式,评估函数则将模型设置为评估模式,并避免梯度计算。
def accuracy(preds, y):
predicted = torch.max(preds.data, 1)[1]
batch_corr = (predicted == y).sum()
acc = batch_corr / len(y)
return acc
def train(model, loader, optimizer, criterion, loader_length):
epoch_loss = 0
epoch_acc = 0
model.train()
for i, (label, text, text_length) in enumerate(loader):
label = label.to(device)
text = text.to(device)
predictions = model(text, text_length).squeeze(1)
# 计算损失和准确率
loss = criterion(predictions, label)
acc = accuracy(predictions, label)
# 反向传播
optimizer.zero_grad()
loss.backward()
optimizer.step()
epoch_loss += loss.item()
epoch_acc += acc.item()
return epoch_loss / loader_length, epoch_acc / loader_length
def evaluate(model, loader, criterion, loader_length):
epoch_loss = 0
epoch_acc = 0
model.eval()
with torch.no_grad():
for i, (label, text, text_length) in enumerate(loader):
label = label.to(device)
text = text.to(device)
predictions = model(text, text_length).squeeze(1)
loss = criterion(predictions, label)
acc = accuracy(predictions, label)
epoch_loss += loss.item()
epoch_acc += acc.item()
return epoch_loss / loader_length, epoch_acc / loader_length
在每个epoch结束时,我们会保存模型的最佳验证结果,并绘制损失和准确率的变化图:
import matplotlib.pyplot as plt
train_losses, train_accs, valid_losses, valid_accs = [], [], [], []
best_valid_loss = float('inf')
num_epochs = 5
for epoch in range(num_epochs):
train_loss, train_acc = train(model, train_loader, optimizer, criterion, len(train_loader))
valid_loss, valid_acc = evaluate(model, valid_loader, criterion, len(valid_loader))
train_losses.append(train_loss)
train_accs.append(train_acc)
valid_losses.append(valid_loss)
valid_accs.append(valid_acc)
if valid_loss < best_valid_loss:
best_valid_loss = valid_loss
torch.save(model.state_dict(), 'best-model.pt')
print(f'Epoch: {epoch+1} | Train Loss: {train_loss:.3f} | Train Acc: {train_acc*100:.2f}%')
print(f'Val Loss: {valid_loss:.3f} | Val Acc: {valid_acc*100:.2f}%')
# 可视化损失和准确率
plt.figure(figsize=(10,6))
plt.plot(train_losses, label='Train Loss')
plt.plot(valid_losses, label='Valid Loss')
plt.legend()
plt.show()
plt.figure(figsize=(10,6))
plt.plot(train_accs, label='Train Acc')
plt.plot(valid_accs, label='Valid Acc')
plt.legend()
plt.show()
7. 测试与预测
我们可以使用训练好的模型对新的文本进行预测。以下是如何测试随机新闻文本:
def predict(text, text_length):
with torch.no_grad():
output = model(text, text_length).squeeze(1)
predicted = torch.max(output.data, 1)[1]
return predicted
test_str = "Google is facing major challenges in its business."
text = torch.tensor(text_pipeline(test_str)).unsqueeze(0).to(device)
text_length = torch.tensor([text.size(1)]).to(device)
prediction = predict(text, text_length)
print(f'预测结果: {prediction.item()}')
结语
通过本文的探索,我们学习了如何通过引入预训练的FastText词嵌入、正交初始化和打包填充序列来提升文本分类模型的性能。这些改进不仅增强了模型的学习效果,还提高了计算效率。此外,我们还使用了Adam优化器,进一步加速了模型的训练过程。
这些技巧在实际应用中非常有用,尤其是处理大规模文本数据时,它们能够显著减少计算资源的消耗,同时提升模型的预测准确性。在接下来的文章中,我们将继续探索更复杂的网络架构,例如LSTM,以进一步优化文本分类任务的表现。
通过不断引入新技术和优化策略,相信读者将能够从中学到更多深层次的自然语言处理(NLP)知识,并逐步应用到实际项目中。
如果你觉得这篇博文对你有帮助,请点赞、收藏、关注我,并且可以打赏支持我!
欢迎关注我的后续博文,我将分享更多关于人工智能、自然语言处理和计算机视觉的精彩内容。
谢谢大家的支持!
标签:loss,Python,text,length,label,TorchText,train,自然语言,valid From: https://blog.csdn.net/ljd939952281/article/details/142153700