ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

基于LSTM的电影评论情感分析毕设实战:从数据预处理到模型调优

基于LSTM的电影评论情感分析毕设实战:从数据预处理到模型调优 简介这份资源面向计算机相关专业正在准备课程设计、期末大作业或毕业设计的学生提供一套基于LSTM实现电影评论情感倾向分析的完整Python项目。项目已获98分评价适合需要项目实战练习、希望掌握深度学习文本分类流程的学习者参考。压缩包共22个文件约30.85MB包含3个Python源码文件、6个JSON配置与索引文件、2个Markdown说明文档、1个PDF实践手册、1个训练好的model模型、2个arrow数据文件及5张结果图片覆盖数据预处理、词向量构建、模型训练与结果可视化等环节。目录中区分了主程序、旧版训练脚本、模型配置与IMDB数据集结构清晰便于按模块阅读与复现。目前已有156人学习下载。读者可从中获得完整的赛题实现方案、可运行的训练与推理代码、词向量与标签映射配置、实验报告及结果图表既能直接用于作业提交也可作为理解LSTM情感分析全流程的实践参考。1. 电影评论情感分析为什么 LSTM 比词袋模型更适合做毕设做情感分析毕设很多同学第一反应是“把评论分词统计正负词频扔进朴素贝叶斯”。这个方案跑起来确实快准确率也能到 80% 左右但答辩时老师一句“否定句怎么处理”就能把你问住。比如“我不觉得这部电影好看”词袋模型看到的是“不”“觉得”“电影”“好看”正负词频一抵消大概率判成正向。而 LSTM 按顺序读入每个词前面的“不”会通过门控机制影响后面“好看”的语义表示这就是循环神经网络在文本任务上的核心优势。这个标题对应的是一套完整的毕设交付物Python 实现的情感分类模型加上一份能写进论文的实验报告。适合正在找毕设题目、或者大作业需要交代码和文档的本科生。技术栈上常见做法是用 PyTorch 或 TensorFlow 搭 LSTM配合中文电影评论数据集比如豆瓣短评或 IMDB 中文翻译版做二分类或多分类。源码部分通常包含数据预处理、模型定义、训练脚本、评估指标和可视化报告则覆盖背景、相关工作、实验设置和结果分析。下面从数据准备到模型调参把这条链路拆开讲清楚。2. 数据准备与预处理从原始评论到 LSTM 可吃的张量2.1 中文电影评论数据集的获取与清洗公开的中文情感分析数据集不算多常见的有 ChnSentiCorp酒店评论但情感极性通用、weibo_senti_100k微博情感口语化强以及从豆瓣短评爬取的电影评论。如果标题里明确是“电影评论”建议优先用豆瓣数据因为领域匹配度高模型学到的特征更贴近电影场景。爬取时注意控制频率别给人家服务器添麻烦一般每请求一次 sleep 1 到 2 秒。拿到原始数据后清洗步骤直接影响最终准确率。我一般会做这几件事去掉 HTML 标签和 URL、统一全半角、去除表情符号或者用占位符替换、处理重复评论。下面是一个清洗函数的骨架import re import jieba def clean_text(text): # 去掉 HTML 标签 text re.sub(r[^], , text) # 去掉 URL text re.sub(rhttp[s]?://\S, , text) # 全角转半角 text .join([chr(ord(ch) - 65248) if 65281 ord(ch) 65374 else ch for ch in text]) # 只保留中文、英文、数字和基本标点 text re.sub(r[^\u4e00-\u9fa5a-zA-Z0-9。、], , text) # 分词 words jieba.lcut(text) # 去掉停用词需自行加载停用词表 # words [w for w in words if w not in stopwords] return .join(words)这段代码的逻辑是先做字符级清洗再用 jieba 做词级切分。参数上re.sub的正则模式决定了保留哪些字符如果你做的是细粒度情感分析比如五分类标点符号可能携带情感信息建议保留感叹号和问号。停用词表不要直接用百度那个通用表最好根据电影评论语料统计一下高频无意义词手动补充“这部电影”“感觉”“整体”这类领域停用词。2.2 构建词表与序列填充pad_sequence 的三种策略LSTM 要求输入是定长序列但评论长度从几个字到几百字不等。常见做法是设定一个最大长度max_len比如 200超过的截断不足的补零。PyTorch 里用pad_sequence配合collate_fn可以动态填充比全局统一截断更省显存。from torch.nn.utils.rnn import pad_sequence from torch.utils.data import Dataset, DataLoader import torch class ReviewDataset(Dataset): def __init__(self, texts, labels, vocab, max_len200): self.texts texts self.labels labels self.vocab vocab self.max_len max_len def __len__(self): return len(self.texts) def __getitem__(self, idx): words self.texts[idx].split() # 词转索引未知词用 unk 的索引 ids [self.vocab.get(w, self.vocab[unk]) for w in words] ids ids[:self.max_len] return torch.tensor(ids, dtypetorch.long), torch.tensor(self.labels[idx], dtypetorch.long) def collate_fn(batch): texts, labels zip(*batch) # 动态填充到当前 batch 的最大长度 texts_padded pad_sequence(texts, batch_firstTrue, padding_value0) labels torch.stack(labels) return texts_padded, labels这里的关键参数是padding_value一般设为 0对应pad的索引。batch_firstTrue让输出形状是(batch_size, seq_len)符合 LSTM 的默认输入格式。注意如果你在 Embedding 层设置了padding_idx0那么补零位置的词向量不会参与梯度更新这是标准做法。另一种策略是全局统一长度实现简单但浪费计算动态填充在 batch 内对齐训练效率更高但需要自定义collate_fn。我一般用动态填充除非显存特别紧张。词表构建时低频词直接映射到unk阈值通常设 2 或 3。词表大小控制在 2 万到 5 万之间太大容易过拟合太小则覆盖不足。可以用collections.Counter统计词频然后按频率排序取 top-k。3. LSTM 模型搭建从 Embedding 到分类头的完整代码3.1 单层 LSTM 与双向 LSTM 的选型对比PyTorch 里定义 LSTM 分类模型核心参数就几个input_size词向量维度、hidden_size隐藏层维度、num_layers层数、bidirectional是否双向。对于电影评论这种中等长度文本我一般从单层双向 LSTM 开始试hidden_size设 128 或 256。import torch.nn as nn class LSTMClassifier(nn.Module): def __init__(self, vocab_size, embed_dim128, hidden_dim256, num_layers1, num_classes2, dropout0.5): super().__init__() self.embedding nn.Embedding(vocab_size, embed_dim, padding_idx0) self.lstm nn.LSTM(embed_dim, hidden_dim, num_layersnum_layers, bidirectionalTrue, batch_firstTrue, dropoutdropout if num_layers 1 else 0) self.dropout nn.Dropout(dropout) self.fc nn.Linear(hidden_dim * 2, num_classes) # 双向所以乘2 def forward(self, x): # x: (batch_size, seq_len) embedded self.embedding(x) # (batch, seq_len, embed_dim) lstm_out, (hidden, cell) self.lstm(embedded) # 取最后一个时间步的隐藏状态双向需要拼接 # hidden: (num_layers*2, batch, hidden_dim) hidden torch.cat([hidden[-2], hidden[-1]], dim1) # (batch, hidden_dim*2) out self.dropout(hidden) logits self.fc(out) return logits逻辑说明Embedding 层把词索引转成稠密向量padding_idx0保证补零位置不更新。LSTM 层输出整个序列的隐藏状态和最后一步的(hidden, cell)。双向 LSTM 的hidden形状是(num_layers*2, batch, hidden_dim)hidden[-2]是正向最后一层hidden[-1]是反向最后一层拼接后送入全连接分类。参数上dropout在num_layers1时不生效PyTorch 会警告所以单层时我通常在外面加nn.Dropout。hidden_dim从 128 到 512 都有人用经验值是 256 在多数电影评论数据集上性价比最高。3.2 训练循环与关键超参数设置训练部分需要定义损失函数、优化器和评估指标。二分类用CrossEntropyLoss优化器用 Adam学习率从 1e-3 开始配合ReduceLROnPlateau在验证集损失不下降时减半。from torch.optim import Adam from torch.optim.lr_scheduler import ReduceLROnPlateau from sklearn.metrics import accuracy_score, f1_score def train_model(model, train_loader, val_loader, epochs10, lr1e-3, devicecuda): model model.to(device) criterion nn.CrossEntropyLoss() optimizer Adam(model.parameters(), lrlr) scheduler ReduceLROnPlateau(optimizer, modemin, factor0.5, patience2) for epoch in range(epochs): model.train() total_loss 0 for texts, labels in train_loader: texts, labels texts.to(device), labels.to(device) optimizer.zero_grad() logits model(texts) loss criterion(logits, labels) loss.backward() # 梯度裁剪防止 LSTM 梯度爆炸 nn.utils.clip_grad_norm_(model.parameters(), max_norm5.0) optimizer.step() total_loss loss.item() # 验证 model.eval() preds, true_labels [], [] val_loss 0 with torch.no_grad(): for texts, labels in val_loader: texts, labels texts.to(device), labels.to(device) logits model(texts) val_loss criterion(logits, labels).item() preds.extend(torch.argmax(logits, dim1).cpu().numpy()) true_labels.extend(labels.cpu().numpy()) acc accuracy_score(true_labels, preds) f1 f1_score(true_labels, preds, averageweighted) scheduler.step(val_loss) print(fEpoch {epoch1}: train_loss{total_loss/len(train_loader):.4f}, val_loss{val_loss/len(val_loader):.4f}, acc{acc:.4f}, f1{f1:.4f})参数说明clip_grad_norm_的max_norm设 5.0 是经验值LSTM 容易梯度爆炸不加裁剪训练后期 loss 可能直接变 NaN。ReduceLROnPlateau的patience2表示验证损失连续两个 epoch 不降就减半学习率。batch_size一般设 32 或 64太小训练不稳定太大显存吃紧。训练轮数 10 到 20 足够配合早停验证集 F1 连续 3 轮不升就停可以防止过拟合。4. 避坑与排查LSTM 情感分析翻车实录4.1 准确率卡在 50% 上不去现象训练 loss 缓慢下降但验证集准确率一直在 50% 左右跟随机猜差不多。原因通常是标签没对齐或者数据里正负样本极度不均衡。解决先打印前 10 条样本的文本和标签确认没有错位再用Counter统计标签分布如果正负比超过 3:1需要在损失函数里加weight参数或者对少数类过采样。4.2 训练 loss 震荡剧烈验证 loss 飙升现象训练 loss 时高时低验证 loss 在几个 epoch 后突然增大。原因一般是学习率太大或者 batch_size 太小导致梯度噪声大。解决把学习率降到 5e-4 或 1e-4batch_size 提到 64同时加上梯度裁剪。如果还不行检查 Embedding 层是否冻结了预训练词向量——如果用了预训练词向量且冻结学习率要设得更小。4.3 模型对否定句仍然判断错误现象“我不喜欢这部电影”被判成正向。原因LSTM 虽然能建模顺序但如果训练数据里否定句太少模型学不到“不”对后续词的影响。解决在数据增强阶段手动构造否定样本比如把“我喜欢”替换成“我不喜欢”把“好看”替换成“不好看”扩充到训练集里。另外可以把hidden_dim从 128 提到 256给模型更多容量去记忆长距离依赖。4.4 推理时显存溢出现象训练时正常推理时 batch 稍微大一点就 OOM。原因推理时没有torch.no_grad()或者pad_sequence填充到了整个数据集的最大长度。解决推理代码包在with torch.no_grad():里并且推理的collate_fn也按 batch 动态填充不要用训练集的全局最大长度。4.5 保存的模型加载后预测结果全一样现象训练完保存state_dict重新加载后所有输入都预测成同一类。原因保存时只存了state_dict加载时模型结构参数vocab_size、hidden_dim等跟保存时不一致导致权重错位。解决保存时同时存model.state_dict()和模型初始化参数或者用torch.save(model, path)保存整个模型不推荐但毕设够用。加载后先在一个已知样本上验证输出是否合理。5. 报告撰写与答辩加分项把实验数据变成论文图表5.1 实验对比表格的设计毕设报告里老师最想看的是对比实验。至少做三组词袋模型 朴素贝叶斯、单层 LSTM、双向 LSTM。表格列包括准确率、精确率、召回率、F1 值。下面是一个示例结构模型准确率精确率召回率F1 值朴素贝叶斯0.8120.8050.7980.801单层 LSTM0.8760.8710.8680.869双向 LSTM0.9030.8990.8970.898注意表格里的数字要跟你实际跑出来的结果一致不要编。如果双向 LSTM 只比单层高 1 个点就如实写然后分析原因比如数据量不够大双向的优势没完全发挥。5.2 混淆矩阵与错误分析除了准确率画一个混淆矩阵能直观看出模型在哪类样本上容易错。用sklearn.metrics.confusion_matrix配合seaborn.heatmap几行代码就能出图。错误分析部分挑 10 条预测错误的样本逐条看是文本本身歧义比如反讽还是预处理把关键信息洗掉了。这部分写进报告答辩时老师会觉得你确实动手分析了不是只跑了个模型。5.3 一个提升答辩通过率的小技巧答辩前把模型对几条典型评论的预测过程打印出来比如“这部电影太棒了”每个词经过 LSTM 后的隐藏状态变化。不用画复杂的图就在终端里打印每个时间步的hidden向量范数展示模型确实在“读”句子。这个技巧我每次带毕设都会让学生做老师看到你理解模型内部行为比只看准确率数字印象深得多。做毕设这些年我最大的教训是别一上来就调参先把数据洗干净、把标签对齐、把基线跑通。LSTM 情感分析这套东西代码网上到处都是但能跑出好结果的人时间都花在数据上。希望帮到你。本文还有配套的精品资源点击获取
返回列表