资讯动态

手把手实现商品评论情感分析:SVM、LSTM与Word2vec实战

发布时间:2026/10/18 9:39:16 来源:尧图企业网站定制
简介这份基于机器学习的商品评论情感分析项目面向计算机相关专业毕业生与需要实战练习的学习者可作毕业设计、课程设计或期末大作业。压缩包共含四十三个文件大小约六十六点六六兆字节涵盖Python源码、CSV与Excel数据集、训练好的模型文件、GUI界面及相关配置从评论采集、清洗、特征构建到模型训练与评估均有对应脚本。资源内含SVM支持向量机、LSTM长短期记忆网络等多种算法实现并预置训练好的词向量和情感分类模型下载后经严格调试可直接运行。目前已有三百一十八人浏览学习。项目按爬虫、预处理、模型、GUI等模块拆分目录结构清晰便于理解完整流程也适合在此基础上二次开发或扩展帮助快速掌握商品评论情感分析的实战思路。1. 商品评论情感分析从数据集到可运行的GUI这份毕设包到底怎么用有过类似经历的人应该都知道想找一份能直接跑通的商品评论情感分析毕设结果下载回来一堆源码环境一换就报错模型文件缺失GUI 点开就闪退。这份资源的不一样在于它把从数据清洗、分词、Word2vec 词向量、SVM 和 LSTM 两条模型路线到 GUI 界面都串通了还附带了训练好的 model.pkl、w2v_model.pkl、lstm_new.h5 等模型文件以及 pos.xls、neg.csv、neutral.csv 等标注数据集。适合正在做毕业设计、需要快速复现一个完整项目的人也适合想从传统机器学习对比到深度学习的初学者。它能让你少走弯路直接在一个已经跑通的项目上动手改参数、换数据、调界面。2. 先把数据洗干净评论预处理和语料维度2.1 原始文件里有什么pos/neg/neutral 三类标签与字段对齐解压之后data 目录下同时出现了 xls 和 csv 两种格式第一次看到会有些困惑。实际上 xls 是早期标注用的工作文件csv 是后面统一喂给模型的标准输入。项目里 pos.xls、neg.xls、pos.csv、neg.csv、neutral.csv、test_set.csv、res_comment.csv 这些文件要区分清楚。pos 和 neg 分别代表正向和负向评论neutral 是中性评论test_set.csv 是评测用的测试集res_comment.csv 是爬虫抓下来后还没来得及打标签的原始结果。这里有个很关键的设计三类标签不是平均分配的。从文件行数能看出负向评论往往比正向更尖锐中性评论在训练时容易被正向和负向吞掉。所以你后面做训练集划分时要显式控制随机种子否则每次跑出来的模型效果都不一样毕设答辩时很难解释。文件读取时还有一个容易翻车的点xls 和 csv 的列名不一致。xls 里可能是“评价内容”“标签”csv 里变成了“comment”“label”。建议一进预处理就把所有列名统一改成 comment 和 label后续所有代码都不再关心数据源是哪一种。2.2 预处理流程去重、去噪、分词与停用词的落地写法review_pretreatment.py 的核心价值在于把中文评论从原始字符串变成分好词的列表。常见做法是先用正则把 URL、用户、多余空格抽掉再用结巴分词切词最后过一遍停用词表。项目里已经集成了这套流程关键代码逻辑如下import re import jieba import pandas as pd stopwords set() with open(./data/stopwords.txt, r, encodingutf-8) as f: for line in f: stopwords.add(line.strip()) def clean_text(text): if not isinstance(text, str): return text re.sub(rhttp\S, , text) # 去掉 URL text re.sub(r\w, , text) # 去掉 用户 text re.sub(r\s, , text) # 合并空白字符 return text.strip() def cut_comment(text): text clean_text(text) words jieba.lcut(text, cut_allFalse) # 精确模式不用全模式 return [w for w in words if w not in stopwords and len(w) 1] df pd.read_csv(./data/pos.csv, encodingutf-8) df[comment_cut] df[comment].apply(cut_comment) df[label] 1逻辑说明先把每条评论清洗成干净字符串再用结巴的精确模式分词。cut_allFalse 很重要全模式会把“苹果手机”切成“苹果”“手机”“果手”对短评论是灾难。停用词过滤后还加了 len(w) 1 的条件把单字词尽量筛掉减少特征噪声。合并后的数据直接写成 train_cut.csv供下一步向量化使用。参数说明stopwords.txt 需要自己补充业务相关词比如“这家”“东西”“还行”这类不承载情感的词。如果你的数据集是餐饮评论建议把“味道”“环境”保留它们本身不表达正负但能作为上下文特征。预处理阶段不要做过度清洗比如把感叹号全删了情感强度会丢失。2.3 label 来源为什么同时存在 xls 和 csv读取时踩的第一个小坑项目里 neg.xls 是早期人工标注的原始表neg.csv 是整理后的版本。这两个文件如果都读进来会导致重复训练。我拆包时先看了行数neg.xls 有几百行neg.csv 也有几百行内容高度重叠。正确的做法是只读取 csv 版本xls 只作为标注来源参考不要直接进训练集。另一个坑是编码。csv 文件保存为 UTF-8但部分 xls 是从老系统导出的可能是 GBK。用 pandas 读取时如果不指定 encodingWindows 下默认会按 GBK 解读出来的中文全是乱码。解决办法是读之前先试 encodingutf-8如果抛 UnicodeDecodeError立刻换 encodinggbk。更建议在预处理入口统一转存为 CSV后续全部用 UTF-8。读取标签时还要注意中性评论的 label 有的是 0有的是 2。项目里 neutral.csv 的原始标签可能写成“中性”需要显式映射。label_map {negative: 0, neutral: 1, positive: 2, neg: 0, pos: 2} df[label] df[label].map(label_map)3. 两个特征层面的模型路线传统 SVM 与 Word2vec3.1 为什么先做 SVM 基线词向量对短文本的区分能力在这类情感分析任务里先用传统模型跑一个基线再用深度学习对比是毕设的标准打法。SVM 的优势在于小样本、高维稀疏特征下不容易过拟合而且训练速度快。项目中 w2v_model.pkl 是一个已经训练好的 Word2vec 模型它把每个词映射成 100 维向量。SVM 不直接吃词向量矩阵而是把一条评论的所有词向量取均值得到一个固定的 100 维向量作为特征。这个做法的逻辑是短评论本身语义密度高取平均词向量能保留大部分语义信息而且不受评论长度影响。相比之下TF-IDF 会制造出上万个稀疏特征在小数据集上训练慢、调参难。Word2vec 平均向量只需要 100 维SVM 跑起来飞快。3.2 训练 SVM 的代码与关键参数C 值、kernel 与模型持久化train_svm.py 里完整的流程是加载 train_cut.csv用 w2v_model 把每条评论的词语转换成向量再交给 sklearn 的 SVC 训练。核心代码片段如下import pickle import numpy as np from gensim.models import Word2Vec from sklearn.svm import SVC w2v_model Word2Vec.load(./model/w2v_model.pkl) def sentence_to_vec(words): vectors [] for word in words: if word in w2v_model.wv: vectors.append(w2v_model.wv[word]) if len(vectors) 0: return np.zeros(100) return np.mean(vectors, axis0) X [] y [] with open(./data/train_cut.csv, r, encodingutf-8) as f: for line in f: parts line.strip().split(\t) if len(parts) 2: words parts[0].split( ) y.append(int(parts[1])) X.append(sentence_to_vec(words)) clf SVC(kernellinear, C1.0, probabilityTrue) clf.fit(np.array(X), np.array(y)) with open(./model/svm_model.pkl, wb) as f: pickle.dump(clf, f)逻辑说明sentence_to_vec 先收集所有在词表里的词向量然后按列求均值。如果一条评论全是停用词或未登录词就返回全零向量避免 SVM 收到空输入。SVC 采用线性核这是文本分类里最常见的设置线性核在高维空间里的表现往往优于 RBF而且可解释性更好。参数说明C 值控制误分类惩罚默认 1.0。如果你的训练集负样本偏多可以把 C 调到 2.0 让 SVM 更重视分类边界。probabilityTrue 是为了后面 GUI 能显示预测概率。这里最容易翻车的是 Word2Vec 版本差异gensim 3.x 用 model.wv[word]4.x 也能用但如果之前保存时用的旧版本加载会有兼容问题后面避坑环节专门讲。3.3 SVM 的预测流程从分词到向量的全链路训练好模型后预测单条评论的完整链路是清洗、分词、过滤停用词、取词向量均值、SVM.predict。项目中 model.pkl 与 svm_model.pkl 可能是同一个模型经过 pickle 序列化后的不同副本实际使用时加载一个即可。预测代码示例import pickle from review_treatment import cut_comment clf pickle.load(open(./model/svm_model.pkl, rb)) text 物流很快质量也不错就是包装有点简陋 words cut_comment(text) vec sentence_to_vec(words).reshape(1, -1) proba clf.predict_proba(vec)[0] label clf.predict(vec)[0]逻辑说明predict_proba 输出的是每个类别的概率标签 0 表示负向1 表示中性2 表示正向。GUI 里展示「好评 / 中评 / 差评」时就是取概率最大的那个类别。如果概率最大值不超过 0.5说明这条评论本身情绪模糊比如“东西一般般吧”这时候与其硬判不如把结果标成“待定”。4. LSTM 那套深度学习流程词向量、序列填充与模型文件4.1 用 Word2vec 还是随机初始化w2v_model.pkl 怎么进 LSTMLSTM 的输入是词索引序列而不是词向量本身。项目里的 lstm 目录下有 train_lstm.py它先加载 w2v_model.pkl把这个静态词向量矩阵作为嵌入层的初始权重。这样做比随机初始化收敛更快泛化也更好。关键点是构建词索引时必须使用和 w2v_model 完全一致的词表否则会出现大量 out-of-vocabulary。常见的做法是写一个 vocab 字典按词频排序后给每个词一个索引然后在构建嵌入矩阵时对齐索引位置。如果词不在词表就用随机小向量初始化。代码结构如下import pickle import numpy as np from keras.models import Sequential from keras.layers import Embedding, LSTM, Dense, Dropout w2v_model pickle.load(open(./model/w2v_model.pkl, rb)) vocab {word: idx for idx, word in enumerate(w2v_model.wv.index_to_key, 1)} embedding_matrix np.zeros((len(vocab) 1, w2v_model.vector_size)) for word, idx in vocab.items(): embedding_matrix[idx] w2v_model.wv[word]逻辑说明index_to_key 是 gensim 4.x 的接口旧版本是 index2word。如果你手里的 w2v_model.pkl 是用旧版 gensim 保存的需要用 index2word 补兼容。embedding_matrix 的第一行全零作为 padding 占位。这样 Embedding 层的 mask_zero 参数才能正常工作让 LSTM 忽略补齐位。4.2 LSTM 网络结构设计与训练超参数embedding_dim、hidden、epochs项目中 train_lstm.py 训练出 lstm_new.h5 和 lstm_three.h5 两个权重文件本质上网络结构一致只是训练轮次或者隐藏单元数不同。lstm.yml 用于保存网络结构描述方便没有原始定义代码时也能恢复图结构。一个可复现的 LSTM 结构如下model Sequential() model.add(Embedding(len(vocab) 1, 100, weights[embedding_matrix], input_length50, mask_zeroTrue, trainableFalse)) model.add(LSTM(128, return_sequencesFalse)) model.add(Dropout(0.5)) model.add(Dense(3, activationsoftmax)) model.compile(losssparse_categorical_crossentropy, optimizeradam, metrics[accuracy]) model.fit(padded_sequences, labels, batch_size64, epochs10, validation_split0.2) model.save(./model/lstm_new.h5)逻辑说明Embedding 层输入长度固定为 50这是对评论截断或补齐后的最大长度。trainableFalse 表示训练时不再微调词向量在小数据集上避免过拟合。LSTM 输出 128 维特征经过 Dropout 后再接 softmax 分成三类。sparse_categorical_crossentropy 配合整数标签不需要 one-hot。参数说明input_length50 意味着超过 50 个词的评论会被截断。如果是长评建议调到 100。batch_size 和 epochs 是毕设项目里最容易出效果差异的参数epochs 默认 10但看训练损失曲线通常到第 5 轮就基本收敛再多就容易过拟合。lstm_three.h5 可能是用三分类标签直接训练的lstm_new.h5 是经过调参后的版本建议加载时先看模型文件大小越大的一般训练越充分。4.3 测试脚本 lstm_test.py加载模型跑单条评论lstm_test.py 演示了如何用已保存的 h5 文件预测新评论。这里有个容易被忽略的点模型预测前要对输入做同款预处理包括分词、补零、确保序列长度等于 50。代码如下from tensorflow.keras.models import load_model from tensorflow.keras.preprocessing.sequence import pad_sequences model load_model(./model/lstm_new.h5) def predict_comment(text): words cut_comment(text) seq [vocab[word] for word in words if word in vocab] if len(seq) 0: seq [0] padded pad_sequences([seq], maxlen50) proba model.predict(padded)[0] return proba proba predict_comment(这家的味道真的很一般等位太久) print(proba, int(proba.argmax()))逻辑说明pad_sequences 是 Keras 提供的序列填充工具超过 50 截断不足 50 在后面补 0。这里特别注意如果评论里所有词都不在词汇表里seq 为空必须强行补一个 0否则 pad_sequences 会报错。返回的 proba 是长度为 3 的概率分布argmax 得到标签索引。4.4 GUI 整合main_page.py 如何把两个模型接进去GUI 目录下 main_page.py 是程序入口它把 SVM 和 LSTM 都封装成了可切换的预测引擎。界面结构不复杂一个输入文本框、一个模型选择下拉框、一个预测按钮、一个结果展示标签。核心切换逻辑如下def on_predict_clicked(): text input_box.toPlainText() if model_combo.currentText() SVM: proba svm_predict(text) else: proba lstm_predict(text) label int(proba.argmax()) result_label.setText([差评, 中评, 好评][label])逻辑说明SVM 和 LSTM 的预测函数都返回长度为 3 的概率数组所以 GUI 层不需要关心底层模型差异。如果你把第三个类别映射成“中评”需要保证训练标签顺序是 0 差评、1 中评、2 好评这个顺序在项目里的 neutral.csv 和 test_set.csv 中是统一的。5. 避坑排查手册从下载解压到 GUI 运行五个翻车现场5.1 解压后缺 pickle 文件先检查相对路径现象运行 train_svm.py 或 main_page.py 时报错 FileNotFoundError: ./model/w2v_model.pkl 不存在但压缩包里明明有 model.pkl。原因解压时把 model 目录单独解压到了另外一层或者把整个 zip 解压后手动移动过子文件夹导致代码中的相对路径失效。另一个常见原因是系统对隐藏文件不显示model 目录里实际有文件但你没看到。解决保持 zip 解压后的原始目录结构不要单独把 model 或 data 拖出来。检查 model 目录下有没有 w2v_model.pkl如果没有说明解压工具跳过了某些文件。解压时选择“全部解压”不要勾选“只解压选中的文件”。启动代码前在终端执行 ls model 确认文件都在。5.2 加载 h5 报错 Unknown layer 或无法导入自定义模块现象load_model(./model/lstm_new.h5) 时抛出 ValueError: Unknown layer: Attention或者提示 Could not import module。原因h5 文件里保存了模型结构但其中可能引用了自定义层比如注意力层或 Lambda 层。当前项目代码里如果没有定义同样的层Keras 就没法恢复模型。更隐蔽的原因是 tensorflow 和 keras 版本混装h5 文件里记录的类名和当前安装的库对不上。解决不要用 load_model 直接加载整张图改成只加载权重。也就是先用代码手动构建一模一样的模型再调用 load_weights(./model/lstm_new.h5)。权重加载对结构版本更宽容。如果连 load_weights 都报名字不匹配说明 h5 里保存的结构和当前定义的层维度不一致需要检查 input_length 和 LSTM 单元数。5.3 中文分词结果全是单个字现象训练出的模型预测准确率只有 50%打印分词结果发现每条评论都被切成一个个汉字比如“这东西真好吃”变成“这/东/西/真/好/吃”。原因jieba 分词时词典没有加载项目自带的用户词典或者 w2v_model.pkl 训练时用的是自定义词典而你的预处理脚本没有引用同一个词典。另一个可能是 jieba 版本太新默认词典对某些商品领域名词切分不对。解决在预处理脚本开头加上 jieba.load_userdict(./data/userdict.txt)把“好吃”“物流”“客服”这类业务词加进去。如果项目里没有 userdict.txt就把 w2v_model.pkl 的词表导出把高频词当成自定义词典。重点是与训练词向量时使用的分词完全一致否则句子向量化时大量词 out of vocabulary。5.4 GUI 点“预测”没反应或卡死现象在 main_page.py 界面输入评论点击预测按钮界面直接无响应过几分钟才恢复甚至直接闪退。原因预测函数在主线程执行加载模型和预测都是耗时操作。尤其是同时加载 svm_model.pkl 和 lstm_new.h5模型文件加起来几十 MB在低配电脑上会阻塞 UI 消息循环。还有一种可能点击预测时调用的分词函数里用了重正则对长文本性能极差。解决把模型加载放在程序初始化阶段只加载一次。预测函数里不要反复 load_model。如果界面仍卡顿用 QThread 把预测任务放到子线程主线程只更新按钮状态和结果文本。代码量不大但效果立竿见影。5.5 数据集读出来标签错位现象训练时 loss 一直在 1.0 附近acc 也提不上去。检查数据时发现 pos.csv 的第一行内容是“评论内容,label”但 label 列读出来全是 NaN。原因部分 csv 文件不是标准逗号分隔可能用了竖线 | 或者制表符。Pandas 的 read_csv 默认按逗号解析遇到文件里没有逗号时会把整行当成一个字段。另外一个常见原因是列名首尾带空格比如 “comment ”和 “label ”导致 df[label] 找不到列名。解决读取时指定 sep 参数先用文本编辑器打开看实际分隔符再像下面这样读df pd.read_csv(./data/pos.csv, sep|, encodingutf-8) df.columns [c.strip() for c in df.columns]如果还是有缺失打印 df.head() 检查前五行。结构化数据文件没问题后再进下一步这个习惯能省掉很多训练环节的玄学问题。6. 进阶技巧用交叉验证和阈值调整把模型再往上拉一截6.1 五折交叉验证评估 SVM 稳定性很多毕设项目只给一个 train_test_split 的结果答辩时评委问“模型稳定吗”就没法答。建议把 SVM 的训练改成 KFold 交叉验证既能压出更可信的准确率也能画出每折的结果对比。实现代码很短from sklearn.model_selection import cross_val_score, StratifiedKFold clf SVC(kernellinear, C1.0) skf StratifiedKFold(n_splits5, shuffleTrue, random_state42) scores cross_val_score(clf, X, y, cvskf, scoringaccuracy) print(scores, mean:, scores.mean())这里用 StratifiedKFold 保证每一折里正负中比例和整体一致。如果五折准确率标准差超过 0.05说明某个类别的样本太少或者预处理分支对某些长评论处理不一致。交叉验证后再用全部数据重新训练一次并保存这样最终的 svm_model.pkl 才拥有最大代表性。验证 LSTM 时也建议使用同样思路但要注意深度模型训练时间长在毕设场景下做三次五折不现实可以只做一次五折取平均。如果时间紧张至少跑两个不同随机种子的 80/20 切分防止单次划分带来的运气。6.2 调整分类阈值让“中评”不再被吞掉默认情况下模型把三条概率中最大的那一个当预测结果。这个做法在样本均衡时才合理。你的数据里 neutral.csv 行数明显低于 pos 和 neg导致模型对“中评”的预测概率总是偏低。一个实用技巧是调整决策阈值只在正向概率超过 0.6 时才判好评负向概率超过 0.6 才判差评否则全部归为中评。这样等于给中评一个缓冲区间更符合真实业务中对模糊评论的处理。def flexible_predict(proba, pos_th0.6, neg_th0.6): if proba[2] pos_th: return 2 if proba[0] neg_th: return 0 return 1阈值设多少需要用测试集跑一遍混淆矩阵。先看差评的召回率如果差评总被误判为中评就把 neg_th 调低到 0.4。反过来如果好评被误判太多就抬高 pos_th。这个参数调整过程虽然简单但能显著提升答辩时展示的 F1 score。我还习惯把不同阈值下的准确率曲线画出来用 matplotlib 保存成 PNG答辩时直接贴进 PPT评委一看就知道你不是只调了个包。项目里的 draw_plot.py 已经实现了类似的绘图功能改一下阈值列表就能复用。最后说一点自己的血泪经验从那以后我每下载一个毕设项目都会先花十分钟梳理文件结构和依赖版本把所有模型加载路径统一改成绝对路径再写一个 smoke_test 脚本验证空文本、全停用词文本和超长评论三种边界情况。这类项目最大的坑往往不在模型算法而在环境与数据接口。等你被“明明模型文件都在却加载失败”折磨过一次就会真正理解这个习惯的价值。希望这份拆解能帮你把项目稳稳跑起来顺利度过毕设这一关。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑