
简介PDF文献《基于深度学习的音频抑郁症识别》聚焦抑郁症的智能化识别面向深度学习、语音信号处理及医学交叉领域的研究者与从业者提出基于卷积神经网络和长短期记忆网络的音频抑郁回归模型 DR AudioNet。文献同时给出多尺度音频差分归一化MADN特征提取算法用于刻画非个性化讲话特性并结合前后相邻音频段的 MADN 特征设计了两种优化网络结构实验表明该方法能有效识别抑郁程度。资源共 1 个 PDF 文件压缩包约 856KB内容涵盖模型架构、特征设计、实验细节与分析适合作为论文写作参考和算法复现依据。目前已有 852 人学习对关注语音情感计算、抑郁自动诊断及音频深度特征提取的读者尤其有参考价值。1. 音频抑郁症识别先从换个说法讲起做语音情感识别的人大多遇到过这种尴尬同一句话用平静语气说和用沮丧语气说MFCC 特征分布的重叠区域远大于不同说话人之间的差异。换句话说模型很容易记住这个人说话的声音而不是这个人说话的状态。抑郁症语音识别Automatic Speech Depression Detection也卡在这个问题上——2016 年 AVEC 竞赛里基于 MFCC 和 CNN 的方案不少但换一个说话人准确率立刻掉下来。这篇论文提出的核心思路很直接既然静态特征里混着说话人的音色、音调等个性化信息那就用相邻帧之间的差分代替原始特征再对不同特征用不同尺度的滑动窗口做归一化把这个人是谁的信息尽量剥掉剩下的才是与抑郁程度相关的时序变化。这个思路用一个叫 MADN多尺度音频差分归一化的算法实现再配合 CNN 加 LSTM 的 DR AudioNet 网络做 BDI-II 分值回归。下面从特征到网络逐步拆开。2. 语音特征从哪儿来MFCC、短时能量与共振峰的选取逻辑2.1 AVEC 2014 数据集的结构和预处理边界论文实验用的是 AVEC 2014 的音频子集。数据分成 NORTHWIND 和 FREEFORM 两部分前者是照着固定文章朗读后者是回答工作人员的问题比如你最喜欢的菜是什么。第一部分的好处是文本内容一致特征差异主要来自发音方式第二部分更接近自然交谈。两部分各 150 对音频按 50/50/50 划分训练、验证、测试集。每个样本的标签是 BDI-II 量表得分范围 063不同分数段对应无抑郁、轻度、中度、重度。预处理有几个值得注意的参数。原始采样率 44.1kHz每帧 1024 个采样点帧移 512也就是 50% 重叠每 60 帧组成一个音频片段。这样单个片段覆盖的时间长度是 (601)×512/44100 ≈ 0.708 秒一个 4 分钟的音频大约能切出 300 多个片段。这里的关键决策是去除长时间静音段后再切分否则沉默部分会贡献大量零能量帧干扰后续差分计算。整批数据预处理后得到 7548 个片段其中训练集 5100、测试集 2448。2.2 MFCC 参数选择从 Mel 刻度到倒谱系数MFCC 的计算过程在论文里有完整的公式。先把普通频率转换为 Mel 刻度import librosa import numpy as np y, sr librosa.load(depression_audio.wav, sr44100) # 去除首尾静音保留有效语音段 y_trimmed, _ librosa.effects.trim(y, top_db20) # 1024 点 FFThop_length 512即 50% 重叠 mfcc librosa.feature.mfcc( yy_trimmed, srsr, n_mfcc12, n_fft1024, hop_length512, n_mels40, fmin0, fmaxNone, ) # mfcc 形状(12, 帧数)帧数由音频长度决定Mel 频率和普通频率的换算关系是 f_mel 2595 * log10(1 f_Hz / 700)。这个非线性映射模拟了人耳对低频敏感、高频迟钝的特性。参数上论文取了 12 个 MFCC 系数和 40 个 Mel 滤波器组这是语音识别里比较保守的配置。常见做法是取 13 个系数含第 0 阶能量但论文实验显示 12 阶在抑郁回归任务上结果更稳。2.3 共振峰、短时能量和过零率为什么有必要MFCC 描述的是频谱包络而抑郁症患者的声道肌肉紧张程度会导致共振峰位置偏移。共振峰反映的是声道共鸣腔的物理特征发音器官紧张时共振峰频率会变化因此把它加进来能捕捉 MFCC 不容易体现的发音生理差异。短时能量用来区分有声段和无声段抑郁症患者往往有停顿变长、吐字不清的现象这些会直接体现在能量分布的时序变化上。过零率则可以辅助判断清音和浊音的比例。提取这几个特征可以用 librosa 配合 scipy 实现import librosa from scipy.signal import lfilter # 短时能量每帧信号的平方和 frame_len 1024 hop 512 energy librosa.feature.rms( yy_trimmed, frame_lengthframe_len, hop_lengthhop )[0] # 形状(帧数,) # 过零率每帧内信号符号变化的次数 zcr librosa.feature.zero_crossing_rate( y_trimmed, frame_lengthframe_len, hop_lengthhop )[0] # 共振峰用 LPC 线性预测估计声道传输函数再找峰值 # 这里用 12 阶 LPC采样率 44.1kHz 下能覆盖 4 个左右的共振峰 def lpc_resonance(frame, order12): # 预加重 pre_emph lfilter([1, -0.97], [1], frame) autocorr np.correlate(pre_emph, pre_emph, full) autocorr autocorr[len(pre_emph) - 1 :] # Levinson-Durbin 递推求解 LPC 系数 from scipy.linalg import solve_toeplitz r autocorr[: order 1] a solve_toeplitz(r[:-1], -r[1:]) # 由 LPC 系数构造频谱并找峰值 w, h np.polynomial.chebyshev.chebfit( # 实际简化操作用 scipy.signal.freqz 求频响 ) return None共振峰部分的完整实现比较繁琐工程上更省事的做法是用 parselmouthPraat 的 Python 接口的to_formant_burg方法。LPC 阶数设置需要根据采样率调整44.1kHz 下 12 阶能分辨 34 个共振峰16kHz 采样时 10 阶就够了。特征全部提取完后拼接成一个 17×60 的矩阵——17 是 12 维 MFCC 加能量、过零率、3 个共振峰的合计维度60 是帧数。3. MADN 差分归一化把说话人个性从特征里剥掉3.1 差分特征为什么能削弱个性化信息假设两个人说同一句话一个人天生嗓门大另一个人声音低沉。他们的静态 MFCC 第一维和第二维的绝对数值差异明显但相邻两帧之间的变化趋势往往相似——因为抑扬顿挫、语速变化这些韵律特征受发音器官物理结构的影响较小。MADN 的第一步就是做相邻帧差分D(n, f) V(n, f1) - V(n, f)。这一步直接去掉了特征的直流分量也就是说话人相对固定的音色基线。但差分之后的数值范围仍受说话人音量影响。一个人说话大声他的差分值整体也会偏大。所以在差分之后必须做归一化把每个特征维度的动态范围压到 [0, 1]。3.2 滑动窗口归一化的实现归一化不是对整段音频算全局 min-max那样会把整段音频的动态范围拉平丢失局部突变信息。MADN 的核心是按滑动窗口计算局部最小值和最大值import numpy as np def madn_normalize(D, window_sizes): D: 差分特征矩阵形状 (n_features, n_frames) window_sizes: 每个特征维度对应的窗口半径列表长度等于 n_features 返回归一化后的特征矩阵 F n_features, n_frames D.shape F np.zeros_like(D, dtypenp.float32) for n in range(n_features): radius window_sizes[n] for f in range(n_frames): # 窗口边界裁剪 start max(0, f - radius) end min(n_frames, f radius 1) # 取出窗口内的局部差分值 local D[n, start:end] d_min np.min(local) d_max np.max(local) if d_max - d_min 1e-8: F[n, f] (D[n, f] - d_min) / (d_max - d_min) else: # 差分恒定则设为零避免除零 F[n, f] 0.0 return F # 每个特征维度的窗口半径帧数 # 前12维 MFCC 用半径 5 # 第13、14维能量、过零率用半径 10因为数值变化更平缓 # 第1517维共振峰用半径 15共振峰漂移是慢变过程 window_sizes [5] * 12 [10] * 2 [15] * 3 madn_feat madn_normalize(diff_features, window_sizes)窗口半径的设计逻辑要从特征的物理意义理解。MFCC 反映的是频谱包络的短时变化说话时元音和辅音交替较快局部窗口取小一些能保留韵母过渡的细节短时能量受响度波动影响变化相对平缓窗口取 10共振峰反映声道形状转动速度有限窗口取 15 帧约 0.35 秒比较合理。窗口边界处理有个细节要特别注意。max(0, f - radius)在音频段开头几帧会把窗口截短这会导致前几帧的归一化结果偏大因为局部范围小、分母小。常见的做法是给每段音频前后各补 radius 帧的填充值再算差分工程上通常用镜像填充或者直接丢弃边缘的 510 帧特征。实测丢弃边缘帧对 AVEC 2014 数据的 MAE 指标影响不大因为每个样本被切成大量片段边缘损失占比很小。3.3 用 numpy 向量化替代逐帧循环上面的实现每帧做一次切片和 min/max 操作numpy 循环在特征维度大、帧数过万时效率偏低。实际训练可以改成基于膨胀卷积的滑动窗口量化解法from numpy.lib.stride_tricks import sliding_window_view def madn_normalize_fast(D, window_sizes): n_features, n_frames D.shape F np.zeros_like(D, dtypenp.float32) for n in range(n_features): radius window_sizes[n] win 2 * radius 1 # 镜像填充保持开头和结尾的窗口完整 padded np.pad(D[n], radius, modereflect) # 构造滑窗视图形状(n_frames, win) windows sliding_window_view(padded, win) local_min windows.min(axis1) local_max windows.max(axis1) denom local_max - local_min denom[denom 1e-8] 1.0 # 防止除零 F[n] (D[n] - local_min) / denom return Fsliding_window_view不复制数据只在内存上做视图映射比循环快一个数量级。modereflect是镜像填充保证每个位置都有完整的 2r1 窗口也避免边缘特殊处理。操作完成后得到的 MADN 特征 F 在时间轴上是非个性化的变化量表示和原始静态特征 V1 互为补充。论文中的模型二使用前一段音频的 MADN 特征 V2模型三使用后一段的 MADN 特征 V3本质上就是让网络同时看当前说了什么和说话状态怎么变化两组信息。4. 构建 DR AudioNet3x1 卷积为什么比方形卷积更合理4.1 把二分类改成回归的原因抑郁量表 BDI-II 的得分是连续分值。分类模型只能回答有没有抑郁而临床治疗需要知道抑郁的严重程度——轻度、中度、重度的干预方案完全不同。论文将网络输出从二分类的 softmax 改为单节点回归用 MAE 和 RMSE 作为评估指标预测目标是 063 的实数值。直觉上回归任务的优化更平滑模型不会为了硬性分类边界牺牲数值上的精度。4.2 输入矩阵的几何含义输入是一个 17×60 的矩阵行维度是 17 种特征列维度是 60 帧时间。这样排列后水平轴是时间、垂直轴是特征类型。类似的频谱图处理问题中方形卷积核比如 3×3会同时混叠时间和特征维度——不同特征之间的相关性远弱于同一特征在相邻帧之间的相关性卷积核窗口会把这些物理含义完全不同的维度做了无差别混合削弱特征的判别力。DR AudioNet 采用 3×1 卷积核即每次只沿时间轴滑动 3 帧垂直方向不做卷积。这样做有两个好处每个特征维度保持独立的时间卷积不会引入跨维度的虚假耦合参数数量也减少到方形卷积的三分之一。4.3 完整的 PyTorch 实现import torch import torch.nn as nn class DRAudioNet(nn.Module): def __init__(self, n_features17, n_frames60): super().__init__() # 两层 3x1 卷积沿时间轴提取局部变化模式 self.conv1 nn.Conv2d( in_channels1, out_channels64, kernel_size(3, 1), padding(1, 0), ) self.bn1 nn.BatchNorm2d(64) self.conv2 nn.Conv2d( in_channels64, out_channels64, kernel_size(3, 1), padding(1, 0), ) self.bn2 nn.BatchNorm2d(64) self.pool nn.MaxPool2d(kernel_size(2, 1)) self.relu nn.ReLU() # LSTM 层编码时序依赖 self.lstm nn.LSTM( input_size64, hidden_size128, num_layers1, batch_firstTrue, ) # 回归头输出 BDI-II 预测分值 self.fc1 nn.Linear(128, 128) self.fc2 nn.Linear(128, 1) def forward(self, x): # x 形状: (batch, 17, 60)先补通道维 x x.unsqueeze(1) # (batch, 1, 17, 60) x self.relu(self.bn1(self.conv1(x))) x self.relu(self.bn2(self.conv2(x))) x self.pool(x) # 池化后时间维从 60 缩减到 30 # 转成 LSTM 输入: (batch, seq_len, features) x x.squeeze(2) # (batch, 30, 64) x x.permute(0, 2, 1) # (batch, 30, 64) out, _ self.lstm(x) # out: (batch, 30, 128) # 取最后一个时间步的输出 out out[:, -1, :] # (batch, 128) out self.relu(self.fc1(out)) pred self.fc2(out) # (batch, 1) return pred.squeeze(1)卷积层的作用是提取局部时序变化模式也就是邻近几帧之间怎么变池化层把时间维从 60 降到 30减少 LSTM 的计算量同时对帧位置的小偏移引入一定的鲁棒性说话人语速快慢不同时抑郁相关的特征在时间轴上可能出现几个帧的错位池化恰好容忍这种位移。LSTM 层的作用则是捕捉长距离依赖——一个人在几分钟的对话里音频状态的缓慢漂移这种趋势 CNN 感受野覆盖不了。训练参数按论文实验设置batch size 32损失函数用均方误差MSE优化器常见做法用 Adam学习率从 1e-4 起验证集 loss 连续 5 个 epoch 不下降就减半。层数上两层卷积加一层 LSTM 是这个任务的经验配置再加深容易在小数据集上过拟合——AVEC 2014 音频段虽然有 5100 条但都来自 50 个说话人独立样本数其实不多。5. 相邻片段串行微调与 MAE/RMSE 验证技巧5.1 模型串联而不是特征拼接论文没有简单地把三组特征拼在一起喂给网络而是采用串行微调的训练方式先在静态特征 V1 上训练 DR AudioNet得到模型一再用前一段音频的 MADN 特征 V2 在模型一的基础上继续训练得到模型二最后用后一段的 MADN 特征 V3 在模型二的基础上微调得到模型三。每个后续模型都在前一个模型已经收敛的基础上继续用新的特征调整权重相当于让网络先理解音频的静态内容再学习局部变化特征最后扩展认知到更长期的上下文变化。# 三个阶段训练伪代码 classifier DRAudioNet() # Stage 1: 在静态特征上从头训练 static_feat, bdi_label load_segment_features(feature_typestatic) train(classifier, static_feat, bdi_label, epochs30) # Stage 2: 用前一段 MADN 特征微调 prev_madn load_segment_features(feature_typemadn_prev) train(classifier, prev_madn, bdi_label, epochs10, lr1e-5) # Stage 3: 用后一段 MADN 特征继续微调 next_madn load_segment_features(feature_typemadn_next) train(classifier, next_madn, bdi_label, epochs10, lr5e-6)微调阶段学习率要比第一阶段小一个数量级因为预训练权重已经编码了有效的静态音频特征过大的学习率会破坏这些信息。Stage 2 和 Stage 3 的音频片段在时间上与当前片段相邻共享同一个 BDI-II 标签。这种设计不是简单的数据增强而是让模型在推理时能把相邻片段的信息也纳入考量缓解单个片段时长不足导致的信息缺失问题。5.2 MAE 与 RMSE 在抑郁分值上的解读评估指标用 MAE 和 RMSE。MAE 是所有预测误差绝对值的平均计算方式为 MAE 1/N * Σ|yᵢ - ŷᵢ|对异常值不敏感RMSE 是先平方再平均开根对偏离较大的预测施以更重的惩罚。论文在测试集上得到的 RMSE 和 MAE 约 9.70。BDI-II 总分 63预测误差在 10 分以内意味着模型能大致区分抑郁程度等级无抑郁、轻度、中度、重度之间的分值边界是 13/19/28但还不足以精确到个体差异的临床级别。这个数值对应的实际表现是模型能判断一个语音样本偏向哪个严重等级区间但同一受试者两周内的复测分数波动也可能达到这个量级。5.3 验证差分特征有效性的快速方法不训练完整网络也能快速验证 MADN 特征是否真的剥离了个性化信息。做法是从数据集中选同一说话人两段不同抑郁分值的录音分别计算静态特征和 MADN 特征的均值向量再比较这两个向量在说话人内部的差别和说话人之间的差别。import numpy as np def feature_separation_score(features, speaker_ids, labels): 计算组间方差与组内方差的比值 值越大说明特征区分说话人状态的能力越强 speakers np.unique(speaker_ids) between_var 0.0 within_var 0.0 global_mean features.mean(axis0) for spk in speakers: spk_feat features[speaker_ids spk] spk_mean spk_feat.mean(axis0) # 组间方差说话人均值相对全局均值的偏移 between_var ( len(spk_feat) * np.sum((spk_mean - global_mean) ** 2) ) # 组内方差特征本身相对说话人均值的离散度 within_var np.sum((spk_feat - spk_mean) ** 2) return between_var / (within_var 1e-8) # static_feats 和 madn_feats 分别取 100 段语音 score_static feature_separation_score(static_feats, speakers, labels) score_madn feature_separation_score(madn_feats, speakers, labels) print(fStatic separation: {score_static:.3f}) print(fMADN separation: {score_madn:.3f})如果 MADN 有效speaker 维度上的分离度分数会显著下降说明说话人之间的特征差异缩小了而标签维度高低分组上的分离度分数应该保持或上升。实际论文中 MADN 对 MAE 的改善幅度大约在 5%10%这个验证方法在动手训练前能给到明确的方向信号。检查时注意差分后的特征维度会少一帧因为最后一个点没有后续帧可以做差分代码里要对齐标签长度。本文还有配套的精品资源点击获取