Ana içeriğe geç
友田 陽大
Voice AI
Python
音声認識
OpenAI API
アーキテクチャ設計
パフォーマンス
可観測性

OpenAI Whisper 本番運用ガイド:セルフホスト(large-v3-turbo)と Audio API(gpt-4o-transcribe)を使い分ける文字起こし設計

OpenAI Whisper を本番品質で使うための実装ガイド。公式ドキュメントに忠実なモデル一覧(large-v3 / turbo)と Audio API(whisper-1 / gpt-4o-transcribe / gpt-4o-mini-transcribe)を整理し、セルフホスト vs API の選定フレームワーク、25MB制限の回避、SRT字幕生成、固有名詞のprompt誘導、幻覚(hallucination)対策、冪等・再開・可観測性まで、実コードで解説します。

Published
Reading time
16 min read
Author
友田 陽大
Paylaş

「音声を文字起こししたい」——要件としては一行です。けれど本番に載せようとした瞬間、判断すべきことが一気に増えます。セルフホストするのか API を叩くのか。どのモデルを選ぶのか。25MB を超える長尺音声はどうするのか。固有名詞の誤変換をどう抑えるのか。無音区間で湧く「幻覚(hallucination)」をどう殺すのか。

この記事は、OpenAI Whisper を本番品質で運用するための実装ガイドです。題材として、私が国内大手放送事業者向けに構築した社内AIプラットフォーム(テロップ誤字検出パイプラインで「発話の文字起こし=耳」として音声認識を使用)での設計判断も交えます。

この記事のルール:モデル仕様・API仕様・価格は OpenAI公式ドキュメント(2026年6月時点) に基づきます。価格やモデル名は改定されるため、本番投入前に必ず公式の料金ページで最新値を確認してください。コードは実運用で使える形に整えていますが、シークレットは環境変数前提です(ハードコード厳禁)。


0. 最初の分岐:「Whisper」という言葉には2つの実体がある

ここを混同したまま設計を始めると、後で必ず詰まります。Whisper には性質の異なる2つの提供形態があります。

① セルフホスト(OSS)② OpenAI Audio API
実体openai-whisper(PyPI)/ Hugging Face の重みapi.openai.com のホスト型エンドポイント
代表モデルlarge-v3 / turbo(large-v3-turbo)whisper-1 / gpt-4o-transcribe / gpt-4o-mini-transcribe
実行場所自前の GPU / CPU(音声が外に出ない)OpenAI のサーバー(音声を送信する)
課金計算リソース(GPU時間)分課金 or トークン課金
ファイル上限なし(メモリ次第)25MB / 1リクエスト
主な強みプライバシー・固定費・無制限長運用ゼロ・高精度・即日導入

「Whisper を使う」と言ったとき、この①と②のどちらを指しているのかを最初に確定させてください。本記事は両方を扱い、選定基準(第3章)まで示します。


1. セルフホスト版:最新モデルと正しい使い方

1.1 モデル一覧(公式の数値)

公式 README のモデル表は次のとおりです。Relative speedlarge を 1x とした相対値です。

SizeParameters必要VRAM(目安)相対速度English-only (.en)
tiny39M~1GB~10xあり
base74M~1GB~7xあり
small244M~2GB~4xあり
medium769M~5GB~2xあり
large1550M~10GB1xなし(多言語のみ)
turbo809M~6GB~8xなし

turbo(= large-v3-turbo)が現行のスイートスポットです。large-v3 のデコーダ層を 32層 → 4層 に削った蒸留・剪定モデルで、精度の劣化を最小限に抑えつつ約8倍速を実現しています。日本語を含む多言語の「文字起こし」用途なら、まず turbo を試すのが定石です。

公式が明記する turbo の落とし穴:turbo は翻訳データを除いて学習されているため、--task translate を指定しても元言語のまま返ることがあります。「日本語音声 → 英語テキスト」の翻訳が欲しいときは mediumlarge を使ってください。英語のみのアプリでは .en モデルの方が精度が出る、とも公式は述べています。

1.2 インストール(ffmpeg必須)

# Python パッケージ
pip install -U openai-whisper

# 音声デコードに ffmpeg が必須(OSごとに導入)
# macOS:        brew install ffmpeg
# Ubuntu/Debian: sudo apt update && sudo apt install ffmpeg

1.3 CLI:まず動かす

# turbo で文字起こし(複数ファイルもまとめて渡せる)
whisper audio.mp3 --model turbo --language Japanese --output_format srt

# 日本語音声を「英語に翻訳」したい場合は medium/large + translate
whisper meeting.wav --model large --language Japanese --task translate

--output_formatsrt / vtt / txt / json を指定でき、字幕ファイルがそのまま出力されます。字幕生成が目的なら、自前でタイムスタンプを組み立てる前に CLI の出力形式で足りないかを先に確認してください(YAGNI)。

1.4 Python API:本番で使う形

最小例は3行ですが、本番では言語固定・単語タイムスタンプ・幻覚抑制を入れます。

import whisper

# モデルは一度だけロードして使い回す(プロセス内で再利用 = コールドスタート回避)
model = whisper.load_model("turbo")

result = model.transcribe(
    "audio.mp3",
    language="ja",            # 言語を固定 → 言語自動検出のブレと初動コストを排除
    word_timestamps=True,     # 単語単位の時刻(字幕・検索・ハイライトに必須)
    # --- 幻覚(hallucination)抑制の三点セット(詳細は第6章) ---
    condition_on_previous_text=False,  # 直前文への引きずられを断つ
    no_speech_threshold=0.6,           # 無音判定を厳しめに
    temperature=0,                     # 決定的に(再現性とテスト容易性)
)

print(result["text"])
for seg in result["segments"]:
    print(f"[{seg['start']:6.2f}{seg['end']:6.2f}] {seg['text'].strip()}")

word_timestamps=True を付けると、各 segmentwordsword / start / end / probability)が入ります。probability の単語=モデルが自信のない箇所なので、校正UIで黄色くハイライトする、といった「人間の確認ゲート」を設計に組み込めます。

設計のコツmodel はリクエストごとにロードしないこと。large 系は読み込みだけで数秒〜十数秒かかります。Web サーバーならプロセス起動時に一度ロードし、ワーカーで共有します(SRP:ロードと推論を分離)。


2. OpenAI Audio API 版:運用ゼロで高精度を取りに行く

GPU を持ちたくない・即日で精度の高い文字起こしが欲しい——その場合はホスト型 API が最短です。

2.1 モデルとエンドポイント(公式の対応表)

モデル用途対応 response_formatストリーミング
gpt-4o-transcribe最高精度の文字起こしjson / text✅ (stream=True)
gpt-4o-mini-transcribe低コスト・高速json / text
gpt-4o-transcribe-diarize話者分離つきdiarized_json ほか
whisper-1旧来の万能型・字幕形式が豊富json / text / srt / verbose_json / vtt

gpt-4o-transcribe 系は GPT-4o を基盤に WER(単語誤り率)を改善した新世代で、認識精度と言語判定が向上しています。一方で SRT/VTT などの字幕形式や verbose_json の細粒度タイムスタンプが必要なら whisper-1 を選びます。「精度重視=gpt-4o系、字幕の機械可読フォーマット重視=whisper-1」と覚えると迷いません。

  • Transcriptions(文字起こし):音声 →「元言語の」テキスト。
  • Translations(翻訳):音声 → 英語テキスト。whisper-1 専用です。

2.2 基本:Python と TypeScript

from openai import OpenAI

client = OpenAI()  # APIキーは環境変数 OPENAI_API_KEY から(ハードコード禁止)

with open("speech.mp3", "rb") as audio_file:
    transcription = client.audio.transcriptions.create(
        model="gpt-4o-transcribe",
        file=audio_file,
        language="ja",  # 言語を渡すと精度とレイテンシが安定する
    )

print(transcription.text)

Next.js / Node(このサイトと同じ TypeScript)なら:

import OpenAI from "openai";
import { createReadStream } from "node:fs";

const openai = new OpenAI(); // process.env.OPENAI_API_KEY を自動参照

const transcription = await openai.audio.transcriptions.create({
  file: createReadStream("audio.mp3"),
  model: "gpt-4o-transcribe",
  language: "ja",
});

console.log(transcription.text);

2.3 字幕(SRT)と単語タイムスタンプ:whisper-1

機械可読な字幕や単語単位の時刻が要るときは whisper-1 + verbose_json を使います。

with open("lecture.mp3", "rb") as audio_file:
    result = client.audio.transcriptions.create(
        model="whisper-1",
        file=audio_file,
        language="ja",
        response_format="verbose_json",
        timestamp_granularities=["segment", "word"],  # verbose_json 必須
    )

# セグメント単位(字幕のかたまり)
for seg in result.segments:
    print(f"[{seg.start:.2f}-{seg.end:.2f}] {seg.text}")

# 単語単位(検索インデックス・ハイライトに)
for w in result.words:
    print(w.word, w.start, w.end)

SRT ファイルそのものが欲しいだけなら response_format="srt" を指定すれば、変換コードを書かずに字幕テキストが返ります(DRY:自前のSRTシリアライザを書かない)。

2.4 固有名詞・専門用語を正しく綴らせる:prompt

文字起こしで最も多いクレームは固有名詞・社名・専門用語の誤変換です。prompt パラメータに「期待する綴り」を渡すと、モデルがそれに寄せてくれます。

GLOSSARY = "登場する固有名詞: 友田陽大, ハコキット, Next.js, Supabase, RLS, gpt-4o-transcribe"

with open("podcast.mp3", "rb") as audio_file:
    transcription = client.audio.transcriptions.create(
        model="gpt-4o-transcribe",
        file=audio_file,
        language="ja",
        prompt=GLOSSARY,  # ドメイン語彙を事前に教える
    )

これは安価で効果が大きいチューニング手段です。番組制作の文字起こしでは「人名・番組名・商品名」の表記ゆれが事故に直結するため、案件ごとに用語集を prompt に流し込む運用が効きます。

2.5 ストリーミング:話しながら出す(gpt-4o系)

会議の議事録やライブ字幕のように「待たせたくない」UXでは、stream=True で部分結果を逐次受け取れます。

stream = client.audio.transcriptions.create(
    model="gpt-4o-transcribe",
    file=open("speech.mp3", "rb"),
    response_format="text",
    stream=True,  # gpt-4o-transcribe / mini で利用可
)

for event in stream:
    # delta: 増分テキスト / done: 確定
    if event.type == "transcript.text.delta":
        print(event.delta, end="", flush=True)
    elif event.type == "transcript.text.done":
        print("\n--- 確定 ---")

マイク入力をリアルタイムに文字化する用途では、ファイルストリーミングではなく Realtime API(WebSocket / WebRTC)を使うのが本筋です。「録音済みファイルの逐次返却」と「ライブの逐次認識」は別物なので、要件を取り違えないでください。


3. 選定フレームワーク:セルフホスト vs API をどう決めるか

「どっちが正解」ではなく、4つの軸で要件に合う方を選びます。

判断軸セルフホスト(turbo/large)が有利Audio API が有利
プライバシー医療・行政・社外秘で音声を外に出せない外部送信が許容される
コスト構造大量・常時稼働で固定費にしたい(GPU償却)少量・スパイク的で変動費が合理的
長さ数時間の長尺を1本で回したい(上限なし)25MB に収まる/分割前提でよい
運用体制GPU・モデル更新・スケールを運用できる運用したくない/即日で精度が欲しい

コストの直感

公式の分課金(2026年6月時点・要確認)はおおむね次の水準です。

  • whisper-1 / gpt-4o-transcribe$0.006/分(≒ $0.36/時)
  • gpt-4o-mini-transcribe$0.003/分(≒ $0.18/時)

月に数十時間程度ならAPIが圧倒的に安い(GPUを1枚借りるより安く、運用ゼロ)。逆に、毎日数百時間を常時処理するようなワークロードでは、turbo をGPUインスタンスで回した方が単価が下がる損益分岐点が現れます。「まずAPIで価値検証 → 量が読めてからセルフホスト移行を検討」が、多くの案件で正しい順序です(YAGNI / コスト効率)。


4. 25MB の壁を越える:長尺音声のチャンク分割

API のファイル上限は 25MB。1時間の会議録音は普通に超えます。無音区間(VAD)で切るのが定石で、文の途中でぶつ切りにしないため精度劣化を抑えられます。

"""長尺音声を無音で分割し、各チャンクを文字起こしして連結する。
固定長で切ると単語が割れるため、無音境界で切るのが要点。"""
from openai import OpenAI
from pydub import AudioSegment, silence

client = OpenAI()

def split_on_silence_bounded(path: str, max_ms: int = 15 * 60_000) -> list[AudioSegment]:
    """無音を境界に、各チャンクを max_ms 以下に収める(25MB制限の安全側)。"""
    audio = AudioSegment.from_file(path)
    silences = silence.detect_silence(audio, min_silence_len=700, silence_thresh=-40)
    cut_points = [s[0] for s in silences] + [len(audio)]

    chunks: list[AudioSegment] = []
    start = 0
    for point in cut_points:
        if point - start >= max_ms:
            chunks.append(audio[start:point])
            start = point
    if start < len(audio):
        chunks.append(audio[start:])
    return chunks

def transcribe_long(path: str, language: str = "ja") -> str:
    parts: list[str] = []
    context = ""  # 直前チャンク末尾を次の prompt に渡し、境界の文脈を維持
    for i, chunk in enumerate(split_on_silence_bounded(path)):
        buf = chunk.export(format="mp3", bitrate="64k")  # 軽量化で25MBに余裕
        res = client.audio.transcriptions.create(
            model="gpt-4o-transcribe",
            file=("chunk.mp3", buf, "audio/mpeg"),
            language=language,
            prompt=context[-200:],  # 境界をまたぐ固有名詞・文脈のヒント
        )
        parts.append(res.text)
        context = res.text
    return "\n".join(parts)

ポイントは3つです。

  1. 無音で切る:固定長分割は単語を割って精度を落とす。VAD境界で切る。
  2. ビットレートを落とす:文字起こしに高音質は不要。64kbps mp3 で十分小さくなり、25MB の心配が消える(コスト効率)。
  3. チャンク間の文脈を渡す:前チャンク末尾を次の prompt に入れ、境界をまたぐ固有名詞・話題の連続性を保つ。

5. 本番運用設計:冪等・リトライ・可観測性

文字起こしは「長時間・外部API・課金あり」のジョブです。素朴に呼ぶと、途中失敗で全部やり直し&二重課金になります。放送局向けプラットフォームで実際に効いた設計を、最小の形で示します。

5.1 チャンク単位の冪等キャッシュ

各チャンクを内容ハッシュをキーにキャッシュすれば、再実行で完了済みチャンクをスキップでき、リトライが冪等になります(=部分失敗からの再開=二重課金の回避)。

import hashlib
from pathlib import Path

def chunk_key(data: bytes, model: str, language: str) -> str:
    """音声内容+パラメータで決まる決定的キー。同入力 → 同キー → キャッシュヒット。"""
    h = hashlib.sha256()
    h.update(data)
    h.update(f"{model}:{language}".encode())
    return h.hexdigest()

def transcribe_chunk_idempotent(data: bytes, model: str, language: str, cache_dir: Path) -> str:
    key = chunk_key(data, model, language)
    cached = cache_dir / f"{key}.txt"
    if cached.exists():
        return cached.read_text(encoding="utf-8")  # 再実行はAPIを叩かない(冪等・節約)

    res = client.audio.transcriptions.create(
        model=model, file=("c.mp3", data, "audio/mpeg"), language=language,
    )
    cached.write_text(res.text, encoding="utf-8")
    return res.text

5.2 指数バックオフ付きリトライ

外部APIはレート制限・一時障害で必ず失敗します。冪等な操作にだけリトライを掛けます。

import time
from openai import APIConnectionError, RateLimitError, APIStatusError

def with_retry(fn, *, max_attempts: int = 4, base: float = 1.0):
    """指数バックオフ。リトライ対象を限定し、4xx(=入力不正)は即時失敗させる。"""
    for attempt in range(1, max_attempts + 1):
        try:
            return fn()
        except (RateLimitError, APIConnectionError) as e:
            if attempt == max_attempts:
                raise
            time.sleep(base * (2 ** (attempt - 1)))  # 1s, 2s, 4s, ...
        except APIStatusError as e:
            if 400 <= e.status_code < 500:
                raise  # 入力不正はリトライしても無駄 → 即失敗(fail fast)
            if attempt == max_attempts:
                raise
            time.sleep(base * (2 ** (attempt - 1)))

5.3 可観測性:何を必ず記録するか

文字起こしジョブでは、本文(PII)ではなくメタデータを記録します(プライバシーと可観測性の両立)。

  • ジョブID / チャンク番号 / 入力ハッシュ / モデル / 言語
  • 処理時間(音声長に対するリアルタイム比 = RTF)
  • 課金量(分 or トークン)と推定コスト
  • 失敗種別(レート制限 / タイムアウト / 入力不正)とリトライ回数

これらを構造化ログ(OpenTelemetry 等)で出すと、「どのチャンクで詰まったか」「コストが妥当か」が一目で追えます。本文ログにPII(個人名・連絡先)を残さないのは、内部統制案件の絶対条件です。


6. 幻覚(Hallucination)と無音問題:精度の最後の1割

Whisper の既知の弱点は、無音・雑音・BGMだけの区間で「ありもしない文」を生成することです。「ご視聴ありがとうございました」のような定型句が湧くのは典型例です。本番では次の多層対策で殺します。

  1. 前処理でVAD(音声区間検出):発話のない区間を推論前に落とす。silero-vad などで無音を除けば、幻覚の温床自体が消える。
  2. condition_on_previous_text=False:直前テキストへの引きずられ(一度湧いた幻覚の連鎖)を断つ。
  3. しきい値で捨てるno_speech_threshold を上げ、logprob_threshold で低確信セグメントを破棄。
  4. 独立した第二の根拠で照合:最強の対策はクロスチェックです。放送局のテロップ誤字検出では、「画面のテロップ(OCR=目)」と「発話の文字起こし(ASR=耳)」という独立した2系統を突き合わせ、食い違いを検出しました。単一情報源の確信度ではなく、2つの情報源の不一致を手がかりにすると、幻覚も誤変換も浮かび上がります。

1情報源の「自信」は当てになりません。**独立した2つの経路の「食い違い」**こそが信頼できる検出シグナルになる——これは音声認識に限らず、AIを本番に載せるときの一般原則です。


7. セキュリティとプライバシー:どこで線を引くか

  • 音声は個人情報になり得る:声・氏名・連絡先・病歴が含まれ得ます。社外秘・要配慮個人情報を含む音声はAPIに送れないことが多い。その場合はセルフホスト(turbo/large)一択です。
  • APIキーはサーバー側に:ブラウザから直接 OpenAI を叩かない(キー漏洩)。Next.js なら Route Handler / Server Action 経由で、キーは環境変数に。
  • 入力の検証:ファイル形式(mp3/mp4/mpeg/mpga/m4a/wav/webm)・サイズ(25MB)・長さを境界でバリデーションしてから API に渡す。ユーザー由来の入力を素通しにしない。
  • 保存方針の明示:文字起こし結果を保存するなら、保持期間・暗号化・削除フローを最初に決める。PIIは AES-256-GCM 等で暗号化し、検索は復号せずトークン化(HMAC)で部分一致させる、といった設計が内部統制では標準です。

8. まとめ:選定チートシート

最後に、迷ったときの早見表です。

  • とりあえず精度高く・即日でgpt-4o-transcribe(API)。言語を渡し、固有名詞は prompt で誘導。
  • コストを最優先gpt-4o-mini-transcribe(API、$0.003/分)。
  • SRT/VTT字幕・単語タイムスタンプが要るwhisper-1 + verbose_json
  • 音声を外に出せない/長尺を大量に回す:セルフホスト turbo(多言語)/ large(翻訳)。
  • ライブ字幕:Realtime API。録音ファイルの逐次返却なら stream=True
  • 本番化の共通装備:無音分割・冪等キャッシュ・指数バックオフ・PIIを出さない可観測性・幻覚の多層対策。

文字起こしは「一行の要件」に見えて、コスト・精度・プライバシー・信頼性のトレードオフを設計する仕事です。私は放送事業者向けの社内AIプラットフォームで、音声認識を「テロップ誤字検出の第二の目」として本番運用し、冪等・再開・可観測性を担保した長時間ジョブとして組み上げました。

「自社の音声をどう文字起こしし、どう業務に組み込むか」——その設計から実装・運用まで、一気通貫で伴走できます。 要件の整理段階からでも、お気軽にご相談ください。


参考(公式ドキュメント)

友田

友田 陽大

Bakanlık (METI) Ödülü kazanan bir ürünün geliştiricisi. TypeScript + Python + AWS ile SaaS, endüstriyel dijital dönüşüm ve üretime hazır üretken yapay zekâyı (RAG) gereksinimlerden altyapıya ve işletmeye kadar tek başıma uçtan uca sunuyorum.

Not sure which model or architecture to pick?

Model selection and quality-evaluation design for source separation, lip-sync, and TTS

Demucs / MDX-Net / BS-RoFormer, MuseTalk / LatentSync, and hosted TTS versus self-hosting — compared on quantitative evaluation, commercial license terms, and GPU cost. Having actually shipped faster-whisper, MuseTalk, and LatentSync into a production pipeline and tuned the quality-versus-speed trade-off stage by stage, I help you lock down the architecture before implementation as a technical advisor.

Hem proje bazlı işler hem de teknik danışmanlık için uygunum. 30 dakikalık ücretsiz bir görüşmeyle başlayın.

最短ルート:カレンダーから直接予約

相談内容が固まっている方は、フォーム送信よりその場で日程を確定する方がスムーズです。下記から空き時間をお選びください。

  • 30分のオンライン無料相談
  • Google Meet / Zoom / Microsoft Teams
  • NDA 商談前締結可・無理な営業はいたしません
無料相談の空き枠を予約する

Bunlar da ilginizi çekebilir