
简介基于深度学习ResNet与AVEC2014数据集的抑郁症诊断系统源码包提供完整Python源码、运行说明和数据集下载地址面向AI医疗或计算机视觉方向的中级开发者也适合需要复现情感计算与人脸表情识别项目的学习者。资源包为zip格式共11个文件以9个Python脚本为核心另有txt与md文档用于环境依赖和项目说明压缩包仅8KB便于快速获取。目前已有314人学习。源码模块划分清晰依次涵盖视频抽帧与MTCNN人脸对齐裁剪、ResNet网络构建、数据加载与标签配对、训练验证测试全流程并接入TensorBoard记录损失可直接迁移至类似面部行为分析任务。随包附带的数据集地址和运行说明能有效降低复现门槛适合作为课程设计或科研入门参考。1. 从一段访谈视频预测抑郁量表分数这个系统到底在做什么来咨询的人刚落座摄像头就开始记录他的表情、语速和身体姿态。治疗师手里的BDI-II量表是一份主观问答但视频里那种持续的低唤醒、回避的目光接触和微表情迟滞往往比嘴上说的更诚实。基于AVEC2014数据集和Resnet网络实现的抑郁症诊断系统做的就是这件事拿一段访谈视频的帧序列用残差网络抽视觉特征再聚合回归出0到63的BDI-II评分。它不是替代医生诊断而是给临床筛查和纵向跟踪提供一个可复现、可量化的视觉信号基线。AVEC2014是音频视觉情感挑战赛的年度数据抑郁子挑战赛提供了上百段被试访谈视频每段视频对应一个由临床评估得到的BDI-II标签。系统落在工程上的核心是两段式pipeline先对视频均匀抽帧、人脸检测、裁剪再用ImageNet预训练的ResNet提取每帧的语义特征最后把整段视频的特征序列压缩成一个分数。适合谁来读做过图像分类、想往时序医疗AI或情感计算方向落地的工程师这个项目是一个把“图像分类”升级为“视频级回归”的完整样本。硬件门槛不高单张消费级GPU就能训练推理数据规模在千级样本不需要搭分布式。这套方案有一个反直觉的地方做回归预测的不是ResNet本身而是它背后那层“把200帧图像抽象成一个向量”的表征能力。ResNet在这里更像一把靠谱的特征提取器真正决定预测精度的是帧采样策略和时序聚合方式。下文按数据准备、特征提取、时序模型、踩坑记录和进阶验证五条线展开把一个能跑通的源码包拆成你自己能复现的方案。2. AVEC2014数据集落地下载、目录组织与标签解析2.1 先看懂抑郁子挑战赛的数据格式和评估口径AVEC2014抑郁子挑战赛的任务定义很直接给定一段被试与虚拟访谈者的互动视频预测视频结束后的BDI-II分数。BDI-II是Beck抑郁量表第二版总分范围0到63分数越高抑郁严重程度越高。官方把数据划分成三个互不相交的子集训练集、开发集和测试集。训练集和开发集的视频都带有公开的BDI-II标签测试集的标签不公开用于竞赛统一评测。离线复现时一般用训练集训练模型开发集做早停和调参再把最终模型在开发集上的MAE和RMSE作为性能报告口径。数据本身是多模态的官方发布包至少包含三部分视频文件MP4或AVI格式、音频轨道、以及人工转写文本。本系统只用视频帧一条模态但目录设计上仍建议按原始结构保留因为后续如果想融合音频特征不需要重新下载整理。每个视频的时长在20分钟到40分钟不等帧率各段不统一这就给抽帧策略留下了讲究空间后面的章节会专门讲。标签文件是CSV格式包含参与人ID、性别、年龄组和BDI-II分数。这里有一个新手常忽略的细节AVEC2014的标签是逐视频给出的不是逐帧给出的。也就是说一段20分钟的视频最终只对应一个分数。模型要做的是把整段视频的信息压缩成一个预测值这决定了数据管道后面必须有一个“序列到标量”的聚合环节而不像图像分类那样每张图一个标签。2.2 目录组织脚本先把原始包整理成模型能吃的结构拿到压缩包后第一件事不是写模型而是把原始文件整理成约定好的目录结构。常见的做法是下面这样子我用一个Python脚本完成解压后的整理避免手工拖拽出错。import os import shutil import pandas as pd RAW_ROOT raw # 官方压缩包解压后的根目录 OUT_ROOT dataset # 整理后的根目录 os.makedirs(OUT_ROOT, exist_okTrue) # 官方包里的典型结构train/dev/test 三个子目录每个目录下有视频和CSV for subset in [train, dev, test]: raw_subset os.path.join(RAW_ROOT, subset) out_subset os.path.join(OUT_ROOT, subset) os.makedirs(out_subset, exist_okTrue) # 视频文件统一改成 participant_ID.mp4 的命名方式 video_dir os.path.join(raw_subset, video) for vfile in os.listdir(video_dir): if vfile.endswith((.mp4, .avi)): src os.path.join(video_dir, vfile) dst os.path.join(out_subset, fparticipant_{vfile}) shutil.copy(src, dst) # 训练集和开发集的标签字段不同统一成 id, gender, bdi for subset in [train, dev]: label_path os.path.join(RAW_ROOT, subset, label.csv) df pd.read_csv(label_path) df df.rename(columns{Participant_ID: id, BDI_II: bdi}) df df[[id, gender, bdi]] df.to_csv(os.path.join(OUT_ROOT, f{subset}_labels.csv), indexFalse)这段脚本的逻辑很简单但有一个关键点视频文件名里的ID必须和CSV里的参与人ID精确匹配哪怕多一个空格后面对齐标签时都会报错。整理完之后花10秒钟跑一下下面这个检查确认数据完整性和标签分布。import pandas as pd import os OUT_ROOT dataset for subset in [train, dev]: labels pd.read_csv(os.path.join(OUT_ROOT, f{subset}_labels.csv)) video_ids set() video_dir os.path.join(OUT_ROOT, subset) for vfile in os.listdir(video_dir): pid vfile.replace(participant_, ).split(.)[0] video_ids.add(pid) label_ids set(labels[id].astype(str)) missing label_ids - video_ids print(f{subset}: 视频数量{len(video_ids)}, 标签数量{len(label_ids)}) if missing: print(f 缺失视频的标签: {missing}) print(labels[bdi].describe())逻辑说明第一段代码做的是物理拷贝而非移动保留原始包作为后悔药。rename字段统一了官方训练集和开发集的列名差异。第二段代码验证的是“每个有标签的ID都有对应视频”这是整个pipeline里最早的检查点也是后面一切结果的根基。参数说明raw_root和out_root是两个顶层目录建议放在同一个项目根下。gender字段当前用不到但保留下来后面做消融分析时可以验证“性别是不是调节变量”。bdi列是回归目标打印describe能看到数值范围正常情况下最小0、最大63左右如果有超出这个区间的脏数据立刻就能暴露。3. ResNet特征提取从视频帧到固定维度向量的转换3.1 为什么选ResNet而不是VGG或EfficientNetAVEC2014的视频帧经过人脸裁剪后本质上还是自然图像ImageNet预训练模型提取到的低级视觉特征——边缘、纹理、面部部件轮廓——可以直接迁移。在残差网络出现之前VGG靠堆深度提升精度但参数量巨大训练和推理都慢。ResNet引入残差连接让梯度可以跨层直接回传解决了深层网络退化问题。对于本系统这种数据量只有几百段视频的医疗AI小样本场景用ImageNet预训练的ResNet18或ResNet50做特征提取器是性价比最高的选择。ResNet18和ResNet50的取舍取决于显存和精度目标。ResNet18在最后全局池化后输出512维特征速度快显存占用低适合快速验证pipeline。ResNet50输出2048维特征语义更丰富但参数多如果显存只有6G批量抽帧时容易溢出。常见做法是先用ResNet18把整条链路跑通确认标签对齐、聚合训练和评估逻辑没有问题后再换ResNet50做最终实验。如果你从源码包里看到的是ResNet50配置也不要奇怪很多作者会用更大模型刷最终的MAE指标但对复现者来说跑通才是第一位。另外要提一下预训练模型的选择。直接用torchvision自带的ResNet权重即可不要去网上下载来路不明的pth文件。torchvision的resnet18(weightsResNet18_Weights.IMAGENET1K_V1)加载的是官方ImageNet权重的clean版本保证后续特征可比。选型时也不要一上来就加FPN或者自注意力先跑一个baseline后面发现聚合效果差再往上加模块。3.2 抽帧与对齐让每一帧的面部位置和大小一致性可控采集视频里被试通常坐在固定位置但头部会自然晃动。直接把整帧缩放后送进ResNet会带进大量背景噪声比如椅背、墙壁、别人的手。更稳的做法是先用OpenCV的DNN人脸检测器或MTCNN做检测然后以人脸框中心为基准向外扩30%后裁剪这样即使头部有小幅转动眼睛和嘴部区域仍然保持在裁切框内。import cv2 import torch def extract_frames(video_path, max_frames300): 从视频中均匀抽取最多 max_frames 帧返回 BGR 列表 cap cv2.VideoCapture(video_path) total_frames int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) # 均匀采样间隔至少 4 帧避免连续帧高度相似造成冗余 step max(4, total_frames // max_frames) frames [] idx 0 while True: cap.set(cv2.CAP_PROP_POS_FRAMES, idx) ret, frame cap.read() if not ret: break frames.append(frame) if len(frames) max_frames: break idx step cap.release() return frames def crop_face(frame): OpenCV DNN 人脸检测 外扩 30% 裁剪失败时退回原帧中心裁剪 detector cv2.dnn.readNetFromCaffe( deploy.prototxt, res10_300x300_ssd_iter_140000.caffemodel ) h, w frame.shape[:2] blob cv2.dnn.blobFromImage(frame, 1.0, (300, 300), (104.0, 177.0, 123.0)) detector.setInput(blob) dets detector.forward()[0, 0] for i in range(dets.shape[0]): conf dets[i, 2] if conf 0.5: continue box dets[i, 3:7] * [w, h, w, h] x1, y1, x2, y2 box.astype(int) # 每个人脸框外扩 30%保证额头和下颌完整 x1 max(0, int(x1 - 0.15 * (x2 - x1))) y1 max(0, int(y1 - 0.15 * (y2 - y1))) x2 min(w, int(x2 0.15 * (x2 - x1))) y2 min(h, int(y2 0.15 * (y2 - y1))) return frame[y1:y2, x1:x2] # 检测不到人脸就取中央 70%至少保证内容不空 return frame[int(h*0.15):int(h*0.85), int(w*0.15):int(w*0.85)]逻辑说明extract_frames函数每隔step帧取一帧而不是从第0帧连续取这样既控制了总帧数又让采样点覆盖整段视频的时间范围。一个20分钟的视频原始帧数约30000帧直接全部抽取会显著拖慢特征提取。crop_face函数用SSD人脸检测器返回置信度最高的人脸框外扩比例设为15%是基于访谈场景的经验值——被试离镜头固定距离框太紧会切掉额头太松会混入背景。检测失败时退回中央裁剪保证pipeline不中断。参数说明max_frames设为300是因为ResNet18批量提取时300帧大约占用不到2G显存。step的计算方式保证的是当视频很短时不会重复抽同一帧。deploy.prototxt和caffemodel两个文件放在项目根目录的models文件夹下如果没准备也可以用MTCNN替代但处理速度会慢不少。3.3 批量提取与缓存把帧特征固化成npy文件视频帧经过人脸裁剪、缩放、归一化后进入ResNet前向传播得到一个形状为(B, 512)的二维张量。这里有一条工程纪律不要在做训练时实时抽帧和提取特征而是预先提取所有视频的特征并保存成npy文件。一是抽帧和检测很慢实时做会在每次训练前重复几百倍计算二是特征文件很小一个300帧的视频只要300×512×4字节约600KB全部视频特征加起来不到几百MB。import torch import torchvision.transforms as T import numpy as np from torchvision import models transform T.Compose([ T.ToPILImage(), T.Resize((224, 224)), T.ToTensor(), T.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]), ]) def extract_video_feature(video_path, model, device): frames extract_frames(video_path, max_frames300) batch torch.stack([transform(cv2.cvtColor(f, cv2.COLOR_BGR2RGB)) for f in frames]) batch batch.to(device) with torch.no_grad(): feat model(batch) # [300, 512] return feat.cpu().numpy() # 转成 numpy 落盘 device cuda if torch.cuda.is_available() else cpu backbone models.resnet18(weightsmodels.ResNet18_Weights.IMAGENET1K_V1) backbone.fc torch.nn.Identity() # 去掉最后的 1000 类分类头 backbone backbone.to(device).eval() for subset in [train, dev]: video_dir fdataset/{subset} os.makedirs(ffeatures/{subset}, exist_okTrue) for vfile in os.listdir(video_dir): feat extract_video_feature(os.path.join(video_dir, vfile), backbone, device) vid vfile.replace(participant_, ).split(.)[0] np.save(ffeatures/{subset}/{vid}.npy, feat)逻辑说明backbone.fc被替换成Identity模型输出的就是全局池化后的512维特征向量不再经过分类头因为这层分类头是ImageNet的1000类语义空间和BDI-II回归无关。整个提取过程在torch.no_grad()下完成不保留梯度节省一半以上显存。每个视频的npy文件名直接用参与人ID后面训练时按文件名前缀对齐标签。参数说明Resize((224,224))是ResNet官方输入尺寸不要用256或其他值因为预训练权重适应的是224这个尺度。Normalize的均值和标准差是ImageNet统计出来的很多新手漏掉这一步导致提取的特征分布偏移。模型放eval模式是必须的如果忘了写BatchNorm会按训练模式更新统计量特征就不是预训练语义了这个问题极其隐蔽后面避坑章会再强调。4. 从帧特征到BDI-II分数时序聚合与回归头4.1 三种聚合方案的对比均值池化、LSTM与自注意力AVEC2014的任务是“一段视频一个分数”但ResNet给的是“一段视频N个帧向量”中间的桥就是时序聚合。最简单的聚合是直接对所有帧特征做平均得到一个512维向量再接一个全连接层输出回归值。这个方案看起来原始但实际效果并不差因为抑郁状态是一种持续性的情绪色调对整段视频做平均恰好可以捕捉“持续低唤醒”的特征。平均池化的问题在于它会抹掉情绪波动的顺序信息而临床观察认为抑郁患者的情绪反应曲线更平坦。LSTM聚合保留了时序顺序对“前10分钟中性、后10分钟情绪崩溃”这类模式更敏感。实现时把抽取的帧特征序列按时间顺序输入LSTM取最后一步的隐藏状态作为整段视频的表示。但LSTM在几百帧的长序列上容易遗忘早期信息而且训练明显更慢需要更多调参。自注意力聚合是近几年更常用的方案。对序列特征计算自注意力加权重加权平均可以让模型自动发现哪些帧对抑郁评分贡献更大。AVEC2014的数据规模只有几百个视频自注意力在这里很容易过拟合一般需要配合位置编码使用才能让模型感知帧的先后关系。import torch.nn as nn class TempoAggregator(nn.Module): 序列聚合器支持 平均 / LSTM / 自注意力 三种模式 def __init__(self, input_dim, hidden_dim, modelstm, num_heads4): super().__init__() self.mode mode self.input_dim input_dim if mode lstm: self.lstm nn.LSTM(input_dim, hidden_dim, batch_firstTrue) self.out_dim hidden_dim elif mode attention: self.attn nn.MultiheadAttention(input_dim, num_heads, batch_firstTrue) self.pos_enc nn.Embedding(256, input_dim) # 简单的可学习位置编码 self.out_dim input_dim else: # mean self.out_dim input_dim def forward(self, seq): # seq: [B, T, D] if self.mode mean: return seq.mean(dim1), None elif self.mode lstm: out, (h, c) self.lstm(seq) return out[:, -1, :], None else: B, T, D seq.shape pos self.pos_enc(torch.arange(T, deviceseq.device))[None, :, :] seq seq pos attn_out, attn_weight self.attn(seq, seq, seq) # 用第一个 token 位置的输出作为整段视频的表征 return attn_out[:, 0, :], attn_weight逻辑说明三种聚合方式返回的特征维度不同LSTM模式返回hidden_dim维均值和注意力模式返回原始input_dim维。位置编码的作用是让注意力模块知道帧与帧的相对顺序没有它模型会认为“第3帧和第250帧互换位置不影响语义”这在抑郁动态评估里是不合理的。注意力模式下取第一个token的输出等价于用一个可学习的CLS token这个token在训练中会逐步学会聚集对BDI-II评分最有区分力的视觉线索。参数说明hidden_dim建议取128或256考虑到AVEC2014数据量小LSTM隐藏维太大很容易过拟合。num_heads取4即可8个头在这个数据规模上几乎没有收益。self-attention里加了位置编码但不会在项目中引入FPN或细粒度特征——那是目标检测方向的做法迁移到视频情感回归里容易把问题复杂化。4.2 最小训练闭环数据加载、回归头、早停聚合器输出的向量还要经过一个回归头才能得到BDI-II分数。回归头设计得简单一点两层全连接加ReLU即可不要堆太深的MLP。数据量小参数越多过拟合越重这个观点在医疗AI小样本场景下尤其成立。import torch from torch.utils.data import Dataset, DataLoader import numpy as np import os class AVDataset(Dataset): 从npy特征文件读取一个视频的特征序列和BDI标签 def __init__(self, feature_dir, label_df): self.feature_dir feature_dir self.labels { str(row.id): row.bdi for row in label_df.itertuples() } self.ids list(self.labels.keys()) def __len__(self): return len(self.ids) def __getitem__(self, idx): vid self.ids[idx] seq np.load(os.path.join(self.feature_dir, f{vid}.npy)) seq torch.from_numpy(seq).float() label torch.tensor(self.labels[vid], dtypetorch.float32) return seq, label # 训练循环只保留最核心的部分 train_ds AVDataset(features/train, train_label_df) train_dl DataLoader(train_ds, batch_size8, shuffleTrue) model nn.Sequential( TempoAggregator(512, 256, modemean), nn.Linear(512, 128), nn.ReLU(), nn.Linear(128, 1), ) optimizer torch.optim.Adam(model.parameters(), lr1e-3, weight_decay1e-4) criterion nn.MSELoss()逻辑说明Dataset中每个样本的特征是变长的——有的视频抽到200帧有的抽到300帧。PyTorch默认DataLoader要求同batch内张量形状一致所以有两种处理一是训练时对较短的序列做padding二是在特征提取阶段就统一帧数。前者会更灵活后者实现更简单。这里给出的是不处理变长的写法当batch内的序列长度不一致时会报错解决办法是写一个collate_fn把序列padding到统一长度同时记录真实长度供聚合层使用。参数说明batch_size8是针对LSTM和注意力模式设定的显存不够就降到4。lr1e-3适合均值池化模式如果换LSTM建议降到5e-4否则LSTM的梯度更新幅度大早停点很难找。weight_decay设1e-4是给回归层用的对抗小数据的过拟合。MSE作为损失函数与BDI-II连续分数匹配但评估时看MAE更直观因为MAE的数值可以直接解读为“平均偏差多少分”。5. 避坑排查让MAE虚高的五个经典坑5.1 对全体特征做标准化导致数据泄漏现象训练集上MAE降到5左右开发集上却是9以上和论文报告值差距很大。原因很多人在预处理阶段对全体视频的npy特征统一做了StandardScaler拟合再拆分训练和开发集这等于让模型在训练时已经“见过”开发集特征的均值和方差数据泄漏。AVEC2014数据量小这种泄漏在回归任务中后果特别严重。解决标准化器只允许在训练集上fit然后用同样的参数transform开发集和测试集。代码上把scaler.fit()放在训练集数据加载之后保存scaler对象到本地后续处理其他数据时只调用transform。另一个自查方法检查训练和开发集的MAE差距如果差距超过2先怀疑标准化再怀疑模型容量。5.2 抽帧间隔太小特征序列高度自相关还撑爆显存现象extract_frames的step设成1一段30分钟的视频抽出来上千帧特征提取时GPU显存直接溢出或者训练LSTM时batch_size1还OOM。原因连续两帧之间的面部变化极其微小模型从中学不到新信息但显存和计算时间翻了几倍。AVEC2014访谈视频的场景相对静止冗余帧问题比普通动作视频严重得多。解决统一抽样逻辑step不小于4每个视频最多保留300帧。如果发现300帧下LSTM显存还是吃紧可以在聚合前先做降采样比如把300帧平均池化成50帧这个操作对最终MAE的影响很小但显存占用直接降到六分之一。5.3 加载预训练ResNet时批量归一化层被强制设为训练模式现象特征提取阶段没有任何报错但训练时loss震荡剧烈MAE一直不下降。原因backbone.eval()写在提取特征的函数前看似没问题但如果在同一个进程里先训练过模型再切到提取特征某些共享张量上的BatchNorm统计量会被污染。更隐蔽的情况是有的代码在backbone上直接调用了.train()导致BatchNorm在推理时也更新running_mean。解决特征提取函数内部显式调用model.eval()并在with torch.no_grad()下运行。如果是多线程数据加载确认子进程不会继承训练状态。自查方法是打印backbone.bn1.running_mean的前几个值如果和ImageNet初始化接近说明是推理模式如果明显漂移说明被训练污染了。5.4 BDI-II标签两极样本少回归模型咬不住极值现象预测值的范围集中在10到30之间但对分数小于5或大于40的样本偏差普遍超过15。原因BDI-II的分布天然是中间多、两端少。MAE是平均指标模型优化时优先靠近多数样本对极值样本的损失惩罚在那几个稀疏点上分摊得很薄。这不是模型bug是回归任务在偏态分布下的客观现象。解决给训练时的损失函数加样本权重权重和标签密度的倒数成正比。实操可以用numpy的histogram统计标签分布对每个样本按所在区间赋权。或者更简单一点对回归目标做log1p变换先压缩动态范围再训练预测时返回expm1还原。这个技巧在本项目的源码包里经常出现效果立竿见影。5.5 自注意力聚合在小数据上过拟合“玄学”般地比均值池化差现象换成自注意力聚合后训练MAE降到3.5开发集MAE反而从7.5涨到9。原因几百个视频样本撑不起多头注意力的参数量注意力权重很容易记住训练集里某几帧的特征组合而不是学到通用的抑郁相关模式。AVEC2014不是ImageNet那种百万级数据越复杂的模型越容易翻车。解决先用均值池化或浅层LSTM把baseline拔到合理水平再尝试自注意力。如果坚持用注意力必须加两点约束位置编码注入顺序信息、dropout设到0.3以上。还有一种实用技巧是对注意力权重做熵正则鼓励权重分布更均匀避免固定到少数帧上。这个坑的真实体验是架构升级带来的收益在小数据上往往被过拟合吞噬别迷信复杂模块。6. 结果验证与进阶技巧用消融实验证明你的系统真的有效跑通之后还要回答两个问题这个系统的预测结果可信吗哪些模块不可替换答案来自一套严格的消融实验。以ResNet18为固定特征提取器分别测均值池化、LSTM、自注意力三种聚合器在开发集上的MAE和RMSE每种子配置跑三次取平均因为小数据上的单次训练结果波动很大单次数字说明不了问题。表格形式如下聚合方式MAERMSE备注均值池化 两层MLP7.810.2显存最低训练最快LSTM(hidden128)7.59.6加dropout0.3自注意力 位置编码8.911.7过拟合明显需早停换ResNet50做同样的对比MAE通常能下降0.3到0.5但特征文件体积、提取时间和训练时间都会上涨。如果最终目标是论文或临床辅助筛查报MAE和RMSE是必须的如果是在工程演示系统里跑还要计算一个分类指标把BDI-II大于等于14定义为抑郁阳性小于14为阴性报告准确率和召回率这能让非技术背景的同事更直观理解。进阶验证方法之一是特征可视化。把每个视频300帧的特征取平均得到512维向量用t-SNE投影到二维平面按标签高低着色。如果抑郁程度高的样本在空间上聚成一团说明ResNet提取的视觉特征确实携带了和抑郁相关的信号如果散成一团就要回头检查抽帧和裁剪环节。这个可视化还能辅助排查标签噪声不需要额外训练任何模型。另一个实用技巧是把位置编码和细粒度特征结合起来做“时间分段注意力”。具体做法是把视频切成长度相等的时间段比如每个片段包含10帧先对片段内做均值池化得到细粒度的片段特征序列再输入自注意力。这个操作既保留了时间粗粒度上的顺序信息又让每帧的局部特征在片段级先做一次融合有效降低了输入序列长度和注意力参数量。AVEC2014视频动辄几十分钟这种两段式设计比直接对300帧做自注意力稳得多。最后的习惯每次实验记录一个配置版本号模型结构、采样帧数、学习率、权重衰减全记下来。MAE不是黑匣子误差从7.5降到6.8可能来自采样策略的调整也可能来自随机种子的运气不做记录的话好结果复现不出来也是最打击人的。希望这篇笔记帮你少走几步弯路让AVEC2014和ResNet跑出属于你的第一个可用的抑郁筛查基线。本文还有配套的精品资源点击获取