PyTorch应用实战六:利用LSTM实现文本情感分类

简介: PyTorch应用实战六:利用LSTM实现文本情感分类

实验环境

python3.6 + pytorch1.8.0 + torchtext0.9.0 + nltk

import torch
import torchtext
import nltk
print(torch.__version__)
print(torchtext.__version__)
print(nltk.__version__)
1.8.0
0.9.0
3.2.4

实验内容

0.导入相关库

import torch
import torch.optim as optim
import torch.nn as nn
import numpy as np
import torch.nn.functional as F
from torchtext.legacy import data, datasets

1.参数设置

class Args: 
    max_vocab_size = 25000  # 限定词表最大规模
    n_labels = 5            # 情感类别个数
    epochs = 5              # 训练总周期
    embedding_dim = 300     # 词向量维度
    hidden_dim = 512        # LSTMCell隐层维度
    n_layers = 3            # LSTM层数
    batch_size = 64         # 批样本量大小
    display_freq = 50       # 输出信息间隔
    lr= 0.01                # 学习率
# 实例化
args = Args() 

2.数据集预处理

text = data.Field()
label = data.LabelField(dtype = torch.float)
text,label
(<torchtext.legacy.data.field.Field at 0x1d8055c3208>,
 <torchtext.legacy.data.field.LabelField at 0x1d8054eada0>)

这段代码使用了torchtext库中的两个类,用于定义文本数据集的字段和标签。具体作用如下:


data.Field类用于表示样本中的文本数据字段。它定义了文本数据的预处理方式(如分词、去除停用词、转为小写等),以及如何构建词汇表(Vocabulary)。当读取数据集时,文本数据会被自动进行相应的预处理,并且会根据Vocabulary将文本数据转换为数值型数据。此外,还可以通过设置该类的其他参数(如batch_first)来自定义数据的格式。

data.LabelField类用于表示样本的标签字段。它定义了标签数据的数据类型(如整型、浮点型等),并且可以将标签数据转换为数值型数据。在训练模型时,标签数据也会被用来计算损失函数。

总之,这两个类都是用来定义数据集中的字段,以便进行后续的处理和训练。在使用torchtext库时,定义字段是必要的一步。

train_data, valid_data, test_data = datasets.SST.splits(text, label, fine_grained=True)

这段程序用到了torchtext库中提供的SST数据集,SST全称为Stanford Sentiment Treebank,是一个情感分析的数据集,包含了电影评论的文本和对应的情感标签。


具体分析如下:


datasets.SST.splits(text, label, fine_grained=True):将SST数据集分成训练集、验证集和测试集。其中text参数表示文本字段,label参数表示标签字段,fine_grained=True表示使用细粒度情感标签。这个函数返回三个数据集对象,分别为训练集train_data、验证集valid_data和测试集test_data。

train_data、valid_data和test_data都是torchtext.data.dataset.Dataset对象。这个对象中包含了数据集的所有实例,每个实例都包含了两个属性:text表示文本序列,label表示情感标签。

最终,这段程序的作用是将SST数据集加载进程序,分成训练集、验证集和测试集,并返回三个数据集对象,方便后续进行模型训练和测试。

text.build_vocab(
    train_data,
    max_size = args.max_vocab_size,
    vectors = "glove.6B.300d",
    unk_init = torch.Tensor.normal_
)
.vector_cache\glove.6B.zip: 862MB [09:42, 1.48MB/s]                               
100%|█████████▉| 399999/400000 [00:42<00:00, 9399.30it/s] 

这是一个用于构建词汇表(Vocabulary)的函数,它接受多个参数:


train_data:训练集数据,是一个由多个文本序列组成的列表或迭代器。

max_size:词汇表的最大大小,即最多包含多少个单词。如果不指定此参数,则默认为无限大。

vectors:词向量的预训练模型。在这里,使用了预训练的GloVe 300维词向量模型。如果不想使用预训练模型,则可以将此参数指定为None。

unk_init:当遇到不存在于预训练模型中的单词时,如何初始化它们的词向量。在这里,使用了正态分布作为初始化值。如果不指定此参数,则默认将所有未知单词的词向量初始化为0。

该函数的作用是将训练集中出现的所有单词加入到词汇表中,并为每个单词分配一个唯一的整数ID。同时,如果指定了预训练模型,则将相应单词的词向量加载到词汇表中。最终,该函数返回一个Vocab对象,其中包含词汇表信息和对应的词向量。

label.build_vocab(train_data)

这行代码是使用 train_data 数据集构建词汇表。词汇表是一个字典,将单词映射到一个唯一的整数值,使得后续处理可以更高效地处理文本数据。


具体来说,这个函数会遍历 train_data 中的所有句子,将其中所有的单词加入到词汇表中。此外,这个函数还可以进行一些额外的配置,例如设置最小出现次数、是否使用预训练的词向量等。


在使用词汇表时,每一个单词都会被替换为它在词汇表中对应的整数值,这个整数值可以作为模型输入的特征。如果有单词不在词汇表中,可以选择将它们替换为一个特殊的 <unk> 单词或者忽略掉

device = 'cpu'  # 使用cpu
train_iter, valid_iter, test_iter = data.BucketIterator.splits(
    (train_data, valid_data, test_data),
    batch_size=args.batch_size,
    device=device
)

该程序用于创建数据迭代器,以便在训练、验证和测试时批量处理数据。


首先,输入训练、验证和测试数据集以及一个批量大小参数。然后,程序使用PyTorch的BucketIterator类创建数据迭代器对象,该对象将数据按照指定的批量大小进行分批,并将其发送到指定的PyTorch设备上。


具体来说,训练、验证、测试数据集以及设备类型等参数传递给BucketIterator的split方法,该方法返回一个包含三个数据迭代器(train_iter、valid_iter和test_iter)的元组。这些迭代器可以用于遍历每个数据集,并按照指定的批量大小将数据分成批次。


在训练网络时,我们可以使用train_iter迭代器遍历训练数据集的所有批次,并使用每个批次来更新模型的权重。验证和测试也可以使用valid_iter和test_iter迭代器遍历验证和测试数据集,以便评估模型的性能。

3.定义神经网络模型

input_dim = len(text.vocab)
output_dim = args.n_labels
class Model(nn.Module):
    def __init__(self, in_dim, emb_dim, hid_dim, out_dim, n_layer):
        super(Model, self).__init__()
        self.embedding = nn.Embedding(in_dim, emb_dim)
        self.rnn = nn.LSTM(emb_dim, hid_dim, n_layer)
        self.linear = nn.Linear(hid_dim, out_dim)
        self.n_layer = n_layer
        self.hid_dim = hid_dim
    def forward(self, text):
        embedded = self.embedding(text)
        h0 = embedded.new_zeros(self.n_layer, embedded.size(1), self.hid_dim)
        c0 = embedded.new_zeros(self.n_layer, embedded.size(1), self.hid_dim)
        output, (hn, cn) = self.rnn(embedded, (h0, c0))
        return self.linear(output[-1])

这段程序定义了一个名为Model的自定义神经网络模型类。该模型包含四个模块:嵌入层、LSTM层、线性层和全连接层。其中,嵌入层将输入的词索引序列转化为对应的词向量表示,LSTM层通过学习输入序列之间的关系,提取文本信息,线性层将LSTM层最后一个时间步的输出映射到隐层状态,全连接层将隐层状态映射到输出向量。


具体来说,输入层的维度为词表的大小(即词表中的词数),输出层的维度为分类的类别数;嵌入层将输入的词索引映射为固定维度的词向量,LSTM层的隐藏状态维度为hid_dim,LSTM层的层数为n_layer;线性层将隐藏状态映射为输出向量。


在前向传播过程中,输入的词索引序列被首先映射为对应的词向量表示,然后在LSTM层中进行学习和提取文本信息的过程,最后通过线性层将LSTM层的最后一个时间步的输出映射到输出向量。

model = Model(input_dim, 
              args.embedding_dim, 
              args.hidden_dim,
              output_dim,
              args.n_layers
             )
pretrained_embeddings = text.vocab.vectors
model.embedding.weight.data.copy_(pretrained_embeddings)
model.to(device)
optimizer = optim.Adam(model.parameters(), lr=args.lr)

4.训练模型

def train(epoch, model, iterator, optimizer):
    loss_list = []
    acc_list = []
    model.train()
    for i, batch in enumerate(iterator):
        optimizer.zero_grad()
        text = batch.text.to(device)
        label = batch.label.long().to(device)
        predictions = model(text)
        loss = F.cross_entropy(predictions, label)
        loss.backward()
        optimizer.step()
        acc = (predictions.max(1)[1] == label).float().mean()
        loss_list.append(loss.item())
        acc_list.append(acc.item())
        if i % args.display_freq == 0:
            print("Epoch %02d, Iter [%03d/%03d],"
                 "train loss = %.4f, train acc = %.4f" % (
                 epoch, i, len(iterator),
                 np.mean(loss_list), np.mean(acc_list)    
            ))
            loss_list.clear()
            acc_list.clear()

5.验证模型

def evaluate(epoch, model, iterator):
    val_loss = 0
    val_acc = 0
    model.eval()
    with torch.no_grad():
        for batch in iterator:
            text = batch.text.to(device)
            label = batch.label.long().to(device)
            predictions = model(text)
            loss = F.cross_entropy(predictions, label)
            acc = (predictions.max(1)[1] == label).float().mean()
            val_loss += loss.item()
            val_acc += acc.item()
    val_loss = val_loss / len(iterator)
    val_acc = val_acc / len(iterator)
    print('...Epoch %02d, val loss = %.4f, val acc = %.4f' % (
        epoch, val_loss, val_acc
    ))
    return val_loss, val_acc

6.测试模型

best_acc = 0
best_epoch = -1
for epoch in range(1, args.epochs + 1):
    train(epoch, model, train_iter, optimizer)
    valid_loss, valid_acc = evaluate(epoch, model, valid_iter)
    if valid_acc > best_acc:
        best_acc = valid_acc
    best_epoch = epoch
    torch.save(model.state_dict(),'best-model.pth')
print('Test best model @ Epoch %02d' % best_epoch)
model.load_state_dict(torch.load('best-model.pth'))
test_loss, test_acc = evaluate(epoch, model, test_iter)
print('Finally, test loss = %.4f, test acc = %.4f' % (test_loss, test_acc))
Epoch 01, Iter [000/134],train loss = 1.5556, train acc = 0.2031
Epoch 01, Iter [050/134],train loss = 1.5830, train acc = 0.2700
Epoch 01, Iter [100/134],train loss = 1.5819, train acc = 0.2631
...Epoch 01, val loss = 1.6984, val acc = 0.2533
Epoch 02, Iter [000/134],train loss = 1.5647, train acc = 0.2969
Epoch 02, Iter [050/134],train loss = 1.5870, train acc = 0.2678
Epoch 02, Iter [100/134],train loss = 1.5791, train acc = 0.2653
...Epoch 02, val loss = 1.5842, val acc = 0.2541
Epoch 03, Iter [000/134],train loss = 1.5969, train acc = 0.3125
Epoch 03, Iter [050/134],train loss = 1.5948, train acc = 0.2628
Epoch 03, Iter [100/134],train loss = 1.5919, train acc = 0.2659
...Epoch 03, val loss = 1.6012, val acc = 0.2767
Epoch 04, Iter [000/134],train loss = 1.5316, train acc = 0.2969
Epoch 04, Iter [050/134],train loss = 1.5864, train acc = 0.2753
Epoch 04, Iter [100/134],train loss = 1.5855, train acc = 0.2772
...Epoch 04, val loss = 1.5731, val acc = 0.2758
Epoch 05, Iter [000/134],train loss = 1.5888, train acc = 0.3125
Epoch 05, Iter [050/134],train loss = 1.5767, train acc = 0.2725
Epoch 05, Iter [100/134],train loss = 1.5942, train acc = 0.2731
...Epoch 05, val loss = 1.5772, val acc = 0.2621
Test best model @ Epoch 05
...Epoch 05, val loss = 1.5856, val acc = 0.2620
Finally, test loss = 1.5856, test acc = 0.2620


目录
相关文章
|
6月前
|
机器学习/深度学习 自然语言处理 算法
【从零开始学习深度学习】49.Pytorch_NLP项目实战:文本情感分类---使用循环神经网络RNN
【从零开始学习深度学习】49.Pytorch_NLP项目实战:文本情感分类---使用循环神经网络RNN
|
4月前
|
机器学习/深度学习 PyTorch TensorFlow
TensorFlow和PyTorch的实际应用比较
TensorFlow和PyTorch的实际应用比较
|
3月前
|
机器学习/深度学习 数据挖掘 TensorFlow
解锁Python数据分析新技能,TensorFlow&PyTorch双引擎驱动深度学习实战盛宴
在数据驱动时代,Python凭借简洁的语法和强大的库支持,成为数据分析与机器学习的首选语言。Pandas和NumPy是Python数据分析的基础,前者提供高效的数据处理工具,后者则支持科学计算。TensorFlow与PyTorch作为深度学习领域的两大框架,助力数据科学家构建复杂神经网络,挖掘数据深层价值。通过Python打下的坚实基础,结合TensorFlow和PyTorch的强大功能,我们能在数据科学领域探索无限可能,解决复杂问题并推动科研进步。
71 0
|
4月前
|
机器学习/深度学习 PyTorch TensorFlow
【PyTorch】PyTorch深度学习框架实战(一):实现你的第一个DNN网络
【PyTorch】PyTorch深度学习框架实战(一):实现你的第一个DNN网络
193 1
|
4月前
|
机器学习/深度学习 数据采集 自然语言处理
PyTorch 在自然语言处理中的应用实践
【8月更文第29天】随着深度学习技术的发展,自然语言处理(NLP)领域取得了显著的进步。PyTorch 作为一款强大的深度学习框架,因其灵活性和易用性而被广泛采用。本文将介绍如何利用 PyTorch 构建文本分类模型,并以情感分析为例进行详细介绍。
69 0
|
5月前
|
机器学习/深度学习 数据挖掘 TensorFlow
解锁Python数据分析新技能,TensorFlow&PyTorch双引擎驱动深度学习实战盛宴
【7月更文挑战第31天】在数据驱动时代,Python凭借其简洁性与强大的库支持,成为数据分析与机器学习的首选语言。**数据分析基础**从Pandas和NumPy开始,Pandas简化了数据处理和清洗,NumPy支持高效的数学运算。例如,加载并清洗CSV数据、计算总销售额等。
65 2
|
5月前
|
机器学习/深度学习 人工智能 数据挖掘
从0到1构建AI帝国:PyTorch深度学习框架下的数据分析与实战秘籍
【7月更文挑战第30天】PyTorch以其灵活性和易用性成为深度学习的首选框架。
76 2
|
5月前
|
机器学习/深度学习 数据挖掘 PyTorch
🚀PyTorch实战宝典:从数据分析小白到深度学习高手的飞跃之旅
【7月更文挑战第29天】在数据驱动的世界里, **PyTorch** 作为深度学习框架新星, 凭借其直观易用性和高效计算性能, 助力数据分析新手成为深度学习专家。首先, 掌握Pandas、Matplotlib等工具进行数据处理和可视化至关重要。接着, 安装配置PyTorch环境, 学习张量、自动求导等概念。通过构建简单线性回归模型, 如定义 `nn.Module` 类、设置损失函数和优化器, 进行训练和测试, 逐步过渡到复杂模型如CNN和RNN的应用。不断实践, 你将能熟练运用PyTorch解决实际问题。
98 1
|
5月前
|
PyTorch 算法框架/工具 索引
pytorch实现水果2分类(蓝莓,苹果)
pytorch实现水果2分类(蓝莓,苹果)
|
6月前
|
机器学习/深度学习 自然语言处理 前端开发
深度学习-[源码+数据集]基于LSTM神经网络黄金价格预测实战
深度学习-[源码+数据集]基于LSTM神经网络黄金价格预测实战
171 0