トヨタコンパクトカー判別アプリの製作

AIを用いてトヨタコンパクトカーの判別アプリを作成しました。
今回は製作したWebアプリについて書いていきます。

製作したWebアプリはこちら
https://car-select.herokuapp.com/

アプリ概要

サイトはこんな感じになっています。

スクリーンショット 2020-11-04 3.31.09

そして、この中に次の画像(カローラスポーツ)をアップしてみます。

画像2

すると、

スクリーンショット 2020-11-04 3.32.17

このように画像とAiが判別した結果が "これは○○です" というふうに表示されます。
それではこのアプリの中身をみていきます。

コード説明

今回は以下のような流れで作りました。
1:画像の水増しとデータセットの作成
2:モデル作成と学習、学習結果の保存
3:メインのコード作成

・画像の水増しについて

import keras
import numpy as np
from keras.utils import np_utils
from matplotlib import pyplot as plt 
from keras.preprocessing import image
from keras.preprocessing.image import ImageDataGenerator
import os
import cv2
import sys
import random


DATA_DIR = '水増ししたい画像のあるディレクトリのパス'
CATEGORIES =['aqua', 'spade', 'calola_sports', 'passo', 'porte', 'rumy', 'tank', 'yaris']

#SAVE_DIR = os.path.join('ディレクトリのパス','新たに作る保存用のディレクトリ名')
IMG_SIZE = 50
training_data = []

#if not os.path.exists(SAVE_DIR):
 #os.makedirs(SAVE_DIR)

datagen = ImageDataGenerator(
 rotation_range=90,
 width_shift_range=0.3,
 height_shift_range=0.3,
 channel_shift_range=40.0,
 shear_range=0.39,
 zoom_range=[0.7, 1.3],
 horizontal_flip=True,
 vertical_flip=True
)

copy_num = 100

def create_training_data():

 for class_num, category in enumerate(CATEGORIES):
   path = os.path.join(DATA_DIR, category)
   
   #save_path = os.path.join(SAVE_DIR, category)

   #if not os.path.exists(save_path):
    # os.makedirs(save_path)

   for image_name in os.listdir(path):
     try:
       img = cv2.imread(os.path.join(path, image_name))
       img_resize = cv2.resize(img, (IMG_SIZE, IMG_SIZE))
       img_array = image.img_to_array(img_resize)
       img_array = np.expand_dims(img_array, axis=0)

       j = 0
       for d in datagen.flow(img_array, batch_size=1,
       save_to_dir=save_path, 
       save_prefix=category, 
       save_format='jpeg'
       ):
         j += 1
         d = np.resize(d,(d.shape[1], d.shape[2], d.shape[3]))
         training_data.append([d, class_num])
         if j == copy_num:
           break
     except Exception as e :
       pass

create_training_data()

random.shuffle(training_data)

X = []
y = []

for feature, label in training_data:
 X.append(feature)
 y.append(label)

X = np.array(X)
y = np.array(y)

np.savez('保存するディレクトリのパス', X, y)

画像を保存して実際に確認するときはコードの#を消します。
ただその場合copy_numを変えないととんでもない画像量になるので注意が必要です。

画像4

例えばこの画像を水増しすると、

画像5

このように加工された画像になります。

水増ししたものを、画像データ・ラベルに分けて保存します。
これで学習用データが出来上がりました。

・モデル作成と学習、学習結果の保存について

from keras import optimizers
from keras.applications.vgg16 import VGG16
from keras.layers import Dense, Dropout, Flatten, Input
from keras.models import Model, Sequential
from keras.utils.np_utils import to_categorical
import numpy as np

npz = np.load('学習用データセットのパス')

npz_X = npz['arr_0']
npz_y = npz['arr_1']

X_train = npz_X[:int(len(npz_X)*0.8)]
y_train = to_categorical(npz_y[:int(len(npz_y)*0.8)])
X_test = npz_X[int(len(npz_X)*0.8):]
y_test = to_categorical(npz_y[int(len(npz_y)*0.8):])

input_tensor = Input(shape=(50,50,3))

vgg16 = VGG16(include_top=False, weights='imagenet', input_tensor=input_tensor)

top_model = Sequential()
top_model.add(Flatten(input_shape=vgg16.output_shape[1:]))
top_model.add(Dense(256, activation='sigmoid'))
top_model.add(Dropout(0.1))
top_model.add(Dense(8, activation='softmax'))

model = Model(inputs=vgg16.input, outputs=top_model(vgg16.output))

for layer in model.layers[:15]:
 layer.trainable = False

#model.load_weights('重みデータのファイル名')

model.compile(loss='categorical_crossentropy', optimizer=optimizers.SGD(lr=1e-4, momentum=0.9), metrics=['accuracy'])

model.fit(X_train, y_train, validation_data=(X_test, y_test), batch_size=32, epochs=3)

model.save('ファイル名', include_optimizer = False)

scores = model.evaluate(X_test, y_test, verbose=1)
print('Test loss:', scores[0])
print('Test accuracy:', scores[1])

転移学習を用いてモデルを作成しました。
そして学習したデータを保存しています。
追加で学習をさせたいときは#を取り除きます。

・メインのコード

データセットを作成し、それを学習させた結果の保存まで終わりました。
ここからメインのコードになります。

まずディレクトリの構成はこのようになっています。

.
├── Procfile
├── car_model1.h5
├── main.py
├── runtime.txt
├── requirements.txt
├── static
│   └── stylesheet.css
└── templates
   └── index.html

main.pyは以下の通りです。

import os
from flask import Flask, request, redirect, url_for, render_template, flash
from werkzeug.utils import secure_filename
from tensorflow.keras.models import Sequential, load_model
from tensorflow.keras.preprocessing import image
import numpy as np
import cProfile
import tensorflow.compat.v1 as tf

CATEGORIES =['aqua', 'spade', 'calola_sports', 'passo', 'porte', 'rumy', 'tank', 'yaris']
image_size = 50

UPLOAD_FOLDER = "static"
ALLOWED_EXTENSIONS = set(['png', 'jpg', 'jpeg', 'gif'])

app = Flask(__name__, )

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

model = load_model('保存した学習結果のパス')

@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(UPLOAD_FOLDER, filename))
     filepath = os.path.join(UPLOAD_FOLDER, filename)

     img = image.load_img(filepath, target_size=(image_size, image_size))
     img = image.img_to_array(img)
     data = np.array([img])

     result = model.predict(data)[0]
     predicted = result.argmax()

     pred_answer = "これは" + CATEGORIES[predicted] + "です"

     return render_template('index.html', answer=pred_answer, images=filepath)
     

 return render_template('index.html', answer='')

if __name__ == '__main__':
 port = int(os.environ.get('PORT', 8080))
 app.run(host = '0.0.0.0', port = port)

続いて、index.html

<!DOCTYPE html>
<html lang="ja">
<head>
   <meta charset="UTF-8">
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
   <meta http-equiv="X-UA-Compatible" content="ie=edge">
   <title>車種判別</title>
   <link rel="stylesheet" href="./static/stylesheet.css">
</head>
<body>
   <header>   
       <a class="header-logo" href="#">車種判別</a>
   </header>

   <div class="main">    
       <h2> 車種の判別を行います。(トヨタコンパクトカー)</h2>
       <p>画像を送信してください</p>
     
       <form method="POST" enctype="multipart/form-data">
           <input class="file_choose" type="file" name="file">
           <input class="btn" value="submit!" type="submit">
       </form>
       
       <img class="image" src="{{images}}">
       <div class="answer">{{answer}}</div>
   </div>

   <footer>
       <small>&copy; たか   </small>   
   </footer>
</body>
</html>

最後にstylesheet.css

header {
 background-color: #53adc9;
 height: 60px;
 margin: -8px;
 display: flex;
 flex-direction: row-reverse;
 justify-content: space-between;
}

.header-logo {
 color: #fff;
 font-size: 25px;
 margin: 15px 25px;
}

.main {
 min-height: 100vh;
 position: relative;
 padding-bottom: 50px;
 box-sizing: border-box;
}

h2 {
 color: #444444;
 margin: 80px 0px 30px;
 text-align: center;
}

p {
 color: #444444;
 margin: 40px 0px 30px 0px;
 text-align: center;
}

.answer {
 color: #444444;
 margin: 5px 0px 30px 0px;
 text-align: center;
}

.image {
 width: 60%;
 display: block;
 margin: 30px auto 0px;
}

form {
 text-align: center;
}

footer {
 background-color: #c0cac4;
 margin: -8px;
 width: 100%;
 position: absolute;
}

small {
 margin: 15px 25px;
 left: 0;
 bottom: 0;
}

Procfile、runtime.txt、requirements.txtをまとめたものがこちら。

<Procfile>

web: python main.py

<runtime.txt>

python-3.7.7

<requirements.txt>

absl-py==0.9.0
bleach==3.1.5
click==7.1.2
certifi==2020.4.5.2
chardet==3.0.4
Flask==1.1.2
future==0.18.0
gast==0.3.3
grpcio==1.30.0
h5py==2.10.0
html5lib==1.0.1
itsdangerous==1.1.0
idna==2.9
Jinja2==2.11.2
Keras==2.4.3
Markdown==3.2.2
MarkupSafe==1.1.1
numpy==1.18.1
oauthlib==3.1.0
pillow==7.1.2
protobuf==3.12.2
PyYAML==5.3.1
requests==2.23.0
scipy==1.4.1
six==1.15.0
tensorboard==2.3.0
tensorflow==2.3.0
termcolor==1.1.0
urllib3==1.25.9
Werkzeug==1.0.1

以上になります。

改善点・コメント

今回AIの判別の精度があまり高くありません。
理由としては、
・学習用の画像が少ないこと。
・herokuにアップロードする際、500Mbまでという制限があるのですが学習データが大きくなるとこの制限に引っかかるため学習データを小さくする必要があったこと。
などが挙げられます。
tesorflowが400Mb弱あるためなかなか学習データに割く容量が残ってませんでした。

なので、
・tensorflowのバージョンを下げて容量を減らし、学習データに割く量を増やす。
・学習用の画像をもっとたくさん集める。
・モデルの試行錯誤
といったことで精度が挙げられると思います。
今後取り組んでいきたいと考えています。


この記事が気に入ったらサポートをしてみませんか?