はじめに
Google Cloud ConsoleからSpeech-to-Text APIを使って、約1時間の音声ファイルを文字起こししたところ、181円かかったのでコストを抑える方法がないかを模索しました。
結果として25円に抑えることができ、約8割のコスト削減に成功しました。
簡単に試すことができるものかつ効果があったので、ログとして残します。
文字起こしでSpeech-to-Text APIを使った理由
文字起こしサービスはいっぱいありますが、ラフに音声認識をしたい時にSpeech-to-Text APIはかなり重宝します。
また、月額だったり、料金が高かったりするのでそこまで頻繁に文字起こしを使わないユーザにとっては従量課金の方が安く済むこともあります。
今回は、複数話者がいる音声で誰が何を話したのかを区別できる「話者ダイアライゼーション」という機能を使いたかったので、Speech-to-Text APIを使いました。
実際の請求額
Speech-to-Text APIはセットアップ不要で、Google Cloud ConsoleからアップロードしてGUIで文字起こしをすることができます。

実際に約1時間分の音声ファイルを文字起こししたら、181円の請求になりました。
APIバージョンV2、話者ダイアライゼーションが使いたかったので、モデルはChrip 3を使用しました。

なぜ高くなる/安くできるのか(コスト構造)
Speech-to-Text APIの料金は、ざっくり言うと「音声の長さ × チャンネル数 × 認識モードの単価」で決まります。
今回試したのは次の2つです。
1. チャンネル数 — モノラル化が効く理由
課金は「処理した音声の合計長」で計算されますが、マルチチャンネルだと全チャンネルの長さを合算して請求されます。
つまり同じ1時間でも、ステレオ(2ch)はモノラル(1ch)の2倍の料金になります。
文字起こしに左右の音の違いは要らないので、ffmpegで1chに落とすだけで単純に半額になります。
2. 認識モード — 標準とバッチで4倍違う
もうひとつが認識の種類です。リアルタイム向けの標準認識は約0.016/分ですが、即時性を捨てたDynamic Batch認識は約$0.003〜0.004/分と、ざっくり1/4まで安くなります。
文字起こしは別にその場で返ってこなくてよいので、バッチで投げました。
この2つが掛け算で効いて、181円 → 25円(約8割減)になりました。ステレオのまま標準認識で投げていたので、高くついたということになります。
コスト削減メソッド
やったことはシンプルで、次の2ステップです。
- ffmpegで1chモノラル化+20分ごとに分割する — チャンネル数を半分にします。また、Speech-to-Text APIは最大20分の音声に対応しているので、分割して送信します。シェルスクリプトファイルにしておくと便利です。
- SDK経由でDynamic Batchingの文字起こしに投げる(Python) — GUI+標準認識ではなく、バッチで投げて単価を下げます。分割したファイルはそれぞれ並列でバッチ投入できます。
# 動画から音声を取り出してモノラル化し、20分(1200秒)ごとにmp3で分割
ffmpeg -i input.mp4 -vn -ac 1 -acodec libmp3lame -ab 128k \
-f segment -segment_time 1200 -reset_timestamps 1 \
output_part_%02d.mp3
実装
肝は、BatchRecognizeRequestのprocessing_strategyに DYNAMIC_BATCHINGを指定するところです。ここが標準認識との料金差を生んでいます。Chirp 3 と話者ダイアライゼーションも同じconfigでまとめて有効化しています。
from google.cloud.speech_v2 import SpeechClient
from google.cloud.speech_v2.types import cloud_speech
def transcribe_batch_chirp(
input_gcs_uri: str, # gs://bucket/audio/foo.wav
output_gcs_uri: str, # gs://bucket/transcripts/
project_id: str,
location: str = "asia-southeast1",
language_code: str = "ja-JP",
model: str = "chirp_3",
) -> cloud_speech.BatchRecognizeResponse:
endpoint = f"{location}-speech.googleapis.com"
client = SpeechClient(client_options={"api_endpoint": endpoint})
config = cloud_speech.RecognitionConfig(
auto_decoding_config=cloud_speech.AutoDetectDecodingConfig(),
language_codes=[language_code],
model=model,
features=cloud_speech.RecognitionFeatures(
# 話者ダイアライゼーション(必要に応じて変更してください)
diarization_config=cloud_speech.SpeakerDiarizationConfig(
min_speaker_count=2,
max_speaker_count=2,
)
),
)
request = cloud_speech.BatchRecognizeRequest(
recognizer=f"projects/{project_id}/locations/{location}/recognizers/_",
config=config,
files=[cloud_speech.BatchRecognizeFileMetadata(uri=input_gcs_uri)],
recognition_output_config=cloud_speech.RecognitionOutputConfig(
gcs_output_config=cloud_speech.GcsOutputConfig(uri=output_gcs_uri)
),
# 標準認識ではなくバッチで投げる
processing_strategy=cloud_speech.BatchRecognizeRequest.ProcessingStrategy.DYNAMIC_BATCHING,
)
operation = client.batch_recognize(request=request)
return operation.result(timeout=1800)
入力音声はGCSに置く必要があるので、全体の流れはこうなります。
- ローカルの音声をGCSにアップロードします
- 上の
transcribe_batch_chirpでバッチ文字起こしします(結果JSONはGCSに出力されます) - 結果JSONをダウンロードしてSRTに変換します
- 文字起こしが終わったらGCS上の音声は削除します(置きっぱなしにすると料金がかかります)
上記のコードを拡張して、GCS音声削除までをAPIで自動化すると便利です。
削減後の請求額
同じ約1時間の音声を、モノラル化+Dynamic Batchingで文字起こしし直したところ、請求は25円になりました。

モデルはどちらもChirp 3のままなので、文字起こしの精度に大きな差はありません。設定を見直しただけで、ほぼ同じ品質の結果を約8割安く得られた計算になります。
| 削減前 | 削減後 | |
|---|---|---|
| チャンネル | ステレオ | モノラル(1ch) |
| 認識モード | 標準認識 | Dynamic Batching |
| 請求額(約1時間) | 181円 | 25円 |
精度の注意点と使いどころ
精度については、日本語だと漢字の変換ミスや固有名詞の誤認識はそれなりにあります。文字起こしをそのまま清書として使うには、手直しが必要なレベルです。
ただ、用途が「AIに食わせる」、たとえばLLMで議事録に要約させたり、内容を検索できるようにしたりする場合は、多少の誤字があっても問題になりません。意味さえ取れていれば、後段のAIが吸収してくれるからです。
逆に、一字一句正確な書き起こし(公開用の字幕や記事など)が必要なら、最終的な校正前提で考えておくのが無難です。今回は話者ダイアライゼーションで「誰の発言か」も分かるので、会議やインタビューの下書きとしては十分実用的だとおも
まとめ
同じChirp 3モデルでも、前処理と認識モードを見直すだけで181円 → 25円(約8割減)になりました。ポイントを振り返ると、
- モノラル化でチャンネル課金を半分に
- Dynamic Batchingで認識単価を約1/4に
の2つが効いています。どちらもコードや設定を少し変えるだけで、精度を落とさずに効く施策です。Speech-to-Text APIで「思ったより高いな」と感じたら、まずはこの2点を試してみる価値はあると思います。
参考文献