
简介这份资源面向计算机类毕业设计与课程作业场景聚焦基于深度学习的语音增强与去混响方向适合已具备一定Python基础、希望将CNN、RNN、LSTM或Transformer等模型落地到语音信号处理的学生。内容围绕带噪与混响语音的分离、模型训练与评估展开涉及信噪比、波形失真度等指标可支撑从文献调研到实验复现的完整流程。资源包共144个文件以43个Python脚本、37个文本说明、21个wav音频样本为主另含少量pyc、sh、list及评估相关文件压缩包约57.81MB便于直接运行与二次修改。目前已有55人学习下载。读者可借助其中的脚本与音频数据快速搭建训练与测试环境理解数据预处理、模型实现及PESQ等评估环节并参考目录组织方式完成自己的毕设或课程作业。1. 语音增强与去混响一个毕设题目背后的真实工程链路很多计算机专业的同学拿到「基于深度学习的语音增强与去混响」这个毕设选题时第一反应是去搜现成代码找到一份能跑的 demo改改参数写篇论文交差。但真正动手之后会发现事情没那么简单远端会议录音里混响拖尾严重近场录音又有稳态噪声单一模型很难同时处理两种退化。更麻烦的是公开数据集里干净语音和带噪语音的配对方式跟你自己录的那段测试音频完全不是一回事。这个方向之所以适合做毕设或课程作业恰恰因为它有一条完整的工程链路可以走从数据合成、特征选择、模型搭建、训练调参到最终的指标评估和音频试听每一步都有明确的输入输出每一步都能单独拿出来写一章论文。而且语音增强和去混响这两个任务在深度学习框架下已经高度模块化你不需要从零推导波束形成公式只需要理解「为什么要这样设计」和「参数改了会怎样」。适合谁做如果你已经学过深度学习入门课程能看懂 PyTorch 的 Dataset 和 nn.Module 基本写法这个题目完全可以在两到三周内跑通一个可演示的版本。如果你连卷积神经网络的基础都没有建议先补完深度学习入门基于 Python 的前几章再回来。下面我按实际做项目的顺序把这条链路拆开讲清楚。2. 先搞清楚任务边界语音增强和去混响到底在做什么2.1 两个任务的区别与联系语音增强Speech Enhancement通常指从带噪语音中恢复干净语音噪声可以是稳态的风扇、空调也可以是非稳态的键盘敲击、翻纸声。去混响Dereverberation则是处理房间冲激响应带来的拖尾效应让语音听起来不「空」。两者在时频域上的表现不同噪声往往在整个频带均匀分布而混响的能量集中在语音起始后的几十到几百毫秒内。实际场景中这两类退化经常同时出现。远端会议录音既有空调噪声又有会议室混响。所以很多方案会用一个统一模型同时处理或者先做去混响再做增强。毕设里常见的做法是把两个任务合并成一个「带噪混响语音 → 干净语音」的映射问题用同一个网络预测目标。这样做的好处是训练目标单一坏处是模型容量要求更高训练数据也更难准备。2.2 为什么选时域还是时频域时频域方法先把波形做短时傅里叶变换STFT得到幅度谱和相位谱然后只增强幅度谱相位沿用带噪相位。这种做法在早期深度学习中很流行因为幅度谱的视觉模式比较直观卷积网络容易学到东西。但相位信息对语音质量影响很大尤其去混响任务里相位失真会带来金属感。时域方法直接对波形做卷积端到端输出增强后的波形。代表工作是 Conv-TasNet 和 Demucs 系列。时域方法的优势是避免了相位重建的误差但训练时对显存和 batch size 更敏感收敛也更慢。我一般建议毕设阶段先用时频域方法跑通全流程因为调试直观、中间结果可听可看等 baseline 稳定后再尝试时域模型做对比实验。2.3 数据准备没有配对数据怎么办这是毕设里最容易翻车的地方。真实场景下你很难同时录到带噪混响语音和对应的干净语音。常见做法是合成找一批干净语音如 LibriSpeech、AISHELL-3再找一批噪声如 MUSAN和房间冲激响应如 openSLR 的 RIR 数据集按一定信噪比和混响时间随机混合。合成时的参数控制很关键。信噪比一般取 -5 dB 到 15 dB 之间随机混响时间 T60 取 0.2 s 到 0.8 s。如果 T60 设得太大合成语音会糊成一团模型很难学到有效特征。另外要注意训练集和测试集的噪声类型、RIR 房间不能重叠否则指标虚高答辩时被问到泛化能力就露馅了。import numpy as np import soundfile as sf def mix_audio(clean_path, noise_path, rir_path, snr_db, output_path): clean, sr sf.read(clean_path) noise, _ sf.read(noise_path) rir, _ sf.read(rir_path) # 用 RIR 对干净语音做卷积模拟混响 reverbed np.convolve(clean, rir, modefull)[:len(clean)] # 调整噪声长度与混响语音一致 if len(noise) len(reverbed): noise np.tile(noise, int(np.ceil(len(reverbed) / len(noise)))) noise noise[:len(reverbed)] # 按信噪比计算噪声缩放系数 clean_power np.sum(reverbed ** 2) / len(reverbed) noise_power np.sum(noise ** 2) / len(noise) scale np.sqrt(clean_power / (noise_power * (10 ** (snr_db / 10)))) noisy reverbed scale * noise # 归一化防止削波 max_val np.max(np.abs(noisy)) if max_val 1.0: noisy noisy / max_val * 0.99 sf.write(output_path, noisy, sr) return noisy这段代码的核心逻辑是先卷积 RIR 制造混响再按目标信噪比缩放噪声后叠加。snr_db参数控制噪声强度值越小噪声越大。modefull保留卷积完整长度再截断到干净语音长度避免引入额外延迟。归一化那一步不能省否则写入 wav 文件时会出现削波失真训练时模型会学到错误的幅度分布。3. 模型选型与训练流程从 CRN 到 Conformer 的取舍3.1 为什么 CRN 是毕设的稳妥起点CRNConvolutional Recurrent Network最早在 2017 年被提出用于语音增强结构是编码器-解码器加 LSTM 瓶颈层。编码器用卷积把时频图压缩成特征图LSTM 建模时间依赖解码器再还原回时频图。这个结构在毕设里的优势很明显代码量适中PyTorch 实现大概两百行训练在单张 1080Ti 上几个小时就能收敛指标PESQ、STOI也能拿得出手。更重要的是CRN 的每个模块都可以单独替换做消融实验。比如把 LSTM 换成 GRU 看参数量变化把卷积核从 3×3 改成 5×5 看感受野影响。这些对比实验写进论文里就是现成的章节。3.2 训练目标掩蔽比映射更好用早期方法直接让网络预测干净幅度谱叫映射Mapping。后来发现让网络预测理想比率掩蔽IRM或理想复数掩蔽ICM效果更好因为掩蔽值的范围在 0 到 1 之间数值稳定性好网络更容易学。IRM 的定义是干净幅度谱除以带噪幅度谱再截断到 0 到 1。训练时用均方误差作为损失函数让网络输出逼近 IRM。推理时把预测的掩蔽乘回带噪幅度谱再结合带噪相位做逆 STFT 重建波形。import torch import torch.nn as nn class CRN(nn.Module): def __init__(self, hidden_dim256): super().__init__() # 编码器两层卷积逐步压缩频率维 self.encoder nn.Sequential( nn.Conv2d(1, 16, kernel_size(3, 3), padding(1, 1)), nn.BatchNorm2d(16), nn.ELU(), nn.Conv2d(16, 32, kernel_size(3, 3), padding(1, 1)), nn.BatchNorm2d(32), nn.ELU() ) # 瓶颈层双向 LSTM 建模时间依赖 self.lstm nn.LSTM(input_size32 * 161, hidden_sizehidden_dim, num_layers2, batch_firstTrue, bidirectionalTrue) # 解码器转置卷积还原时频图 self.decoder nn.Sequential( nn.ConvTranspose2d(64, 16, kernel_size(3, 3), padding(1, 1)), nn.BatchNorm2d(16), nn.ELU(), nn.ConvTranspose2d(16, 1, kernel_size(3, 3), padding(1, 1)), nn.Sigmoid() # 输出掩蔽值限制在 0 到 1 ) def forward(self, x): # x: (batch, 1, freq, time) enc self.encoder(x) b, c, f, t enc.shape # 重塑为 (batch, time, freq*channel) 送入 LSTM enc enc.permute(0, 3, 1, 2).reshape(b, t, c * f) lstm_out, _ self.lstm(enc) # 还原为 (batch, channel, freq, time) lstm_out lstm_out.reshape(b, t, c, f).permute(0, 2, 3, 1) mask self.decoder(lstm_out) return mask这段代码里hidden_dim控制 LSTM 隐藏层大小256 是毕设常用值再大显存吃紧。编码器输出通道数 32 是经验值太小欠拟合太大过拟合。Sigmoid保证掩蔽值在 0 到 1 之间符合 IRM 的定义。注意 LSTM 输入维度是32 * 161161 是 16 kHz 采样率下 320 点 FFT 的频率 bin 数如果你的 STFT 参数不同这个值要跟着改。3.3 训练时的三个必调参数第一个是学习率。Adam 优化器下初始学习率设 1e-3 比较稳如果 loss 震荡就降到 5e-4。第二个是 batch size。时频域模型对 batch size 不敏感8 到 16 都行但如果你改成时域模型batch size 至少要 4 以上否则 BatchNorm 统计量不准。第三个是梯度裁剪。LSTM 容易梯度爆炸把clip_grad_norm设为 5 能有效防止 loss 突然变成 NaN。训练过程中要同时监控训练集和验证集的 loss。如果训练 loss 持续下降但验证 loss 在某个 epoch 后开始上升说明过拟合了可以加 dropout 或者早停。我一般会在验证 loss 连续 5 个 epoch 不下降时停止训练保存验证 loss 最低的模型权重。4. 避坑与排查毕设里最容易翻车的五个地方4.1 现象训练 loss 正常下降但试听音频全是噪声原因通常是 STFT 和 ISTFT 的参数不匹配。比如训练时用n_fft320、hop_length160推理时忘了改回来或者用了默认值。另一个可能是掩蔽乘错了对象把掩蔽乘到了相位谱上。解决把 STFT 参数写成一个配置字典训练和推理共用同一份。推理时先检查重建波形的长度是否和原始带噪语音一致不一致就说明 hop_length 或窗函数有问题。4.2 现象PESQ 指标很高但人耳听起来有金属感这是典型的相位失真。时频域方法只增强幅度谱相位沿用带噪相位在混响较强时相位误差会带来金属感。PESQ 对相位失真不敏感所以指标好看但听感差。解决如果毕设要求听感建议加一个相位增强模块或者直接换时域模型。如果时间不够至少在论文里讨论这个局限性答辩时老师反而会觉得你思考全面。4.3 现象验证集 loss 比训练集还低这通常不是好事而是验证集太简单。检查一下验证集的噪声类型和信噪比分布是否和训练集一致。如果验证集全是低噪声样本loss 自然低。解决重新划分数据集确保训练集和验证集的信噪比范围、噪声类型、RIR 房间都按比例分布。可以用sklearn.model_selection.train_test_split的stratify参数按信噪比分层抽样。4.4 现象模型在合成数据上表现好真实录音一塌糊涂合成数据和真实录音的失配来自多个方面麦克风频响不同、背景噪声非稳态、混响不是简单的卷积关系。毕设里如果只用合成数据训练泛化能力必然有限。解决至少留一段自己用手机录的音频做最终测试不要只报合成测试集指标。如果条件允许在训练时加入少量真实录音做微调哪怕只有几分钟也能明显改善听感。4.5 现象训练到一半 loss 突然变成 NaNLSTM 梯度爆炸是常见原因尤其是序列较长时。另外如果输入特征没有做归一化幅度谱动态范围过大也会导致数值不稳定。解决加梯度裁剪把输入幅度谱做对数压缩或归一化到 0 到 1。检查数据加载环节有没有出现全零或全 NaN 的样本这种脏数据会让 loss 直接崩掉。5. 评估与对比怎么让毕设论文的实验章节站得住脚5.1 客观指标PESQ、STOI、SI-SDR 各看什么PESQ 是语音质量感知评估得分范围 -0.5 到 4.5越高越好。它对人耳感知的失真比较敏感但计算时需要参考干净语音所以只适用于合成测试集。STOI 是短时客观可懂度范围 0 到 1主要反映语音的可懂程度对噪声和混响都敏感。SI-SDR 是尺度不变的信噪比直接衡量波形层面的失真适合时域模型评估。毕设里建议三个指标都报不要只报一个。PESQ 高但 STOI 低说明语音听着自然但听不清反之则是听清了但音质差。三个指标一起看才能全面反映模型性能。5.2 消融实验怎么做才有说服力消融实验的目的是证明你设计的每个模块都有用。比如你用了 CRN 加注意力机制那就要对比不加注意力的 CRN、加了注意力的 CRN、以及只加注意力不加 LSTM 的版本。每次只改一个变量其他超参数保持一致。表格里除了指标还要写清楚参数量和推理时间。参数量反映模型复杂度推理时间反映实际部署可行性。如果加了注意力后指标只涨了 0.05 但参数量翻倍这个设计就不划算论文里要如实讨论。模型版本PESQSTOISI-SDR参数量(M)推理时间(ms)CRN baseline2.450.8912.33.218CRN Attention2.580.9113.15.725CRN GRU2.410.8811.92.815这张表里注意力机制确实带来了指标提升但参数量和推理时间也增加了。GRU 版本参数量更小但指标略降。论文里可以据此讨论不同场景下的选型建议如果部署在算力受限的设备上GRU 版本更合适如果追求极致指标注意力版本值得付出额外计算代价。5.3 频谱图对比一张图胜过三张表论文里一定要放频谱图对比。干净语音、带噪混响语音、增强后语音三张图并排读者一眼就能看出模型去掉了哪些成分。频谱图的横轴是时间纵轴是频率颜色深浅代表能量。好的增强结果应该保留语音的谐波结构同时去掉噪声的均匀底色和混响的拖尾。画图时注意统一色标范围否则视觉对比不公平。用librosa.display.specshow时把vmin和vmax设成所有子图共享的值。另外建议把音频文件也附在论文附件里答辩时直接播放比任何指标都有说服力。6. 从毕设到可演示系统一个能跑通的推理脚本长什么样6.1 加载模型并处理任意长度音频毕设答辩时老师很可能拿一段自己的录音让你现场测试。所以推理脚本不能只处理固定长度的片段要能处理任意时长音频。做法是分帧处理每帧长度和训练时一致帧间加重叠最后用重叠相加法重建完整波形。import torch import numpy as np import soundfile as sf import librosa def enhance_audio(model_path, noisy_path, output_path, n_fft320, hop_length160): # 加载模型 device torch.device(cuda if torch.cuda.is_available() else cpu) model CRN().to(device) model.load_state_dict(torch.load(model_path, map_locationdevice)) model.eval() # 读取音频并做 STFT noisy, sr sf.read(noisy_path) if len(noisy.shape) 1: noisy noisy.mean(axis1) # 转单声道 stft librosa.stft(noisy, n_fftn_fft, hop_lengthhop_length) mag, phase np.abs(stft), np.angle(stft) # 分帧处理每帧 256 帧时间步 frame_len 256 enhanced_mag np.zeros_like(mag) for i in range(0, mag.shape[1], frame_len): seg mag[:, i:iframe_len] if seg.shape[1] frame_len: seg np.pad(seg, ((0, 0), (0, frame_len - seg.shape[1]))) # 构造网络输入 (batch, channel, freq, time) inp torch.FloatTensor(seg).unsqueeze(0).unsqueeze(0).to(device) with torch.no_grad(): mask model(inp).squeeze().cpu().numpy() enhanced_mag[:, i:iframe_len] seg * mask[:, :seg.shape[1]] # 用增强幅度和原始相位重建波形 enhanced_stft enhanced_mag * np.exp(1j * phase) enhanced librosa.istft(enhanced_stft, hop_lengthhop_length) sf.write(output_path, enhanced, sr) return enhanced这段脚本的关键点是分帧长度frame_len要和训练时保持一致否则 LSTM 看到的时间步分布不同输出会异常。重叠相加那一步这里简化了实际使用时帧间要加 50% 重叠再用窗函数平滑否则帧边界会有咔嗒声。model.eval()和torch.no_grad()都要加否则 BatchNorm 会用当前 batch 的统计量结果不稳定。6.2 实时演示的取巧做法如果答辩现场要求实时演示不要试图做流式推理那个工程量太大。取巧的做法是提前录好三段音频安静环境、嘈杂环境、强混响环境现场用脚本处理并播放。每段音频控制在 10 秒以内处理时间在 GPU 上大概 1 到 2 秒老师不会等太久。另外准备一个对比播放的界面用 Python 的simpleaudio或pyaudio库按空格键切换原始音频和增强音频。这种 A/B 对比比单独播放增强结果更有冲击力老师能直接感受到模型去掉了什么。6.3 我踩过的一个坑采样率不匹配有一次答辩前夜我用 16 kHz 数据训练的模型去处理一段 44.1 kHz 的录音结果输出全是刺啦声。原因是 STFT 的n_fft和hop_length是按 16 kHz 设计的44.1 kHz 下频率 bin 的物理含义完全变了。模型看到的频谱模式和训练时不一致输出的掩蔽自然失效。解决很简单推理前先统一重采样到 16 kHz。librosa.resample一行代码的事但当时没想到折腾到凌晨三点。从那以后我养成了一个习惯任何音频进模型之前先打印采样率、时长、幅度范围确认和训练配置一致再往下走。这个习惯帮我省了很多后悔药。希望帮到你。本文还有配套的精品资源点击获取