こんにちは、2020年新卒入社予定の山口です!修論と引越し準備とアルバイトで慌ただしい日々を過ごしています。今日は業務で触った、Google製API Google Cloud Speech-to-Text API について皆さんと共有できればと思います。
- Google Cloud Speech-to-Text API とは
- APIを導入していく
- 実際に試してみる
- 認識モデルを変更してみる
- マルチチャンネルで試してみる
- ファイル形式・サンプリング周波数を比較してみる
- おわりに
Google Cloud Speech-to-Text API とは
Google Cloud Speech-to-Text API は名前の通り、音声データから文字起こしをするAPIです。この音声データは私たちが日頃聞いているような音声ファイル(.wavや.flacなど)から、マイクから入力されるストリーム形式まで幅広く対応しています。サービス自体は数年前からありましたが、最近になりストリーミング音源のテキスト起こしが時間無制限でできるようになったみたいなので試してみました。
APIを導入していく
簡単にGoogle Cloud Speech-to-Text API の導入までの手順を解説していきます。もうすでに導入した事がある方は飛ばしてください。導入をする際はGCP(Google Cloud Platform)側とそれを使うPC側とでいくつか設定をする必要があります。
(参考にさせて頂いたページ:GCPクイックスタートページ、Qiita - "Google Cloud Speech APIのリアルタイム音声認識は使い物になる精度なのか?")
GCP側
- まずGoogleアカウントを作成し、GCPコンソールにログインしてプロジェクトを作成します。
- Google Speech-to-Text APIを有効にします。
- 「認証情報」を選択して、「認証情報を作成」から「サービスアカウントキー」をクリックして、jsonファイルを作成し、任意の場所に保存します。このjsonファイルを読みこむことによってAPIサービスを使うことができます。
- Google Cloud SDKをインストールします。
以上がGCP側で設定する内容です。
PC側
- APIを実際に使う環境を用意します。今回はanacondaを使って、python3.6の仮想環境を作成します。
必要なライブラリをインストールします。
pip install google gcloud google-auth google-cloud-speech grpc-google-cloud-speech-v1beta1 pyaudio
先述したサービスアカウントキーを登録します。([PATH]は先ほどダウンロードした「サービスアカウントキー」のjsonファイルのパスになります。)
MacOS、Linuxの場合
export GOOGLE_APPLICATION_CREDENTIALS="[PATH]"
Windowsの場合
set GOOGLE_APPLICATION_CREDENTIALS=[PATH]
ここまでで設定は以上になります。
実際に試してみる
実際にAPIの方を利用していきます。実行環境はMacOS、Python3.6になります。コードはこちらを実行していきます。
一部日本語に対応させるためにコードを変更します。
- language_code='en-US', + language_code='ja-JP',
language_codeは音声をテキストに直す際の言語を設定するパラメーターです。 その他の言語のコードはこちらを参照にしてください。
Google Cloud Speech-to-Text APIリアルタイム解析結果出力画面
実際に動かした感想としてはきれいにテキストに直せてますね!
この後15分ほど認識させ続けましたが、プログラムは落ちることなく動き続けることができました!
認識モデルを変更してみる
Google Cloud Speech-to-Text API には様々な状況に応じて、認識モデルが存在するみたいです。確認できたモデルは以下の通り
- video ・・・ 動画の字幕作成などに適したモデル
- phone-call ・・・ 電話から発せられた音声に適したモデル
- command_and_search ・・・ 音声検索やコマンド("OK!, Google!"とか)に適したモデル
- default ・・・ 上記のいずれでもない場合に適したモデル(何も設定していないとこのモデルになります)
結構バリエーション豊かですよね!ただこれらのモデルは言語によっては対応していないものもあるとか... それも含めて確かめていきます。モデルの変更はconfigに追加すると、変更ができます。
model = 'モデル名'
- video -> 日本語に対応していない
- phone-call- > 日本語に対応していない
- command_and_search -> 単語の精度は一番きれいかも...、ただ長文の認識は難しくなる?
ほとんどが日本語に対応してない... けれどモデルの説明通りの認識力です。
気を取り直して...。 これらのモデルにはエンハンス(性能を向上させる)オプションがあるらしく、唯一日本語対応していたcommand_and_searchモデルに使ってみます(defaultモデルにはないみたいです)。下記のコードを先ほど追加したコードの下に追加してください。
use_enhanced=True
試してみると精度が上がった感じがしました。 もともとが精度が高いのであまり実感がありませんでしたが、精度が気になった場合は試してみるといいかもしれません。
比較結果(比較をするためにファイル形式で文字起こしをしています。違う部分は「」で示してあります。変化はほぼないですが...)
・原文(比較のため句読点を除いています)
メロスは激怒した 必ずかの邪智暴虐の王を除かなければならぬと決意した メロスには政治がわからぬ メロスは村の牧人である 笛を吹き羊と遊んで暮して来た けれども邪悪に対しては人一倍に敏感であった きょう未明メロスは村を出発し野を越え山越え十里はなれた此のシラクスの市にやって来た
・モデル:default, use_enhanced:×(対応していない)
メロスは激怒した 必ずかの邪智暴虐の王を除かねばならぬと決意した メロスには政治がわからぬ メロスは村の「巻一」である 笛を吹き羊と遊んで暮らしてきた けれども邪悪に対しては人一倍に敏感であった 今日未明メロスは村を出発しの「声」山を越え十里離れたこのシラクスの市に行ってきた
・モデル:command_and_search, use_enhanced:×
メロスは激怒した 必ずかの邪智暴虐の王を除かねばならぬと決意した メロスには政治がわからぬ メロスは村の「人」である 笛を吹き羊と遊んで暮らしてきた けれども邪悪に対しては人一倍に敏感であった 今日未明メロスは村を出発しの「越え」山を越え十里離れたこのシラクスの市に行ってきた
・モデル:command_and_search, use_enhanced:○
メロスは激怒した 必ずかの邪智暴虐の王を除かねばならぬと決意した メロスには政治がわからぬ メロスは村の「人」である 笛を吹き羊と遊んで暮らしてきた けれども邪悪に対しては人一倍に敏感であった 今日未明メロスは村を出発しの「越え」山を越え十里離れたこのシラクスの市に行ってきた
マルチチャンネルで試してみる
調べてみるとGoogle Cloud Speech-to-Text API ではマルチチャンネルに対応しているみたいです("audioChannelCount"の項目です)。これができれば多人数の会議などの場面で、発話者別に音声の文字起こしができますね!
今回はストリーミング形式でマルチチャンネルを試そうと思うのですが、マイクをいくつも用意して、接続しながら試すのは大変なので、今回は以下の流れで試してみます。
音声ファイル -> ストリームに変換 -> 文字起こし
まずは音声ファイルを作成していきます。今回は音声ファイルを録音した音源を元に作成します。まずマイクから録音することができる適当なフリーソフトをインストールします。できればこの時モノラル(1ch)で録音できるものがあれば音声をすぐに合成できます。インストールしたソフトから録音データを作成し、音声を合成していきます。
8chの音声を作成し、実際にテキスト起こしをしてみたのですが...
(gifの長さの関係で8chだけしか表示されてないですが...)
8chと7chの音声しか文字起こしがされないです。
試しに4chの音声で試すと今度は4chと3chだけに...
音声が間違っているかもしれないので、ストリーム形式ではなくファイルアップロード形式に変更して確かめてみると問題なくチャンネル別にテキストができました。
どうやらリアルタイムのストリーミング形式だと2chの文字起こしが限界みたいでした。
ファイル形式・サンプリング周波数を比較してみる
最後にストリーミング形式の文字起こしから離れて、ファイルアップロード形式の文字起こしをおこなっていきます。 Google Cloud Speech-to-Text API は様々なファイル形式やサンプリング周波数に対応しています。 比較用のコードは以下になります。
import argparse def transcribe_file(args): from google.cloud import speech from google.cloud.speech import enums from google.cloud.speech import types import io client = speech.SpeechClient() with io.open(args.input, 'rb') as audio_file: content = audio_file.read() if args.encode == 'flac': encode = speech.enums.RecognitionConfig.AudioEncoding.FLAC elif args.encode == 'wav': encode = speech.enums.RecognitionConfig.AudioEncoding.LINEAR16 elif args.encode == 'ogg': encode = speech.enums.RecognitionConfig.AudioEncoding.OGG_OPUS elif args.encode == 'amr': encode = speech.enums.RecognitionConfig.AudioEncoding.AMR elif args.encode == 'awb': encode = speech.enums.RecognitionConfig.AudioEncoding.AMR_WB audio = types.RecognitionAudio(content=content) config = types.RecognitionConfig( encoding=encode, sample_rate_hertz=args.hertz, language_code='ja-JP', audio_channel_count=1, enable_separate_recognition_per_channel=True) response = client.recognize(config, audio) with io.open(args.input + ".txt", "w", encoding="utf-8") as f: for result in response.results: f.write(u'Transcript: {}'.format(result.alternatives[0].transcript)) if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument( '-i', '--input', action='store') parser.add_argument( '-hz', '--hertz', type=int, action='store') parser.add_argument( '-e', '--encode', action='store') transcribe_file(parser.parse_args())
録音した音声ファイルをAMR-NB、AMR-WB、FLAC、OPUS、WAVにサンプリング周波数を変えながら、ffmpeg(ver4.2.2)で変換を行い、比較してみました。 今回の比較はナレーション音声と走れメロスの読み上げを録音し、下記の表で比較をします。
AMR-NB | AMR-WB | FLAC | OPUS | WAV | |
---|---|---|---|---|---|
8000Hz | ○ | × | ○ | ○ | ○ |
16000Hz | × | ○ | ○ | ○ | ○ |
ナレーション音声(「本日は〜」の音声です。)
( *違う部分に「」をしています。)
・原文(比較のため句読点を除いています)
本日はご来場いただきまして誠にありがとうございます 開演に先立ちましてお客様にお願い申し上げます 携帯電話など音の出るものの電源はお切りください また許可のない録音撮影はご遠慮ください 皆様のご協力をよろしくお願いいたします
・AMR-NB(8000Hz)
「本日は」ご来場いただきまして誠にありがとうございます開演に先立ちましてお客様にお願い申し上げます携帯電話など音の出るものの電源はお切りくださいまた許可のない録音撮影はご遠慮ください皆様のご協力をよろしくお願いいたします
・FLAC(8000Hz), WAV(8000Hz)
「」ご来場頂きまして誠にありがとうございます開演に先立ちましてお客様にお願い申し上げます携帯電話など音の出るものの電源はお切りくださいまた許可のない録音撮影はご遠慮ください皆様のご協力をよろしくお願いいたします
・OPUS(8000Hz)
「実は」ご来場頂きまして誠にありがとうございます開演に先立ちましてお客様にお願い申し上げます携帯電話など音の出るものの電源はお切りくださいまた許可のない録音撮影はご遠慮ください皆様のご協力をよろしくお願いいたします
・AMR-WB(16000Hz)
「本日は」ご来場いただきまして誠にありがとうございます開演に先立ちましてお客様にお願い申し上げます携帯電話など音の出るものの電源はお切りくださいまた許可のない録音撮影はご遠慮ください皆様のご協力をよろしくお願いいたします
・FLAC(16000Hz), OPUS(16000Hz), WAV(16000Hz)
「実は」ご来場頂きまして誠にありがとうございます開演に先立ちましてお客様にお願い申し上げます携帯電話など音の出るものの電源はお切りくださいまた許可のない録音撮影はご遠慮ください皆様のご協力をよろしくお願いいたします
走れメロス_朗読
Google Cloud Text To Speech API 調査テスト用音声(走れメロス冒頭部)
( *違う部分に「」をしています。)
・原文(比較のため句読点を除いています)
メロスは激怒した 必ずかの邪智暴虐の王を除かなければならぬと決意した メロスには政治がわからぬ メロスは村の牧人である 笛を吹き羊と遊んで暮して来た けれども邪悪に対しては人一倍に敏感であった きょう未明メロスは村を出発し野を越え山越え十里はなれた此のシラクスの市にやって来た
・FLAC(8000Hz), OPUS(8000Hz), WAV(8000Hz)
メロスは激怒した 必ずかの邪智暴虐の王を除かねばならぬと決意した メロスには政治がわからぬ 「エロスは村野巻一」である 「笛を吹き」羊と遊んで暮らしてきた けれども邪悪に対しては人一倍に敏感であった 今日未明メロスは村を出発し「のう越え」山を越え「十里」離れたこのシラクスの市に行ってきた
・AMR-NB(8000Hz)
メロスは激怒した 必ずかの邪智暴虐の王を除かねばならぬと決意した メロスには政治がわからぬ 「メロスは村の巻一」である 「笛吹き」羊と遊んで暮らしてきた けれども邪悪に対しては人一倍に敏感であった 今日未明メロスは村を出発し「の声」山を越え「十里」離れたこのシラクスの市に行ってきた
・AMR-WB(16000Hz), OPUS(16000Hz), WAV(16000Hz)
メロスは激怒した 必ずかの邪智暴虐の王を除かねばならぬと決意した メロスには政治がわからぬ 「メロスは村の巻一」である 「笛を吹き」羊と遊んで暮らしてきた けれども邪悪に対しては人一倍に敏感であった 今日未明メロスは村を出発し「の声」山を越え「十里」離れたこのシラクスの市に行ってきた
・FLAC(16000Hz)
メロスは激怒した 必ずかの邪智暴虐の王を除かねばならぬと決意したメロスには政治がわからぬ 「メロスは村の巻一」である 「笛吹き」羊と遊んで暮らしてきた けれども邪悪に対しては人一倍に敏感であった 今日未明メロスは村を出発し「の声」山を越え「樹里」離れたこのシラクスの市に行ってきた
そこまで大きく認識の結果は変わらなかったのですが、所々認識できていないところがあったりもしました。それぞれ同じファイルを複数回試してみたのですが、結果はどれも変わらなかったです。非可逆圧縮のファイル形式であっても、結果は変わらないみたです。全体的に見て16000Hzの方が認識がよく、WAV形式が安定している感じがします。
注意しなければならない点としてAMR-NB形式は8000Hz、AMR-WB形式は16000Hzにしか対応していない事が挙げられます。詳しくはGCPのリファレンスを確認してください。
おわりに
今回はGoogle Cloud Speech-to-Text APIについていろいろ調査をしてきました。音声認識の精度はかなりいい感じでしたが、使用できるモデルによっては日本語対応をしていないものもありました。またいくつかの設定項目については執筆時点では記述とは違った挙動をするものも... 実際に動かしながら確認するのも大事なんだなと思った結果になりました。
オプティムでは、新卒内定者がアルバイトとして業務に関わることも可能です。採用についてはこちらをご覧ください。 www.optim.co.jp