☰
基于LSTM的电商评论情感分析毕业设计源码与实战指南
2026/10/5 1:08:58 网站建设 项目流程

简介:这份资源是面向计算机相关专业毕业设计、课程设计与期末大作业场景的深度学习实战项目,聚焦电商购物评论的情感分析任务,采用LSTM模型完成中文文本的正负情感判别。项目包含完整源码与全部数据,下载后可直接运行,适合正在准备毕设或需要Python项目实战练习的学习者参考。压缩包共39个文件,约164.29MB,其中8个py脚本承担数据爬取、模型训练与预测流程,6组data、index、meta文件为TensorFlow模型权重与检查点,另有checkpoint、model、cfg等配置文件和7张png结果图、3个txt说明文档,结构清晰便于按模块阅读。目前已有405人学习下载。读者可借此掌握从京东评论采集、中文分词与停用词处理,到LSTM建模、训练、评估与情感预测的完整链路,并参考readme与使用说明快速复现实验,为论文撰写和答辩提供可落地的案例支撑。

1. 电商评论里的 LSTM 情感分析:一份毕业设计源码能跑通的关键在哪

电商评论区的文本,短、碎、口语化,还夹着反讽和表情符号。用传统词典法做情感分析,遇到“质量真好,用了三天就坏了”这种句子直接翻车。LSTM 之所以在这个场景里被反复提起,是因为它的门控结构能记住前文语境,把“三天就坏了”对“质量真好”的语义反转捕捉到。这份毕业设计打包了源码和全部数据,核心就是一套基于 LSTM 的电商购物评论情感二分类或多分类流程。它适合正在做深度学习方向毕业设计、需要一份能跑通、能改、能写进论文的完整项目的同学,也适合想快速了解 LSTM 文本分类落地路径的初级工程师。拿到压缩包之后,真正决定你能不能跑出结果的,不是模型多深,而是数据清洗、词表构建、序列对齐和训练配置这几个环节有没有踩对。

2. LSTM 做电商评论情感分析,原理和选型理由得先立住

2.1 为什么电商短文本更适合 LSTM 而不是朴素贝叶斯

电商评论的情感分析,本质上是一个文本分类任务。输入是一段用户评价,输出是正面、负面,有时还加中性。朴素贝叶斯和 SVM 在词袋模型上能跑出不错的基础分,但它们丢掉了词序。词序在电商评论里恰恰很要命。“不推荐”和“推荐”只差一个字,词袋模型可能把“不”当成噪声,而 LSTM 的遗忘门和输入门会决定前一个词对当前状态的影响程度,从而保留否定前缀的作用。

LSTM 相比普通 RNN,多了细胞状态和三个门:遗忘门决定丢弃哪些旧信息,输入门决定写入哪些新信息,输出门决定当前时刻暴露什么。这套机制在长评论里能缓解梯度消失,在短评论里也能把关键情感词前后的修饰语关联起来。电商评论通常不长,但情感表达密集,LSTM 的序列建模能力刚好匹配。

选型时还要考虑一个现实问题:毕业设计的时间预算。Transformer 和 BERT 效果更好,但训练成本高,调参复杂,对显卡有要求。LSTM 在中等规模数据上,用 CPU 也能跑出可接受的结果,代码结构清晰,论文里好画图、好解释。常见做法是先用 LSTM 跑通基线,再考虑要不要换模型做对比实验。

2.2 从原始评论到模型输入:数据管道的四个环节

拿到数据后,不能直接喂给模型。电商评论里混着 HTML 标签、表情符号、重复标点、无意义的口语词。数据管道一般分四步:清洗、分词、建词表、序列对齐。

清洗阶段去掉 HTML 标签、URL、特殊符号,统一全半角。中文分词用 jieba 比较稳,电商领域可以加自定义词典,把“包邮”“性价比”“翻车”这类词固定下来。建词表时统计词频,保留高频词,低频词统一映射为<UNK>。序列对齐是设定一个最大长度,比如 128 或 256,短的补零,长的截断。

下面是一个可复现的数据预处理脚本片段,假设原始数据是 CSV,两列:review和label。

import pandas as pd import jieba import re from collections import Counter # 读取原始数据 df = pd.read_csv('data/reviews.csv') print(df.head()) print(df['label'].value_counts()) # 清洗函数 def clean_text(text): text = re.sub(r'<.*?>', '', text) # 去 HTML 标签 text = re.sub(r'http\S+', '', text) # 去 URL text = re.sub(r'[^\u4e00-\u9fa5a-zA-Z0-9,。!?]', '', text) # 保留中文、英文、数字和常用标点 text = text.strip() return text df['clean_review'] = df['review'].astype(str).apply(clean_text) # 分词 def tokenize(text): return list(jieba.cut(text)) df['tokens'] = df['clean_review'].apply(tokenize) # 统计词频,构建词表 all_tokens = [token for tokens in df['tokens'] for token in tokens] word_counter = Counter(all_tokens) # 保留出现次数 >= 2 的词 vocab = {word: idx + 2 for idx, (word, count) in enumerate(word_counter.most_common()) if count >= 2} vocab['<PAD>'] = 0 vocab['<UNK>'] = 1 print(f'词表大小: {len(vocab)}') # 转成索引序列 def to_index(tokens, vocab, max_len=128): ids = [vocab.get(token, vocab['<UNK>']) for token in tokens] if len(ids) < max_len: ids = ids + [vocab['<PAD>']] * (max_len - len(ids)) else: ids = ids[:max_len] return ids df['input_ids'] = df['tokens'].apply(lambda x: to_index(x, vocab)) print(df[['clean_review', 'input_ids']].head())

这段代码的逻辑是:先清洗掉干扰字符,再用 jieba 切词,然后基于词频建词表,最后把每条评论转成固定长度的索引序列。参数上,max_len=128是电商短文本的常用值,评论特别长可以调到 256。词频阈值count >= 2是为了过滤只出现一次的低频词,减少词表噪声。如果数据量很小,可以放宽到 1,但要注意过拟合。

提示:词表构建必须在训练集上做,验证集和测试集只能用训练集的词表映射,否则会造成数据泄露。

2.3 LSTM 模型结构怎么搭:嵌入层、LSTM 层、全连接层的参数含义

模型部分用 PyTorch 写比较直观。一个典型的 LSTM 情感分类模型包含嵌入层、LSTM 层、全连接层。嵌入层把词索引映射成稠密向量,LSTM 层处理序列,全连接层输出类别概率。

import torch import torch.nn as nn class LSTMClassifier(nn.Module): def __init__(self, vocab_size, embed_dim=128, hidden_dim=256, num_layers=2, num_classes=2, dropout=0.5): super(LSTMClassifier, self).__init__() self.embedding = nn.Embedding(vocab_size, embed_dim, padding_idx=0) self.lstm = nn.LSTM( input_size=embed_dim, hidden_size=hidden_dim, num_layers=num_layers, batch_first=True, bidirectional=True, dropout=dropout if num_layers > 1 else 0 ) self.dropout = nn.Dropout(dropout) self.fc = nn.Linear(hidden_dim * 2, num_classes) # 双向 LSTM 输出拼接 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) # 取最后一个时间步的输出,双向拼接 last_output = lstm_out[:, -1, :] # (batch, hidden_dim * 2) out = self.dropout(last_output) out = self.fc(out) return out # 实例化模型 model = LSTMClassifier(vocab_size=len(vocab), embed_dim=128, hidden_dim=256, num_layers=2, num_classes=2) print(model)

参数说明:embed_dim=128是词向量维度,数据量大可以加到 256;hidden_dim=256是 LSTM 隐藏状态维度,双向之后输出维度翻倍;num_layers=2表示两层 LSTM,层数越多拟合能力越强,但容易过拟合;dropout=0.5在嵌入层和全连接层前做正则化;bidirectional=True让模型同时看前文和后文,对情感分析通常有提升。padding_idx=0告诉嵌入层忽略填充位置,不参与梯度更新。

训练时用交叉熵损失,优化器选 Adam,学习率一般设 1e-3 到 1e-4。批次大小 32 或 64,根据显存调整。训练轮数看验证集损失,通常 5 到 10 轮就能看出趋势。

3. 把源码跑起来:环境、训练、评估的完整操作路径

3.1 环境依赖和目录结构确认

拿到压缩包后,先看目录结构。常见布局是data/放原始数据,src/放源码,models/存训练好的权重,notebooks/放实验记录。不要急着改代码,先确认 Python 版本和依赖。

# 建议用 conda 建独立环境 conda create -n lstm_sentiment python=3.8 conda activate lstm_sentiment # 安装核心依赖 pip install torch==1.12.0 pandas jieba scikit-learn numpy matplotlib

如果源码里有requirements.txt,直接pip install -r requirements.txt。注意 PyTorch 版本和 CUDA 版本的匹配,没有 GPU 就用 CPU 版,训练会慢但能跑通。检查数据文件是否完整,CSV 编码常见是 UTF-8 或 GBK,用 pandas 读取时报编码错误就换encoding='gbk'试。

3.2 训练脚本的关键参数和运行命令

训练脚本一般包含数据加载、模型定义、训练循环、验证、保存权重。下面是一个简化的训练流程,展示关键参数怎么设。

from torch.utils.data import DataLoader, TensorDataset import torch.optim as optim from sklearn.model_selection import train_test_split # 准备数据 X = torch.tensor(df['input_ids'].tolist(), dtype=torch.long) y = torch.tensor(df['label'].tolist(), dtype=torch.long) X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.2, random_state=42, stratify=y) train_dataset = TensorDataset(X_train, y_train) val_dataset = TensorDataset(X_val, y_val) train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True) val_loader = DataLoader(val_dataset, batch_size=64, shuffle=False) # 设备 device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') model = LSTMClassifier(vocab_size=len(vocab), embed_dim=128, hidden_dim=256, num_layers=2, num_classes=2).to(device) # 损失和优化器 criterion = nn.CrossEntropyLoss() optimizer = optim.Adam(model.parameters(), lr=1e-3, weight_decay=1e-5) # 训练循环 num_epochs = 10 best_val_acc = 0.0 for epoch in range(num_epochs): model.train() total_loss = 0 for batch_x, batch_y in train_loader: batch_x, batch_y = batch_x.to(device), batch_y.to(device) optimizer.zero_grad() outputs = model(batch_x) loss = criterion(outputs, batch_y) loss.backward() optimizer.step() total_loss += loss.item() # 验证 model.eval() correct = 0 total = 0 with torch.no_grad(): for batch_x, batch_y in val_loader: batch_x, batch_y = batch_x.to(device), batch_y.to(device) outputs = model(batch_x) _, predicted = torch.max(outputs, 1) total += batch_y.size(0) correct += (predicted == batch_y).sum().item() val_acc = correct / total print(f'Epoch {epoch+1}, Loss: {total_loss/len(train_loader):.4f}, Val Acc: {val_acc:.4f}') if val_acc > best_val_acc: best_val_acc = val_acc torch.save(model.state_dict(), 'models/best_lstm.pth') print(f'最佳验证准确率: {best_val_acc:.4f}')

运行命令就是python train.py,如果源码里参数是写在配置文件里的,改配置文件再运行。关键参数:batch_size=64影响训练稳定性和速度;lr=1e-3是 Adam 的常用起点,训练不收敛就降到 1e-4;weight_decay=1e-5做 L2 正则,防止过拟合。验证集准确率不再提升就可以停,不用死磕轮数。

3.3 评估指标怎么看:准确率之外的精确率和召回率

情感分析如果类别不平衡,准确率会骗人。比如 90% 是正面,模型全猜正面也有 90% 准确率,但负面评论一条都抓不到。所以要看精确率、召回率和 F1。

from sklearn.metrics import classification_report, confusion_matrix model.eval() all_preds = [] all_labels = [] with torch.no_grad(): for batch_x, batch_y in val_loader: batch_x, batch_y = batch_x.to(device), batch_y.to(device) outputs = model(batch_x) _, predicted = torch.max(outputs, 1) all_preds.extend(predicted.cpu().numpy()) all_labels.extend(batch_y.cpu().numpy()) print(classification_report(all_labels, all_preds, target_names=['负面', '正面'])) print(confusion_matrix(all_labels, all_preds))

classification_report会输出每个类别的精确率、召回率、F1 和支持度。如果负面类别的召回率很低,说明模型对负面评论不敏感,可能是负面样本太少,或者词表里负面情感词覆盖不够。解决办法:过采样负面样本、调整类别权重、补充领域词典。

4. 避坑与排查:LSTM 情感分析毕业设计里最容易翻车的五件事

4.1 损失不下降,准确率卡在 50% 左右

现象:训练几轮后,loss 几乎不变,验证准确率在随机水平附近。原因通常是学习率太大导致震荡,或者数据标签没对齐,或者词表映射全错。解决:先把学习率降到 1e-4 试;检查input_ids是不是全零或全同一个值;打印几条样本的原始文本、分词结果和标签,确认没有错位。另一个常见原因是padding_idx设错,填充符参与了梯度更新,把模型带偏。

4.2 验证集准确率很高,测试集一塌糊涂

现象:验证集 95%,换一批数据掉到 60%。原因是数据泄露,比如词表用了全量数据构建,或者训练集和验证集有重复样本。解决:严格按训练集建词表,验证集和测试集只用训练集词表;去重后再划分数据集;如果数据本身有同质化,检查是不是同一个用户的评论被分到了不同集合。

4.3 显存不够,batch_size 调小后训练不稳定

现象:GPU 显存不足,把 batch_size 从 64 降到 8,loss 波动很大。原因是小批次梯度噪声大,Adam 的自适应学习率在小批次下表现不稳定。解决:用梯度累积,比如每 8 个批次更新一次参数,等效于 batch_size=64;或者换用 CPU 训练,虽然慢但稳定;还可以减小hidden_dim和num_layers,降低模型参数量。

4.4 中文分词把情感词切碎了

现象:模型对“不推荐”“超级好”这类词不敏感。原因是 jieba 默认词典把“不推荐”切成“不”和“推荐”,否定信息被稀释。解决:加自定义词典,把电商领域的情感短语固定成词;或者在预处理阶段做否定前缀处理,把“不”和后面的词合并;还可以用字符级模型做对比,字符级能保留更多细节,但序列会变长。

4.5 训练完模型不会用,预测新评论报错

现象:训练脚本跑通了,但写预测脚本时输入一条新评论,模型报维度错误。原因是预测时的预处理和训练时不一致,比如没做清洗、没转索引、没对齐长度。解决:把训练时的预处理函数封装成独立模块,预测时调用同一个函数;确保max_len和词表路径一致;输入用torch.tensor包一层,维度对到(1, seq_len)。

5. 让毕业设计多拿几分:LSTM 情感分析的进阶技巧和验证习惯

5.1 用预训练词向量初始化嵌入层

随机初始化的嵌入层需要大量数据才能学好。如果手头有 Word2Vec 或 GloVe 的中文词向量,可以加载进来初始化nn.Embedding的权重。做法是:读取词向量文件,对词表里每个词找到对应向量,组成矩阵,赋值给embedding.weight.data。训练时可以冻结嵌入层,也可以微调。冻结适合数据量小的情况,微调适合数据量中等偏上。这一步在论文里可以写成“基于预训练词向量的迁移学习”,比纯随机初始化更有说服力。

5.2 加注意力机制让模型聚焦情感词

LSTM 的最后一个时间步输出不一定能代表整句的情感。加一个注意力层,让模型对不同时间步的输出加权求和,权重高的位置就是情感词所在。实现上,用一个线性层计算每个时间步的分数,softmax 归一化后对 LSTM 输出做加权平均。代码改动不大,但效果通常有提升,论文里也多了一个创新点。

class LSTMAttention(nn.Module): def __init__(self, vocab_size, embed_dim=128, hidden_dim=256, num_layers=2, num_classes=2, dropout=0.5): super(LSTMAttention, self).__init__() self.embedding = nn.Embedding(vocab_size, embed_dim, padding_idx=0) self.lstm = nn.LSTM(embed_dim, hidden_dim, num_layers, batch_first=True, bidirectional=True, dropout=dropout) self.attention = nn.Linear(hidden_dim * 2, 1) self.dropout = nn.Dropout(dropout) self.fc = nn.Linear(hidden_dim * 2, num_classes) def forward(self, x): embedded = self.embedding(x) lstm_out, _ = self.lstm(embedded) # (batch, seq_len, hidden*2) scores = self.attention(lstm_out) # (batch, seq_len, 1) weights = torch.softmax(scores, dim=1) context = torch.sum(weights * lstm_out, dim=1) # (batch, hidden*2) out = self.dropout(context) out = self.fc(out) return out

注意力层的参数只有一个线性映射,计算量增加很少。训练时注意padding位置不应该参与注意力权重,可以在计算 scores 后把填充位置的分数设成负无穷再 softmax,这里为了简洁没加,实际项目里建议补上。

5.3 交叉验证和随机种子:让结果可复现

毕业设计的实验结果要经得起追问。单次划分的验证集准确率波动可能很大,用 5 折交叉验证取平均,结果更可信。同时固定随机种子,包括 Python、NumPy、PyTorch 的种子,确保每次运行结果一致。

import random import numpy as np def set_seed(seed=42): random.seed(seed) np.random.seed(seed) torch.manual_seed(seed) torch.cuda.manual_seed_all(seed) torch.backends.cudnn.deterministic = True torch.backends.cudnn.benchmark = False set_seed(42)

cudnn.deterministic = True会让 GPU 计算变慢,但结果可复现。如果追求速度,可以关掉,但论文里报告的结果要注明是否固定种子。

5.4 我踩过的坑和现在的习惯

我最早做 LSTM 情感分析时,把测试集也拿去建词表了,结果验证准确率虚高,答辩时被老师问了一句“测试集参与训练了吗”,当场卡住。后来养成习惯:数据划分、词表构建、标准化,全部只在训练集上做,验证集和测试集只做 transform。另一个血泪经验是,不要迷信最后一轮模型,验证集最好的那一轮才是你要保存的。训练日志里记下每轮的 loss 和验证指标,画成曲线,论文里直接放图。最后,预测脚本一定要单独写一个,用训练好的权重加载,输入几条手工构造的评论,看输出是否符合直觉。这个习惯帮我提前发现了好几次预处理不一致的问题。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询