【Node-RED】AIによる広島名物とのそっくり度からオススメのお店を紹介するLINE BOTを作ったよ


今回は、9月12日・13日にオンラインで開催されたハッカソンで作ったものの作り方を紹介します。

ハッカソンのテーマ

今すぐ "GoTo 広島" の人にも、今すぐには行かなくても将来行くかもしれない人にも お勧めできる ~ "ひろしま" 体験プロダクト ~ 

作ったもの

タイトル

【広島県へ提案】AI時代のそっくりさんをGO TO広島で地方創生

説明

新型コロナで旅行者が激減しており、新たな観光施策が必要です。
ただし、「ばらまき」は一過性にすぎないので、広島に興味をもってもらうような施策を考えました。
そこで、私は、
「AI時代のそっくりさん、GoTo広島キャンペーン」
を提案します。

私たちは、自分に似ている人に特別に親しみをもつ傾向があります。
そこで、広島にゆかりのあるものに似ている人を呼ぼうと考えます。

とはいえ、ただ似ているだけでは面白くありません。
時はAI時代です。
AIが見た「そっくりさん」を呼びましょう。
AIが見た「そっくりさん」は我々が認識する「そっくりさん」とは違います。人知を超えた感覚に触れることができるでしょう。
それが、AI時代の観光キャンペーンなのです。



めちゃくちゃ意味不明ですが、ハッカソンという短時間の間で自分の技術の範囲内で実装するアイデアが浮かび、後付けで無理やり「GoTo広島」にこじつけたのが実態ですwww

デモ

写真を投稿すると、「広島の名物と似てる度」をAIが算出し、その広島名物に関するオススメのお店を紹介してくれます。

このデモでは、最初の男性はつけ麺度71%なので「風風ラーメン」というお店をレコメンドし、次の男性は汁なし担担麺度が53%なので、「武蔵房」というお店をレコメンドしました。

他の広島名物には、広島風お好み焼き、牡蠣、尾道ラーメンを登録しています。

審査員や他の参加者から「確かに、似てると言われたら行きたくなる」などのコメントいただきました。

広島県さん、「GO TO広島キャンペーン」にいかがでしょうか?

チャットボットのQRコード

実際に動いてますので、是非ためしてみてください。


システム

構成図

enebular、Node-REDをクラウドの中にいれてるような書き方でいいのか分かりませんが雰囲気的には伝わると思いますのでヨシとします。

環境

開発  :enebular + Node-RED
クラウド:Heroku

機械学習モデル作成

まずは、機械学習用に、ネットから広島名物(お好み焼き、汁なし担担麺、つけ麺、牡蠣、尾道ラーメン)の画像をスクレイピングのプログラムを使って取得します。

スクレイピングのプログラムはこちらを拝借しました。
https://itstudio.co/2018/12/28/8664/


画像が揃ったら、Teachable Machineという簡単に機械学習モデルを作れるサービスで学習させました。


Node-RED詳細

Node-REDで作りました。

参考サイト

以下の2つのサイトをめちゃくちゃ参考にしてます。
本当にありがとうございました。

1〜4、12、13は、
Teachable Machine x LINE Bot x Node-RED ハンズオン

11は、
Node-REDで待ち合わせ(同期・非同期)フローの作る方法を丁寧に説明してみました。

ノードを追加

デフォルトにはないTeachable MachineとLINE Massaging APIのノードを「パレットの管理」欄から追加

  • node-red-contrib-teachable-machine

  • node-red-contrib-line-messaging-api

各ノードの説明

1.[post]_linebot

2.function01

function01.js
const messageID = msg.payload.events[0].message.id;
const replyToken = msg.payload.events[0].replyToken;

msg.messageID = messageID;
msg.replyToken = replyToken;

return msg;



3.http request

トークンは、LINE Messaging APIのChannel access tokenを入力



4.teachable machine

Url欄に Teachable Machineで作った機械学習モデルのURLを入力
Output欄は、作品に応じて



5.AI-function
後ほどの11の非同期処理のためにタグをつけます。

function01.js
msg.tag = "AI";
return msg;



6.change



7.ぐるなび function01

GuruNavi-function01.js
msg.payload = encodeURIComponent(msg.payload)
return msg;



8.http request

URLにAPIのURLを入力

「広島駅から3000メートル以内でAIが算出した広島名物がキーワードのお店」という設定にしました。

https://api.gnavi.co.jp/RestSearchAPI/v3/?keyid=YourAPIKey&latitude=34.392777&longitude=132.457525&range=5&freeword={{{payload}}}

ぐるなびのAPIはこちらを参照
https://api.gnavi.co.jp/api/manual/restsearch/


9.json



10.ぐるなび function02
後ほどの11の非同期処理のためにタグをつけます。

GuruNavi-function01.js
msg.tag = "GNAVI";
return msg;



11. AIとぐるなび両方揃うまで待機

AI-GuruNavi-Taiki.js
let chA = context.get('Ai')||[];
let chB = context.get('Gnavi')||[];
let tag = msg.tag;
delete msg.tag;

if (tag == "AI") {
    chA.push(msg);
} else if (tag == "GNAVI") {
    chB.push(msg);

} else {
    return msg;
}

let outA;
let outB;
if ((chA.length === 0) || (chB.length === 0)) {
    context.set('Ai',chA);
    context.set('Gnavi',chB);
    node.warn('hogehoge:'+chB.length);
    return null;
} else {
    outA = chA.shift();    
    outB = chB.shift();    
    context.set('Ai',chA);
    context.set('Gnavi',chB);
}

let newMsg = {payload:""};
newMsg.ai = outA;
newMsg.gnavi = outB;

return newMsg;



12.LINE BOT に出力する前処理

実はオススメのお店はただの乱数でした。

maeshori.js
const className = msg.ai.payload[0].class;
const score = parseInt(msg.ai.payload[0].score*100);

//オススメのお店 (10先から乱数)
let rand = Math.floor( Math.random () * msg.gnavi.payload.rest.length);
const msgText = className+""+score+"\n\n[オススメのお店]\n"+msg.gnavi.payload.rest[rand].name+"\n"+msg.gnavi.payload.rest[rand].url;

msg.payload = {};
msg.payload.events = [
    {
        "type": "message",
        "replyToken": msg.ai.replyToken,
        "message":
            {
            "type": "text",
            "text": msgText
            }        
    }

];
return msg;



13.LINE BOT出力

LINE Developerからmessaging APIのシークレットキーとアクセストークンを入力




これで完成!!

是非、広島に遊びに来てください!
そのときは、AIのオススメするお店でお食事しましょう。