22.02.06 교통표지판 이미지 분류 프로젝트 공부

2022. 2. 7. 18:37작업/ComputerVision

프로젝트 목차

1. 데이터 분석 :

 - 이미지 데이터 정보 파악 Meta, Train, Test 데이터

 

2. 데이터 전처리 : 

 - 이미지 데이터 읽어보기

 - label 읽기

 - 데이터 분리하기

 

3. 딥러닝 모델 : 

 - CNN 모델 설정

 - 학습(training) 수행

 - 모델 성능 평가 및 예측

 

데이터 출처

필요한 라이브러리들 import하기

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. 데이터 분석

이미지 데이터 정보 파악하기 Meta

file_list = os.listdir('./data') # ./data 에 어떤 파일들이 존재하는지 확인하기
file_list
['Meta', 'Meta.csv', 'Test', 'Test.csv', 'Train', 'Train.csv']

확인해보니 3개의 폴더와 3개의 csv파일이 있다. 대부분 이미지 데이터가 csv파일로 제공되는 건 해당 이미지의 디렉토리 정보가 저장되어 있다.

 

csv파일을 dataframe으로 읽자

import pandas as pd

df_Meta = pd.read_csv('./data/Meta.csv')
df_Meta

Meta.csv파일은 Meta폴더 내 이미지에 대한 정보를 담고있다. 여기서 이미지를 출력할 수도 있다.

Meta_images = []
Meta_labels = []

plt.figure(figsize=(16,16))
for i in range(len(df_Meta)): #df_Meta는 3가지 가 있다.
    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])
    
 print(Meta_images)

이미지 데이터 정보 파악하기 Train

 이번엔 Train.csv를 확인해보자

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)

이건 sns 데이터셋으로 한번 확인해본거

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()

이건 아까 만든 df_Train을 본 것.. 보니까 30~35의 폭 높이를 갖는 이미지가 제일 많다

이미지 크기를 통일할 때 너무 작은 이미지는 큰 이미지의 정보 손실을 발생시키며, 너무 큰 이미지는 작은 이미지의 정보 부족한 정보량을 부각한다. 따라서 적절한 이미지 크기를 잡는 것도 중요한 파라미터 조정이며, 우리는 33x33으로 해보자

image_height = 33
image_width = 33
image_channel = 3 # 컬러 이미지이기에 3채널

이제 ROI(Region Of Interest) 데이터에 대해(관심있는부분) 알아보자. 이번 프로젝트에서 ROI는 표지판이 있는 부분이다. 우리가 찾은 데이터셋은 csv에 ROI 좌표가 다 표시되어 있었다.

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

PIL Image 클래스여서 위와 같은 crop을 한번 해봤다.

 

이미지 데이터 정보 파악하기 Test

df_Test = pd.read_csv('./data/Test.csv')
df_Test

 

퀴즈1 resize 하기

# crop함수를 활용하여 이미지를 잘라낼 수 있습니다.
# 이 후, resize함수를 활용하여 사이즈 변경을 수행합니다.
img_sample_test = Image.open('./data/' + df_Test['Path'][0])
img_sample_crop = img_sample_test.crop((df_Test['Roi.X1'][0], df_Test['Roi.Y1'][0], df_Test['Roi.X2'][0], df_Test['Roi.Y2'][0]))


img_sample_crop_resized = img_sample_crop.resize((300,300))
img_sample_crop_resized

2. 데이터 전처리

이미지 데이터 읽기

training용 이미지들을 읽어와서 array형태 [] 에 픽셀값들로 저장한다. 자동으로 target_size로 변환해준다.

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)
# 배열로 저장한 Train_images 확인해보기
Train_images # 이미지가 배열로 저장되어있다.
Train_images[0]

첫번째 Train_images[0]

같은 방식으로 test용 이미지들도 array형태로 저장한다. test도 역시 target_size로 맞춰줘야 한다.

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)

 

label 데이터 읽기

training, test 데이터의 각 label은 csv파일 속 'ClassId' 컬럼에 저장되어 있다.

Train_labels = df_Train['ClassId'].values
Train_labels

Test_labels = df_Test['ClassId'].values
Test_labels

데이터 분리하기

 딥러닝 학습 시 과적합을 막기위해 validation 데이터를 training 데이터에서 분리한다.

이들을 numpy array로 저장한다! (split)

즉, Training / Test / Validation 용 데이터로 나눈다.

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)
# test 데이터도 np array에 넣어준다

3. 딥러닝 모델

CNN 모델 설정

CNN을 사용해 간단한 모델을 구현해보자. filters, kernel_size 등의 사이즈는 하이퍼파라미터로 나만의 모델로 튜닝이 가능하다.

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), # 학습할 때 25% 노드를 사용하지 않는다. 과적합 방지
    # 그러나 학습을 완료해서 predict할 때는 모든 노드를 사용함.
    
    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') # 클래스 3가지로 분류.
])

model.summary()

학습을 수행하면서 accuract와 loss변화를 볼 수도 있다.

 

학습수행

3개의 class가 있기 때문에 loss로 sparse_categorical_crossentropy를 설정하고 optimizer로는 adam 사용!

model.compile(
    loss='sparse_categorical_crossentropy',
    optimizer='adam',
    metrics=['accuracy']
)
# 처음 만든 모델이라면 EPOCHS를 1~5개로 하여 잘 돌아가는지 성능을 확인해보고 값을 증가 시켜 봅시다. 
EPOCHS = 30

# EPOCHS에 따른 성능을 보기 위하여 history 사용
# model.fit -> 이 과정이 training 과정.. 오래걸린다.
history = model.fit(x_train, 
                    y_train,
                    validation_data = (x_val, y_val), # validation 데이터 사용
                    epochs=EPOCHS, 
                   )

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()

모델 성능 평가 및 예측

 이제 training을 다 했으니 test용 데이터에서 성능이 잘 나오는지 evaluate(답 맞추기)를 해보자

test_loss, test_accuracy = model.evaluate(x_test, y_test, verbose=0)

print('test set accuracy: ', test_accuracy)

evaluate는 했으니, 이제 predict를 해보게 한다. (답이 없다고 생각)

test_prediction = np.argmax(model.predict(x_test), axis=-1)
# predict결과를 시각화해본다.
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(CM)을 확인해보자. TN, TP, FN, FP 확인하는 느낌

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)

CM : 대각선방향(TP, TF ? 가 많을 수록 정확도가 높은 것)

퀴즈2

# 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)

챌린지

 

 

이번 프로젝트에서 3개의 label에 대해서만 학습을 수행했지만, 원본 데이터에서는 43개의 label로 이루어져 있습니다. label 개수가 많아진 만큼 정확도를 높이기 위해서 전처리 단계에서 crop 이미지를 사용하거나 Data augmentation을 통해서 상대적으로 적은 이미지 데이터의 수를 늘리는 방법 사용할 수 있습니다. 딥러닝 모델에서는 위 커스텀 CNN 모델 이외로 어느 정도 성능이 검증된 VGG, GoogLeNet, ResNet 등을 사용하여 성능을 높여보시길 바랍니다.