https://kindpark.jpn.org/dogemotion/

上記ページでウェブアプリを試すことができます。

以前の記事で、CNNを使って数字を判別するAIの作り方を紹介しました。今回は、犬の画像をもとに犬の感情を判別するAIの作り方を紹介します。正解率98%のモデルを作ることができました。

まず、目標から設定します。私の目で犬の画像を見て、4つの状態が推測できると考えます。

  • 嬉しい。満足。
  • 元気ない 。眠い。つまんない。
  • 普通。特に感情ない。
  • 怒っている。または恐怖。

もちろん、獣医師として実際の犬を見たときには、痛みを感じているとか神経症状があるとか甲状腺機能低下症とか、そういった情報も犬を見たらだいたい分かります。ただ、画像だけを見た時には上記4つくらいしか判断できないかなというところです。

この4つの感情を画像から判断するAIを作りたいと思います。

  1. 4つのカテゴリごとに画像を集める(100枚ずつ計400枚)
  2. 画像を変形縮小回転して枚数を5倍にする(計2000枚)
  3. Convolutional Neural Network(CNN)に学習させる
  4. CNNを使って画像から犬の気持ちを予測する

1.4つのカテゴリごとに画像を集める(100枚ずつ計400枚)

CNNを作成するにあたって、必要な枚数についてのガイドラインはありません。今回、4カテゴリ100枚ずつを用意して98%の正解率を出すことができたので、逆に言えば、この問題はこれくらいの枚数でいいということでしょう。

画像を集める方法としては、以下の方法があります。

  1. 自分で写真撮る
  2. Githubのgoogle-images-downloadを使う
  3. chrome拡張機能を使う
  4. ブックマークレットを使う

2-4はgoogleの定期的な仕様変化により、すぐに古い方法は使えなくなります。今、記載しても確実に半年後には使えなくなるので、方法については検索をお願いします!4のブックマークレットが手早くていいと思います。

感情のフォルダごとに画像を保存してください。

2.画像を変形縮小回転して枚数を5倍にする(計2000枚)

tensorflowにImageDataGeneratorという便利な機能があります。この機能は、画像を拡大・縮小・右回転・左回転・明るさの変化等の加工をして別の画像を作る機能です。この機能を使う事で、学習のもとになる画像の数を飛躍的に増やすことができるのです。

大事なことは、フォルダごとに画像を読み込みますが、numpy arrayにする際にはその順番をシャッフルしてください。CNNに読み込ませるときにバッチ内が全て同じ感情のグループだと、うまく学習できません。最初、そのことを知らなくてvalとaccが全然あがらずに、なぜなんだろうと頭を抱えていました。ほかの機械学習にはないポイントで、異質ですね。

詳しくは、以下の記事を参照してください。コードが載っています。

3.Convolutional Neural Network(CNN)に学習させる

tensorflowを使って、CNNをモデリングして学習させます。自力でベーシックなCNNを作りましたが、ACC6割程度のしか実現できませんでした。4カテゴリなので、ACC25%がベースです。

詳しい方法は以下に記載しています。

ポイントは、3カテゴリ以上なのでlossには、categorical_crossentropyを使います。また、最後のDense層はactivationにsoftmaxを使います。

結局、AutoMLのAutoKerasを使ってtest setでACC98%を実現しました。

7/7 [==============================] – 41s 491ms/step – loss: 0.0951 – accuracy: 0.9804 [0.09505873173475266, 0.9803921580314636]

途中にEfficientNetB7が組み込まれているパラメーター64,107,938の巨大なモデルができあがりました。

もちろんリアルタイム判定はかなり厳しいです。10秒くらい判別にかかります。opencvを使えば、ウェブカメラからも表情判別ができます。

4.CNNを使って画像から犬の気持ちを予測する

上記のautokerasで作成したNNモデル(efficientnetが入ってるからCNN?)を保存して、次は予測に使います。インターフェイスつきのプログラムを作りました。参考にコードを載せておきます。

# -*- coding: utf-8 -*-
import os
os.environ["CUDA_VISIBLE_DEVICES"] = "-1"

###### Analyzer application ###################################
import pandas as pd
import glob
import cv2
import numpy as np
import tkinter
from tkinter import filedialog
import tkinter.ttk
import tkinter.messagebox
import os
from tensorflow.keras.preprocessing.image import load_img, img_to_array,array_to_img
from tensorflow.keras.models import load_model
import autokeras
import tensorflow as tf
from PIL import Image, ImageOps

#################### function to close the tinker window ####################
def _destroyWindow(root):   
    root.quit()
    root.destroy()
    
#################### function to ask which directly contains target pictures ####################
def askfileplace1():
    file_name = tkinter.filedialog.askopenfilename(title = "Select file",filetypes = (("jpg","*.jpg"),("all files","*.*")))
    global path
    path.set(file_name)

#################### function to ask where the trained model ####################
def askfileplace2():
    file_name = tkinter.filedialog.askopenfilename(title = "Select file",filetypes = (("h5 file","*.h5"),("all files","*.*")))
    global model_path
    model_path.set(file_name)
    
#################### function to recognize number  #################################
def face_recognize(selected_path,  model_path):
    
    classlist = ["happy","angry","neutral","no energy"]

    X_trial = []
    im = Image.open(selected_path)
    img = im.resize((224,224))
    img = img_to_array(img)
    img = img.reshape(224, 224, 3)
    img = img.astype('float32')/255
    X_trial.append(img)
    X_trial = np.array(X_trial, dtype='float') 

    model = load_model(model_path,custom_objects=autokeras.CUSTOM_OBJECTS) 

    pred = model.predict(X_trial)
    result = classlist[np.argmax(pred)]

    tkinter.messagebox.showinfo('表情認識AI','解析が終了しました。\n結果は\n{}です'.format(result))

#################### Interface #################################
def interface():
    global root
    
    root = tkinter.Tk()
    root.title('表情認識AI')
    root.resizable(True, True)
    root.geometry("600x200")

    frame1 = tkinter.ttk.Frame(root, padding=(32))
    frame1.grid()
    
    #create path text
    label1 = tkinter.ttk.Label(frame1, text='写真ファイルの場所', padding=(5, 2))
    label1.grid(row=0, column=0, sticky=tkinter.E)

    # create path textboxes
    global path
    path =tkinter.StringVar()
    path_entry = tkinter.ttk.Entry(frame1,textvariable=path,width=30)
    path_entry.insert(0,path_input)
    path_entry.grid(row=0, column=1,columnspan=2)
    
    # create file dialog
    path_button = tkinter.ttk.Button(frame1,text="ファイル選択",command= lambda : [askfileplace1()] )
    path_button.grid(row=0, column=3)
    
    #create path text2
    label2 = tkinter.ttk.Label(frame1, text='CNNモデル(h5ファイル)場所', padding=(5, 2))
    label2.grid(row=1, column=0, sticky=tkinter.E)

    # create path textboxes2
    global model_path
    model_path =tkinter.StringVar()
    model_path_entry = tkinter.ttk.Entry(frame1,textvariable=model_path,width=30)
    model_path_entry.insert(0,model_path_input)
    model_path_entry.grid(row=1, column=1,columnspan=2)
    
    # create file dialog2
    path_button = tkinter.ttk.Button(frame1,text="ファイル選択",command= lambda : [askfileplace2()] )
    path_button.grid(row=1, column=3)
     
    # 撮影開始button
    button1 = tkinter.ttk.Button(
        frame1, text='解析開始',
        width = 20,
        command= lambda : [face_recognize(path.get(),
                                          model_path.get())])                 
    
    button1.grid(row=2, column=1, pady = 50, padx = 10 )
    
    # 終了button
    button2 = tkinter.ttk.Button(frame1, text='終了',width = 20,command= lambda : [_destroyWindow(root)])
    button2.grid(row=2, column=2, pady = 50, padx = 10 )

    root.mainloop()

#################### initial parameters ####################
                                         
path_input=""
model_path_input = os.path.join(os.path.abspath(os.path.dirname(__file__)), "dogfaceemotion_autokeras.h5")
#model_path_input = ""

interface() # call the function to create a interface.

UI

この画像はどうでしょう?激おこですね

freegrによるPixabayからの画像

これはどうでしょうか?ねむーい

Kevin PhillipsによるPixabayからの画像

これはどうでしょう?歯が出てるから、怒っているのと判別難しいでしょうか?ゴールデン、ラブはこのような優しい幸せそうな顔が素敵ですよね。

Agata NygaによるPixabayからの画像

これはどうでしょう。無ですね。

Thorsten SchulzeによるPixabayからの画像

完全に私の判断と一致しています!

リアルタイム性がないのが残念ですが、精度はとてもいいものができました!!

Categories:

category