Edited at

画像認識で「レタスとキャベツと人参を判定する」AIをつっくてみた


はじめに

Udemyの[画像判定AI自作にチャレンジ!]をやってみたのでアウトプット用として記事をかきました。


環境

・windows

・anaconda

・python3.6


画像収集

pythonライブラリicrawlerを使い簡単に画像データを集めるを参考にして画像を集めました。

集めてた画像を質の良いものだけにするために手作業で選択しました。


実装概要

1.画像を学習/検証データにする

2.モデルを構築/学習する

3.flaskを使ってwebアプリ化


1.画像を学習/検証データにする

データについてラベリングを実施して学習用と評価用に分割します。


gen_data.py

from PIL import Image

import os, glob
import numpy as np
import sklearn
from sklearn import model_selection

classes = ["cabbage","lettuce","gensing"]
num_classes = len(classes)
image_size = 80

# 画像の読み込み
#画像データをnumpyの配列に変換
X = []
Y = []
for index, classlabel in enumerate(classes):
photos_dir = "./" + classlabel
files = glob.glob(photos_dir + "/*.jpg")
for i, file in enumerate(files):
if i >= 93: break
image = Image.open(file)
image = image.convert("RGB")
image = image.resize((image_size, image_size))
data = np.asarray(image)
X.append(data)
Y.append(index)
#listからnumpyに変換
X = np.array(X)
Y = np.array(Y)
#データを学習用と評価用に分割する
#学習用と評価用を3:1の比率に分割
X_train, X_test, y_train, y_test = model_selection.train_test_split(X, Y)
xy = (X_train, X_test, y_train, y_test)
np.save("./vegetable.npy", xy)



2.モデルを構築/学習する

トレーニングを実行するためのメインの関数を定義します。


vegetable_cnn.py

from keras.models import Sequential

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

classes = ["cabbage","lettuce","gensing"]
num_classes = len(classes)
image_size = 100

# メインの関数を定義する

def main():
X_train, X_test, y_train, y_test = np.load("./vegetable.npy")#ファイルからデータを配列に読み込む
X_train = X_train.astype("float") / 256#データを正規化
X_test = X_test.astype("float") / 256
y_train = np_utils.to_categorical(y_train, num_classes)
y_test = np_utils.to_categorical(y_test, num_classes)

#トレーニング関数と評価関数の呼び出し
model = model_train(X_train, y_train)
model_eval(model, X_test, y_test)


次にトレーニング関数を作成します。

シンプルにモデルを構築。


vegetable_cnn.py

#モデルの構築

#kerasのドキュメントを参考
def model_train(X, y):
model = Sequential()
model.add(Conv2D(32,(3,3), padding='same',input_shape=X.shape[1:]))
model.add(Activation('relu'))
model.add(Conv2D(32,(3,3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(Dropout(0.25))

model.add(Conv2D(64,(2,2), padding='same'))
model.add(Activation('relu'))
model.add(Conv2D(64,(3,3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(3,3)))
model.add(Dropout(0.25))

model.add(Flatten())
model.add(Dense(512))
model.add(Activation('relu'))
model.add(Dropout(0.5))
model.add(Dense(3))#画像の種類数によって変える
model.add(Activation('softmax'))
#最適化の処理
opt = keras.optimizers.rmsprop(lr=0.0001, decay=1e-6)
#正解と推定値の誤差が小さくなるようにする
model.compile(loss='categorical_crossentropy',optimizer=opt,metrics=['accuracy'])
model.fit(X, y, batch_size=20, epochs=75)

# モデルの保存
model.save('./vegetable_cnn.h5')

return model


評価関数の作成です。


vegetable_cnn.py

def model_eval(model, X, y):

scores = model.evaluate(X, y, verbose=1)
print('Test Loss: ', scores[0])
print('Test Accuracy: ', scores[1])#精度の表示

if __name__ == "__main__":
main()


3.flaskを使ってwebアプリ化

flaskのドキュメントを参考しています。


flask.py


import os
from flask import Flask, request, redirect, url_for
from werkzeug.utils import secure_filename

from keras.models import Sequential,load_model
import keras,sys
import numpy as np
from PIL import Image
classes = ["cabbage","lettuce","gensing"]
num_classes = len(classes)
image_size = 100

UPLOAD_FOLDER = './uploads'
ALLOWED_EXTENSIONS = set(['png', 'jpg', 'gif'])

app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER

def allowed_file(filename):
return '.' in filename and filename.rsplit('.',1)[1].lower() in ALLOWED_EXTENSIONS

@app.route('/', methods=['GET', 'POST'])
def upload_file():
if request.method == 'POST':
if 'file' not in request.files:
flash('ファイルがありません')
return redirect(request.url)
file = request.files['file']
if file.filename == '':
flash('ファイルがありません')
return redirect(request.url)
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
filepath=os.path.join(app.config['UPLOAD_FOLDER'], filename)

model=load_model('./vegetable_cnn.h5')
image=Image.open(filepath)
image=image.convert("RGB")
image=image.resize((image_size,image_size))
data=np.asarray(image)
X=[]
X.append(data)
X=np.array(X)
result=model.predict([X])[0]
predicted=result.argmax()
percentage=int(result[predicted]*100)
return classes[predicted]+str(percentage)+"%"

return '''
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>ファイルをアップロードして判定しよう</title></head>
<body>
<h1>ファイルをアップロードして判定しよう!</h1>
<form method = post enctype = multipart/form-data>
<p><input type=file name=file>
<input type=submit value=Upload>
</form>
</body>
</html>
'''

from flask import send_from_directory

@app.route('/uploads/<filename>')
def uploaded_file(filename):
return send_from_directory(app.config['UPLOAD_FOLDER'], filename)


学習結果を載せときます。

204/204 [==============================] - 12s 57ms/step - loss: 1.1278 - acc: 0.4412

Epoch 2/50
204/204 [==============================] - 11s 51ms/step - loss: 0.9492 - acc: 0.5343
Epoch 3/50
204/204 [==============================] - 11s 52ms/step - loss: 0.8064 - acc: 0.6078
中略
Epoch 48/50
204/204 [==============================] - 14s 67ms/step - loss: 0.2428 - acc: 0.8922
Epoch 49/50
204/204 [==============================] - 15s 75ms/step - loss: 0.2876 - acc: 0.8676
Epoch 50/50
204/204 [==============================] - 17s 81ms/step - loss: 0.2715 - acc: 0.8676
69/69 [==============================] - 2s 28ms/step
Test Loss: 0.4421425206937652
Test Accuracy: 0.7971014501391978

学習用データでは86%で評価用では79%になりました。


おわりに

今回は評価用では79%でしたが元の画像を増やすことや画像を回転・反転させ水増しさせるこによるなどの精度を上げるための策はまだあります。

記事にミスや改善点等がありましたらご指摘いただけると幸いです。