做课程教学评价系统很多人第一反应是“搞个问卷打分不就行了”。但真正跑过教学管理的朋友都清楚光靠学生打分和督导听课记录很难回答“为什么这门课评价低”“教学效果到底受哪些因素影响”“同一个老师连续几个学期评分波动背后藏着什么”这类问题。我这次基于 python-django 搭了一套课程教学评价系统核心思路就是把深度神经网络用在评价数据的挖掘和预测上再用可视化大屏把结论直接怼到管理者和老师面前。这套系统不是简单的“学生评分 排行榜”而是把评价拆成了三个层次第一层是常规的问卷采集与统计第二层是对开放式评语做文本情感分析第三层是用神经网络模型对“教学效果”做综合预测和归因分析。大屏则负责把这三层结果集中呈现所有核心指标都在一块屏上动态刷新方便教学管理者做决策。如果你正在做教学评价、质量监测、教育数据分析相关的项目或者准备在 django 生态里接深度学习的活这篇内容基本覆盖了从需求拆解、模型设计、数据流搭建到大屏落地的完整路径可以直接参考。1. 项目背景与核心需求拆解1.1 传统课程评价的痛点在哪传统教学评价一般依赖三种数据来源学生期末打分、督导随堂听课、同行教师互评。这些方式看似全面实际存在几个硬伤。第一指标维度单一。学生打分往往是“满意、较满意、不满意”式的粗粒度评价缺少对教学过程的具体描述。哪怕总分只有 70 分你也说不清到底是“讲授不清晰”拉低的还是“课堂互动不够”拖的后腿。第二数据利用率极低。每学期积累几千条评语基本躺在数据库里面吃灰人工看不过来也不可能有精力逐条归类。第三评价结果滞后。等学期结束、分数汇总完再反馈给任课老师下一轮课早就排完了谈不上实时改进。当时我在梳理需求阶段和几个负责教务的老师聊了一圈得到一个很明确的信号他们不想要一个“给老师排名”的系统而是想要一个“帮助老师改进教学”的工具。排名只是表象背后的原因诊断和趋势分析才是刚需。这个定位直接影响了我后面的所有技术选型。1.2 深度神经网络在评价场景能做什么把深度神经网络引入课程评价不是赶时髦而是要解决上面提到的两个核心问题文本评语的自动分析和教学效果的多维预测。先说文本分析。学生写的开放式评语是典型的短文本包含大量口语化表达、网络用语、甚至错别字。传统的规则匹配或词典打分碰到这类文本基本歇菜深度学习模型则可以捕捉上下文语义。我在系统里用了一个轻量级的文本分类模型把评语自动归类到“教学质量”“课程内容”“课堂互动”“作业反馈”“学习收获”五个维度同时输出情感极性正向/中性/负向。这样一来几百条评语不再是一堆死文字而是可以直接聚合、对比、筛选的结构化结果。再说多维预测。课程评价数据其实是一个多特征输入的回归/分类问题教师职称、教龄、课程类型、学生年级、班级规模、授课时段、到课率、作业提交率等都在影响最终教学效果评分。传统统计方法只能做相关性分析没法刻画特征之间的非线性组合作用。我采用了一个带有嵌入层和特征交叉能力的深度神经网络以历史和当期数据为输入预测课程综合评分区间并输出各特征对评分的贡献权重。对教学管理者来说这个输出的解释价值远高于一个孤零零的预测分数。1.3 可视化大屏为什么是刚需数据挖掘的结果如果不被人看到价值就少了一大半。管理层讨论教学质量的时候需要的不是一张 Excel 表和一个模型准确率而是一眼就能看懂的全局视图。大屏在这个项目里有三个不可替代的价值一是把碎片数据集中化学生评分、评语情感分布、到课率趋势、教师对比全部放到一个逻辑视图里二是把时间维度的变化直观呈现同一门课、同一个老师连续几个学期的评价趋势通过折线图一目了然三是支持向下钻取管理层从大屏上发现问题之后可以直接点开某个教师的评价详情逐条查看学生的文本反馈。所以在设计之初可视化大屏就不是“锦上添花的装饰”而是系统对外输出价值的核心窗口我甚至把大屏的数据接口设计放在业务接口开发之前。2. 技术架构与方案选型2.1 为什么选 Django 作为后端主框架这个项目的后端选型我几乎没有犹豫就定了 django。原因很实际。首先是生态完整。django 自带 ORM、Admin 后台、认证体系、CSRF 防护、表单处理对于课程评价这种典型的 CRUD 权限管理场景开发效率比用 Flask 或者 FastAPI 高很多。尤其后台管理教务人员需要经常维护教师信息、课程信息、评价指标django admin 稍微改改就能直接投入使用。其次是和深度学习模型的集成没有想象中难。虽然模型训练我用的是 PyTorch但推理服务没有直接耦合在 django 进程里而是通过 django-rest-framework 暴露的 API 做调用。训练好的模型导出成 TorchScript 文件挂在独立推理服务上django 这边只管拼装请求参数、解析返回结果。这样模型更新时不需要重启 Web 服务也不会因为某个批处理任务把 Web 请求堵住职责边界非常清楚。还有一点不能忽略团队里有人会 python但没什么人懂前端工程化。django 的模板系统配合少量 JavaScript加上开箱即用的分页、消息框架让后端出身的人也能把完整功能搭起来不至于被 node 生态的重型构建流程劝退。2.2 深度神经网络模型的设计思路模型这块我走了两套并行方案对应两类业务数据。第一套是文本情感分析模型。输入是学生评语输出是情感极性正向、中性、负向和维度标签教学质量、课程内容、课堂互动、作业反馈、学习收获。模型结构是这样的嵌入层Embedding把分词后的评语转成向量序列经过两层双向 LSTM 捕捉上下文语义再接入一个全连接分类头。考虑到数据量不大第一批标注样本只有约 8000 条我没有上 BERT 这类大模型而是用了一个预训练词向量加上浅层 LSTM在标注数据上训练后 F1 值能做到 0.86 左右已经能满足业务要求。第二套是综合评分预测模型。输入特征包括教师职称编码、教龄归一化、课程类型编码、学生年级编码、班级人数归一化、授课时段编码、前序学期评分归一化、作业提交率归一化等 12 个特征。网络结构是全连接层 64 - ReLU - BatchNorm - 全连接层 32 - ReLU - 输出层 1回归同时加了一个特征重要性打分模块训练完成后导出每个特征对最终评分的贡献权重。这个模块的核心价值不是预测本身而是给管理者提供“到底什么因素在影响评分”的解释依据。2.3 可视化大屏的技术选型对比大屏方案我对比了三条路线纯 ECharts 自研、第三方大屏模板、开源可视化平台。最终选择是 ECharts 自研 自定义布局。对比结果如下方案优点缺点适用场景纯 ECharts 自研灵活可控定制程度高无额外依赖开发量大布局、适配、动效都要自己写需要深定制的大屏第三方大屏模板上手快视觉效果成熟改动受限数据接口要适配模板二次开发成本不低演示和临时用开源可视化平台组件丰富有数据接入能力部署复杂访问速度受平台影响风格难以统一团队有运维能力我做这块的经验是如果项目时间充足建议选 ECharts 自研因为教学评价大屏的核心是“数据叙事逻辑”而不是炫酷动效。一个好的布局顺序应该是先放核心数据卡片综合评分、参评人数、预警课程数再放趋势图、雷达图、词云、热力图最后加上二级钻取的联动面板。每一块图表都要为某一个具体的业务问题服务而不是为了占满屏幕。前端框架我就用了原生 HTML jQuery ECharts没有上 Vue 或 React。原因很简单大屏页面是单页场景交互复杂度有限原生写法反而减少打包、路由等不必要的环节部署也轻量。3. 核心功能模块与数据流设计3.1 系统整体模块划分整个系统按业务边界拆成六个模块基础信息管理、评价任务管理、数据采集与清洗、深度分析引擎、可视化大屏、系统配置与权限中心。模块之间通过数据表和 API 解耦互不直接依赖实现。基础信息管理维护教师、课程、学生、班级等主数据评价任务管理负责创建学期评价任务、配置评价指标、设定参评范围数据采集模块承接学生端问卷、督导端评分、评语文本的录入深度分析引擎对接模型训练与推理服务可视化大屏从分析引擎拉取聚合结果权限中心区分管理员、教务人员、教师、学生四个角色做到“不同角色看到不同数据范围”。这里有一个设计上比较关键的点分析引擎与业务库是物理分离的。业务库跑在 MySQL 上分析引擎的中间结果和模型特征数据单独落在另一个库。为什么要分开因为深度模型的批处理任务耗时长、I/O 密集如果不隔离一个模型训练或者大批量推断任务可能把在线业务拖垮。我用 Celery 定时触发分析任务把结果写入分析库大屏只读取分析库不直接查业务大表性能和稳定性都有保障。3.2 评价数据采集与预处理数据采集阶段最容易被低估其实它决定整个模型效果的上限。我在这部分花的时间占了整个项目开发周期的三分之一。采集端设计了一张主评价表和若干张明细表。主评价表记录评价任务 ID、课程 ID、参评人 ID、总分、提交时间明细表按维度拆分包括教学质量、课程内容、课堂互动、作业反馈、学习收获五个评分维度每个维度是 1~10 的整数分。评语文本单独存大字段同时在采集入口做了关键词过滤和长度校验防止空值和无意义文本干扰模型训练。预处理流程我用管道方式组织一共四步无效数据剔除、文本分词与去停用词、特征编码与归一化、样本划分。特征工程里有一个容易犯的错直接把教师职称、课程类型这类类别特征用 0、1、2、3 编码喂给神经网络。这种做法会引入错误的顺序关系比如“副教授 1、教授 2”会误导模型认为教授比副教授大一个单位的量化数值。正确做法是使用 one-hot 编码django 应用层把编码逻辑封装好避免在 ORM 里混入这类业务规则。3.3 神经网络模型训练与评价预测模型训练是整个系统里最需要耐心的一环。我先把训练脚本单独拆出项目目录用 PyTorch 实现。评语情感分类模型的训练数据来自教务科积累的历史评语我一个人逐批清洗、去重、标注最后花了不少时间才攒到 8000 条左右有效样本。数据量不大所以训练非常快用 GPU 跑一个 epoch 只需要几十秒重点反而在验证和调参上。损失函数和优化器的选择上文本模型用的交叉熵损失加 Adam 优化器学习率初始设置为 2e-3每 3 个 epoch 衰减一次衰减系数 0.8。评分预测模型用的平滑 L1 损失目的是降低异常学期评分的梯度影响。训练完成后两个模型都做了评估文本模型在测试集上准确率 0.88、F1 值 0.86评分预测模型平均绝对误差约 3.7 分评分范围是 0~100在业务上属于可接受水平。预测流程上模型推理不是请求来了才现算而是借助 Celery 定时任务每天凌晨对当天新增的评价数据批量推断结果写回分析库。教学管理者第二天打开大屏看到的就是前一晚更新完毕的完整分析结果全程不需要人工干预。3.4 大屏数据接口设计大屏接口我统一走 REST API格式保持与前端图表需求的字段一一对应减少前端拼接逻辑。比如综合评分卡片接口返回的核心字段是这样的{ semester: 2024-2025-1, avg_score: 86.4, credit_count: 1560, evaluate_count: 1528, coverage_rate: 97.9, alert_course_count: 3 }趋势图、雷达图、词云等接口也都返回类似结构化的 JSON。设计接口的时候我特别强调一个原则接口返回值要和 ECharts 的 option 保持接近但不相等。也就是说接口只返回业务数据不返回配色、字体、坐标轴等前端展示配置这样前端调整样式时不需要重新改接口。如果前端需要做单位换算、千分位格式化统一在前端处理后端只给最原始的业务数值。接口性能方面给大屏单独开了一个只读账号并设置了较短的缓存时间。Redis 缓存大屏聚合结果 2 分钟避免每次刷新都穿透到 MySQL。后面的部署环节我会具体讲这个缓存策略。4. 关键实现与实操记录4.1 Django 项目骨架与数据模型搭建项目创建我用的是标准的 django-admin 命令。考虑到模块划分清晰我没有把所有 app 塞在同一个包里而是建了一个微型的多 app 工程每个 app 对应一个业务域。settings 里单独配置了 MySQL、Redis、Celery 的连接参数生产环境和本地开发环境用独立的配置文件区分靠环境变量切换。数据模型核心表的结构我做了几张主要的下面是课程评价主表的 Model 定义示例写作时做了适当简化但字段和约束关系是实际在用的from django.db import models class EvaluationTask(models.Model): name models.CharField(评价任务名称, max_length200) semester models.CharField(学期, max_length32) start_date models.DateField(开始日期) end_date models.DateField(结束日期) status models.CharField(状态, max_length16, defaultpending) created_at models.DateTimeField(创建时间, auto_now_addTrue) class Meta: db_table evaluation_task indexes [models.Index(fields[semester, status])]class EvaluationRecord(models.Model): task models.ForeignKey(EvaluationTask, on_deletemodels.CASCADE, verbose_name评价任务) course models.ForeignKey(course.CourseInfo, on_deletemodels.CASCADE, verbose_name课程) teacher models.ForeignKey(teacher.TeacherInfo, on_deletemodels.CASCADE, verbose_name教师) student models.ForeignKey(student.StudentInfo, on_deletemodels.CASCADE, verbose_name学生) total_score models.DecimalField(总分, max_digits5, decimal_places2) comment_text models.TextField(评语, blankTrue) submitted_at models.DateTimeField(提交时间, auto_now_addTrue) class Meta: db_table evaluation_record constraints [ models.UniqueConstraint( fields[task, course, teacher, student], nameuniq_eval_task_course_teacher_student, ) ]这条唯一约束很重要它能从根上防止同一个学生对同一门课重复提交。实际的评价问卷在前端也做了防重但后端这个约束才是最后一道防线没有它脏数据会直接污染模型训练样本。4.2 模型训练的完整流程与参数细节模型训练的工程目录我单独放在ml_service/下和 django 项目根目录平级避免相互干扰。目录结构大概是ml_service/ ├── train_text_clf.py ├── train_score_reg.py ├── export_model.py ├── preprocess/ │ ├── text_clean.py │ └── feature_build.py ├── models/ │ └── __init__.py └── saved_model/ ├── text_clf.pt └── score_reg.pt训练流程分三步走。第一步从 MySQL 导出数据经过清洗、标注转换后保存为 JSONL 格式第二步执行训练脚本模型指标和损失曲线写入 TensorBoard 日志第三步用export_model.py导出 TorchScript 模型推到推理服务器。这里有个参数选择的经验训练文本分类模型时序列最大长度我设为 64。短评语超过这个长度的很少设太大反而会增加显存占用、拖慢训练速度。词向量维度用的 100 维没有自己训练直接用一个通用的开源中文词向量做初始化效果在相似任务上验证过性价比很高。综合评分模型的归一化方法也值得提一句所有连续特征用 Z-score 归一化类别特征做 one-hot 后不再额外处理。归一化的均值、方差要在训练集上算好并保存下来推理的时候必须用同一组参数绝对不能拿全量数据重新算一遍否则会出现训练和推理分布不一致的问题。4.3 大屏图表渲染与动态刷新实现大屏页面我采用了 16:9 的固定设计分辨率和 1920x1080 基准页面内容用百分比定位缩放适配通过 resize 事件统一处理。这样的好处是不管屏幕是 1366 还是 3840画面都能等比缩放不会出现布局错位。布局上分了三个区域顶部是标题栏和全局统计数字中间主区域左侧放课程评价综合排名中间放趋势图和雷达图右侧放评语情感分布和词云底部是一条横向滚动栏放置各院系的对比柱状图和预警课程列表。ECharts 图表的初始化方式我这里给一个核心示例以综合评分趋势图为例const trendChart echarts.init(document.getElementById(trendChart)); function renderTrendChart(data) { trendOption { tooltip: { trigger: axis }, grid: { left: 60, right: 30, top: 40, bottom: 50 }, xAxis: { type: category, data: data.semesterList, }, yAxis: { type: value, name: 平均分, min: 60, max: 100, }, series: [{ data: data.avgScoreList, type: line, smooth: true, areaStyle: { opacity: 0.15 }, }], }; trendChart.setOption(trendOption); }数据刷新我用的是setInterval定时轮询间隔 30 秒。为什么不直接上 WebSocket因为大屏场景对“实时性”没那么敏感30 秒的延迟完全够用反而轮询实现更简单、更稳定也不容易出现前端连接断开后需要重连的维护问题。如果后续要做手机端实时预警推送再上 WebSocket 也不迟。每次轮询后的更新逻辑是拿到接口返回的 JSON对比上次数据是否有变化如果有变化才调用setOption更新图表。这样避免了无谓的 DOM 操作长时间挂屏时浏览器内存占用一直很稳定。4.4 部署架构与性能优化部署我用了常见的 单机 nginx uwsgi mysql redis 结构。深度学习推理服务单独跑在一个 Python 进程上开放三个内部接口评语情感分析、综合评分预测、特征归因分析。django 应用通过 requests 调用推理服务接口而且加了简单的熔断逻辑推理服务连续超时 3 次后续请求直接走兜底规则比如情感分析默认返回“中性”保证教学管理者在极端情况下仍然能打开大屏看到基础统计数据。性能调优主要做了三件事。第一件事是数据库索引优化给evaluation_record的 task、course、teacher、submitted_at 字段建了联合索引大屏按学期聚合查询时从原来的秒级降到了 200 毫秒左右。第二件事是 Redis 缓存大屏接口全部走缓存key 按业务维度拼接比如课程排名缓存 5 分钟、趋势缓存 2 分钟、词云缓存 10 分钟。第三件事是大屏静态资源压缩ECharts 按需引入模块js 体积从我最初全量引入的 1.2MB 降到了 360KB弱网环境下加载速度提升明显。部署上线前还要特别留意服务器时区和 Django 的TIME_ZONE配置一致性。我踩过这个坑服务器时区是 UTCDjango 里设置的却是本地时区结果大屏上展示的“今日参评人数”每天延迟 8 小时才更新排查了半天才反应过来是时区配置不一致导致定时任务执行的起点偏移了。5. 常见问题与排查技巧实录5.1 评价数据样本不平衡怎么办课程评价数据的分布天然是不平衡的。绝大多数评语是正向的负向评语和中性评语占比很低。如果直接用原始数据训练文本分类模型模型会偷懒到把一切都预测成“正向”因为这样准确率也能轻松到 85% 以上看起来很好看实际没有任何业务价值。我解决这个问题用了三层手段。第一层是数据层面对负向和中性样本做了简单的随机过采样把比例从 1:20:5 调整到 1:4:4 左右。第二层是训练策略层面在交叉熵损失里给每个类别加权负向样本的权重设为 3.0中性样本设为 1.5让模型在损失计算时更关注少数类。第三层是评估指标层面不看准确率主要看宏平均 F1 值避免被多数类的高准确率迷惑。三层做完负向评语识别的召回率从最初的不到 30% 提升到 74% 左右。从业务结果看这个提升直接影响了“预警课程”的检出能力漏报明显减少。5.2 大屏加载慢和渲染卡顿的处理大屏打开要白屏三五秒这在汇报现场是灾难级的体验。问题通常出在三个位置接口慢、资源大、渲染卡。接口慢的问题我用看请求耗时的方式直接定位。发现最慢的接口是“课程综合排名”接口SQL 里 join 了教师表、课程表、加上子查询算平均分还做了分页数据量大时执行时间飙到 1.4 秒。优化方案是把排名预计算写入一张独立的course_rank_summary表由 Celery 每小时刷新接口只查这张表耗时降到 80 毫秒。渲染卡的问题主要来自词云和雷达图的频繁 setOption。我的处理办法是给图表设置lazyUpdate并且只在数据变化时才更新图表实例。词云数据量控制在 100 个词以内多余的高频词合并展示渲染帧率稳定了很多。5.3 前后端联调时接口字段不一致的坑大屏联调阶段几乎每天都会遇到字段对不上号的问题。后端返回的是semesterList前端代码里写的却是semesters后端字段是avg_score图表配置里取的是score换一个人写就换一套命名。后来我定了一个硬性约定所有大屏接口的字段名统一用 snake_case前端 JavaScript 里直接使用这些小写字母和下划线的对象属性不做任何驼峰转换。这个约定在项目里定死了后端写接口时按这个规范前端取数时也按这个规范再没出过字段不一致的问题。另外一个容易踩的坑是数值类型MySQL 的 DECIMAL 字段经过 JSON 序列化后到了前端可能变成字符串。我在 django 的 DRF Serializer 里统一配置了coerce_to_stringFalse保证avg_score传给前端的是86.4而不是86.4否则图表轴序或者 tooltip 展示很容易出诡异的表现。5.4 推理服务偶尔不可用的兜底策略深度学习推理服务不是 100% 可用。一个很现实的问题是如果模型推理进程因为内存溢出崩了django 还在跑大屏数据接口就应该有降级方案。我的实现是django 调用推理服务时用try...except包住捕获超时异常和连接异常然后读分析库里上一次成功运行的结果缓存。如果分析库中也完全没有数据则返回一个“暂无分析数据”的占位结构前端对应位置显示为灰色空状态。这样大屏永远不会有白屏情况最多是没有最新分析结论但基础统计和评分分布依然可见。这个兜底逻辑同事们评价很高因为在汇报演示的时候稳定性太重要了宁可展示旧数据也不能让屏幕卡在 loading 出不来内容。6. 项目复盘与扩展建议6.1 从实际使用中学到的几件小事系统上线之后我用了差不多半个学期去做回访和调优有几个体会非常深刻。第一件事模型输出别追求“准确”要追求“可用”。我做评分预测模型时一开始总想把平均绝对误差降到 3 分以内后来发现教务管理者真正关心的是趋势预警误差在 5 分以内已经够用了。他们希望看到的是“这门课连续两学期下滑超过 8 分”这样的判断而不是精确到 84.3 分还是 84.7 分。第二件事好的可视化是“让人一眼看到重点”。第一版大屏我塞了 13 个图表信息密度非常高结果使用者反馈“看不过来不知道先看哪里”。后来精简成 8 个模块并用颜色阈值区分综合评分等级——90 分以上绿色、75~90 蓝色、75 以下橙色预警——才真正起到辅助决策的作用。第三件事定时任务要留足缓冲时间。Celery 定时任务最初设定在凌晨 00:10 执行结果遇到学期末参评扎堆数据量暴涨到 40 万条批处理任务跑了 2 个小时还没跑完影响白天大屏数据更新。后来把时间改到凌晨 02:00并给任务加上超时告警问题就缓解了。任何带动批量计算的定时任务都必须评估最坏情况下的执行时间。6.2 后续还可以怎么扩展这套系统目前已经跑通了“采集—分析—展示”的完整链路但离真正的教学闭环还差两步我觉得后续值得补上。第一步是增加教师端个人分析报告。大屏解决的是管理层需求教师本人需要的是更私密、更具体的改进建议比如“您这门课在课堂互动维度的评分低于同类课程 8%学生的负向评语主要集中于互动形式单一建议增加分组讨论环节”。这类建议需要引入知识库匹配把模型输出的维度标签和预警信息转化为自然语言建议。第二步是引入归因分析的细粒度化。现在特征重要性只能到“课程类型”“班级规模”这个层级后续可以继续做分学院、分课程类型的子模型让每个二级学院有一个独立小模型来适配自身特点而不是共用同一个全局模型。这样做会牺牲一部分样本量但解释性和贴合度会明显提升。我把这段经历写出来的意义更多是给正在做类似项目的朋友一个完整参考。教学评价加深度神经网络重点不在算法本身多复杂而在于把技术工具落到真实的教学管理场景里产出能被理解和信任的结果。我的建议是先搞清楚业务方真正想要什么再决定用什么算法、上什么大屏顺序不能反这样项目才能真正从“能演示”走向“能使用”。