Whisper:影音轉錄字幕檔
faster-whisper 是 OpenAI Whisper 的加速實作,可以在本機把影片或音訊轉錄成字幕。
以前我都只用預設參數,後來請 LLM 幫我把幾個實用參數補上,轉錄品質明顯穩定許多。
環境準備
開始前需要三樣東西:
- Python:安裝時記得勾選「Add Python to PATH」,裝完在終端機輸入
python --version確認裝好。 - faster-whisper 套件:在終端機執行
pip install faster-whisper安裝。 - ffmpeg:負責讀取影音檔,系統要裝好並加進 PATH(Windows 可用
winget install ffmpeg安裝)。
資訊
模型檔不用自己下載,第一次執行時程式會自動抓,請耐心等候。
載入模型
from faster_whisper import WhisperModel
model = WhisperModel("large-v3", device="cuda", compute_type="int8_float16")
| 參數 | 用途 |
|---|---|
large-v3 | 模型大小,越大越準、越吃資源也越慢。常見有 tiny、base、small、medium、large-v3,電腦跑不動就往小的換。另外還有 turbo,是 large-v3 的精簡版,速度快 5~6 倍、準度接近但略低,求快可改用它 |
device="cuda" | 用 Nvidia GPU 跑(沒有獨立顯卡就改 "cpu",速度會比較慢) |
compute_type="int8_float16" | 模型的運算精度,數字越精細越準、越吃顯卡記憶體。GPU 可用 "int8_float16"、"float16" 或 "int8";CPU 建議直接指定 "int8" |
轉錄範例
segments, info = model.transcribe(
"input.mp4",
language="ja",
beam_size=5,
vad_filter=True,
vad_parameters=dict(
min_silence_duration_ms=500,
max_speech_duration_s=10,
),
condition_on_previous_text=False,
no_speech_threshold=0.6,
temperature=[0.0, 0.2, 0.4, 0.6],
word_timestamps=True,
)
| 參數 | 用途 |
|---|---|
language="ja" | 指定影片語言(ja 是日文),語言代碼可在 Whisper 支援語言清單 查到 |
beam_size=5 | 搜尋寬度,越大越準但越慢 |
vad_filter=True | 開啟語音活動偵測,自動跳過無人聲的片段 |
min_silence_duration_ms=500 | 靜音超過 0.5 秒才切句,避免把一句話切得太碎 |
max_speech_duration_s=10 | 單段語音上限 10 秒,避免連續說話沒停頓時被拼成一句超長字幕 |
condition_on_previous_text=False | 不把前一段當提示,避免前面認錯字污染後面(上下文能力會變弱) |
no_speech_threshold=0.6 | 判定為「無語音」的門檻,過濾掉雜訊段落 |
temperature=[0.0, 0.2, 0.4, 0.6] | 卡住時自動換溫度重試,從最保守往上加 |
word_timestamps=True | 逐字時間戳,把句尾收斂到最後一個字,避免一句字幕卡很久 |
寫出 SRT
transcribe 回傳的是惰性 generator,可以邊跑邊寫檔,這樣轉錄過程中隨時都有部分成品:
def format_timestamp(seconds: float) -> str:
ms = int(round(seconds * 1000))
h, ms = divmod(ms, 3600_000)
m, ms = divmod(ms, 60_000)
s, ms = divmod(ms, 1000)
return f"{h:02d}:{m:02d}:{s:02d},{ms:03d}"
with open("output.jp.srt", "w", encoding="utf-8") as f:
for i, seg in enumerate(segments, start=1):
# 用逐字時間收斂首尾,而不是整段的 seg.start/seg.end
if seg.words:
start, end = seg.words[0].start, seg.words[-1].end
else:
start, end = seg.start, seg.end
f.write(f"{i}\n")
f.write(f"{format_timestamp(start)} --> {format_timestamp(end)}\n")
f.write(f"{seg.text.strip()}\n\n")
用首尾字的時間而不是整段的 seg.start/seg.end,是因為 Whisper 常把說完話後的靜音也算進去,字幕會掛在畫面上好幾秒。
如何翻成中文
| 方式 | 優點 | 缺點 |
|---|---|---|
| translatesubtitles.co(Google 翻譯) | 一秒翻完,免安裝 | 不吃上下文,翻得很爛 |
| 線上 LLM(ChatGPT、Gemini) | 快、品質好,還能順便總結 | 可能被審查,內容會上傳 |
| 本地 LLM | 不審查、資料不外流 | 要自己架,見「Ollama:用本地 LLM 翻譯字幕」 |