纯 PyTorch 实现,离线可跑,只需要 torch + numpy。
输入一段 ≥3 通道的远场阵列录音,输出 两路近场人声。
本仓库是 2026 iCAN 大学生创新创业大赛 AI 应用创新挑战赛参赛作品的模型与源代码。
模型以官方 Conv-TasNet(JorisCos/ConvTasNet_Libri2Mix_sepclean_16k)为起点,
在自建仿真远场训练集(5777 条 / 20.46 h)上微调 30 轮,并把 mask 网络的
bottleneck 1×1 卷积输入通道从 512 扩到 518,用于接入 6 路 ITD/ILD 空间特征。
其余结构(编码器 / TCN / 解码器 / mask 头)一字未改。
pip install torch numpy soundfile scipy # torch 用 CPU 版即可
python conv_tasnet_spatial.py --check # 按 config.json 建网 + 载权重 + 前向 → PASS
python spatial_feat.py --selftest # 特征提取器自校验 → PASS
python separate.py 素材/main_meeting_pos_d8m_1580-141083-0053.wav --outdir 结果输出 结果/*_spk1.wav 与 *_spk2.wav 两路 16 kHz 语音。
更短的一页版见 0_三步跑通.md。
不需要 GPU、不需要联网、不需要 asteroid、也不需要另下官方 pytorch_model.bin。
一条 4.04 秒的 7 通道样本:CPU ≈ 3.1 s,GPU ≈ 2.5 s。
- 是:远场双说话人分离模型 —— 把混在一起的两个人的声音分成两路。
- 不是声纹条件 TSE:它不认识"谁",只负责"把人分开",不负责"挑出指定的人"。
- 不具备拒识能力:目标人不在场时,它会把在场的另一个人完整输出。
- 不是流式:整段(或分块)处理,有秒级延迟。
- 中文未经验证:训练语料是英文。
- 仿真是仿真:结论来自仿真远场,不等于真实录音上的表现。
评测条件:仿真远场测试网格 10800 条(3 房间 × 6 距离 × 600),统计单元 = 句子, 每个极性 n = 300 句;目标恒 az+0°、干扰恒 ±90°、SIR 恒 0 dB。
| 指标 | 值 |
|---|---|
| SI-SDR(选路后) | 6.3433 dB |
| SI-SDRi(选路后) | 6.3441 dB(95% CI [6.0652, 6.6266]) |
| WER | 0.3377(混合直连 0.5809,相对降 41.9%) |
| 与单通道基线(同数据、同轮数微调)的差 | ΔSI-SDRi +0.0156 dB(95% CI [−0.0118, +0.0447],句级 p = 0.62,不显著) |
| ΔWER(同单通道基线) | −0.0012(句级 p = 0.68) |
如实说明(这也是本仓库最想说清楚的一件事):
- 空间特征带来的增益很小(+0.0156 dB),句级统计不显著;
- 分房间:
office+0.0086 dB(p = 0.51)、meeting+0.0079(p = 0.88)、hall+0.0303(p = 0.57); - ±45° 干扰方位下 +0.0173 dB(p = 0.39)—— 只能说"未观察到崩塌",不能说"有提升";
- 10 m 距离档结果不稳(正负向都出现过波动);
- 负向样本(只有干扰人、无目标人)SI-SDRi 约 −59 dB,即完全没有拒识能力;
- 干净数据上的上界(另一组对照条件)为 +3.3297 dB,与上面的网格增益不是同一把尺子,不要混引。
因此本仓库的贡献是:完整实现并量化了"空间特征早融合"这条技术路线,给出可复现的结论, 而不是宣称在分离质量上超过开源基线。
| 项 | 取值 |
|---|---|
| 通道数 | ≥ 3(第 4 路起忽略;7 通道录音可直接用前 3 路) |
| 前 3 路是什么 | linear3 线性三麦:ch0 / ch1 / ch2,间距 2 cm |
| 参考麦 | ch1(波形只取这一路,不是对多通道求平均) |
| 采样率 | 16 kHz(其它采样率会自动重采样,需要 scipy) |
| 阵列几何 | 写死为 y 轴 −2 / 0 / +2 cm |
阵列几何、通道顺序、间距这三条是写死的。摆错或接错不会报任何错, 只会让空间特征落到训练分布之外、结果悄悄变差。这是本模型最容易被误用的地方。
<输入名>_spk1.wav 16 kHz / PCM16 / 峰值归一 0.9
<输入名>_spk2.wav 同上
<输入名>_debug.png 逐帧 ITD/ILD 曲线 + 几何上限参考线(没装 matplotlib 时退化为 .csv)
<输入名>_debug.csv 逐帧空间特征(原始量纲 + z 版)输出是"两路",不是"目标那一路"。
_spk1/_spk2的编号是任意的,先听再选。
.
├─ 0_三步跑通.md 一页三步跑通(先看这份)
├─ separate.py 主脚本:读录音 → 出两路
├─ spatial_feat.py 空间特征提取器(6 通道 ITD/ILD)
├─ conv_tasnet_spatial.py 网络定义(纯 PyTorch)
├─ model_loader.py 加载与推理封装(严格加载 + 帧对齐)
├─ load_and_infer.py 最小示例:50 行看懂怎么加载、怎么喂
├─ config.json 结构配方 + 空间特征契约 + 训练溯源
├─ convtasnet_spatial.pt 权重:20 389 069 B / md5 c04bfb9257351ecb7b95bef369bfec6a / 345 张量
├─ space_feat_stats.json 6 通道标准化标尺(md5 f0041eb724322535e0016b896722d2ff)
├─ 素材/ 6 条 7 通道 16 kHz 仿真样本 + 素材清单.csv
├─ README.md 本文档
├─ LICENSE 本仓库原创代码与文档的许可(BSD-3-Clause)
├─ THIRD_PARTY_NOTICES.md 第三方组件许可与署名(asteroid MIT / 权重 CC BY-SA 4.0)
└─ 训练记录/ 留档:ckpt_ep30.pt + 训练侧代码 + 随包官方底座 + 模型说明
- 起点:官方
JorisCos/ConvTasNet_Libri2Mix_sepclean_16k(20 394 640 B / md542e901d57d7c2f79b9d8a74a8077b7b0,随包在训练记录/代码/pretrained/) - 训练集:自建仿真远场训练集,5777 条 / 原始 20.46 h (装载丢弃短于 3 s 的 132 条 → 每 epoch 5645 条 / 可用 20.37 h)
- 优化:Adam,lr 1e-4 + warmup 500 步 + 余弦退火,weight decay 0; batch 2 × 累积 4 = 有效 8;3 s 段(48000 样本);PIT SI-SDR 损失;梯度裁剪 5
- 其他:
--deterministic开、seed 26;30 epoch,交付最后一个 epoch,不做验证集与早停 - 耗时:30 epoch 实测 19 090 s(≈5.3 h,约 4.1 it/s)
- 训练环境:PyTorch 2.11.0+cu128
训练数据与仿真生成脚本不在本仓库(属于内部训练资产)。权重不可再生成,请备份
convtasnet_spatial.pt。
| 官方 Conv-TasNet | 本模型 | 单通道基线(同数据、同轮数微调) | |
|---|---|---|---|
| 输入 | 单通道波形 | 单通道波形(ch1)+ 6 路 ITD/ILD | 单通道波形 |
bottleneck 输入通道 |
512 | 518 | 512 |
| 参数量 | 5 066 929 | 5 067 697(+768) | 5 066 929 |
| FLOPs(1 s 输入) | — | 9.9451 G | 9.9436 G |
| RTF(4 s,稳态) | — | 0.0049 | 0.0051 |
| 需要什么硬件 | 任意麦克风 | 2 cm 间距线性三麦 | 任意麦克风 |
本模型相对官方底座唯一的结构改动就是 bottleneck 那一层的输入通道数 —— 这是"早融合空间特征"
这个自变量能被干净归因的前提。
结构参数(逐字抄自官方配方):FreeFB;n_filters=512 / kernel_size=32 / stride=16;
bn_chan=128 / hid_chan=512 / skip_chan=128 / conv_kernel_size=3;
n_blocks=8 / n_repeats=3;n_src=2 / norm_type=gLN / mask_act=relu。
(官方配方里的 sample_rate = 8000 是官方自己写错的值,前向只走 model(wav)。)
本仓库的建模代码是自己写的(纯 PyTorch),所以必须证明它与训练时用的实现完全一致: 把同一份权重分别灌进「训练侧实现(asteroid 0.7.0)」与「本仓库实现」,喂同样输入逐元素比对:
| 用例 | max 绝对差 |
|---|---|
| 随机波形 T = 16000(1 s) | 0.000e+00 |
| 随机波形 T = 48000(3 s) | 0.000e+00 |
| 真实仿真样本 T = 156066(9.75 s) | 0.000e+00 |
| 同上,GPU | 0.000e+00 |
另有静态核验:张量名集合一致(345 / 345)、参数量一致(5 067 697)。
即:换掉的是依赖,不是模型。 唯一版本差异来自 numpy 浮点累加顺序,
实测 z 域特征 max 绝对差 = 4.6e-05,两路输出间 SI-SDR = 131 dB。
本仓库按部分适用不同许可(逐件对照、许可原文与署名见 THIRD_PARTY_NOTICES.md):
| 部分 | 来源 | 许可 |
|---|---|---|
| 算法 | Conv-TasNet(Luo & Mesgarani, TASLP 2019) | 论文 |
| 参考实现 | asteroid 0.7.0(网络定义是它的逐行等价移植) | MIT |
| 权重起点与配方 | HuggingFace JorisCos/ConvTasNet_Libri2Mix_sepclean_16k(作者 Cosentino Joris) |
CC BY-SA 4.0 |
本模型微调权重 convtasnet_spatial.pt(含训练检查点 训练记录/ckpt_ep30.pt) |
拾音客(改编自上面的起点权重) | CC BY-SA 4.0 |
随包再分发的起点权重 训练记录/代码/pretrained/ |
同上(原样再分发) | CC BY-SA 4.0 |
| 训练语音干声 | LibriSpeech(openslr.org/12) | CC BY 4.0 |
| 房间冲激响应 | 自建(pyroomacoustics 镜像源法仿真) | MIT |
| 本仓库原创的代码与文档 | 拾音客 | BSD-3-Clause(见 LICENSE) |
- 改编声明(CC BY-SA 要求):
convtasnet_spatial.pt与训练检查点训练记录/ckpt_ep30.pt是在 Cosentino Joris 的ConvTasNet_Libri2Mix_sepclean_16k基础上修改得到的 —— 在自建仿真远场训练集上微调 30 轮, 并把masker.bottleneck的 1×1 卷积输入通道由 512 扩到 518,其余结构未改。 本仓库的微调权重与随包的起点权重均以 CC BY-SA 4.0 提供,署名 Cosentino Joris。 LICENSE(BSD-3-Clause)只覆盖本仓库原创的代码与文档:不覆盖任何权重文件、不覆盖素材/中的样本、也不覆盖逐行移植自 asteroid 的网络定义(那部分是 MIT)。素材/中的 6 条样本由 LibriSpeech 干声 + 自建 RIR 仿真混合生成(已做修改), 使用与再分发请保留 LibriSpeech 的 CC BY 4.0 署名。
@misc{convtasnet-spatial-2026,
title = {ConvTasNet-Spatial:Conv-TasNet + 6 路 ITD/ILD 空间特征早融合的远场双说话人分离},
author = {{拾音客}},
year = {2026},
note = {2026 iCAN 大学生创新创业大赛 AI 应用创新挑战赛参赛作品},
url = {https://github.com/lvliyudashuai/ConvTasNet-Spatial}
}