##前書き
春休みに入り、特にやることがないので前から気になっていた記事を元に、リアルタイムに表示する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 で画像の解像度を変えたり、回転させたりと水増しを行う
# -- 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 割ほどをテストデータにまわす。
作成した教師/テストデータは保存。
# -*- 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
#モデル構築
教師/テストデータを用意できた。次はモデルを構築。
構築したモデルは保存。
# -*- 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
#写真を判定
# -*- 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'