0


【自然语言处理(NLP)】基于循环神经网络实现情感分类

【自然语言处理(NLP)】基于循环神经网络实现情感分类


在这里插入图片描述

活动地址:CSDN21天学习挑战赛


作者简介:在校大学生一枚,华为云享专家,阿里云星级博主,腾云先锋(TDP)成员,云曦智划项目总负责人,全国高等学校计算机教学与产业实践资源建设专家委员会(TIPCC)志愿者,以及编程爱好者,期待和大家一起学习,一起进步~
.
博客主页:ぃ灵彧が的学习日志
.
本文专栏:人工智能
.
专栏寄语:若你决定灿烂,山无遮,海无拦
.
在这里插入图片描述

文章目录


任务描述

本示例教程演示如何在IMDB数据集上用RNN网络完成文本分类的任务。

IMDB数据集是一个对电影评论标注为正向评论与负向评论的数据集,共有25000条文本数据作为训练集,25000条文本数据作为测试集。 该数据集的官方地址为: http://ai.stanford.edu/~amaas/data/sentiment/


一、环境配置

导入相关包

  1. import paddle
  2. import numpy as np
  3. import matplotlib.pyplot as plt
  4. import paddle.nn as nn
  5. print(paddle.__version__)# 查看当前版本# cpu/gpu环境选择,在 paddle.set_device() 输入对应运行设备。
  6. device = paddle.set_device('gpu')

二、数据准备

  1. 由于IMDB是NLP领域中常见的数据集,飞桨框架将其内置,路径为 paddle.text.datasets.Imdb。通过 mode 参数可以控制训练集与测试集。
  1. print('loading dataset...')
  2. train_dataset = paddle.text.datasets.Imdb(mode='train')
  3. test_dataset = paddle.text.datasets.Imdb(mode='test')print('loading finished')
  1. 构建了训练集与测试集后,可以通过 word_idx 获取数据集的词表。在飞桨框架2.0版本中,推荐使用padding的方式来对同一个batch中长度不一的数据进行补齐,所以在字典中,我们还会添加一个特殊的词,用来在后续对batch中较短的句子进行填充。
  1. word_dict = train_dataset.word_idx # 获取数据集的词表# add a pad token to the dict for later padding the sequence
  2. word_dict['<pad>']=len(word_dict)for k inlist(word_dict)[:5]:print("{}:{}".format(k.decode('ASCII'), word_dict[k]))print("...")for k inlist(word_dict)[-5:]:print("{}:{}".format(k ifisinstance(k,str)else k.decode('ASCII'), word_dict[k]))print("totally {} words".format(len(word_dict)))

(一)、参数设置

  1. 在这里我们设置一下词表大小,embedding的大小,batch_size,等等
  1. vocab_size =len(word_dict)+1print(vocab_size)
  2. emb_size =256
  3. seq_len =200
  4. batch_size =32
  5. epochs =2
  6. pad_id = word_dict['<pad>']
  7. classes =['negative','positive']# 生成句子列表defids_to_str(ids):# print(ids)
  8. words =[]for k in ids:
  9. w =list(word_dict)[k]
  10. words.append(w ifisinstance(w,str)else w.decode('ASCII'))return" ".join(words)
  1. 在这里,取出一条数据打印出来看看,可以用 docs 获取数据的list,用 labels 获取数据的label值,打印出来对数据有一个初步的印象。
  1. # 取出来第一条数据看看样子。
  2. sent = train_dataset.docs[0]
  3. label = train_dataset.labels[1]print('sentence list id is:', sent)print('sentence label id is:', label)print('--------------------------')print('sentence list is: ', ids_to_str(sent))print('sentence label is: ', classes[label])

(二)、用padding的方式对齐数据

文本数据中,每一句话的长度都是不一样的,为了方便后续的神经网络的计算,常见的处理方式是把数据集中的数据都统一成同样长度的数据。这包括:对于较长的数据进行截断处理,对于较短的数据用特殊的词进行填充。接下来的代码会对数据集中的数据进行这样的处理。

  1. # 读取数据归一化处理defcreate_padded_dataset(dataset):
  2. padded_sents =[]
  3. labels =[]for batch_id, data inenumerate(dataset):
  4. sent, label = data[0], data[1]
  5. padded_sent = np.concatenate([sent[:seq_len],[pad_id]*(seq_len -len(sent))]).astype('int32')
  6. padded_sents.append(padded_sent)
  7. labels.append(label)return np.array(padded_sents), np.array(labels)# traintest数据进行实例化
  8. train_sents, train_labels = create_padded_dataset(train_dataset)
  9. test_sents, test_labels = create_padded_dataset(test_dataset)# 查看数据大小及举例内容print(train_sents.shape)print(train_labels.shape)print(test_sents.shape)print(test_labels.shape)for sent in train_sents[:3]:print(ids_to_str(sent))

(三)、用Dataset 与 DataLoader 加载

将前面准备好的训练集与测试集用Dataset 与 DataLoader封装后,完成数据的加载。

  1. classIMDBDataset(paddle.io.Dataset):'''
  2. 继承paddle.io.Dataset类进行封装数据
  3. '''def__init__(self, sents, labels):
  4. self.sents = sents
  5. self.labels = labels
  6. def__getitem__(self, index):
  7. data = self.sents[index]
  8. label = self.labels[index]return data, label
  9. def__len__(self):returnlen(self.sents)
  10. train_dataset = IMDBDataset(train_sents, train_labels)
  11. test_dataset = IMDBDataset(test_sents, test_labels)
  12. train_loader = paddle.io.DataLoader(train_dataset, return_list=True,
  13. shuffle=True, batch_size=batch_size, drop_last=True)
  14. test_loader = paddle.io.DataLoader(test_dataset, return_list=True,
  15. shuffle=True, batch_size=batch_size, drop_last=True)

三、模型配置

样本出现的时间顺序对于自然语言处理、语音识别、手写体识别等应用非常重要。
对了适应这种需求,就出现了题主所说的另一种神经网络结构——循环神经网络RNN

本示例中,我们将会使用一个序列特性的RNN网络,在查找到每个词对应的embedding后,简单的取平均,作为一个句子的表示。然后用

  1. Linear

进行线性变换。为了防止过拟合,我们还使用了

  1. Dropout

RNN对具有序列特性的数据非常有效,它能挖掘数据中的时序信息以及语义信息,利用了RNN的这种能力,使深度学习模型在解决语音识别、语言模型、机器翻译以及时序分析等NLP领域的问题时有所突破。

在普通的全连接网络或CNN中,每层神经元的信号只能向上一层传播,样本的处理在各个时刻独立,因此又被成为前向神经网络(Feed-forward Neural Networks)。而在RNN中,神经元的输出可以在下一个时间戳直接作用到自身,即第i层神经元在m时刻的输入,除了(i-1)层神经元在该时刻的输出外,还包括其自身在(m-1)时刻的输出!表示成图就是这样的:

在这里插入图片描述

可以看到在隐含层节点之间增加了互连。为了分析方便,我们常将RNN在时间上进行展开,得到如图所示的结构:

在这里插入图片描述

(t+1)时刻网络的最终结果O(t+1)是该时刻输入和所有历史共同作用的结果!这就达到了对时间序列建模的目的。

  1. import paddle.nn as nn
  2. import paddle
  3. # 定义RNN网络classMyRNN(paddle.nn.Layer):def__init__(self):super(MyRNN, self).__init__()
  4. self.embedding = nn.Embedding(vocab_size,256)
  5. self.rnn = nn.SimpleRNN(256,256, num_layers=2, direction='forward',dropout=0.5)
  6. self.linear = nn.Linear(in_features=256*2, out_features=2)
  7. self.dropout = nn.Dropout(0.5)defforward(self, inputs):
  8. emb = self.dropout(self.embedding(inputs))#output形状大小为[batch_size,seq_len,num_directions * hidden_size]#hidden形状大小为[num_layers * num_directions, batch_size, hidden_size]#把前向的hidden与后向的hidden合并在一起
  9. output, hidden = self.rnn(emb)
  10. hidden = paddle.concat((hidden[-2,:,:], hidden[-1,:,:]), axis =1)#hidden形状大小为[batch_size, hidden_size * num_directions]
  11. hidden = self.dropout(hidden)return self.linear(hidden)

四、模型训练

(一)、可视化定义

  1. # 可视化定义defdraw_process(title,color,iters,data,label):
  2. plt.title(title, fontsize=24)
  3. plt.xlabel("iter", fontsize=20)
  4. plt.ylabel(label, fontsize=20)
  5. plt.plot(iters, data,color=color,label=label)
  6. plt.legend()
  7. plt.grid()
  8. plt.show()

(二)、对模型进行封装

  1. # 对模型进行封装deftrain(model):
  2. model.train()
  3. opt = paddle.optimizer.Adam(learning_rate=0.001, parameters=model.parameters())
  4. steps =0
  5. Iters, total_loss, total_acc =[],[],[]for epoch inrange(epochs):for batch_id, data inenumerate(train_loader):
  6. steps +=1
  7. sent = data[0]
  8. label = data[1]
  9. logits = model(sent)
  10. loss = paddle.nn.functional.cross_entropy(logits, label)
  11. acc = paddle.metric.accuracy(logits, label)if batch_id %500==0:# 500epoch输出一次结果
  12. Iters.append(steps)
  13. total_loss.append(loss.numpy()[0])
  14. total_acc.append(acc.numpy()[0])print("epoch: {}, batch_id: {}, loss is: {}".format(epoch, batch_id, loss.numpy()))
  15. loss.backward()
  16. opt.step()
  17. opt.clear_grad()# evaluate model after one epoch
  18. model.eval()
  19. accuracies =[]
  20. losses =[]for batch_id, data inenumerate(test_loader):
  21. sent = data[0]
  22. label = data[1]
  23. logits = model(sent)
  24. loss = paddle.nn.functional.cross_entropy(logits, label)
  25. acc = paddle.metric.accuracy(logits, label)
  26. accuracies.append(acc.numpy())
  27. losses.append(loss.numpy())
  28. avg_acc, avg_loss = np.mean(accuracies), np.mean(losses)print("[validation] accuracy: {}, loss: {}".format(avg_acc, avg_loss))
  29. model.train()# 保存模型
  30. paddle.save(model.state_dict(),str(epoch)+"_model_final.pdparams")# 可视化查看
  31. draw_process("trainning loss","red",Iters,total_loss,"trainning loss")
  32. draw_process("trainning acc","green",Iters,total_acc,"trainning acc")
  33. model = MyRNN()
  34. train(model)

输出结果如下图1所示:

在这里插入图片描述


五、模型评估

  1. '''
  2. 模型评估
  3. '''
  4. model_state_dict = paddle.load('1_model_final.pdparams')# 导入模型
  5. model = MyRNN()
  6. model.set_state_dict(model_state_dict)
  7. model.eval()
  8. accuracies =[]
  9. losses =[]for batch_id, data inenumerate(test_loader):
  10. sent = data[0]
  11. label = data[1]
  12. logits = model(sent)
  13. loss = paddle.nn.functional.cross_entropy(logits, label)
  14. acc = paddle.metric.accuracy(logits, label)
  15. accuracies.append(acc.numpy())
  16. losses.append(loss.numpy())
  17. avg_acc, avg_loss = np.mean(accuracies), np.mean(losses)print("[validation] accuracy: {}, loss: {}".format(avg_acc, avg_loss))

六、模型预测

  1. defids_to_str(ids):
  2. words =[]for k in ids:
  3. w =list(word_dict)[k]
  4. words.append(w ifisinstance(w,str)else w.decode('UTF-8'))return" ".join(words)
  5. label_map ={0:"negative",1:"positive"}# 导入模型
  6. model_state_dict = paddle.load('1_model_final.pdparams')
  7. model = MyRNN()
  8. model.set_state_dict(model_state_dict)
  9. model.eval()for batch_id, data inenumerate(test_loader):
  10. sent = data[0]
  11. results = model(sent)
  12. predictions =[]for probs in results:# 映射分类label
  13. idx = np.argmax(probs)
  14. labels = label_map[idx]
  15. predictions.append(labels)for i,pre inenumerate(predictions):print(' 数据: {} \n 情感: {}'.format(ids_to_str(sent[0]), pre))breakbreak

输出结果如下图2所示:

在这里插入图片描述


总结

本系列文章内容为根据清华社出版的《机器学习实践》所作的相关笔记和感悟,其中代码均为基于百度飞桨开发,若有任何侵权和不妥之处,请私信于我,定积极配合处理,看到必回!!!

最后,引用本次活动的一句话,来作为文章的结语~( ̄▽ ̄~)~:

学习的最大理由是想摆脱平庸,早一天就多一份人生的精彩;迟一天就多一天平庸的困扰。

ps:更多精彩内容还请进入本文专栏:人工智能,进行查看,欢迎大家支持与指教啊~( ̄▽ ̄~)~

在这里插入图片描述


本文转载自: https://blog.csdn.net/m0_54754302/article/details/126439308
版权归原作者 云曦智划 所有, 如有侵权,请联系我们删除。

“【自然语言处理(NLP)】基于循环神经网络实现情感分类”的评论:

还没有评论