今回はVue.jsとFlaskで文章の受け渡しを行って、Chatterbotの返答に合わせてCubismWeb(Live2D Cubism3SDK for Web)のモデルを動かしてみます。
Chatterbotとは
機械学習ベースの簡易的な対話エンジンを作ることができるPythonライブラリ
会話のデータ(コーパス)を用意することで、入力された文章に最も近いデータ(文字列)を検索して応答を生成する
全体の流れ
1. Vueのinput要素にテキストを入力する
2. 入力されたテキストをChatterbotに渡す
3. Chatterbotが生成した返答をVueに返す
4. 返された文章に合わせてCubismWebのモデルを更新する
使用するイラスト
CubismWebで表示するモデルです。
© Unity Technologies Japan/UCL
上記モデルには以前の記事で Live2D のテンプレート「FaceRig」を適用しています。
テンプレートを適用する手順についてはこちらで紹介しています。
動作確認用のサンプル
ChromeとEdgeで動作を確認しています。
サンプルでは予めchatterbot_corpusに用意した文章で会話を行っています。
対話エンジンのボディとしてのCubismWeb
対話エンジン(Chatterbot)のボディとしてCubismWebのモデルを適用するまでの流れ
1. コーパスを編集する
まずは会話のトレーニングデータとなるchatterbot_corpusをgithubからダウンロードします。
現在(2019/4/20)の時点では日本語に翻訳されたデータも入っていたので、ある程度はそのまま使えます。
corpus_dataはymlファイルとしてカテゴリごとに用意されているので、簡単に内容を編集できます。
2. chatterbotを動かしてみる
最初にchatterbotをインストールします。
condaでは見つからなかったので、Anaconda環境では避けたほうが良いかもしれないです。
pip install chatterbot
日本語のトレーニングコーパスを指定して「おはよう、元気?」と問いかけてみます。
from chatterbot import ChatBot from chatterbot.trainers import ChatterBotCorpusTrainer chatbot = ChatBot("automaton") trainer = ChatterBotCorpusTrainer(chatbot) # トレーニング用のコーパスを指定 trainer.train("chatterbot.corpus.japanese.conversations") # 問いかけ response = chatbot.get_response("おはよう、元気?") print(response)
ファイルを実行すると「おはよう、元気?」に対応する言葉が返ってきます。
3. VueでUIを作成する
Vue.jsでテキストを入力する場所や、返答を表示する場所を作成します。
<div id="message_window"> <p id="ans">[[ answer ]]</p> </div> <div id="input"> <div class="inputA"> <input v-model="message"> <button @click="getAnswer" id="btn">try</button> </div> <div class="message_container"> <p id="mes">[[ message ]]</p> </div> </div>
4. テキストの受け渡しを処理する
Vueのinput要素に入力されたテキストをchatterbotに渡して、返事を受け取ります。
<script> const talk = new Vue({ el: "#exa", delimiters: ["[[", "]]"], data: { message: '', answer: '//メッセージを入力してください//' }, methods: { update_answer: function(str) { this.answer = str; }, getAnswer:function() { const src = this.message; callback = this.update_answer; fetch('http://localhost:5000/chatCubism', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(src), }).then(function(res){ return res.json(); }).then(function(src) { callback(src.ans); //chatterbotからの返事 const count = 0; const countup = function() { callback(''); } setTimeout(countup, 7000); }).catch(function(error) { console.log(error) }) }} }) </script>
5. chatterbotの処理
2.で行った動作確認と同じ流れでVueから渡されたテキストを使用します。
コーパスを(“chatterbot.corpus.japanese”)とすると、日本語の全てのカテゴリを指定できます。
def answer(src): chatbot = ChatBot("automaton") trainer = ChatterBotCorpusTrainer(chatbot) trainer.train("chatterbot.corpus.japanese.botprofile") response = chatbot.get_response(src) return response
chatterbotが生成した返答をVueに返します。
@app.route('/chatCubism', methods = ['GET', 'POST']) def chatCubism(): src = request.json if request.method == 'POST': responce = answer(src) ans = str(responce) return jsonify({'ans': ans}) elif request.method == 'GET': return render_template('chatCubism.html')
6. CubismWebの画面を表示
以前の記事(アヤメの分類)と同じ手順でCubismWebの画面を設置します。
7. 会話の内容に合わせてCubismWebのモデルを更新
今回は入力したテキストに「?」が含まれているか、返答する文章が長いか短いかを参照して、再生するモーションを切り替えることにしました。
用意したモーションは下記の4種類です。
モーションA:入力したテキストに「?」が含まれている、返事の長さが11文字以上
モーションB:入力したテキストに「?」が含まれている、返事の長さが10文字以下
モーションC:入力したテキストに「?」が含まれていない、返事の長さが11文字以上
モーションD:入力したテキストに「?」が含まれていない、返事の長さが10文字以下
window.onload = () => { let motionNum = 0; const getbtn = document.getElementById("btn"); getbtn.onclick = () => { setTimeout( () => { const getmes = document.getElementById("mes"); const getans = document.getElementById("ans"); if (getmes.textContent.indexOf('初回読み込み') != -1) { motionNum = 0; } else if (getmes.textContent.indexOf('?') != -1) { if (getans.textContent.length > 10) { motionNum = 1; } else if (getans.textContent.length <= 10) { motionNum = 2; } } else { if (getans.textContent.length > 10) { motionNum = 3; } else if (getans.textContent.length <= 10) { motionNum = 4; } } this.onUpdate = () => { //onUpdateの中身と同じ// if (motionNum == 0) { //待機時のモーションを再生 return; } for (let i = 0; i < this._models.getSize(); i++) { if (motionNum == 1) { this._models.at(i).startMotion(LAppDefine.MotionGroupAdd, 1, LAppDefine.PriorityNormal); } else if (motionNum == 2) { this._models.at(i).startMotion(LAppDefine.MotionGroupAdd, 2, LAppDefine.PriorityNormal); } else if (motionNum == 3) { this._models.at(i).startMotion(LAppDefine.MotionGroupAdd, 3, LAppDefine.PriorityNormal); } else if (motionNum == 4) { this._models.at(i).startMotion(LAppDefine.MotionGroupAdd, 4, LAppDefine.PriorityNormal); } } motionNum = 0; //一回モーションを再生したら、待機時のモーションに移行する } },2000); //メッセージが表示されるまで少し時間がかかるので、その分処理を遅らせる } }
キャラクタが表示されるだけで印象がだいぶ変わりますね。