[Microsoft Bot Framework] LUISに日本語を解析させる

概要

MicrosoftのLUISは自然言語を解析してくれるサービスですが、現状 英語/フランス語/イタリア語/スペイン語/中国語 にしか対応していないという問題があります。

【参考記事】
『Build2016 : LUIS による自然言語入力解析』

しかし、Bot Connectorを通すことでその自動翻訳機能を利用することができ、結果的に「自然な日本語で受け答えができるボット」を開発することができます。

今回は「日付を入力したらその曜日を答える」ボットを作ってみます。

ボットを作成し、Bot Connectorに登録する

下記記事にMicrosoft Bot Frameworkを使って作成したボットをBot Connectorに登録するまでの流れが紹介されています。

『SlackとWeb上のチャットをつなぐ / MS謹製のbot作成フレームワーク「Bot Builder」で遊んでみた!』

記事中に公式ドキュメントから引用された図が貼られていますが、”Bot Connector”の欄に”Automatic translation to 30+ languages”という記述があります。今回はこれのお世話になろうというわけです。

コードは「Bot Builderを利用して」に貼られている方をベースとします。以下、転載となります。

var restify = require('restify');
var builder = require('botbuilder');

var bot = new builder.BotConnectorBot({ appId: process.env.appId, appSecret: process.env.appSecret});
var dialog = new builder.CommandDialog();

dialog.matches(['Hi', 'Hello', 'こんにちは'], function (session) {
session.send('こんにちは');
});
bot.add('/', dialog);

var server = restify.createServer();
server.post('/v1/messages', bot.listen());
server.listen(process.env.port || 8080, function () {
console.log('%s listening to %s', server.name, server.url);
});

これをAzure(App Service)あたりに上げます。
よろしければ、下記記事もご覧ください。

『Macで開発したボットをAzureで運用する』

Bot Connectorに登録する際、エンドポイントとして用意したパス(上記コードで言うとserver.post('/v1/messages', bot.listen());の部分)を登録するのがポイントです。

スクリーンショット 2016-06-15 10.12.40

appId, appSecret は「アプリケーション設定 > アプリ設定」から渡します。

スクリーンショット 2016-06-15 10.25.08

OK! 無事、Bot Connectorを通して受け答えができるようになりました。

スクリーンショット 2016-06-15 10.37.35

LUISにIntent, Entityを登録する

Microsoft LUISに関する説明、簡単な使い方は下記記事が参考になります。

『Build2016 : LUIS による自然言語入力解析』

このあたりから少し詳しく説明していきます。
まず、新規アプリケーションを作成します。

スクリーンショット 2016-06-15 10.44.21

ご覧の通り、選択できる言語のリストに日本語はありません。
ここは”English”を選択しておきます。

今回は「日付を入力したらその曜日を答える」ボットを作るのが目的だったわけですが、ここで具体的な問答を考えてみましょう。

問:今日は何曜日ですか?
答:その日は水曜日です。

上記問の「今日」の部分はいろいろな日付表現を受け付けたいところです。これが今回の”Entity”となります。
LUISには組み込みのエンティティ(Pre-built Entities)に日付(datetime)があるので、それを利用しましょう。

左メニュー「Pre-built Entities」横の追加ボタンを押し、”datetime”を選択し、[OK]を押します。

スクリーンショット 2016-06-15 11.01.00

“datetime”が利用可能になりました。

スクリーンショット 2016-06-15 11.01.35

次にIntentを登録します。
例文を英語で入力する必要がありますが、Bot Connectorで自動翻訳された英文がLUISに渡されるのであれば、Bing翻訳の英訳で十分でしょう。これで英語に自信がない人でも大丈夫です。

スクリーンショット 2016-06-15 11.05.54

左メニュー「Intents」横の追加ボタンを押し、インテント名(任意)と先程翻訳した例文を登録し、[Save]を押します。

スクリーンショット 2016-06-15 11.08.02

Entity候補として自動的に”today”の部分が選択されました。これで問題ありませんので、[Submit]を押します。

スクリーンショット 2016-06-15 11.08.23

“what_day”が登録されました。
“None”は最初から登録されているIntentで、どれにもマッチしない場合を表します。

スクリーンショット 2016-06-15 11.10.48

左下”Train”ボタンを押して、例文を学習させます。

スクリーンショット 2016-06-15 11.25.41

左メニュー「Publish」から[Publish web service]を押すとURLが表示されます。
ここから、idとsubscription-keyを取得します。

スクリーンショット 2016-06-15 11.29.09

Intentに対する返答を実装する

ここまでで以下の流れができるようになっています。

[chat] 今日は何曜日ですか?
 ↓
[Bot Connector] Today what is the day of the week?
 ↓
[LUIS] Intent=”what_day”, Entity(datetime)=”today”

これに対する返答をNode.jsで実装してやります。
サンプルコードは下記の通りです。
CommandDialogの代わりにLuisDialogを利用しています。`id`, `subscription-key`は「アプリケーション設定 > アプリ設定」から渡すようにします。
単に返答を返すだけでなく、どのようなデータが受け渡されているかをログに出力してみました。

var restify = require('restify');
var builder = require('botbuilder');

var bot = new builder.BotConnectorBot({ appId: process.env.appId, appSecret: process.env.appSecret});
var url = 'https://api.projectoxford.ai/luis/v1/application?id=' + process.env.luisId
+ '&subscription-key=' + process.env.luisSubscriptionKey
var dialog = new builder.LuisDialog(url);

bot.add('/', dialog);

// Intent="what_day"の場合の処理
dialog.on('what_day', function(session, args) {
console.log('message:');
console.log(session.message);

var date = builder.EntityRecognizer.findEntity(args.entities, 'builtin.datetime.date');
console.log('date:');
console.log(date);

if (date != undefined && date.resolution != undefined) {
var d = new Date(date.resolution.date);
var day = '日月火水木金土'[d.getDay()];
session.send('その日は「' + day + '曜日」です。');
} else {
session.send('日付を取得できませんでした。');
}
});

// Intent="None"の場合の処理
dialog.onDefault(function(session, args) {
console.log('args:');
console.log(args);

console.log('message:');
console.log(session.message);

session.send("質問を理解できませんでした。もう一度、少し表現を変えて質問してみてください。")
});

var server = restify.createServer();
server.post('/v1/messages', bot.listen());
server.listen(process.env.port || 8080, function () {
console.log('%s listening to %s', server.name, server.url);
});

動作確認

スクリーンショット 2016-06-15 12.19.13

入力した文章を理解してもらえませんでした。

args:
{ intents:
[ { intent: ‘None’, score: 0.57440114 },
{ intent: ‘what_day’, score: 0.0471038148 } ],
entities: [] }
message:
{ type: ‘Message’,
id: ‘9UcWTbjioJI’,
conversationId: ‘DFS1VbGtB9ae5Ez4m49dp5TFGUj6kP5ol81I6nu7AVEEJ3Il’,
created: ‘2016-06-15T03:15:44.8295028Z’,
language: ‘en’,
text: ‘今日は何曜日ですか?’,

ログを確認すると、LUISはこの文を’what_day’ではなく’None’だと解釈したようです。
Messageの内容を見ると、`language: ‘en’`となっています。入力をそのまま英文として解釈したようですね。

ボットに対してこれから日本語を使うことを伝え、再度同じ質問をすると今度は期待した答えをくれました。

スクリーンショット 2016-06-15 12.18.01

message:
{ type: ‘Message’,
id: ‘GuWITxWtRCb’,
conversationId: ‘7Uj2OT5w85OUFhL9UNfzG2p8fF5wR78wFwaAVlDgCLs7om’,
created: ‘2016-06-15T03:17:29.3002618Z’,
sourceText: ‘今日は何曜日ですか?’,
sourceLanguage: ‘ja’,
language: ‘en’,
text: ‘Today what is the day of the week?’,
(中略)
date:
{ entity: ‘today’,
type: ‘builtin.datetime.date’,
startIndex: 0,
endIndex: 4,
resolution: { date: ‘2016-06-15’ } }

sourceText → text と自動翻訳されているのが分かります。
また、翻訳された”today”がEntityとして解釈され、それがLUISによって”2016-06-15″と解決されていることも分かります。

Bot Connectorの設定項目に”Default Conversation Language”というのがあり、これに”ja”と入れることで言語切替なしでいけることを期待したのですが、そうなりませんでした。他の設定手段があるのでしょうか?

スクリーンショット 2016-06-15 11.48.18

それはさておき、他のパターンも試してみましょう。

スクリーンショット 2016-06-15 12.29.42

和暦も理解してくれるのはありがたいですね!

message:
{ type: ‘Message’,
id: ‘GQPdJkOyxrc’,
conversationId: ‘7Uj2OT5w85OUFhL9UNfzG2p8fF5wR78wFwaAVlDgCLs7om’,
created: ‘2016-06-15T03:31:41.7877018Z’,
sourceText: ‘平成28年6月16日は何曜日だっけ?’,
sourceLanguage: ‘ja’,
language: ‘en’,
text: ‘6/16/2016, what is the day of the week?’,
(中略)
date:
{ entity: ‘6/16/2016’,
type: ‘builtin.datetime.date’,
startIndex: 0,
endIndex: 8,
resolution: { date: ‘2016-06-16’ } }

では、デタラメな質問を投げると…?

スクリーンショット 2016-06-15 13.31.58

Intentの判定には成功しましたが、Entityは取得できませんでした。
妥当な結果だと思います。

message:
{ type: ‘Message’,
id: ‘7ZrZJP1tEcH’,
conversationId: ‘7Uj2OT5w85OUFhL9UNfzG2p8fF5wR78wFwaAVlDgCLs7om’,
created: ‘2016-06-15T04:31:46.4026778Z’,
sourceText: ‘私は何曜日でしょうか?’,
sourceLanguage: ‘ja’,
language: ‘en’,
text: ‘I what what day of the week?’,
(中略)
date:
null

このクオリティであれば、十分実用に耐えうるのではないでしょうか。