简介:这份资源是面向高校学生与Python初学者的文本情感分析完整项目,基于LSTM神经网络实现,适合用于期末大作业、课程设计或毕业设计等场景。项目难度适中,源码经过本地编译验证可正常运行,评审得分达到98分,内容也经过助教老师审定,能够满足学习与提交需求。压缩包共9个文件,约7.15MB,包含3个Python脚本分别负责模型训练、预测与对话交互,1个h5模型文件与1个pickle词典文件用于加载已训练权重和词表,另有json训练数据、png网络结构图与系统说明图,以及README说明文档,整体结构清晰、便于按模块阅读与二次修改。目前已有140人学习下载。读者可从中获得一套可直接运行的LSTM情感分析方案,理解数据预处理、词向量构建、模型搭建与预测推理的完整链路,并借助结构图与说明文档快速理清项目逻辑,适合作为入门深度学习与文本分类的实践参考。
1. 一份能直接跑通的 LSTM 文本情感分析系统,到底长什么样
期末大作业、课程设计、毕业设计三座大山压下来的时候,大多数人卡的不是算法本身,而是「从零搭一套能演示、能答辩、能改的系统」这件事。这份基于 Python + LSTM 的文本情感分析系统源码,给的就是一个已经跑通的完整闭环:训练脚本、预测脚本、对话机器人脚本、预训练模型、词表、训练数据、网络结构图、系统说明文档,一应俱全。它不是那种只丢一个model.py让你自己补全的「半成品」,而是train.py能训、predict.py能测、robot_chat.py能交互的成品级项目。
适合谁?如果你正在做情感分析方向的课设或毕设,需要一份结构清晰、注释完整、能讲清楚原理又能现场演示的代码底座,这份资源省掉的是你从零调通数据预处理到模型保存的那两三天。如果你只是想学 LSTM 在 NLP 里的落地方式,它的目录结构本身就是一份很好的工程范本。下面我按「拆包看结构 → 跑通训练 → 理解数据流 → 避坑 → 进阶改造」的顺序,把这份源码从头到尾过一遍。
2. 拆开压缩包先看什么:目录结构与模块职责
拿到一个陌生项目,我的习惯是先不跑代码,而是把目录树和每个文件的职责摸清楚。这份资源的文件布局很典型,属于「教学友好型」——没有过度工程化,每个文件对应一个明确功能,答辩时你能指着文件说清楚每部分是干嘛的。
2.1 文件清单与职责对照
解压后大致是这些内容,我按功能分组说明:
| 文件/目录 | 类型 | 职责 |
|---|---|---|
main | 目录 | 主程序入口相关代码 |
data/train.json | 数据 | 训练语料,JSON 格式,含文本与标签 |
train.py | 脚本 | 模型训练主流程 |
predict.py | 脚本 | 单条/批量文本情感预测 |
robot_chat.py | 脚本 | 基于情感模型的对话演示 |
model/my_model.h5 | 模型 | 已训练好的 Keras 模型权重 |
model/word_dict.pickle | 词表 | 序列化保存的词到索引映射 |
img/LSTM-Nework.png | 图 | LSTM 网络结构示意图 |
img/system_describe.png | 图 | 系统流程说明图 |
README.md | 文档 | 项目说明与运行指引 |
这里有个关键点:my_model.h5和word_dict.pickle是配套的。模型训练时用的词表决定了输入维度,如果你换了词表却没重训模型,预测阶段必然报维度不匹配。很多人拿到项目直接改predict.py里的输入文本就运行,结果报错,八成是没意识到词表和模型是绑定的。
2.2 先确认运行环境再动手
在跑任何脚本之前,先把环境对齐。这份项目基于 Keras/TensorFlow 的 LSTM 实现,常见做法是建一个独立虚拟环境,避免和你机器上已有的 TensorFlow 版本打架。
# 创建虚拟环境,Python 3.7~3.9 兼容性最好 python -m venv venv # 激活(Windows) venv\Scripts\activate # 激活(macOS/Linux) source venv/bin/activate # 安装核心依赖,版本按项目实际需求调整 pip install tensorflow==2.6.0 pip install numpy pandas scikit-learn h5py逻辑说明:tensorflow 2.6自带 Keras,能直接加载.h5模型;h5py是读写 HDF5 格式模型文件的底层依赖,缺了它加载模型会报ImportError。参数上,如果你机器是 Apple Silicon,TensorFlow 版本要换tensorflow-macos,否则装不上。这一步别偷懒,环境不对后面全是玄学报错。
提示:先别急着
pip install -r requirements.txt,这份项目不一定带 requirements 文件,按 README 里写的依赖手动装更稳。
3. 把训练流程跑通:从 train.json 到 my_model.h5
环境就绪后,核心动作是理解train.py干了什么。LSTM 情感分析的本质是:把变长文本转成定长索引序列,送进 Embedding 层变成向量,再过 LSTM 提取时序特征,最后接全连接层做二分类(正面/负面)。这一章把数据预处理、模型结构、训练参数三块拆开讲。
3.1 数据加载与文本向量化
data/train.json是训练语料,典型结构是每条样本带text和label两个字段。训练前必须做两件事:分词、建词表。下面是我按这份项目风格还原的核心预处理逻辑:
import json import pickle from tensorflow.keras.preprocessing.text import Tokenizer from tensorflow.keras.preprocessing.sequence import pad_sequences # 1. 读取训练数据 with open('data/train.json', 'r', encoding='utf-8') as f: data = json.load(f) texts = [item['text'] for item in data] labels = [item['label'] for item in data] # 2. 构建词表,只保留出现频率最高的 5000 个词 tokenizer = Tokenizer(num_words=5000, oov_token='<OOV>') tokenizer.fit_on_texts(texts) # 3. 文本转索引序列 sequences = tokenizer.texts_to_sequences(texts) # 4. 统一长度,短补长截,maxlen 决定 LSTM 时间步 maxlen = 100 X = pad_sequences(sequences, maxlen=maxlen, padding='post', truncating='post') # 5. 保存词表,预测阶段必须用同一个 with open('model/word_dict.pickle', 'wb') as f: pickle.dump(tokenizer, f)逻辑说明:num_words=5000控制词表规模,太小会丢信息,太大容易过拟合且拖慢训练;oov_token处理未登录词,预测时遇到训练集没见过的词会映射到它。maxlen=100是时间步长度,直接决定 LSTM 输入张量的第二维,改这个值必须同步重训模型。padding='post'表示在序列尾部补零,和truncating='post'保持一致,避免头尾处理方式不统一导致语义错位。
3.2 LSTM 模型结构搭建
模型部分用 Keras 的 Sequential 就能搭出来,结构不复杂,但每一层的参数都有讲究:
from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Embedding, LSTM, Dense, Dropout model = Sequential([ # 词嵌入层:5000 词表,每个词映射成 128 维向量 Embedding(input_dim=5000, output_dim=128, input_length=maxlen), # LSTM 层:64 个隐藏单元,返回序列用于堆叠 LSTM(64, return_sequences=False), # Dropout 抑制过拟合 Dropout(0.5), # 全连接输出层,sigmoid 做二分类 Dense(1, activation='sigmoid') ]) model.compile( loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'] ) model.summary()逻辑说明:Embedding的output_dim=128是词向量维度,越大表达力越强但显存占用越高,课设场景 128 足够。LSTM(64)的 64 是隐藏状态维度,return_sequences=False表示只取最后一个时间步的输出送进全连接层——这是做分类任务的标准写法,如果后面还要接一层 LSTM,就得设成True。Dropout(0.5)在训练时随机丢弃一半神经元,是防止小数据集过拟合最直接的手段。损失函数选binary_crossentropy是因为二分类,输出层用sigmoid把值压到 0~1 之间当概率用。
3.3 训练与模型保存
数据、模型都齐了,训练就是几行的事,但参数设置直接决定你能不能训出一个能用的模型:
from sklearn.model_selection import train_test_split # 划分训练集和验证集,8:2 X_train, X_val, y_train, y_val = train_test_split( X, labels, test_size=0.2, random_state=42 ) history = model.fit( X_train, y_train, validation_data=(X_val, y_val), epochs=10, batch_size=32, verbose=1 ) # 保存模型,供 predict.py 加载 model.save('model/my_model.h5')逻辑说明:test_size=0.2留出验证集监控过拟合,random_state=42保证每次划分一致,方便复现。epochs=10是训练轮数,太少欠拟合、太多过拟合,看验证集准确率不再上升就可以停。batch_size=32是每批样本数,显存小就调小。训练完model.save存成.h5,这个文件和前面保存的word_dict.pickle是一对,缺一不可。
注意:如果你改了
maxlen、num_words或Embedding维度,必须重新训练并重新保存模型,不能拿旧模型配新词表。
4. 预测与对话演示:predict.py 和 robot_chat.py 怎么用
训练只是前半程,答辩现场真正要演示的是「输入一句话,系统告诉你它是正面还是负面」。这一章讲预测脚本的调用方式,以及那个对话机器人是怎么把情感模型串起来的。
4.1 单条文本预测的完整链路
predict.py的核心逻辑是:加载词表和模型 → 对新文本做同样的预处理 → 送进模型 → 输出概率 → 映射成标签。这里最容易翻车的是预处理必须和训练时完全一致:
import pickle import numpy as np from tensorflow.keras.models import load_model from tensorflow.keras.preprocessing.sequence import pad_sequences # 加载词表和模型 with open('model/word_dict.pickle', 'rb') as f: tokenizer = pickle.load(f) model = load_model('model/my_model.h5') def predict_sentiment(text): # 用训练时的 tokenizer 转序列,保证索引一致 seq = tokenizer.texts_to_sequences([text]) # 同样的 maxlen 和 padding 策略 padded = pad_sequences(seq, maxlen=100, padding='post', truncating='post') # 输出 0~1 概率 prob = model.predict(padded)[0][0] label = '正面' if prob > 0.5 else '负面' return label, float(prob) if __name__ == '__main__': result, score = predict_sentiment('这个产品质量很好,物流也快') print(f'情感倾向:{result},置信度:{score:.4f}')逻辑说明:tokenizer.texts_to_sequences用的是训练时保存的那个 tokenizer,词到索引的映射完全一致,这是预测正确的前提。pad_sequences的maxlen和padding必须和训练时一模一样,否则输入张量形状对不上,模型直接报错。model.predict返回的是二维数组,取[0][0]拿到标量概率,阈值 0.5 是二分类默认分界,你可以根据业务需求调整。
4.2 robot_chat.py 的对话演示逻辑
robot_chat.py是在情感模型外面包了一层交互循环,让演示更直观。它的思路通常是:接收用户输入 → 调用情感预测 → 根据情感倾向返回不同风格的回复。这种设计在答辩时很讨巧,因为评委能直接看到模型「活」起来。
from predict import predict_sentiment def chat(): print('情感对话机器人已启动,输入 q 退出') while True: user_input = input('你:') if user_input.lower() == 'q': break label, score = predict_sentiment(user_input) if label == '正面': print(f'机器人:感受到你的积极情绪了(置信度 {score:.2f})') else: print(f'机器人:听起来你不太满意(置信度 {score:.2f})') if __name__ == '__main__': chat()逻辑说明:这里直接复用predict_sentiment函数,避免重复写预处理逻辑,是工程上推荐的做法。回复文案根据情感标签分支,置信度一并打印出来,演示时更有说服力。实际项目里你可以把回复换成更丰富的模板,甚至接一个检索式问答,但核心还是情感分类这一层。
提示:演示前先用几条明显正面/负面的句子测一遍,确认模型输出符合预期,别到现场才发现模型把「很好」判成负面。
5. 避坑与排查:跑这份源码最容易翻车的五个地方
这份项目虽然整体跑得通,但从我拆包和复现的经验看,有几个坑几乎每个人都会踩一次。下面按「现象 → 原因 → 解决」列出来,照着排查能省不少时间。
5.1 加载模型报 Unknown layer 或维度不匹配
现象:运行predict.py时抛ValueError: Input 0 is incompatible with layer或Unknown layer: LSTM。原因通常是 TensorFlow 版本和训练模型时的版本不一致,或者你改了maxlen却没重训。解决:先确认 TensorFlow 版本和 README 一致,再检查pad_sequences的maxlen是否和训练时相同。如果还不行,用model = load_model('model/my_model.h5', compile=False)跳过编译再手动 compile。
5.2 中文分词缺失导致预测全是同一类
现象:不管输入什么句子,模型都输出「正面」或都输出「负面」。原因多半是预处理时没做分词,整句话被当成一个 token,词表里根本没有,全映射成<OOV>,模型等于在瞎猜。解决:确认训练和预测阶段用了同一套分词逻辑(常见是 jieba),分词后再进 tokenizer。如果原项目用的是字符级切分,那预测时也要按字符切,不能混用。
5.3 train.json 编码问题读不进来
现象:json.load报UnicodeDecodeError或读出来是乱码。原因是文件不是 UTF-8 编码,或者带 BOM 头。解决:打开时显式指定encoding='utf-8',如果还报错就试encoding='utf-8-sig'去掉 BOM。Windows 下用记事本另存为 UTF-8 无 BOM 格式也能解决。
5.4 训练准确率高但预测效果差
现象:训练日志里 accuracy 到 0.95,实际预测却一塌糊涂。原因是过拟合,模型把训练集背下来了。解决:加大Dropout比例、减少epochs、增加训练数据,或者加 L2 正则。课设场景数据量小,过拟合是常态,验证集准确率才是真实水平,别只看训练集。
5.5 h5 模型文件损坏或下载不完整
现象:load_model报OSError: Unable to open file或Truncated file read。原因是压缩包解压不完整,或者.h5文件在传输中损坏。解决:重新解压,核对文件大小是否和压缩包里一致。如果模型文件确实坏了,只能重新跑train.py生成,这也是为什么建议你保留训练脚本和数据。
6. 进阶改造:把这份课设级项目往工程方向推一步
跑通只是起点。如果你想让这份项目在答辩时更有亮点,或者真的拿去做点小工具,有几个改造方向性价比很高。我一般会从「换模型结构」和「加验证手段」两头入手。
先说模型改造。当前是单层 LSTM,你可以换成Bidirectional(LSTM(64))做双向,让模型同时看前文和后文,情感分类里「但是」这种转折词对双向模型更友好。再进一步,把 LSTM 换成Bidirectional(LSTM(64, return_sequences=True))叠一层,后面接GlobalMaxPooling1D,能捕捉关键情感词而不被长文本稀释。改完记得重训,对比验证集准确率,别凭感觉说「应该更好」。
from tensorflow.keras.layers import Bidirectional, GlobalMaxPooling1D model = Sequential([ Embedding(input_dim=5000, output_dim=128, input_length=100), Bidirectional(LSTM(64, return_sequences=True)), GlobalMaxPooling1D(), Dropout(0.5), Dense(1, activation='sigmoid') ])逻辑说明:Bidirectional把 LSTM 正反两个方向输出拼接,return_sequences=True保留每个时间步的输出供池化层使用。GlobalMaxPooling1D取每个特征维度的最大值,相当于自动挑出最强烈的情感信号,比只取最后一步更鲁棒。这套结构在中小数据集上通常比单层 LSTM 高 2~5 个百分点。
再说验证手段。课设答辩最怕被问「你怎么证明模型有效」。除了准确率,我建议补一个混淆矩阵和几条错误案例分析。用sklearn.metrics几行就能出:
from sklearn.metrics import classification_report, confusion_matrix y_pred = (model.predict(X_val) > 0.5).astype(int) print(confusion_matrix(y_val, y_pred)) print(classification_report(y_val, y_pred, target_names=['负面', '正面']))逻辑说明:confusion_matrix让你看到模型在正负样本上分别错多少,如果负面样本召回率特别低,说明模型偏向预测正面,可能需要调阈值或做类别加权。classification_report给出精确率、召回率、F1,比单看 accuracy 有说服力得多。答辩时把这两个结果截图放进 PPT,比空口说「准确率 98%」扎实。
最后说一个我踩过的坑:别在演示前临时改代码。我见过太多人答辩前一晚「优化」了一下预处理,结果现场模型加载失败。从那以后我每次演示前都强制走一遍「全新环境 + 原始代码 + 三条测试用例」的流程,确认无误再上台。这份源码本身已经跑通,你要做的是理解它、小步改造它,而不是推倒重来。希望帮到你。
本文还有配套的精品资源,点击获取