当前位置: 首页 > article >正文

NLP自然语言处理——使用飞桨实现基于LSTM的情感分析

任务说明: 通过对电影评论历史数据分析,构建深度学习分类模型,最终完成对新的数据样本的识别分类。
任务要求: 运用神经网络算法,创建、训练、评估模型,完成对电影评论的情感分类任务。
数据集说明: IMDB数据集包含来自互联网的50000条严重两极分化的评论,该数据被分为用于训练的25000条评论和用于测试的25000条评论,训练集和测试集都包含50%的正面评价和50%的负面评价。

  • 情绪类别:正面/负面
  • 文件样本格式:标签 + 英文文本 + \n

一、模块导入

import numpy as np
import pandas as pd
import paddle
import paddle.nn as nn
import paddle.nn.functional as F
from paddle.nn import LSTM, Embedding, Dropout, Linear
import paddlenlp

二、加载数据

print('loading dataset...')
train_dataset =  paddle.text.datasets.Imdb(mode='train')  #导入训练集
test_dataset =  paddle.text.datasets.Imdb(mode='test')   #导入测试集
print('loading finished')

三、获取词表

word_dict = train_dataset.word_idx  # 获取数据集的词表
#pad设置, 设置填充词为<pad>,用于后续填充句子,以使批数据训练时句子长度保持一致
#添加'pad'字段到词表最后
word_dict['<pad>'] = len(word_dict) 

#查看词表前五个词和id
for k in list(word_dict)[:5]:
    print("{}:{}".format(k.decode('ASCII'), word_dict[k])) #将bytes转化成ASCII字符串,并查看id序号
print("...")
#打印后词表五个词和id号
for k in list(word_dict)[-5:]:
    #如果字典键的类型不是str,则进行转换
    print("{}:{}".format(k if isinstance(k, str) else k.decode('ASCII'), word_dict[k])) 
# 查看词表中单词的总个数
print("totally {} words".format(len(word_dict)))

四、构造数据结构


# 读取数据归一化处理
seq_len = 200  #句子的最大长度
def create_padded_dataset(dataset):
    pad_id = word_dict['<pad>']
    padded_sents = [] #存放句子id信息的列表
    valid_length=[]#计算句子转换成id后的长度
    labels = []  #存放标签id的列表
    for batch_id, data in enumerate(dataset):
        sent, label = data[0], data[1]  #获取  数据id和数据标签
        #对每一个句子长度小于200的进行padding填充,
        padded_sent = np.concatenate([sent[:seq_len], [pad_id] * (seq_len - len(sent))]).astype('int64')
        
        padded_sents.append(padded_sent) #存放句子id
        
        valid_length.append(np.array(len(padded_sent),dtype="int64"))  
        labels.append(label)#存放标签
    return np.array(padded_sents),np.array(valid_length) ,np.array(labels)  

# 对train、test数据进行实例化
#返回句子和标签
train_sents, train_valid_length,train_labels = create_padded_dataset(train_dataset)
test_sents,test_valid_length, test_labels = create_padded_dataset(test_dataset)

# 查看数据大小及举例内容
print(train_sents.shape)
print(train_labels.shape)
print(train_valid_length.shape)
print(test_valid_length.shape)
print(test_sents.shape)
print(test_labels.shape)

(1) ny.concatenate() 详解

在数据处理和机器学习任务中,常常需要合并多个数组或矩阵。NumPy库中的 np.concatenate 函数是一个强大的工具,用于沿指定轴将多个数组合并成一个新的数组。详细介绍如下:

1.函数定义和参数

numpy.concatenate((a1, a2, ...), axis=0, out=None)

np.concatenate 函数接收以下参数:

(a1, a2, …): 数组序列,注意要用 () 或者 [] 符号括起来,否则会报错。
axis: 指定合并的轴,即沿着哪个维度进行合并。默认值为 0,表示沿着第一个维度进行合并。
out: 指定输出数组的可选参数。

2.合并一维数组

import numpy as np
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
c = np.concatenate((a, b))
print(c)

输出结果:

[1 2 3 4 5 6]

3.合并二维数组

import numpy as np
a = np.array([[1, 2], [3, 4]])
b = np.array([[5, 6], [7, 8]])
c = np.concatenate(
	(a, b), 
	axis=1#沿着轴1(列)进行合并
	)
print(c)

输出结果:

[[1 2 5 6]
 [3 4 7 8]]

五、组装成mini-batch

batch_size = 64  #批数据数量
class IMDBDataset(paddle.io.Dataset):
    '''
    继承paddle.io.Dataset类进行封装数据
    '''
    def __init__(self, sents, valid_lengths,labels):
        self.sents = sents
        self.valid_lengths=valid_lengths
        self.labels = labels
    #根据给定索引获取数据集中的样本
    #在 paddle.io.DataLoader 中需要使用此函数通过下标获取样本
    def __getitem__(self, index):
        data = self.sents[index]
        valid_length=self.valid_lengths[index]
        label = self.labels[index]
        #返回句子id,标签id
        return data,valid_length, label

    def __len__(self):
        #返回句子个数
        return len(self.sents)
    
train_dataset = IMDBDataset(train_sents,train_valid_length, train_labels)
test_dataset = IMDBDataset(test_sents,test_valid_length, test_labels)

train_loader = paddle.io.DataLoader(
    train_dataset, #数据集
    return_list=True, #以list 形式返回
    shuffle=True,#是否打乱mini-batch的索引顺序
    batch_size=batch_size,#批数据的数量
    drop_last=True#去除末尾剩余数据
    )
test_loader = paddle.io.DataLoader(test_dataset, return_list=True,
                                    shuffle=True, batch_size=batch_size, drop_last=True)

如果批次大小(batch_size)增大,通常可以线性增加学习率(learning_rate)。经验法则是,当批次大小增加到原来的k倍时,学习率乘以 k。例如,如果批次大小从 32 增加到 64,学习率可以翻倍。
:以上经验法则仅适用于批次大小相对较小的时候。

六、定义LSTM网络

LSTM网络结构如下:

  • 第一层:word Embedding层;(文本向量化)
  • 第二层:seq2vec层;(序列学习)
  • 第三层:全连接层;(特征学习)
  • 第四层:输出层;(分类)
import paddlenlp as ppnlp
class LSTMModel (nn.Layer):#Layer首字母必须大写
    def __init__(self,
        vocab_size,#词表长度
        num_classes,#分类数
        emb_dim=128,
        padding_idx=0,
        lstm_hidden_size=198,
        direction='forward',
        lstm_layers=1,
        dropout_rate=0.0,
        pooling_type=None,
        fc_hidden_size=128):

        super().__init__()
        #文本向量化
        #首先将输入word_id 查表后映射成word embedding
        self.embedder=nn.Embedding(
            num_embeddings=vocab_size,
            embedding_dim=emb_dim,
            padding_idx=padding_idx
            )
        #序列学习
        #将word_embedding经过LSTMEncoder变换到文本语义特征空间中
        self.lstm_encoder=ppnlp.seq2vec.LSTMEncoder(
            emb_dim,
            lstm_hidden_size,
            num_layers=lstm_layers,
            direction=direction,
            dropout=dropout_rate,
            pooling_type=pooling_type
        )
        #特征学习
        self.fc=nn.Linear(self.lstm_encoder.get_output_dim(),fc_hidden_size)
        #输出层
        self.output_layer=nn.Linear(fc_hidden_size,num_classes)

    def forward(self,text,seq_len):
        embedded_text=self.embedder(text)
        text_repr=self.lstm_encoder(embedded_text,sequence_length=seq_len)
        fc_out=paddle.tanh(self.fc(text_repr))
        logits=self.output_layer(fc_out)
        probs=F.softmax(logits,axis=-1)
        return probs

七、模型训练

(1) 定义lstm训练参数

epoch_num=4#迭代次数
batch_size=64#mini_batch样本数量
learning_rate=0.001#学习率(控制参数更新步长)
dropout_rate=0.2#随机让部分神经元失效的比率
embedding_size=256
hidden_size=256
num_layers=1
#词表大小
vocab_size = len(word_dict)
print(vocab_size)

(2) 实例化LSTM模型

model=LSTMModel(
    vocab_size,
    num_classes=2,
    emb_dim=embedding_size,
    lstm_layers=num_layers,
    direction='bidirectional',
    padding_idx=word_dict['<pad>']
)
#指定优化策略,更新模型参数
optimizer=paddle.optimizer.Adam(learning_rate=learning_rate,beta1=0.9,beta2=0.999,parameters=model.parameters())

(3) 定义训练函数

steps=[]
losses=[]
acc=[]
def train(model):
    model.train()
    global_step=0

    for epoch in range(epoch_num):
        for step,(sentences,valid_length,labels) in enumerate(train_loader):
            logits=model(sentences,valid_length)
            # 计算损失
            loss = F.cross_entropy(input=logits, label=labels, soft_label=False)
            loss = paddle.mean(loss)
            ACC = paddle.metric.accuracy(logits, labels)
            # 后向传播
            loss.backward()
            # 更新参数
            optimizer.step()
            # 清除梯度
            optimizer.clear_grad()
            global_step+=1
            if global_step % 50 == 0:
                # 记录当前步骤的loss变化情况
                steps.append(global_step)
                losses.append(loss.numpy()[0])
                acc.append(ACC.numpy()[0])
                print("epoch %d, step %d, loss %.3f,acc %.4f" % (epoch,global_step, loss.numpy()[0],ACC.numpy()[0]))
train(model)
# 保存模型,包含两部分:模型参数和优化器参数
model_name = "sentiment_classifier"
# 保存训练好的模型参数
paddle.save(model.state_dict(), "checkpoint/{}.pdparams".format(model_name))
# 保存优化器参数,方便后续模型继续训练
paddle.save(optimizer.state_dict(), "checkpoint/{}.pdopt".format(model_name))

八、模型评估

@paddle.no_grad()
#七、模型评估
def evaluate(model):
    # 开启模型测试模式,在该模式下,网络不会进行梯度更新
    model.eval()

    # 定义以上几个统计指标
    tp, tn, fp, fn = 0, 0, 0, 0
    total_steps=0
    
    for sentences,valid_lens, labels in test_loader:
        total_steps+=1
    
        # 获取模型对当前batch的输出结果
        logits = model(sentences,valid_lens)
        
        
        # 使用softmax进行归一化
        probs = F.softmax(logits)

        eval_acc=paddle.metric.accuracy(probs,labels)

        # 把输出结果转换为numpy array数组,比较预测结果和对应label之间的关系,并更新tp,tn,fp和fn
        probs = probs.numpy()
        for i in range(len(probs)):
            # 当样本是的真实标签是正例
            if labels[i][0] == 1:
                # 模型预测是正例
                if probs[i][1] > probs[i][0]:
                    tp += 1
                # 模型预测是负例
                else:
                    fn += 1
            # 当样本的真实标签是负例
            else:
                # 模型预测是正例
                if probs[i][1] > probs[i][0]:
                    fp += 1
                # 模型预测是负例
                else:
                    tn += 1
        if total_steps % 100==0:
            print(eval_acc.numpy()[0])

    # 整体准确率
    accuracy = (tp + tn) / (tp + tn + fp + fn)
    # 输出最终评估的模型效果
    print("TP: {}\nFP: {}\nTN: {}\nFN: {}\n".format(tp, fp, tn, fn))
    print("Accuracy: %.4f" % accuracy)
# 加载训练好的模型进行预测,重新实例化一个模型,然后将训练好的模型参数加载到新模型里面
state_dict=paddle.load('checkpoint/sentiment_classifier.pdparams')
model.load_dict(state_dict)
# 评估模型
evaluate(model)

九、可视化

import matplotlib.pyplot as plt
# 可视化定义
def draw_process(title,color,steps,losses,label):
    plt.title(title, fontsize=24)    #设置图像标题,fontszie标题字号
    plt.xlabel("steps", fontsize=20)  #设置横轴标题,字号
    plt.ylabel(label, fontsize=20)   #设置竖轴标题,字号
    plt.plot(steps,losses, color=color,label=label)   #根据横轴(inters)、竖轴(data)数据,用指定的颜色(color)及指定的图像描述(label)绘图
    plt.legend() #显示label
    plt.grid()  #显示网格
    plt.show()   #显示图像
# 可视化查看
    #绘制损失函数与迭代次数变化图象
draw_process("trainning loss","red",steps,losses,"trainning loss")
#绘制准确率值与迭代次数变化图象
draw_process("trainning acc","green",steps,acc,"trainning acc")
# 筛选出训练过程中损失最小和准确度最高的模型
lowest_loss_idx = losses.index(min(losses))
print(lowest_loss_idx)

损失函数
准确率


http://www.kler.cn/a/459035.html

相关文章:

  • Kernel Stack栈溢出攻击及保护绕过
  • 日志聚类算法 Drain 的实践与改良
  • HackMyVM-Airbind靶机的测试报告
  • Redis(基础篇 + 实践篇 )
  • Ansys Aqwa 中 Diffraction Analysis 的疲劳结果
  • 【服务器】上传文件到服务器并训练深度学习模型下载服务器文件到本地教程
  • Tonghttpserver6.0.1.3 使用整理(by lqw)
  • AMBA总线协议
  • 鸿蒙1.2:第一个应用
  • 深入理解MemCache
  • 【STM32项目】基于STM32单片机温湿度PM2.5粉尘甲醛环境质量监测系统wifi【完整工程资料源码】
  • 海外招聘丨 苏黎世联邦理工学院—机器学习在社会和政治科学中的应用博士后
  • SpringCloudAlibaba实战入门之路由网关Gateway过滤器(十三)
  • 【ArcGIS Pro】完整的nc文件整理表格模型构建流程及工具练习数据分享
  • Java [后端] 开发日常记录(1)
  • go-xorm连接
  • 智能充电桩物联网方案,ESP32-C3芯片应用,设备智能互联通信
  • 快排与归并排序
  • Java-将一个大列表均分成多个小列表,每个小列表包含10个元素
  • Day60 图论part10
  • 【OTA】论文笔记--《智能网联汽车整车OTA功能设计研究》智能网联汽车OTA系统设计分析报告
  • nuscenes数据集pkl文件转txt
  • 网络安全概念详解
  • 最新版Edge浏览器加载ActiveX控件技术——alWebPlugin中间件V2.0.28-迎春版发布
  • Kafka 性能提升秘籍:涵盖配置、迁移与深度巡检的综合方案
  • MIPI相关