当前位置:   article > 正文

Pytorch实现基于LSTM的情感分析_pytorch lstm框架

pytorch lstm框架

本文参考

PyTorch深度学习项目实战100例

https://weibaohang.blog.csdn.net/article/details/127154284?spm=1001.2014.3001.5501

这段代码是一个基于PyTorch实现的情感分析模型,使用了LSTM(长短时记忆网络)作为核心结构。情感分析是一个自然语言处理任务,旨在确定给定文本的情感或情感极性,例如正面、负面

导入必要的包

介绍torchnet

Torchnet 是一个轻量级框架,旨在为 PyTorch 提供一些抽象和实用工具,以简化常见的深度学习研究任务。Torchnet 的设计是模块化和扩展性的,这使得研究者可以更轻松地尝试新的思路和方法。

!pip install torchnet
!pip install keras
  • 1
  • 2
import pickle
import numpy as np
import pandas as pd
import torch
import torch.nn as nn
from tensorflow.keras.preprocessing.sequence import pad_sequences
from sklearn.model_selection import train_test_split
from torch.utils.data import TensorDataset
from torch import optim
from torchnet import meter
from tqdm import tqdm```
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

做数据的导入

数据+代码

给必要的参数命名

# config file
#模型输入参数,需要自己根据需要调整
num_layers = 3 # LSTM的层数
hidden_dim = 100 # LSTM中的隐层大小
epochs = 20 # 迭代次数
batch_size = 32 # 每个批次样本大小
embedding_dim = 20 # 每个字形成的嵌入向量大小
output_dim = 2 # 输出维度,因为是二分类
lr = 0.003 # 学习率
device = 'cuda:0'
file_path = 'data_single.csv' # 数据路径
input_shape = 180 # 每句话的词的个数,如果不够需要使用0进行填充
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

加载文本数据


# 加载文本数据
def load_data(file_path, input_shape=20):
    df = pd.read_csv(file_path)

    # 标签及词汇表
    labels, vocabulary = list(df['label'].unique()), list(df['evaluation'].unique())

    # 构造字符级别的特征
    string = ''
    for word in vocabulary:
        string += word

    # 所有的词汇表
    vocabulary = set(string)

    # word2idx 将字映射为索引
    word_dictionary = {word: i + 1 for i, word in enumerate(vocabulary)}
    with open('word_dict.pk', 'wb') as f:
        pickle.dump(word_dictionary, f)
    # idx2word 将索引映射为字
    inverse_word_dictionary = {i + 1: word for i, word in enumerate(vocabulary)}
    # label2idx 将正反面映射为0和1
    label_dictionary = {label: i for i, label in enumerate(labels)}
    with open('label_dict.pk', 'wb') as f:
        pickle.dump(label_dictionary, f)
    # idx2label 将0和1映射为正反面
    output_dictionary = {i: labels for i, labels in enumerate(labels)}

    # 训练数据中所有词的个数
    vocab_size = len(word_dictionary.keys())  # 词汇表大小
    # 标签类别,分别为正、反面
    label_size = len(label_dictionary.keys())  # 标签类别数量

    # 序列填充,按input_shape填充,长度不足的按0补充
    # 将一句话映射成对应的索引 [0,24,63...]
    x = [[word_dictionary[word] for word in sent] for sent in df['evaluation']]
    # 如果长度不够input_shape,使用0进行填充
    x = pad_sequences(maxlen=input_shape, sequences=x, padding='post', value=0)
    # 形成标签0和1
    y = [[label_dictionary[sent]] for sent in df['label']]
    #     y = [np_utils.to_categorical(label, num_classes=label_size) for label in y]
    y = np.array(y)

    return x, y, output_dictionary, vocab_size, label_size, inverse_word_dictionary,word_dictionary,vocabulary

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46

读取数据返回参数

变量名描述
x输入数据
y标签数据
output_dictionary标签的反向映射
vocab_size词汇表大小
label_size标签类别数量
inverse_word_dictionary字符索引的反向映射
word_dictionary字符到索引的映射
vocabulary所有可能的字符集合

创建LSTM 网路结构

LSTM(Long Short-Term Memory)是一种循环神经网络(Recurrent Neural Network,RNN)的变种,它在处理序列数据时具有很好的性能,特别是在长序列上能够更好地捕捉长期依赖关系。下面是关于LSTM网络结构的说明:

背景:LSTM是为了解决传统RNN中的梯度消失和梯度爆炸问题而提出的。它引入了特殊的记忆单元来维护和控制信息的流动,以更好地捕捉序列数据中的长期依赖关系。

LSTM单元:LSTM网络的基本构建单元是LSTM单元。每个LSTM单元包括以下组件:

  • 输入门(Input Gate):控制新信息的输入。
  • 遗忘门(Forget Gate):控制过去信息的遗忘。
  • 输出门(Output Gate):控制输出的生成。
  • 细胞状态(Cell State):用于维护长期依赖关系的记忆。

记忆细胞:LSTM单元内部的细胞状态是其核心。它可以看作一个传送带,可以在不同时间步骤上添加或删除信息。通过输入门、遗忘门和输出门来控制信息的读取、写入和遗忘,以保持对序列中重要信息的长期记忆。

输入门:输入门决定了在当前时间步骤中,新的输入信息中哪些部分将会更新细胞状态。输入门通常由一个Sigmoid激活函数和一个tanh激活函数组成,用于产生0到1之间的权重和-1到1之间的新候选值。

遗忘门:遗忘门决定了哪些信息应该从细胞状态中丢弃。它使用Sigmoid激活函数来产生0到1之间的权重,控制细胞状态中哪些信息应该保留。

输出门:输出门决定了基于当前细胞状态和输入信息,LSTM单元应该输出什么。它使用Sigmoid激活函数来确定输出的哪些部分应该激活,并使用tanh激活函数来生成可能的输出值。
在这里插入图片描述

# 定义网络结构
class LSTM(nn.Module):
    def __init__(self, vocab_size, hidden_dim, num_layers, embedding_dim, output_dim):
        super(LSTM, self).__init__()
        self.hidden_dim = hidden_dim  # 隐层大小
        self.num_layers = num_layers  # LSTM层数
        # 嵌入层,会对所有词形成一个连续型嵌入向量,该向量的维度为embedding_dim
        # 然后利用这个向量来表示该字,而不是用索引继续表示
        self.embeddings = nn.Embedding(vocab_size + 1, embedding_dim)
        # 定义LSTM层,第一个参数为每个时间步的特征大小,这里就是每个字的维度
        # 第二个参数为隐层大小
        # 第三个参数为lstm的层数
        self.lstm = nn.LSTM(embedding_dim, hidden_dim, num_layers)
        # 利用全连接层将其映射为2维,即正反面的概率
        self.fc = nn.Linear(hidden_dim, output_dim)

    def forward(self, x):
        # 1.首先形成嵌入向量
        embeds = self.embeddings(x)
        # 2.将嵌入向量导入到lstm层
        output, (h_n, c_n) = self.lstm(embeds)
        timestep, batch_size, hidden_dim = output.shape
        output = output.reshape(-1, hidden_dim)
        # 3.将其导入全连接层
        output = self.fc(output)  # 形状为batch_size * timestep, 2
        output = output.reshape(timestep, batch_size, -1)
        return output[-1]  # 返回最后一个时间片的输出,维度为 batch_size, 2

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

数据前处理

# 1.获取训练数据
x, y, output_dictionary, vocab_size, label_size, inverse_word_dictionary,word_dictionary,vocabulary = load_data(file_path, input_shape)

# 2.划分训练、测试数据
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.1, random_state=42)

# 3.将numpy转成tensor
x_train = torch.from_numpy(x_train).to(torch.int32)
y_train = torch.from_numpy(y_train).to(torch.float32)
x_test = torch.from_numpy(x_test).to(torch.int32)
y_test = torch.from_numpy(y_test).to(torch.float32)

# 4.形成训练数据集
train_data = TensorDataset(x_train, y_train)
test_data = TensorDataset(x_test, y_test)

# 5.将数据加载成迭代器
train_loader = torch.utils.data.DataLoader(train_data,
                                           batch_size,
                                           True)

test_loader = torch.utils.data.DataLoader(test_data,
                                          batch_size,
                                          False)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

模型训练

# 6.模型训练
model = LSTM(vocab_size=len(inverse_word_dictionary), hidden_dim=hidden_dim, num_layers=num_layers,
             embedding_dim=embedding_dim, output_dim=output_dim)

Configimizer = optim.Adam(model.parameters(), lr=lr) # 优化器
criterion = nn.CrossEntropyLoss() # 多分类损失函数

model.to(device)
loss_meter = meter.AverageValueMeter()

best_acc = 0 # 保存最好准确率
best_model = None # 保存对应最好准确率的模型参数

for epoch in range(epochs):
    model.train() # 开启训练模式
    epoch_acc = 0 # 每个epoch的准确率
    epoch_acc_count = 0 # 每个epoch训练的样本数
    train_count = 0 # 用于计算总的样本数,方便求准确率
    loss_meter.reset()

    train_bar = tqdm(train_loader)  # 形成进度条
    for data in train_bar:
        x_train, y_train = data  # 解包迭代器中的X和Y

        x_input = x_train.long().transpose(1, 0).contiguous()
        x_input = x_input.to(device)
        Configimizer.zero_grad()

        # 形成预测结果
        output_ = model(x_input)

        # 计算损失
        loss = criterion(output_, y_train.long().view(-1))
        loss.backward()
        Configimizer.step()

        loss_meter.add(loss.item())

        # 计算每个epoch正确的个数
        epoch_acc_count += (output_.argmax(axis=1) == y_train.view(-1)).sum()
        train_count += len(x_train)

    # 每个epoch对应的准确率
    epoch_acc = epoch_acc_count / train_count

    # 打印信息
    print("【EPOCH: 】%s" % str(epoch + 1))
    print("训练损失为%s" % (str(loss_meter.mean)))
    print("训练精度为%s" % (str(epoch_acc.item() * 100)[:5]) + '%')

    # 保存模型及相关信息
    if epoch_acc > best_acc:
        best_acc = epoch_acc
        best_model = model.state_dict()

    # 在训练结束保存最优的模型参数
    if epoch == epochs - 1:
        # 保存模型
        torch.save(best_model, './best_model.pkl')       
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59

验证


 # 将输入句子转换为索引
sent = "商品很满意,我还会再光临的"
indexed_sent = [word_dictionary[char] for char in sent if char in word_dictionary]
input_data = torch.tensor(indexed_sent)
input_data = input_data.view(len(input_data), 1)
model = LSTM(vocab_size=len(inverse_word_dictionary), hidden_dim=hidden_dim, num_layers=num_layers,
             embedding_dim=embedding_dim, output_dim=output_dim)

with torch.no_grad():
  predictions = model(input_data)
  predicted_class = torch.argmax(predictions, dim=1).item()

	if predicted_class == 0:
	  print("负面")
	else:
	  print("正面")

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

弄成函数好调用

# 预测函数,用于对输入的文本进行情感分类预测
def predictions(sent):
    # 将输入文本的字符转换为对应的索引,忽略不在词汇表中的字符
    indexed_sent = [word_dictionary[char] for char in sent if char in word_dictionary]
    
    # 将索引化后的输入数据转换为PyTorch张量
    input_data = torch.tensor(indexed_sent)
    
    # 调整张量的形状,将其变成列向量
    input_data = input_data.view(len(input_data), 1)
    
    # 创建LSTM模型,使用预定义的参数
    model = LSTM(vocab_size=len(inverse_word_dictionary), hidden_dim=hidden_dim, num_layers=num_layers,
                 embedding_dim=embedding_dim, output_dim=output_dim)
    
    # 将模型设置为评估模式,不进行梯度计算
    model.eval()
    
    # 使用不计算梯度的上下文进行模型的前向传播,获取预测结果
    with torch.no_grad():
        predictions = model(input_data)
    
    # 根据预测结果选择具体的类别
    predicted_class = torch.argmax(predictions, dim=1).item()
    
    # 打印预测结果,0代表负面,1代表正面
    if predicted_class == 0:
        print("负面")
    else:
        print("正面")
    
    # 返回预测的类别,并打印出来
    return print("预测值", predicted_class)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

测试

 sent = "电视刚安装好,说实话,画质不怎么样,很差!"
 predictions(sent)
  • 1
  • 2

结果

负面
预测值 0
  • 1
  • 2
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/367846
推荐阅读
相关标签
  

闽ICP备14008679号