公司动态
基于深度学习的课堂学生行为识别系统:Django+PyTorch完整实战解析
简介图像分类是计算机视觉的基础任务其核心原理是通过卷积神经网络提取图像特征并映射到离散类别标签。在工程实践中迁移学习利用预训练模型在大型数据集上学到的通用特征有效解决了小样本场景下的模型训练难题显著提升了分类准确率与泛化能力。这一技术路径已被广泛应用于安防监控、智慧教育等领域的视觉理解任务。在课堂教学场景中基于深度学习的图像分类方法能够自动识别学生的听课、写字、玩手机等行为状态为教学评估提供数据支持。通过Django框架将训练好的PyTorch模型封装为Web服务可实现图片上传、实时推理与结果可视化构成一套完整的智能课堂行为分析系统。本文围绕模型选型、数据增强、池化原理、系统集成与部署优化等关键环节提供了从算法到工程的全面实践指南。 我们直接进入正题。这个项目标题很典型——“基于深度学习的上课学生行为识别系统(django)”一看就是课程设计或者毕业设计的经典方向。这类系统要解决的核心问题其实很明确课堂场景下老师不可能时刻盯着每一个学生而通过摄像头结合深度学习模型可以自动分析学生的听课状态比如是否在认真听讲、是否趴桌子、是否玩手机、是否举手发言等。我这里会从项目设计思路、模型选型、Django工程集成、训练细节、部署踩坑几个维度完整拆一遍帮你把这个项目从“能跑通”做到“能答辩、能演示、能写进简历”。先说结论这套系统本质上是一个“图像分类 Web可视化”的组合项目。深度学习部分负责从视频帧或图片中识别学生行为Django部分负责把模型包装成WEB服务提供上传、识别、结果展示、记录查询这些功能。技术栈不复杂但胜在完整非常适合作为深度学习入门后的第一个综合实战项目。1. 项目整体设计与思路拆解在动手写代码之前我建议你先想清楚三个问题识别什么、用什么识别、识别完之后怎么展示。这三个问题决定了你的系统架构和数据流。1.1 核心需求解析上课场景下的行为识别到底识别什么课堂学生行为识别关键词是“上课场景”。这个限定非常关键它决定了数据采集方式、模型输入形式和行为类别的定义。日常的图像分类任务比如区分猫和狗类别之间差异非常大模型比较容易学。但课堂行为不一样学生都坐在教室里背景高度相似姿势差异也小属于细粒度图像分类问题。根据常见的课程设计要求和实际课堂场景行为类别通常定义在5到8类之间行为类别特征描述识别难点认真听讲目视前方或老师方向坐姿端正侧面角度与发呆难区分低头写字头部低垂手部有书写动作与趴桌子容易混淆玩手机手部在桌面以下或胸前视线向下遮挡严重需依赖手部特征趴桌子头部完全贴合桌面与低头写字难区分需看肩部角度举手单臂上举与伸懒腰相似交头接耳头部转向同桌嘴部有动作与转头看窗外相似睡觉闭眼头部低垂长时间不动单帧图片无法判断需时序信息初次做这个项目我建议不要贪多先选5类认真听讲、低头写字、玩手机、趴桌子、举手。这几类已经能满足大多数演示和评分要求。1.2 系统架构设计Django如何与深度学习模型协作很多初学者最搞不清楚的就是“深度学习模型怎么放进Django里”。其实非常简单Django在中间扮演的是“调度者”和“展示者”的角色模型本身是一个独立的推理模块。整体数据流是这样的用户通过浏览器访问Django页面上传一张图片或一段视频。Django视图函数接收到上传文件后将文件保存到临时目录或内存中。Django调用训练好的深度学习模型对图片进行预处理执行推理。模型返回预测结果类别标签 置信度。Django将结果存入数据库并渲染到HTML模板展示给用户。这套架构最核心的设计思想是“模型与Web解耦”。模型推理逻辑封装成一个独立的Python模块建议用model_utils.py或services.py来管理。Django视图只负责接收请求、调用推理函数、返回响应不直接接触模型内部细节。这样做的好处是模型更新时可以不动Django代码模型推理出错时也不会导致整个Web服务崩溃。如果识别的是视频则数据流会多一步Django调用OpenCV对视频逐帧抽取对每一帧执行行为识别然后汇总所有帧的识别结果统计每个行为出现的帧数占比生成报告。1.3 技术选型与方案对比为什么选择Django而不是Flask几乎每届学生都会问Django和Flask都能做Web为什么选择Django我的选择依据很简单项目规模、内置功能、生态成熟度。Flask是微框架轻量灵活适合API服务或极简应用。但课堂行为识别系统不只是提供一个识别接口它通常还需要用户登录、历史记录管理、数据统计展示、文件上传管理等配套功能。这些功能如果从零用Flask搭工作量会多出不少。Django自带Admin后台、ORM数据库映射、用户认证系统、表单处理、模板引擎这些正好覆盖了毕设系统的常见需求。更重要的是Django的项目结构是固定的manage.py、urls.py、views.py、models.py、templates/、static/这种固定的目录组织方式反而对初学者友好不容易写着写着就乱套。在深度学习推理方面Django和Flask没有本质区别都是Python进程内直接调用PyTorch或TensorFlow模型。但有一个实际坑需要提前说明Django开发服务器是单进程的深度学习模型加载会占用大量内存如果模型参数超过500MB建议在settings.py中将DEBUG设为False并配置STATIC_ROOT用更稳定的方式提供服务。2. 核心细节解析与实操要点这个项目的核心难点全部集中在深度学习部分。Django只是“外衣”模型识别效果才是评分和演示的关键。很多同学最后翻车不是Web出了问题而是模型准确率太低识别结果惨不忍睹。2.1 深度学习模型选型从CNN到迁移学习行为识别本质上是图像分类任务深度学习模型的选择直接影响识别效果。常用的模型分为三类第一类是经典CNN模型如VGG16、ResNet50。VGG16结构简单、易于理解适合在论文中画结构图但参数量高达1.38亿训练和推理都比较慢。ResNet50引入了残差连接解决了深层网络退化问题识别效果和速度的平衡较好是这类项目的首选。第二类是轻量级模型如MobileNetV3、EfficientNet Lite。MobileNetV3使用深度可分离卷积参数量只有ResNet50的十分之一左右推理速度在CPU上也能达到实时。如果你的部署环境没有GPU强烈建议选MobileNet系列。实测在CPU上MobileNetV3单张图片推理时间约80msResNet50约350ms。第三类是检测模型如YOLOv5、YOLOv8、SSD。如果你不仅要识别行为还要定位“哪个位置的学生在玩手机”就需要用目标检测模型输出的是边界框和类别。但目标检测的数据标注成本远高于图像分类每张图都要画框。对于课程设计来说图像分类的难度和完成度已经足够。更实际的做法是不从头训练模型而是使用在ImageNet上预训练好的权重做迁移学习。迁移学习是这一节的重点。核心思路是预训练模型已经学会了通用的图像特征边缘、纹理、形状我们只需要把最后一层全连接层换成适配自己任务类别的结构然后冻结前面层只训练后面的分类层或者以较小学习率微调整个网络。以PyTorch为例加载预训练ResNet50并换分类头的标准写法如下import torchvision.models as models import torch.nn as nn model models.resnet50(pretrainedTrue) num_features model.fc.in_features num_classes 5 # 你的行为类别数 model.fc nn.Linear(num_features, num_classes)这一步微调网络的方法能让你的模型在小规模数据集上也能达到85%以上的准确率初学者一般不需要头铁到从零开始训练一个几百万参数的网络。2.2 数据准备与增强模型效果的“隐形分水岭”模型效果的上限由数据和标注质量决定。课堂行为识别数据集的来源通常有三种渠道第一种是公开数据集比如Classroom Behaviour Dataset、Student Classroom Behavior数据集但这些数据集类别定义各不相同且大多数人不容易下载到。第二种是自己录制的课堂视频对真实课堂场景进行截帧。第三种是网络图片搜索按类别搜索相关图片然后人工清洗。无论哪种渠道你都面临一个共性问题数据量不够。深度学习的图像分类任务每类至少需要300到500张图片才算勉强够用。如果每一类只有几十张图模型必定过拟合——训练集准确率99%验证集只有60%。解决数据量不足的关键手段是数据增强。数据增强不是改变图片内容而是通过随机变换增加样本多样性让模型看到更多“变体”。常用的增强操作包括随机水平翻转学生举手方向不重要翻转后仍代表同一行为。随机旋转在正负15度范围内旋转模拟摄像头角度差异。随机亮度对比度调整模拟教室不同时段光照变化。随机裁剪缩放模拟不同距离拍摄的尺度差异。随机遮挡模拟前排学生遮挡后排的实际情况。PyTorch中可以用torchvision.transforms直接组合这些增强方式from torchvision import transforms train_transform transforms.Compose([ transforms.Resize((224, 224)), transforms.RandomHorizontalFlip(p0.5), transforms.RandomRotation(degrees15), transforms.ColorJitter(brightness0.3, contrast0.3), transforms.RandomResizedCrop(size224, scale(0.8, 1.0)), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ])迁移学习配套的数据增强策略是“训练时用强增强提高泛化能力验证时只用Resize和规范化确保评估结果的真实性”。验证集千万别加RandomHorizontalFlip之类的增强否则评估结果会虚高误导你对模型真实性能的判断。2.3 训练策略超参数设置与评判指标训练环节直接决定最终效果这里给出一个经过验证的、适合小规模数据集的训练策略。以小规模数据集每类300-500张共5类为例推荐的超参数如下超参数推荐值说明优化器Adam收敛快对学习率不敏感适合初学者初始学习率0.0001迁移学习时不能用太大学习率会破坏预训练权重批大小16 或 32取决于GPU显存显存不够就调小训练轮数30-50轮配合早停Early Stopping学习率衰减每10轮乘以0.1后期精调损失函数CrossEntropyLoss标准分类损失内置Softmax训练过程中需要同时关注训练集和验证集的准确率与损失值。当训练准确率持续上升而验证准确率不再变化时说明模型过拟合此时应该停止训练或增加数据增强强度。当训练准确率和验证准确率都很低低于70%说明模型欠拟合可能是学习率太大导致不收敛或模型结构有问题。评价指标不能只看准确率。课堂行为数据集通常存在类别不均衡问题比如“认真听讲”的样本远多于“玩手机”这时准确率会被多数类拉高掩盖少数类的低识别率。建议同时计算每个类别的精确率、召回率和F1值精确率 预测为该类且正确的样本数 / 预测为该类的样本数要看“查得准不准”。召回率 预测为该类且正确的样本数 / 该类实际样本数要看“找得全不全”。F1值 2 x 精确率 x 召回率 / (精确率 召回率)综合两者。举一个实际的例子模型对“玩手机”类别的召回率只有50%意味着有一半玩手机的学生被漏掉很可能被误判成了“低头写字”。究其原因是“低头写字”和“玩手机”在视觉特征上高度相似手都在桌面以下头部都低垂。针对这类问题可能需要考虑添加手部姿态的关键点检测或者在数据集中增加更多从侧面视角拍摄的样本。2.4 模型优化的关键知识点池化层与特征提取在训练模型时“池化”这个词会高频出现因为CNN几乎每个卷积块后面都跟一个池化层。很多初学者只看会调用模型API却不理解池化的作用导致在模型解释和答辩环节被问住。池化层Pooling Layer的核心作用是下采样也就是缩小特征图的尺寸。以最常用的最大池化Max Pooling为例它的做法是在一个2x2的窗口内取最大值然后滑动窗口并输出这些最大值组成的新特征图。经过一次2x2最大池化特征图的长和宽各缩小一半参数量和计算量也随之减少。池化带来三个关键好处第一是降低计算复杂度让网络能处理更大尺寸的输入第二是扩大感受野池化后的特征点能看到原始图像更大的区域高层网络因此能学到语义更丰富的特征第三是提供一定的平移不变性同一目标的像素稍微偏移几个位置池化后的特征基本不变这对学生坐姿在画面中稍有偏移的场景非常有用。答辩时如果被问到“为什么用全局平均池化而不是全连接层”你可以这样回答ResNet50最后一层结构是“卷积输出 - 全局平均池化 - 全连接层”。全局平均池化把每个通道的特征图平均成一个数值这样每个通道都对应一个类别特征响应减少参数量同时防止过拟合是目前主流分类网络的标配设计。3. 实操过程与核心环节实现这一部分直接进入代码与实现。我假定你已经有Python基础安装好了Anaconda电脑上能跑PyTorch。如果环境还没有建议先安装好PyTorch CPU版也可以完成大部分工作只是训练会慢一些。3.1 Django工程与项目结构搭建先创建Django项目和应用。以项目名behavior_system、应用名recognition为例在命令行依次执行django-admin startproject behavior_system cd behavior_system python manage.py startapp recognition python manage.py migrate这时你的项目目录结构大致如下behavior_system/ ├── manage.py ├── behavior_system/ │ ├── __init__.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py └── recognition/ ├── __init__.py ├── admin.py ├── apps.py ├── models.py ├── views.py └── tests.py首先需要在settings.py中注册应用把recognition加入INSTALLED_APPS。同时配置好模板目录和静态文件目录。如果你的模板文件打算放在项目根目录下的templates文件夹则在settings.py中找到TEMPLATES配置将DIRS: []改为DIRS: [os.path.join(BASE_DIR, templates)]。紧接着创建数据库模型。行为识别记录表至少需要包含以下字段上传图片名称、识别结果类别、置信度、识别时间。如果你是做了视频上传还需要记录视频文件名。Django模型定义在recognition/models.py中代码如下from django.db import models class BehaviorRecord(models.Model): image models.ImageField(upload_toimages/) result_label models.CharField(max_length50) confidence models.FloatField() created_at models.DateTimeField(auto_now_addTrue) def __str__(self): return f{self.result_label} - {self.confidence:.2f}定义好模型后执行python manage.py makemigrations和python manage.py migrate生成数据库表。Django默认使用的是SQLite数据库对于课程设计项目来说完全足够不需要额外安装MySQL。3.2 深度学习模型模块封装独立加载与推理这里要重点强调一个工程实践模型加载代码与Django视图代码分层。我习惯在recognition应用下创建一个model_utils.py文件专门负责模型加载、预训练、推理。Django的views.py不直接出现任何PyTorch代码只调用model_utils.py暴露的函数。在model_utils.py中全局加载模型一次是有讲究的。如果每次请求都重新加载模型Django服务会变得极慢因为加载模型权重非常耗时。正确做法是模块加载时初始化一次模型之后所有请求复用。import torch import torchvision.models as models from torchvision import transforms from PIL import Image import os class BehaviorClassifier: _instance None def __new__(cls): if cls._instance is None: cls._instance super().__new__(cls) cls._instance._initialize() return cls._instance def _initialize(self): self.device torch.device(cuda if torch.cuda.is_available() else cpu) self.model models.resnet50(weightsmodels.ResNet50_Weights.DEFAULT) in_features self.model.fc.in_features self.model.fc torch.nn.Linear(in_features, 5) # 加载自己训练好的权重 model_path os.path.join(models, behavior_model.pth) self.model.load_state_dict(torch.load(model_path, map_locationself.device)) self.model.to(self.device) self.model.eval() self.class_names [认真听讲, 低头写字, 玩手机, 趴桌子, 举手] self.transform transforms.Compose([ transforms.Resize((224, 224)), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ]) def predict(self, image_path): image Image.open(image_path).convert(RGB) input_tensor self.transform(image).unsqueeze(0).to(self.device) with torch.no_grad(): outputs self.model(input_tensor) _, predicted torch.max(outputs, 1) probs torch.nn.functional.softmax(outputs, dim1)[0] label self.class_names[predicted.item()] confidence probs[predicted.item()].item() return label, confidence这里使用了单例模式__new__中判断_instance确保整个Django进程中只初始化一次模型。代码中还有一个关键细节加载保存的权重时用了load_state_dict因为保存的只是模型的权重参数不含模型结构。训练完模型后应该用torch.save(model.state_dict(), behavior_model.pth)保存而不是直接保存整个模型。3.3 视频识别实现逐帧抽取与结果汇总如果你希望系统支持上传视频而不是单张图片需要在预测函数中加入视频抽帧逻辑。这里我提供一个经过实际测试的视频处理方案。视频处理的思路是使用OpenCV读取视频每隔固定帧数抽取一帧送入模型预测然后统计所有帧的类别分布。间隔帧数需要根据视频长度和实时性需求平衡一般建议每5到10帧取一帧。以一个2分钟的课堂视频为例标准是30fps总帧数3600每10帧取一帧最后得到360个预测结果覆盖了整个视频的12个采样点足够统计行为占比。核心代码如下import cv2 def predict_video(video_path): cap cv2.VideoCapture(video_path) frame_count 0 results [] while True: ret, frame cap.read() if not ret: break frame_count 1 if frame_count % 10 ! 0: continue # 保存当前帧为临时图片或直接转为PIL图像 pil_img cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) from PIL import Image pil_img Image.fromarray(pil_img) # 复用上面的transform和预测逻辑 input_tensor classifier.transform(pil_img).unsqueeze(0).to(device) with torch.no_grad(): outputs classifier.model(input_tensor) _, predicted torch.max(outputs, 1) results.append(classifier.class_names[predicted.item()]) cap.release() # 汇总统计 from collections import Counter counter Counter(results) total len(results) summary {k: round(v / total, 4) for k, v in counter.items()} most_common counter.most_common(1)[0] return most_common[0], most_common[1] / total, summary注意视频处理不要每帧都停下来拿PIL转换这样会很慢。上面的代码中已经做了优化只在抽帧时才转换。如果你处理的视频分辨率很高建议先用cv2.resize把帧缩小到416或640宽度再送入模型因为上课场景下细节对分类的影响在缩小后依然保留但推理速度能提升好几倍。3.4 Django视图与前端交互实现后端视图逻辑是Django项目另一大核心。这里需要处理好POST请求的两种类型图片上传识别和视频上传识别。在recognition/views.py中编写两个视图函数一个渲染上传页面另一个处理识别请求。处理识别请求的视图需要同时处理图片和视频可以按文件扩展名或文件类型区分import os from django.shortcuts import render from django.conf import settings from django.http import JsonResponse from .models import BehaviorRecord from .model_utils import BehaviorClassifier classifier BehaviorClassifier() def index(request): return render(request, index.html) def predict_view(request): if request.method ! POST: return JsonResponse({error: 请求方法错误}, status405) uploaded_file request.FILES.get(file) if not uploaded_file: return JsonResponse({error: 未收到文件}, status400) # 保存上传文件 upload_dir os.path.join(settings.MEDIA_ROOT, uploads) os.makedirs(upload_dir, exist_okTrue) file_path os.path.join(upload_dir, uploaded_file.name) with open(file_path, wb) as dest: for chunk in uploaded_file.chunks(): dest.write(chunk) ext os.path.splitext(file_path)[-1].lower() image_exts [.jpg, .jpeg, .png, .bmp] if ext in image_exts: label, conf classifier.predict(file_path) BehaviorRecord.objects.create( imagefile_path, result_labellabel, confidenceconf ) return JsonResponse({label: label, confidence: round(conf, 4)}) elif ext in [.mp4, .avi, .mov, .mkv]: label, ratio, summary predict_video(file_path) return JsonResponse({ label: label, ratio: round(ratio, 4), summary: summary }) else: return JsonResponse({error: 不支持的文件类型}, status400)前端页面用HTML模板实现配合JavaScript的fetch实现异步上传避免刷新页面。在base.html或index.html中引入静态文件界面部分建议做得简洁核心突出“选择图片 → 点击识别 → 显示结果”三步流程不需要复杂的前端框架。3.5 路由配置与本地服务启动最后把视图和URL绑定起来。在behavior_system/urls.py中引入recognition应用的视图from django.contrib import admin from django.urls import path from recognition import views as recognition_views urlpatterns [ path(admin/, admin.site.urls), path(, recognition_views.index, nameindex), path(predict/, recognition_views.predict_view, namepredict), ]接着还需要在settings.py里配置MEDIA_URL和MEDIA_ROOT否则上传的图片无法在页面中正常显示MEDIA_URL /media/ MEDIA_ROOT os.path.join(BASE_DIR, media)然后在根URL中添加静态服务路径开发环境使用from django.conf import settings from django.conf.urls.static import static urlpatterns static(settings.MEDIA_URL, document_rootsettings.MEDIA_ROOT)全部配置完成后在项目根目录执行python manage.py runserver在浏览器访问http://127.0.0.1:8000/就能看到上传页面了。4. 常见问题与排查技巧实录这个项目虽然整体难度中等但实操中总能遇到各种问题。我把常见的坑整理一下分环境、数据、模型、部署四类来排查。4.1 训练阶段损失不下降与过拟合训练损失不下降是最常遇到的问题之一。出现这种情况先别急着调整网络结构按顺序排查以下三个地方第一个是数据读取是否正常。用matplotlib或PIL可视化几张训练图片看图片是否被正确读取、标注是否错位。我见过学员把训练集和验证集放反了导致验证集一直比训练集效果差很多还误以为是过拟合。第二个是标准化是否需要。如果你用了ImageNet预训练权重那么输入数据必须使用ImageNet的均值和标准差进行归一化否则模型输出会异常。数据增强里的transforms.Normalize(mean[0.485,0.456,0.406], std[0.229,0.224,0.225])这套标准的值是ImageNet数据集的统计值不是随便写的。第三个是学习率是否过大。Adam优化器虽然自适应学习率但初始学习率从0.001调大时仍很容易早发散。一个快速验证方法是在训练集上只训练50个batch如果损失完全不降说明学习率过高或数据读取错误。过拟合的表现是训练准确率接近100%验证准确率停滞甚至下降。解决办法从轻到重排列加大数据增强强度更换模型为更轻量的网络如MobileNetV3减少全连接层维度或增加Dropout以及增加数据量。4.2 推理阶段模型加载慢与预测速度慢模型加载慢的根源是每次请求都重新加载权重。解决方法是使用单例模式见上文BehaviorClassifier的__new__实现或者把模型放到Django启动时的AppConfig.ready()方法中预加载。后者的写法是# recognition/apps.py from django.apps import AppConfig class RecognitionConfig(AppConfig): default_auto_field django.db.models.BigAutoField name recognition def ready(self): import recognition.model_utils预测速度慢通常是因为输入图片尺寸过大。ResNet50的输入尺寸是224x224如果你的原始图片是1920x1080直接送入网络会造成大量无效计算。建议在预处理阶段先将图片缩放到短边不超过512再Resize到224x224。如果运行服务器没有GPUCPU推理时需要额外确认PyTorch是否安装了CPU版本。CPU版和CUDA版的推理速度差距可能高达20倍。检查方式python -c import torch; print(torch.__version__, torch.cuda.is_available())如果输出torch.cuda.is_available()为False说明当前环境是CPU版本视频抽帧识别会明显卡顿。此时可以优先考虑换用MobileNetV3能明显改善推理效率。4.3 Django部署静态文件404与图片不显示Django开发环境调试时图片能显示一关闭DEBUG就全坏了这是典型的静态文件配置问题。DEBUGTrue时Django自动处理静态文件DEBUGFalse时就需要配置STATIC_ROOT并执行收集静态文件命令STATIC_ROOT os.path.join(BASE_DIR, staticfiles)然后执行python manage.py collectstatic同时如果你用python manage.py runserver跑生产模式测试MEDIA_ROOT下的上传文件并不会自动被Django服务需要手动在URL配置中添加static()支持。如果没有更专业的部署方式这里说明一下runserver仅适合开发和课程设计演示不适合正式生产环境。对于毕设演示用runserver已经足够。4.4 环境配置国内环境安装依赖的加速技巧Python环境安装Django和PyTorch时在国内网络环境下默认源的速度通常非常慢。建议把pip源切换为国内镜像源。Linux或Windows命令行下执行pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple这样Django、Pillow、opencv-python等依赖包的安装速度会快很多。安装PyTorch时如果直接使用官方命令pip install torch torchvision下载体积非常大超过2GB建议到PyTorch官网选择适合自己CUDA版本的安装命令。如果你的电脑没有NVIDIA GPU直接安装CPU版即可pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu如果你是在Ubuntu 22.04或24.04这类Linux环境下配置还要注意OpenCV对libGL.so.1的依赖。安装opencv-python后运行项目时如果报错ImportError: libGL.so.1: cannot open shared object file需要额外安装系统库sudo apt update sudo apt install libgl1 -y4.5 训练数据与模型文件的命中率问题类别不均衡课堂行为数据集中“认真听讲”样本往往远多于“玩手机”样本这种数据分布会导致模型对高频类别偏好明显。解决类别不均衡的实操手段有几种第一对少数类进行过采样在读取数据时让“玩手机”图片的重复概率更高。第二使用加权损失函数根据类别样本数量的倒数设置CrossEntropyLoss的weight参数。第三训练时对少数类使用更强的数据增强。我在实际项目中最常用的是加权损失改动最小效果立竿见影import torch.nn as nn class_counts torch.tensor([500, 450, 200, 300, 150], dtypetorch.float) weights 1.0 / class_counts weights weights / weights.sum() * len(class_counts) criterion nn.CrossEntropyLoss(weightweights)训练结束后如果要查看各类别的精确率和召回率可以使用sklearn.metrics.classification_report输出结果是一张表格方便直接贴到论文里作分析。5. 项目扩展从单帧识别到实时视频流如果做完基础版本还有余力可以往这两个方向扩展第一个是实时摄像头识别。把Django与WebSocket或前端getUserMedia结合浏览器调用摄像头后逐帧将图片POST给DjangoDjango返回识别结果。这种方式在局域网内演示效果很好现场交互体验比上传视频更震撼。需要说明的是Django开发服务器对并发支持有限做实时演示建议只在演示现场启动单用户访问不会有明显卡顿。第二个是引入时序模型。当前系统对单帧图片分类无法识别“睡觉”这类需要观察时间跨度的行为。改进方案是使用视频片段作为输入例如每5秒截取20帧用3D CNN或Transformer时域注意力模型做行为识别。训练难度较大但如果能在论文中展示这一部分整个项目的技术高度会提升不少。课程设计如果有余力建议把“单帧静态分类 视频滑动窗口统计”结合在演示时既能展示准确率又能展示时序判断逻辑答辩会更稳。还有一个值得做的方向把识别结果按时间序列绘图画出课堂不同时间段内各类行为的占比变化曲线。这个功能只需用ECharts在前端绘制折线图数据来自Django返回的按时间分段的识别结果统计。这种可视化图表在展示中往往比模型本身更抓眼球也更容易被评委理解和认可。6. 实操总结与心得分享这个项目我陆陆续续做过几次最深的感受是行为识别本身并不是最大的难题难的是把工程链路理清楚。模型训练、Django集成、前后端联调、环境配置、数据整理每一环都需要投入时间。建议初次动手时严格按照下面的顺序推进先跑通PyTorch官方ResNet50迁移学习样例确认自己的数据能训练出可用的模型再搭建Django工程先写一个返回“hello world”的页面第三步把模型封装进model_utils.py在单独的Python脚本中测试预测函数最后再接入Django视图并整合前端页面。每一步都有明确的验证节点出了问题容易定位。环境管理上强烈建议用Anaconda创建独立的Python虚拟环境而不是直接使用系统Python。因为毕设项目周期长中间可能装各种依赖包独立环境即使装坏了也可以直接删除重建不污染系统环境。还有个建议是模型文件的管理。训练好的behavior_model.pth文件最好统一放在Django项目根目录下的models文件夹中并在README中写明文件来源和训练数据集信息。因为你做演示时的机器可能没有训练时的环境如果模型文件和代码分开存放换机器部署时就会很痛苦。最后说一下心态课上项目的时间通常是两个月左右你不需要把模型做到学术界SOTA水平只要在你自己采集的演示数据上准确率稳定在85%到90%配合完整的Django前后端交互已经是一个完成度很高的项目。如果再能流畅解释清楚每一个参数选择和模型结构的原理比如为什么选ResNet50、什么是池化、为什么用迁移学习那应付课程答辩基本没有压力。这个系统后续还能继续扩展比如把识别结果定期生成课堂报告推送给辅导员或者结合实验室的闸机实现学生到课率统计。起点虽然只是一套常规的深度学习Web系统但应用场景一旦跑通很多实际需求都能往这个框架上挂。你做完这个项目之后再回头看会发现练到的东西其实远不止“行为识别”这四个字。本文还有配套的精品资源点击获取