7.1.3.2、使用飞桨实现基于LSTM的情感分析模型的网络定义

本文涉及的产品
函数计算FC,每月15万CU 3个月
简介: 该文章详细介绍了如何使用飞桨框架实现基于LSTM的情感分析模型,包括网络定义、模型训练、评估和预测的完整流程,并提供了相应的代码实现。


7.1.3、使用飞桨实现基于LSTM的情感分析模型

3.2 网络定义

在讲解卷积神经网络的的章节,我们详细列出了每一种神经网络使用基础算子拼装的详细网络配置,但实际上对于一些常用的网络结构,飞桨框架提供了现成的中高层函数支持。下面用于情感分析的长短时记忆模型就使用paddle.nn.LSTMAPI实现。如果读者对使用基础算子拼装LSTM的内容感兴趣,可以查阅paddle.nn.LSTM类的源代码。

3.2.1 seq2vec的文本表示

在讲解情感分析网络搭建之前,我们先介绍一下seq2vec

句子情感分析的关键技术是如何将文本表示成一个携带语义的文本向量。随着深度学习技术的快速发展,目前常用的文本表示技术有LSTM,GRU,RNN等方法。 PaddleNLP提供了一系列的文本表示技术,集成在seq2vec模块中。

paddlenlp.seq2vec模块的作用是将输入的序列文本,表示成一个语义向量。

图10:paddlenlp.seq2vec示意图

本模块主要使用了seq2vec的LSTMEncoder部分,其核心结构是使用的LSTM,LSTMEncoder的核心是实现下面的内容:

  • LSTMEncoder:
  • get_input_dim : encoder 的输入的维度
  • get_output_dim : encoder 的输出维度
  • forward : 前向传播的逻辑

LSTMEncoder核心实现代码如下:

class LSTMEncoder(nn.Layer):
    def __init__(self,
                 input_size,
                 hidden_size,
                 num_layers=1,
                 direction="forward",
                 dropout=0.0,
                 pooling_type=None,
                 **kwargs):
        super().__init__()
        self._input_size = input_size
        self._hidden_size = hidden_size
        self._direction = direction
        self._pooling_type = pooling_type
        # LSTM层
        self.lstm_layer = nn.LSTM(
            input_size=input_size,
            hidden_size=hidden_size,
            num_layers=num_layers,
            direction=direction,
            dropout=dropout,
            **kwargs)
    def get_input_dim(self):
        # 获取LSTM的输入的维度
        return self._input_size
    def get_output_dim(self):
        # 获取LSTM的输出的维度
        if self._direction == "bidirect":
            return self._hidden_size * 2
        else:
            return self._hidden_size
    def forward(self, inputs, sequence_length):
        encoded_text, (last_hidden, last_cell) = self.lstm_layer(
            inputs, sequence_length=sequence_length)
        # 如果是双向的LSTM,则输出最后一个时刻
        if self._direction != 'bidirect':
            # 输出最后一层的向量
            output = last_hidden[-1, :, :]
        else:
            # 单向的LSTM的实现,把倒数第一层和倒数第二层的向量拼接后输出
            output = paddle.concat(
                    (last_hidden[-2, :, :], last_hidden[-1, :, :]), axis=1)
        return output

3.2.2 情感分析网络

图11:情感分析网络

上图显示的是情感分析网络,分为文本向量化,序列学习,特征学习,分类四部分:

1.文本向量化:这部分的作用就是把文本id的形式转换成稠密的向量的形式;

2.序列学习:序列学习是seq2vec层,具体使用的是双向LSTM用来学习文本序列的相关关系

3.特征学习:特征学习使用了全连接层,主要是对前面的序列特征进一步学习,得到全局的特征信息;

4.分类:分类是输出层,本质上是一个全连接层,主要是把特征转化成情感类别概率输出

情感分析网络代码实现如下:

import paddlenlp as ppnlp
class LSTMModel(nn.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=96):
        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)
        # 特征学习
        # LSTMEncoder.get_output_dim()方法可以获取经过encoder之后的文本表示hidden_size
        self.fc = nn.Linear(self.lstm_encoder.get_output_dim(), fc_hidden_size)
        # 输出层
        # 最后的分类器
        self.output_layer = nn.Linear(fc_hidden_size, num_classes)
     # forwad函数即为模型前向计算的函数,它有两个输入,分别为:
    # input为输入的训练文本,其shape为[batch_size, max_seq_len]
    # seq_len训练文本对应的真实长度,其shape维[batch_size]
    def forward(self, text, seq_len):
        # 输入的文本的维度(batch_size, num_tokens, embedding_dim)
        embedded_text = self.embedder(text)
        # lstm的输出的维度: (batch_size, num_tokens, num_directions*lstm_hidden_size)
        # 如果lstm是双向的,则num_directions = 2,如果是单向的则num_directions的维度是1
        text_repr = self.lstm_encoder(embedded_text, sequence_length=seq_len)
        # 全连接层的的维度是(batch_size, fc_hidden_size)
        fc_out = paddle.tanh(self.fc(text_repr))
        # 输出层的维度(batch_size, num_classes)
        logits = self.output_layer(fc_out)
        # probs 分类概率值
        probs = F.softmax(logits, axis=-1)
        return probs

3.3 模型训练

在完成模型定义之后,我们就可以开始训练模型了。当训练结束以后,我们可以使用测试集合评估一下当前模型的效果,代码如下:

# 定义训练参数
epoch_num = 4
batch_size = 128
learning_rate = 5e-5
dropout_rate = 0.2
num_layers = 1
hidden_size = 256
embedding_size = 256
vocab_size=len(vocab)
print(vocab_size)
# 实例化LSTM模型
model= LSTMModel(
        vocab_size,
        num_classes=2,
        emb_dim=embedding_size,
        lstm_layers=num_layers,
        direction='bidirectional',
        padding_idx=vocab['[PAD]'])
# 指定优化策略,更新模型参数
optimizer = paddle.optimizer.Adam(learning_rate=learning_rate, beta1=0.9, beta2=0.999, parameters= model.parameters())

W0402 11:50:48.497135 8808 device_context.cc:447] Please NOTE: device: 0, GPU Compute Capability: 7.0, Driver API Version: 10.1, Runtime API Version: 10.1

W0402 11:50:48.502187 8808 device_context.cc:465] device: 0, cuDNN Version: 7.6.

paddle.seed(0)
random.seed(0)
np.random.seed(0)
# 定义训练函数
# 记录训练过程中的损失变化情况,可用于后续画图查看训练情况
losses = []
steps = []
def train(model):
    # 开启模型训练模式
    model.train()
    
    global_step =  0
    for epoch in range(epoch_num):
        for step, (sentences,valid_length, labels) in enumerate(train_loader):
        
            # 前向计算,将数据feed进模型,并得到预测的情感标签和损失
            logits = model(sentences,valid_length)
            # 计算损失
            loss = F.cross_entropy(input=logits, label=labels, soft_label=False)
            loss = paddle.mean(loss)
            # 后向传播
            loss.backward()
            # 更新参数
            optimizer.step()
            # 清除梯度
            optimizer.clear_grad()
            
            global_step+=1
            if global_step % 100 == 0:
                # 记录当前步骤的loss变化情况
                losses.append(loss.numpy()[0])
                steps.append(step)
                # 打印当前loss数值
                print("epoch %d, step %d, loss %.3f" % (epoch,global_step, loss.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))

epoch 0, step 100, loss 0.690

epoch 1, step 200, loss 0.680

epoch 1, step 300, loss 0.560

epoch 2, step 400, loss 0.468

从上述的loss变化过程可以看到,loss是不断在降低的。

tips

使用GPU训练,设置epoch为20,训练2分钟左右,loss降低到0.313,大概1100个step之后就可以达到90.50%的准确率。训练的epoch越多,精度可能会更高,但不是绝对的会升高,会出现过拟合现象(训练集的loss降低,验证集的精度反而升高的现象)。

3.4 模型评估

在模型训练阶段,保存了训练完成的模型参数。因此在模型评估阶段,首先需要加载保存到磁盘的模型参数,在获得完整的模型之后,利用相应的测试集开始进行模型评估

@paddle.no_grad()
def evaluate(model):
    # 开启模型测试模式,在该模式下,网络不会进行梯度更新
    model.eval()
    # 定义以上几个统计指标
    tp, tn, fp, fn = 0, 0, 0, 0
    
    for sentences,valid_lens, labels in test_loader:
    
        # 获取模型对当前batch的输出结果
        logits = model(sentences,valid_lens)
        
        # 使用softmax进行归一化
        probs = F.softmax(logits)
        # 把输出结果转换为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
    # 整体准确率
    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)

TP: 512

FP: 99

TN: 508

FN: 81

Accuracy: 0.8500

从输出结果可以看到,模型的准确率达到了86%。TP数量是481,FP的数量是56,TN的数量是551,FN的数量是112.(每次运行结果稍有差异)

3.5 模型预测

模型预测部分就是把文本转换成Tensor形式的ID行时候,利用模型预测得到输出,然后选取概率最大值的索引就行了。

label_map = {0: 'negative', 1: 'positive'}
text=test_ds[0]['text']
# 文本转换成ID的形式
input_ids=tokenizer.encode(text)
valid_lens=len(input_ids)
# 转换成Tensor的形式
input_ids=paddle.to_tensor([input_ids])
valid_lens=paddle.to_tensor([valid_lens])
# 模型预测
probs=model(input_ids,valid_lens)
# 取概率最大值的ID
idx = np.argmax(probs, axis=-1)
idx = idx.tolist()
# 得到预测标签
labels = [label_map[i] for i in idx]
# 看看预测样例分类结果
print('Data: {} \t Label: {}'.format(text, labels[0]))

Data: 这个宾馆比较陈旧了,特价的房间也很一般。总体来说一般 Label: negative

四、思考一下

[1] 情感分析任务对你有什么启发?

[2] 除了LSTM,你还能想到那些其他方法,构造一个句子的向量表示?

[3] 对一个句子生成一个单一的向量表示有什么缺点,你还知道其他方式吗?

相关实践学习
【文生图】一键部署Stable Diffusion基于函数计算
本实验教你如何在函数计算FC上从零开始部署Stable Diffusion来进行AI绘画创作,开启AIGC盲盒。函数计算提供一定的免费额度供用户使用。本实验答疑钉钉群:29290019867
建立 Serverless 思维
本课程包括: Serverless 应用引擎的概念, 为开发者带来的实际价值, 以及让您了解常见的 Serverless 架构模式
相关文章
|
2月前
|
消息中间件 存储 Serverless
函数计算产品使用问题之怎么访问网络附加存储(NAS)存储模型文件
函数计算产品作为一种事件驱动的全托管计算服务,让用户能够专注于业务逻辑的编写,而无需关心底层服务器的管理与运维。你可以有效地利用函数计算产品来支撑各类应用场景,从简单的数据处理到复杂的业务逻辑,实现快速、高效、低成本的云上部署与运维。以下是一些关于使用函数计算产品的合集和要点,帮助你更好地理解和应用这一服务。
|
2月前
|
Kubernetes 负载均衡 网络安全
Kubernetes 网络模型与实践
【8月更文第29天】Kubernetes(K8s)是当今容器编排领域的佼佼者,它提供了一种高效的方式来管理容器化应用的部署、扩展和运行。Kubernetes 的网络模型是其成功的关键因素之一,它支持服务发现、负载均衡和集群内外通信等功能。本文将深入探讨 Kubernetes 的网络模型,并通过实际代码示例来展示服务发现和服务网格的基本概念及其实现。
62 1
|
4天前
|
安全 NoSQL Java
一文搞懂网络通信的基石✅IO模型与零拷贝
【10月更文挑战第1天】本文深入探讨了网络通信中的IO模型及其优化方法——零拷贝技术。首先介绍了IO模型的概念及五种常见类型:同步阻塞、同步非阻塞、多路复用、信号驱动和异步IO模型。文章详细分析了每种模型的特点和适用场景,特别是多路复用和异步IO在高并发场景中的优势。接着介绍了零拷贝技术,通过DMA直接进行数据传输,避免了多次CPU拷贝,进一步提升了效率。最后总结了各种模型的优缺点,并提供了相关的代码示例和资源链接。
一文搞懂网络通信的基石✅IO模型与零拷贝
|
25天前
|
机器学习/深度学习 人工智能 算法
鸟类识别系统Python+卷积神经网络算法+深度学习+人工智能+TensorFlow+ResNet50算法模型+图像识别
鸟类识别系统。本系统采用Python作为主要开发语言,通过使用加利福利亚大学开源的200种鸟类图像作为数据集。使用TensorFlow搭建ResNet50卷积神经网络算法模型,然后进行模型的迭代训练,得到一个识别精度较高的模型,然后在保存为本地的H5格式文件。在使用Django开发Web网页端操作界面,实现用户上传一张鸟类图像,识别其名称。
73 12
鸟类识别系统Python+卷积神经网络算法+深度学习+人工智能+TensorFlow+ResNet50算法模型+图像识别
|
1天前
|
自动驾驶 物联网 5G
|
1天前
|
Kubernetes 负载均衡 Docker
Docker 网络模型
【10月更文挑战第3天】
14 1
|
3天前
|
网络协议 网络架构
【网络】TCP/IP 五层网络模型:数据链路层
【网络】TCP/IP 五层网络模型:数据链路层
17 1
|
3天前
|
网络协议 算法 程序员
【网络】TCP/IP 五层网络模型:网络层
【网络】TCP/IP 五层网络模型:网络层
33 1
|
3天前
|
XML JSON 网络协议
【网络】TCP/IP五层网络模型:应用层
【网络】TCP/IP五层网络模型:应用层
12 1
|
17天前
|
机器学习/深度学习 数据采集 网络安全
使用Python实现深度学习模型:智能网络安全威胁检测
使用Python实现深度学习模型:智能网络安全威胁检测
71 5

热门文章

最新文章