# 数据准备问题排查 ## 现象 ### 推理报错 ```text KeyError: 'file .../multispeaker_session_7.wav is already part of AUDIO_RTTM_MAP, it might be duplicated, Note: file basename must be unique' ``` ### 训练异常(6小时训炸) 用户推测为数据问题导致,与推理报错同源。 --- ## 根因分析 ### 1. Manifest 格式不匹配 数据准备脚本 `run_all_speakers_simulation.sh` 生成的 manifest 为 **训练格式(segment-level)**: ```json {"audio_filepath": ".../multispeaker_session_0.wav", "offset": 1.164, "duration": 19.82, ...} {"audio_filepath": ".../multispeaker_session_0.wav", "offset": 20.692, "duration": 1.825, ...} {"audio_filepath": ".../multispeaker_session_0.wav", "offset": 21.982, "duration": 39.57, ...} ``` 同一个 wav 文件通过不同 `offset`/`duration` 被切分为多个 segment 行。训练 dataloader 按 segment 逐条加载,这是正确的。 但推理脚本 `e2e_diarize_speech.py:370` 调用 `audio_rttm_map()` 时: ```python # speaker_utils.py:113 uniqname = get_uniqname_from_filepath(filepath=meta['audio_filepath']) # → os.path.splitext(os.path.basename(filepath))[0] # → "multispeaker_session_0" (所有 segment 返回相同的值) if uniqname not in AUDIO_RTTM_MAP: AUDIO_RTTM_MAP[uniqname] = meta else: raise KeyError(...) # 第二条 segment 触发此分支 ``` **`get_uniqname_from_filepath()` 仅取 basename,不区分 offset。当同一个 wav 文件有多个 segment 行时,第二条及之后的行必定触发 KeyError。** ### 2. 推理期望的 Manifest 格式 推理端 `audio_rttm_map()` 设计的前提是 **每个 wav 文件只出现一次**,即 **file-level manifest**: ```json {"audio_filepath": ".../multispeaker_session_0.wav", "offset": 0, "duration": null, "rttm_filepath": ".../multispeaker_session_0.rttm", "num_speakers": 10} {"audio_filepath": ".../multispeaker_session_1.wav", "offset": 0, "duration": null, "rttm_filepath": ".../multispeaker_session_1.rttm", "num_speakers": 10} ``` 关键字段: - `offset` = 0(从文件开头处理) - `duration` = null(处理完整文件) ### 3. 当前数据结构 | Manifest | 行数 | 唯一 wav 文件数 | 平均 segment/wav | 格式 | |----------|------|----------------|-----------------|------| | `train.json` | 24,804 | 1,800 | 13.8 | segment-level | | `dev.json` | 3,842 | 180 | 21.3 | segment-level | | `test.json` | 3,772 | 180 | 21.0 | segment-level | > 所有 split 均包含重复的 `audio_filepath`,直接用于推理必然报错。 ### 4. 训练异常的原因推测 训练不会直接调用 `audio_rttm_map()`,因此 KeyError 不会出现在训练 loop 中。但以下问题可能导致训练失败: 1. **显存 OOM**:当前 10spk config 中 `batch_size=12`、`session_len_sec=1200`,在 V100-32G 上可能显存不足。 2. **Validation 阶段问题**:如果训练配置中某些验证逻辑间接调用了 `audio_rttm_map()`,会触发同样的 KeyError。 3. **数据完整性**:segment 切分生成时可能存在边界不齐、重叠或缺失的问题。 --- ## 修复方案 ### 方案 1: 生成推理专用 Manifest(推荐) ```python #!/usr/bin/env python3 """从 segment-level manifest 生成 file-level inference manifest""" import json import os from pathlib import Path def generate_infer_manifest(segment_manifest_path: str, output_path: str): seen = set() with open(segment_manifest_path) as f_in, open(output_path, 'w') as f_out: for line in f_in: d = json.loads(line) key = d.get('audio_filepath') if not key or key in seen: continue seen.add(key) # file-level format for inference entry = { "audio_filepath": d["audio_filepath"], "offset": 0, "duration": None, "rttm_filepath": d.get("rttm_filepath"), "num_speakers": d.get("num_speakers"), "text": "-", } f_out.write(json.dumps(entry) + "\n") print(f"Generated {len(seen)} entries → {output_path}") if __name__ == "__main__": base = "data/dump/simulated_10spk_dataset" for split in ["train", "dev", "test"]: generate_infer_manifest( f"{base}/{split}.json", f"{base}/{split}_infer.json", ) ``` 生成后修改 `infer.sh` 中 `dataset_manifest` 指向新的 `test_infer.json`。 ### 方案 2: 修改音频文件命名避免冲突 确保数据仿真时每个 segment 对应独立命名的 wav 文件(`multispeaker_session_0_seg001.wav`),但这会增加大量文件复制/软链接。 ### 方案 3: 修改 `audio_rttm_map()` 对重复做去重 ```python # speaker_utils.py:115 if uniqname not in AUDIO_RTTM_MAP: AUDIO_RTTM_MAP[uniqname] = meta else: logging.warning(f"Duplicate audio entry skipped: {meta['audio_filepath']}") continue ``` 但这只是绕过错误,不代表推理正确——后续逻辑仍可能因缺少正确的 file-level 映射而出错。 --- ## 建议的完整修复流程 1. 用方案 1 脚本生成 `train_infer.json` / `dev_infer.json` / `test_infer.json` 2. 推理时使用 `test_infer.json` 作为 manifest 3. 训练时继续使用 `train.json` / `dev.json`(segment-level 格式) 4. 为避免后续混淆,在 `run_all_speakers_simulation.sh` 末尾自动生成 file-level manifest 5. 测试训练:用 `batch_size` 从小开始(如 2-4),观察 GPU 显存使用和 loss 趋势,逐步上调