深度学习:LSTM循环神经网络实现评论情感分析
目录
一、任务介绍
1.任务要求
2.信息内容
3.待思考问题
二、问题解决
1.将评论内容转换成语料库
2.获取每条评论的词向量、标签和长度
3.数据打包
4.建立LSTM循环神经网络模型
1.主程序代码
2.模型代码
5.建立训练集函数和测试集函数
一、任务介绍
1.任务要求
- 项目任务:对微博评论信息的情感分析,建立模型,自动识别评论信息的情绪状态。
2.信息内容
- 第一行是标头
- 每一行顶格标着每个评论代表的情绪
-
{0: '喜悦', 1: '愤怒', 2: '厌恶', 3: '低落'}

3.待思考问题
- 思考:向模型中传递数据时,需要提前处理好数据
1、目标:将评论内容转换为词向量。
2、每个词/字转换为词向量长度(维度)200
3、每一次传入的词/字的个数是否就是评论的长度?
应该是固定长度,每次传入数据与图像相似。 例如选择长度为32。则传入的数据为32*200
4、一条评论如果超过80个词/字怎么处理?
直接删除后面的内容
5、一条评论如果没有70个词/字怎么处理?
缺少的内容,统一使用一个数字(非词/字的数字)替代。
6、如果语料库中的词/字太多是否可以压缩?
可以,某些词/字出现的频率比较低,可能训练不出特征。因此可以选择频率比较高的词来训练。例如选择4760个。
7、被压缩的词/字如何处理?
可以统一使用一个数字(非词/字的数字)替代。
二、问题解决
1.将评论内容转换成语料库
- 遍历每一行评论,除去第一行
- 取每行索引2之后的内容
- 然后对每行评论分字
- 获取每个字出现的次数,次数等于1的去掉
- 然后字作为键,出现次数作为值,将其装入字典
- 按照值的大小进行降序排列,只保留前4760个字
- 将值更新为索引,之后将<UNK>和<PAD>添加在字典末尾
- 至此获取了整个文件的语料库以及每个字的独热编码
- 将其以二进制形式保存在pkl文件里
from tqdm import tqdm
import pickle as pklMAX_VOCAB_SIZE = 4760 # 词表长度限制
UNK, PAD = '<UNK>', '<PAD>' # 未知字符号 padding 无含义 unk 识别不出来的字def build_vocab(file_path, max_size, min_freq):'''功能:基于文本内容建立词表vocab,vocab中包含语料库中的字参数:file_path: 需要读取的语料库的路径max_size: 获取词频最高的前max_size个词.min_freq 剔除字频低于min_freq个的词'''tokenizer = lambda x: [y for y in x] # 分字函数vocab_dic = {} # 用于保存词的字典with open(file_path, 'r', encoding='utf8') as f:i = 0for line in tqdm(f): # 用来显示循环的进度条if i == 0:i += 1continuelin = line[2:].strip() # 获取评论内容 剔除标签 不用split分割 因为评论内容中可能存在逗号if not lin:continue # 如果lin中没有内容则 continuefor word in tokenizer(lin):vocab_dic[word] = vocab_dic.get(word, 0) + 1 # 统计每个字出现的次数 .get(key,default) 这个键有值就返回该值 , 没有的话返回默认值vocab_list = sorted([_ for _ in vocab_dic.items() if _[1] > min_freq], key=lambda x: x[1], reverse=True)[:max_size]vocab_dic = {word_count[0]: idx for idx, word_count in enumerate(vocab_list)}vocab_dic.update({UNK: len(vocab_dic), PAD: len(vocab_dic) + 1})print(vocab_dic)pkl.dump(vocab_dic, open('simplifyweibo_4_moods.pkl', 'wb')) # 将字典以二进制形式保存在pkl 统计完所有文字 每个文字都有独热编码print(f"Vocab size:{len(vocab_dic)}") # 将评论的内容根据词表vocab_dic 转换成词向量return vocab_dicif __name__ == '__main__':vocab = build_vocab('simplifyweibo_4_moods.csv', MAX_VOCAB_SIZE, 1) # 获取语料库中每个字的词向量pass# print('vocab')
输出:
- 字典,键是字 值是该字的词向量,整体按照字出现的次数排序

2.获取每条评论的词向量、标签和长度
- 读取评论文件
- 遍历每一行,
- 获取评论标签、评论内容以及评论的真实长度
- 判断评论长度是否大于70
- 若大于,则只取70个字
- 若小于,则在末尾填充<PAD>
- 读取上一步保存的语料库文件
- 判断每条评论中的每个字是否在内,
- 不在内的将其转换成<UNK>
- 在内的获取该字的值
- 一条评论的值装入一个列表,加上该评论的标签和真实长度,将其装入一个元组然后放入另一个列表中
- 至此列表中装着每条评论的词向量、标签和长度
- 取前80%作为训练集,80%-90%作为验证集,90%-100%作为测试集
from tqdm import tqdm
import pickle as pkl
import random
import torchUNK, PAD = '<UNK>', '<PAD>' # 未知字符号def load_dataset(path, pad_size=70):contents = []vocab = pkl.load(open('simplifyweibo_4_moods.pkl', 'rb')) # 读取vocab文件tokenizer = lambda x: [y for y in x]with open(path, 'r', encoding='utf8') as f:i = 0for line in tqdm(f):if i == 0:i += 1continueif not line:continuelabel = int(line[0]) # 获取该行评论标签content = line[2:].strip('\n') # 获取该行评论内容 去掉末尾换行符words_line = []token = tokenizer(content) # 将每一行内容进行分字seq_len = len(token) # 获取每一行评论的字长if pad_size:if len(token) < pad_size: # 如果一行字少于70 则补充<PAD>token.extend([PAD] * (pad_size - len(token)))else:token = token[:pad_size] # 只取当前评论前70个seq_len = pad_size # 将当前评论长度换成70for word in token:words_line.append(vocab.get(word, vocab.get(UNK)))contents.append((words_line, int(label), seq_len))random.shuffle(contents) # 打乱顺序train_data = contents[:int(len(contents) * 0.8)] # 前80%为训练dev_data = contents[int(len(contents) * 0.8):int(len(contents) * 0.9)]test_data = contents[int(len(contents) * 0.9):]return vocab, train_data, dev_data, test_dataif __name__ == '__main__':vocab, train_data, dev_data, test_data = load_dataset('simplifyweibo_4_moods.csv')print(train_data, dev_data, test_data)
输出:
- 每一个元组第一个元素是列表,列表里装着该条评论每个字的独热编码
- 第二个元素是该评论的标签
- 第三个元素使该评论的真实长度

3.数据打包
- 将数据及其标签打包成128条评论一个的包,并将其转换成张量
- 通过if判断,将最后一个不满128的数据打成一个包,同样转换成张量
- 最后得到每条评论的独热编码、标签和长度的张量类型数据
- 将其传入GPU
class DatasetIterater(object):"""将数据batches切分为batch_size的包"""def __init__(self, batches, batch_size, device):self.batches = batchesself.batch_size = batch_sizeself.device = deviceself.n_batches = len(batches) // batch_size # 数据划分batch的数量self.residue = False # 记录划分后的数据是否存在剩余的数据if len(batches) % self.n_batches != 0: # 表示有余数self.residue = Trueself.index = 0def _to_tensor(self, datas):x = torch.LongTensor([_[0] for _ in datas]).to(self.device) # 评论内容y = torch.LongTensor([_[1] for _ in datas]).to(self.device) # 评论情感 最好转换成LongTensor# pad前的长度seq_len = torch.LongTensor([_[2] for _ in datas]).to(self.device)return (x, seq_len), ydef __next__(self):if self.residue and self.index == self.n_batches:batches = self.batches[self.index * self.batch_size:len(self.batches)]self.index += 1batches = self._to_tensor(batches)return batcheselif self.index > self.n_batches:self.index = 0raise StopIteration # 停止迭代else:batches = self.batches[self.index * self.batch_size:(self.index + 1) * self.batch_size]self.index += 1batches = self._to_tensor(batches)return batchesdef __iter__(self):return selfdef __len__(self):if self.residue:return self.n_batches + 1else:return self.n_batches
4.建立LSTM循环神经网络模型
1.主程序代码
- 下载腾讯自然语言处理模型嵌入层的参数并将其转换成张量类型
- 嵌入层的输出神经元设置为200
embedding_pretrained = torch.tensor(np.load('embedding_Tencent.npz')['embeddings'].astype('float32'))
# embedding_pretrained = None # 不使用外部训练的词向量
embed = embedding_pretrained.size(1) if embedding_pretrained is not None else 200
class_list = ['喜悦', '愤怒', '厌恶', '低落']
num_classes = len(class_list)
model = demo4TextRNN.Model(embedding_pretrained, len(vocab), embed, num_classes).to(device)
test(model, test_iter, class_list)
2.模型代码
- 告诉模型填充词的独热编码是多少
import torch.nn as nnclass Model(nn.Module):def __init__(self, embedding_pretrained, n_vocab, embed, num_classes):super(Model, self).__init__()if embedding_pretrained is not None:self.embedding = nn.Embedding.from_pretrained(embedding_pretrained, padding_idx=n_vocab - 1, freeze=False)# embedding_pretrained: Tensor,形状为(n_vocab, embed),其中n_vocab是词汇表大小,embed是嵌入维度。# freeze: 是否冻结embedding层的权重else:self.embedding = nn.Embedding(n_vocab, embed, padding_idx=n_vocab - 1)# padding_idx默认None 如果指定 则参数不会对梯度产生影响self.lstm = nn.LSTM(embed, 128, 3, bidirectional=True, batch_first=True, dropout=0.3)# embed: 输入特征的维度或词嵌入的大小。# 128: LSTM 隐藏层的大小,也就是隐藏状态的维度。整数,表示 LSTM 隐藏层输出的特征数量。# 3: LSTM层数(堆叠的LSTM层数量)。# bidirectional = True: 使用双向LSTM,考虑前向和后向序列信息。# batch_first = True: 输入输出形状为(batch_size, seq_length, input_size)。# dropout = 0.3: 在LSTM层之间应用的dropout比率(30% 表示 30% 的神经元会被丢弃)。self.fc = nn.Linear(128 * 2, num_classes) # 因为是双向 所以 *2def forward(self, x):x, _ = x # 只提取评论的独热编码out = self.embedding(x)out, _ = self.lstm(out) # 一个字256个特征 因为是双向的out = self.fc(out[:, -1, :]) #return out
5.建立训练集函数和测试集函数
- 传入模型,训练集数据,验证集数据,测试集数据和分类
- 后面的操作与多分类时函数逻辑一致
import torch.optim
import numpy as np
from sklearn import metrics
import torch.nn.functional as Fdef evaluate(class_list, model, data_iter, test=False):model.eval()loss_total = 0predict_all = np.array([], dtype=int)labels_all = np.array([], dtype=int)with torch.no_grad():for texts, labels in data_iter:outputs = model(texts)loss = F.cross_entropy(outputs, labels)loss_total += losslabels = labels.data.cpu().numpy() # NumPy 操作仅在 CPU 张量上有效predict = torch.max(outputs.data, 1)[1].cpu().numpy()labels_all = np.append(labels_all, labels)predict_all = np.append(predict_all, predict)acc = metrics.accuracy_score(labels_all, predict_all)if test:report = metrics.classification_report(labels_all, predict_all, target_names=class_list, digits=4)return acc, loss_total / len(data_iter), reportreturn acc, loss_total / len(data_iter)def test(model, test_iter, class_list):model.load_state_dict(torch.load('TextRNN.ckpt')) # 使用最优模型model.eval()test_acc, test_loss, test_report = evaluate(class_list, model, test_iter, test=True)msg = 'Test Loss:{0:>5.2},Test Acc:{1:>6.2%}'print(msg.format(test_loss, test_acc))print(test_report)def train(model, train_iter, dev_iter, test_iter, class_list):model.train()optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)total_batch = 0 # 记录进行到多少batchdev_best_loss = float('inf') # 表示无穷大last_improve = 0 # 记录上次验证集loss下降的batch数flag = False # 记录是否很久没有效果提升epochs = 2for epoch in range(epochs):print("{}/{}".format(epoch + 1, epochs))for i, (trains, labels) in enumerate(train_iter):outputs = model(trains)loss = F.cross_entropy(outputs, labels)model.zero_grad()loss.backward()optimizer.step()if total_batch % 100 == 0:predict = torch.max(outputs.data, 1)[1].cpu() # 第一个参数是要计算的张量,第二个参数是维度。在这里,1 表示按行计算最大值 返回元组 (最大值 对应的索引)train_acc = metrics.accuracy_score(labels.data.cpu(), predict)dev_acc, dev_loss = evaluate(class_list, model, dev_iter)if dev_loss < dev_best_loss:dev_best_loss = dev_loss # 保存最优模型torch.save(model.state_dict(), 'TextRNN.ckpt')last_improve = total_batchmsg = 'Iter:{0:>6},Train Loss:{1:>5.2},Train Acc:{2:>6.2%},Val Loss:{3:>5.2},Val Acc:{4:>6.2%}'print(msg.format(total_batch, loss.item(), train_acc, dev_loss, dev_acc))model.train()total_batch += 1if total_batch - last_improve > 10000:print('no')flag = Trueif flag:break
最后在主程序使用测试集测试一下
由于样本数据不太均衡,所以有些种类的正确率比较低

相关文章:
深度学习:LSTM循环神经网络实现评论情感分析
目录 一、任务介绍 1.任务要求 2.信息内容 3.待思考问题 二、问题解决 1.将评论内容转换成语料库 2.获取每条评论的词向量、标签和长度 3.数据打包 4.建立LSTM循环神经网络模型 1.主程序代码 2.模型代码 5.建立训练集函数和测试集函数 一、任务介绍 1.任务要求 项…...
基于Arduino的环境监测装置
基于Arduino的环境监测装置 引言痛点功能前期准备软件硬件 项目开发硬件开发软件开发 功能演示更多精彩,欢迎关注 引言 本项目使用机智云Gokit2.0开发板,实现基于Arduino的环境监测装置,解决目前大多数人对环境数据要求逐渐增高的痛点。 痛…...
深度学习:模型攻击(Model Attack)详解
模型攻击(Model Attack)详解 模型攻击通常指在机器学习和人工智能领域中,故意设计的行为或方法,旨在操纵或欺骗机器学习模型的输出。这类攻击可能导致模型做出错误的决策或泄露敏感信息,对于安全性至关重要的应用&…...
CesiumLab介绍
软考鸭小程序 学软考,来软考鸭! 提供软考免费软考讲解视频、题库、软考试题、软考模考、软考查分、软考咨询等服务 CesiumLab是一个围绕Cesium平台设计的完整易用的数据预处理工具集,它旨在最大化提升三维数据可视化效率。本文将详细介绍CesiumLab的安装、主要功能…...
PyQt 入门教程(3)基础知识 | 3.2、加载资源文件
文章目录 一、加载资源文件1、PyQt5加载资源文件2、PyQt6加载资源文件 一、加载资源文件 常见的资源文件有图像与图标,下面分别介绍下加载资源文件的常用方法 1、PyQt5加载资源文件 2、PyQt6加载资源文件 PyQt6版本暂时没有提供pyrcc工具,下面介绍下在不…...
老照片修复工作流教程:用 ComfyUI 轻松还原历史记忆
你是否有过这样的遗憾? 那些珍贵的老照片因为时间的流逝,早已失去了当年的色彩,变得模糊、褪色,甚至破损? 今天带你了解如何使用 ComfyUI 的老照片修复工作流,通过简单的几步操作,在短短十几秒…...
ESP-IDF Blink实例学习
文章目录 一、引言二、工程创建1、打开vscode点击ESP-IDF资源管理器2、选择ESP-IDF框架3、选择Show Examples4、选择blink5、点击Create project using example blink ,选择创建目录6、创建完成 三、硬件电路LED管脚分配四、修改menuconfig五、编译和下载运行 一、引言 Blink实…...
QT QML 练习8-Simple Transformations
简单的转换(Simple Transformations) 转换操作改变了一个对象的几何状态。QML元素对象通常能够被平移,旋转,缩放。下面我们将讲解这些简单的操作和一些更高级的用法。 我们先从一个简单的转换开始。用下面的场景作为我们学习的开始…...
低空产业园搭建技术详解
低空产业园的搭建技术是一个复杂而系统的工程,涉及多个方面的技术和策略。以下是对低空产业园搭建技术的详细解析: 一、规划与设计 1. 总体规划:低空产业园的规划需要结合地方经济发展、产业基础、政策导向等因素,制定科学合理的…...
Python网络爬虫从入门到实战
目录 引言 一、网络爬虫的概念 二、 网络爬虫的基本工作流程 (一)过程: (二)安装requests模块和beautifulsoup4模块 (三)requests库的使用 1、requests库的基本介绍 2、导入requests库的…...
探索Theine:Python中的AI缓存新贵
文章目录 探索Theine:Python中的AI缓存新贵背景:为何选择Theine?Theine是什么?如何安装Theine?简单的库函数使用方法场景应用场景一:Web应用缓存场景二:分布式系统中的数据共享场景三࿱…...
js拼图(神鹰黑手哥)
直接上代码 再解释 这是最终效果图 css代码如下 * {margin: 0;padding: 0;}body {height: 800px;width: 100%;background-color: blanchedalmond;display: flex;justify-content: space-around;align-items: center;position: relative;}.img-box {display: flex;flex-wrap: w…...
值得推荐的五款数据恢复工具!!
当谈及我们日常工作生活中无法避免的数据丢失情况时,很多小伙伴一定急得如热锅上的蚂蚁,无助与忐忑。特别是现在社会,信息量庞大,一旦电脑上的重要数据不慎丢失,无论是工作文件、生活照片还是珍贵的视频,都…...
股票金融市场中的tick,分钟,日线数据
在金融市场中,股票数据的分析对于投资者来说至关重要。股票数据可以根据时间粒度的不同,分为几种不同的类型,包括Tick数据、分钟数据和日线数据。下面将详细介绍这些数据类型,并对比它们之间的差别。 Tick数据 Tick数据…...
OKG Research:如何衡量链上数据的开放价值?
在新加坡Token2049期间,欧科云链研究院受邀参加Bloomberg主办的企业另类资产投资峰会2024,与多位专家围绕未来数据形态与前景进行了深入交流。 活动后,欧科云链研究院负责人Lola Wang与资深研究员Jason Jiang在大公网发表署名文章《如何衡量…...
向日葵下载教程以及三款远程控制工具推荐!!!
向日葵远程控制下载教程!! 亲爱的朋友们,如果你对远程控制软件有所需求,那么向日葵绝对是一个不错的选择。现在我将带你走一遍向日葵的下载流程。 1. 打开你的浏览器,输入“向日葵官方网站”,进入官方网站…...
Studio One 6中文版及最新功能介绍 Studio One 6音乐软件安装包
Studio One 6是一款功能强大的数字音频工作站(DAW),专为音乐制作和录音而设计。它提供了从初学者到专业人士的所有需求,无论是创作、录音、混音还是母带处理。 Studio One 6拥有直观的界面和强大的虚拟乐器、插件和音频处理工具&a…...
【数据结构】栈和队列 + 经典算法题
目录 前言 一、栈 二、栈的实现 三、栈的循环遍历演示 四、栈的算法题 // 一、队列 二、队列的实现 三、使用演示 四、队列的算法题 总结 前言 本文完整实现了栈和队列的数据结构,以及栈和队列的一些经典算法题,让我们更加清楚了解这两种数据…...
C# 基于winform 使用NI-VISA USB口远程控制电源 万用表
1.下载完整版本NI-VISA NI-VISA Download - NI *注意支持的操作系统,以便后期编译 安装完成之后,打开NI MAX,插上usb口,打开测试面板进行通信 2.编程示例 见本地文件夹C:\Users\Public\Documents\National Instruments\NI-VIS…...
Python设计方差分析实验
前言 方差分析(ANOVA)是一种用于检测多个样本均值之间差异的统计方法,广泛应用于实验设计与数据分析中。通过分析不同因素对实验结果的影响,方差分析能够帮助评估哪些因素显著影响了实验结果,并且可以提供各因素交互作用的深入理解。在多因子实验设计中,随机化、重复和平…...
深度学习在微纳光子学中的应用
深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向: 逆向设计 通过神经网络快速预测微纳结构的光学响应,替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...
应用升级/灾备测试时使用guarantee 闪回点迅速回退
1.场景 应用要升级,当升级失败时,数据库回退到升级前. 要测试系统,测试完成后,数据库要回退到测试前。 相对于RMAN恢复需要很长时间, 数据库闪回只需要几分钟。 2.技术实现 数据库设置 2个db_recovery参数 创建guarantee闪回点,不需要开启数据库闪回。…...
关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案
问题描述:iview使用table 中type: "index",分页之后 ,索引还是从1开始,试过绑定后台返回数据的id, 这种方法可行,就是后台返回数据的每个页面id都不完全是按照从1开始的升序,因此百度了下,找到了…...
【HTML-16】深入理解HTML中的块元素与行内元素
HTML元素根据其显示特性可以分为两大类:块元素(Block-level Elements)和行内元素(Inline Elements)。理解这两者的区别对于构建良好的网页布局至关重要。本文将全面解析这两种元素的特性、区别以及实际应用场景。 1. 块元素(Block-level Elements) 1.1 基本特性 …...
基于 TAPD 进行项目管理
起因 自己写了个小工具,仓库用的Github。之前在用markdown进行需求管理,现在随着功能的增加,感觉有点难以管理了,所以用TAPD这个工具进行需求、Bug管理。 操作流程 注册 TAPD,需要提供一个企业名新建一个项目&#…...
【C++进阶篇】智能指针
C内存管理终极指南:智能指针从入门到源码剖析 一. 智能指针1.1 auto_ptr1.2 unique_ptr1.3 shared_ptr1.4 make_shared 二. 原理三. shared_ptr循环引用问题三. 线程安全问题四. 内存泄漏4.1 什么是内存泄漏4.2 危害4.3 避免内存泄漏 五. 最后 一. 智能指针 智能指…...
如何应对敏捷转型中的团队阻力
应对敏捷转型中的团队阻力需要明确沟通敏捷转型目的、提升团队参与感、提供充分的培训与支持、逐步推进敏捷实践、建立清晰的奖励和反馈机制。其中,明确沟通敏捷转型目的尤为关键,团队成员只有清晰理解转型背后的原因和利益,才能降低对变化的…...
Vue 模板语句的数据来源
🧩 Vue 模板语句的数据来源:全方位解析 Vue 模板(<template> 部分)中的表达式、指令绑定(如 v-bind, v-on)和插值({{ }})都在一个特定的作用域内求值。这个作用域由当前 组件…...
消防一体化安全管控平台:构建消防“一张图”和APP统一管理
在城市的某个角落,一场突如其来的火灾打破了平静。熊熊烈火迅速蔓延,滚滚浓烟弥漫开来,周围群众的生命财产安全受到严重威胁。就在这千钧一发之际,消防救援队伍迅速行动,而豪越科技消防一体化安全管控平台构建的消防“…...
《信号与系统》第 6 章 信号与系统的时域和频域特性
目录 6.0 引言 6.1 傅里叶变换的模和相位表示 6.2 线性时不变系统频率响应的模和相位表示 6.2.1 线性与非线性相位 6.2.2 群时延 6.2.3 对数模和相位图 6.3 理想频率选择性滤波器的时域特性 6.4 非理想滤波器的时域和频域特性讨论 6.5 一阶与二阶连续时间系统 6.5.1 …...
