IBMのWatson Speech to TextをカスタマイズしてUnityで使う

#はじめに

私がUnityで作っているVR英語学習アプリでは、英語の発音練習のために音声認識を導入することを検討していたのですが、スマホでも使えるような手ごろな方法が無くて難渋していました。
しかし最近、IBMのBluemixが提供しているWatson Speech to Textサービスが、Unity上から簡単に利用できることを知ったので、ちょっと使ってみました。特に、言語モデルをカスタマイズできる機能が面白いので、そちらについて書いてみたいと思います。

#UnityからのWatson Speech to Textの使い方

公式のUnity SDKは[こちら](https://github.com/watson-developer-cloud/unity-sdk)です。ユーザー登録を含む、詳しい使い方については、[のしぷさんのブログ](http://noshipu.hateblo.jp/entry/2016/05/29/003111) が参考になるので、ご覧ください。

SDK内のunity-sdk/Scenes/UnitTests/TestSpeechToText.unityシーンも参考になるので一度試しましょう。私は、これを改変したものを自分のアプリに使ってます。

ちなみに、Speech To Textなどのサービスは有料ですが、無料トライアルもあるので、まずはそちらで試してください。トライアル期間が終了しても、無料使用枠があるのでご安心を（クレカ登録が必要）。

#言語モデルをカスタマイズする。

##準備

言語モデルのカスタマイズは、[IBMの公式ブログ](https://www.ibm.com/developerworks/community/wikis/home?lang=en#!/wiki/%E3%81%93%E3%81%BF%E3%81%AD%E3%81%AE%E6%8A%80%E8%A1%93%E3%83%A1%E3%83%A2/page/%E9%9F%B3%E5%A3%B0%E8%AA%8D%E8%AD%98%E3%81%A7%E3%81%AE%E3%82%AB%E3%82%B9%E3%82%BF%E3%83%A0%E3%83%A2%E3%83%87%E3%83%AB%E3%81%AE%E4%BD%BF%E7%94%A8)によると、2016年9月にBetaがリリースされた、比較的新しい機能のようです。同年12月には日本語認識のカスタマイズも可能になったようです（[参考](https://www.ibm.com/watson/developercloud/doc/speech-to-text/release-notes.html)）。

実際のカスタマイズの方法は、こちらの[英語のドキュメンテーション](https://www.ibm.com/watson/developercloud/doc/speech-to-text/custom.html)が一番丁寧で詳しいです。これを日本語で短くまとめた[公式ブログ](https://www.ibm.com/developerworks/community/wikis/home?lang=en#!/wiki/%E3%81%93%E3%81%BF%E3%81%AD%E3%81%AE%E6%8A%80%E8%A1%93%E3%83%A1%E3%83%A2/page/%E9%9F%B3%E5%A3%B0%E8%AA%8D%E8%AD%98%E3%81%A7%E3%81%AE%E3%82%AB%E3%82%B9%E3%82%BF%E3%83%A0%E3%83%A2%E3%83%87%E3%83%AB%E3%81%AE%E4%BD%BF%E7%94%A8)もあります。

これらのドキュメンテーションにあるように、カスタマイズをするにはカスタム言語モデルをSpeech to Textサービスを提供しているBluemix上に作成する必要があります。いくつか方法があるようですが、自分はドキュメンテーションで説明されているcURLを使いました。cURLのクライアントは[こちら](https://curl.haxx.se/download.html)からダウンロードできます。Win64システムなら、"Win64 - Generic"セクションの"Win64 x86_64 7zip"を使うと良いみたいです。ダウンロードしたら、curl.exeへのパスを環境変数PATHに登録してください（[参考](https://help.zendesk.com/hc/en-us/articles/229136847-Installing-and-using-cURL#install)）。

![image](https://qiita-image-store.s3.amazonaws.com/0/58503/094b7bf0-9a47-bf77-6383-08592d050d3a.png)

うまく設定できれば、コマンドプロンプトで"curl"と入力すると、"curl: try 'curl --help' or 'curl --manual' for more information"といったメッセージが出るはずです。

Speech To Textサービスにうまく繋がるかをテストするために、サンプル音声を認識させてみましょう。[こちら](https://www.ibm.com/watson/developercloud/doc/speech-to-text/getting-started.html)にある audio-file.flacをダウンロードして、コマンドプロンプトで、以下のコマンドを送ります。

```
curl -X POST -u 6e********9e:RJ*****qA --header "Content-Type: audio/flac" --data-binary @C:\Users*******audio-file.flac "https://stream.watsonplatform.net/speech-to-text/api/v1/recognize?continuous=true"
```
ここで、`6e********9e`と`RJ*****qA`は、それぞれSpeech To Textサービスのユーザーネームとパスワードです。以下の。Bluemixの設定画面から取得してください。

![image](https://qiita-image-store.s3.amazonaws.com/0/58503/976e32a5-2223-1a79-f98f-9ffef8d89b43.png)

また、`@C:\Users*******audio-file.flac"`はファイルへのパスです。

うまくいくと

```
{
   "results": [
      {
         "alternatives": [
            {
               "confidence": 0.891,
               "transcript": "several tornadoes touch down as a line of severe thunderstorms swept through Colorado on Sunday "
            }
         ],
         "final": true
      }
   ],
   "result_index": 0
}
```
こんなレスポンスが帰ってくるはずです。これが確認できれば、準備OKです。

##カスタマイゼーションIDを作成して、言語モデルをWatsonに学習させる。

（以下の内容は、公式チュートリアルとほぼ同じなので、そちらを読んだ方は読み飛ばしてOKです）

カスタム言語モデルを使用する際は、customization_idを作る必要があります。Bluemix上には複数の言語モデルを作れるので、その区別にcustomization_idを使います。公式ドキュメントをもとに、以下のコマンドを使ってみます。

```
curl -X POST -u 6e********9e:RJ*****qA --header "Content-Type: application/json" --data "{\"name\": \"Qiita Example model\",  \"base_model_name\": \"en-US_BroadbandModel\",\"description\": \"Example custom language model for Qiita\"}" "https://stream.watsonplatform.net/speech-to-text/api/v1/customizations"
```
すると、{"customization_id": "93ca***********de"}のようなcustomization_idが返ってきます。

つぎに、customization_idに対して、新しいcorpora（文章例）を加えます。

```
curl -X POST -u 6e********9e:RJ*****qA --data-binary @D:\Documen***e\dokosco.txt "https://stream.watsonplatform.net/speech-to-text/api/v1/customizations/93***8de/corpora/dokosco"
```
上記"@D:\Documen***e\dokosco.txt"の部分は、文章例のファイルへのパスです。文章例は[こちら](https://www.ibm.com/watson/developercloud/doc/speech-to-text/custom.html#prepareCorpus)にあるcorpus.txtなどを参考にしてつくります。覚えさせたい文章例をたくさん与えると良いようです。わたしは、以下の内容を学習させました。

```dokosco.txt
Start
Skip.
Go back.
I'm in London.
I'm in New York.
I'm in Kyoto.
I'm in Tokyo.
I'm in Osaka.
I'm in America.
I'm in Rome.
I'm in Italy.
I'm in Canada.
I'm in Paris.
I'm in France.
I'm in Sydney.
```

`https://stream.watsonplatform.net/speech-to-text/api/v1/customizations/93***8de/corpora/dokosco` の93***8deの部分はcustomization_idです。dokoscoというのは、私がつけたcorpusの名前ですが、皆さんは自由につけてください。

このコマンドを送ると、"{ }"だけ返ってきます。Bluemix側で処理が行われるので、処理が終わったかどうかは 以下のコマンドで確認します。

```
curl -X GET -u 6e********9e:RJ*****qA "https://stream.watsonplatform.net/speech-to-text/api/v1/customizations/93***8de/corpora/dokosco"
```

すると、以下のようなメッセージが返ってきます。

```
{
   "out_of_vocabulary_words": 0,
   "total_words": 41,
   "name": "dokosco",
   "status": "analyzed"
}
```

statusがanalyzedになっていればOKです。

つぎに、以下のコマンドでモデルのトレーニングを行います。

```
curl -X POST -u 6e********9e:RJ*****qA --data "{}" "https://stream.watsonplatform.net/speech-to-text/api/v1/customizations/93***8de/train"
```

このコマンドを送ると、"{ }"だけ返ってきます。Bluemix側で学習が行われるので、学習が終わったかどうかは 以下のコマンドで確認します。

```
curl -X GET -u 6e********9e:RJ*****qA "https://stream.watsonplatform.net/speech-to-text/api/v1/customizations/93***8de"
```

すると、以下の結果が返ってきます。

```
{
   "owner": "8******9",
   "base_model_name": "en-US_BroadbandModel",
   "customization_id": "9*******e",
   "created": "2017-04-24T09:01:17.602Z",
   "name": "Qiita Example model",
   "description": "Example custom language model for Qiita",
   "progress": 100,
   "language": "en-US",
   "status": "available"
}
```

statusがavaialbleになればOKです。

#学習内容をもとに、Unityから音声を認識させる。

ここからは、Unityでの作業です。WatsonのSDKで、Speech to textを行うのはSpeechToText.csです。その中で、Bluemixに接続する際のパラメータを変更します。パラメータについては、[こちら](https://www.ibm.com/watson/developercloud/doc/speech-to-text/websockets.html)に一覧がありますが、そのなかにcustomization_idがあるので、これを使います。

SpeechToText.csの中を見ていくと、#region Sessionless - Streamingというのがあり、連続的に音声を認識する際の処理がかかれています。その中のSendStart()でWebSocketを使ってリクエストをしており、 `start["word_confidence"] =...`などの形でパラメータを渡してます。そこに、 `start["customization_id"] = "93***8de";`を加えて、customization_idを設定することにしました。

更に下には、#region Sessionless Non-Streamingというのがあり、ここにはAudioClipをRecognizeという関数で認識させています。下をみていくとreq.Parameters[..]という形でリクエストにパラメータを設定しているので、そこに`req.Parameters["customization_id"] = "93***8de";`を加えて、customization_idを渡します。

それでは、以下のコードでAudioClipを認識させるテストを行います。ゲームオブジェクトにアタッチしてください。

```csharp:SampleSpeechToText.cs
using IBM.Watson.DeveloperCloud.Services.SpeechToText.v1;
using UnityEngine;

public class SampleSpeechToText : MonoBehaviour
{
    private SpeechToText m_SpeechToText = new SpeechToText();
    public AudioClip AudioClip;

    void Start()
    {
        m_SpeechToText.Recognize(AudioClip, HandleOnRecognize);
    }

    private void HandleOnRecognize(SpeechRecognitionEvent result)
    {
        if (result != null && result.results.Length > 0)
        {
            foreach (var res in result.results)
            {
                foreach (var alt in res.alternatives)
                {
                    string text = alt.transcript;
                    Debug.Log(string.Format("{0} ({1}, {2:0.00})\n", text, res.final ? "Final" : "Interim", alt.confidence));
                }
            }
        }
    }
}
```

AudioClipには[こちら](https://dl.dropboxusercontent.com/u/79017414/Kojiro4.wav)を使います。これを認識させると、customization_idを与えても、与えなくてもI'm in Londonと正しく認識しますが、与えるとConfidenceが0.99なのに対し、与えないと0.89なるので、一定の効果はあるようです。

#おわりに

自分自身、Watson初心者なので、間違いがあるかも知れません。お気軽にコメントしてください。あと、カスタマイズの設定は、CURLのかわりにUnityのSDKを使ってもできるようですが、まだ試していません。今度がんばります。