一、为什么你需要 WhisperX?
假设你手头有一段 2 小时的会议录音,要整理成逐字稿,还得标注"张三说了什么、李四说了什么"。传统的做法是:先扔进某个转写工具拿到纯文本,再人工听一遍去猜每句话是谁说的------一份 1 小时的音频至少要花 3-4 小时手动整理。
OpenAI 开源的 Whisper 模型能做语音转文字,但它有两个硬伤:没有精确的时间戳对齐 ,更不认说话人 。WhisperX 就是来解决这个问题的------它在 Whisper 基础上叠加了强制对齐(forced alignment) 和**说话人分离(speaker diarization)**两套技术,一条命令就能拿到"谁在什么时间说了什么"的完整结果。

上图是 WhisperX 的完整处理管线:音频输入 → Whisper 转录 → 强制对齐时间戳 → 说话人分离 → 带说话人标签的最终文本。
本文用 3 个步骤 带你跑通整个流程,每一步都有完整可运行代码和输出示例。
二、环境准备
硬件要求
| 配置项 | 最低要求 | 推荐配置 |
|--------|---------|----------|
| GPU 显存 | 4 GB(small 模型) | 10 GB+(large-v3) |
| 内存 | 8 GB | 16 GB+ |
| 磁盘 | 5 GB | 10 GB+ |
纯 CPU 也能跑,但一段 10 分钟的音频可能转写 5-10 分钟------有 GPU 会快 10 倍以上。
第 1 步:安装依赖
bash
# 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__)"
运行结果:
text
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.1
- https://huggingface.co/pyannote/segmentation

⚠️ 这一步很容易漏掉!如果不点 Agree,运行时会报 `403 Forbidden`,卡住半天不知道原因。
三、实战步骤
准备工作:准备一段测试音频
建议先用一段有 2-3 个人对话、时长 30 秒到 2 分钟的音频测试。可以用手机录一段同事讨论,或者从播客截取一小段。格式支持 mp3 / wav / m4a / ogg。
下文中 AUDIO_FILE 变量统一指向你的音频文件路径。
第 1 步:Whisper 语音转文字(含词级时间戳)
这一步拿到带单词级别时间戳的转录结果------每个单词精确到它在音频的第几秒到第几秒出现。
python
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", device=DEVICE, compute_type=COMPUTE_TYPE)
result = model.transcribe(audio, batch_size=BATCH_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
运行结果示例:
text
音频加载完成,时长: 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)**来逐词校准位置。
python
import torch
# 1. 加载对齐模型(语言要和上一步检测结果一致)
align_model, metadata = whisperx.load_align_model(
language_code=result["language"],
device=DEVICE
)
# 2. 执行对齐------给每个单词精确标定时间
result = whisperx.align(
result["segments"],
align_model,
metadata,
audio,
device=DEVICE,
return_char_alignments=False # 不需要字级对齐,省内存
)
# 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
运行结果示例:
text
对齐后的词级时间戳(前 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 模型,它能分析音频的声纹特征,自动判断"这段是谁在说话"。
python
# ========== Hugging Face Token ==========
HF_TOKEN = "hf_你的token" # ← 替换为你的 HuggingFace Token
# =========================================
# 1. 初始化说话人分离管线
diarize_model = whisperx.DiarizationPipeline(
use_auth_token=HF_TOKEN,
device=DEVICE
)
# 2. 执行说话人分离
# min_speakers / max_speakers 可选------如果你知道有几个人,就传进去
diarize_segments = diarize_model(
audio,
min_speakers=2, # 至少 2 个人
max_speakers=4 # 最多 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
运行结果示例:
text
======================================================================
最终转录结果(含说话人标签)
======================================================================
--- 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 步合并
把上面三步合并成一个可复用函数------以后换一段音频,改一下文件路径就能跑:
python
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, device=device, compute_type=compute_type)
result = model.transcribe(audio, batch_size=batch_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_code=result["language"], device=device
)
result = whisperx.align(
result["segments"], align_model, metadata, audio, device=device
)
print(f" 词级对齐完成")
gc.collect()
torch.cuda.empty_cache()
del align_model
# --- Step 3: 说话人分离 ---
print(f"→ 加载说话人分离模型...")
diarize_model = whisperx.DiarizationPipeline(
use_auth_token=hf_token, device=device
)
diarize_segments = diarize_model(
audio, min_speakers=min_speakers, max_speakers=max_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_path="meeting_audio.mp3",
hf_token="hf_你的token",
model_name="large-v3",
min_speakers=2,
max_speakers=4,
)
# 保存为带说话人标签的 SRT 字幕
with open("output_diarized.srt", "w", encoding="utf-8") as f:
for i, seg in enumerate(result["segments"], start=1):
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")
运行结果:
text
✓ 音频加载完成: 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 环境 | device="cpu", compute_type="float32"------能用但慢 |
| 长音频(>1小时) | 先切分成 5-10 分钟的片段分别处理 |
2. 说话人检测不准?
- **指定人数**:如果你知道音频里有几个人,传 `min_speakers` 和 `max_speakers` 参数能显著提升准确率
- **音频质量**:背景噪音大、多人重叠说话时准确率会明显下降------这是 pyannote 的已知局限
- **中文优化**:Whisper `large-v3` 对中文识别效果最好,不要用 `turbo` 模型(不支持 diarization)
3. 模型选择参考
| 模型 | 显存 | 速度 | 中文识别 | 适用场景 |
|------|------|------|----------|----------|
| tiny / base | 1-2 GB | 极快 | 一般 | 英文测试 |
| small / medium | 2.5-5 GB | 中等 | 较好 | 个人项目 |
| large-v3 | ~10 GB | 较慢 | 最佳 | 生产环境 |
六、总结
WhisperX 把原本需要多个工具拼接 + 大量手工操作的音频转写任务,压缩成了一条 Python 管线:
text
音频文件 → [Whisper 转录] → [强制对齐] → [说话人分离] → 带说话人标签的逐字稿
三个核心步骤,不到 60 行代码,就能解决会议纪要、访谈整理、播客字幕等实际场景的痛点。
如果你在实际使用中遇到问题,推荐查阅 WhisperX 官方仓库(https://github.com/m-bain/whisperX) 的 Issues 区------12k+ star 的社区非常活跃,常见问题基本都有解答。
本文首发于 CSDN,转载请注明出处。