简介:这是一份基于Python的机器学习文本情感系统毕业设计文档,面向计算机相关专业毕业生与自然语言处理初学者,系统讲解从需求分析到系统实现的完整流程。文档围绕Python、Django框架与MySQL数据库展开,给出了情感分类系统的总体架构、功能模块划分、数据库设计,并具体展示登录模块、管理员首页、文本分类、文本管理、用户信息管理等核心界面的实现思路,适合课程设计、毕业设计或作为相关项目起步参考。资源仅包含1个docx文档,压缩包约1.07MB,目录覆盖绪论、系统分析、系统设计、系统实现与系统测试等完整章节,结构清楚、便于按需查阅。目前已有323人学习下载,是一份能够帮助读者快速理解文本情感分析系统设计要点并在此基础上扩展开发的实用资料。
1. 基于python的机器学习文本情感系统:它解决的是什么问题
把一段网络评论自动判断成“积极”或“消极”,听起来只是个小功能,但真正要搭一套能演示、能答辩、能在浏览器里完整跑通的系统,背后还牵涉中文分词、模型训练、页面交互、数据落库和后台管理一整套链路。这套基于python的机器学习文本情感系统设计与实现,选用的是 Python + Django + MySQL 的 B/S 架构:用户在网页文本框里输入一句中文,系统调用训练好的情感分类模型返回情绪倾向,并把判断记录存进数据库,管理员登录后可以查看、删除分类记录,维护用户信息。它适合正在做文本情感方向毕业设计或课程设计的人拿来复现,也适合刚接触机器学习项目、想弄清“算法到底是怎么被 Web 项目调用起来”的初学者。
2. Django + MySQL 技术选型:MTV 架构与数据库设计落地
2.1 为什么是 Django 而不是 Flask:MTV 分层带来的开发效率
文本情感系统的核心是算法,但这不代表 Web 部分可以随便凑合。论文里选定 Django 而不是 Flask,核心原因是 Django 是一个自带电池的框架:ORM、表单处理、会话管理、CSRF 防护、后台模板引擎全部内置,开发者不需要花费额外精力去拼装第三方库。对一个以“情感分类效果好、页面能跑通”为目标的毕设项目来说,用 Flask 虽然轻量,但登录、权限、数据库迁移这些通用机制都要自己接,整体工作量反而会大不少。
更要紧的是 Django 的 MTV 分层方式,恰好把“算法调用”和“业务逻辑”拆得很清晰。M 对应 Model,负责和 MySQL 的表结构做映射;T 对应 Template,负责渲染 HTML 页面;V 对应 View,负责接收 HTTP 请求、调用情感分类模型、组织数据并返回响应。实际写代码时,训练好的模型被封装成一个独立组件,放在 View 层里被调用,算法部分和 Web 部分互不干扰。这也是答辩时经常被问到的概念题:MVC 和 MTV 的区别到底是什么。
Django 的 MTV 在职责安排上对应经典 MVC 模式,但控制器角色被框架一部分吸收掉了。URL 路由负责把请求分发给对应 View,而 View 内部处理业务逻辑,模板系统承担展示逻辑。简单说,你在 Django 里写的 View,承担的其实更接近 Controller 加 Model 调用的混合职责。理解了这一点,后面写文本分类接口、登录校验、记录管理时,心里会非常有数。
2.2 数据库表设计:管理员表与文本分类表字段定义
论文中的 E-R 模型给出了两个核心实体:管理员实体和文本分类实体。管理员实体包含管理员编号、用户名、密码;文本分类实体包含分类编号、分类内容。落到 MySQL 里,按常规表结构设计思路扩展即可。
管理员表负责保存后台登录账号,字段包括用户 ID、用户名、加密后的密码、手机号、创建时间。文本分类表要记录每一次情感判断过程,字段包括分类记录 ID、关联用户 ID、原始输入文本、模型判断结果、创建人、创建时间。之所以要把“原始输入文本”和“判断结果”分开存,是为了后续在文本管理界面能回看每一次判断的来源和依据,也为以后扩充训练数据做准备。MySQL 在这类场景下非常合适:数据量不大,关系型结构清晰,多用户并发查询也有保障。
创建数据库表的 SQL 语句可以这样写:
CREATE DATABASE sentiment_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE TABLE admin_user ( id INT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(255) NOT NULL, phone VARCHAR(20), create_time DATETIME DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE sentiment_record ( id INT AUTO_INCREMENT PRIMARY KEY, user_id INT, content_text TEXT NOT NULL, sentiment_result VARCHAR(10) NOT NULL, creator VARCHAR(50), create_time DATETIME DEFAULT CURRENT_TIMESTAMP );这段建表逻辑有几个关键参数值得说明。数据库字符集指定 utf8mb4,而不是直接使用默认字符集,是为了让中文文本能够完整存储,也兼容 Emoji 符号,这个细节在部署到 Windows 或 Linux 时经常能避免一场乱码事故。sentiment_result 字段预留 VARCHAR(10),足够存放“积极”“消极”两个字;如果你后续想把分类扩展到“中性”甚至更细粒度,只需要加一个模型输出映射,不需要改动表结构。Django 实际开发时我们一般直接借助 ORM 的 migrate 来自动建表,但保留一份手写 SQL 在论文里做数据字典,对答辩展示更直观。
2.3 初始化 Django 项目并接入 MySQL:从 startproject 到跑通登录页
环境准备阶段,先创建项目和工作目录。
pip install django mysqlclient jieba scikit-learn joblib django-admin startproject sentiment_system cd sentiment_system python manage.py startapp sentiment安装包时建议用虚拟环境,不要把包直接装到系统 Python 里,这能减少后面实验环境依赖冲突的概率。其中 mysqlclient 是 MySQL 的 Python 驱动,安装失败时可以考虑用 PyMySQL 替代,这一点在避坑章节会展开讲。创建完应用后,需要把 sentiment 加入 INSTALLED_APPS,再在 settings.py 里配置数据库连接。
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'sentiment_db', 'USER': 'root', 'PASSWORD': '你的数据库密码', 'HOST': '127.0.0.1', 'PORT': '3306', 'OPTIONS': { 'charset': 'utf8mb4', }, } }这里的核心参数就三组:ENGINE 指定后端数据库类型,NAME 对应刚才用 SQL 创建的库名,HOST 和 PORT 决定连接地址。OPTIONS 里显式加 charset 是惯用做法,可以避免后续读取中文时出现编码不一致的问题。配置完成后执行 python manage.py migrate,Django 会为自带的用户、会话等表自动建表。跑通这一步以后,项目就能和 MySQL 正常通信,接下来才轮到文本情感分类的核心模块。
3. 情感分类模块实现:TF-IDF + 朴素贝叶斯从训练到调用
3.1 算法选型思路:为什么先上 TF-IDF + 朴素贝叶斯
文本情感分类的主角是模型,但模型并不是越复杂越好。对于中文短文本分类来说,TF-IDF 加朴素贝叶斯组合几乎是入门阶段性价比最高的方案,原因有三。词频思路非常直观,把每一句话转成词频向量,再计算每个词在文档中的重要程度;实现成本低,sklearn 中几行代码就能完成;性能足够,在情感倾向这种二分类任务上,配合适量数据通常能稳定达到可用水平。
比它更复杂的方案——LSTM、BERT、TextCNN——当然效果更好,但代价是数据需求大、训练时间长、环境配置重。一个毕业设计系统如果要兼顾算法深度和项目完成度,朴素贝叶斯这类经典模型反而更容易把整条链路讲清楚。论文中提及自然语言分类是热点话题,且系统设计目标是让机器能判断语义表达是积极还是消极,这个任务用 TF-IDF 将文本向量化之后交给朴素贝叶斯分类,完全能够满足设计目标。更重要的是,这套方案在普通笔记本上就能完成训练,不需要 GPU,也不涉及复杂的中文预训练模型下载,复现门槛非常低。
还有一层原因和项目的可解释性有关。朴素贝叶斯本质上是基于条件概率的判别模型,它把每个词在类别下出现的概率累计起来,最后取最大概率的类别作为判断结果。这种计算逻辑在论文中描述起来非常清晰:机器选定一些情感倾向明显的词作为特征词,把句子中的这些词加权汇总,再对比积极类和消极类的综合得分得出判断。比起深度学习模型的黑匣子特性,朴素贝叶斯的决策过程更容易用朴素的语言展示给评委。
3.2 训练脚本:分词、向量化、模型保存
中文文本和英文文本最大的差别在于没有天然空格分词,所以任何中文文本分类项目的第一步都是中文分词。这里使用 jieba 分词库,它是最常用的开源中文分词工具,支持精确模式、全模式和搜索引擎模式,默认采用精确模式就能满足大多数短文本分类需求。分好词以后,把词用空格拼接,再用 TF-IDF 向量化器转换,最后喂给朴素贝叶斯模型训练。
# train_model.py import jieba import joblib from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.naive_bayes import MultinomialNB # 样本格式:(文本内容, 标签),1 表示积极,0 表示消极 raw_data = [ ("这家酒店环境很好,服务到位", 1), ("下次还会再来,性价比高", 1), ("交通方便,房间也干净", 1), ("卫生特别差,不会再住", 0), ("环境太差了,窗户还漏风", 0), ("前台态度很差,等了一个小时", 0), ] def cut_words(text): return " ".join(jieba.cut(text)) X = [cut_words(text) for text, _ in raw_data] y = [label for _, label in raw_data] vectorizer = TfidfVectorizer() X_vec = vectorizer.fit_transform(X) model = MultinomialNB() model.fit(X_vec, y) joblib.dump(vectorizer, "vectorizer.pkl") joblib.dump(model, "sentiment_model.pkl") print("模型训练完成并已保存")这段脚本的逻辑链路是:先对每一条文本用 jieba.cut 做分词,再转成空格分隔的字符串;TfidfVectorizer 接收这个字符串列表,统计每个词在全部文本中的出现频率和逆文档频率,把每条文本变成一个稀疏向量;MultinomialNB 对这个稀疏向量做训练,学习积极类和消极类各自的特征分布。最后用 joblib 把向量化器和模型分别保存成 pkl 文件,供 Django 项目调用。
关键的参数调节点是 TfidfVectorizer 的初始化参数。默认情况下它会忽略单个字符的词,这对中文场景基本够用,但如果希望在短文本上有更好效果,可以显式指定 token_pattern 来保留中文上下文特征。另一个值得调整的点是 ngram_range,默认值 (1, 1) 只提取单个词,改成 (1, 2) 后可以把相邻的两个词当成一个特征,比如“环境 差”和“环境 好”就能被区分得更开。需要留意的是,ngram_range 提大会成倍增加特征维度,在数据集不大的情况下反而可能引入噪声,所以这里并不建议一上来就盲目调大。
3.3 在 Django View 中加载模型并返回判断结果
模型训练完毕以后,Django 项目需要做的事情非常纯粹:在视图函数里加载 pkl 文件,对用户输入做相同流程的分词和向量化,然后调用 predict 得到结果,最后把结果返回给模板展示。因为训练脚本已经保存了完整的向量化器,分类接口不需要重新实现 TfidfVectorizer 逻辑,只需要复用同一个 vectorizer.pkl。
# sentiment/views.py import jieba import joblib from django.shortcuts import render from .models import SentimentRecord model = joblib.load("sentiment_model.pkl") vectorizer = joblib.load("vectorizer.pkl") def classify_text(request): if request.method == "POST": content = request.POST.get("content", "").strip() if not content: return render(request, "text_classify.html", {"error": "请输入文本内容"}) words = " ".join(jieba.cut(content)) vec = vectorizer.transform([words]) predict = model.predict(vec)[0] sentiment = "积极" if predict == 1 else "消极" SentimentRecord.objects.create( user=request.user, content_text=content, sentiment_result=sentiment, creator=request.user.username, ) return render(request, "text_classify.html", { "content": content, "sentiment": sentiment, }) return render(request, "text_classify.html")这个视图包含三个关键环节。第一个环节是文本预处理:从 POST 请求中取到用户输入,用 strip 去掉首尾空字符,再用 jieba.cut 做分词,注意这里分词方式必须和训练脚本完全一致,否则向量特征对不上,判断结果就会失控。第二个环节是模型预测:把分词后的字符串交给 vectorizer.transform 转成向量,再交给 model.predict,返回结果是整数 1 或 0,映射成“积极”“消极”展示给用户。第三个环节是记录入库:每次判断完成后把内容、结果、创建人写进 SentimentRecord 表,这样文本管理界面就有数据可以展示。
还有一个容易被忽略的细节:模型加载时机。上面代码把 joblib.load 放在模块顶层,服务启动后只加载一次,后续所有请求共享内存中的模型对象,响应速度会快很多。如果放在视图函数内部每次加载,输入一句话要等几秒钟才能出结果,论文里描述“经过一段时间的响应”就是这种体验。不过这里也要注意,训练脚本每次更新模型后,必须重启 Django 服务进程才能让新模型生效,这一点在调试时容易踩坑。
4. 登录、文本管理、用户管理:三个后台功能的 Django 实现
4.1 登录模块与 Session 校验
后台管理功能建立在登录基础之上,Django 提供的 auth 模块可以大幅简化登录逻辑。教科书式做法是直接用 authenticate 和 login 两个内置函数完成认证与登录,并在需要权限的视图上使用 login_required 装饰器做访问控制。
from django.contrib import auth from django.shortcuts import render, redirect def login_view(request): if request.method == "POST": username = request.POST.get("username", "") password = request.POST.get("password", "") user = auth.authenticate(username=username, password=password) if user: auth.login(request, user) return redirect("index") return render(request, "login.html", {"error": "用户名或密码错误"}) return render(request, "login.html")这段代码的逻辑很直白:authenticate 负责核对用户名和密码,如果验证通过返回用户对象,失败则返回 None;auth.login 将用户状态写入 session,后续请求 Django 就能识别当前登录用户。redirect 指向首页,也就是论文里提到的管理员登录首页,这里还可以顺手把用户信息写进 session,方便其他页面读取。要注意的是密码字段在数据库中不能以明文存储,创建管理员时应该使用 Django 提供的 set_password 方法处理。
管理员登录首页里展示的统计数据,通常包括当前系统中的文本数量、分类类别、用户数量以及年份信息。数据来源就是前面建好的 sentiment_record 表和 admin_user 表,通过 ORM 的 count 方法聚合统计。左侧菜单栏包含首页、文本分类、文本管理、密码修改、用户信息查看等入口,模板逻辑不复杂,但需要注意菜单选中态的判断,让用户知道当前处在哪个模块中。
4.2 文本分类页面:从表单提交到记录入库
文本分类页面是整套系统里最核心的用户入口。用户在文本框中输入一句话,点击开始分类按钮,后端调用模型判断情绪,并立刻把结果展示在同一页面上。表单提交时采用 POST 方式,模板文件中的 form 标签里必须带上 Django 的 CSRF 令牌,否则请求会被拦截,具体细节在避坑清单中会展开。
<form method="post" action="/classify/"> {% csrf_token %} <textarea name="content" rows="4" placeholder="请输入要判断的中文文本"></textarea> <button type="submit">开始分类</button> </form> {% if sentiment %} <p>输入内容:{{ content }}</p> <p>情感判断:{{ sentiment }}</p> {% endif %}模板里有两个关键绑定:name="content" 的值会被 POST 请求体发到后端,视图函数通过 request.POST.get("content") 取出;渲染结果用 {{ sentiment }} 和 {{ content }} 占位符输出。如果后端返回 error 字段,页面上也应该有对应的错误提示容器。这里的重点是前后端字段名必须一致,否则会出现输入了文本但后端取不到值的翻车情况。
分类完成写入数据库的动作在视图函数里已经处理过,这里需要说明的是记录的用户归属。表结构里 user 字段存的是当前登录用户的 ID,creator 存的是用户名。这样文本管理界面在展示记录时,可以直接通过外键关联拿到用户信息,也可以直接用 creator 字段显示创建者,两个字段分别是逻辑关联和冗余展示,后一种写法在简单管理系统中更直观,减少一次表连接。
4.3 文本管理与用户管理:后台维护的两种典型写法
文本管理界面需要把 sentiment_record 表里的所有记录以列表形式展示出来,展示字段包括记录 ID、输入文本、机器判断结果、创建人,并提供删除操作。论文截图里体现了管理员可以对某条信息进行删除,这是后台管理模块中最常规的功能。
def text_manage(request): records = SentimentRecord.objects.all().order_by("-create_time") return render(request, "text_manage.html", {"records": records}) def delete_record(request, record_id): if request.method == "POST": SentimentRecord.objects.filter(id=record_id).delete() return redirect("text_manage") return redirect("text_manage")这里的逻辑要点是删除操作用的是 POST 提交而不是 GET 请求。原因很简单,GET 请求会暴露在浏览器地址栏和日志记录中,还容易触发误删,用 POST 方式配合表单按钮可以大大减少误操作概率。order_by("-create_time") 表示按创建时间倒序排列,最新记录显示在最上方,这个细节能明显提升用户体验。
用户信息管理模块的处理方式和文本管理几乎一致。管理员可以查看用户列表,列表中能显示用户名、手机号等信息,支持的操作为修改和删除。Django 中可以直接对自带的 User 模型做查询,如果需要展示更多字段,可以建立 Profile 扩展表。这里要特别提醒一个操作边界:删除用户前应该检查该用户是否有历史文本记录,以免外键关联引发数据异常。比较稳妥的做法是使用逻辑删除,给用户表加一个 is_active 字段,将账号置为不可用而不是物理删除,这样可以保留完整的历史数据链路,对后续做数据分析也更有利。
5. 避坑清单:部署调试中常见的 5 个问题
5.1 中文乱码:建库时没指定 utf8mb4
现象:系统登录后输入中文文本,分类结果页面正常显示,但打开数据库管理工具或文本管理列表,看到的内容变成一串问号或者乱码。
原因:创建数据库时使用了 MySQL 默认字符集,在部分环境下默认是 latin1,无法完整表达中文;另一种情况是数据库字符集正确但连接时没有指定 charset,Django 和 MySQL 之间传输编码不一致。
解决:建库时显式声明 utf8mb4,配置 Django 数据库连接时在 OPTIONS 里加上 charset,两个位置缺一个都可能出问题。已经建错库的话,可以用 ALTER DATABASE sentiment_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci 修复,同时把存量表也做一次字符集转换。从那以后我每次初始化项目,都会先在 SQL 里写好字符集,再回头配置 Django 连接,顺序反了很容易在后期才爆雷。
5.2 启动报错:Python 3 下找不到 MySQLdb 模块
现象:执行 python manage.py runserver 后控制台报 ModuleNotFoundError: No module named 'MySQLdb',Django 无法连接数据库。
原因:Django 通过 MySQLdb 模块连接 MySQL,但 Python 3 环境中这个模块不是内置的,需要安装第三方驱动。最常用的驱动是 mysqlclient,但它依赖系统编译环境,Windows 上经常因为缺少 MSVC 编译工具而安装失败。
解决:优先尝试 pip install mysqlclient,如果安装失败就改用 PyMySQL。安装完成后在项目init.py 里加一段兼容代码,把 PyMySQL 伪装成 MySQLdb:
import pymysql pymysql.install_as_MySQLdb()这段代码必须在 Django 正式连接数据库之前执行,通常放在项目包的init.py 里。要注意 PyMySQL 和 mysqlclient 不能并存,二者同时安装可能导致驱动冲突。
5.3 模型预测不准:训练集太小且样本不均衡
现象:输入“环境太差了”这类明显消极的文本,模型返回“积极”,完全不符合常识。
原因:训练数据只有几条,模型几乎没有学到情感特征,只是记忆了训练样本中的孤立词汇。尤其在样本不均衡时,积极类样本数量远超消极类,模型会倾向把所有输入都猜测为数量占优的类别。
解决:先把训练集扩大到几百条甚至几千条,保持积极和消极样本数量基本均衡。如果短期内没有足够标注数据,可以退而求其次使用 SnowNLP 自带的预训练情感模型,效果比几条样本强不少。人工检查模型效果时,不要只挑自己设置过的句子测试,随意找几条真实评论效果会更有参考价值。
5.4 页面有结构无样式:静态文件加载 404
现象:登录页能显示输入框和按钮,但页面完全没有样式,浏览器控制台里 CSS 文件返回 404。
原因:Django 开发服务器不会自动收集每个应用下的静态文件,如果模板中引用的 CSS 路径和实际目录不匹配,就会加载失败。更隐蔽的情况是 STATICFILES_DIRS 配置的是当前项目下的目录,但实际文件放在应用目录下。
解决:在 settings.py 里设置 STATIC_URL 为 /static/,并配置 STATICFILES_DIRS 指向实际静态文件目录。模板文件开头使用 {% load static %},引用时写 {% static 'css/login.css' %} 而不是硬编码路径。部署环节再执行 python manage.py collectstatic,把分散在各应用的静态文件统一收集起来。按照这个顺序配置,静态文件基本不会再有幺蛾子。
5.5 表单提交 403:CSRF 令牌缺失
现象:点击登录或分类按钮,页面显示 CSRF verification failed,请求被拒绝执行。
原因:Django 默认启用 CSRF 防护机制,所有 POST 表单都会校验 csrfmiddlewaretoken。模板中如果没有添加令牌字段,Django 会判定请求不合法并返回 403。
解决:在每个 POST 表单的 form 标签内加上 {% csrf_token %},Django 渲染时会自动生成一个隐藏字段并写入 cookie,提交时两者不一致会被拦截。如果使用的是 AJAX 请求,则需要在发送前从 cookie 读取 csrftoken 并添加到请求头中。这个坑在开发早期遇到会让人一头雾水,排查几分钟后基本都是同一个原因。
6. 上线前先算清准确率:验证方法与一个关键习惯
6.1 准备留出法测试集,先算准确率再谈效果
模型在训练集上表现很好并不代表真实场景可用。稳妥的做法是把标注数据按 8:2 比例划分成训练集和测试集,训练脚本完成后把测试集数据喂给模型,统计准确率、精确率和召回率。这一步在答辩时非常重要,因为评委很可能会问“你这个情感分类的准确率到底是多少”,而不是只看页面演示效果。
from sklearn.metrics import accuracy_score, precision_score, recall_score y_true = [0, 1, 1, 0, 1, 0] y_pred = [0, 1, 0, 0, 1, 1] print("准确率:", accuracy_score(y_true, y_pred)) print("精确率:", precision_score(y_true, y_pred)) print("召回率:", recall_score(y_true, y_pred))准确率表示预测正确的样本占总样本的比例,精确率表示预测为积极类的样本中真正是积极类的占比,召回率表示真实积极类样本中被成功找出来的占比。文本情感分类中消极样本往往较少,只看准确率容易掩盖问题,把这三个指标一起看才能更客观地判断模型是否真的可用。
6.2 扩展思路:从朴素贝叶斯到更复杂的分类器
如果测试集上的准确率不够理想,优先排查数据集质量而不是急着换模型。词数稀疏、情感词被否定词反转、训练样本覆盖的场景过窄,这些因素都会让准确率上不去。数据规模扩充到几千条以后,再考虑用支持向量机、逻辑回归或者 fastText 替换朴素贝叶斯,效果通常会有明显提升。替换算法的成本其实很低,因为分词和向量化两个环节不用改动,只需要把训练代码中的分类器换掉,重新训练模型文件即可。
做完这些,最后想分享一个习惯:那以后我每次拿到这类带模型的 Django 项目,都强制自己先跑一遍训练脚本和测试评测,再把模型接进视图,绝不跳过评测直接演示。文本情感系统最大的风险点不在页面写得好不好,而在模型本身到底能不能稳定判断,提前算清楚准确率和召回率,既是对用户负责,也能避免答辩现场翻车。希望帮到你。
本文还有配套的精品资源,点击获取