Microsoft Cognitive Services の Custom Vision Service は、オリジナルの画像判定エンジンを作成して API で推定値を取得できるサービスです。また、駅すぱあとwebサービス は、国内初の乗換案内ソフト 駅すぱあと がもつ様々な情報をWebAPIで提供しているサービスです。
今回は Custom Vision Service で作成した画像判定エンジンを利用して、
鉄道の路線を画像から判別し、 駅すぱあとwebサービス を利用して路線に含まれる駅を検索し、それを返す BOT を作成する方法を紹介します。
Microsoft Cognitive Services の Custom Vision Service は、オリジナルの画像判定エンジンを作成して API で推定値を取得できるサービスです。
機械学習などで画像判定ロジックを構築しなくても、画像をアップロードしてタグ付けを行うことで、画像判定エンジンを構築できます。
駅すぱあとwebサービス は、国内初の乗換案内ソフト 駅すぱあと がもつ様々な情報をWebAPIで提供しているサービスです。
経路探索はもちろん、駅データや時刻表などの情報を取得することができます。
無料で使えるフリープランを用意しており、企業にかぎらず個人でもご利用いただけます。
この BOT アプリは Bot Framework Channel Emulator を使ってローカル環境で稼働確認することが可能です。また、Web 公開 & Bot Framework に登録すると、埋め込み可能な Web Chat が利用できます。
駅すぱあと x Cognitive Services で 画像DE路線当てBOT を作ろう!
(1) Custom Vision 編
(2) BOT実装 編
- Node.js ver 編 ※このページ
- C# ver 編
Custom Vision 編、BOT実装 編を通して作成できる 画像DE路線当てBOT↓
この資料では、 Bot Builder を用いてBOTを実装する部分を紹介します。
必要な環境、サブスクリプションの準備
開発環境
Bot Framework Channel Emulator
BOTの挙動をローカルで確認する際に必要となります。
https://github.com/Microsoft/BotFramework-Emulator/releases/ からダウンロードすることができます。
Node.js
Node.jsを利用してBOTを開発します。
https://nodejs.org/en/ からダウンロードすることができます。
ngrok
ローカルホストを外部からでも叩けるようにするツールです。
今回は、BOTを経由して Custom Vision API に 画像を添付してPOSTリクエストを行うのですが、その際に必要となります。
https://ngrok.com/download からダウンロードできます。
駅すぱあと Webサービス のアクセスキー
駅すぱあと Webサービス のフリープランをお申込みいただき、アクセスキーを取得してください。
https://ekiworld.net/service/sier/webservice/free_provision.html
(即時発行ではないので少しばかしお時間かかります・・すいませんorz)
Custom Vision 編 を実施していない場合
Custom Vision 編 を実施していない場合はMicrosoft アカウントを準備してください。([Custom Vision 編] と同じもので問題ありません。)
Microsoft アカウント
Cognitive Services (と必要に応じて Azure) サブスクリプションの申し込みに必要ですので、持っていない場合は取得しておきます。
BOT アプリの実装
BOTを動かす
まず最初に、BOTが自分の環境でちゃんと動くかどうかを確認します。
BOTを実装する作業ディレクトリに移動し、以下のコマンドを実行してください。
$ npm init
ぽちぽちエンターを押して設定していきますが、entry pointの項目だけは、デフォルトではなくapp.js
に設定することをオススメします。
設定が完了したら、BOT開発に必要な以下のモジュールをインストールします。
$ npm install --save botbuilder restify
- botbuilder...MicrosoftBotFrameworkで動くBOTを作成できるSDKです。
- restify...REST形式のwebサービス構築に特化したフレームワークです。
作業ディレクトリ直下に、app.jsファイルを作成します。
その中に、以下のコードをコピーして保存してください。
最低限の機能を揃えたサンプルコードです。
var restify = require('restify');
var builder = require('botbuilder');
var server = restify.createServer();
server.listen(process.env.port || process.env.PORT || 3978, function () {
console.log('%s listening to %s', server.name, server.url);
});
var connector = new builder.ChatConnector({
appId: process.env.MICROSOFT_APP_ID,
appPassword: process.env.MICROSOFT_APP_PASSWORD
});
server.post('/api/messages', connector.listen());
var bot = new builder.UniversalBot(connector, function (session) {
session.send("You said: %s", session.message.text);
});
ファイルを保存したら、挙動の確認を行います。
以下のコマンドを打ち込み、サービスを起動させます。
$ node app.js
サービスが起動すると、リクエストを受け付けるポート番号がターミナルに表示されます。
エミュレータを立ち上げます。
ウィンドウ上部のEnter your endpoint URLをクリックし、以下のURLを打ち込みます。
*{{ポート番号}}*の部分は、サービスを起動した時に表示されるポート番号に置き換えてください。
http://localhost:{{ポート番号}}/api/messages
Microsoft App IDとMicrosoft App Passwordを指定する画面が表示されますが、それは無視して、Connectをクリックします。
メッセージ入力欄に適当に何かメッセージを書き込んでみましょう。
例えばHelloと入力したら、You said: Helloと返れば成功です。
Custom Vision の呼び出し
さて、自分の環境で無事にBOTが動かせることが確認できたら、Custom Vision APIをBOT内で呼び出してみましょう。
必要なモジュールをインストールします。
$ npm install --save dotenv request
- dotenv...環境変数を扱う際に便利なモジュールです。
- request...HTTPリクエストを扱う際に便利なモジュールです。
先ほど作成したapp.jsを開きます。
app.jsの上部に、app.js内でdotenv、requestが使用できるように、モジュールの読み込み記述を行います。
var restify = require('restify');
var builder = require('botbuilder');
// 追記する部分 ここから
require('dotenv').config();
var request = require('request');
// ここまで
:
中略
:
画像をURLとして取得し、Custom Vision を呼び出します。
var bot = new builder.UniversalBot(connector, function (session) {
から以下の行を、まるっと下記のコードに書き換えます。
:
中略
:
var bot = new builder.UniversalBot(connector, function (session) {
// メディア(画像)が添付されているか
if(session.message.attachments.length > 0) {
var tag = ""; // custom vision で定義したカテゴリータグ
var customVisionApiRequestOptions = {
uri: process.env.CUSTOM_VISION_API_URI,
headers: {
"Content-Type": "application/json",
"Prediction-Key": process.env.CUSTOM_VISION_PREDICTION_KEY
},
json: {
"Url": session.message.attachments[0].contentUrl
}
}
// Custom Vision API へのPOSTリクエスト
request.post(customVisionApiRequestOptions, function (error, response, body) {
if(!error && response.statusCode == 200) {
// Probability (≒信頼度) の高いものからリストされるため、0番目を取得
tag = response.body.Predictions[0].Tag;
session.send("%s に、いちばんにてるね!", tag);
} else {
console.log("error: " + error);
session.send("はんていにしっぱいしちゃった。もういちどがぞうを送ってください。");
}
})
} else {
session.send("でんしゃのしゃしんを送ってね!");
}
});
process.env.環境変数名
で、指定された環境変数名から値を取得することができます。
Custom Vision API のPrediction-KeyやURLは、環境変数で扱いますので、環境変数に値をセットします。
app.jsと同じ階層に、.envという名前でファイルを生成し、その中に環境変数を記述していきます。
CUSTOM_VISION_API_URI={{ご自身の Custom Vision API のURL}}
CUSTOM_VISION_PREDICTION_KEY={{ご自身の Custom Vision API のPrediction Key}}
※windows10の場合、拡張子の前に名前がないとファイルを作らせてくれないようで、ファイルエクスプローラから*.env*ファイルを新規作成できないことが分かりました。こちら( [Win10] Windows上で拡張子だけのファイルを作成する方法 )のページにコマンドプロンプトで作成する方法が挙げられています。
*{{ご自身の Custom Vision API のURL}}と{{ご自身の Custom Vision API のPrediction Key}}*の値は、Custom Vision 編のAPIアクセス情報画面から取得できます。
Prediction URLの画面の、*If you have an image URL:*と書かれた方の値を利用します。
挙動を確認します。
エミュレータを開き、メニューボタンから、App Settingsを開きます。
ngrokをダウンロードしてzipファイルを解凍すると、ngrokという名の実行ファイルが入っています。
その実行ファイルが置いてあるパスを指定します。
Bypass ngrok for local addressesと書かれたチェックボックスがありますが、チェックを外してください(チェックを入れてしまうと、意図しないエラーが発生してしまいます)。
最後にSAVEをクリックします。
サービスを起動します。
$ node app.js
入力エリアの画像アイコンをクリックして、分析した画像を選択し、BOTに送信すると、判定されたタグが返信されるのを確認してください。
駅すぱあとwebサービス の呼び出し
画像を判定して取得できたタグに応じてメッセージを変更します。
今回は、 駅すぱあとWebサービス を利用して、路線名から停車駅を取得し、表示するようにします。
駅すぱあとwebサービスの詳しい使い方は、駅すぱあとwebサービス フリープラン ドキュメントに掲載しています。
路線名、停車駅はこちらのAPIを利用して取得できます。
今回は予め路線コードはアプリ内に保持し、停車駅のみをAPIで取得しています。
app.jsを編集します。
var bot = new builder.UniversalBot(connector, function (session) {
から以下の行を、まるっと下記のコードに書き換えます。
:
中略
:
var bot = new builder.UniversalBot(connector, function (session) {
// メディア(画像)が添付されているか
if(session.message.attachments.length > 0) {
var lineName = ""; // 駅すぱあと運航路線名
var lineCode = ""; // 駅すぱあと運航路線コード
var tag = ""; // custom vision で定義したカテゴリータグ
var stations = []; // 路線停車駅情報(名前、緯度経度、都道府県..etc)リスト
var stationNames = []; // 路線停車駅名リスト
var customVisionApiRequestOptions = {
uri: process.env.CUSTOM_VISION_API_URI,
headers: {
"Content-Type": "application/json",
"Prediction-Key": process.env.CUSTOM_VISION_PREDICTION_KEY
},
json: {
"Url": session.message.attachments[0].contentUrl
}
}
// Custom Vision API へのPOSTリクエスト
request.post(customVisionApiRequestOptions, function (error, response, body) {
if(!error && response.statusCode == 200) {
// Probability (≒信頼度) の高いものからリストされるため、0番目を取得
tag = response.body.Predictions[0].Tag;
switch (tag) {
case "Chuo_Sobu":
lineName = "JR中央・総武線各駅停車";
lineCode = "110";
break;
case "Chuo_Ex":
lineName = "JR中央線快速";
lineCode = "109";
break;
case "Keihin-Tohoku":
lineName = "JR京浜東北線";
lineCode = "115";
break;
case "Tokaido":
lineName = "JR東海道本線";
lineCode = "117";
break;
case "Yamanote":
lineName = "JR山手線";
lineCode = "113";
break;
case "Yokosuka_SobuEx":
lineName = "JR横須賀線";
lineCode = "116";
break;
}
session.send("%s に、いちばんにてるね!", lineName);
var ekispertRequestOptions = {
url: encodeURI("https://api.ekispert.jp/v1/json/station?operationLineCode=" + lineCode + "&key=" + process.env.EKISPERT_ACCESS_KEY),
json: true
}
// 駅すぱあとWebサービスへのGETリクエスト
request.get(ekispertRequestOptions, function(error, response, body) {
if (!error && response.statusCode == 200) {
session.send("%s は、以下のえきをはしるよ。", lineName);
stations = response.body.ResultSet.Point;
// 駅名称を列挙した配列を作成
for(var i=0; i<stations.length; i++) {
stationNames.push(stations[i].Station.Name);
}
session.send("%s", stationNames.join(' → '));
} else {
console.log("error: " + error);
}
})
} else {
console.log("error: " + error);
session.send("はんていにしっぱいしちゃった。もういちどがぞうを送ってください。");
}
})
} else {
session.send("でんしゃのしゃしんを送ってね!");
}
});
.envファイルに、 駅すぱあとwebサービス のアクセスキーを追加します。
:
中略
:
EKISPERT_ACCESS_KEY={{ご自身の 駅すぱあとwebサービス のアクセスキー}}
*{{ご自身の 駅すぱあとwebサービス のアクセスキー}}*は、駅すぱあとwebサービスにアクセスする為に必要な値です。
駅すぱあとwebサービス フリープラン 申し込みページから無料でお申し込みいただけます。
挙動を確認します。
$ node app.js
入力エリアの画像アイコンをクリックして、分析した画像を選択、BOTに送信します。
設定したメッセージが返答されたら完成です。
Appendix
完成形のソースコードを GitHub にて公開しました。
https://github.com/hmaruyama/train-finder-bot
終わりに
様々なサービスを組み合わせることで、簡単にBOTを実装することができました。
作成したBOTをベースに、画像判定の精度をあげるもよし、対応路線のバリエーションを増やすもよし、会話のバリエーションを増やすもよし・・。
パブリックに公開するなら、こちらの手順(Microsoft Bot Framework、Azureを利用したBot開発!Bot作成からWeb上で公開するまで - hi, hikaru)が参考になると思います。