社内ポッドキャストやミーティング録音のトランスクリプションにWhisperを6ヶ月間本番環境で運用して、ほとんどのチュートリアルが触れていない重要なポイントをいくつか掴んできた。この記事では核心に直接切り込む:どのアプローチを選ぶべきか、その理由、そして実際の導入方法について共有する。
1. Whisperを使う3つの主要アプローチ
Whisperに初めて触れたとき、3つの明確な方向性があることがわかった:
- Whisperローカル(オープンソース) — マシン/サーバー上で直接実行、APIコスト不要
- Whisper API(OpenAI) — APIで呼び出し、音声の分単位で課金
- faster-whisper — CTranslate2でWhisperを再実装、オリジナルより4〜5倍高速
3つのアプローチのアウトプットは一見似ているが、選択を誤るとすぐに後悔する — 速度、コスト、プライバシーの面でまったく異なるものだ。
2. 各アプローチのメリット・デメリット分析
Whisperローカル(openai-whisper)
OpenAI公式リポジトリ、pipでインストール、完全オフライン動作。最大のメリットはAPIコストがなく、データがサーバー外に出ないこと — 社内音声データを扱う場合に特に重要だ。
デメリットは遅さだ。通常のCPUではlarge-v3モデルで1分の音声のトランスクリプションに3〜4分かかる。NVIDIA GPUがあれば大幅に速くなるが、すべてのサーバーに搭載されているわけではない。音声キューが積み上がりすぎたため、2週間でこの方法を断念した。
Whisper API(OpenAI)
openai.audio.transcriptions.create()で呼び出し、現在の料金は約$0.006/分 — 思っていたより安い。ファイル上限25MB、対応フォーマット:mp3、mp4、wav、webm、flacなど。
メリット:非常に高速 — 60分の音声ファイルを約1〜2分で処理、ハードウェアの心配不要。デメリット:音声をOpenAIのサーバーに送信する必要があるため機密データには不向きで、安定したインターネット接続が必要。
faster-whisper
現在4ヶ月近く本番環境で運用している。faster-whisperはローカルで動くが、INT8量子化によりオリジナルより大幅に高速だ。large-v3モデルで4コアCPUを使えば1分の音声を40〜60秒でトランスクリプション — バッチ処理には十分許容できる速度だ。
3. 適切なアプローチの選び方
まとめると以下のようになる:
- 公開音声、速さ重視、予算に余裕あり → Whisper API
- 社内/機密音声、GPUあり → openai-whisperローカル(large-v3モデル)
- 社内音声、CPUのみ、準リアルタイムが必要 → faster-whisper
自分のユースケース:社内ミーティング録音、最小限の予算、社内音声をクラウドに上げたくない → faster-whisperが明らかな選択肢だ。
4. 実践的な導入ガイド
依存関係のインストール
# まず仮想環境を作成する
python -m venv venv
source venv/bin/activate
# faster-whisperをインストール(推奨)
pip install faster-whisper
# またはOpenAI公式版を使う場合
pip install openai-whisper
# ffmpegは必須 — 音声フォーマット処理用
sudo apt install ffmpeg # Ubuntu/Debian
brew install ffmpeg # macOS
faster-whisperでトランスクリプション(ローカル)
from faster_whisper import WhisperModel
# モデルを一度ロードして複数回再利用する
# device="cpu":GPUのないサーバー向け
# compute_type="int8":RAM使用量を大幅に削減
model = WhisperModel("large-v3", device="cpu", compute_type="int8")
def transcribe_audio(audio_path: str, language: str = "vi") -> str:
"""
音声ファイルをテキストにトランスクリプションする。
language="vi"はベトナム語、Noneで自動検出。
"""
segments, info = model.transcribe(
audio_path,
language=language,
beam_size=5,
vad_filter=True, # 無音区間をフィルタリングしハルシネーションを抑制
vad_parameters=dict(min_silence_duration_ms=500)
)
# segmentsはジェネレーター — すべて収集する
transcript = " ".join(segment.text.strip() for segment in segments)
return transcript
# 使用例
result = transcribe_audio("meeting_2024.mp3")
print(result)
実践的なメモ:vad_filter=Trueは、Whisperが無音区間からテキストをハルシネーション(「空想」)する現象を確認してから追加したパラメーターだ。VADフィルターを有効にするとこの問題はほぼ完全に解決される。
Whisper API(OpenAI)でトランスクリプション
import openai
from pathlib import Path
client = openai.OpenAI(api_key="sk-...")
def transcribe_with_api(audio_path: str, language: str = "vi") -> str:
audio_file = Path(audio_path)
# 25MB超えのファイルは事前に分割が必要
if audio_file.stat().st_size > 25 * 1024 * 1024:
raise ValueError(f"ファイルサイズが大きすぎます: {audio_file.stat().st_size / 1024 / 1024:.1f}MB。上限は25MBです。")
with open(audio_path, "rb") as f:
transcript = client.audio.transcriptions.create(
model="whisper-1",
file=f,
language=language,
response_format="text" # または"json"でメタデータも取得可能
)
return transcript
result = transcribe_with_api("interview.mp3")
print(result)
長い音声ファイルの処理 — 音声の分割
30分を超えるレコーディングに直面したとき、APIに送信する前にpydubで分割している:
pip install pydub
from pydub import AudioSegment
import os
def split_audio(audio_path: str, chunk_minutes: int = 10) -> list[str]:
"""音声を小さなチャンクに分割し、ファイルパスのリストを返す。"""
audio = AudioSegment.from_file(audio_path)
chunk_ms = chunk_minutes * 60 * 1000
chunks = []
for i, start in enumerate(range(0, len(audio), chunk_ms)):
chunk = audio[start:start + chunk_ms]
chunk_path = f"/tmp/chunk_{i:03d}.mp3"
chunk.export(chunk_path, format="mp3", bitrate="64k")
chunks.append(chunk_path)
return chunks
def transcribe_long_audio(audio_path: str) -> str:
chunks = split_audio(audio_path, chunk_minutes=10)
full_transcript = []
try:
for chunk_path in chunks:
text = transcribe_with_api(chunk_path)
full_transcript.append(text)
finally:
# 一時ファイルの削除
for f in chunks:
os.unlink(f)
return " ".join(full_transcript)
タイムスタンプ付きの出力
どのセグメントがいつ発話されたかを知りたい場合(字幕作成やミーティングレビュー用)、faster-whisperはタイムスタンプを標準で返す:
def transcribe_with_timestamps(audio_path: str) -> list[dict]:
segments, _ = model.transcribe(audio_path, language="vi", vad_filter=True)
result = []
for seg in segments:
result.append({
"start": round(seg.start, 2),
"end": round(seg.end, 2),
"text": seg.text.strip()
})
return result
# 出力例:
# [{"start": 0.0, "end": 4.5, "text": "みなさん、こんにちは。今日は..."}, ...]
5. よくある問題と対処法
ベトナム語の誤認識
auto-detectに任せず、常にlanguage="vi"を指定すること。auto-detectは音質の低い音声でベトナム語を中国語やクメール語と誤認識することがある。実際にこの現象を目撃している。
largeモデルのロード時のRAM不足
faster-whisperのINT8量子化では:large-v3は約1.5〜2GB RAM、mediumは約0.8GBが必要。サーバーのRAMが少ない場合はmediumを使う — 精度はわずかに落ちるが、一般的なベトナム語には十分だ。RAM 4GBの本番ステージング環境でこの方法を適用し、かなり安定した結果が得られている。
音質が悪い音声
Whisperはある程度のノイズは処理できる。ただし、騒がしい環境での電話録音の場合は、事前にffmpegで前処理しておくとよい:
# ノーマライズ + 基本的なノイズ低減
ffmpeg -i input.mp3 -af "highpass=f=200,lowpass=f=3000,afftdn=nf=-25" output.mp3
まとめ
Whisperはベトナム語向けに使ってきた中で最高のspeech-to-textツールだ — テストしたユースケースではGoogle Speech-to-Textを大きく上回った。今からやり直すなら、サーバーのRAMに応じてmediumまたはlarge-v3モデルでfaster-whisperを直接使い、vad_filterを有効にしてlanguage="vi"をハードコードする。その手順だけでよくある問題の90%は解決できる。

