Lego Image를 분류하는 model 만들어보기
AI Development/Keras

Lego Image를 분류하는 model 만들어보기

학교 졸업작품으로 컴퓨터비전 기반 Lego sorting machine을 만들게 되었다.

컨베이어 벨트위에 있는 Lego를 Detect하는 부분은 OpenCV로 개발하였고

OpenCV가 전달해준 Lego Image를 어떤 블럭인지 Classification하는 모델을 만들어보았다.

모델 개요

88x88 그레이스케일 입력을 받아서 Prediction 결과로 총 11개의 Lego block중 1개를 출력한다.
loss 함수 : categorical_crossentropy
optimizer : Adam

사용된 학습관련 스킬

- 배치 정규화 
- 가중치 초기화(he_normal) 
- 1x1 convolution 
- Dropout Layer 

모델 소스코드

train
In [1]:
import numpy as np
import matplotlib.pyplot as plt
import tensorflow as tf
%matplotlib inline

from tensorflow.keras.layers import Input, Activation, Dense, Flatten, RepeatVector, Reshape
from tensorflow.keras.layers import Conv2D, MaxPooling2D, Dropout, BatchNormalization
from tensorflow.keras.models import Model
from tensorflow.keras import Sequential
from tensorflow.keras import optimizers
from tensorflow.keras.preprocessing.image import ImageDataGenerator
import cv2
from tensorflow.keras.callbacks import EarlyStopping, TensorBoard
import time
from datetime import datetime
In [2]:
train_datagen = ImageDataGenerator(rescale=1./255)

train_generator = train_datagen.flow_from_directory(
        'dataset/train',
        target_size=(88, 88),
        batch_size=70,
        color_mode="grayscale",
        class_mode='categorical')

test_datagen = ImageDataGenerator(rescale=1./255)

test_generator = test_datagen.flow_from_directory(
        'dataset/valid',
        target_size=(88, 88),    
        batch_size=80,
        color_mode="grayscale",
        class_mode='categorical')
Found 94500 images belonging to 11 classes.
Found 18800 images belonging to 11 classes.
In [3]:
model = Sequential()
model.add(Conv2D(32, kernel_size=(3, 3), input_shape=(88,88,1), kernel_initializer='he_normal'))
model.add(Conv2D(filters = 32, kernel_size = (1,1), strides = (1,1), padding = 'valid', kernel_initializer='he_normal'))
model.add(BatchNormalization())
model.add(Activation('relu'))

model.add(Conv2D(32, kernel_size=(3, 3), input_shape=(88,88,1), kernel_initializer='he_normal'))
model.add(Conv2D(filters = 32, kernel_size = (1,1), strides = (1,1), padding = 'valid', kernel_initializer='he_normal'))
model.add(BatchNormalization())
model.add(Activation('relu'))

model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.7))

model.add(Conv2D(64, kernel_size=(3, 3), input_shape=(88,88,1), kernel_initializer='he_normal'))
model.add(Conv2D(filters = 64, kernel_size = (1,1), strides = (1,1), padding = 'valid', kernel_initializer='he_normal'))
model.add(BatchNormalization())
model.add(Activation('relu'))

model.add(Conv2D(64, kernel_size=(3, 3), input_shape=(88,88,1), kernel_initializer='he_normal'))
model.add(Conv2D(filters = 64, kernel_size = (1,1), strides = (1,1), padding = 'valid', kernel_initializer='he_normal'))
model.add(BatchNormalization())
model.add(Activation('relu'))

model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.5))

model.add(Flatten())

model.add(Dense(256, kernel_initializer='he_normal'))
model.add(BatchNormalization())
model.add(Activation('relu'))
model.add(Dropout(0.7))

model.add(Dense(11, activation='softmax', kernel_initializer='he_normal'))

model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
model.summary()
Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d (Conv2D)              (None, 86, 86, 32)        320       
_________________________________________________________________
conv2d_1 (Conv2D)            (None, 86, 86, 32)        1056      
_________________________________________________________________
batch_normalization (BatchNo (None, 86, 86, 32)        128       
_________________________________________________________________
activation (Activation)      (None, 86, 86, 32)        0         
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 84, 84, 32)        9248      
_________________________________________________________________
conv2d_3 (Conv2D)            (None, 84, 84, 32)        1056      
_________________________________________________________________
batch_normalization_1 (Batch (None, 84, 84, 32)        128       
_________________________________________________________________
activation_1 (Activation)    (None, 84, 84, 32)        0         
_________________________________________________________________
max_pooling2d (MaxPooling2D) (None, 42, 42, 32)        0         
_________________________________________________________________
dropout (Dropout)            (None, 42, 42, 32)        0         
_________________________________________________________________
conv2d_4 (Conv2D)            (None, 40, 40, 64)        18496     
_________________________________________________________________
conv2d_5 (Conv2D)            (None, 40, 40, 64)        4160      
_________________________________________________________________
batch_normalization_2 (Batch (None, 40, 40, 64)        256       
_________________________________________________________________
activation_2 (Activation)    (None, 40, 40, 64)        0         
_________________________________________________________________
conv2d_6 (Conv2D)            (None, 38, 38, 64)        36928     
_________________________________________________________________
conv2d_7 (Conv2D)            (None, 38, 38, 64)        4160      
_________________________________________________________________
batch_normalization_3 (Batch (None, 38, 38, 64)        256       
_________________________________________________________________
activation_3 (Activation)    (None, 38, 38, 64)        0         
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 19, 19, 64)        0         
_________________________________________________________________
dropout_1 (Dropout)          (None, 19, 19, 64)        0         
_________________________________________________________________
flatten (Flatten)            (None, 23104)             0         
_________________________________________________________________
dense (Dense)                (None, 256)               5914880   
_________________________________________________________________
batch_normalization_4 (Batch (None, 256)               1024      
_________________________________________________________________
activation_4 (Activation)    (None, 256)               0         
_________________________________________________________________
dropout_2 (Dropout)          (None, 256)               0         
_________________________________________________________________
dense_1 (Dense)              (None, 11)                2827      
=================================================================
Total params: 5,994,923
Trainable params: 5,994,027
Non-trainable params: 896
_________________________________________________________________
In [4]:
early_stopping = EarlyStopping(monitor='val_loss',mode='min', patience=10)
logdir="logs/" + datetime.now().strftime("%Y%m%d-%H%M%S")
tensorboard = tf.keras.callbacks.TensorBoard(log_dir=logdir)
In [5]:
model.fit_generator(
        train_generator,
        steps_per_epoch=1350,
        epochs=20,
        validation_data=test_generator,
        validation_steps=235,
        callbacks=[early_stopping, tensorboard])
WARNING:tensorflow:From <ipython-input-5-824ee9f0ae77>:7: Model.fit_generator (from tensorflow.python.keras.engine.training) is deprecated and will be removed in a future version.
Instructions for updating:
Please use Model.fit, which supports generators.
Epoch 1/20
   1/1350 [..............................] - ETA: 0s - accuracy: 0.0143 - loss: 4.1189WARNING:tensorflow:From /home/hagler/anaconda3/envs/capstone/lib/python3.5/site-packages/tensorflow/python/ops/summary_ops_v2.py:1277: stop (from tensorflow.python.eager.profiler) is deprecated and will be removed after 2020-07-01.
Instructions for updating:
use `tf.profiler.experimental.stop` instead.
   2/1350 [..............................] - ETA: 3:11 - accuracy: 0.0429 - loss: 3.8035WARNING:tensorflow:Callbacks method `on_train_batch_end` is slow compared to the batch time (batch time: 0.0546s vs `on_train_batch_end` time: 0.2285s). Check your callbacks.
1350/1350 [==============================] - 1440s 1s/step - accuracy: 0.7947 - loss: 0.5679 - val_loss: 1.5803 - val_accuracy: 0.6002
Epoch 2/20
1350/1350 [==============================] - 171s 126ms/step - accuracy: 0.9241 - loss: 0.2253 - val_loss: 1.3993 - val_accuracy: 0.6046
Epoch 3/20
1350/1350 [==============================] - 171s 127ms/step - accuracy: 0.9522 - loss: 0.1418 - val_loss: 0.3394 - val_accuracy: 0.8807
Epoch 4/20
1350/1350 [==============================] - 171s 127ms/step - accuracy: 0.9670 - loss: 0.0995 - val_loss: 0.2046 - val_accuracy: 0.9423
Epoch 5/20
1350/1350 [==============================] - 172s 127ms/step - accuracy: 0.9745 - loss: 0.0771 - val_loss: 0.8809 - val_accuracy: 0.8279
Epoch 6/20
1350/1350 [==============================] - 173s 128ms/step - accuracy: 0.9779 - loss: 0.0659 - val_loss: 0.3385 - val_accuracy: 0.9039
Epoch 7/20
1350/1350 [==============================] - 176s 130ms/step - accuracy: 0.9816 - loss: 0.0562 - val_loss: 0.1645 - val_accuracy: 0.9487
Epoch 8/20
1350/1350 [==============================] - 177s 131ms/step - accuracy: 0.9834 - loss: 0.0499 - val_loss: 0.3784 - val_accuracy: 0.9064
Epoch 9/20
1350/1350 [==============================] - 173s 128ms/step - accuracy: 0.9852 - loss: 0.0442 - val_loss: 0.1575 - val_accuracy: 0.9562
Epoch 10/20
1350/1350 [==============================] - 173s 128ms/step - accuracy: 0.9888 - loss: 0.0344 - val_loss: 0.6045 - val_accuracy: 0.8895
Epoch 11/20
1350/1350 [==============================] - 173s 128ms/step - accuracy: 0.9894 - loss: 0.0340 - val_loss: 0.0927 - val_accuracy: 0.9821
Epoch 12/20
1350/1350 [==============================] - 173s 128ms/step - accuracy: 0.9896 - loss: 0.0322 - val_loss: 0.1994 - val_accuracy: 0.9605
Epoch 13/20
1350/1350 [==============================] - 172s 127ms/step - accuracy: 0.9908 - loss: 0.0287 - val_loss: 0.0903 - val_accuracy: 0.9802
Epoch 14/20
1350/1350 [==============================] - 174s 129ms/step - accuracy: 0.9911 - loss: 0.0271 - val_loss: 0.2905 - val_accuracy: 0.9459
Epoch 15/20
1350/1350 [==============================] - 173s 128ms/step - accuracy: 0.9916 - loss: 0.0258 - val_loss: 0.6012 - val_accuracy: 0.8846
Epoch 16/20
1350/1350 [==============================] - 172s 128ms/step - accuracy: 0.9921 - loss: 0.0244 - val_loss: 0.1897 - val_accuracy: 0.9424
Epoch 17/20
1350/1350 [==============================] - 173s 128ms/step - accuracy: 0.9924 - loss: 0.0236 - val_loss: 0.0950 - val_accuracy: 0.9777
Epoch 18/20
1350/1350 [==============================] - 172s 128ms/step - accuracy: 0.9928 - loss: 0.0208 - val_loss: 0.5748 - val_accuracy: 0.9145
Epoch 19/20
1350/1350 [==============================] - 172s 128ms/step - accuracy: 0.9930 - loss: 0.0217 - val_loss: 0.2085 - val_accuracy: 0.9539
Epoch 20/20
1350/1350 [==============================] - 173s 128ms/step - accuracy: 0.9932 - loss: 0.0199 - val_loss: 0.0690 - val_accuracy: 0.9852
Out[5]:
<tensorflow.python.keras.callbacks.History at 0x7f96f7426d30>
In [11]:
print("-- Evaluate --")
scores = model.evaluate_generator(test_generator, steps=5)
print("%s: %.2f%%" %(model.metrics_names[1], scores[1]*100))
-- Evaluate --
accuracy: 98.50%
In [12]:
print("-- Predict --")
output = model.predict_generator(test_generator, steps=5)
np.set_printoptions(formatter={'float': lambda x: "{0:0.3f}".format(x)})
print(test_generator.class_indices)
print(output)
-- Predict --
WARNING:tensorflow:From <ipython-input-12-ff8f58f7659a>:2: Model.predict_generator (from tensorflow.python.keras.engine.training) is deprecated and will be removed in a future version.
Instructions for updating:
Please use Model.predict, which supports generators.
{'2': 3, '5': 6, '9': 10, '11': 2, '6': 7, '10': 1, '8': 9, '4': 5, '3': 4, '7': 8, '1': 0}
[[0.000 0.000 0.000 ... 0.000 0.000 0.000]
 [0.000 0.000 0.000 ... 0.000 0.000 0.000]
 [0.000 0.000 0.000 ... 0.000 1.000 0.000]
 ...
 [0.000 0.000 0.000 ... 0.000 0.000 0.000]
 [0.000 0.000 0.000 ... 0.000 0.000 0.000]
 [0.000 0.000 1.000 ... 0.000 0.000 0.000]]
In [13]:
model.save("lego_sorter_v7.h5")

 

모델 Train 결과

accuracy
Loss