Python 实现音频转文字的完整方案:从 Whisper 到本地部署(2026 年代码实战)
最近在做一个批量处理会议录音的工具链,音频转文字在线转换软件免费推荐这一环对比了好几种方案,踩了不少坑,这篇就整理一下实现思路和代码细节。
音频转文字的技术原理
音频转文字本质上是自动语音识别(ASR,Automatic Speech Recognition)的工程化应用。整个流程分为三层:
音频预处理层:原始音频文件需要标准化处理——统一采样率到 16kHz、转单声道、去掉不必要的编码格式开销。这一步很关键,因为大多数开源 ASR 模型都是在 16kHz 单声道的训练数据上优化的,采样率不匹配会直接拉低识别准确率 5-10 个百分点。
特征提取层:音频信号被转换成梅尔频谱(Mel-spectrogram),这是一种模仿人耳听觉特性的频域表示,能把音频的冗余信息压缩到合理维度。
模型推理层:现在主流的做法是用 Whisper(OpenAI 开源)或 FunASR(阿里达摩院)这类 Transformer encoder-decoder 架构的模型。Encoder 端把梅尔频谱编码成语义向量,Decoder 端用自回归方式生成文本序列。相比传统的 CTC 模型,这种架构对口音、背景音、代码混用的鲁棒性更强。
方案一:本地 Whisper 推理
Whisper 有多个模型大小(tiny、base、small、medium、large),速度和精度相反。对大多数场景,base 模型是性价比最高的选择——推理速度约是实时速度的 1-2 倍,准确率在 95% 以上。
首先安装依赖:
pip install openai-whisper moviepy librosa
基础转文字代码:
import whisper
from pathlib import Path
# 加载模型(首次下载约 140MB)
model = whisper.load_model("base", device="cpu")
# 转文字
audio_path = "meeting.mp3"
result = model.transcribe(audio_path, language="zh")
# 打印结果
for segment in result["segments"]:
print(f"[{segment['start']:.2f}s - {segment['end']:.2f}s] {segment['text']}")
# 保存为文本
with open("output.txt", "w", encoding="utf-8") as f:
f.write(result["text"])
这个代码直接调用 Whisper 的转录接口,模型会自动处理采样率和格式问题。但是对于长音频(超过 10 分钟),直接扔给模型容易 OOM(内存溢出),因为音频会被完整加载到内存。
改进方案是分块处理。FFmpeg 可以帮我们按静音切分:
# 用 FFmpeg 抽音频并标准化:16kHz 单声道 WAV
ffmpeg -i meeting.mp3 -vn -ac 1 -ar 16000 -f wav audio_normalized.wav
然后用 librosa 按静音自动分割:
import whisper
import librosa
import numpy as np
def split_audio_by_silence(audio_path, silence_threshold=-40, min_duration=1.0):
"""按静音切分音频,返回 (start, end) 时间戳列表"""
# 加载音频
y, sr = librosa.load(audio_path, sr=16000, mono=True)
# 计算分贝值
S = librosa.feature.melspectrogram(y=y, sr=sr)
db = librosa.power_to_db(S, ref=np.max)
# 判断是否为静音(低于阈值)
is_silent = np.mean(db, axis=0) < silence_threshold
# 找切点
change_points = np.where(np.diff(is_silent.astype(int)))[0]
# 转换为时间戳并合并过短的片段
segments = []
for i in range(0, len(change_points), 2):
if i + 1 < len(change_points):
start = librosa.frames_to_time(change_points[i], sr=sr)
end = librosa.frames_to_time(change_points[i+1], sr=sr)
if end - start >= min_duration:
segments.append((start, end))
return segments
# 获取切点
segments = split_audio_by_silence("audio_normalized.wav")
print(f"检测到 {len(segments)} 个音频段")
# 加载模型
model = whisper.load_model("base")
# 逐段转写
results = []
for start, end in segments:
# 用 librosa 截取音频
y, sr = librosa.load("audio_normalized.wav", sr=16000, offset=start, duration=end-start)
# 转文字
result = model.transcribe(y, language="zh")
results.append({
"start": start,
"end": end,
"text": result["text"]
})
# 合并结果
full_text = " ".join([r["text"] for r in results])
print(full_text)
这样做的好处是每段只加载到内存 30-60 秒的音频,内存占用控制在 100MB 以内,适合处理长达几小时的录音。
方案二:faster-whisper 加速推理
Whisper 官方库对 CPU 推理优化不足。faster-whisper 是一个优化过的推理框架,用 CTranslate2 后端替换原生 PyTorch,速度能快 4 倍,同时内存占用减少一半。
安装:
pip install faster-whisper
使用代码:
from faster_whisper import WhisperModel
# 加载模型(可指定 compute_type 来权衡速度和精度)
# device 可以是 "cpu" 或 "cuda"(如果有 GPU)
# compute_type: "default"(float32) / "int8"(8 位量化,更快) / "int8_float16"(混合)
model = WhisperModel("base", device="cpu", compute_type="int8")
# 转文字(返回生成器,省内存)
segments, info = model.transcribe("audio_normalized.wav", language="zh", beam_size=5)
# 逐段输出
transcript = []
for segment in segments:
print(f"[{segment.start:.2f}s -> {segment.end:.2f}s] {segment.text}")
transcript.append(f"[{segment.start:.2f}s - {segment.end:.2f}s] {segment.text}")
# 保存为 SRT 字幕
with open("output.srt", "w", encoding="utf-8") as f:
for i, segment in enumerate(segments, 1):
start_time = format_timestamp(segment.start)
end_time = format_timestamp(segment.end)
f.write(f"{i}\n{start_time} --> {end_time}\n{segment.text}\n\n")
def format_timestamp(seconds):
"""将秒数转换为 SRT 时间格式 HH:MM:SS,mmm"""
hours = int(seconds // 3600)
minutes = int((seconds % 3600) // 60)
secs = int(seconds % 60)
millis = int((seconds % 1) * 1000)
return f"{hours:02d}:{minutes:02d}:{secs:02d},{millis:03d}"
faster-whisper 在 int8 量化模式下,base 模型推理速度约是实时速度的 3-4 倍,而精度损失不超过 1%。这是在自己的机器上跑音频转文字最推荐的方案。
方案三:云端 ASR API
如果机器配置有限或不想本地部署,可以用云厂商的 ASR 服务。以阿里云 NLS 为例:
import requests
import json
def transcribe_with_aliyun(audio_file, region="cn-hangzhou"):
"""用阿里云 ASR 服务转写音频"""
# 申请 AccessKey(在阿里云控制台),配置后端鉴权
access_key = "你的 AccessKey"
secret_key = "你的 Secret"
# 上传音频文件到 OSS(对象存储)
# 这里省略 OSS 上传逻辑,假设文件已在 OSS
oss_url = "https://your-bucket.oss-cn-hangzhou.aliyuncs.com/audio.wav"
# 调用 NLS 异步转写 API
api_url = "https://nls-gateway.cn-hangzhou.aliyuncs.com/stream"
payload = {
"audio_source": {
"type": "oss",
"uri": oss_url
},
"request": {
"format": "wav",
"sample_rate": 16000,
"phrases": "meeting,project,deadline" # 自定义热词
}
}
headers = {
"Content-Type": "application/json",
# 这里需要用 SDK 签名,完整实现见阿里云官方文档
}
# 发起转写请求
response = requests.post(api_url, json=payload, headers=headers)
if response.status_code == 200:
result = response.json()
return result.get("result", {}).get("text", "")
else:
print(f"请求失败: {response.status_code}")
return None
# 使用示例
transcript = transcribe_with_aliyun("meeting.wav")
print(transcript)
云端方案的优点是不需要本地 GPU,支持实时流式转写,对复杂的商用场景(如多人对话、强背景音)有更强的鲁棒性。缺点是每条音频按分钟计费(阿里云约 0.01 元/分钟),隐私数据需要上传到服务器。
音频转文字常见的坑
坑 1:采样率不匹配导致识别效果飘忽
症状:同一段音频,直接用 Whisper 和用 faster-whisper 识别结果完全不同。
原因:Whisper 库内部会自动 resample 音频到 16kHz,但如果输入音频本身损坏或格式异常,自动转换会失败,模型就是在原始采样率上瞎识别。
解决:在转写前用 FFmpeg 明确转换一遍,并用 librosa 验证:
import librosa
y, sr = librosa.load("audio.wav", sr=None) # sr=None 保留原始采样率
print(f"原始采样率: {sr}")
if sr != 16000:
y_resampled = librosa.resample(y, orig_sr=sr, target_sr=16000)
librosa.output.write_wav("audio_16k.wav", y_resampled, sr=16000)
坑 2:长音频直接喂给模型导致 OOM
症状:处理超过 30 分钟的音频,程序直接崩溃,错误信息是 CUDA out of memory 或系统 MemoryError。
原因:Whisper 会把整个音频加载到内存再推理,音频越长占用越多。一段 2 小时的 16kHz 音频大约需要 2GB+ 内存。
解决:要么分块处理(上面的分割代码),要么用流式推理库。faster-whisper 实际上也是分块处理的,但对用户透明。
坑 3:中英文混合识别时语言参数设错了反而更差
症状:在 language="auto" 时,识别出来的中英文混淆,英文被读成中文拼音。
原因:Whisper 的多语言识别是通过语言标记(language token)实现的,auto 模式下模型要边识别边推断语言,容易在切换点出错。
解决:如果你的音频主要是中文,就明确指定 language="zh";如果确实是混合内容,用 medium 或更大的模型会更稳。
# 如果是中英混合但以中文为主
result = model.transcribe("audio.wav", language="zh")
# 如果是双语且难分清,考虑升级模型
model = whisper.load_model("medium") # 比 base 大 4 倍,但准确率好 3-5%
result = model.transcribe("audio.wav", language="auto")
坑 4:生成 SRT 字幕时时间戳精度丢失
症状:字幕里有些时间戳格式错误,比如 "00:01:60,500"(秒数超过 60)。
原因:浮点数转换成整数时取整方式不对,或者没有正确处理边界情况。
解决:用前面代码块中的 format_timestamp 函数,它正确处理了边界。
不想装环境?用小程序省事
这套方案对非技术用户门槛有点高——要装 Python、下载模型、调试环境。如果你只是偶尔处理一两个音频文件,或者不想折腾本地环境,其实有更直接的做法。
我自己平时懒得起虚拟环境的时候,会用微信里搜的提词匠。它是一款小程序,支持直接上传音频或视频转文字,拖入文件等几十秒就能出结果,中英混合识别也不错。界面很简单,不需要什么技术基础。缺点是如果你有复杂的批量场景或需要自定义输出格式(比如生成特定的字幕编码、自动加时间戳、集成到工作流里),还是建议走脚本方案。
对于公司内部的会议转写或者播客内容处理,本地脚本方案的可控性和隐私性更好。
总结
2026 年做音频转文字,技术路线已经很成熟了。如果机器上能跑 Python,faster-whisper + int8 量化是最平衡的选择,速度够快精度也不错。对于超大文件或实时需求,云端 API 值得试。选哪条路主要看你的环境限制和数据隐私需求。
有遇到类似场景的欢迎评论区讨论,特别是中文识别和长音频的处理经验。如果后续有新的模型或更好的优化方案,再来更新。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)