☰
Python酒店评论中文情感分析实战:Keras模型+PyQt5界面完整源码解析
2026/10/1 13:15:27 网站建设 项目流程

简介:这是一套面向计算机相关专业学生与项目实战学习者的Python酒店评论情感分析系统,可直接用于毕业设计、课程设计或期末大作业。项目以中文酒店评论为数据源,围绕文本预处理、情感分类模型训练与可视化界面展开,属于自然语言处理方向的入门到进阶实践案例。压缩包共43个文件,约56.55MB,包含6个py源码文件、20张png界面与结果图、3个jpg示意图、1个ui界面文件、1个ttf字体、1个h5模型权重、1个csv测试数据、1个json配置及说明文档等,覆盖从数据爬取、模型训练到图形界面展示的完整链路。目前已有291人学习下载。项目经过严格调试,确保可运行,附带使用说明与项目说明,读者可据此快速理解情感分析流程、复用界面代码、参考模型权重与测试数据,并在此基础上完成二次开发或论文撰写,适合需要完整可运行方案的学习者参考。

1. 从一份能跑通的酒店评论情感分析源码说起

打开这份利用Python实现酒店评论的中文情感分析.zip,第一眼看到的不是一堆散乱脚本,而是一套完整可运行的桌面系统:main.py是入口,main.ui配mainWindow.py撑起界面,crawl.py负责抓评论,weigths.h5是训练好的模型权重,test.csv、test.json、test.txt是现成的测试样本,accuracy_test.py单独做精度验证,font.ttf解决中文显示,wordCloud.jpg和analyse.png是可视化产物。它解决的核心问题很具体——把中文酒店评论自动判成好评、中评、差评,并且用图形界面把结果摆出来。适合正在做 Python 毕业设计、期末大作业,或者想找一个能直接跑通的中文情感分析实战项目练手的人。很多人卡在“模型训练完不知道怎么接到界面上”,这份代码把这条链路走通了。

2. 拆开这套源码:文件结构、技术栈与选型理由

2.1 目录里每个文件到底干什么

拿到压缩包解压后,根目录下大致分四类东西。第一类是入口与界面:main.py启动程序,main.ui是 Qt Designer 画出来的界面文件,mainWindow.py是它编译后的 Python 代码,resource.qrc和resource_rc.py管理图标、图片等资源。第二类是数据与模型:crawl.py抓取评论,weigths.h5保存训练好的网络权重,test.csv、test.json、test.txt是不同格式的测试数据。第三类是评估与可视化:accuracy_test.py算准确率,analyse.png、wordCloud.jpg、train.png、Average.png、Poor.png、Fair.png、Good.png、Excellent.png、count.png是各类统计图。第四类是辅助:font.ttf中文字体,使用说明.txt操作说明,demo1.png到demo4.png是运行截图。

这里有个容易忽略的点:__pycache__里带着cpython-36的 pyc 文件,说明作者当初是在 Python 3.6 环境下跑通的。你如果直接用 3.11 或 3.12,某些库的 API 可能已经变了,这是后面避坑章节要重点说的。

2.2 为什么用 Keras + h5 而不是 PyTorch

从weigths.h5这个文件名就能判断,模型是用 Keras(TensorFlow 后端)保存的 HDF5 格式。选它的理由很实际:毕业设计场景下,Keras 搭 LSTM 或 BiLSTM 做文本分类,代码量比 PyTorch 少,model.save('weigths.h5')一行就能把结构和权重一起存下来,加载时load_model直接还原,不需要额外写网络定义。对于中文情感分析这种“嵌入层 + 循环层 + 全连接”的经典结构,Keras 的Sequential模型足够用。

常见做法是:先用Tokenizer把中文评论转成整数序列,pad_sequences统一长度,然后走Embedding → LSTM → Dense(sigmoid 或 softmax)。三分类就用 softmax 输出 3 维,二分类就用 sigmoid 输出 1 维。这份代码从图片文件名看有 Excellent、Good、Fair、Average、Poor 五档,但实际建模时通常会合并成正面、中性、负面三类,否则样本不均衡会很难训。

2.3 界面层为什么选 PyQt5

main.ui+mainWindow.py的组合是 PyQt5 的典型工作流。用 Qt Designer 拖控件,保存成.ui,再用pyuic5转成.py。相比 tkinter,PyQt5 做出来的界面更接近商业软件,图表嵌入、表格展示、图片切换都更顺手。毕业设计答辩时,一个能点按钮、能出图、能切页签的界面,比命令行打印结果加分得多。

resource.qrc是 Qt 的资源系统,把图标、图片打包进 Python 文件,避免运行时找不到路径。resource_rc.py就是pyrcc5 resource.qrc -o resource_rc.py的产物。这一步很多人会忘,导致换台电脑图标全丢。

2.4 环境怎么搭:从零到能跑

先确认 Python 版本。源码里是 3.6,我建议用 3.7 或 3.8,太新的版本 TensorFlow 和 PyQt5 的兼容性会出问题。用 conda 建独立环境最稳:

conda create -n hotel_sa python=3.8 conda activate hotel_sa

然后装依赖。这份代码没有requirements.txt,需要手动装。核心就几个:

pip install tensorflow==2.4.0 pip install PyQt5==5.15.4 pip install jieba pip install pandas numpy matplotlib pip install wordcloud

tensorflow负责 Keras 模型加载和推理,PyQt5撑界面,jieba做中文分词,pandas读 csv,matplotlib画图,wordcloud生成词云。版本不要盲目追新,TensorFlow 2.4 配 Python 3.8 是经过验证的组合。

装完后先别急着跑main.py,先单独验证模型能不能加载:

from tensorflow.keras.models import load_model # 加载训练好的权重,compile=False 避免自定义损失函数报错 model = load_model('weigths.h5', compile=False) model.summary()

compile=False是关键。如果原模型用了自定义的损失函数或指标,直接加载会报Unknown loss function,加上这个参数先看结构,确认没问题再决定要不要补custom_objects。

3. 把评论喂进模型:分词、序列化与预测全流程

3.1 中文分词与停用词处理

中文情感分析第一步不是建模,是把句子切成词。jieba是标配:

import jieba def cut_text(text): # 精确模式分词,适合情感分析这种需要保留完整语义的场景 words = jieba.lcut(text) # 过滤掉单字和空白,单字对情感贡献低还增加噪声 words = [w for w in words if len(w) > 1 and w.strip()] return ' '.join(words) sample = "这家酒店位置很好,但是房间隔音太差了" print(cut_text(sample)) # 输出:酒店 位置 很好 但是 房间 隔音 太差

逻辑说明:jieba.lcut返回列表,比jieba.cut直接拿生成器方便。过滤单字是因为“的”“了”“很”这类词在情感分类里权重极低,留着只会让词表膨胀。参数上,len(w) > 1这个阈值可以调,如果你发现“差”“好”这种单字情感词被误删,可以改成保留特定情感词典里的单字。

停用词表一般放在stopwords.txt里,逐行读取后转成 set,分词后做差集。这份代码的test.txt可能就是测试语料,test.json可能是带标签的样本。

3.2 用 Tokenizer 把词转成数字

Keras 的Tokenizer负责建词表和转序列:

from tensorflow.keras.preprocessing.text import Tokenizer from tensorflow.keras.preprocessing.sequence import pad_sequences # num_words 限制词表大小,只保留出现频率最高的 5000 个词 tokenizer = Tokenizer(num_words=5000, oov_token='<OOV>') tokenizer.fit_on_texts(corpus) # 转成整数序列 sequences = tokenizer.texts_to_sequences(corpus) # maxlen 统一长度,短的补 0,长的截断 X = pad_sequences(sequences, maxlen=100, padding='post', truncating='post')

参数说明:num_words=5000是经验值,酒店评论的词汇量通常不大,5000 足够覆盖。oov_token给未登录词一个占位符,避免预测时遇到新词直接报错。maxlen=100意味着每条评论保留 100 个词,酒店评论一般不会超过这个长度,超了就截断。padding='post'在末尾补零,truncating='post'从末尾截断,保持一致。

这里有个坑:训练时的Tokenizer必须和预测时用的是同一个。如果你重新fit_on_texts,词表变了,序列号就对不上,模型输出全是乱的。正确做法是把训练好的tokenizer用pickle存下来,预测时加载。

3.3 加载模型做单条预测

import pickle from tensorflow.keras.models import load_model from tensorflow.keras.preprocessing.sequence import pad_sequences # 加载词表 with open('tokenizer.pickle', 'rb') as f: tokenizer = pickle.load(f) # 加载模型 model = load_model('weigths.h5', compile=False) def predict_sentiment(text): words = cut_text(text) seq = tokenizer.texts_to_sequences([words]) padded = pad_sequences(seq, maxlen=100, padding='post', truncating='post') # 输出三维概率,取最大值对应的类别 pred = model.predict(padded)[0] labels = ['负面', '中性', '正面'] return labels[pred.argmax()], pred result, probs = predict_sentiment("房间很干净,服务态度也好") print(result, probs)

逻辑说明:model.predict返回的是 batch 维度的数组,取[0]拿单条。argmax取概率最大的索引,映射到标签。如果你发现预测结果总是同一类,大概率是词表没对上,或者输入文本没做和训练时一样的分词。

3.4 批量测试与准确率验证

accuracy_test.py干的就是这件事。用test.csv里的带标签数据跑一遍:

import pandas as pd from sklearn.metrics import classification_report df = pd.read_csv('test.csv') true_labels = df['label'].tolist() pred_labels = [predict_sentiment(t)[0] for t in df['comment'].tolist()] # 输出精确率、召回率、F1 print(classification_report(true_labels, pred_labels))

classification_report比单纯看准确率有用得多。如果负面类召回率特别低,说明模型把差评误判成了好评,这在酒店场景里是致命的——用户看到差评被标成好评,直接不信任系统。

4. 界面与模型怎么接:PyQt5 调用 Keras 的实操细节

4.1 从 .ui 到可运行窗口

main.ui是设计文件,不能直接跑。需要转成 Python:

pyuic5 main.ui -o mainWindow.py pyrcc5 resource.qrc -o resource_rc.py

第一条把界面布局转成类,第二条把图片资源转成 Python 模块。转完后mainWindow.py里会有一个Ui_MainWindow类,main.py里实例化它并绑定按钮事件。

常见做法是在main.py里写一个继承QMainWindow的类,把Ui_MainWindow作为成员,然后在__init__里连接信号槽:

from PyQt5.QtWidgets import QApplication, QMainWindow from mainWindow import Ui_MainWindow import sys class HotelApp(QMainWindow, Ui_MainWindow): def __init__(self): super().__init__() self.setupUi(self) # 绑定按钮点击到预测函数 self.pushButton.clicked.connect(self.on_predict) def on_predict(self): text = self.textEdit.toPlainText() if not text.strip(): return result, _ = predict_sentiment(text) self.label_result.setText(f'预测结果:{result}') app = QApplication(sys.argv) window = HotelApp() window.show() sys.exit(app.exec_())

逻辑说明:setupUi负责把控件摆好,clicked.connect把按钮和槽函数绑起来。toPlainText()拿多行文本框内容,setText更新结果标签。注意predict_sentiment要提前定义或导入,否则界面点下去会报NameError。

4.2 把 matplotlib 图表嵌进 PyQt5

analyse.png、wordCloud.jpg这些图如果只是静态展示,用QPixmap加载到QLabel就行。但如果要动态画图,得用FigureCanvas:

from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas from matplotlib.figure import Figure class PlotCanvas(FigureCanvas): def __init__(self, parent=None): fig = Figure(figsize=(5, 4), dpi=100) super().__init__(fig) self.axes = fig.add_subplot(111) def plot_bar(self, labels, values): self.axes.clear() self.axes.bar(labels, values) # 中文标签需要指定字体,否则显示方块 self.axes.set_title('情感分布', fontproperties='font.ttf') self.draw()

font.ttf就是为这一步准备的。matplotlib 默认字体不含中文,不指定就会显示成方框。用font_manager注册后全局设置更省事:

from matplotlib import font_manager font_manager.fontManager.addfont('font.ttf') plt.rcParams['font.family'] = 'SimHei'

4.3 词云生成的参数怎么调

wordCloud.jpg是词云产物,生成代码通常长这样:

from wordcloud import WordCloud # 把正面评论拼成一个长字符串 text = ' '.join(positive_comments) wc = WordCloud( font_path='font.ttf', # 中文字体路径,不设会乱码 width=800, height=400, # 画布尺寸 background_color='white', # 背景色 max_words=100, # 最多显示 100 个词 colormap='viridis' # 配色方案 ).generate(text) wc.to_file('wordCloud.jpg')

参数说明:font_path必须指向支持中文的 ttf,max_words控制密度,太多会挤成一团。colormap可以换成Reds或Blues来配合情感倾向——正面用暖色,负面用冷色,视觉上更直观。

5. 避坑与排查:跑不起来时先看这几条

5.1 报No module named 'tensorflow'或版本冲突

现象:pip install tensorflow装完,导入时报ImportError或DLL load failed。

原因:Python 版本和 TensorFlow 版本不匹配。源码是 3.6 环境,你如果用 3.11,TensorFlow 2.4 根本没有对应的 wheel。

解决:降 Python 到 3.8,或者升 TensorFlow 到 2.10 以上。但升 TensorFlow 后load_model可能因为 Keras 版本差异报错,这时候用compile=False先加载,再手动compile。

5.2 加载 h5 模型报Unknown loss function

现象:load_model('weigths.h5')直接抛异常,提示某个 loss 或 metric 找不到。

原因:训练时用了自定义函数,保存时只存了名字,加载时没有对应的定义。

解决:先load_model('weigths.h5', compile=False)只加载结构,预测不受影响。如果必须 compile,用custom_objects传进去:

model = load_model('weigths.h5', custom_objects={'focal_loss': focal_loss})

5.3 界面中文显示成方块

现象:PyQt5 界面上的中文标签、按钮文字全是方框。

原因:系统缺少中文字体,或者 matplotlib 没指定字体。

解决:PyQt5 一般跟随系统字体,如果系统有中文字体还乱码,检查font.ttf是否被正确加载。matplotlib 那边必须显式设置font_path或rcParams['font.family']。

5.4 预测结果全是同一类

现象:不管输入什么评论,模型都输出“正面”或都输出“负面”。

原因:最常见的是词表对不上。训练时的Tokenizer和预测时的不是同一个,序列号全错。其次是输入文本没做分词,直接把整句喂进去,texts_to_sequences找不到对应词,全变成 OOV。

解决:确认tokenizer.pickle是训练时保存的那个,预测前先cut_text分词。如果还不行,打印padded看看是不是全零。

5.5crawl.py抓不到数据

现象:运行爬虫脚本报连接错误或返回空列表。

原因:目标页面结构变了,或者请求头没带 User-Agent 被拒。

解决:检查requests.get是否带了headers,解析用的BeautifulSoup或XPath是否还匹配当前页面。这类脚本依赖外部网站,时效性差,建议直接用test.csv里的现成数据做演示,别在答辩现场跑爬虫。

6. 让这份代码真正变成你的:改哪里、怎么验、如何讲

拿到一份能跑的源码,最忌讳的是“跑通就完事”。答辩老师或面试官问一句“你这个模型为什么用 LSTM 不用 TextCNN”,答不上来就露馅了。我的习惯是先把weigths.h5的结构打出来,逐层看参数:

model = load_model('weigths.h5', compile=False) for i, layer in enumerate(model.layers): print(i, layer.name, layer.output_shape if hasattr(layer, 'output_shape') else '')

看完你会知道嵌入层维度是多少、LSTM 单元数是多少、最后 Dense 是几维。这些数字就是你讲故事的素材——嵌入层 128 维说明词向量压缩得比较狠,LSTM 64 单元说明模型不大,适合小数据集。

然后做一件很多人不做的事:把test.csv按 8:2 切开,自己重新训一版,对比原模型的准确率。如果原模型在测试集上 92%,你自己训出来 88%,差距在哪?可能是学习率、batch size、或者早停策略。把这个对比写进论文的“实验分析”章节,比单纯贴一张准确率截图有说服力得多。

再进一步,改main.py里的界面逻辑。原版可能只支持单条输入,你加一个“批量导入 CSV”按钮,读文件后逐条预测,结果写回新 CSV。这个改动不大,但演示效果直接翻倍——老师看到你能处理批量数据,印象分完全不一样。

验证方法上,除了accuracy_test.py,我建议加一个混淆矩阵:

from sklearn.metrics import confusion_matrix import seaborn as sns cm = confusion_matrix(true_labels, pred_labels) sns.heatmap(cm, annot=True, fmt='d', cmap='Blues') plt.xlabel('预测') plt.ylabel('真实') plt.savefig('confusion_matrix.png')

这张图能直观看出模型在哪类评论上容易翻车。如果“中性”那一行错得特别多,说明中性评论的特征不明显,可以考虑合并到正面或负面,或者加更多中性样本。

最后说个血泪经验:答辩前一定要在答辩用的电脑上完整跑一遍。我见过太多人在自己电脑上好好的,换台机器就因为 Python 版本、缺少字体、路径带中文而翻车。把font.ttf、weigths.h5、tokenizer.pickle这三个文件单独拷出来,写一个最小加载脚本,确认在新环境下能跑通再上场。从那以后我每次交付这类项目,都强制走一遍“换机验证”,希望帮到你。

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

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

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

立即咨询