Whisper OpenAI:PythonによるAI音声テキスト変換の実践ガイド

Artificial Intelligence tutorial - IT technology blog
Artificial Intelligence tutorial - IT technology blog

社内ポッドキャストやミーティング録音のトランスクリプションに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%は解決できる。

Share: