前阵子接手了一个课堂场景下的学生行为检测系统项目,用深度学习里的YOLOv11做目标检测算法,Python写后端,Django搭整套Web平台,最终实现了图像检测、视频文件检测、摄像头实时检测三种工作模式。配套的技术文档也整理得很完整,从数据标注规范、模型训练参数到接口协议、部署手册全都有。这套系统解决的问题非常具体:在教室这种人员密集、相互遮挡普遍、光线变化明显的环境下,自动识别学生当前的行为状态——听讲、玩手机、趴桌睡觉、举手、站立、交头接耳、随意走动——并且在Web页面上实时展示检测结果。
如果你正在做类似的毕业设计、智慧课堂项目或者落地到高校/培训机构的真实场景,这篇内容值得耐心看完。我不会只给你贴一堆代码,而是把我从需求分析到模型训练、从Django后端设计到三种检测模式实现的整个思路、踩过的坑、最后验证有效的方案都讲清楚。
1. 项目架构与设计思路
1.1 从需求到架构:先搞清楚系统到底要解决什么问题
学生行为检测这个概念听起来不复杂,很多人的第一反应是“这不就是套一个目标检测模型吗”。真正动手做起来就会发现,问题远不止“模型能不能识别”这么简单。
第一层需求是识别能力:在教室场景下,一个画面里可能有几十个学生,目标密集、彼此遮挡、后排人脸模糊,每个人的姿态多种多样,光线还随着时间变化。第二层需求是系统架构:检测结果要给谁看、在哪里看、以什么形式呈现。是老师课后看录像回放?还是教务管理人员实时盯着大屏?或者学生家长定期收到行为统计报告?不同使用角色决定了系统的功能边界。第三层需求才是交互方式:图片上传、视频文件上传、接入摄像头实时画面,这三种输入模式在很多真实项目里是同时要求的。
我的选型思路是:模型层用YOLOv11,业务层用Django搭建B/S架构。原因很直接,Python生态里目标检测的主流方案就是YOLO系列,而Django作为Python阵营最成熟的Web框架,自带ORM、Admin后台、用户认证和表单处理,配合Django REST Framework写API非常顺手,模型训练和Web服务不需要跨语言,整个链路的数据格式、类型转换成本最低。
1.2 技术栈选型:为什么是YOLOv11而不是其他版本
目标检测领域可选模型其实很多,Faster R-CNN、SSD、YOLO系列、更前沿的DETR,但放在学生行为检测这个场景里权衡,YOLOv11是最合适的选择。
YOLOv11相比之前的版本,在模型结构上有几处明显的调整:主干网络和颈部网络用了效率更高的结构设计,同时引入了注意力机制模块,使得模型在处理小目标和密集目标时特征提取更具针对性。教室画面里后排学生目标尺寸很小,多人相邻导致边界框重叠率高,这两点恰好是判断检测模型能不能用的硬指标。实际体验下来,YOLOv11的n/s/m这几个尺寸的模型,在精度和推理速度之间很容易找到一个平衡点,普通显卡甚至纯CPU都能跑起来,这对真实教室环境里可能没有高端GPU的情况很重要。
模型速度方面,nano版本在GPU上单张640×640图片推理能达到毫秒级,即使是在摄像头实时视频流这种对延迟敏感的场景,也能做到基本无感。m版本精度更高,代价是推理时间增加,但我们用视频抽帧策略和线程池并发,仍然能满足实时性要求。如果你的部署机器是一块消费级显卡,我的建议是优先考虑nano或small版本,而不是追逐更高的mAP。课堂行为检测的瓶颈往往不在模型精度,而在工程链路的稳定性和整体延迟。
1.3 行为检测的技术路线取舍:直接检测还是两阶段方案
关于学生行为检测,技术路线上有几条不同的方案路径。
最直接的做法是把它当成一个普通的目标检测多分类问题:在标注阶段直接给每个学生框打上行为类别标签,比如“听讲”“玩手机”“趴桌”“举手”,训练一个单阶段检测模型,学习完成后直接输出位置和类别。这个方案最简单,也是我最终采用的路线。
另一种方案是两阶段:先用一个通用目标检测模型把所有学生检测出来,然后对每个学生区域做裁剪,再送给一个行为分类模型。这种方案看起来灵活,实际上工程复杂度高了不少,行为分类模型需要额外训练,整个链路多一次推理,延迟翻倍,而且分类网络单独训练时很容易因为裁剪区域质量不稳定导致精度反而更差。
还有一种更复杂的路线是姿态估计+行为识别,先检测人体关键点,再根据关键点之间的空间关系推断行为。这个方法对玩手机、举手这类姿态特征明显的行为效果极好,但它的缺点也很突出:训练数据需求量大,标注成本高,教室场景遮挡严重时关键点检测失败率会明显上升。从落地性价比来看,普通项目根本没必要上这么重的方案。
我最终选择单阶段目标检测直接分类,并对类别定义做了细节优化。比如“玩手机”这个类别,在远距离画面里人眼都很难判断一个人是否在看手机,模型更难学到稳定特征。实际操作中我把这种模糊类别拆成更明确的“手持手机”和“低头发呆”——前者目标框内能看到手机轮廓,后者看头部的低垂角度和姿态。类别的边界清晰了,标注一致性才能保证,模型才能学到可靠的东西。
2. 数据准备与模型训练细节
2.1 数据采集与标注策略:数据质量决定模型上限
深度学习项目里有一句老话:模型效果的上限由数据和标注质量决定,模型本身只是在逼近这个上限。学生行为检测项目里,这句话体现得尤其明显。
数据采集阶段要注意视角问题。教室摄像头通常装在黑板正上方或教室前侧角落,视角是俯拍斜视的,这和常规目标检测数据集里那种水平视角的日常图片差别很大。如果你直接用公开数据集训练,部署到真实教室摄像头时很可能出现严重的域偏移——训练时模型见到的都是正面姿势,实际输入全是俯拍角度,检测效果断崖式下跌。所以数据采集一定要尽量贴近真实部署角度,如果条件允许,最好直接用目标教室的摄像头录几节课作为训练数据来源。
标注工具我推荐LabelImg或者Roboflow,前者免费开源,后者在线协作方便,导出YOLO格式时可以直接生成txt文件,省去格式转换的时间。标注框的选取原则是紧贴目标、包含主要特征区域。比如标“手持手机”时,框要包含手和手机,但不用把整个胳膊都框进去;标“趴桌”时,框应该贴合头和手臂所在区域,同时框的边缘不要过多延伸出去截到旁边同学的区域。
类别体系我最终定为七类:听讲、手持手机、趴桌、举手、站立、交头接耳、走动。这里有个关键设计:交头接耳和走动这两个类别不是严格意义的“姿态”,而是两个人的相对位置关系或位移状态。实际标注时,交头接耳我标的是两个相邻学生的头部同时侧向对方的区域,走动标的是站立且相对上帧位移较大的目标。这种标法虽然不够“学术严谨”,但胜在工程上可行、模型学得会。
标注数量方面,我的经验是单场景项目每个类别至少500个实例,总标注量建议在3000到5000个框以上。如果发现模型对某个类别经常漏检,优先检查那个类别的标注数量是不是明显偏少——这通常比调参管用得多。
2.2 模型训练的关键参数与训练流程
模型训练我基于Ultralytics官方框架,YOLOv11在Ultralytics版本里可以直接通过一行命令启动训练。这里分享我验证过的一组稳定参数组合,适合教室场景的数据量在3000~8000张图片区间的项目:
输入尺寸imgsz设为640。这个尺寸是检测精度和速度的公认平衡点,如果主要做摄像头实时检测且机器性能一般,降到480能换回明显更快的推理速度,代价是后排小目标精度下降,需要你自己权衡。batch size设为16或根据显卡显存自动调整。epochs设置在120到150之间,同时开启早停机制,patience设为20个epoch,防止后期过拟合浪费训练时间。优化器用AdamW,初始学习率设为0.01,权重衰减5e-4。数据划分按8:1:1分训练集、验证集、测试集,划分时要注意同一节课的连续帧尽量分到同一个集合里,避免数据泄露导致评估虚高。
训练过程中我会重点看两个指标:一是loss曲线是否平稳下降,二是验证集上的mAP50和mAP50-95是否持续上升。如果训练后期mAP50-95还在上升而mAP50已经稳定,说明模型正在优化定位精度,可以继续训练。如果loss降不下去,先怀疑标注中存在大量错误标签,而不是急着去调学习率。
预处理层面还有一个容易被忽略的细节:推理时图片要保持等比缩放后padding到640×640,而不是直接拉伸。直接拉伸会让人的比例失真,小目标的位置偏移被放大,模型在教室这种密集场景下的表现会明显变差。这个细节在代码里就是一行letterbox处理的事,但效果差异非常可观。
2.3 推理优化:从PyTorch到ONNX再到TensorRT
模型训练完成后,如果直接拿PyTorch原模型做Web服务推理,性能其实还有不少优化空间。我建议把模型导出为ONNX格式,再视部署环境决定是否进一步转TensorRT或OpenVINO。
导出ONNX很简单,Ultralytics框架自带导出命令,一句model.export(format="onnx", dynamic=False, half=True)就能完成。导出后推理时推荐用ONNX Runtime而不是继续用PyTorch,原因包括:一来ONNX Runtime更轻量,部署时不用装整个PyTorch环境;二来半精度推理能明显降低显存占用和推理延迟;三来ONNX Runtime的多线程CPU推理优化做得不错,即使没有GPU也能跑出可用的速度。
如果你的部署机器有英伟达显卡,我建议把ONNX再转成TensorRT的engine文件。TensorRT推理相比PyTorch原生推理,延迟往往能下降30%到50%,尤其在批量推理场景下优势更明显。转换过程会有一点折腾,比如需要安装匹配的TensorRT版本、处理动态输入维度,但收益完全值得。我当时在同一块显卡上对比过,YOLOv11n模型在PyTorch上单帧推理大约5~6毫秒,转成TensorRT后能压到3毫秒左右,对摄像头实时检测这种对帧率敏感的场景帮助非常大。
如果你部署机器的CPU是Intel的,那另一个可选方案是OpenVINO,它在CPU上的推理加速效果甚至优于ONNX Runtime的CPU后端,而且Ultralytics框架对OpenVINO也做了适配,导出和加载都是一条命令的事。
3. Django后端设计:模型服务与Web平台怎么融合
3.1 为什么选用Django来做业务平台
确定用Python做深度学习模型后,Django几乎是Web框架层的首选。原因不仅仅是“同一种语言生态衔接方便”,更因为它本身的内置能力能帮项目省掉大量重复劳动。
Django自带一个成熟的Admin后台系统。用户管理、检测记录管理、班级课程信息管理等基础功能,在Admin界面里改几行配置就能得到可用的管理面板,这在项目初期快速出原型阶段特别有价值。Django的ORM把数据库操作封装成了Python对象操作,建表、迁移、增删改查都不需要手写SQL,配合SQLite或PostgreSQL都很顺。Django内置了CSRF防护、XSS过滤、SQL注入防护等安全机制,虽然做内部系统时这些能力不一定全都用得上,但至少不用担心基础安全问题。
接口层我用Django REST Framework来做,序列化器直接把模型推理结果转成JSON,对前端同学相当友好。最终的系统结构是:Django处理HTTP请求、操作数据库、管理用户会话;推理模块以独立服务的方式集成进来,通过Python的线程池和消息队列把耗时推理任务和Web请求解耦,避免一次慢推理拖垮整个Web服务。
3.2 模型推理模块的加载与并发设计
模型推理嵌入Django的时候,最容易犯的错误是每个请求都重新加载一遍模型权重。模型加载过程涉及读取权重文件、初始化网络结构、在GPU上分配显存,耗时可能是几百毫秒到几秒不等。如果每次请求都重复这个过程,系统的高并发能力基本为零。
正确做法是全局单例加线程池。应用启动时加载一次模型,之后所有请求共享同一个模型实例做推理。Django是同步阻塞模型,如果直接在视图函数里同步调用模型推理,一个慢请求会把整个事件循环阻塞住,导致其他用户请求排队等待。解决方法是使用concurrent.futures.ThreadPoolExecutor,把推理任务提交到线程池中异步执行,主线程立即返回任务ID,前端通过轮询或WebSocket推送拿结果。
这里要注意一个GPU并发的细节:如果多个线程同时调用同一个GPU模型做推理,PyTorch和ONNX Runtime都提供了并发支持,但实际并发数不宜太高,否则显存占用翻倍且推理延迟反而上升。我实测下来的合理值是线程池大小等于GPU一次能并行处理的batch数,比如模型单次推理batch=8的GPU,线程池开4到8个并发是比较稳的。如果并发需求更大,更靠谱的方案是引入消息队列如Redis或Celery做任务缓冲,而不是无限加线程。
代码层面,一个可用的模型管理单例大概是这样的:
# inference/singleton.py import threading import onnxruntime as ort class ModelSingleton: _instance = None _lock = threading.Lock() def __new__(cls, model_path): if cls._instance is None: with cls._lock: if cls._instance is None: cls._instance = super().__new__(cls) sess_options = ort.SessionOptions() sess_options.intra_op_num_threads = 4 cls._instance.session = ort.InferenceSession( model_path, sess_options, providers=["CUDAExecutionProvider", "CPUExecutionProvider"] ) cls._instance.input_name = cls._instance.session.get_inputs()[0].name return cls._instance这个单例在Django应用的apps.py的ready()方法里初始化,或者在视图第一次调用时惰性加载,都可以。关键是确保整个进程生命周期内只有一份推理会话存在。
3.3 接口设计与三种模式的统一框架
接口层我采用了统一响应格式,所有接口都返回包含code、message、data三个字段的JSON,这样前端处理逻辑可以统一。核心接口有:
- 图片检测接口:接收上传图片,返回检测框列表、类别、置信度和标注后图片地址
- 视频检测接口:接收视频文件,创建检测任务,返回任务ID,前端异步轮询进度
- 视频任务状态接口:查询任务处理进度和最终结果
- 实时检测接口:通过WebSocket建立长连接,持续下发检测结果
- 行为统计接口:按课程/班级维度聚合行为分布数据,给可视化大屏用
三种模式里,图片和视频是典型的请求-响应模式,摄像头实时检测则是长连接推送模式。WebSocket在Django里最常用的方案是django-channels,它把Django从同步HTTP扩展到异步WebSocket能力。实时检测场景下,后端不断从摄像头拉帧、推理、绘制标注框,再把处理后的帧或者检测结果JSON推送给前端,前端的视频流因此具有极低的延迟。
4. 三种检测模式的完整实现
4.1 图像检测:上传、推理、可视化一条龙
图像检测是整个系统的功能基石,逻辑最简单,却把整个推理链路跑通了。流程是:前端上传一张教室照片,后端保存图片,预处理后送入模型推理,模型输出每个目标的边界框、类别和置信度,后端在原始图片上把框和标签画出来,同时把检测结果结构化存储到数据库,最后把标注后的图片URL和JSON数据一起返回给前端。
预处理这一步的letterbox处理很关键。原始图像尺寸五花八门,有的是1920×1080,有的是4032×3024,统一缩放到640×640时必须保持原始长宽比,多的边用灰色填充。这样保证送入模型的图片目标比例不被破坏。模型输出的边界框坐标是相对于640×640输入图的归一化坐标,需要按比例映射回原始图片尺寸,再传给OpenCV绘图函数。
绘图时我会根据类别区分颜色,文本框里同时写上类别中文名和置信度百分比。有一个经验值分享:置信度阈值设0.35到0.45之间,画面里目标多时可以适当调高到0.5,减少误检的干扰框;目标稀疏时调低到0.25,避免漏检。这个阈值在推理参数里设置一次就好,不需要实时调整。
4.2 视频文件检测:抽帧策略、任务队列和结果合成
视频检测和图片检测相比,核心难点从“怎么识别”变成了“怎么设计一套能跑完整个视频的工程流程”。一段四十分钟的课堂视频,每秒25帧的话就是六万帧,如果每帧都做完整推理,哪怕模型只有几毫秒延迟,总体耗时依然不可接受,而且大量相邻帧高度重复,信息冗余极大。
我的做法是抽帧检测。默认每5帧抽1帧做检测,也就是等效于每秒检测5帧左右。这个频率对于课堂行为统计已经足够,还能省下大量算力。如果视频是走动的学生比较多的高动态场景,把抽帧间隔缩到每3帧抽1帧;如果场景静态、学生大都在坐着听讲,每10帧抽1帧也能满足需求。
视频上传后,Django先把文件保存到服务器,然后创建一条检测任务记录,把任务提交给后台线程池异步处理。前端通过任务ID轮询进度,后台线程在每处理完一批帧后更新进度百分比。检测过程中不光要把每帧的结果画出来,还要按时间轴聚合统计每个行为类别出现的总帧数和占比,最终生成一段标注完毕的视频文件和一个行为统计报告。
抽帧的一个细节:检测结果存在相邻帧跳变的情况,比如某帧学生被同桌挡住导致检测中断,下一帧又检测出来。处理时不要直接丢弃孤立的检测框,而是做一个简单的时序平滑:如果某个ID的目标在连续两帧出现在附近位置,就认为它是同一个目标,它的检测状态以多数帧结果为准。这个逻辑用代码实现起来不复杂,但对最终统计报表的准确性影响很大。
4.3 摄像头实时检测:低延迟的完整链路
摄像头实时检测是三种模式里工程最难、也最能体现系统价值的模式。这里说的摄像头可以是教室里的RTSP网络摄像头,也可以是电脑自带的USB摄像头。RTSP摄像头是教室场景的主流设备,OpenCV的VideoCapture支持直接拉取RTSP流,实现成本最低。
整体链路分两个组件协作:一个专门的拉流线程持续从摄像头读取帧,把帧放入一个有上限的缓冲队列;一个检测线程从队列取帧、做预处理、推理、绘制标注框、编码为JPEG,最后通过WebSocket推送给前端。实时场景的帧率往往是25或30帧每秒,而检测模型实际跑不到这个速度。正确处理方式是丢帧策略:检测线程每次取队列里最新的一帧,而不是按顺序逐帧处理。这样就算检测速度只有8帧每秒,前端的实时画面也不会越来越卡,因为每次推送给用户的都是当前最新画面,画面只是间隔性地更新,而不是滞后地回放旧帧。
缓冲队列用Python的collections.deque,设置最大长度为4或5,每次从队列取帧时先做帧时间戳校验,过期帧直接丢弃。这一步很关键,避免了摄像头丢帧时OpenCV读接口卡死或者帧时间错乱的问题。
代码里WebSocket消费端的核心部分我用django-channels实现,大致结构是:
# consumers.py import cv2 import asyncio import json from channels.generic.websocket import AsyncWebsocketConsumer class DetectionConsumer(AsyncWebsocketConsumer): async def connect(self): camera_url = self.scope["url_route"]["kwargs"]["camera_id"] await self.accept() self.capture = cv2.VideoCapture(camera_url) self.running = True asyncio.create_task(self.push_stream()) async def push_stream(self): while self.running: ret, frame = self.capture.read() if not ret: await asyncio.sleep(0.1) continue results = self.detect_on_frame(frame) # 把检测后的帧编码成 JPEG,通过 WebSocket 二进制发送 await self.send(bytes_data=encoded_jpeg) async def disconnect(self, close_code): self.running = False self.capture.release()需要说明的是,上面这段代码简化了Django Channels的异步边界问题,生产环境还要加线程池或让检测逻辑运行在独立进程。但核心思路是明确的:拉流线程和推送通道分离,检测在线程池执行,主循环只负责帧的调度和转发。
实际操作中,摄像头实时模式最容易出现的问题是RTSP流断开后恢复困难。网络摄像头受网络波动影响,拉流线程经常在某个时间点读到黑帧或者直接read失败。处理办法是给拉流线程加自动重连机制:读取失败连续超过30次,就释放当前VideoCapture实例,延迟两秒重新创建连接。实测下来这样处理大部分瞬时断流都能自动恢复,不需要人工干预。
5. 常见问题与排查技巧实录
5.1 问题速查表
我在这个项目开发和调试过程中遇到了一些很典型的问题,整理成表格供参考,大部分问题都有一套可迁移的排查路径。
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 图片检测结果漏掉后排小目标 | 输入尺寸过小或letterbox填充过多 | 提高输入分辨率到960或1280,或改用m型号模型 |
| 视频检测进度条不更新 | 任务线程被阻塞或异常退出 | 检查日志中的异常堆栈,确认线程池是否被占满 |
| 摄像头画面推送延迟持续增大 | 拉流队列积压,消费速度跟不上 | 确认使用了丢帧策略,队列最大长度不要超过5 |
| WebSocket连接频繁断开 | 前后面数据流量过大或心跳超时 | 开启Channels心跳机制,前端做自动重连 |
| GPU显存持续增长 | 每请求都加载模型或张量未释放 | 确认模型全局单例,检查推理结果是否及时释放 |
| 类别中文名在界面显示为乱码 | 前后端编码不一致或缺少字体 | 后端统一返回UTF-8,确认渲染端字体文件包含中文字库 |
| CPU推理速度过慢 | 未使用letterbox预处理,或输入尺寸过大 | 检查预处理逻辑,必要时先用nano模型跑通链路 |
| 同一个目标被输出多个重叠框 | NMS阈值不合适 | 适当调低NMS IoU阈值到0.5左右 |
排查时我习惯遵循从简单到复杂的顺序:先看输入数据是不是符合预期,再看模型输出是不是符合预期,最后才怀疑框架和代码逻辑。大多数问题最终都是出在前两步的数据格式上,真正的模型bug反而很少。
5.2 几个容易踩坑的工程细节
第一个坑是Django的同步阻塞问题。如果直接在视图函数里同步调用模型推理,单用户测试时一切正常,但一旦多人同时上传视频或者同时打开实时检测页面,整个Django进程都会卡住——用户的图片上传请求、登录请求、数据库查询全部排队。这个问题的标准解法就是前面提到的线程池加任务队列,永远不要把耗时推理任务放在请求处理线程里同步执行。
第二个坑是RTSP摄像头拉流的中文路径和特殊字符问题。有些摄像头的RTSP URL里包含了中文字符或者特殊符号,OpenCV的VideoCapture有时会解析失败。解决办法是在拼URL时对用户名密码做URL encode,或者干脆让摄像头管理员把用户名密码改成纯字母数字,避免各种解析异常。
第三个坑是AI模型输出的坐标解析。ONNX Runtime或TensorRT输出的张量shape是[batch, num_dets, 6],6个元素分别是中心点x、中心点y、宽、高、置信度、类别索引。很多人第一次写解析代码时容易把坐标和置信度的顺序搞错,导致画出来的框全偏到一边。建议先在单张测试图片上写一个坐标可视化脚本,确认输出完全正确再接入Web系统,省去后续各种混淆调试的时间。
第四个坑是前端视频流的性能消耗。实时检测时后端推给前端的是JPEG流,直接用JavaScript的<img>标签接二进制流是可行方案,但如果前端同时开了多个摄像头画面,浏览器内存会迅速飙升。更好的做法是只在用户浏览器当前可见的区域渲染实时画面,用户切走时就暂停渲染,减少不必要的刷新。这个优化在真实教室场景中很常用。
5.3 从“能跑”到“好用”:项目的扩展方向
基础检测能力搭建完成之后,这个系统还有很多值得扩展的功能点。我个人觉得其中最值得做的是行为统计数据的时间聚合:把整个课堂时间线按一定间隔切片,统计每个时间段各类行为的人数和占比,生成波形图。老师下课后能看到这节课的前20分钟和最后10分钟学生的专注度变化趋势,这对教学改进的帮助远大于零散的检测框。
另一个实用方向是异常行为告警。比如检测到学生在课堂上趴桌超过10秒,或者出现长时间站立、走动等异常状态时,自动推送通知给教务或管理人员。告警逻辑可以在后端加一个状态跟踪器,维护每个目标的连续帧状态耗时,超过阈值再触发通知,避免单帧检测结果误报。
还有就是把检测能力和教务系统打通。Django本身有完整的用户和权限体系,可以在现有结构上增加班级管理、课程管理、学生档案模块,每次检测任务自动关联到具体班级和课程,逐步积累成一个课堂行为数据库。这个数据积累到一定量级后,还能做学生个体间的行为对比分析。这些扩展方向都不需要推翻现有架构,在Django的框架里逐个添加模块就行,这也是当初选择Django最重要的原因之一。
整套系统做完以后,我个人体会最深的一点是:模型训练只是项目里相对简单的一部分,真正难的是把模型稳定、高效地嵌进一套完整的业务系统里,并且让它能在真实环境里长期运行不崩。图像、视频、摄像头三种模式看起来是三个功能,实际上共用了一套核心架构:统一的模型推理服务、标准的接口协议、异步任务调度机制、WebSocket推送通道。把这套架构做稳了,后面加任何新功能都只是在往上叠模块而已。如果你也要做类似的系统,建议先把推理服务独立出来,再让Django去调用它,这条路比把模型直接塞进视图函数里要靠谱得多。