1. 데이터 가져오기

- 43개의 교통 표지판 과 관련된 4만여 개의 이미지로 만들어진 데이터 셋

- https://sid.erda.dk/public/archives/daaeac0d7ce1152aea9b61d9f1e19370/published-archive.html

 

Public Archive: daaeac0d7ce1152aea9b61d9f1e19370

Support ERDA User Guide Questions about ERDA? Please contact us at support@erda.dk

sid.erda.dk

- https://drive.google.com/drive/folders/1AktNCWqVBv-4xxme4OUC82OLJTHzMFsq?usp=sharing

 

 

- 데이터 다운로드

   colab
	!wget https://sid.erda.dk/public/archives/daaeac0d7ce1152aea9b61d9f1e19370/GTSRB_Final_Training_Images.zip
	!unzip -qq "GTSRB_Final_Training_Images.zip"
  • GTSRB\Final_Training\Images 디렉토리 안에 디렉토리 별로 이미지가 ppm 확장자로 분류되어 있다.

 

- ppm: 무손실 압축 이미지 파일 형식

 

- image 크기: 32 x 32

 

 

- 신경망 구조를 만들 때 선택 사항

  • 2차원 컨볼루션에서 필터 개수 와 커널 크기
  • Max Pool 에서의 커널 크기
  • Fully Connected 계층(Dense)에서의 유닛 개수
  • 계층 별 배치 크기, 최적화 알고리즘, 학습률, 할성화 함수, epoch 수 등

 

- 이미지 전처리

  • 이미지 크기를 32*32로 수정
  • 43개의 레이블을 생성: 디렉토리 이름이 레이블
  • 표지판의 경우 구분해야 할 것은 색상이 아니라 모양: 이미지를 흑백으로 변경
  • 데이터를 다룰 때 목적을 먼저 생각하고 작업을 시작해야 한다. (도메인)

 

- 필요한 패키지 와 상수 선언

  • 클래스 개수와 이미지의 크기를 변수에 저장
  • 이 두개의 절대로 변하지 않을 값
  • 변하지 않는 값을 장할 변수의 이름은 모두 대문자로 작성하는 것이 좋다.
    • SNAKE 표기법이라고 한다.
N_CLASSES = 43
RESIZED_IMAGE = (32, 32)

 

 

# 모듈

import matplotlib.pyplot as plt
#디렉토리를 핸들링 할 때 사용하는 모듈
import glob

from skimage.color import rgb2lab
from skimage.transform import resize
import numpy as np

from collections import namedtuple

from sklearn.model_selection import train_test_split

 

 

- 이미지 전처리를 위한 함수

  • 이름이 있는 튜플 생성 - 다른 언어에서는 튜플을 이런 형태로 만든다.
  • 튜플의 원래 목적은 변경할 수 없는 하나의 행(record, row)을 표현하기 위한 것
  • 인덱스가 아니라 이름으로 구별하는 것이 타당
Dataset = namedtuple('Dataset', ['X', 'y'])

 

 

# 포맷 변경해주는 함수

def to_tf_format(imgs):
    return np.stack([img[:, :, np.newaxis] for img in imgs], axis=0).astype(np.float32)

 

 

- 이미지 디렉토리에 있는 모든 이미지들에 라벨링을 하기 위한 작업
- 이미지의 크기 변경도 여기서 수행

def read_dataset_ppm(rootpath, n_labels, resize_to):
    #이미지 데이터 와 라벨을 저장할 list
    images = []
    labels = []
    
    for c in range(n_labels):
        #루트경로/00001/ 형태로 이미지 경로를 생성
        full_path = rootpath + "/" + format(c, '05d') + "/"
        #각 이미지 디렉토리를 순회하면서 확장자가 ppm 인 파일의 경로를 가지고
        for img_name in glob.glob(full_path + "*.ppm"):
            #이미지 읽어오기
            img = plt.imread(img_name).astype(np.float32)
            #이미지를 정규화
            img = rgb2lab(img / 255.0)[:, :,0]
            #이미지 크기를 조정
            if resize_to:
                img = resize(img, resize_to, mode='reflect')
            #라벨 생성
            #43개 짜리 배열을 만들어서 자신의 인덱스에 해당하는 값에만 1을 대입
            #원핫 인코딩 된 라벨
            label = np.zeros((n_labels, ), dtype=np.float32) 
            label[c] = 1.0
            
            images.append(img.astype(np.float32))
            labels.append(label)

    return Dataset(X = to_tf_format(images).astype(np.float32), y = np.matrix(labels).astype(np.float32))

 

 

# 이미지 전처리를 수행해서 데이터 만들기

dataset = read_dataset_ppm('./data/GTSRB/Final_Training/Images',
                          N_CLASSES,
                          RESIZED_IMAGE)
print(dataset.X.shape)
print(dataset.y.shape)
(39209, 32, 32, 1)
(39209, 43)

 

 

# 이미지 확인

plt.imshow(dataset.X[0, :, :, :].reshape(RESIZED_IMAGE))
print(dataset.y[0, :])
[[1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
  0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]]

 

 

 

- 훈련 데이터와 테스트 데이터 분리

#훈련 데이터 와 테스트 데이터 분리
idx_train, idx_test = train_test_split(range(dataset.X.shape[0]), test_size=0.25,
                                      random_state=42)

X_train = dataset.X[idx_train, :, :, :]
X_test = dataset.X[idx_test, :, :, :]

y_train = dataset.y[idx_train, :]
y_test = dataset.y[idx_test, :]

print(X_train.shape)
print(X_test.shape)
print(y_train.shape)
print(y_test.shape)
(29406, 32, 32, 1)
(9803, 32, 32, 1)
(29406, 43)
(9803, 43)

 

 

- 모델 생성

model = keras.Sequential()

#입력층 - Convolution 
model.add(keras.layers.Conv2D(32, kernel_size=3, activation='relu',
                             padding='same', input_shape=(32, 32, 1)))
model.add(keras.layers.MaxPooling2D(2))

model.add(keras.layers.Conv2D(64, kernel_size=3, activation='relu',
                             padding='same'))
model.add(keras.layers.MaxPooling2D(2))

model.add(keras.layers.Conv2D(128, kernel_size=3, activation='relu',
                             padding='same'))
model.add(keras.layers.MaxPooling2D(2))

model.add(keras.layers.Flatten())

model.add(keras.layers.Dense(100, activation='relu'))
model.add(keras.layers.Dropout(0.4))

model.add(keras.layers.Dense(100, activation='relu'))
model.add(keras.layers.Dropout(0.4))

model.add(keras.layers.Dense(100, activation='relu'))
model.add(keras.layers.Dropout(0.4))

model.add(keras.layers.Dense(43, activation='softmax'))

model.summary()

 

 

- 모델 컴파일

model.compile(optimizer='adam',
             loss='categorical_crossentropy',
             metrics=['accuracy'])

 

 

- 훈련

checkpoint_cb = keras.callbacks.ModelCheckpoint('best-cnn-models.keras')
early_stopping_cb = keras.callbacks.EarlyStopping(patience=2,
                                                 restore_best_weights=True)
history = model.fit(X_train, y_train, epochs=20,
                   callbacks = [checkpoint_cb, early_stopping_cb])

 

 

- 평가

print(model.evaluate(X_test, y_test))
307/307 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.8567 - loss: 0.3981
[0.39652204513549805, 0.853820264339447]

 

!git clone https://github.com/MachineLearning2020/Homework2-Caltech101.git