教室后排的摄像头每隔一两秒拍下一帧画面,画面里几十个学生的小脑袋在课桌间晃动,有的低头写题,有的趴着不动,有的侧身跟同桌说话。把这些连续的画面变成结构化的行为数据,再进一步换算成班里每个学生的专注度曲线、整堂课的师生互动报告,这就是智慧课堂行为识别系统在做的事。这篇文章就完整复盘一个课堂行为自动分析项目的设计思路、数据集构建和模型落地过程,重点讲我踩过的坑和沉淀下来的方法,适合正在做智慧教育方向算法、数据集建设、或准备把YOLO系列检测模型用到真实课堂场景的同学参考。
先交代一下这个项目的基本盘:检测模型采用YOLO系列改进版本,内部代号叫YOLO26,对应实验记录里的第10614期训练版本;数据侧自建了一个覆盖学生行为、教师教学状态的多场景数据集;输出侧不是只做检测框,而是把行为类别转成了专注度评分、教师教学状态统计等课堂管理关注的核心指标。整个项目边界很清晰:摄像头只装在教室里,只分析教学场景内的行为,不做任何与教学无关的扩展。
1. 项目整体思路与方案选型
1.1 课堂行为识别到底要解决什么问题
这个项目最初的需求来自某学校教务管理部门的日常痛点:传统听评课依赖人工进入教室记录,一节45分钟的课听下来,记录员最多只能写出课堂互动次数、学生举手人数这类粗略数据,没办法持续量化每个学生的专注状态,也不可能覆盖全校所有班级。教务想要的是“每节课后能自动生成一份课堂行为报告,老师可以回看自己讲课时的走动轨迹,班主任能快速看到哪些学生这节课专注度偏低”。
从这个需求出发,行为识别的核心目标被拆成了三个独立但互相配合的方向:
- 学生端专注度评估:识别学生是否在看黑板、看书、写字、举手、起立、发呆、趴桌、交头接耳、看手机等行为,用时间窗口内的行为占比计算专注度分数。
- 教师端教学状态监测:识别教师的讲授、板书、巡视、使用多媒体、暂停沉默等状态,统计教师一节课中各个环节的时间占比和活动轨迹。
- 课堂互动事件识别:包括学生举手被应答、教师走到学生身边指导、小组讨论等事件,这类事件能反映课堂活跃度,后续也能跟专注度数据做交叉分析。
方案选型上,我们明确放弃了一上来就做“情感计算”“表情识别”这类过于抽象的方向。原因很实际:课堂摄像头距离学生较远,人脸分辨率普遍偏低,表情特征的可靠性远低于姿态和头部朝向;而且表情与“专注”之间的映射关系在心理学层面本身就没有统一标准。行为检测则不同,它可以直接落在可观测的物理动作上,“头趴在桌面上”就是趴桌,“眼睛朝向黑板方向且头部无大幅移动”在工程上可以归为注视/听讲,这些规则可解释性强,也方便老师理解系统的判断依据。
1.2 为什么最终选定YOLO系列做检测主干
课堂场景做目标检测有几个非常硬性的约束:教室人数多导致密集小目标,学生头部在画面里往往只有三四十像素;摄像头视角固定但存在课桌椅遮挡;推理终端可能是教室内的普通算力设备,预算不支撑高端GPU服务器。这就要求检测器必须同时满足三点:小目标检测能力足够、推理速度够快、工程生态成熟方便快速迭代。
YOLO系列在这些方面天然有优势。它把目标检测处理成回归问题,一次前向就能输出类别和框,速度上动辄上百FPS;新版本迭代中持续强化了多尺度特征融合,对密集小目标场景做了大量针对性优化。更重要的是,YOLO的社区资料和部署工具链非常齐全,从自定义数据集训练到TensorRT导出都有成熟的参考路径,这对一个需要持续迭代落地的项目来说非常关键。
为了验证选型合理性,我拿课堂场景数据做过一组对比实验:同一批标注数据,在边缘设备上分别测试了YOLO系列与另两类检测算法。YOLO在mAP50接近的前提下,单帧推理耗时控制在20毫秒以内;另两类算法中小目标版本精度略高,但推理耗时翻倍,已经影响实时抽帧分析。最终我们确定以YOLO系列作为检测主干,并针对课堂场景做了检测头和数据增强的改造,这就是项目里代号“YOLO26”模型的由来——我们内部每训练一版就记录一条实验编号,做到第10614期时,模型在课堂自建测试集上的综合表现达到了可上线的标准。
1.3 一段视频怎么变成行为报表
很多刚接触这个方向的同学容易把项目想成“训练一个模型就完事”,实际上YOLO26只能解决视觉检测这一步,它输出的不过是一堆框和类别。要真正回答“这节课学生专注度如何”,还需要一条完整的分析链路。
整条链路是:视频流按固定间隔抽帧,抽出的图像送入YOLO26检测模型,得到每个目标的类别和位置;检测结果交给跟踪器,保证同一个学生连续帧中是同一个ID;系统为每个ID维护一个短时行为缓冲区,用状态机或投票机制判断当前行为;然后以1分钟或5分钟为窗口,把窗口内行为帧占比换算成专注度分数,写入数据库;最后报表服务从数据库读取数据生成课堂报告。离线部分则做数据回流,把推理产生的“难例图像”定期抽出来,补充进数据集重新训练模型,形成数据闭环。
2. 数据集构建:行为识别的地基
2.1 行为类别和标注规范怎么定
数据集是整个项目的根基,但也是初期最容易失控的地方。类别设计直接决定模型能学到什么。我们经过模块划分后,最终定义了学生端和教师端两个独立的类别集合。
学生端包括:专注(facing_board / writing / reading)、举手(hand_up)、起立(stand_up)、分心(looking_around / talking)、趴桌(lying)、使用手机(phone)、遮挡(occluded)。教师端包括:讲授(lecture)、板书(blackboard)、巡视(walk_around)、使用多媒体(multimedia)、暂停(pause)。
这里有一个非常关键的准则:类别必须互斥且可按优先级判定。比如一个学生右手举起来同时头转向同桌,按规则这个时刻就应该判定为“举手”,因为举手事件在课堂评估中优先级高于“交头接耳”;头部朝向黑板但手里在转笔,归为“专注”还是“分心”?工程上我们规定只要头部朝向教学区域且持续无大幅移动,就算“专注”类,手里的小动作模型不识别,避免把评估指标搅得太复杂。
标注规范同样要落到文字并反复培训标注员。我踩过的第一个坑就是标注框的范围不统一:有人标人头,有人标上半身,有人把整张桌子都圈进去。后来规定统一标注“包含头肩或整个人体的最小外接矩形”,如果目标被严重遮挡但仍然能判断出上半身位置,就标遮挡类并保留框;如果完全被前排同学挡住没有足够视觉信息,则跳过不标,绝不猜测位置。
2.2 数据采集和多人标注的质量控制
课堂数据的获取比一般目标检测数据集麻烦在场景不可控。不同教室的光线差异很大,有的教室是暖色灯,有的是白色LED;摄像头安装有前视和后视之分,前视能拍到学生正脸,后视拍到的是后脑勺;同一个行为在不同视角下的视觉特征也完全不同。所以采集阶段我们只做两个硬性要求:覆盖多个教室和多个时段,上午一二节、下午第一节、晚自习都要有;每个摄像头机位采集时长不少于两小时,且包含完整的课堂环节——讲授、板书、讨论、练习穿插进行。
标注工具上,团队用开源标注工具比较多,我建议用支持多人协作和自动保存的版本,避免传统单机标注工具在断线重连时丢失进度。多人标注最大的问题是标准漂移:标注员A把“低头看书”标成专注,标注员B标成reading_focus,但标注员C可能把它标成分心。为了控制一致性,我采用了两步策略:先由项目负责人标注一批共识样本,覆盖容易混淆的行为边界,作为“标准答案集”;新标注员先标完全同一个视频,跟标准答案对比,一致率低于95%就继续培训;正式标注阶段每天抽检10%的已完成任务,如果某标注员的抽检错误率连续三天超过3%,就暂停让其重新学习规范。
2.3 数据增强与样本平衡的实战取舍
行为识别数据集天然存在严重的类别不平衡。学生大多数时间是“专注”状态,分心和趴桌只占很小比例,教师端的“讲授”也远超“暂停”。如果直接训练,模型会对高频类别过拟合,低频类别几乎学不到。解决不平衡有几个常规手段,我都试过,但有一个经验尤其值得分享:针对低频率行为类别专门采集“补拍数据”。比如趴桌行为平时出现频率低,就让班级配合在班会课上做一些模拟趴桌、看手机的动作,同时混杂正常行为,让标注员按真实场景标注。这样比单纯在训练时做类别权重加权效果稳定得多,因为欠的是视觉特征本身,不只是样本数量。
数据增强层我强烈建议保留Mosaic和Copy-paste,但课堂场景使用Mosaic要克制。YOLO训练中常用的Mosaic增强会把四张图拼成一张图,小目标密度会增加,但拼接产生的硬边界会让模型学到不真实的上下文信息,比如一个被拼贴出来的“学生”上半身来自A教室、下半身来自B教室,模型在推理真实完整画面时反而可能感到混乱。实践下来,我们把Mosaic概率调到0.3以下,同时配合随机亮度、饱和度扰动来模拟不同教室光线变化。增强的目的是让模型理解真实世界的多样性,而不是让它见过太多不存在于真实世界的合成画面。
3. 模型训练与专注度评估方案
3.1 数据预处理:抽帧、清洗与目标尺寸分析
原始视频不能直接全部送进训练,需要先做抽帧。我们根据动作发生的节奏确定抽帧间隔为2秒一帧,因为像转头、举手这类行为最短持续也在1秒以上,2秒一帧既能捕获主要行为,又不会产生过多高度相似的相邻帧。一个45分钟的课堂视频,抽下来约1300帧,以720p分辨率存储,标注工作量约12人天。训练数据总量控制在3万帧左右,覆盖约30节不同教室、不同年级的真实课堂录像。
清洗阶段有一个被很多人忽略的环节:截图查重。视频相邻帧高度相似,如果不做去重,模型会反复看到几乎相同的画面,验证集上指标虚高,真实场景泛化能力下降。我们对抽出的图像算感知哈希和直方图距离,阈值内判为相似帧,每批只保留一张。清洗后训练集实际减少了约15%,但训练收敛速度明显变快。
训练前还要做一步目标尺寸统计分析。我写脚本统计了所有标注框的长宽和像素面积,发现学生目标框平均宽度约28像素,高度约52像素,放在640×640的输入分辨率下,属于典型的小目标。这直接指导了两个决策:训练输入分辨率从默认的640提升到960,以保留更多小目标细节;Anchor尺寸根据统计结果重新聚类,得到适合课堂目标宽高比的先验框。
3.2 训练参数配置与实验记录
模型训练选了与YOLO26内部结构匹配的一组超参数,这里给出一份可以直接参考的基准配置。需要说明的是,不同显存容量下需要适当缩放batch size,表格中的参数是单卡24GB显存跑出来的经验值。
| 配置项 | 参数值 | 说明 |
|---|---|---|
| 输入分辨率 | 960×960 | 提升小目标召回率 |
| batch size | 16 | 与梯度累积配合 |
| 初始学习率 | 0.001 | 配合余弦退火 |
| 动量 | 0.937 | YOLO系列常用 |
| 权重衰减 | 0.0005 | 防止过拟合 |
| 训练轮数 | 200 | 早停机制在150轮触发 |
| Anchor | 自适应聚类 | 按课堂目标宽高比生成 |
| Mosaic增强概率 | 0.3 | 课堂场景避免过度拼贴 |
| 类别权重 | 低频行为×2 | 缓解类别不平衡 |
训练过程中我坚持每天记录一组验证集指标曲线,重点关注mAP50和mAP50-95的差值。差值过大说明模型虽然能检出目标但定位质量差,这在课堂场景会直接影响跟踪效果。最终模型在自建测试集上mAP50达到0.93,mAP50-95约0.61,单帧推理耗时在边缘设备上约22毫秒,具备了上线条件。
3.3 从检测结果到专注度分数的计算逻辑
这是整个项目里最有价值的一块逻辑设计。YOLO26输出的只是“这一帧里某个人在干什么”,但课堂评估需要的是一个可比较的分数。我们决定以分钟为基本评估窗口,对单条行为序列做聚合计算。
基础公式设计如下:对每个学生,统计一个时间窗口内的总有效帧数T(排除被遮挡和未检出目标帧),其中专注行为帧数为F,阅读/书写等中性但仍在学习状态的行为帧数为R,分心行为帧数为D,使用手机帧数为P。专注度得分S的计算方式为:
S = (F + 0.6×R - 0.5×D - 1.0×P) ÷ T × 100
这个公式的权重设定不是拍脑袋,而是跟一线教师访谈后做的量化映射。R这类“做题、看书”行为虽然头部未朝向黑板,但属于正常学习状态,权重取0.6;D类包括交头接耳和左顾右盼,扣0.5;P类使用手机是课堂管理中最不希望出现的越界行为,直接扣1.0。最终得分范围在-100到100之间,再统一线性映射到0到100分展示。
举个具体例子:某学生在一个5分钟窗口里被有效跟踪到300帧,其中专注帧180帧,做题阅读帧30帧,分心帧60帧,看手机帧30帧。计算结果是:
S = (180 + 0.6×30 - 0.5×60 - 1.0×30) ÷ 300 × 100 = (180 + 18 - 30 - 30) ÷ 300 × 100 = 138 ÷ 300 × 100 = 46分
如果只看“专注帧占比”,该生专注度是60%,但用加权公式后是46分,更真实地反映了既有做题也有分心还有看手机的复杂状态。教师端评估则聚焦在状态时长和活动轨迹:统计教师在一节课中讲授、板书、巡视分别占总课时的百分比,再结合YOLO26输出的教师框中心点轨迹绘制整堂课的走动热力路线。
4. 实操过程与核心环节实现
4.1 推理链路的关键细节
从模型推理到前端展示,中间有几个工程细节决定了系统能不能稳定跑起来。第一个问题是抽帧频率和检测频率的配合。我们不做每帧检测,而是每2秒抽一帧送入模型,其余时间留给跟踪和业务逻辑。抽帧模块用独立线程从视频流读取,通过带缓存的队列把帧推给推理进程,避免视频流阻塞把检测进程卡死。
跟踪模块采用ByteTrack方案,它在检测置信度较低的场景下表现比纯Sort稳定得多,尤其适合课堂这种人员交叉走动频繁的场景。跟踪拿到稳定的ID序列后,行为判定模块会为每个活跃ID维护最近10帧的类别记录,采用“滑动窗口投票”决定当前行为:如果最近5帧中有3帧以上输出“专注”,则当前行为判定为专注,这样可以有效避免单帧误检造成的抖动。
数据入库选用轻量的时序数据库。每5分钟写一条汇总记录,字段包括班级、课程、学生ID、专注度得分、行为分布、教师状态统计。报表服务从库中拉取数据后渲染成课堂看板,教师端主要看自己和全班学生的专注度曲线,教务端看全校的横向对比。
4.2 密集小目标与遮挡难题的工程处理
教室全景画面里一个学生可能只有二三十像素高,即使输入分辨率提到960,直接检测仍然不够稳。我试过几类方案,最终稳定下来一套组合拳。首先做推理时的两阶段策略:先用低分辨率整图跑一遍,定位出可能存在学生的区域(通常是教室中后排座位范围),然后在目标密集区域做局部裁剪放大再跑一次精细检测,把两个阶段的检测结果合并。这样整图推理和局部推理配合,能明显提升小目标召回,代价是推理耗时增加,但经过TensorRT优化后仍能控制在40毫秒左右。
遮挡问题的核心矛盾是“看不见就没法判断”。对于被前排同学遮挡的学生,工程上选择诚实处理:不猜,标记为遮挡/未知,在专注度统计中不计入有效帧。这一点尤其重要,因为很多不靠谱的产品会硬猜结果,导致老师看到荒谬的数据后对整个系统失去信任。系统设计时就要明确告诉用户:被遮挡时段的专注度是缺失的,不是0分。
4.3 部署优化与实时性平衡
教室边缘设备通常只有入门级算力,必须做模型压缩。压缩路径上,我推荐先用TensorRT做FP16推理,效果通常已经足够;如果设备算力更差,再考虑INT8量化。但INT8量化在行为识别场景要非常小心,因为趴桌、举手这类小目标动作细微区别可能被量化误差抹掉。我们实际测试INT8后mAP下降了约6个百分点,表现为趴桌和低头看书互相混淆明显增多,最终回退到FP16方案。
实时性的另一个重要优化点是“动态帧率”。白天上课时学生动作频繁,系统自动把抽帧间隔缩短到1秒;晚自习或考试场景学生动作极少,间隔拉长到5秒也不会漏掉关键行为。这个策略让边缘设备的平均负载下降约一半,发热和风扇噪音问题也缓解了。
5. 常见问题与排查技巧实录
5.1 学生目标漏检和重复检测
现象:后排小目标经常没有被框出来;前排学生被重复画了两三个框。
排查思路:先看单帧检测结果图,用小目标可视化工具把预测框叠加在原图上,确认是图像太小还是同类目标之间互相抑制。如果是后一排学生被前排误框重叠覆盖,大概率是NMS的IoU阈值设得太高导致邻近框互相抑制,把IoU从0.7下调到0.5能缓解。如果整片区域都没框,说明该区域特征在训练数据中出现不足,需要补充对应机位的数据。
另一个容易忽略的问题是摄像头机位变化。教室里的摄像头有时会被调整位置,哪怕角度偏了15度,检测效果就会明显下降。对策是训练数据中专门保留一部分“视角扰动”增强,对图像做小角度的随机旋转和透视变换。
5.2 行为类别混淆的边界划分
现象:趴桌被识别成低头看书,看手机被识别成翻书。
这类混淆根源在视觉特征接近。低头看手机和低头看书在二维画面上都是“头低下来”,如果不考虑手腕动作和目标物体,确实无法区分。我们的处理方式不依赖模型强行分类,而是引入时间信息:看书会有翻页、握笔等周期性动作,而看手机时头部通常静止且持续低头;结合头部关键点和手部区域位置做二次判断,手腕区域与头部靠近且持续静止,才判定为看手机。
边界规则一定要清楚:视觉判定只负责输出最可信的类别,真正区分“趴桌”和“低头看书”这样模糊边界的,是后续状态逻辑。比如趴桌行为要求头部框与桌面预置区域重叠面积超过70%,并持续超过3秒才判定;不足3秒的低头动作一律归为阅读或专注。
5.3 多人ID跳变
现象:课堂录像中同一个学生在不同时间段被分配了不同的跟踪ID,导致专注度统计断裂成好几段。
长时段监控中的ID跳变几乎无法完全避免,尤其学生前后排互相遮挡后重新出现。工程上能做的是让ID分配更稳定:跟踪模块在遮挡前后加入“外观特征缓存”,记录每个人的表观特征向量,遮挡结束后通过特征相似度找回原ID,降低跳变率。另一个有效措施是统计阶段不完全依赖ID连续性,而是用位置先验做修正——如果新出现的ID位置和上一段时间某消失ID的位置重合度高,就认为属于同一个目标。
5.4 多人标注口径不一致
现象:训练前检查标注文件,发现两名标注员对同一批图像的行为类别标注重合率只有88%,远低于95%的合格线。
这种问题不能靠事后返工解决,必须在标注流程上做前置控制。我们制作了一份标注决策树文档,目录包括行为优先级、边界案例说明、标注框范围定义、遮挡处理规则。另外搭建了争议讨论群,标注员遇到拿不准的截图直接发群里投票决定,每天的争议案例汇总后定期更新到决策树文档里。坚持两周后,重合率稳定在96%以上,质量抽检的错误率大幅下降。
做数据分析的还容易碰到标注文件类别分布与真实课堂分布不一致的情况。有的标注员倾向于多标分心行为,导致训练集中分心类别占比虚高,模型上线后反而把很多正常专注的学生误判成分心。这类问题通过训练后的类别混淆矩阵可以提前发现:如果专注类被明显误判成分心,就要检查训练数据是否行为分布失真。
做完这个项目,我最大的体会是:课堂行为识别表面上是“训练一个模型”,实际上拼的是数据质量、行为规则的细节设计、以及边缘端的工程调优能力。模型检测精度做到90分很容易,但从90分到能真实辅助教学管理,中间隔着的是对课堂行为的深度理解和对不可控场景的大量防御性设计。如果你想在这个方向继续深入,我建议重点关注两件事:一是构建自己的课堂行为数据集,过程很苦但价值极大;二是多跟一线老师沟通,他们眼中的“专注”和算法眼中的“头部朝向黑板”之间,需要靠精心设计的权重公式来弥合。