AI/DL

LeNet으로 교통 신호 분류하기

hu6r1s 2025. 2. 17. 12:28

Overview

교통 표지판 분류는 자율 주행 자동차에 중요한 과제이다.
이 프로젝트에서는 LeNet이라는 딥 네트워크를 교통 표지판 이미지 분류에 사용한다.
데이터 세트에는 43개의 서로 다른 이미지 클래스가 포함되어 있다.
클래스는 아래와 같다.

( 0, b'Speed limit (20km/h)') ( 1, b'Speed limit (30km/h)') ( 2, b'Speed limit (50km/h)') ( 3, b'Speed limit (60km/h)') ( 4, b'Speed limit (70km/h)')( 5, b'Speed limit (80km/h)') ( 6, b'End of speed limit (80km/h)') ( 7, b'Speed limit (100km/h)') ( 8, b'Speed limit (120km/h)') ( 9, b'No passing')
(10, b'No passing for vehicles over 3.5 metric tons') (11, b'Right-of-way at the next intersection') (12, b'Priority road') (13, b'Yield') (14, b'Stop')
(15, b'No vehicles') (16, b'Vehicles over 3.5 metric tons prohibited') (17, b'No entry')
(18, b'General caution') (19, b'Dangerous curve to the left')
(20, b'Dangerous curve to the right') (21, b'Double curve')
(22, b'Bumpy road') (23, b'Slippery road')
(24, b'Road narrows on the right') (25, b'Road work')
(26, b'Traffic signals') (27, b'Pedestrians') (28, b'Children crossing')
(29, b'Bicycles crossing') (30, b'Beware of ice/snow')
(31, b'Wild animals crossing')
(32, b'End of all speed and passing limits') (33, b'Turn right ahead')
(34, b'Turn left ahead') (35, b'Ahead only') (36, b'Go straight or right')
(37, b'Go straight or left') (38, b'Keep right') (39, b'Keep left')
(40, b'Roundabout mandatory') (41, b'End of no passing')
(42, b'End of no passing by vehicles over 3.5 metric tons')

LeNet

사용된 네트워크는 Yann LeCun이 발표한 LeNet이다.

STEP 1: THE FIRST CONVOLUTIONAL LAYER #1

Input = 32x32x1
Output = 28x28x6
Output = (Input-filter+1)/Stride* => (32-5+1)/1=28
Used a 5x5 Filter with input depth of 3 and output depth of 6
Apply a RELU Activation function to the output
pooling for input, Input = 28x28x6 and Output = 14x14x6

STEP 2: THE SECOND CONVOLUTIONAL LAYER #2

Input = 14x14x6
Output = 10x10x16
Layer 2: Convolutional layer with Output = 10x10x16
Output = (Input-filter+1)/strides => 10 = 14-5+1/1
Apply a RELU Activation function to the output
Pooling with Input = 10x10x16 and Output = 5x5x16

STEP 3: FLATTENING THE NETWORK

Flatten the network with Input = 5x5x16 and Output = 400

STEP 4: FULLY CONNECTED LAYER

Layer 3: Fully Connected layer with Input = 400 and Output = 120
Apply a RELU Activation function to the output

STEP 5: ANOTHER FULLY CONNECTED LAYER

Layer 4: Fully Connected Layer with Input = 120 and Output = 84
Apply a RELU Activation function to the output

STEP 6: FULLY CONNECTED LAYER

Layer 5: Fully Connected layer with Input = 84 and Output = 43
 

Import Library and Dataset

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import pickle

이미지를 가져오기 위한 picke를 설치해줘야 한다.

with open("./traffic-signs-data/train.p", mode='rb') as training_data:
    train = pickle.load(training_data)
with open("./traffic-signs-data/valid.p", mode='rb') as validation_data:
    valid = pickle.load(validation_data)
with open("./traffic-signs-data/test.p", mode='rb') as testing_data:
    test = pickle.load(testing_data)

훈련, 검증, 테스트 세트를 각각 가져와준다.

X_train, y_train = train['features'], train['labels']
X_validation, y_validation = valid['features'], valid['labels']
X_test, y_test = test['features'], test['labels']

각 데이터를 입력값과 출력값을 분리해준다.

Image Exporation

i = 5000
plt.imshow(X_train[i])
y_train[i]

이미지가 잘 매칭되어 있는지 확인해보기 위해서 imshow를 사용한다.

37번으로 나왔고, 위의 클래스 분류를 확인해보면 (37, b'Go straight or left') 라고 나와있다.

잘 매칭되어 있는 것 같다.

Data Preparation

from sklearn.utils import shuffle
X_train, y_train = shuffle(X_train, y_train)

훈련을 하기 위해서 셔플을 해주는 것이 모델 학습에 도움이 된다. 순서대로 학습을 하는 것보다 무작위로 데이터를 받아 학습하는 것이 더 좋을 것이다.

X_train_gray = np.sum(X_train/3, axis=3, keepdims=True)
X_validation_gray  = np.sum(X_validation/3, axis=3, keepdims=True)
X_test_gray  = np.sum(X_test/3, axis=3, keepdims=True)

현재 이미지 데이터는 컬러인데 grayscale로 변경하는 것이 모델 학습 속도와 학습 효과에도 좋을 것이다.

컬러는 3개의 채널을 가지고 있고 흑백은 1개의 채널을 사용하기 때문에 학습 속도가 향상되고,

흑백은 픽셀의 영향을 덜 받기 때문에 학습 효과에도 좋다.

X_train_gray_norm = (X_train_gray - 128)/128 
X_test_gray_norm = (X_test_gray - 128)/128
X_validation_gray_norm = (X_validation_gray - 128)/128

normalization을 해줘서 학습 효과를 더 좋게 할 수 있다.

i = 5000
plt.imshow(X_train_gray[i].squeeze(), cmap='gray')
plt.figure()
plt.imshow(X_train[i])
plt.figure()
plt.imshow(X_train_gray_norm[i].squeeze(), cmap='gray')

흑백을 만든 데이터과 원본 데이터가 매칭이 잘 되어 있는지 확인해보자.

잘 되어 있는 것 같다.

Model Training

from keras.models import Sequential
from keras.layers import Conv2D, MaxPooling2D, AveragePooling2D, Dense, Flatten, Dropout
from keras.optimizers import Adam
from keras.callbacks import TensorBoard

모델을 설계하기 위해서 라이브러리를 import해주자.

cnn_model = Sequential()

cnn_model.add(Conv2D(filters=6, kernel_size=(5, 5), activation='relu', input_shape=(32, 32, 1)))
cnn_model.add(MaxPooling2D())

cnn_model.add(Conv2D(filters=16, kernel_size=(5, 5), activation='relu'))
cnn_model.add(MaxPooling2D())

cnn_model.add(Flatten())
cnn_model.add(Dense(units=120, activation='relu', ))
cnn_model.add(Dense(units=84, activation='relu'))
cnn_model.add(Dense(units=43, activation='softmax'))

이 모델 설계는 위의 구조에 맞도록 설계한 것이다.

cnn_model.compile(loss='sparse_categorical_crossentropy', optimizer=Adam(learning_rate=0.001), metrics=['accuracy'])

옵티마이저는 adam을 사용하였다.

history = cnn_model.fit(X_train_gray_norm, y_train, batch_size=500, epochs=300, verbose=1, validation_data=(X_validation_gray_norm, y_validation))

epochs와 batch size를 적절한 값으로 입력하여 학습을 시킨다.

train accuracy는 100%가 나오고, valid accuracy는 93%가 나온다.

Model Evaluation

score = cnn_model.evaluate(X_test_gray_norm, y_test)
print('Test Accuracy: {}'.format(score[1]))

학습한 모델을 평가해보면 91%가 나온다.

from sklearn.metrics import confusion_matrix
cm = confusion_matrix(y_true, predicted_classes)
plt.figure(figsize=(25, 25))
sns.heatmap(cm, annot=True)

실제 값과 예측 값을 confusion matrix로 평가해보자.

꽤 정확하게 예측이 된 것을 확인할 수 있다.

L = 7
W = 7

fig, axes = plt.subplots(L, W, figsize=(12, 12))
axes = axes.ravel()

for i in np.arange(L*W):
    axes[i].imshow(X_test[i])
    axes[i].set_title('Prediction = {}\n True = {}'.format(predicted_classes[i], y_true[i]))
    axes[i].axis('off')

plt.subplots_adjust(wspace=1)

실제 데이터와 모델이 예측한 값이 어떠한지 이미지를 확인해보고 비교해보자.

맨 마지막의 이미지는 18번 클래스인데 모델은 30번으로 예측했다.

이 이미지는 사람이 확인해도 해상도가 떨어져 확인하기 쉽지 않다..