恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
微表情识别实战:CASME2模型部署与摄像头实时推理源码解析
首页
资讯中心
/
微表情识别实战:CASME2模型部署与摄像头实时推理源码解析
微表情识别实战:CASME2模型部署与摄像头实时推理源码解析
发布时间:2026/10/12 0:58:42
简介基于CASME2数据集训练的微表情识别项目支持摄像头实时识别、图片与视频检测主要面向需要完成毕业设计、期末大作业或课程设计的计算机专业学生。资源包含完整Python源码与详细文档说明代码带有注释部署简单系统界面完善、操作便捷适合快速上手与二次开发。压缩包共43个文件以Python脚本为主22个py同时包含模型权重h5、人脸检测xml配置、测试视频avi、说明文档md/txt及少量缓存pyc覆盖数据预处理、模型训练、效果评估与识别推理全流程整体大小约60.75MB目录结构清晰易检索。已有371人学习下载。开发者可从中获得一套可直接运行的微表情识别方案包括基于VGG16、ResNet50、MobileNet等网络的训练与预测代码摄像头调用脚本图片和视频识别示例预训练权重及项目使用说明既能满足毕业设计或课程作业的验收要求也便于后续扩展与算法研究。1. 拿CASME2训练好的微表情识别模型直接怼摄像头源码能让你少走三个月弯路微表情识别在情绪分析里一直是个黑匣子数据集难找、样本量小、标注口径还不统一。很多人在CASME2上折腾几个月模型训练完一上真实摄像头就翻车不是检测框乱飘就是全程输出others。这套基于CASME2数据集训练好的微表情识别源码自带图片、视频、摄像头三种检测入口适合正在做毕业设计、期末大作业或者想快速跑通一条微表情视觉管线的从业者。它解决的是两个最现实的问题不用从零收集数据打标注也不用自己搭训练流程解压后改改路径就能跑通推理。但先说清楚它不是一个开箱即用的商业产品而是一份能让你在真实摄像头场景下把微表情识别跑起来的工程样本。坑不少我下面逐一拆。2. CASME2数据集与技术选型样本为什么这么小模型该往哪个方向调CASME2几乎是微表情识别绕不开的基准数据集。很多人之前用CK、FER2013跑普通表情识别特别顺换到微表情上立刻失灵。原因是微表情本身是低强度、短时程的面部动作普通表情数据集的纹理差异比微表情大一个量级模型很容易学到“夸张表情”的表象而完全抓不住微妙的面部运动变化。所以圈子里做微表情识别第一件事不是堆模型而是先把CASME2这类自发微表情数据集的特性吃透。2.1 CASME2标注体系里的三个关键事实CASME2给我的直观感受就一句话标注做得比普通表情数据集精细得多但样本数量少到让人不敢乱用大模型。第一个关键事实是它的样本规模非常小全部有效微表情片段只有两百段上下三十五名被试。这个体量放在深度学习的标准来看连一个类别的样本都不够塞牙缝。所以训练策略必须围绕小样本设计预训练权重、数据增强、时序裁剪缺一不可。第二个关键事实是它原生帧率很高视频帧率在200fps左右但单个片段的时长很短一个微表情一般只有几十到一两百帧。换算成时间就是零点二秒到零点五秒。这意味着模型不能只盯着单帧看还要能捕捉帧与帧之间的短时变化输入设计必须带上时间维度。第三个关键事实是它的情绪标签以happiness、disgust、surprise、repression为主剩下的标成others作为背景兜底。很多人训练时图省事把others直接删掉这是个大坑。删除后模型确实在训练集上准确率高一旦拿到摄像头场景没有微表情的普通画面会全部乱识别。因为模型没见过“什么都不算”的样本只能硬挑一个表情出来。CASME2官方标注表里常见的字段包括被试编号、情绪类别、微表情起止帧、AU动作单元编号等。我一般会先把表读进来看一眼分布再决定怎么裁窗。import pandas as pd from pathlib import Path coding_path Path(data/CASME2/CASME2-coding.csv) df pd.read_csv(coding_path) # 剔除“others”样本会让模型失去背景判别能力训练时保留 # 但统计类别分布时可以单独看 print(df[Emotion].value_counts()) # 查看每个样本的起止帧字段名不同版本命名可能不一样 print(df.columns.tolist()) print(df.head(3))逻辑说明先用pandas读入标注CSVvalue_counts用来确认情绪类别分布避免某一类样本数少到让训练根本收敛不了。columns打印是排查习惯因为网上流传的CASME2标注表有多个重命名版本字段名可能是Onset/Offset也可能是Apex等先看清楚再写后面的切片逻辑。参数说明这里假设压缩包里的标注文件叫CASME2-coding.csv如果实际文件名不同改成你自己的路径。Emotion列是情绪标签Onset和Offset两列决定了一个微表情片段从哪一帧开始、哪一帧结束。2.2 为什么3D卷积比“单帧分类”更适合微表情普通表情识别里一张静止的脸加上纹理差异就能分类所以ResNet、MobileNet这类2D模型直接套用也能出活。微表情不是这样。一个微表情在单帧画面里可能只是嘴角轻微抽动人眼都要反复看才能确认单帧CNN几乎不可能学到有效特征。因此工程上更常见的做法是使用3D卷积或者先提取光流场再喂给2D CNN。这套源码默认采用3D-ResNet18结构输入是16帧的连续人脸序列每帧112×112最后输出5类概率。为什么不是视频TransformerCASME2总共才两百多段样本Transformer这类模型非常吃数据没有大规模预训练的情况下很容易陷入过拟合。3D-ResNet18的时序归纳偏置更强参数规模小配合在普通视频数据上预训练过的权重在小样本微表情任务上更稳。这也是源码里选择3D-ResNet18而不是I3D或SlowFast的原因后者在行为识别上很强但在这种微型数据集上性价比太低。方案输入形式所需数据量CASME2上的表现推理速度单帧2D CNN一张脸大容易过拟合类间混淆严重最快3D-ResNet1816帧序列中小稳定可解释性好快光流2D CNN光流图堆叠中对运动敏感但光流提取耗时中等视频Transformer16~32帧序列大不微调预训练权重基本跑不动慢从实际部署角度3D-ResNet18还有一个好处它可以直接把视频片段变成一个张量输入不需要额外跑光流算法。光流法有一个很麻烦的问题OpenCV的Farneback光流在低分辨率下噪声大在真实摄像头场景里帧率波动一厉害光流图就花掉。后面对摄像头实时推理时光流法会明显拖后腿。2.3 数据加载与事件片段采样训练前的数据加载不是把整段视频直接塞进模型而是按标注的起止帧裁剪。常见错误是把整个视频文件全读了中间大量中性帧会把模型带偏训练出来loss降了实际识别却不对。正确做法是定位到Onset到Offset之间从这段有效区间里均匀抽16帧。import cv2 def sample_frames(video_path, onset, offset, frame_len16, size(112, 112)): frames [] cap cv2.VideoCapture(video_path) total_frames int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) # 防止标注越界CASME2偶尔有起止帧超出实际帧数的情况 onset max(0, onset) offset min(total_frames - 1, offset) # 在有效区间内均匀取frame_len帧而不是连续取前16帧 indices np.linspace(onset, offset, frame_len, dtypeint) frame_index -1 while True: ok, frame cap.read() if not ok: break frame_index 1 if frame_index in indices: frame cv2.resize(frame, size) frames.append(frame) if frame_index offset: break cap.release() return frames逻辑说明通过linspace在起止帧之间均匀采样保证一个微表情的完整动作过程都能被覆盖。如果直接取前16帧或后16帧很容易只截到动作刚开始或者已经结束的画面。这里还用了一个小保护逻辑如果标注里的offset超过视频总帧数就把越界帧掐掉。参数说明frame_len是输入到3D模型的序列长度源码训练时固定为16。size是缩放尺寸112是3D-ResNet的常见输入尺寸。如果摄像头采集到的图片比较大推理阶段同样会先缩放到这个尺寸再进模型否则会报维度错误。3. 环境搭建与源码目录先把推理跑通再决定碰不碰训练很多同学下载完源码第一件事就是跑train.py结果跑了两小时发现环境依赖崩了。我的建议恰恰相反先跑通图片推理再跑视频最后接摄像头。因为推理链路短出问题容易定位训练链路长一上来就让一堆超参数和CUDA版本打架心态很容易炸掉。3.1 源码目录结构哪些文件是推理必需的解压后的目录结构大致是下面这张表。注意每个工程不完全一样以你压缩包里的实际结构为准。文件/目录作用是否推理必需configs/config.yaml模型路径、置信度阈值、输入尺寸等配置必需checkpoints/casme2_best.pthCASME2训练好的权重文件必需models/recognition.py模型定义与推理封装必需utils/preprocess.py人脸检测、对齐、图像缩放必需test_image.py单张图片推理入口必需test_video.py视频文件推理入口必需test_camera.py摄像头实时推理入口必需train.py训练脚本可选data/CASME2/数据集与标注CSV训练必需推理可跳过我拿到源码后的第一个建议是先确认checkpoints目录下有没有权重文件。很多网上下载的源码包会把权重文件单独放网盘压缩包内只有一个空的目录此时直接跑推理会报文件不存在。3.2 创建Python环境与安装依赖这套源码基于Python 3.8和PyTorch 1.13开发OpenCV版本建议4.8以上。以下命令在Linux和Windows都可以用只是conda激活命令略有差异。conda create -n micro_expr python3.8 -y conda activate micro_expr cd micro_expression_source pip install -r requirements.txtrequirements.txt里至少应该包含这几项但具体版本号以你包里给出的为准。numpy1.24.4 opencv-python4.8.1.78 pandas2.0.3 torch1.13.1 torchvision0.14.1 pyyaml6.0逻辑说明先用conda隔离一个干净环境避免把系统里的其他Python项目搞坏。pip install会一次性装齐依赖但如果你的机器没有NVIDIA显卡torch需要改成CPU版本否则装完也不一定能用。安装成功与否直接决定了后续所有步骤。参数说明torch和torchvision版本必须搭配不能一个高一个低。conda创建环境时的python3.8是兼容性最稳的选择更老或更新版本的Python在编译部分依赖时可能报错。3.3 权重文件放置与config.yaml调整推理前先把权重文件放到checkpoints目录下然后打开configs/config.yaml看一眼。默认配置大概是这样的model: backbone: resnet18_3d num_classes: 5 checkpoint: checkpoints/casme2_best.pth input_size: 112 seq_len: 16 infer: confidence_threshold: 0.7 use_sliding_window: true device: cuda:0逻辑说明checkpoint字段指向权重文件的实际路径路径写错或者文件位置不对模型加载第一步就会抛FileNotFoundError。num_classes和训练时的类别数必须一致源码里用5对应happiness、disgust、surprise、repression、others五类如果读者自己重新训练时改成4类这里也要同步改。参数说明confidence_threshold是置信度阈值默认0.7实际使用中可以根据场景调低到0.6或者调高到0.8。device字段改成cpu就能在无显卡机器上运行但3D卷积在CPU上推理速度会比较慢摄像头实时检测会有明显卡顿。use_sliding_window这个开关很关键它控制视频和摄像头推理时是用16帧滑窗还是只对当前帧做单帧分类建议始终开启。4. 图片、视频、摄像头三种输入全打通从本地测试到RTSP取流这是整套源码使用频率最高的部分。图片推理适合验证模型权重是否正常视频推理适合对录好的样本做批处理摄像头推理才是真实场景演练。三种模式共用同一个模型推理封装只是输入源和帧处理逻辑不同。4.1 图片推理人脸对齐与单帧置信度先跑图片因为最快、最容易排查。一般流程是读图、做人脸检测与对齐、裁剪人脸区域、缩放后送入模型。import cv2 from utils.preprocess import align_face from models.recognition import MicroExpressionRecognizer # 加载模型这里会自动读取config.yaml里的checkpoint路径 model MicroExpressionRecognizer(configs/config.yaml) img cv2.imread(samples/surprise.jpg) # 1. 人脸检测 对齐检测不到人脸时返回None face, bbox align_face(img) if face is not None: # 2. 送入模型拿到标签、置信度和检测框 label, confidence, bbox model.predict_image(face, bbox) # 3. confidence低于阈值时不显示具体表情直接显示“normal” if confidence 0.7: cv2.putText(img, f{label} {confidence:.2f}, (bbox[0], bbox[1] - 5), cv2.FONT_HERSHEY_SIMPLEX, 1.0, (0, 0, 255), 2) else: cv2.putText(img, normal, (bbox[0], bbox[1] - 5), cv2.FONT_HERSHEY_SIMPLEX, 1.0, (0, 255, 0), 2) cv2.imwrite(output/surprise_result.jpg, img)逻辑说明align_face内部封装了人脸检测器可能用OpenCV自带的人脸级联也可能用MTCNN具体看util文件里的实现。这一步非常关键CASME2训练样本全部是对齐后的人脸推理时不对齐就送进模型准确率会直线下降。输出的label是字符串类别名confidence是模型softmax后置信度bbox是原始图像上的人脸框坐标。参数说明0.7是硬编码的阈值这里和图片模式完全一致。如果想在图片上看到更多结果可以降为0.5但会出现大量误检。请记住一个规律微表情识别不是随便就能高置信度输出的任务图片上如果连续几张都是normal不代表模型坏而是真实场景中大多数画面本来就不包含微表情。4.2 视频推理滑窗16帧比单帧靠谱视频推理不能像图片那样逐帧独立判定。刚才说过微表情是一个短时动作单帧判别会把微表情和普通面部抽动混在一起。源码默认使用滑窗模式每次取连续16帧窗口每次向后移动2帧保证一段微表情至少被多个窗口覆盖。import cv2 from models.recognition import MicroExpressionRecognizer model MicroExpressionRecognizer(configs/config.yaml) cap cv2.VideoCapture(samples/test.mp4) out cv2.VideoWriter(output/result.mp4, cv2.VideoWriter_fourcc(*mp4v), 30, (int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)), int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)))) buffer [] window_size 16 stride 2 while True: ok, frame cap.read() if not ok: break # 先检测人脸并归档到buffer face, bbox model.detect_face(frame) if face is not None and len(buffer) window_size: buffer.append(face) # 凑够16帧后做一次推理 if len(buffer) window_size: label, confidence model.predict_sequence(buffer) if confidence 0.7: cv2.putText(frame, f{label} {confidence:.2f}, (bbox[0], bbox[1] - 5), cv2.FONT_HERSHEY_SIMPLEX, 1.0, (0, 0, 255), 2) else: cv2.putText(frame, normal, (bbox[0], bbox[1] - 5), cv2.FONT_HERSHEY_SIMPLEX, 1.0, (0, 255, 0), 2) # 从头部移除stride帧保持滑窗不断向前 del buffer[:stride] out.write(frame) cap.release() out.release()逻辑说明滑窗的核心是buffer这个列表。它保存最近的人脸序列当长度等于16时执行一次3D推理然后从头部删掉2帧继续等待新的帧进来。这样一段视频中每个16帧片段都会被评估不会漏掉中间那个微表情。参数说明window_size必须和config.yaml里的seq_len保持一致否则predict_sequence会报维度错误。stride设置得越小窗口重叠越多检测越密集但计算量也越大。实时摄像头场景下不建议把stride设为1CPU会直接跑满。输出视频用VideoWriter保存编码格式选mp4v是为了兼容性拉流到VLC不会花屏。4.3 摄像头推理本地USB摄像头与IPC的RTSP取流摄像头推理和视频推理几乎一样只是把VideoCapture的输入从文件路径换成设备索引或RTSP地址。本地USB摄像头直接写0或1网络摄像头则需要先确认RTSP取流地址。import cv2 # 本地USB摄像头0代表系统默认摄像头 # 在Linux下如果报错先执行 ls /dev/video* cap cv2.VideoCapture(0) # 网络摄像头以海康威视为例把下面地址换成自己的设备参数 # rtsp://用户名:密码IP地址:554/Streaming/Channels/101 # cap cv2.VideoCapture(rtsp://admin:admin123192.168.1.64:554/Streaming/Channels/101) # 设置分辨率保持与config.yaml输入尺寸协调 cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480) while True: ok, frame cap.read() if not ok: break # 这里复用视频推理的滑窗逻辑略 label, confidence run_sliding_window(frame) cv2.imshow(micro expression, frame) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()逻辑说明本地摄像头直接拿OpenCV读取即可但要注意摄像头是否被其他程序占用。网络摄像头则完全不同RTSP地址里有账号密码、IP、端口和通道号任何一个地方写错都打不开。这里用注释保留了一个海康地址示例实际使用时换成自己手头的摄像头参数。参数说明RTSP地址中最后一个101值得细讲。101的第一位1表示主码流第二位01表示第一路通道102就是子码流。主码流分辨率高画面清晰但延迟大、解码开销高子码流分辨率低适合实时推理。我自己做微表情识别时习惯优先用子码流因为识别任务对图片分辨率没有那么敏感反而对帧率和延迟更敏感。如果你的摄像头是树莓派CSI摄像头比如OV5647模块OpenCV的VideoCapture(0)经常打不开需要先执行这类命令把CSI摄像头注册成V4L2设备再试操作方式跟USB摄像头不同。源码本身不区分摄像头类型但Linux驱动层绕不过去。4.4 实时性取舍主码流、子码流与帧率下限微表情持续时间短30fps基本是最低要求。如果摄像头只有15fps一个零点三秒的微表情只能拍到四五帧滑窗16帧要凑一秒多识别结果基本没有实用价值。所以摄像头选择上优先支持30fps以上的设备。摄像头类型帧率推荐配置备注USB摄像头30fps640x480子码流便宜直接用海康网络摄像头25/30fpsRTSP子码流102延迟比USB高树莓派OV564730fpsV4L2转换需加载内核模块手机虚拟摄像头15~30fps仅调试用延迟不稳定为什么我不推荐主码流网络摄像头主码流通常是1080p甚至4K视频解码就已经占用大量CPU3D卷积在剩下的算力里根本跑不动。另外OpenCV对某些摄像头的H.265主码流支持很差读出来的画面花屏怎么调参数都无效。换成H.264子码流以后问题通常立刻消失。官方说的“支持摄像头”当然是指能接但能不能实时跑出结果完全取决于摄像头配置和取流用的码流通道。这个取舍比模型本身的推理速度更影响最终体验。5. 避坑与常见问题CASME2推理最容易翻车的五个现场我把这套源码在实战中高频踩过的坑整理成五条。每一条都是先讲现象再讲原因最后给出能落地的解法。这些坑不解决你会反复在“为什么报错”和“为什么结果不对”之间横跳。5.1 摄像头打不开或画面全黑先区分权限、占用和取流地址现象程序运行不报错但VideoCapture.read()永远返回False或者弹出一个黑窗口什么都看不到。如果用的是网络摄像头经常卡在拉流阶段。原因本地摄像头打不开通常是权限或占用问题。Linux下用户没有加入video组或者摄像头被另一个Python进程占用。网络摄像头打不开绝大多数是RTSP地址不对或者该通道不需要密码认证用户名密码却带了一大串。解决Linux先执行ls /dev/video*确认设备节点存在再执行sudo apt install v4l-utils v4l2-ctl --list-devices看摄像头是否被系统识别。被占用时用lsof /dev/video0找到占用程序并退出。网络摄像头先用VLC输入RTSP地址测一下能否播放VLC能播而源码跑不通问题出在OpenCV版本对RTSP协议的支持上建议改用FFmpeg拉流再送OpenCV处理。5.2 模型一直输出others阈值和背景类在作怪现象不管是图片、视频还是摄像头识别结果永远是others或normal几乎看不到具体表情类别。原因两个因素叠加。一是置信度阈值设得太高比如0.9微表情本身的特征强度就不高很难达到这个分数。二是训练时保留了others作为背景类模型对哪些脸不属于四类表情的判断过于保守在真实场景中把大部分中性表情都归到others。解决先把config.yaml里的confidence_threshold降到0.6再跑图片测试。如果还是全程others检查一下训练时others样本占的比例。比例过高会把模型“带偏”让它认为全世界都是背景类。一个可行做法是限制others样本数量在训练时随机采样让others和其他类别比例接近1:1而不是有多少放多少。这个问题在摄像头场景下比视频文件更严重因为视频文件至少是精心录制的片段摄像头里大量中性画面会持续冲击背景类概率。5.3 视频推理跳帧导致检测框错位OpenCV读高帧率视频的坑现象用CASME2原始视频或手机高帧率视频测试时画面一卡一卡检测框位置明显对不上人脸。视频检测框时而框在眼睛上时而框在嘴上。原因OpenCV读取高帧率视频时底层解码策略在不同版本上差别很大。特别是200fps这种高帧率MV逐帧读取可能导致丢帧读取到的图像时间和实际帧推进时间错位。人脸检测本身也有耗时上一帧的检测框还没算完下一帧已经读进来了就会导致位置滞后。解决不要直接用OpenCV逐帧读先用FFmpeg把视频抽帧成图片序列再按抽帧结果送入检测流程。或者在读取时记录PTS时间戳按时间戳而不是按帧顺序做对齐。对于CASME2这种高帧率数据我的习惯是先跑ffmpeg -i input.mkv -vsync 0 frame_%05d.png把帧抽出来再做推理不做实时处理。这样虽然慢一点但检测框不再乱跳。5.4 训练loss降了但验证崩没有按Onset/Offset裁剪现象训练脚本跑得很快训练集loss降到很低验证集却一塌糊涂。有些看起来已经收敛的模型到推理阶段对真实视频完全无效。原因这是微表情训练最容易翻车的地方。很多人直接把整段视频当作一个样本送进网络但一个视频里百分之八十都是中性帧模型学到的是“脸不动”这个特征。真正微表情发生时只占几十帧淹没在大量中性帧里模型根本没有机会学会区分微表情类别。解决严格按照标注CSV里的Onset和Offset字段裁剪训练样本只把微表情发生区间拿出来做训练。之前2.3节的sample_frames函数就是干这个的。如果某些样本标注缺失宁可直接丢弃也不要整段视频硬上。验证集也要用同样的裁剪逻辑否则评估指标没有意义。5.5 网络摄像头RTSP花屏延迟主码流和子码流选错现象海康或其他品牌网络摄像头接入后画面能显示但延迟两三秒图像还有马赛克。反复尝试降低推理帧率仍然没用。原因RTSP地址默认用了主码流。主码流在监控回放场景下很合适因为需要看清人脸细节但在微表情识别场景下反而拖后腿。OpenCV解码H.265主码流的性能本身就差加上3D卷积的推理模型整个管线被解码卡死。解决把RTSP地址从101换成102走子码流或在摄像头Web后台把主码流分辨率调低。子码流默认是4CIF或720p解码快很多。如果花屏依旧关掉摄像头的H.265编码改H.264OpenCV兼容性会好很多。这个坑很容易被忽略因为“画面能出来”让人误以为取流没问题实际上延迟已经让实时微表情检测失去了意义。6. 进阶阈值校准与自定义数据扩展让模型在新场景下少瞎猜当你把图片、视频、摄像头三种模式都跑通以后剩下的工作不是增加训练数据量而是校准阈值和扩展自己的场景数据。这是我从实际项目里总结出的最有效优化方式。6.1 校准置信度阈值CASME2是实验室环境录制画面背景单一表情强度相对高。换成办公室、教室或者路边场景后光照、距离、遮挡都会让置信度整体下降。此时需要做一次阈值扫描找到最适合当前场景的数值。import numpy as np from models.recognition import MicroExpressionRecognizer model MicroExpressionRecognizer(configs/config.yaml) def calibrate(loader, model): best_threshold 0.7 best_f1 0.0 for threshold in np.arange(0.5, 0.95, 0.05): preds, labels [], [] for batch in loader: pred_prob, label model.predict_sequence(batch[frames]) preds.append(int(pred_prob threshold)) labels.append(label) f1 compute_f1(labels, preds) if f1 best_f1: best_f1 f1 best_threshold threshold return best_threshold逻辑说明这个函数遍历0.5到0.95的阈值每个阈值下统计预测标签与实际标签的F1分数最终返回F1最高时对应的阈值。它解决的是“用官方默认阈值0.7在新场景里表现很差”的问题。微表情任务样本不均衡只用准确率选阈值会被多数类骗F1更稳。参数说明阈值扫描步长0.05够用如果时间充裕可以改成0.01精细扫描。loader自定义数据集的输出需要有frames和label两个字段align_face已经在前面做过loader只需要负责把对齐结果包装成批次即可。6.2 把新场景样本续进训练集如果阈值调完还是误报严重就说明当前模型在你的场景里确实没见过类似的光线或表情强度。此时我不建议完全重新训练而是把新采集到的高置信度样本少量加入原有训练集做最后的微调。常见做法是固定3D-ResNet18的前几层只微调最后一两个全连接层学习率设成原有训练时的十分之一。# 伪代码示意只微调分类头 for name, param in model.named_parameters(): if fc in name: param.requires_grad True else: param.requires_grad False逻辑说明微表情数据集本来就小如果全量微调新场景样本很容易把原有CASME2知识冲掉。只训练分类头让backbone继续保留之前学到的时序运动特征只在最后一层适应新场景的分布偏移。从那以后我每次拿到新摄像头都会强制自己走一遍“取流测试、阈值扫描、采集少量新样本微调”这条流程不再相信默认配置能通吃所有场景。希望帮到你也希望这份源码能成为你微表情识别项目的稳定起点而不是又一个解压完就跑不起来的收藏品。本文还有配套的精品资源点击获取