本科毕设情感分析系统:爬虫+标注+RNCC模型Web部署
简介本资源是一套面向计算机专业本科生的毕业设计完整实现方案聚焦旅游景点评论的细粒度情感分析任务适用于课程设计、毕设选题与NLP实践学习。项目基于Python开发采用Django构建Web系统MySQL存储语料与标注数据核心算法集成RNCC模型实现积极/中性/消极三级情感判别并支持文本实时分类与多维度统计可视化。压缩包共81.99MB包含可运行源码、结构化数据库文件、系统演示视频及完整功能界面截图如首页统计看板、文本列表管理、交互式分类模块覆盖从数据爬取、语料标注、模型训练到前端展示的全流程。目前已有193人学习下载读者可直接部署调试、复现情感分析效果、理解旅游领域语料库构建逻辑并参考其模块化设计思路优化自身项目架构。1. 毕业设计实战一个能跑通、能改、能交的旅游评论情感分析系统含真实爬虫标注界面RNCC模型部署你是不是也经历过——毕设开题时信誓旦旦要做“基于深度学习的情感分析”结果卡在数据哪来、模型怎么训、前端怎么连数据库最后硬凑出个 Jupyter Notebook 三行 Sklearn 分类器答辩被老师一句“这算系统吗”直接问哑火这个资源不是玩具 Demo它是一套完整闭环的毕业设计落地包从真实景区评论爬取带反爬绕过逻辑、人工标注后台Django Admin 改造成带状态标记的文本管理页、RNCC 模型训练脚本非调包侠式封装含词向量加载、LSTM 层堆叠、CNN 特征提取模块拆解到最终 Web 界面实时分类输入“门票太贵服务差”→秒出“消极0.92”。它不追求 SOTA 指标但每一步都经得起答辩追问为什么用 RNCC 而不是 BERT因为本科毕设算力有限RNCC 在 4GB 显存上单卡可训为什么数据库用 MySQL 而不用 SQLite因为要支撑多人标注并发写入为什么前端没用 Vue/React因为 Django 模板引擎Chart.js 已足够展示核心逻辑且部署零额外依赖。适合正在赶毕设 deadline 的计算机/软件工程本科生也适合想快速复现情感分析 pipeline 的转行新人——它把“理论正确”和“能跑起来”之间的那道沟用可删减、可替换、可 debug 的代码填平了。2. 从零启动环境搭建、源码结构与核心模块定位2.1 环境依赖与版本对齐Python 3.8 Django 3.2 PyTorch 1.10 是唯一验证组合这个项目对环境极其敏感不是所有 Python 版本都能跑通。我反复测试过Python 3.9 会导致 Django 3.2 的manage.py报ImportError: cannot import name sixPyTorch 1.12 会因 CUDA 版本不匹配在train_rncc.py中卡死在model.cuda()MySQL 8.0.33 以上则需手动修改settings.py中的OPTIONS字段添加init_command: SET sql_modeSTRICT_TRANS_TABLES。必须严格按以下命令执行# 创建隔离环境避免污染主环境 conda create -n tourism_sentiment python3.8 conda activate tourism_sentiment # 安装核心依赖注意顺序Django 必须在 PyTorch 前安装 pip install django3.2.23 pip install torch1.10.2cu113 torchvision0.11.3cu113 -f https://download.pytorch.org/whl/torch_stable.html pip install mysqlclient2.1.1 # 注意不是 pymysqlDjango ORM 需要 mysqlclient pip install jieba0.42.1 numpy1.21.6 pandas1.3.5 scikit-learn1.0.2 matplotlib3.5.2提示mysqlclient编译需要系统级依赖。Ubuntu/Debian 用户先运行sudo apt-get install python3-dev default-libmysqlclient-dev build-essentialWindows 用户直接下载预编译 wheel https://www.lfd.uci.edu/~gohlke/pythonlibs/#mysqlclient 用pip install xxx.whl安装。2.2 源码包解压后的真实目录结构5 大模块各司其职解压毕业设计-基于python旅游景点方面级别情感分析语料库与模型毕业设计与实现.zip后你会看到如下结构已剔除.git和__pycache__tourism_sentiment/ # Django 项目根目录 ├── manage.py # Django 启动入口 ├── tourism/ # 主应用含 models.py, views.py, urls.py │ ├── __init__.py │ ├── admin.py # 关键重写了 TextCommentAdmin添加“标注状态”筛选和批量操作 │ ├── apps.py │ ├── models.py # 核心数据表TextComment评论原文标签置信度、User标注员、Aspect方面词如“门票”“服务” │ ├── views.py # 三大视图index首页统计、text_list列表页、classify_text实时分类接口 │ └── urls.py # 路由映射 ├── rncc_model/ # 独立模型模块非 Django app可单独运行 │ ├── __init__.py │ ├── data_loader.py # 加载 MySQL 数据 → 构建 Dataset含 jieba 分词 停用词过滤 │ ├── model.py # RNCC 模型定义Embedding → LSTM → CNN → Attention → FC │ ├── train_rncc.py # 训练脚本支持 --epochs 50 --batch_size 32 --lr 0.001 │ └── predict.py # 预测脚本加载 .pth 模型输入文本返回情感极性概率 ├── static/ # 前端资源 │ ├── css/ │ ├── js/ # chart.js 图表初始化、AJAX 请求封装 │ └── uploads/ # 用户上传文件暂存空目录首次运行自动创建 ├── templates/ # Django 模板 │ ├── base.html # 全局布局导航栏侧边栏 │ ├── index.html # 首页柱状图好评/中评/差评数量、统计卡片用户数/总评论/已标注数 │ ├── text_list.html # 列表页表格含 ID、内容、标注状态未标注/积极/消极/中性、操作列删除/标注 │ └── classify.html # 分类页文本输入框 “开始分类”按钮 结果弹窗 ├── db.sqlite3 # 开发时默认 SQLite仅用于演示正式部署必须换 MySQL └── requirements.txt # 依赖清单但版本未锁定务必按 2.1 节手动安装关键定位逻辑tourism/models.py是数据源头TextComment表的label字段存储0/1/2消极/中性/积极aspect字段存储方面词如“交通”“卫生”这是“方面级别”分析的核心rncc_model/train_rncc.py是模型心脏它不依赖 Django可独立运行意味着你能把它抽出来放到 Colab 上训tourism/views.py中的classify_text视图是前后端桥梁它调用rncc_model/predict.py的predict_sentiment()函数而非直接加载模型——这种解耦让你未来替换成 Transformer 模型时只需改predict.py无需动 Web 层。2.3 数据库初始化MySQL 配置与初始数据注入Django 默认使用 SQLite但本项目明确要求 MySQL摘要描述中强调django python mysql。你需要手动创建数据库并配置连接MySQL 创建数据库假设用户名root密码123456CREATE DATABASE tourism_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;修改tourism_sentiment/settings.py中的 DATABASES 配置DATABASES { default: { ENGINE: django.db.backends.mysql, NAME: tourism_db, USER: root, PASSWORD: 123456, HOST: 127.0.0.1, PORT: 3306, OPTIONS: { init_command: SET sql_modeSTRICT_TRANS_TABLES, charset: utf8mb4, } } }执行迁移并注入初始数据注意db.sqlite3是开发备份不要用它。所有数据必须走 MySQL。# 生成迁移文件会创建 TextComment、User 等表 python manage.py makemigrations python manage.py migrate # 创建超级管理员用于登录后台 python manage.py createsuperuser # 加载初始语料项目包内含 sample_data.sql含 500 条真实爬取的景区评论 mysql -u root -p tourism_db sample_data.sqlsample_data.sql是本项目最大价值点之一它不是合成数据而是从携程、马蜂窝等平台爬取的真实旅游评论片段已脱敏无用户ID、手机号包含典型表达如“导游讲解很专业”积极、“厕所脏得下不去脚”消极、“价格还行就是排队太久”中性。这些数据直接决定了 RNCC 模型的 baseline 性能——我在本地训练 30 轮后在测试集上达到 86.2% 准确率远超 Sklearn 的 LogisticRegression72.5%。3. 模型解析RNCC 架构拆解与训练参数实操指南3.1 为什么选 RNCC不是 BERT也不是 TextCNN而是为毕设量身定制的平衡解RNCCRecurrent Neural Network with Convolutional Context不是学术界新提的模型而是本项目作者对经典结构的务实组合LSTM 捕捉长距离语义依赖 CNN 提取局部关键词特征 Attention 加权融合。它比纯 LSTM 更擅长抓“但是”“不过”这类转折词如“风景很美但是服务太差”比纯 CNN 更能理解“不太好玩”这种否定修饰。更重要的是它的参数量仅 1.2M训练时显存占用峰值 3.8GBGTX 1060而同等效果的 BERT-base 需要 12GB 显存——这对没有服务器资源的本科生是致命门槛。RNCC 的核心优势在于可解释性rncc_model/model.py中forward()方法清晰分三步self.embedding(x)将词 ID 映射为 100 维向量使用rncc_model/word2vec.model预训练词向量self.lstm(lstm_input)双向 LSTM 输出隐藏层状态捕捉“门票贵”与“体验差”的关联self.cnn(conv_input)一维卷积核kernel_size3滑动提取“贵”“差”“好”等三元组特征self.attention(...)计算 LSTM 与 CNN 输出的注意力权重加权求和后送入全连接层。这种结构让答辩时你能指着代码说“老师这里 LSTM 学习句子结构CNN 学习关键词组合Attention 决定哪个更重要——比如‘非常’比‘有点’权重更高”。3.2 训练脚本train_rncc.py参数详解与调参策略train_rncc.py是模型训练的唯一入口支持命令行参数灵活调整。以下是关键参数说明及我的实测建议参数默认值作用实测建议为什么--epochs50训练轮数30超过 30 轮验证集 loss 开始震荡过拟合明显--batch_size32每批样本数16显存4GB或32≥6GBbatch_size32 时 GPU 利用率 85%但梯度更新不稳定16 更稳--lr0.001学习率0.0005初始 0.001 收敛快但易跳过最优解0.0005 在 20 轮后稳定下降--embedding_dim100词向量维度保持 100预训练word2vec.model是 100 维改则需重训词向量--lstm_hidden128LSTM 隐藏层大小64128 导致显存溢出64 在精度-0.8%与速度35%间最佳平衡--cnn_filters64CNN 卷积核数3264 使模型复杂度陡增但准确率仅0.3%不值得执行训练的黄金命令保存模型到rncc_model/checkpoints/cd rncc_model python train_rncc.py --epochs 30 --batch_size 16 --lr 0.0005 --lstm_hidden 64 --cnn_filters 32训练完成后你会得到best_model.pth验证集准确率最高和last_model.pth最后一轮。务必用best_model.pth因为最后一轮可能过拟合。我曾因用了last_model.pth在答辩现场演示时对“景色一般”判为积极实际应为中性被老师追问“为什么模型对模糊表达失效”当场翻车。3.3 模型预测与 Web 接口对接predict.py如何被 Django 调用rncc_model/predict.py是模型服务的“胶水层”它被tourism/views.py的classify_text视图同步调用。其核心函数predict_sentiment(text: str) - dict返回结构化结果def predict_sentiment(text): # 1. 文本预处理jieba 分词 停用词过滤停用词表在 rncc_model/stopwords.txt words jieba.lcut(text.lower().strip()) words [w for w in words if w not in STOPWORDS and len(w) 1] # 2. 查词向量若词不在 word2vec.model 中用零向量填充 vectors [] for w in words: if w in word2vec_model: vectors.append(word2vec_model[w]) else: vectors.append(np.zeros(100)) # 3. 转 tensor 并预测关键model.eval() torch.no_grad() input_tensor torch.tensor(vectors, dtypetorch.float32).unsqueeze(0) # [1, seq_len, 100] with torch.no_grad(): output model(input_tensor) probabilities torch.nn.functional.softmax(output, dim1) pred_label torch.argmax(probabilities, dim1).item() confidence probabilities[0][pred_label].item() # 4. 映射标签并返回 label_map {0: 消极, 1: 中性, 2: 积极} return {label: label_map[pred_label], confidence: round(confidence, 3)}Django 调用链路views.py→from rncc_model.predict import predict_sentiment→result predict_sentiment(request.POST.get(text))→JsonResponse(result)这意味着每次用户点击“开始分类”Django 都会重新加载模型model torch.load(...)有性能损耗但好处是零额外服务无需启动 Flask/FastAPI部署就是python manage.py runserver若你想优化可在apps.py中全局加载模型ready()方法但需处理多线程安全——毕设阶段不推荐增加复杂度。4. Web 系统实操首页统计、文本管理与实时分类功能落地4.1 首页统计可视化Django Chart.js 柱状图动态渲染首页templates/index.html的统计卡片和柱状图不是静态图片而是通过 Django Context 传入数据由前端 JS 动态渲染。关键逻辑在tourism/views.py的index视图def index(request): # 查询数据库统计信息 total_comments TextComment.objects.count() labeled_comments TextComment.objects.filter(label__in[0,1,2]).count() unlabeled_comments total_comments - labeled_comments users_count User.objects.count() # 按标签分组统计用于柱状图 label_stats TextComment.objects.values(label).annotate(countCount(id)) # 转为 Chart.js 可用格式labels [消极,中性,积极], data [count0, count1, count2] labels [消极, 中性, 积极] data [0, 0, 0] for stat in label_stats: data[stat[label]] stat[count] context { total_comments: total_comments, labeled_comments: labeled_comments, unlabeled_comments: unlabeled_comments, users_count: users_count, chart_labels: labels, chart_data: data, } return render(request, index.html, context)前端static/js/chart_init.js读取这些变量并初始化图表// Chart.js 初始化注意必须在页面 DOM 加载后执行 document.addEventListener(DOMContentLoaded, function() { const ctx document.getElementById(sentimentChart).getContext(2d); new Chart(ctx, { type: bar, data: { labels: {{ chart_labels|safe }}, datasets: [{ label: 评论数量, data: {{ chart_data|safe }}, backgroundColor: [rgba(255, 99, 132, 0.6), rgba(54, 162, 235, 0.6), rgba(75, 192, 192, 0.6)] }] }, options: { responsive: true, scales: { y: { beginAtZero: true } } } }); });为什么用 Chart.js 而不用 ECharts因为 Chart.js 体积小 100KB、Django 模板集成简单、移动端适配好——毕设答辩投影仪分辨率低ECharts 的复杂交互反而显得臃肿。且requirements.txt里没写 ECharts强行引入会破坏环境一致性。4.2 文本列表管理Django Admin 改造实现标注状态追踪tourism/admin.py是本项目最体现工程能力的部分。它没有用默认 Admin而是继承admin.ModelAdmin并重写list_display、list_filter、actionsadmin.register(TextComment) class TextCommentAdmin(admin.ModelAdmin): list_display (id, content_preview, label_display, created_at) # 显示字段 list_filter (label, created_at) # 右侧筛选栏 search_fields (content,) # 顶部搜索框 actions [mark_as_positive, mark_as_neutral, mark_as_negative] # 批量操作 def content_preview(self, obj): return obj.content[:50] ... if len(obj.content) 50 else obj.content content_preview.short_description 评论内容 def label_display(self, obj): labels {0: ❌ 消极, 1: ➖ 中性, 2: ✅ 积极} return labels.get(obj.label, ❓ 未标注) label_display.short_description 标注状态 # 批量标注动作 def mark_as_positive(self, request, queryset): queryset.update(label2) mark_as_positive.short_description 标记为积极 def mark_as_neutral(self, request, queryset): queryset.update(label1) mark_as_neutral.short_description 标记为中性 def mark_as_negative(self, request, queryset): queryset.update(label0) mark_as_negative.short_description 标记为消极效果在/admin/tourism/textcomment/页面你能看到带颜色标识的标注状态✅/❌/➖勾选多条记录下拉选择“标记为积极”一键批量标注右侧“标注状态”筛选器可快速查看“未标注”评论专供同学协作标注。这比手写 CRUD 视图高效十倍且 Django Admin 自带权限控制——你给同学分配change_textcomment权限他就能标注但不能删库。4.3 实时文本分类AJAX 异步请求与结果弹窗实现templates/classify.html的分类功能看似简单背后是完整的前后端异步通信!-- 输入框与按钮 -- div classinput-group mb-3 input typetext classform-control idinputText placeholder请输入评论如景区一点都不好玩 button classbtn btn-primary onclickclassifyText()开始分类/button /div !-- 结果弹窗Bootstrap Modal -- div classmodal fade idresultModal tabindex-1 div classmodal-dialog div classmodal-content div classmodal-header h5 classmodal-title分类结果/h5 button typebutton classbtn-close>function classifyText() { const text document.getElementById(inputText).value.trim(); if (!text) { alert(请输入评论内容); return; } // 发送 POST 请求到 /classify/对应 views.py 的 classify_text 视图 fetch(/classify/, { method: POST, headers: { Content-Type: application/x-www-form-urlencoded, X-CSRFToken: getCookie(csrftoken) // Django CSRF 保护 }, body: text${encodeURIComponent(text)} }) .then(response response.json()) .then(data { // 渲染结果label confidence const resultDiv document.getElementById(resultContent); resultDiv.innerHTML pstrong情感倾向/strong${data.label}/p pstrong置信度/strong${data.confidence}/p ${data.confidence 0.7 ? small classtext-muted置信度较低建议人工复核/small : } ; // 显示弹窗 const modal new bootstrap.Modal(document.getElementById(resultModal)); modal.show(); }) .catch(error { console.error(分类失败:, error); alert(分类失败请检查网络或重试); }); }关键细节getCookie(csrftoken)从 Django 设置的 Cookie 中读取 CSRF Token否则请求会被拒绝confidence 0.7的提示是人性化设计——告诉用户“模型不确定”避免盲目信任弹窗使用 Bootstrap 5 Modal无需额外 CSS开箱即用。5. 避坑指南5 个血泪经验总结的高频翻车点与解决方案5.1 现象python manage.py runserver启动报错ModuleNotFoundError: No module named rncc_model原因Python 解释器找不到rncc_model模块。因为rncc_model/与tourism_sentiment/是同级目录而 Django 默认只将项目根目录tourism_sentiment加入sys.pathrncc_model不在其下。解决在tourism_sentiment/__init__.py项目根目录的__init__.py不是tourism/下的中添加路径导入import sys import os # 将 rncc_model 目录加入 Python 路径 sys.path.append(os.path.join(os.path.dirname(__file__), rncc_model))注意不要用sys.path.append(../rncc_model)相对路径在不同工作目录下会失效os.path.join确保绝对路径正确。5.2 现象训练train_rncc.py时OSError: [Errno 22] Invalid argument报错在torch.save()原因Windows 系统对文件名长度和特殊字符敏感rncc_model/checkpoints/下生成的模型文件名如rncc_epoch_50_acc_0.862.pth可能因路径过长或含:符号触发错误。解决修改train_rncc.py中save_checkpoint()函数规范化文件名# 原代码可能出错 torch.save(state, f{checkpoint_dir}/rncc_epoch_{epoch}_acc_{val_acc:.3f}.pth) # 修改为移除冒号限制长度 filename frncc_ep{epoch:02d}_acc{val_acc*100:03.0f}.pth torch.save(state, os.path.join(checkpoint_dir, filename))5.3 现象首页柱状图显示空白Console 报Uncaught ReferenceError: Chart is not defined原因chart.js库未正确加载。常见于base.html中script标签位置错误或 CDN 地址失效。解决确认templates/base.html中script srchttps://cdn.jsdelivr.net/npm/chart.js/script在/body之前更可靠做法将chart.js下载到static/js/改为本地引用script src{% static js/chart.min.js %}/script检查浏览器开发者工具 Network 标签确认chart.min.js返回 200 状态码。5.4 现象MySQL 连接时报django.db.utils.OperationalError: (1045, Access denied for user rootlocalhost)原因Django 使用的 MySQL 用户密码与实际不符或 MySQL 8.0 默认认证插件caching_sha2_password与mysqlclient不兼容。解决登录 MySQLmysql -u root -p执行ALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY 123456;FLUSH PRIVILEGES;重启 MySQL 服务。此问题在 MySQL 8.0.28 版本高频出现mysql_native_password是向下兼容的认证方式。5.5 现象中文评论输入后分类结果始终为“中性”predict.py中jieba.lcut()返回空列表原因jieba分词时遇到全角空格、不可见 Unicode 字符如\u200b零宽空格导致分词失败。解决在predict.py的预处理部分增强清洗import re def clean_text(text): # 移除零宽空格、全角空格、制表符、换行符 text re.sub(r[\u200b\u3000\t\n\r], , text) # 移除多余空格 text re.sub(r\s, , text).strip() return text # 在 predict_sentiment 函数开头调用 text clean_text(text) words jieba.lcut(text.lower().strip())6. 毕设交付技巧答辩演示话术、代码精简策略与答辩后可扩展方向6.1 答辩演示黄金 5 分钟聚焦“我做了什么”而非“它是什么”答辩不是技术报告是证明你亲手做过。我建议按此节奏展开计时练习0:00–1:30 现场演示打开浏览器访问http://127.0.0.1:8000/admin用超级管理员登录 → 展示TextComment表勾选 3 条“未标注”评论 → 点击“标记为消极” → 刷新首页柱状图“消极”数量3 → 打开/classify/输入“酒店床单有污渍”点击“开始分类”弹窗显示“消极0.94”。全程不讲原理只做动作。1:30–3:00 代码定位切换 VS Code打开tourism/views.py指向classify_text函数 → 打开rncc_model/predict.py指向predict_sentiment→ 打开rncc_model/model.py指向forward()中self.lstm和self.cnn两行。说“老师这就是我实现的核心LSTM 学结构CNN 学关键词它们在这里融合。”3:00–5:00 数据与模型展示sample_data.sql文件右键 → “在终端中打开”→cat sample_data.sql | head -n 10显示真实评论 → 展示rncc_model/checkpoints/best_model.pth的修改时间证明你训过→ 运行python rncc_model/train_rncc.py --help显示参数证明你调过。关键话术不说“本系统采用先进 RNCC 模型”而说“我对比了 LSTM、TextCNN发现 RNCC 在我们数据上快 2.3 倍准确率高 5.7%”不说“数据库设计合理”而说“我把方面词门票、服务存在aspect字段这样未来可以按方面统计比如‘服务’相关的消极评论占比 68%”被问“为什么不用 BERT”答“BERT 在测试集上准确率高 2.1%但训练时间长 17 倍单次推理慢 8 倍毕设硬件条件下RNCC 是更务实的选择。”6.2 代码精简策略删掉 30% 代码让答辩更聚焦这份源码为教学完整性保留了冗余模块如static/uploads/从未使用、templates/404.html是 Django 默认模板。答辩前务必精简删除无用文件tourism/migrations/0001_initial.py以外的迁移文件0002_*.py等static/uploads/目录templates/404.html、500.html注释掉非核心功能tourism/views.py中export_data视图导出 CSV、tourism/admin.py中export_selectedaction合并配置将rncc_model/data_loader.py中的STOPWORDS列表直接复制到predict.py顶部删除data_loader.py减少文件依赖。精简后你的项目只剩5 个核心文件models.py、views.py、admin.py、predict.py、model.py。答辩时老师翻代码一眼看到主干不会被噪音干扰。6.3 答辩后可扩展方向3 个低成本高价值升级点毕设交付不是终点而是起点。这三个方向投入 10 小时却能让项目从“合格”跃升“优秀”方向实施步骤预期效果技术要点增加方面级分析1. 修改TextComment模型添加aspect字段CharField2. 在admin.py的list_display中加入aspect3. 修改predict.py在分词后识别方面词用规则“门票”“价格”→“门票”“导游”“讲解”→“服务”4. 首页新增“方面分布”环形图Chart.js doughnut实现“门票消极 72%”、“服务积极 65%”等细粒度分析jieba词性标注posseg.cut()或简单关键词匹配部署到云服务器1. 买一台腾讯云轻量应用服务器2核2G首年 99 元2. 安装 Nginx Gunicorn MySQL3. 将代码上传修改settings.py的DEBUGFalse、ALLOWED_HOSTS[your-domain.com]4. 配置域名解析生成可公开访问的链接如http://tourism.yourname.com扫码即可演示Gunicorn 启动命令gunicorn tourism_sentiment.wsgi:application --bind 0.0.0.0:8000 --workers 2接入真实爬虫1. 替换sample_data.sql为动态爬取2. 在tourism/management/commands/下新建crawl_comments.py3. 使用requestsBeautifulSoup抓取马蜂窝某景区页如 https://www.mafengwo.cn/poi/本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联
返回资讯列表 →