会議の録音、顧客との通話、セミナー動画——「あとで文字にしたい音声」は現場に溢れています。コールセンターの事例研究で書いたとおり、音声認識(ASR)は「使える閾値」を越え、業務システムの部品になりました。

このハンズオンでは、その部品を自分の手で動かします。題材はWhisper[1]——正確には、その高速再実装であるfaster-whisper[3]を使い、日本語音声の書き起こしパイプラインをローカルで組みます。API課金なし・音声を外部に送らない構成なので、社内音声の検証にも使いやすいはずです。

本記事のコードは faster-whisper 1.2.1 / macOS 26 / CPU(int8量子化)で動作確認済みです。実行結果もその実測値を掲載しています。

なぜ faster-whisper か

Whisperの公式実装[2]はPyTorchベースで、そのまま使うとCPUでは重めです。faster-whisperは推論エンジンCTranslate2による再実装で、同じモデル・ほぼ同じAPIのまま、数倍速く・省メモリに動きます。GPUがない環境でも、int8量子化でsmallモデルが実用速度で回ります。ローカル書き起こしの現在の定番です。

セットアップと最小コード

python -m venv .venv && source .venv/bin/activate
pip install faster-whisper

書き起こしの最小コードはこれだけです。

from faster_whisper import WhisperModel

# GPUがあれば device="cuda"。CPUなら int8 量子化が実用的
model = WhisperModel("small", device="cpu", compute_type="int8")

segments, info = model.transcribe(
    "sample.wav",
    language="ja",     # 言語が分かっているなら指定した方が安定する
    vad_filter=True,   # 無音区間をスキップ(長い録音・通話で効く)
)

print(f"言語: {info.language}(確度 {info.language_probability:.2f})")
for seg in segments:
    print(f"[{seg.start:7.2f}s -> {seg.end:7.2f}s] {seg.text}")

初回実行時はモデル(smallで約500MB)が自動ダウンロードされます。segmentsはジェネレータで、イテレートした時点で初めて推論が走る点だけ注意してください(リストにしたければ list(segments))。

実測 — 動かすと何が起きるか

手元で検証用の音声(合成音声で「お電話ありがとうございます。本日はどのようなご用件でしょうか。先日購入した製品の返品についてお伺いしたいです」と読み上げたWAV)を作り、モデルサイズを変えて書き起こした実測結果です。

モデル出力(実測)
tiny「HASILA、SFG。」(崩壊。日本語として成立せず)
small「お電話ありがとうございます。本日はどのようなご要件でしょうか。先日購入した製品の変品についてお伺いしたいです。」

この実測から、実務に直結する教訓が2つ読み取れます。

  1. 日本語はsmall以上を使う。tinyやbaseは英語なら動きますが、日本語では実用になりません。品質重視なら large-v3、速度と品質のバランスなら small〜medium が目安です
  2. 誤りは「同音異義語」に集中する。smallの結果は文構造こそ完璧ですが、「用件→要件」「返品→変品」と、音が同じ単語の変換を外しています。ASRの誤りはランダムではなく、ドメイン用語と同音異義語に偏る——この性質が、次の補正テクニックにつながります

ドメイン用語の補正 — initial_prompt と後処理

固有名詞・専門用語の誤変換への対策は2段構えです。

segments, info = model.transcribe(
    "sample.wav",
    language="ja",
    vad_filter=True,
    # 冒頭の「文脈」として用語を与えると、表記が引っ張られる
    initial_prompt="通話記録。返品、用件、注文番号、配送状況。",
)
  • initial_prompt: モデルに「直前の文脈」として渡すテキストで、ドメイン用語や表記(漢字/カタカナ)をここに並べると出力がその表記に寄ります。万能ではありませんが、コストゼロで試せる第一手です
  • 後処理の置換辞書: それでも残る定番の誤変換(社名・製品名)は、正規表現の置換テーブルで補正します。コールセンター事例で「実務の大半は用語整備」と書いたのは、まさにこの層のことです

フォルダ一括処理パイプライン

実務では「録音フォルダを毎晩まとめてテキスト化」が基本形です。

from pathlib import Path
from faster_whisper import WhisperModel

AUDIO_DIR = Path("recordings")
OUT_DIR = Path("transcripts")
OUT_DIR.mkdir(exist_ok=True)

model = WhisperModel("small", device="cpu", compute_type="int8")

for audio in sorted(AUDIO_DIR.glob("*.[wm][ap][v3]*")):  # wav / mp3 / m4a
    out = OUT_DIR / f"{audio.stem}.txt"
    if out.exists():
        continue  # 処理済みはスキップ(再実行に強くする)
    segments, _ = model.transcribe(str(audio), language="ja", vad_filter=True)
    text = "\n".join(seg.text.strip() for seg in segments)
    out.write_text(text, encoding="utf-8")
    print(f"done: {audio.name}")

「処理済みスキップ」を入れておくと、失敗しても再実行するだけで続きから走ります。夜間バッチはこの冪等性が生命線です。

おまけ: 字幕(SRT)を出す

タイムスタンプがあるので、字幕ファイルも数行で作れます。

def to_srt(segments) -> str:
    def fmt(t: float) -> str:
        h, rest = divmod(t, 3600)
        m, s = divmod(rest, 60)
        return f"{int(h):02}:{int(m):02}:{int(s):02},{int((s % 1) * 1000):03}"

    lines = []
    for i, seg in enumerate(segments, 1):
        lines += [str(i), f"{fmt(seg.start)} --> {fmt(seg.end)}", seg.text.strip(), ""]
    return "\n".join(lines)

動画に字幕を焼きたい場合は、この出力をffmpegに渡すだけです。

運用に載せる前の注意

  • 音声は要配慮の塊: 通話・会議録音には個人情報が含まれます。ローカル処理はその点で有利ですが、書き起こしテキストの保存先・アクセス権・保持期間の設計はデータ管理の問題として残ります
  • 話者分離は別技術: 「誰が話したか」(ダイアライゼーション)はWhisperの守備範囲外で、pyannote系など別のモデルを組み合わせます。まず「全文が文字になる」価値から始めるのが定石です
  • リアルタイムは別設計: 本記事はバッチ処理です。通話中のリアルタイム書き起こしはストリーミング対応の構成が別途必要で、難易度が一段上がります

次のステップ — 書き起こしは素材にすぎない

テキスト化した瞬間、音声はLLMの入力になります。要約して対応記録の下書きにする、構造化出力で用件・約束事項を抽出する、全文を検索対象にする——コールセンターの4層スタックの残り3層は、今日作ったこのパイプラインの上に載ります。

まとめ

  • faster-whisperなら、GPUなし・API課金なしで日本語書き起こしがローカルで動く
  • 日本語はsmall以上。tinyは実測で崩壊した。品質重視ならlarge-v3
  • ASRの誤りは同音異義語・ドメイン用語に偏る。initial_promptと置換辞書の2段で補正する
  • 実務の基本形は「フォルダ一括+処理済みスキップ」の冪等なバッチ
  • 話者分離・リアルタイムは別技術。まず「文字になる」価値から始める

30分あれば、手元の録音が検索できるテキストに変わります。まずは自分の会議録音1本から試してみてください。

参考文献・一次情報

  1. [1]
    PAPERRobust Speech Recognition via Large-Scale Weak Supervision (Whisper)

    Alec Radford, Jong Wook Kim, 他2022DOI: 10.48550/arXiv.2212.04356

    Whisperの原論文。68万時間の弱教師あり学習による頑健なASR

  2. [2]
    OFFICIALopenai/whisper

    GitHub(OpenAI)

    Whisper公式実装。モデルサイズと言語別性能の一次情報

  3. [3]
    OFFICIALSYSTRAN/faster-whisper

    GitHub(SYSTRAN)

    CTranslate2によるWhisper再実装。公式実装より高速・省メモリ