音视频转换成文字faster-whisper
Ubuntu 20下用Python将视频/音频转文字的脚本,主要使用 whisper(OpenAI开源)或 faster-whisper(更快更省资源)。
纯 CPU 方案(无显卡)
使用 int8 量化,CPU也能流畅运行
python3 transcribe.py audio.mp3 --model base --device cpu --compute int8
模型目录结构要求
./faster-whisper-base/ 目录下需要包含这些文件:
./faster-whisper-base/
├── model.bin (模型权重,约145MB for base)
├── config.json (模型配置)
├── tokenizer.json (分词器)
└── vocabulary.txt (词表)
faster-whisper 支持的模型及其特点:
| 模型 | 参数量 | 磁盘大小 | 速度 | 准确率 | 适用场景 | 显存需求 |
|---|---|---|---|---|---|---|
| tiny | 39M | ~75MB | 最快 | 一般 | 实时字幕、快速草稿、资源极有限 | ~1GB |
| tiny.en | 39M | ~75MB | 最快 | 一般(英文优化) | 仅英文实时场景 | ~1GB |
| base | 74M | ~150MB | 很快 | 够用 | 日常使用、播客、会议记录 | ~1GB |
| base.en | 74M | ~150MB | 很快 | 够用(英文优化) | 仅英文日常场景 | ~1GB |
| small | 244M | ~480MB | 快 | 较好 | 平衡选择,推荐大多数场景 | ~2GB |
| small.en | 244M | ~480MB | 快 | 较好(英文优化) | 仅英文高质量场景 | ~2GB |
| medium | 769M | ~1.5GB | 中等 | 好 | 对准确率要求高 | ~5GB |
| medium.en | 769M | ~1.5GB | 中等 | 好(英文优化) | 仅英文高精度场景 | ~5GB |
| large-v1 | 1.5B | ~3GB | 慢 | 很好 | 专业转录、多语言 | ~10GB |
| large-v2 | 1.5B | ~3GB | 较慢 | 很好 | 推荐的大模型版本 | ~10GB |
| large-v3 | 1.5B | ~3GB | 较慢 | 最好 | 最新版,多语言最强 | ~10GB |
| large-v3-turbo | 1.5B | ~3GB | 快 | 接近large-v3 | 速度与准确率的最佳平衡 | ~6GB |
关键说明
.en 后缀模型
- 仅针对英语优化,其他语言效果差
- 如果你只处理中文内容,不要用
.en模型
速度对比(参考值,CPU int8)
| 模型 | 1小时音频处理时间 |
|---|---|
| tiny | ~5分钟 |
| base | ~10分钟 |
| small | ~20分钟 |
| medium | ~1小时 |
| large-v3 | ~2-3小时 |
选择建议
| 你的场景 | 推荐模型 |
|---|---|
| 快速测试/草稿 | tiny |
| 日常中文视频/播客 | base 或 small |
| 会议记录(要准确) | small 或 medium |
| 专业字幕/翻译 | medium 或 large-v3 |
| 多语言混合内容 | large-v3 |
| 追求速度+准确率平衡 | large-v3-turbo |
你的 CPU 场景建议
# 日常够用,速度快
python3 transcribe.py audio.mp3 --model base --device cpu --compute int8
# 想要更好效果(推荐)
python3 transcribe.py audio.mp3 --model small --device cpu --compute int8
# 如果内存够(8G+),追求准确
python3 transcribe.py audio.mp3 --model medium --device cpu --compute int8
large-v3 在纯 CPU 上会很慢,除非内容特别重要,否则不建议。
如何使用HF-Mirror🌟
本站域名 hf-mirror.com,用于镜像 huggingface.co 域名。作为一个公益项目,致力于帮助国内AI开发者快速、稳定的下载模型、数据集。捐赠支持请看网页左下角,感谢支持!
更多详细用法请看《这篇教程》。
方法一:网页下载
在本站搜索,并在模型主页的Files and Version中下载文件。
方法二:huggingface-cli
huggingface-cli 是 Hugging Face 官方提供的命令行工具,自带完善的下载功能。使用方法如下:
1. 安装依赖
pip install -U huggingface_hubCopy
2. 设置环境变量
Linux
export HF_ENDPOINT=https://hf-mirror.comCopy
Windows Powershell
$env:HF_ENDPOINT = "https://hf-mirror.com"Copy
建议将上面这一行写入 ~/.bashrc。
3.1 下载模型
huggingface-cli download --resume-download gpt2 --local-dir gpt2Copy
3.2 下载数据集
huggingface-cli download --repo-type dataset --resume-download wikitext --local-dir wikitextCopy
可以添加 --local-dir-use-symlinks False 参数禁用文件软链接,这样下载路径下所见即所得,详细解释请见上面提到的教程。
方法三:使用 hfd
hfd 是本站开发的 huggingface 专用下载工具,基于成熟工具 aria2,可以做到稳定高速下载不断线。
1. 下载hfd
wget https://hf-mirror.com/hfd/hfd.sh
chmod a+x hfd.shCopy
2. 设置环境变量
Linux
export HF_ENDPOINT=https://hf-mirror.comCopy
Windows Powershell
$env:HF_ENDPOINT = "https://hf-mirror.com"Copy
3.1 下载模型
./hfd.sh gpt2Copy
3.2 下载数据集
./hfd.sh wikitext --datasetCopy
方法四:使用环境变量(非侵入式)
非侵入式,能解决大部分情况。huggingface 工具链会获取HF_ENDPOINT环境变量来确定下载文件所用的网址,所以可以使用通过设置变量来解决。
HF_ENDPOINT=https://hf-mirror.com python your_script.pyCopy
不过有些数据集有内置的下载脚本,那就需要手动改一下脚本内的地址来实现了。
常见问题
Q: 有些项目需要登录,如何下载?
A:部分 Gated Repo 需登录申请许可。为保障账号安全,本站不支持登录,需先前往 Hugging Face 官网登录、申请许可,在官网这里获取 Access Token 后回镜像站用命令行下载。
部分工具下载 Gated Repo 的方法:
huggingface-cli: 添加--token参数
huggingface-cli download --token hf_*** --resume-download meta-llama/Llama-2-7b-hf --local-dir Llama-2-7b-hfCopy
hfd: 添加--hf_username``--hf_token参数
hfd meta-llama/Llama-2-7b --hf_username YOUR_HF_USERNAME --hf_token hf_***Copy
其余如from_pretrained、wget、curl如何设置认证 token,详见上面第一段提到的教程。
transcribe.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
视频/音频转文字脚本 (Ubuntu 20)
使用 faster-whisper,支持 GPU/CPU
"""
import os
import sys
import argparse
import subprocess
import json
from pathlib import Path
from datetime import timedelta
# 尝试导入 faster-whisper,失败则提示安装
try:
from faster_whisper import WhisperModel
except ImportError:
print("错误:未安装 faster-whisper")
print("请运行: pip install faster-whisper")
sys.exit(1)
def format_timestamp(seconds: float) -> str:
"""将秒数转换为 SRT 时间格式 HH:MM:SS,mmm"""
td = timedelta(seconds=seconds)
total_seconds = int(td.total_seconds())
hours = total_seconds // 3600
minutes = (total_seconds % 3600) // 60
secs = total_seconds % 60
millis = int((seconds - int(seconds)) * 1000)
return f"{hours:02d}:{minutes:02d}:{secs:02d},{millis:03d}"
def extract_audio(video_path: str, output_audio: str = None) -> str:
"""
从视频文件中提取音频(使用 ffmpeg)
"""
if output_audio is None:
output_audio = video_path.rsplit('.', 1)[0] + '.wav'
# 检查是否已有音频文件
if os.path.exists(output_audio):
print(f"音频文件已存在: {output_audio}")
return output_audio
print(f"正在从视频提取音频: {video_path}")
cmd = [
'ffmpeg', '-y', '-i', video_path,
'-vn', # 不处理视频
'-acodec', 'pcm_s16le', # 16位PCM编码
'-ar', '16000', # 采样率16kHz(whisper推荐)
'-ac', '1', # 单声道
output_audio
]
try:
result = subprocess.run(
cmd,
capture_output=True,
text=True,
check=True
)
print(f"音频提取完成: {output_audio}")
return output_audio
except subprocess.CalledProcessError as e:
print(f"ffmpeg 提取音频失败: {e.stderr}")
sys.exit(1)
except FileNotFoundError:
print("错误:未找到 ffmpeg,请先安装: sudo apt install ffmpeg")
sys.exit(1)
def transcribe(
audio_path: str,
model_size: str = "base",
device: str = "auto",
compute_type: str = "default",
language: str = "zh",
output_format: str = "txt",
output_path: str = None,
vad_filter: bool = True
) -> list:
"""
使用 faster-whisper 进行语音识别
参数:
audio_path: 音频文件路径
model_size: 模型大小 (tiny/base/small/medium/large/large-v2/large-v3)
device: 计算设备 (cuda/cpu/auto)
compute_type: 计算精度 (float16/int8/int8_float16/float32)
language: 语言代码 (zh/en/ja/...)
output_format: 输出格式 (txt/srt/json)
output_path: 输出文件路径
vad_filter: 是否启用语音活动检测(过滤静音)
"""
# 自动选择设备
if device == "auto":
import torch
device = "cuda" if torch.cuda.is_available() else "cpu"
print(f"自动选择设备: {device}")
# 根据设备调整计算类型
if compute_type == "default":
compute_type = "float16" if device == "cuda" else "int8"
print(f"加载模型: {model_size} (设备: {device}, 精度: {compute_type})")
# 加载模型(优先使用 ./faster-whisper-base 目录下的本地模型)
model_dir = os.environ.get("WHISPER_MODEL_DIR", "./faster-whisper-base")
print(f"模型目录: {os.path.abspath(model_dir)}")
# 加载模型(优先使用 ./faster-whisper-base 目录下的本地模型)
if os.path.exists(model_dir):
print("使用本地模型...")
# 直接传模型目录路径给第一个参数
model = WhisperModel(model_dir, device=device, compute_type=compute_type)
else:
print("本地模型不存在,尝试下载...")
model = WhisperModel(model_size, device=device, compute_type=compute_type)
print(f"开始识别: {audio_path}")
print(f"语言: {language}, VAD过滤: {vad_filter}")
# 执行识别
segments, info = model.transcribe(
audio_path,
language=language,
vad_filter=vad_filter,
vad_parameters=dict(min_silence_duration_ms=500),
condition_on_previous_text=True
)
print(f"检测到语言: {info.language} (概率: {info.language_probability:.2f})")
print(f"预计时长: {info.duration:.2f} 秒")
# 收集结果
results = []
for i, segment in enumerate(segments, 1):
result = {
"id": i,
"start": segment.start,
"end": segment.end,
"text": segment.text.strip(),
"start_formatted": format_timestamp(segment.start),
"end_formatted": format_timestamp(segment.end),
}
results.append(result)
# 实时打印
print(f"[{result['start_formatted']} --> {result['end_formatted']}] {result['text']}")
# 保存结果
if output_path is None:
output_path = audio_path.rsplit('.', 1)[0] + f'.{output_format}'
save_results(results, output_path, output_format)
print(f"\n结果已保存: {output_path}")
return results
def save_results(results: list, output_path: str, format_type: str):
"""保存识别结果到文件"""
if format_type == "txt":
# 纯文本格式
with open(output_path, 'w', encoding='utf-8') as f:
for r in results:
f.write(r["text"] + "\n")
elif format_type == "srt":
# SRT 字幕格式
with open(output_path, 'w', encoding='utf-8') as f:
for r in results:
f.write(f"{r['id']}\n")
f.write(f"{r['start_formatted']} --> {r['end_formatted']}\n")
f.write(f"{r['text']}\n\n")
elif format_type == "json":
# JSON 格式
with open(output_path, 'w', encoding='utf-8') as f:
json.dump(results, f, ensure_ascii=False, indent=2)
elif format_type == "vtt":
# WebVTT 格式
with open(output_path, 'w', encoding='utf-8') as f:
f.write("WEBVTT\n\n")
for r in results:
# VTT 使用点号分隔毫秒
start = r['start_formatted'].replace(',', '.')
end = r['end_formatted'].replace(',', '.')
f.write(f"{start} --> {end}\n")
f.write(f"{r['text']}\n\n")
def batch_process(
input_paths: list,
model_size: str = "base",
**kwargs
):
"""批量处理多个文件"""
for path in input_paths:
print(f"\n{'='*50}")
print(f"处理文件: {path}")
print(f"{'='*50}")
ext = Path(path).suffix.lower()
# 如果是视频,先提取音频
if ext in ['.mp4', '.avi', '.mkv', '.mov', '.flv', '.wmv', '.webm']:
audio_path = extract_audio(path)
else:
audio_path = path
# 转文字
transcribe(audio_path, model_size=model_size, **kwargs)
def main():
parser = argparse.ArgumentParser(
description='视频/音频转文字工具 (基于 faster-whisper)',
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
示例:
# 识别单个音频文件
python3 transcribe.py audio.mp3
# 识别视频并生成字幕
python3 transcribe.py video.mp4 -f srt -l zh
# 使用大模型提高准确率(需要更多显存)
python3 transcribe.py audio.mp3 --model large-v3 --device cuda
# 批量处理
python3 transcribe.py *.mp3 --model small
# 输出JSON(带时间戳)
python3 transcribe.py audio.mp3 -f json
模型大小说明:
tiny - 39M 参数, 最快, 准确率一般
base - 74M 参数, 较快, 日常够用
small - 244M 参数, 平衡选择
medium - 769M 参数, 较准, 较慢
large-v3 - 1.5B 参数, 最准, 最慢, 需8G+显存
"""
)
parser.add_argument('input', nargs='+', help='输入文件路径(支持视频/音频)')
parser.add_argument('-m', '--model', default='base',
choices=['tiny', 'base', 'small', 'medium', 'large', 'large-v2', 'large-v3'],
help='模型大小 (默认: base)')
parser.add_argument('-d', '--device', default='auto',
choices=['cuda', 'cpu', 'auto'],
help='计算设备 (默认: auto)')
parser.add_argument('-c', '--compute', default='default',
choices=['float16', 'float32', 'int8', 'int8_float16', 'default'],
help='计算精度 (默认: 自动)')
parser.add_argument('-l', '--language', default='zh',
help='语言代码,如 zh, en, ja (默认: zh)')
parser.add_argument('-f', '--format', default='txt',
choices=['txt', 'srt', 'json', 'vtt'],
help='输出格式 (默认: txt)')
parser.add_argument('-o', '--output', default=None,
help='输出文件路径 (默认: 同输入文件名)')
parser.add_argument('--no-vad', action='store_true',
help='禁用语音活动检测')
args = parser.parse_args()
# 批量处理
batch_process(
args.input,
model_size=args.model,
device=args.device,
compute_type=args.compute,
language=args.language,
output_format=args.format,
output_path=args.output,
vad_filter=not args.no_vad
)
if __name__ == "__main__":
main()本作品采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。
微信
支付宝