XamarinからLuisを使ってみる

このエントリは [Xamarin Advent Calendar 2016](http://qiita.com/advent-calendar/2016/xamarin) の13日目の投稿です。

# LUIS

## MicrosoftのBot用言語解析フレームワーク

[LUIS](https://www.luis.ai/) を調べてみると、9割がたBot用に使用する、みたいな記事がヒットします。
自分もそちらから興味を持った口なのですが。

何をするものかというと、ざっくり言って「自然言語 → コンピュータ用コマンド」のトランスレーターです。
人が話したり文字にしたものを定義されている文例から探し出し、スコアを付けてどのコマンドと思われるかを出力します。

実際、どのようなものか見てもらう方が早いですね。

## LUISの設定画面

![luis1.PNG](https://qiita-image-store.s3.amazonaws.com/0/59270/08f55467-830c-6766-35c2-f5e2beee3970.png)

今回、ホームオートメーション的なものをXamarin.Formsで作ることを考えてみました。
「ヒーターをつける」「クーラーをつける」などが入力文章、「turnOnTheHeater」「turnOnTheCooler」がIntent（コマンド）です。
これの対応付けをしてくれるんですね。

色がついている部分はEntity（パラメータ）で、返答に含めるパラメータを指定できます。

![luis2.PNG](https://qiita-image-store.s3.amazonaws.com/0/59270/d1a662ba-601a-e4d0-8dab-15a01e80af62.png)

今回の場合、どの機器を使用するか、をパラメータとして含めています。

## LUISとの通信

LUISの設定画面の右下「Train」を押すと学習が始まり、右上「Publish」でアプリの発行ができます。
「Publish」を行うと、リクエストのURLが表示され、そのURLに「 `&q=` 」のパラメータを付けることでリクエストを出すことができます。

```csharp
            var client = new HttpClient();
            var json = await client.GetStringAsync(String.Format("https://api.projectoxford.ai/luis/v2.0/apps/****?subscription-key=****&verbose=true&q={0}", Uri.EscapeDataString(query)));
```

上記の設定で、 `query="風呂を沸かす"` でリクエストを行うと、以下のようなJSONが取得出来ます。

```json
{
  "query": "風呂を沸かす",
  "topScoringIntent": {
    "intent": "heatBathWater",
    "score": 0.999999046,
    "actions": [
      {
        "triggered": true,
        "name": "heatBathWater",
        "parameters": [
          {
            "name": "item",
            "type": "item",
            "required": false,
            "value": null
          }
        ]
      }
    ]
  },
  "intents": [
    {
      "intent": "heatBathWater",
      "score": 0.999999046,
      "actions": [
        {
          "triggered": true,
          "name": "heatBathWater",
          "parameters": [
            {
              "name": "item",
              "type": "item",
              "required": false,
              "value": null
            }
          ]
        }
      ]
    },
    {
      "intent": "None",
      "score": 0.157952726
    }
  ],
  "entities": [
    {
      "entity": "風呂",
      "type": "item::Bath",
      "startIndex": 0,
      "endIndex": 1,
      "score": 0.9311934
    }
  ],
  "dialog": {
    "contextId": "*-*-*-*",
    "status": "Finished"
  }
}
```

後は、このJSONから得られるデータを解析すればよい、という事になります。

## 解析

あまり何も考えずに解析させるなら、[Bot Builder SDK](https://github.com/Microsoft/BotBuilder) の中から該当のソースを拝借してしまいましょう。
[Luis/Models](https://github.com/Microsoft/BotBuilder/tree/master/CSharp/Library/Microsoft.Bot.Builder/Luis/Models) の中にソースがあります。
`LuisResult.cs` の中の `public virtual void Validate()` 関数を削除するとそのまま使えます。
（MITライセンスですよ！実際に使用するときには注意）

```csharp
                var result = JsonConvert.DeserializeObject<LuisResult>(json);
```

上のリクエスト用のソースでJSONの文字列が入ってきますので、そのままJSONのDeserializeを行います。
これでresultはLuisResultになりましたので、それ用の処理を行うだけになります。

## 終わり

実際に使っている処理の部分はまあ…。
また今度にでも。

今回は、ホームオートメーション用ということで考えてみましたが、これを画面でポチポチボタンを選んで動かす、なんていうソフトの場合、Xamarin.Formsでソースの共通化が図りやすいですね。
で、音声入力を行うばあい、今回のLuisにコマンドを丸投げしてしまう、という方法を使えばソースの共通率はぐっと上がります。
しかも、学習は端末とは違うところで行えるので、Intentさえしっかり作っておけば方言が来てもLuisの学習だけで対応でき、ソフトの作り直しは要りません。

難点は、まあ、遅いんですよねー。Luisさん。