PythonとGoogle Cloud Speech API を使った音声の文字起こし手順


白金鉱業.FMというPodcastをしているので、その音声データを文字起こしするために今回はGoogle Cloud Speech APIを試してみました。
(※Googleの文字起こしAPIは、公式でも「Google Cloud Speech API」と「Cloud Speech-to-Text」という名前で揺れているような。多分一緒のものです。謎。)

収録&音声データを作るまでの手順はこちらを参照してください。

Google Cloud Speech APIの利用方法について以下のブログが最も詳しいです。

Google Cloud Platformの利用方法を含め、基本は上記のブログを参照すると実行することができると思いますが、それでもちょっと分かりづらかったところの補足をしつつ手順をメモします。

Speech APIにインプットするまでの音声データの前処理

Speech APIへのインプットは、mp3ファイルではなく.flacという音声ファイル形式を前提にしているため、手元のmp3やwavをおもむろに突っ込めばよいという簡単なものではなく多少の前処理が必要です。ここではMacのGarageBandアプリで作成した音声ファイルからSpeech APIに適したインプット形式人するまでの手順を記載します。

1. GarageBandでWAVEファイルを書き出し

  • GarageBandで編集済みの音声データを >「共有」>「曲をディスクに書き出す」> "WAVE"で"非圧縮16ビット"でダウンロード

2. AudacityでWAVE→FLACファイルへ変換

  • 保存した.wavファイルをAudacityに放り込む > 左下にあるサンプリングレートから"16000"を選択 > 波形画像部分を選択してフォーカス > 「トラック」>「ミックス」>「ステレオからモノラルへ」を選択すると波形データが2つから1つになる>「ファイル」>「書き出し」>「音声の書き出し」>"FLACファイル(レベル5 ビット, 深度16bit) で保存 > (メタデータは必要であれば記入, 何もなしでもok)

このあたりの図付き操作方法はこちらのブログが詳しいです。

※サンプリングレートを16000にしている根拠は、Google Cloudが公式に発表しているSpeech API利用のベストプラクティスに従っています。

3. Google Cloud Platformへ音声データを設置

  • Google Cloud Platformの登録方法・プロジェクト作成・バケットの作成方法・API有効化方法・クレデンシャルファイル(jsonファイル)のダウンロード方法などはこちらのブログを参照してください。
  • Strageへさっき保存したflacファイルをアップロードして保存(ファイルの権限などは特に変更しなくてOK)
  • アップロードしたファイルには"gs://hoge_folder/hoge.flac" のようなURIが振られる

Cloud Shellでコードを実行しテキストファイルを作成する

Speech APIをローカルから叩く方法がいまいちわからなかったので、ここではGoogle Cloud Plattform上(ブラウザ上)でAPIを叩くコードを実行することで文字起こし処理を行う方法をメモします。

  • コンソール右上にあるCloud Shellボタンを押してCloud Shellを起動

    • linuxコマンドを受け付けるshellが立ち上がる
  • Speech APIを実行するコードは以下。以下コードを transcribe.pyとして保存

  # !/usr/bin/env python
  # coding: utf-8
  import argparse
  import io
  import sys
  import codecs
  import datetime
  import locale

  def transcribe_gcs(gcs_uri):
      from google.cloud import speech
      from google.cloud.speech import enums
      from google.cloud.speech import types
      client = speech.SpeechClient()

      audio = types.RecognitionAudio(uri=gcs_uri)
      config = types.RecognitionConfig(
          sample_rate_hertz=16000, #according to the set parameter
          encoding=enums.RecognitionConfig.AudioEncoding.FLAC,
          language_code='ja-JP')

      operation = client.long_running_recognize(config, audio)

      print('Waiting for operation to complete...')
      operationResult = operation.result()

      d = datetime.datetime.today()
      today = d.strftime("%Y%m%d-%H%M%S")
      fout = codecs.open('output{}.txt'.format(today), 'a', 'utf-8')

      for result in operationResult.results:
          for alternative in result.alternatives:
              fout.write(u'{}\n'.format(alternative.transcript))
      fout.close()

  if __name__ == '__main__':
      parser = argparse.ArgumentParser(
          description=__doc__,
          formatter_class=argparse.RawDescriptionHelpFormatter)
      parser.add_argument(
          'path', help='GCS path for audio file to be recognized')
      args = parser.parse_args()
      transcribe_gcs(args.path)

  • クレデンシャルファイル(xxxx.json)を transcribe.py と同じ階層に設置する。

  • モジュールのpipとクレデンシャルファイルのパスを指定する。以下2つは実行必須。

    • sudo pip install google-cloud-speech
    • export GOOGLE_APPLICATION_CREDENTIALS=xxxx.json
  • 注意!)Cloud Shellでは、セッションが切られるたびにpipしたモジュールやpathセッティングなどがリセットされるのでshellを立ち上げるたびに上記2つを毎回再実行する必要がある。

  • Cloud Shell上で、

  python transcribe.py gs://hoge_folder/hoge.flac

を実行

  • 上記コードでは、うまくいけば "Waiting for operation to complete..."が表示されたまま処理待ち状態になる
  • おおよそ75分の音声ファイルで25分の処理時間
  • 生成された .txtファイルは dl hoge.txt でローカルにDLされる

Speech APIを使った文字起こし方法は以上です。

文字起こし精度が低い問題

ここまで頑張ってアレですが、自分の用意した音源では喋った言葉の内、体感値で4割ほどしかうまく文字起こしされていない印象でした。(4割だとテキストを読むだけでは何の話をしているのかわからない程度に壊滅的で文字起こしをする意味がなくなります)

音源が汚いのではという疑惑もありますが、使用したのはこちらで、手前味噌ですがノイズも少なく相当クリアで特に問題ないはずです。
そしてSpeech APIで文字起こししたテキストがこちらです。うーむ。

ではSpeech APIが世に言われているほどの性能ではないのかといえばそうとも言い切れず、例えばrebuild.fmの文字起こしには同じくGoogleのSpeech APIを使っており、8割はうまく文字起こしできているそうです(どこかのエピソードで話されていましたが忘れた)。
実際に有料会員は文字起こししたものを対象にエピソード検索もできるようなので実用的なレベルでは文字起こしができていることが予想されます。

何が問題か?

「音源はクリアである」&「Speech APIの性能は低くない」ことを前提にした場合、個人的に怪しいなと思っているのは『サンプリングレート』と『ノイズ処理低減処理の有無』です。

1. 「サンプリングレート16000」は適切か?

今回のパラメータで16000を設定している根拠は、Google Cloudが公式に発表しているSpeech API利用のベストプラクティスに記載されているためです。

しかし、別の公式ページではサンプリングレートについて以下のように記述されており、16000が必ずしてもベストではなく、録音デバイスなどの依存するようです。

ソース素材のエンコード時に選択できる場合は、16,000 Hz のサンプルレートを使用して音声をキャプチャします。これより低い値では、音声認識の精度が低下する可能性があり、レベルを高くしても音声認識品質に目立った効果はありません。

ただし、音声データがすでに 16,000 Hz 以外の既存のサンプルレートで録音されている場合は、音声を 16,000 Hz で再サンプリングしないでください。たとえば、以前のほとんどの電話音声では 8,000 Hz のサンプルレートが使われており、正確な結果が生成されないことがあります。そのような音声を使用する必要がある場合は、ネィティブ サンプルレートで音声を Speech API に渡します。

自分が録音に使っているbleuのyetiはデフォルトで録音を「サンプリングレート:48kHz」で行うため、もしかするとこれを16,000 Hz で再サンプリングしているのが何かしら認識精度悪化に影響してるのではと思っています。

2. Speech APIのインプットには「ノイズ処理低減処理」をしてはいけない?

Podcast配信方法を教えるサイトでは、収録した音源に対してAudacityなどのアプリを用いて「ノイズ処理低減処理」(ホワイトノイズ削減)を行うことを推奨しています。

しかし、Speech API公式によると、Speech APIはノイズの乗った音源がインプットされることを前提としており、ノイズ低減処理を行うと一般に認識精度が低下すると記載されています。

高音質の適切な位置に置かれたマイクを使用して、可能な限り明瞭な音声を生成することをおすすめします。ただし、音声をサービスに送信する前にノイズ低減信号処理を適用すると、一般に認識精度が低下します。このサービスはノイズのある音声を処理するように設計されています。

自分がインプットしていた音源はAudacityによってノイズ削減したものだったので、この処理をしていない場合の文字起こし精度も比較して見る必要があります。

1&2については今後比較を行ってみてその結果をまとめたいと思います。たぶんSpeech APIはもっとできる子。

特に有用だった参照記事3つ

  1. http://note103.hateblo.jp/entry/2018/07/23/115930 (やり方全般の参考)
  2. https://qiita.com/knyrc/items/7aab521edfc9bfb06625 (1の補足など)
  3. https://qiita.com/kokky/items/659bde4cdc8ce5c78e29 (コードの参考)