4
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

簡単画像判別AIをつくる[綾鷹AI]

Last updated at Posted at 2021-02-20

##前書き
春休みに入り、特にやることがないので前から気になっていた記事を元に、リアルタイムに表示するAIアプリを作ろうと考えていた。
苦しめられながらも完成させるまでの道筋を備忘録として残す。

ちなみに現在AppleSiliconM1 ではKerasが対応していない為、MacBookPro2014で作業した
モデル構築の際、140枚×10種類=1400枚の学習は5分程度で終了した。

参考
画像認識で「綾鷹を選ばせる」AIを作る
機械学習入門者が一日でシェルティー判定AIを作成してみた

#環境を作る

##仮想環境の構築

2年前の俺へ
$ cd
$ mkdir python_project  # python_projectファイルを作る
$ cd python_project
$ python3 -m venv env1  # python_projectファイルの中にenv1(仮想環境設定ファイル)を作る
$ source /'Path'/env1/bin/activate  # 仮想環境を有効化にする

(env) (base)  ← こんなのがついてたら成功

ターミナルを開いてターミナルにファイルをドラック&ペーストしてみたら
Path(ファイルがどこに保存されているかの道筋)が簡単にわかる

$ python3 -m venv env1  
$ source /'Path'/env1/bin/activate  

##ライブラリのインストール

KerasはTensorflowに含まれたのでインストールは必要ない

$ pip install tensorflow
$ pip install numpy
$ pip install matplotlib
$ pip install pillow

以上で環境設定は完了
それ以外にもVScodeを使ったが説明がめんどくさいので割愛

構成

└─AyatakaAI
   ├─data
   │ ├─images
   │ │ ├─original              : 元画像
   │ │ │ ├─Ayataka             : 綾鷹の写真データ集
   │ │ │ ├─16Tea               : 十六茶の写真データ集
   │ │ │ └─ほか8種類,多いので割愛
   │ │ ├─extended              : 加工して水増しした画像
   │ │ │ ├─Ayataka             : 水増しした綾鷹の写真データ集
   │ │ │ ├─16Tea               : 水増しした十六茶の写真データ集
   │ │ │ └─ほか8種類,多いので割愛
   │ │ └─testData              : AI の動作確認用の画像
   │ │   ├─test1.jpg           : 新しく撮った綾鷹の写真データ
   │ │   └─test2.jpg           : お茶を2本並べてみた写真データ
   │ ├─train_test_data         : extended の画像を使って作成した教師/テストデータ
   │ │ └─data.npy              
   │ └─model                   : 作成したモデル
   │   ├─Training_and_validation_accuracy.png
   │   ├─Training_and_validation_loss.png
   │   ├─model_predict.hdf5
   │   └─model_predict.json
   ├─img_duplicator            : 画像水増しデータ作成
   │ └─main.py
   ├─train_test_data_generator : 教師/テストデータ作成
   │ └─main.py
   ├─model_generator           : モデル生成
   │ └─main.py
   └─ai                        : 画像判定
     └─main.py
事前に用意するフォルダは
└─AyatakaAI
   ├─data
   │ ├─images
   │ │ ├─original              : 元画像
   │ │ │ ├─Ayataka             : 綾鷹の写真データ集
   │ │ │ ├─16Tea               : 十六茶の写真データ集
   │ │ │ └─ほか8種類,多いので割愛
   │ │ └─test                  : AI の動作確認用の画像
   │ │   ├─test1.jpg           : 新しく撮った綾鷹の写真データ
   │ │   └─test2.jpg           : お茶を2本並べてみた写真データ
   │ ├─train_test_data         : extended の画像を使って作成した教師/テストデータ          
   │ └─model                   : 作成したモデル
   ├─img_duplicator            : 画像水増しデータ作成
   │ └─main.py
   ├─train_test_data_generator : 教師/テストデータ作成
   │ └─main.py
   ├─model_generator           : モデル生成
   │ └─main.py
   └─ai                        : 画像判定
     └─main.py

お茶の写真はそれぞれ40枚スクエアで撮影した。

#写真を水増しする
40枚では学習するには少ない為、Keras の ImageDataGenerator で画像の解像度を変えたり、回転させたりと水増しを行う

img_duplicator/main.py
# -- coding: utf-8 --
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers

import os
import glob
import numpy as np

CATEGORIES = [
    u'16Tea',
    u'AYATAKA!!',
    u'BarleyTea_Tsurube',
    u'Halows_barlyTea',
    u'Halows_Ocha',
    u'Halows_OolongTea',
    u'Halows_RoastedGreenTea',
    u'OiOcha',
    u'RichGreenTea',
    u'RichRoastedGreenTea']

# 画像サイズ
IMG_SIZE = 150

# ImageDataGeneratorを定義
DATA_GENERATOR = keras.preprocessing.image.ImageDataGenerator(horizontal_flip=0.3, zoom_range=0.1)

#画像をまとめているファイルまでのパス
IMG_ROOT_DIR = '/../AyatakaAI_py/data/images'

for idx, category in enumerate(CATEGORIES):  #idxにはインデックス番号、categoryには'16Tea'などが入る
    # コピー元
    img_dir = os.path.join(IMG_ROOT_DIR, 'original', category)
    # コピー先
    out_dir = os.path.join(IMG_ROOT_DIR, 'extended', category)
    os.makedirs(out_dir, exist_ok=True)

    files = glob.glob(os.path.join(img_dir, '*.JPG'))
    for i, file in enumerate(files):
        img = keras.preprocessing.image.load_img(file)
        img = img.resize((IMG_SIZE, IMG_SIZE))
        x = keras.preprocessing.image.img_to_array(img)
        x = np.expand_dims(x, axis=0)
        g = DATA_GENERATOR.flow(x, batch_size=1, save_to_dir=out_dir, save_prefix='img', save_format='jpg')
        for i in range(10):
            batch = g.next()
    print(u'{} : ファイル数は {} 件です。'.format(category, len(os.listdir(out_dir))))

$ cd /../img_dumplicator
$ python main.py

#教師データを作成
大量の画像を用意したので教師データを作成。
全データの 2 割ほどをテストデータにまわす。
作成した教師/テストデータは保存。

train_test_data_generator/main.py
# -*- coding: utf-8 -*-

from PIL import Image
import os, glob
import numpy as np
import random, math
from sklearn.model_selection import train_test_split
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers

# 画像が保存されているルートディレクトリのパス
IMG_ROOT_DIR = '/../AyatakaAI_py/data/images/extended'
# カテゴリ
CATEGORIES = [
    u'16Tea',
    u'AYATAKA!!',
    u'BarleyTea_Tsurube',
    u'Halows_barlyTea',
    u'Halows_Ocha',
    u'Halows_OolongTea',
    u'Halows_RoastedGreenTea',
    u'OiOcha',
    u'RichGreenTea',
    u'RichRoastedGreenTea']

# 密度
DENSE_SIZE = len(CATEGORIES)
# 画像サイズ
IMG_SIZE = 150
# 画像データ
X = []
# カテゴリデータ
Y = []
# 教師データ
X_TRAIN = []
Y_TRAIN = []
# テストデータ
X_TEST = []
Y_TEST = []
# データ保存先
TRAIN_TEST_DATA = '/../AyatakaAI_py/data/train_test_data/data.npy'


# カテゴリごとに処理する
for idx, category in enumerate(CATEGORIES):
    # 各ラベルの画像ディレクトリ
    image_dir = os.path.join(IMG_ROOT_DIR, category)
    files = glob.glob(os.path.join(image_dir, '*.jpg'))
    for f in files:
        # 各画像をリサイズしてデータに変換する
        img = Image.open(f)
        img = img.convert('RGB')
        img = img.resize((IMG_SIZE, IMG_SIZE))
        data = np.asarray(img)
        X.append(data)
        Y.append(idx)

X = np.array(X)
Y = np.array(Y)

# 正規化
X = X.astype('float32') /255
Y = keras.utils.to_categorical(Y, DENSE_SIZE)

# 教師データとテストデータを分ける
X_TRAIN, X_TEST, Y_TRAIN, Y_TEST = train_test_split(X, Y, test_size=0.20)

# 教師/テストデータを保存する
np.save(TRAIN_TEST_DATA, (X_TRAIN, X_TEST, Y_TRAIN, Y_TEST))
print(u'教師/テストデータの作成が完了しました。: {}'.format(TRAIN_TEST_DATA))

$ cd /../train_test_data_generator
$ python main.py

#モデル構築
教師/テストデータを用意できた。次はモデルを構築。
構築したモデルは保存。

model_generator/main.py
# -*- coding: utf-8 -*-

#モデルの構築
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers

import numpy as np
import matplotlib.pyplot as plt
import os

# カテゴリ
CATEGORIES = [
    u'16Tea',
    u'AYATAKA!!',
    u'BarleyTea_Tsurube',
    u'Halows_barlyTea',
    u'Halows_Ocha',
    u'Halows_OolongTea',
    u'Halows_RoastedGreenTea',
    u'OiOcha',
    u'RichGreenTea',
    u'RichRoastedGreenTea']

# 密度
DENSE_SIZE = len(CATEGORIES)
# 画像サイズ
IMG_SIZE = 150
INPUT_SHAPE = (IMG_SIZE, IMG_SIZE,3)
# 教師データ
X_TRAIN = []
Y_TRAIN = []
# テストデータ
X_TEST = []
Y_TEST = []
# データ保存先
TRAIN_TEST_DATA = '/../AyatakaAI_py/data/train_test_data/data.npy'
# モデル保存先
MODEL_ROOT_DIR = '/../AyatakaAI_py/data/model/'


# ----- モデル構築 ----- #
model = keras.models.Sequential()
model.add(layers.Conv2D(32,(3,3),activation="relu",input_shape=INPUT_SHAPE))
model.add(layers.MaxPooling2D((2,2)))
model.add(layers.Conv2D(64,(3,3),activation="relu"))
model.add(layers.MaxPooling2D((2,2)))
model.add(layers.Conv2D(128,(3,3),activation="relu"))
model.add(layers.MaxPooling2D((2,2)))
model.add(layers.Conv2D(128,(3,3),activation="relu"))
model.add(layers.MaxPooling2D((2,2)))
model.add(layers.Flatten())
model.add(layers.Dropout(0.5))
model.add(layers.Dense(512,activation="relu"))
model.add(layers.Dense(DENSE_SIZE,activation="sigmoid"))

#モデル構成の確認
model.summary()
# ----- /モデル構築 ----- #

# ----- モデルコンパイル ----- #
model.compile(loss="binary_crossentropy",
              optimizer=keras.optimizers.RMSprop(lr=1e-4),
              metrics=["acc"])
# ----- /モデル構築 ----- #

# ----- モデル学習 ----- #
# 教師データとテストデータを読み込む
X_TRAIN, X_TEST, Y_TRAIN, Y_TEST = np.load(TRAIN_TEST_DATA, allow_pickle=True)
model = model.fit(X_TRAIN,
                  Y_TRAIN,
                  epochs=10,
                  batch_size=6,
                  validation_data=(X_TEST, Y_TEST))
# ----- /モデル学習 ----- #

# ----- 学習結果プロット ----- #
acc = model.history['acc']
val_acc = model.history['val_acc']
loss = model.history['loss']
val_loss = model.history['val_loss']

epochs = range(len(acc))

plt.plot(epochs, acc, 'bo', label='Training acc')
plt.plot(epochs, val_acc, 'b', label='Validation acc')
plt.title('Training and validation accuracy')
plt.legend()
plt.savefig(os.path.join(MODEL_ROOT_DIR, 'Training_and_validation_accuracy.png'))

plt.figure()

plt.plot(epochs, loss, 'bo', label='Training loss')
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('Training and validation loss')
plt.legend()
plt.savefig(os.path.join(MODEL_ROOT_DIR, 'Training_and_validation_loss.png'))
# ----- /学習結果プロット ----- #

# ----- モデル保存 ----- #
# モデル保存
json_string = model.model.to_json()
open(os.path.join(MODEL_ROOT_DIR, 'model_predict.json'), 'w').write(json_string)

#重み保存
model.model.save_weights(os.path.join(MODEL_ROOT_DIR, 'model_predict.hdf5'))
# ----- /モデル保存 ----- #

$ cd /../model_generator
$ python main.py

#写真を判定

ai/main.py
# -*- coding: utf-8 -*-

import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers

import numpy as np
import sys
import os

# モデル保存先
MODEL_ROOT_DIR = '/../AyatakaAI_py/data/model/'
MODEL_PATH = os.path.join(MODEL_ROOT_DIR, 'model_predict.json')
WEIGHT_PATH = os.path.join(MODEL_ROOT_DIR, 'model_predict.hdf5')
# カテゴリ
CATEGORIES = [
    u'16Tea',
    u'AYATAKA!!',
    u'BarleyTea_Tsurube',
    u'Halows_barlyTea',
    u'Halows_Ocha',
    u'Halows_OolongTea',
    u'Halows_RoastedGreenTea',
    u'OiOcha',
    u'RichGreenTea',
    u'RichRoastedGreenTea']

CATEGORIES_NAME = [
    u'十六茶',
    u'綾鷹',
    u'鶴瓶麦茶',
    u'ハローズ_麦茶',
    u'ハローズ_お茶',
    u'ハローズ_烏龍茶',
    u'ハローズ_ほうじ茶',
    u'おーいお茶',
    u'生茶',
    u'生ほうじ茶']

# 画像サイズ
IMG_SIZE = 150
INPUT_SHAPE = (IMG_SIZE, IMG_SIZE,3)

# モデルを読み込む
model = keras.models.model_from_json(open(MODEL_PATH).read())
model.load_weights(WEIGHT_PATH)

# 入力引数から画像を読み込む
args = sys.argv
img = keras.preprocessing.image.load_img(args[1], target_size=INPUT_SHAPE)
x = keras.preprocessing.image.img_to_array(img)
x = np.expand_dims(x, axis=0)

# モデルで予測する
features = model.predict(x)
print("確率:")
for i in range(0, 10):
    print(str(CATEGORIES_NAME[i]) + '' + str(features[0][i]))
print("----------------------------------------------")
print("計算結果")
if np.argmax(features[0]) == 1:
    print(u'選ばれたのは綾鷹でした。')
else:
    print(u'綾鷹ではなく' + str(CATEGORIES_NAME[np.argmax(features[0])]) + 'が選ばれました。')
print("----------------------------------------------")

$ cd /../ai
$ python main.py '../AyatakaAI/data/images/testData/test1.jpg'
4
8
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
4
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?