WhisperX实战:3步部署语音转文字,精准识别说话人 一、为什么你需要 WhisperX假设你手头有一段 2 小时的会议录音要整理成逐字稿还得标注张三说了什么、李四说了什么。传统的做法是先扔进某个转写工具拿到纯文本再人工听一遍去猜每句话是谁说的——一份 1 小时的音频至少要花 3-4 小时手动整理。OpenAI 开源的 Whisper 模型能做语音转文字但它有两个硬伤没有精确的时间戳对齐更不认说话人。WhisperX 就是来解决这个问题的——它在 Whisper 基础上叠加了强制对齐forced alignment和说话人分离speaker diarization两套技术一条命令就能拿到谁在什么时间说了什么的完整结果。上图是 WhisperX 的完整处理管线音频输入 → Whisper 转录 → 强制对齐时间戳 → 说话人分离 → 带说话人标签的最终文本。本文用3 个步骤带你跑通整个流程每一步都有完整可运行代码和输出示例。二、环境准备硬件要求| 配置项 | 最低要求 | 推荐配置 ||--------|---------|----------|| GPU 显存 | 4 GBsmall 模型 | 10 GBlarge-v3 || 内存 | 8 GB | 16 GB || 磁盘 | 5 GB | 10 GB |纯 CPU 也能跑但一段 10 分钟的音频可能转写 5-10 分钟——有 GPU 会快 10 倍以上。第 1 步安装依赖# 1. 安装 ffmpeg音频解码必须 # Ubuntu/Debian sudo apt install ffmpeg -y # macOS brew install ffmpeg # Windows: 从 https://ffmpeg.org/download.html 下载添加至 PATH # 2. 安装 WhisperX pip install whisperx # 3. 验证安装 python -c import whisperx; print(whisperx.__version__)运行结果3.1.2 # 版本号以实际为准第 2 步获取 Hugging Face Token说话人分离依赖 pyannote 模型需要你同意其使用协议注册 [huggingface.co](https://huggingface.co) 账号前往 [Settings → Access Tokens](https://huggingface.co/settings/tokens) 创建一个 READ token**依次访问以下两个页面点击 Agree and access repository**https://huggingface.co/pyannote/speaker-diarization-3.1https://huggingface.co/pyannote/segmentation⚠️ 这一步很容易漏掉如果不点 Agree运行时会报 403 Forbidden卡住半天不知道原因。三、实战步骤准备工作准备一段测试音频建议先用一段有 2-3 个人对话、时长 30 秒到 2 分钟的音频测试。可以用手机录一段同事讨论或者从播客截取一小段。格式支持 mp3 / wav / m4a / ogg。下文中AUDIO_FILE变量统一指向你的音频文件路径。第 1 步Whisper 语音转文字含词级时间戳这一步拿到带单词级别时间戳的转录结果——每个单词精确到它在音频的第几秒到第几秒出现。import whisperx import gc # 配置 AUDIO_FILE meeting_audio.mp3 # 替换为你的音频文件 DEVICE cuda # 无 GPU 改为 cpu COMPUTE_TYPE float16 # CPU 用 float32低显存用 int8 BATCH_SIZE 16 # 显存不够就改小到 8 或 4 # # 1. 加载音频自动重采样到 16kHz 单声道 audio whisperx.load_audio(AUDIO_FILE) print(f音频加载完成时长: {len(audio)/16000:.1f} 秒) # 2. 加载 Whisper 模型并转录 model whisperx.load_model(large-v3, deviceDEVICE, compute_typeCOMPUTE_TYPE) result model.transcribe(audio, batch_sizeBATCH_SIZE) print(f检测到语言: {result[language]}) print(f\n转录片段数: {len(result[segments])}) print(- * 50) for seg in result[segments][:5]: # 先看前 5 条 print(f[{seg[start]:.2f}s → {seg[end]:.2f}s] {seg[text].strip()}) # 释放模型显存 gc.collect() del model运行结果示例音频加载完成时长: 87.3 秒 检测到语言: zh 转录片段数: 23 -------------------------------------------------- [0.00s → 4.82s] 今天我们來討論一下第二季度產品的迭代計劃 [5.10s → 9.34s] 首先請產品經理介紹一下目前的進展 [9.68s → 15.21s] 好的 目前我們完成了用戶調研階段 收集了大概兩百份有效問卷 [15.68s → 21.03s] 數據顯示用戶最關心的三個功能分別是批量導出 權限管理 和自動備份 [21.45s → 24.87s] 批量導出的優先級是最高的 建議下個版本先上✅第 1 步完成。纯文本 时间戳已经到手但还不知道每句话是谁说的。第 2 步词级对齐强制对齐Whisper 原生的时间戳是估算的——特别是中文和混合语言场景下秒数经常不准。WhisperX 使用音素级强制对齐forced alignment来逐词校准位置。import torch # 1. 加载对齐模型语言要和上一步检测结果一致 align_model, metadata whisperx.load_align_model( language_coderesult[language], deviceDEVICE ) # 2. 执行对齐——给每个单词精确标定时间 result whisperx.align( result[segments], align_model, metadata, audio, deviceDEVICE, return_char_alignmentsFalse # 不需要字级对齐省内存 ) # 3. 查看对齐后的词级结果 print(对齐后的词级时间戳前 20 个词) print(- * 60) word_count 0 for seg in result[segments]: if words not in seg: continue for w in seg[words]: if word_count 20: break print(f {w[word]:12s} {w[start]:.2f}s → {w[end]:.2f}s (置信度: {w.get(score, 0):.2f})) word_count 1 # 释放对齐模型 gc.collect() torch.cuda.empty_cache() del align_model运行结果示例对齐后的词级时间戳前 20 个词 -------------------------------------------------------------- 今天 0.02s → 0.38s (置信度: 0.92) 我們 0.38s → 0.74s (置信度: 0.88) 來 0.74s → 0.90s (置信度: 0.95) 討論 0.90s → 1.28s (置信度: 0.91) 一下 1.28s → 1.62s (置信度: 0.87) 第二季度 1.62s → 2.30s (置信度: 0.93) 產品 2.30s → 2.66s (置信度: 0.90) 的 2.66s → 2.78s (置信度: 0.96) 迭代 2.78s → 3.14s (置信度: 0.89) 計劃 3.14s → 3.58s (置信度: 0.94) ...✅第 2 步完成。现在每个词都有精准的时间锚点——这是下一步说话人分离的基础。第 3 步说话人分离Speaker Diarization这是 WhisperX 的核心增值能力。利用 pyannote 模型它能分析音频的声纹特征自动判断这段是谁在说话。# Hugging Face Token HF_TOKEN hf_你的token # ← 替换为你的 HuggingFace Token # # 1. 初始化说话人分离管线 diarize_model whisperx.DiarizationPipeline( use_auth_tokenHF_TOKEN, deviceDEVICE ) # 2. 执行说话人分离 # min_speakers / max_speakers 可选——如果你知道有几个人就传进去 diarize_segments diarize_model( audio, min_speakers2, # 至少 2 个人 max_speakers4 # 最多 4 个人 ) # 3. 将说话人标签赋给每个单词 result whisperx.assign_word_speakers(diarize_segments, result) # 4. 打印最终结果 print(\n * 70) print( 最终转录结果含说话人标签) print( * 70 \n) prev_speaker None for seg in result[segments]: speaker seg.get(speaker, UNKNOWN) start seg[start] end seg[end] text seg[text].strip() # 说话人切换时加分隔线 if speaker ! prev_speaker: print(f\n--- {speaker} ---\n) timestamp f{int(start//60):02d}:{start%60:05.2f} print(f[{timestamp}] {text}) prev_speaker speaker print(\n * 70) # 清理资源 gc.collect() torch.cuda.empty_cache() del diarize_model运行结果示例 最终转录结果含说话人标签 --- SPEAKER_00 --- [00:00.00] 今天我们來討論一下第二季度產品的迭代計劃 [00:05.10] 首先請產品經理介紹一下目前的進展 --- SPEAKER_01 --- [00:09.68] 好的 目前我們完成了用戶調研階段 收集了大概兩百份有效問卷 [00:15.68] 數據顯示用戶最關心的三個功能分別是批量導出 權限管理 和自動備份 [00:21.45] 批量導出的優先級是最高的 建議下個版本先上 --- SPEAKER_02 --- [00:25.32] 我補充一點 從客服那邊反饋來看 自動備份的需求也很強烈 [00:29.87] 很多用戶因為沒有自動備份丟過數據 --- SPEAKER_00 --- [00:33.14] 好的 那這兩個功能我們都納入下個版本 開發週期大概需要多久 ✅第 3 步完成。至此你拿到了结构化的谁→何时→说了什么完整数据。四、一键脚本3 步合并把上面三步合并成一个可复用函数——以后换一段音频改一下文件路径就能跑import whisperx import gc import torch def transcribe_with_diarization( audio_path: str, hf_token: str, model_name: str large-v3, device: str cuda, compute_type: str float16, batch_size: int 16, min_speakers: int None, max_speakers: int None, ): 一键完成 转录 对齐 说话人分离 # --- 加载音频 --- audio whisperx.load_audio(audio_path) print(f✓ 音频加载完成: {len(audio)/16000:.1f}s) # --- Step 1: 转录 --- print(f→ 加载 Whisper 模型: {model_name}...) model whisperx.load_model(model_name, devicedevice, compute_typecompute_type) result model.transcribe(audio, batch_sizebatch_size) print(f 检测语言: {result[language]}, 片段数: {len(result[segments])}) gc.collect() torch.cuda.empty_cache() del model # --- Step 2: 对齐 --- print(f→ 加载对齐模型...) align_model, metadata whisperx.load_align_model( language_coderesult[language], devicedevice ) result whisperx.align( result[segments], align_model, metadata, audio, devicedevice ) print(f 词级对齐完成) gc.collect() torch.cuda.empty_cache() del align_model # --- Step 3: 说话人分离 --- print(f→ 加载说话人分离模型...) diarize_model whisperx.DiarizationPipeline( use_auth_tokenhf_token, devicedevice ) diarize_segments diarize_model( audio, min_speakersmin_speakers, max_speakersmax_speakers ) result whisperx.assign_word_speakers(diarize_segments, result) # 统计说话人 speakers set() for seg in result[segments]: if speaker in seg: speakers.add(seg[speaker]) print(f 检测到 {len(speakers)} 位说话人: {sorted(speakers)}) gc.collect() torch.cuda.empty_cache() del diarize_model return result # 使用示例 if __name__ __main__: result transcribe_with_diarization( audio_pathmeeting_audio.mp3, hf_tokenhf_你的token, model_namelarge-v3, min_speakers2, max_speakers4, ) # 保存为带说话人标签的 SRT 字幕 with open(output_diarized.srt, w, encodingutf-8) as f: for i, seg in enumerate(result[segments], start1): speaker seg.get(speaker, UNKNOWN) text seg[text].strip() def fmt(sec): h, m, s int(sec//3600), int((sec%3600)//60), int(sec%60) ms int((sec - int(sec)) * 1000) return f{h:02d}:{m:02d}:{s:02d},{ms:03d} f.write(f{i}\n{fmt(seg[start])} -- {fmt(seg[end])}\n) f.write(f[{speaker}] {text}\n\n) print(\n✓ SRT 字幕已保存至 output_diarized.srt)运行结果✓ 音频加载完成: 87.3s → 加载 Whisper 模型: large-v3... 检测语言: zh, 片段数: 23 → 加载对齐模型... 词级对齐完成 → 加载说话人分离模型... 检测到 3 位说话人: [SPEAKER_00, SPEAKER_01, SPEAKER_02] ✓ SRT 字幕已保存至 output_diarized.srt五、常见问题与调优建议1. 显存不够怎么办| 问题 | 解决方案 ||------|----------|| CUDA out of memory | ① 换小模型large-v3→medium或small②compute_type改用int8③batch_size降到 4 || 纯 CPU 环境 |devicecpu,compute_typefloat32——能用但慢 || 长音频1小时 | 先切分成 5-10 分钟的片段分别处理 |2. 说话人检测不准**指定人数**如果你知道音频里有几个人传 min_speakers 和 max_speakers 参数能显著提升准确率**音频质量**背景噪音大、多人重叠说话时准确率会明显下降——这是 pyannote 的已知局限**中文优化**Whisper large-v3 对中文识别效果最好不要用 turbo 模型不支持 diarization3. 模型选择参考| 模型 | 显存 | 速度 | 中文识别 | 适用场景 ||------|------|------|----------|----------||tiny/base| 1-2 GB | 极快 | 一般 | 英文测试 ||small/medium| 2.5-5 GB | 中等 | 较好 | 个人项目 ||large-v3| ~10 GB | 较慢 |最佳| 生产环境 |六、总结WhisperX 把原本需要多个工具拼接 大量手工操作的音频转写任务压缩成了一条 Python 管线音频文件 → [Whisper 转录] → [强制对齐] → [说话人分离] → 带说话人标签的逐字稿三个核心步骤不到 60 行代码就能解决会议纪要、访谈整理、播客字幕等实际场景的痛点。如果你在实际使用中遇到问题推荐查阅 [WhisperX 官方仓库](https://github.com/m-bain/whisperX) 的 Issues 区——12k star 的社区非常活跃常见问题基本都有解答。本文首发于 CSDN转载请注明出处。