首页 > 编程语言 >基于Python的自然语言处理系列(9):使用TorchText与预训练词嵌入进行新闻分类

基于Python的自然语言处理系列(9):使用TorchText与预训练词嵌入进行新闻分类

时间:2024-09-16 22:22:31浏览次数:12  
标签:loss Python text length label TorchText train 自然语言 valid

        在前一篇文章中,我们展示了如何使用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

相关文章

  • 基于Python的人工智能应用案例系列(2):分类
            在本篇文章中,我们将探讨分类问题,具体的应用场景是贷款审批预测。通过该案例,我们将学习如何使用Python处理分类问题,训练模型并预测贷款是否会被批准。案例背景        该数据集包含贷款申请的相关信息,目标是预测贷款是否会被批准(Loan_Status为目标变......
  • 【Azure Developer】通过SDK(for python)获取Azure服务生命周期信息
    问题描述需要通过PythonSDK获取Azure服务的一些通知信息,如:K8S版本需要更新到指定的版本,Azure服务的维护通知,服务处于不健康状态时的通知,及相关的操作建议等内容。 问题解答AzureResourceHealth是Azure提供的一项服务,旨在帮助用户了解其资源的健康状态。通过AzureResource......
  • 聪明办法学Python丨202409TASK1学习笔记
        踏入Python编程的世界之初,我便深刻地体会到了这门语言的独特魅力。Python凭借其简洁明了的语法与强大的功能性,迅速吸引了我的注意。相较于C语言等编译型语言,Python的语法更加接近自然语言,这使得即使是初次接触编程的人也能快速上手。Python的设计理念强调代码的可......
  • 如何利用Python进行数据分析与可视化的具体操作指南
    成长路上不孤单......
  • 用Python打造互动式中秋节庆祝小程序
    中秋节,这个充满传统韵味的节日,不仅是家人团聚的时刻,也是程序员展示创意的好机会。本文将引导您使用Python创建一个互动式中秋节庆祝小程序,它不仅能够展示节日祝福,还能通过一些简单的特效增加节日气氛。文章目录......
  • 用Python做一个小游戏
    首先,我们需要定义一个类,然后创建一副牌,最后实现一些基本的功能定义扑克牌类:classCard:def__init__(self,suit,rank):self.suit=suitself.rank=rankdef__repr__(self):returnf"{self.rank}of{self.suit}"创建一副扑克牌:suits=[......
  • 深入理解Python生成器、装饰器和异常处理
    一、Python生成器1.1什么是生成器?生成器(Generator)是Python中一种特殊的迭代器,它允许你在遍历大型数据集时节省内存。与普通函数不同,生成器函数使用yield关键字返回值,而不是return。生成器每次被调用时,函数的执行会在yield语句处暂停,并保存函数的状态,下一次再调用时从上次......
  • 【编程小白必看】python使用tkinter页面操作秘籍一文全掌握
    【编程小白必看】python使用tkinter页面操作秘籍......
  • 用python写一段代码:读取一张图片中的所有颜色信息,并按照占比大小,从大到小依次列出颜色
    fromPILimportImagefromcollectionsimportCounterimportnumpyasnpdefsave_colors_to_file(image_path,output_file):#打开图片文件image=Image.open(image_path)image=image.convert('RGB')#将图片转换为numpy数组pixels=np.ar......
  • [Python手撕]归并排序
    classSolution:defsortArray(self,nums:List[int])->List[int]:defmerge(nums1,nums2):ifnotnums1andnotnums2:returnNoneifnotnums1:returnnums2ifnotnums2......