
大家好这一篇我带你从零做一个能跑、能看、能答辩的毕业设计文本情感分类系统。这有一些练手的项目完整项目https://pan.quark.cn/s/1e54aa2ae950老规矩先聊清楚这东西是干嘛的。你去淘宝买个手机评论区一堆话你去豆瓣看电影短评一片。商家也好、平台也好最想知道的就是这些评论里大家到底是夸还是骂。人工看太费劲机器来分就很快。这个项目做的就是这件事给模型一句话它告诉你这句话是正面情感还是负面情感顺便告诉你它有多大的把握比如服务态度很好→ 正面 84%。我用的是最经典、最好讲、答辩时也最不容易被问倒的一套组合jieba 中文分词 TF-IDF 特征 逻辑回归分类器然后再加一个Tkinter 图形界面把数据预览、预测结果、分布图都放进一个窗口里看着就比纯命令行高级不少。整个项目所有图片都统一用 SimHei 黑体来显示中文Windows 下一般自带了不会出现方块乱码。一、项目结构长什么样先说目录心里有数了再往下看代码15_文本情感分类/ ├── data/ │ └── reviews.csv # 训练数据120 条中文评论 ├── tokenizer.py # jieba 分词函数训练和预测共用 ├── build_model.py # 训练模型 输出评估结果 画图 ├── app.py # Tkinter 图形界面 ├── 模型/ │ ├── vectorizer.pkl # 训练好的词表 │ └── model.pkl # 训练好的分类器 ├── 图/ │ ├── 01_数据分布.png # 正负样本数量柱状图 │ └── 02_混淆矩阵.png # 测试结果混淆矩阵热力图 └── requirements.txt运行顺序就两步先build_model.py把模型训练出来再app.py打开界面。下面一步步拆开讲。二、数据是怎么准备的网上有很多公开的情感数据集但对毕设来说自己造一份小数据反而更可控标签准、量不大、逻辑清晰答辩时每一行你都能解释。我的reviews.csv一共120 条正负各60 条内容和淘宝/京东评论风格很像。数据大概是这个样子的节选文本标签这个手机屏幕非常清晰运行速度很快一点都不卡positive客服态度很差问半天没人理体验非常糟糕negative价格实惠性价比很高推荐购买positive东西质量太差了用了一个星期就坏了气人negative外观设计很漂亮手感舒适非常满意positive发货太慢了等了半个月才到太失望了negative注意一个细节CSV 存成utf-8-sig编码带 BOM这样既不会乱码Excel 打开也正常。读数据时用 pandas 一行搞定df pd.read_csv(DATA_FILE, encodingutf-8-sig)先说结论这 120 条数据的分布是类别条数占比正面 positive6050.0%负面 negative6050.0%合计120100%正负完全平衡这样训练出来的模型不会偏科——它不会因为负面数据多就偷偷全判负面。三、核心算法为什么这么选很多人一看情感分析就以为得上深度学习其实这种短文本二分类传统机器学习就够了而且胜在能讲清楚、答辩稳。三条流水线环环相扣1. 分词jieba中文和英文不一样英文天然有空格分词中文是糊成一串的。服务态度很好这句话得先切成服务 / 态度 / 很好这样的词模型才知道看哪些单元。jieba 是最常用的中文分词库速度也快。2. 特征化TF-IDF机器不认识汉字只能认数字。TF-IDF 做的事就是把每句话变成一个向量向量的每一维代表一个词或者一个词组合。它的核心思想很朴素一个词在一句话里出现的次数越多越重要TF但如果它在所有评论里到处都在出现那它的区分度就下降IDF。比如质量差这个词就比东西这个词更能代表负面情绪。3. 分类逻辑回归逻辑回归输出的不是简单的是或否而是属于正面的概率比如 0.84。这点特别适合做界面展示能直接给用户一个置信度。它本身就是一个线性模型加上 sigmoid 激活好解释答辩老师问起来不慌。四、训练代码逐段讲解完整的训练脚本是build_model.py我把它拆成几块讲。第一部分基础配置和字体设置import os import sys import joblib import numpy as np import pandas as pd sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) from tokenizer import jieba_tokenizer import matplotlib matplotlib.use(Agg) import matplotlib.pyplot as plt from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.linear_model import LogisticRegression from sklearn.model_selection import train_test_split from sklearn.metrics import (accuracy_score, precision_score, recall_score, f1_score, confusion_matrix, classification_report) plt.rcParams[font.sans-serif] [SimHei] plt.rcParams[axes.unicode_minus] False两行rcParams很关键第一行把 matplotlib 的默认字体换成SimHei图里的中文标题、标签才不会变成小方块第二行是让坐标轴的负号正常显示。下面那行sys.path.insert是为了让训练脚本能顺利引入同目录下的tokenizer.py这样无论从哪个目录启动都不会找不到模块。第二部分加载数据SEED 42 def load_data(): df pd.read_csv(DATA_FILE, encodingutf-8-sig) df.columns [c.strip() for c in df.columns] df df.dropna().reset_index(dropTrue) return dfdf.columns一行是防坑用的防止表头里带看不见的空格dropna()把有空值的行扔掉保证训练数据干干净净。SEED 42是随机种子固定下来保证每次跑出来的结果一致答辩演示时前后数据对得上。第三部分jieba 分词函数def jieba_tokenizer(text): return [w for w in jieba.lcut(text) if w.strip()]jieba.lcut是把一句话切成词列表比如客服态度很差会切成客服 / 态度 / 很差。if w.strip()是过滤掉空白字符。为什么要单独放到tokenizer.py里而不是写在训练脚本里因为训练和预测要共用同一个分词函数而且这个函数会被存进模型文件写到独立模块里才能保证保存、加载都不出错。第四部分训练模型def build_model(X_train, y_train): vectorizer TfidfVectorizer( tokenizerjieba_tokenizer, ngram_range(1, 2), sublinear_tfTrue, token_patternNone) clf LogisticRegression(max_iter1000, C5.0) X_tr vectorizer.fit_transform(X_train) clf.fit(X_tr, y_train) return vectorizer, clf这里有两个值得一提的调参点ngram_range(1, 2)除了单个词还把相邻的两个词当一个特征。这样质量好和质量差这种带修饰的词组能被单独识别出来单看质量一个词反而分不清好坏。sublinear_tfTrue对词频做一次对数压缩防止某个词出现次数太多把其他特征盖过去。逻辑回归里max_iter1000是给足迭代次数让它收敛C5.0是正则化强度的反比调大一点让模型拟合得更充分。就这几行模型已经能用了。第五部分训练集测试集划分X_train, X_test, y_train, y_test train_test_split( df[文本], df[标签], test_size0.2, random_stateSEED, stratifydf[标签])test_size0.2表示 120 条里抽 96 条训练、24 条测试。stratify是分层抽样保证测试集里正负比例和全量一样都是 50% 对 50%不然测试结果会有偏差。第六部分评估模型acc accuracy_score(y_test, y_pred) p precision_score(y_test, y_pred, pos_labelpositive) r recall_score(y_test, y_pred, pos_labelpositive) f1 f1_score(y_test, y_pred, pos_labelpositive)四个指标一起看比单看准确率科学。准确率是整体预测对的占比精确率是预测为正面里真有几分是正面召回率是真正面里被找出几分F1 是精确率和召回率的调和平均两者偏科任何一头都会把它拉下来。测试集预测对错还会打印出来方便答辩时随手指着说你看这句预测对了置信度 73%。第七部分画图和保存模型def draw_distribution(df): counts df[标签].value_counts() ... plt.title(训练数据中正面 / 负面评论数量分布) plt.ylabel(评论条数) plt.savefig(os.path.join(IMAGE_DIR, 01_数据分布.png), dpi150) def draw_confusion(cm): ... plt.title(测试集混淆矩阵) plt.savefig(os.path.join(IMAGE_DIR, 02_混淆矩阵.png), dpi150) joblib.dump(vectorizer, os.path.join(MODEL_DIR, vectorizer.pkl)) joblib.dump(clf, os.path.join(MODEL_DIR, model.pkl))图一是正负样本数量对比柱状图图二是混淆矩阵热力图都保存到图目录。最后用joblib.dump把词表和分类器存成两个 pkl 文件这就是训练和预测解耦的关键界面启动时不用重新训练直接加载现成的模型秒开。五、真实运行结果数据展示我跑了一遍真实输出是这样的总样本数 120 正面比例50.0% 负面比例50.0% 测试集数量 24 准确率 79.17% 精确率 76.92% 召回率 83.33% F1 值 80.00%分类报告类别精确率召回率F1样本数负面0.820.750.7812正面0.770.830.8012测试样本对照前 8 条对表示预测正确判定预测置信度原文对正面60%物流速度杠杠的第二天就到了包装也很严实对正面71%锅炉加热快热水充足冬天用很舒服对正面56%音质特别好低音震撼戴上就不想摘下来对正面54%卖家服务热情有问必答售后很及时对负面68%客服敷衍退货一直被拖态度恶劣对负面55%信号极差经常断网打电话都听不清对负面73%做工粗糙缝隙很大明显是次品对负面79%防水效果差下雨天没一会就进水了在 120 条小数据上做到79% 的准确率、80% 的 F1对短文本二分类来说已经是不错的成绩了。混淆矩阵里可以看到模型在负面判成正面和正面判成负面上大概各错两三例这就是小样本数据的正常表现答辩时如实说清楚就是加分项。六、图形界面是怎么做的命令行输出再漂亮也抵不上一个窗口来得直观。我用 Python 自带的Tkinter写了个界面不需要额外装 UI 库。窗口分成三块顶部输入区一个文本框贴一句评论点开始预测。中间结果区大字显示正面情感 / 负面情感颜色红绿区分旁边再标置信度。下方标签页一页是数据集预览表格一页是本次会话所有预测结果的分布柱状图。关键代码是这样root tk.Tk() SentimentApp(root, vectorizer, clf) root.mainloop()预测的核心就三行vec self.vectorizer.transform([text]) label self.clf.predict(vec)[0] conf max(self.clf.predict_proba(vec)[0])注意这里用的是transform而不是fit_transform——因为词表已经在训练时建好了预测时只需要把新句子套进同一套词表这就是保存模型的好处。predict_proba返回的是两个概率取最大值就是置信度比如这个耳机音质太棒了预测正面置信度 84%界面上就显示绿色的大字正面情感 84%。柱状图是用 matplotlib 的FigureCanvasTkAgg嵌进 Tkinter 窗口的self.fig plt.Figure(figsize(5, 3), dpi100) self.ax self.fig.add_subplot(111) self.canvas FigureCanvasTkAgg(self.fig, mastertab2) self.canvas.get_tk_widget().pack(fillboth, expandTrue)每点一次开始预测就会往self.records里追加一条结果然后重画柱状图。你连续测十几句话能看着柱子一点点长高数据分布一目了然。界面的字体统一用了 SimHei中文标题、按钮、提示文字都不会乱码。七、怎么运行起来环境要求Python 3.8以及这几个库。numpy pandas scikit-learn matplotlib joblib jieba装完库之后两步走先训练模型在项目目录下运行python build_model.py运行完会看到评估结果同时生成模型里的两个 pkl 文件和图里的两张图片。再开界面运行python app.py会弹出图形窗口直接输入中文评论点预测就行。如果直接打开app.py提示找不到模型文件那就是第一步还没跑先回去把build_model.py跑一遍。八、答辩时老师可能问什么几个大概率被问到的问题提前想好答案问为什么不用深度学习答短文本二分类用传统机器学习已经能到 80% 左右的 F1逻辑回归训练快、好解释、还不用显卡更适合教学和毕设场景。如果数据量到几万条再考虑 BERT 之类的深度模型。问TF-IDF 到底在算什么答TF 是一个词在文本里出现的频率IDF 是这个词在整个语料里的稀有程度。两者相乘既看重词语在句子里是否重要又削弱那些到处都有的常见词。问置信度是什么答逻辑回归输出的本质是一个概率属于正面的概率 0.84就说明模型有 84% 的把握认为这句话是正面的。问还能怎么改进答增大训练数据量、加一个情感词典、试试支持向量机或者朴素贝叶斯做对比实验都能让准确率再上一截。