こんにちは。品質管理部エンジニアリングチームの遠藤です。 私の所属している品質管理部では、業務の一環として、ZOZOSUIT計測精度の向上のために新しいアプリがリリースされる度に精度のチェックを行っております。
常に計測、比較、検証などを行っており、特に計測は実際にZOZOSUITを着用し、計測、結果の記録を行っているのでとても時間がかかる作業です。 計測プロセス自体は毎回(大体)同じなので、その点をエンジニアリング的に解決できないか試してみました。だってエンジニアリングチームなんですもの!
どうやって計測するか
最初大まかに考えた構成は、ディスプレイに写真を表示して、その写真を適度なタイミングで撮影、計測、計測値の取得という流れです。 効率のため並列で動くようにして、だれでも気軽に動かせる画面を用意する、こんな感じでしょうか。
実現には様々な技術を組み合わせる必要があるかと思いますが 今回はその中で音声認識についてスポットを当ててみようと思います。
音声認識について
ZOZOSUITの計測は主に音声案内が使われています。まずそこをどうすればいいか考えました。なら音声認識すればいいのでは? という単純な理由で音声認識を調べると、Google Speech APIというホットなサービスがありました。有料ですが初回利用時に300ドル分のトライアルを無料でもらえたのでそちらを利用させていただくことにしました。
単純な音声認識をしてみる(python3)
とりあえず、Googleのサンプルを実行してみました。 音声ファイルはZOZOTOWNアプリの計測時の「もう一度数字を読み上げます。数字を押してください 9」というおなじみの音声をMacBook Proの内蔵マイクで録音したものを使ってみました。
import io import os # Imports the Google Cloud client library from google.cloud import speech from google.cloud.speech import enums from google.cloud.speech import types # Instantiates a client client = speech.SpeechClient() # The name of the audio file to transcribe file_name = os.path.join( os.path.dirname(__file__), 'resources', 'audio.wav') # Loads the audio into memory with io.open(file_name, 'rb') as audio_file: content = audio_file.read() audio = types.RecognitionAudio(content=content) config = types.RecognitionConfig( encoding=enums.RecognitionConfig.AudioEncoding.LINEAR16, # sample_rate_hertz=16000, # language_code='en-US') sample_rate_hertz=44100, language_code='ja-JP') # Detects speech in the audio file response = client.recognize(config, audio) for result in response.results: print('Transcript: {}'.format(result.alternatives[0].transcript))
Googleのサンプルそのままですが。
sample_rate_hertz=44100 language_code='ja-JP' 'audio.raw' → 'audio.wav'
の部分のみ音声ファイルに合わせて変更しています。
それでは実際に実行してみましょう。 ラジオしてくださいって!
どうやらうまく認識できていないようです。 いろいろ記事をみてみると、Google Speech APIの認識率はとても高いようですので、音声ファイルに何らかの原因がありそうです。
音声ファイルを聞いてみて気になった点としては
- 最初に何秒か無音部分がある
- 環境を気にせず録音したためエコーがかったように聞こえる
- 音声のボリュームが小さい
というのがありました。
その点を注意し試しに別の音声を取り込んでみると、 今度はうまくいきました。
音声認識の手順がわかったところで今度は音声の取り込みを試します。 静かな部屋の中、一台の端末を行うなら内臓マイクで録音するのも良いかと思いますが、複数端末を行いたいのでAndroid端末のイヤホンジャックから音声を取り込むことにします。
音声デバイスのインデックスはこちらのコードで確認できます。
import pyaudio audio = pyaudio.PyAudio() for i in range(audio.get_device_count()): dic = audio.get_device_info_by_index(i) print(dic['index'], dic['name'])
0 Built-in Microphone 1 Built-in Output 2 DisplayPort 3 HDMI
Androidのヘッドフォン端子から音声を入力するために マイク端子を増設するケーブルをUSBに接続するとこのように表示されます。
0 Built-in Microphone 1 Built-in Output 2 DisplayPort 3 HDMI 4 USB PnP Sound Device
4のUSB PnP Sound Deviceを指定して録音を行えばケーブルからの音声の取り込みで動作させられそうです。
実際このようなシステムを構築し、実験してみました。
実際にやってみて
アプリの流れに沿って音声認識でデバイスを制御してみると、いろいろと問題があることを把握できました。
言葉の区切りごとでAPIにリクエストすれば高い精度で認識してくれるのですが、言葉の途中でリクエストしてしまうとそこで聞き取った音声がうまく認識されませんでした(当たり前ですね)。
また、タイミングよくリクエストしても、APIからの返答までに次の音声案内に入ってしまうという問題もありました。
対策として 音声ボリュームを確認しながら無音が何秒かあった場合は言葉の区切りと見なしてみたり、 リクエスト中に別スレッドが録音を担当するように手分けしてみました。 それでも100%に近いかたちで音声を認識させることは難しかったです。
また録音した音声を実際に聞いてみるとなぜか音声にノイズが含まれていることもわかりました(試せていないのですが、もしかしたらオーディオ変換ケーブルの相性があるかもしれません)。
結論
単純に音声認識をすることは問題なく行えたのですが、実際にそれを組み込み、意図した動作させることにはまだまだ課題がありそうです。
感想
一通り構築してみて、うまくいかなかった点はありましたがいろいろ学ぶことはとても多かったです。 音声認識については意図した動作をさせることができませんでしたが 「それは失敗ではなくて、その方法ではうまくいかないことがわかったんだから成功なんだ」
ってエジソン先生も言ってることですし、試してみて問題があれば、ひとつひとつ解決していく方法を探していきたいと思います。
参考サイト
Cloud Speech-to-Text API クイックスタートクライアント ライブラリの使用 https://cloud.google.com/speech-to-text/docs/quickstart-client-libraries?hl=ja#client-libraries-install-python
最後に
ZOZOテクノロジーズでは、一緒にサービスを作成、サポートしてくれるエンジニアを大募集中です。 ご興味のある方は、以下のリンクからぜひご応募ください!