[ํ๋ก์ ํธ] ๊ตํต ํ์งํ ์ด๋ฏธ์ง ๋ถ๋ฅ
ํ๋ก์ ํธ ๋ชฉํ
- ๊ตํต ํ์งํ ์ด๋ฏธ์ง ๋ฐ์ดํฐ๋ฅผ ๋ถ์ํ๊ณ ๋ฅ๋ฌ๋ฉ ๋ชจ๋ธ์ ํตํ์ฌ ํ์งํ ์ข ๋ฅ๋ฅผ ์์ธกํ๋ ๋ถ๋ฅ ๋ชจ๋ธ ์ํ
- ๋๋์ ์ด๋ฏธ์ง ๋ฐ์ดํฐ๋ฅผ ์ ์ฒ๋ฆฌํ๋ ๊ณผ์ ๊ณผ ์ด์ ๋ฐ๋ฅธ CNN ๋ชจ๋ธ์ ์ฑ๋ฅ ๋ณํ๋ฅผ ํ์ต
ํ๋ก์ ํธ ๋ชฉ์ฐจ
- ๋ฐ์ดํฐ ๋ถ์: ์ด๋ฏธ์ง ๋ฐ์ดํฐ๋ฅผ ์ด๋ฃจ๊ณ ์๋ ์์์ ๋ํด์ Dataframe๋ฅผ ์ฌ์ฉํ์ฌ ๋ถ์ ๋ฐ ํ์ธ
1-1. ์ด๋ฏธ์ง ๋ฐ์ดํฐ ์ ๋ณด ํ์ ํ๊ธฐ - Meta
1-2. ์ด๋ฏธ์ง ๋ฐ์ดํฐ ์ ๋ณด ํ์ ํ๊ธฐ - Train
1-3. ์ด๋ฏธ์ง ๋ฐ์ดํฐ ์ ๋ณด ํ์ ํ๊ธฐ - Test
- ๋ฐ์ดํฐ ์ ์ฒ๋ฆฌ: ์ด๋ฏธ์ง ๋ฐ์ดํฐ๋ฅผ ์ฝ์ด์ค๊ณ ๋ฅ๋ฌ๋ ๋ชจ๋ธ์ ์
๋ ฅ์ผ๋ก ์ ์ฒ๋ฆฌ
2-1. ์ด๋ฏธ์ง ๋ฐ์ดํฐ ์ฝ๊ธฐ
2-2. label ๋ฐ์ดํฐ ์ฝ๊ธฐ
2-3. ๋ฐ์ดํฐ ๋ถ๋ฆฌํ๊ธฐ
- ๋ฅ๋ฌ๋ ๋ชจ๋ธ: CNN ๋ชจ๋ธ์ ๊ตฌํํ๊ณ ํ์ต, ํ๊ฐ ๋ฐ ์์ธก์ ์ํ
3-1. CNN ๋ชจ๋ธ ์ค์
3-2. ํ์ต ์ํ
3-3. ๋ชจ๋ธ ์ฑ๋ฅ ํ๊ฐ ๋ฐ ์์ธก
๋ฐ์ดํฐ ์ถ์ฒ
https://www.kaggle.com/meowmeowmeowmeowmeow/gtsrb-german-traffic-sign
https://2021nipa.elice.io/explore
ํ๋ก์ ํธ ๊ฐ์
์ฐจ๋ ์ด์ ์ ํ๋ฉด์ ๋๋ก ๊ตํต ํ์งํ์ ๋ณด๊ณ ๊ท์น์ ์งํค๋ ๊ฒ์ ์ด์ ์์ ๋ฐ ๊ตํต ์์ ์ ์ํด์ ์ค์ํ ์ผ์ ๋๋ค. ๋ง์ผ ์ฌ๋์ด ์๋ ๊ธฐ๊ณ๊ฐ ์ด๋ฅผ ์ํํด์ผ ํ๋ค๋ฉด, ์ด๋ป๊ฒ ํ์งํ์ ๊ตฌ๋ถํ ์ ์์๊น์? ์ด๋ฌํ ๋ฌผ์์ ์์จ ์ฃผํ์ฐจ ๊ธฐ์ ์ด ๋ฐ์ ํ๋ฉด์ ์ค์ํ ์ด์๊ฐ ๋์๊ณ , ๋ฅ๋ฌ๋ ๊ธฐ์ ๋ฐํ์ ๋ถ๋ฅ ๋ชจ๋ธ์ด ์๋นํ ์์ค์ ์ ํ๋๋ฅผ ๋ณด์ด๋ฉฐ ์ ์ฉ๋๊ณ ์์ต๋๋ค.
์ด๋ฒ ํ๋ก์ ํธ์์๋ ๊ตํต ํ์งํ ๋ถ๋ฅ์ ์ฒซ ๋ฒ์งธ ์คํ ์ผ๋ก ๊ฐ๋จํ๊ฒ ๊ตํต ํ์งํ ์ด๋ฏธ์ง๊ฐ ์ ๋ ฅ ๋์์ ๋ ์ด ๊ฒ์ด 43 ์ข ์ ํ์งํ ์ค ์ด๋ค ๊ฒ์ธ๊ฐ๋ฅผ ๋ถ๋ฅํ๋ ๋ฅ๋ฌ๋ ๋ชจ๋ธ์ ๊ตฌํํฉ๋๋ค. ์ด๋ฅผ ํตํ์ฌ ๊ตํต ํ์งํ ์ด๋ฏธ์ง ๋ฐ์ดํฐ๋ค์ ํน์ง๊ณผ CNN ๋ชจ๋ธ์ ํตํ์ฌ ๋ถ๋ฅ๋ฅผ ์ํํ๋ ๊ฒ์ ํ์ตํ ์ ์์ต๋๋ค.
์ฒซ ๋ฒ์งธ ์คํ ์ ํ์ฅํ์ฌ, ์ถ ํ์๋ ๊ตํต ํ์งํ๋ง ์๋ ์ด๋ฏธ์ง๋ฅผ ์ ๋ ฅ์ผ๋ก ํ์ง ์๊ณ ๋๋ก ์ด๋ฏธ์ง์์ ๊ตํต ํ์งํ์ ๋ถ๋ฆฌํ์ฌ ๋ถ๋ฅํ๋ Object detection ๋ชจ๋ธ์ ์ฌ์ฉํ ๊ฒ์ด๋ฉฐ, ์ต์ข ์ ์ผ๋ก๋ ์ผ์๋ก ์ ๋ ฅ๋ฐ๋ ๋น๋์ค ์ด๋ฏธ์ง์์์ ๊ตํต ํ์งํ์ ๋ถ๋ฆฌํ๊ณ ๋ถ๋ฅํ๋ ๋ชจ๋ธ์ ์ํํ ์ ์์ต๋๋ค.
import os
import pathlib
import numpy as np
import matplotlib.pyplot as plt
from tqdm import tqdm
from sklearn.model_selection import train_test_split
from tensorflow.keras.preprocessing import image
from tensorflow.keras.preprocessing.image import ImageDataGenerator, img_to_array, array_to_img, load_img
from tensorflow.keras.utils import to_categorical
from tensorflow.keras.layers import Conv2D, MaxPool2D, Dense, Flatten, Dropout
from tensorflow.keras.models import Sequential
%matplotlib inline
1. ๋ฐ์ดํฐ ๋ถ์
1-1. ์ด๋ฏธ์ง ๋ฐ์ดํฐ ์ ๋ณด ํ์ ํ๊ธฐ - Meta
file_list = os.listdir('./data')
file_list
Meta_images = []
Meta_labels = []
plt.figure(figsize=(16,16))
for i in range(len(df_Meta)):
img = load_img('./data/'+df_Meta['Path'][i])
plt.subplot(1, 3, i+1)
plt.imshow(img)
Meta_images.append(img)
Meta_labels.append(df_Meta['ClassId'][i])
1-2. ์ด๋ฏธ์ง ๋ฐ์ดํฐ ์ ๋ณด ํ์ ํ๊ธฐ - Train
df_Train = pd.read_csv('./data/Train.csv')
df_Train
Width์ Height ์ ๋ณด๋ ์ด๋ฏธ์ง์ ํญ๊ณผ ๋์ด์ ๋ํ ์ ๋ณด๋ก ๊ฐ๋จํ ์ํ๋ง ๋ด๋ ๋ค์ํ ํฌ๊ธฐ๋ฅผ ๊ฐ๋ ๊ฒ์ ์ ์ ์์ต๋๋ค. ์ด๋ฏธ์ง ํฌ๊ธฐ๊ฐ ๋ชจ๋ ๋ค๋ฅด๋ค๋ฉด ์ด๋ฏธ์ง๋ง๋ค ์๋ก ๋ค๋ฅธ feature์ ๊ฐ์๊ฐ ์๋ ๊ฒ์ด๊ธฐ์ ์ด๋ฅผ ํต์ผํด์ฃผ๋ ์ ์ฒ๋ฆฌ๊ฐ ํ์ํฉ๋๋ค. ๊ทธ๋ ๋ค๋ฉด ์ด๋ค ํฌ๊ธฐ๋ก ํต์ผ์ ํด์ผ ํ ๊น์? ์ด๋ฏธ์ง ํฌ๊ธฐ์ ๋ถํฌ๋ฅผ ๋ณด๊ณ ํ๋จํด๋ด ์๋ค.
import seaborn as sns
plt.figure(figsize=(20,10))
ax = sns.countplot(x="Width", data=df_Train)
df_cutWidth = pd.cut(df_Train['Width'], np.arange(0,200,10)).value_counts(sort=False)
fig, ax = plt.subplots(figsize=(20,10))
ax.bar(range(len(df_cutWidth)),df_cutWidth.values)
ax.set_xticks(range(len(df_cutWidth)))
ax.set_xticklabels(df_cutWidth.index)
fig.show()
* 30~35์ ํญ ๋๋ ๋์ด๋ฅผ ๊ฐ๋ ์ด๋ฏธ์ง๊ฐ ์ ์ผ ๋ง์์ ํ์ธํ์ต๋๋ค. ์ด๋ฏธ์ง ํฌ๊ธฐ๋ฅผ ํต์ผํ๋ ๋ฐ ์์ด์ ๋๋ฌด ์์ ์ด๋ฏธ์ง๋ ํฐ ์ด๋ฏธ์ง์ ์ ๋ณด ์์ค์ ๋ฐ์ํ๋ฉฐ, ๋๋ฌด ํฐ ์ด๋ฏธ์ง๋ ์์ ์ด๋ฏธ์ง์ ์ ๋ณด ๋ถ์กฑํ ์ ๋ณด๋์ ๋ถ๊ฐํ ๊ฒ์ ๋๋ค. ๋ฐ๋ผ์ ์ ์ ํ ์ด๋ฏธ์ง ํฌ๊ธฐ๋ฅผ ์ก๋ ๊ฒ์ ํ๋์ ํ๋ผ๋ฏธํฐ ์กฐ์ ์ด ๋๋ฉฐ, ์ด๋ฒ ํ๋ก์ ํธ์์๋ ์ด๋ฏธ์ง ๋ถํฌ ๊ธฐ๋ฐ์ผ๋ก ๋๋ค์๋ฅผ ์ฐจ์งํ๋ ํฌ๊ธฐ์ธ 33x33 ํฌ๊ธฐ๋ก ํต์ผํ๊ฒ ์ต๋๋ค. (์ดํ, ๊ฐ์ธ์ ์ธ ์ค์ต์์ ๋ฐ์ดํฐ ํฌ๊ธฐ ์กฐ์ ์ ๋ฐ๋ฅธ ์ฑ๋ฅ ๋ณํ๋ฅผ ์ดํด๋ณด๋ ๊ฒ๋ ์ข์ ํ์ต์ด ๋ ๊ฒ์ ๋๋ค.)
image_height = 33
image_width = 33
image_channel = 3 # ์ปฌ๋ฌ ์ด๋ฏธ์ง์ด๊ธฐ์ 3์ฑ๋
from PIL import Image
from PIL import ImageDraw
img_sample = Image.open('./data/'+df_Train['Path'][0])
draw = ImageDraw.Draw(img_sample)
draw.rectangle([df_Train['Roi.X1'][0], df_Train['Roi.Y1'][0], df_Train['Roi.X2'][0], df_Train['Roi.Y2'][0]], outline="red")
img_sample_resized = img_sample.resize((300,300))
img_sample_resized
* Roi ๋ฐ์ดํฐ๋ฅผ ์ฌ์ฉํ๋ฉด ๋ณด๋ค ๋ช ํํ๊ฒ ํ์งํ ๋ถ๋ถ๋ง์ crop ํ ์ ์์ผ๋ฉฐ, ์ด๋ฌํ ๋ฐ์ดํฐ ์ ์ฒ๋ฆฌ๋ฅผ ํตํ์ฌ ๋ถ๋ฅ์ ์ฑ๋ฅ์ ๋์ผ ์ ์์ต๋๋ค. (์ด๋ฒ ํ๋ก์ ํธ์์๋ ํด๋น ์ ์ฒ๋ฆฌ๋ฅผ ์๋ตํฉ๋๋ค.)
img_sample_crop = img_sample.crop((df_Train['Roi.X1'][0], df_Train['Roi.Y1'][0], df_Train['Roi.X2'][0], df_Train['Roi.Y2'][0]))
# Shows the image in image viewer
img_sample_crop_resized = img_sample_crop.resize((300,300))
img_sample_crop_resized
1-3. ์ด๋ฏธ์ง ๋ฐ์ดํฐ ์ ๋ณด ํ์ ํ๊ธฐ - Test
df_Test = pd.read_csv('./data/Test.csv')
df_Test
Train.csv์ ๊ฐ์ ํํ๋ก ๊ตฌ์ฑ๋์ด ์๋ ๊ฒ์ ์ ์ ์์ต๋๋ค. test ๋ฐ์ดํฐ์ ๋ํด์๋ ๋ถ์ํ๋ ๊ฒ์ ํ์งํ์ ๋ถ๋ฅ ํ๋ ๋ชฉ์ ์ ์์ด ํฐ ์๋ฏธ๊ฐ ์์ง ์๊ธฐ์ ์ด๋ฒ ์ค์ต์์๋ ์๋ตํฉ๋๋ค.
ํด์ฆ1. Roi ์์ญ์ ํฌ๊ธฐ์ ๋ง๊ฒ df_Test['Path'][0] ํด๋น๋๋ ์ด๋ฏธ์ง์ crop์ ์ํํ ํ (300, 300) ํด์๋๋ก resize ํฉ๋๋ค.
# cropํจ์๋ฅผ ํ์ฉํ์ฌ ์ด๋ฏธ์ง๋ฅผ ์๋ผ๋ผ ์ ์์ต๋๋ค.
# ์ด ํ, resizeํจ์๋ฅผ ํ์ฉํ์ฌ ์ฌ์ด์ฆ ๋ณ๊ฒฝ์ ์ํํฉ๋๋ค.
img_sample_test = Image.open('./data/'+df_Train['Path'][0])
img_sample_crop_test = img_sample_test.crop((df_Train['Roi.X1'][0], df_Train['Roi.Y1'][0], df_Train['Roi.X2'][0], df_Train['Roi.Y2'][0]))
# Shows the image in image viewer
img_sample_crop_resized = img_sample_crop.resize((300,300))
img_sample_crop_resized
2. ๋ฐ์ดํฐ ์ ์ฒ๋ฆฌ
2-1. ์ด๋ฏธ์ง ๋ฐ์ดํฐ ์ฝ๊ธฐ
image_height = 33
image_width = 33
image_channel = 3
Train_images = []
Train_labels = []
for i in tqdm(range(len(df_Train))):
img = load_img('./data/'+df_Train['Path'][i], target_size = (image_height, image_width))
img = img_to_array(img)
Train_images.append(img)
Test_images = []
Test_labels = []
for i in tqdm(range(len(df_Test))):
img = load_img('./data/'+df_Test['Path'][i], target_size = (image_height, image_width))
img = img_to_array(img)
Test_images.append(img)
2-2. label ๋ฐ์ดํฐ ์ฝ๊ธฐ
Train_labels = df_Train['ClassId'].values
Train_labels #array([0, 0, 0, ..., 2, 2, 2])
Test_labels = df_Test['ClassId'].values
Test_labels
'''
array([1, 1, 2, 2, 1, 1, 2, 1, 1, 1, 1, 2, 1, 2, 2, 1, 2, 1, 2, 1, 2, 2,
1, 0, 0, 1, 2, 1, 1, 1, 1, 1, 2, 1, 1, 2, 2, 2, 2, 1, 1, 2, 1, 0,
2, 2, 2, 1, 2, 1, 2, 1, 2, 2, 1, 2, 1, 1, 1, 1, 1, 1, 2, 0, 1, 2,
'''
2-3. ๋ฐ์ดํฐ ๋ถ๋ฆฌํ๊ธฐ
x_train, x_val, y_train, y_val = train_test_split(np.array(Train_images), np.array(Train_labels), test_size=0.4)
x_test = np.array(Test_images)
y_test = np.array(Test_labels)
3. ๋ฅ๋ฌ๋ ๋ชจ๋ธ
3-1. CNN ๋ชจ๋ธ ์ค์
CNN์ ์ฌ์ฉํ์ฌ ๊ฐ๋จํ๊ฒ ๋ชจ๋ธ์ ๊ตฌํํด ๋ณด๊ฒ ์ต๋๋ค. filters, kernel ๋ฑ์ ์ฌ์ด์ฆ๋ ํ์ดํผ ํ๋ฆฌ๋ฏธํฐ๋ก ์์ ๋ง์ ๋ชจ๋ธ๋ก ํ๋์ด ๊ฐ๋ฅํฉ๋๋ค.
model = Sequential([
Conv2D(filters=32, kernel_size=(3,3), activation='relu', input_shape=(image_height, image_width, image_channel)),
MaxPool2D(pool_size=(2, 2)),
Dropout(rate=0.25),
Conv2D(filters=64, kernel_size=(3,3), activation='relu'),
MaxPool2D(pool_size=(2, 2)),
Dropout(rate=0.25),
Flatten(),
Dense(512, activation='relu'),
Dropout(rate=0.25),
Dense(3, activation='softmax')
])
model.summary()
3-2. ํ์ต ์ํ
model.compile(
loss='sparse_categorical_crossentropy',
optimizer='adam',
metrics=['accuracy']
)
# ์ฒ์ ๋ง๋ ๋ชจ๋ธ์ด๋ผ๋ฉด EPOCHS๋ฅผ 1~5๊ฐ๋ก ํ์ฌ ์ ๋์๊ฐ๋์ง ์ฑ๋ฅ์ ํ์ธํด๋ณด๊ณ ๊ฐ์ ์ฆ๊ฐ ์์ผ ๋ด
์๋ค.
EPOCHS = 30
# EPOCHS์ ๋ฐ๋ฅธ ์ฑ๋ฅ์ ๋ณด๊ธฐ ์ํ์ฌ history ์ฌ์ฉ
history = model.fit(x_train,
y_train,
validation_data = (x_val, y_val), # validation ๋ฐ์ดํฐ ์ฌ์ฉ
epochs=EPOCHS,
)
'''
Epoch 30/30
51/51 [==============================] - 1s 28ms/step - loss: 0.4736 - accuracy: 0.7948 - val_loss: 0.3615 - val_accuracy: 0.8511
'''
ํ์ต์ ์ํํ๋ฉด์ Accuracy์ Loss์ ๋ณํ๋ฅผ ๊ทธ๋ํ๋ก ์ถ๋ ฅํ๋ฉด ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
accuracy = history.history['accuracy']
val_accuracy = history.history['val_accuracy']
loss=history.history['loss']
val_loss=history.history['val_loss']
epochs_range = range(EPOCHS)
plt.figure(figsize=(16, 8))
plt.subplot(1, 2, 1)
plt.plot(epochs_range, accuracy, label='Training Accuracy')
plt.plot(epochs_range, val_accuracy, label='Validation Accuracy')
plt.legend(loc='lower right')
plt.title('Training and Validation Accuracy')
plt.subplot(1, 2, 2)
plt.plot(epochs_range, loss, label='Training Loss')
plt.plot(epochs_range, val_loss, label='Validation Loss')
plt.legend(loc='upper right')
plt.title('Training and Validation Loss')
plt.show()
3-3. ๋ชจ๋ธ ์ฑ๋ฅ ํ๊ฐ ๋ฐ ์์ธก
test_loss, test_accuracy = model.evaluate(x_test, y_test, verbose=0)
print('test set accuracy: ', test_accuracy)
#test set accuracy: 0.7814285755157471
ํ ์คํธ ๋ฐ์ดํฐ๋ฅผ ์ ๋ ฅํ์ฌ ์์ธก๋ ๊ฒฐ๊ณผ๋ฅผ ๋น๊ตํด ๋ณด๊ฒ ์ต๋๋ค. 25๊ฐ์ ํ ์คํธ ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฌ์ ์ค์ class์ ์์ธก class๋ฅผ ์ถ๋ ฅํ๋ฉด ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
test_prediction = np.argmax(model.predict(x_test), axis=-1)
plt.figure(figsize = (13, 13))
start_index = 0
for i in range(25):
plt.subplot(5, 5, i + 1)
plt.grid(False)
plt.xticks([])
plt.yticks([])
prediction = test_prediction[start_index + i]
actual = y_test[start_index + i]
col = 'g'
if prediction != actual:
col = 'r'
plt.xlabel('Actual={} || Pred={}'.format(actual, prediction), color = col)
plt.imshow(array_to_img(x_test[start_index + i]))
plt.show()
confusion matrix๋ฅผ ์๊ฐํ ํ์ฌ ๋ถ๋ฅ ํ์ต ๊ฒฐ๊ณผ๋ฅผ ํ์ธํด๋ด ๋๋ค.
import seaborn as sns
from sklearn.metrics import confusion_matrix
cm = confusion_matrix(y_test, test_prediction)
plt.figure(figsize = (20, 20))
sns.heatmap(cm, annot = True)
ํด์ฆ2. ์ ํ์ตํ CNN ๋ชจ๋ธ์์ ํ์ต์ฉ ๋ฐ์ดํฐ(x_train, y_train)์ confusion matrix๋ฅผ ๊ตฌํ์ธ์.
# CNN ๋ชจ๋ธ์ x_train์ ๋ํ ์์ธก๊ฐ์ ๊ตฌํ๊ณ confusion_matrix() ๋ฅผ ์ฌ์ฉํ๋ฉด confusion matrix๋ฅผ ๊ตฌํ ์ ์์ต๋๋ค.
train_prediction = np.argmax(model.predict(x_train), axis=-1)
cm_train = confusion_matrix(y_train, train_prediction)
# confusion_matrix() ๊ฒฐ๊ณผ๊ฐ์ ์ ์ฅํฉ๋๋ค.
quiz_2 = cm_train
์ฑ๋ฆฐ์ง
์ด๋ฒ ํ๋ก์ ํธ์์ 3๊ฐ์ label์ ๋ํด์๋ง ํ์ต์ ์ํํ์ง๋ง, ์๋ณธ ๋ฐ์ดํฐ์์๋ 43๊ฐ์ label๋ก ์ด๋ฃจ์ด์ ธ ์์ต๋๋ค. label ๊ฐ์๊ฐ ๋ง์์ง ๋งํผ ์ ํ๋๋ฅผ ๋์ด๊ธฐ ์ํด์ ์ ์ฒ๋ฆฌ ๋จ๊ณ์์ crop ์ด๋ฏธ์ง๋ฅผ ์ฌ์ฉํ๊ฑฐ๋ Data augmentation์ ํตํด์ ์๋์ ์ผ๋ก ์ ์ ์ด๋ฏธ์ง ๋ฐ์ดํฐ์ ์๋ฅผ ๋๋ฆฌ๋ ๋ฐฉ๋ฒ ์ฌ์ฉํ ์ ์์ต๋๋ค. ๋ฅ๋ฌ๋ ๋ชจ๋ธ์์๋ ์ ์ปค์คํ CNN ๋ชจ๋ธ ์ด์ธ๋ก ์ด๋ ์ ๋ ์ฑ๋ฅ์ด ๊ฒ์ฆ๋ VGG, GoogLeNet, ResNet ๋ฑ์ ์ฌ์ฉํ์ฌ ์ฑ๋ฅ์ ๋์ฌ๋ณด์๊ธธ ๋ฐ๋๋๋ค.