LoginSignup
13
13

More than 3 years have passed since last update.

Google Assistant で 深層学習 - KerasをRaspberry Piで音声操作する

Last updated at Posted at 2017-05-31

Google Assistant + Raspberry Piを使って深層学習ライブラリ「Keras」を動作させてみました。まずは深層学習のHelloWorld、「赤りんごと青りんごを見分ける」のを人工知能にさせてみました。こんな感じです。

グーグル・アシスタントさんがりんごを見分けるよ。
Google assistant classifies apples.#GoogleAssistant #RaspberryPi #TensorFlow #keras pic.twitter.com/lmk9cyklZH

— ポンダッド (@pon_dad) 2017年5月30日

GoogleAssistantから「アクション(ここではSamantha)」を呼び出し、「インテント(意図)」によりりんごの写真を撮影します。「Say Cheese」で写真を撮影し、保存します。

Screenshot from DSC_0112.MOV.png

「インテント」「What is this?」の呼びかけで画像認識を開始します。

今回はここからりんごを深層学習ライブラリ「Keras」を利用して「赤りんご」か「青りんご」かを判別させます。ここでは認識率90%以上で正と判断させました。

Screenshot from DSC_0112.MOV - 1.png

「Green Apple」と正しく判定しました。

環境

環境は前回記事と同じです。

ファイル_000.jpeg

ちょこちょこと改造しているのですが、今回はプッシュボタンと合わせてLEDをGPIOに取り付けています。興味のある方はこちらの動画 Button Trigger for Google Assistant on Raspberry Pi ーYouTube で詳しく取り付け方やコードを紹介しているのご覧になると良いでしょう。

これでモニターが無い環境でも、LEDが点灯するので音声待受状態を確認することが出来ます。

pushtotalk.py - Gist

大まかな仕組み

Google Cloud Platform

Data centers – Google Data centers.png

画像はGoogle Data Centers より引用

Google Cloud Platformに設置したGoogle Assistantを呼び出して会話を行います。

API.aiキーワードの設定し、「Google Cloud Functions」で実際のアクションを設定します。

「Google Cloud Functions」はNode.jsを利用して、リクエストを受ける度にイベントを駆動させます。

無題の図形描画.png

index.js
'use strict';

process.env.DEBUG = 'actions-on-google:*';
const App = require('actions-on-google').ApiAiApp;

const exec = require('child_process').exec;
const camera_adress = "https://xxxxxxx.ap.ngrok.io/camera"
const what_adress = "https://xxxxxxx.ap.ngrok.io/what"

// [START YourAction]
exports.myAction = (request, response) => {
  const app = new App({request, response});
  console.log('Request headers: ' + JSON.stringify(request.headers));
  console.log('Request body: ' + JSON.stringify(request.body));

  function responseCamera (app) {
    exec(`curl ${camera_adress}`, (error, stdout, stderr) => {
      if (error) {
        console.error(`exec error: ${error}`);
        return;
      }
      console.log(`${stdout}`);
      app.ask(`<speak>I took <break time='1' /></speak>` + `${stdout}`);
    });
  }

  function responseWhat (app) {
    exec(`curl ${what_adress}`, (error, stdout, stderr) => {
      if (error) {
        console.error(`exec error: ${error}`);
        return;
      }
      console.log(`${stdout}`);
      app.ask(`<speak>This is a <break time='1' /></speak>` + `${stdout}`);
    });
  }

  function responseCorrect (app) {
      app.ask(`<speak>Thank you.</speak>`);
  }

  const actionMap = new Map();
  actionMap.set('talk.camera', responseCamera);
  actionMap.set('talk.what', responseWhat);
  actionMap.set('talk.correct', responseCorrect);

  app.handleRequest(actionMap);
};
// [END YourAction]

「Google Cloud Functions」を利用して、RaspberryPiにたてたローカルサーバーWebhookを送信します。ファイアウォールを超えるためにここではngrokを利用してアクセスします。

Raspberry Pi

ファイル_000 (1).jpeg

以前作成した(Raspberry Pi 深層学習で「赤りんご」と「青りんご」を見分ける(Keras・Open CV))学習用のapple.npyファイルを利用して再度学習させました。

Kerasが2.0にアップデートし、一部APIが変更になったので以下のようにモデル作成用のファイルを書き換えました。

apple_keras.py
import numpy as np
from keras.models import Sequential
from keras.layers import Dense, Dropout, Activation, Flatten
from keras.layers import Conv2D, MaxPooling2D
from keras.utils import np_utils

root_dir = "./image/"
categories = ["red_apple", "green_apple"]
nb_classes = len(categories)
image_size = 32

def main():
    X_train, X_test, y_train, y_test = np.load("./image/apple.npy")
    X_train = X_train.astype("float") / 256
    X_test  = X_test.astype("float")  / 256
    y_train = np_utils.to_categorical(y_train, nb_classes)
    y_test  = np_utils.to_categorical(y_test, nb_classes)
    model = model_train(X_train, y_train)
    model_eval(model, X_test, y_test)

def build_model(in_shape):
    model = Sequential()
    model.add(Conv2D(32, 3, 3,
    border_mode='same',
    input_shape=in_shape))
    model.add(Activation('relu'))
    model.add(MaxPooling2D(pool_size=(2, 2)))
    model.add(Dropout(0.25))
    model.add(Conv2D(64, 3, 3, border_mode='same'))
    model.add(Activation('relu'))
    model.add(Conv2D(64, 3, 3))
    model.add(MaxPooling2D(pool_size=(2, 2)))
    model.add(Dropout(0.25))
    model.add(Flatten())
    model.add(Dense(512))
    model.add(Activation('relu'))
    model.add(Dropout(0.5))
    model.add(Dense(nb_classes))
    model.add(Activation('softmax'))
    #model.compile(loss='binary_crossentropy',
    #optimizer='rmsprop',
    #metrics=['accuracy'])
    return model

def model_train(X, y):
    model = build_model(X.shape[1:])
    print(X.shape[1:])
    history = model.fit(X, y, batch_size=32, nb_epoch=10, validation_split=0.1)
    hdf5_file = "./image/apple-model.h5"
    model.save_weights(hdf5_file)
    return model

def model_eval(model, X, y):
    score = model.evaluate(X, y)
    print('loss=', score[0])
    print('accuracy=', score[1])

if __name__ == "__main__":
    main()

再度学習したモデルを使いカメラで読み込んだ画像をpredict()メソッドを利用して推測させます。

whatisthis.py
from flask import Flask
from flask import request, jsonify
import json
import numpy as np
import cv2
import subprocess
import time
import apple_keras as apple
import sys, os
import cv2
from keras.preprocessing.image import load_img, img_to_array
from keras import backend as K

app = Flask(__name__)

image_size = 32
categories = ["red apple", "green apple"]

@app.route('/camera')
def camera():
    cap = cv2.VideoCapture(0)
    cap.set(3, 760)
    cap.set(4, 480)
    while(True):
        ret, frame = cap.read()
        cv2.imshow('frame', frame)
        subprocess.call( ["aplay", "camera.wav"] )
        time.sleep(0.5)
        cv2.imwrite("apple.png", frame)
        text = "picture"
        break

    cap.release()
    cv2.destroyAllWindows()

    return text

@app.route('/what')
def whatisthis():
    X = []
    img = load_img("./apple.png", target_size=(image_size,image_size))
    in_data = img_to_array(img)
    X.append(in_data)
    X = np.array(X)
    X  = X.astype("float")  / 256

    model = apple.build_model(X.shape[1:])
    model.load_weights("./image/apple-model.h5")

    pre = model.predict(X)
    print(pre)
    if pre[0][0] > 0.9:
        print(categories[0])
        text = categories[0]
        text = text.encode('utf-8')
        return text
    elif pre[0][1] > 0.9:
        print(categories[1])
        text = categories[1]
        text = text.encode('utf-8')
        return text

if __name__ == '__main__':
    app.run(host="127.0.0.1", port=8080)

Raspberry側ではローカルサーバーをFlaskで立て、エンドポイントにWebhookでリクエストを受けた際に実行を行い戻り値をreturnで返します。

ここでは「青りんご」の推測値が90%以上のため、「GreenApple」を返しています。

まとめ

Googele Assistantは音声認識からアクション実行まで5秒以上経過するとエラー処理を起こすようです。今回は比較的簡単なモデルですので推測実行がRaspberryPiでもなんとか行えました。(動画をご覧になれば分かるのですがそれでも結構時間が掛かってしましまいます。)

もう少し複雑な深層学習モデルを利用する際は、RaspberryPiで処理を行うよりもクラウドにKerasの実行環境を作ったほうが良いかも知れません。

GCPでKerasを行う方法はこんな感じでまとめられています。

Deep Learning with Keras on Google Compute Engine - Medium

GCPの使い方を覚えたら今度やってみたいと思います。ではまた。

ご参考

興味のある方は過去記事も合わせてご参照下さい。

13
13
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
13