본 프로젝트의 목표는 카메라 입력 이미지를 기반으로 차량의 조향각(steering angle)을 예측하는 End-to-End 모델을 구현하는 것이다.
주행 데이터셋에서 이미지 프레임을 추출하고, 각 이미지 파일명에 포함된 조향각을 레이블로 사용하였다.
즉, 이미지 → 신경망 → 조향각 흐름을 학습시키는 것이다.
!pip install tensorflow==1.14 keras==2.2.5 h5py==2.10.0
Google Drive에 저장된 주행 데이터를 압축 해제한 후, .png 파일과 조향각(angle)을 불러온다.
import os, fnmatch
import pandas as pd
from PIL import Image
import matplotlib.pyplot as plt
data_dir = '/content/video'
file_list = os.listdir(data_dir)
image_paths, steering_angles = [], []
pattern = "*.png"
for filename in file_list:
if fnmatch.fnmatch(filename, pattern):
image_paths.append(os.path.join(data_dir, filename))
angle = int(filename[-7:-4]) # 파일명에서 조향각 추출
steering_angles.append(angle)
df = pd.DataFrame({"ImagePath": image_paths, "Angle": steering_angles})
print(df.head())
import numpy as np
num_of_bins = 25
hist, bins = np.histogram(df['Angle'], num_of_bins)
plt.hist(df['Angle'], bins=num_of_bins, color='blue')
plt.title("Steering Angle Distribution")
plt.show()

👉 결과: 특정 조향각 구간에 데이터가 몰려 있어 데이터 불균형이 존재함.
train_test_splitimport cv2
from sklearn.model_selection import train_test_split
def my_imread(image_path):
return cv2.imread(image_path)
def img_preprocess(image):
return image / 255.0 # 정규화
X_train, X_valid, y_train, y_valid = train_test_split(
image_paths, steering_angles, test_size=0.2
)
print(len(X_train), len(X_valid))
Nvidia에서 제안한 자율주행 CNN 아키텍처를 기반으로 구현하였다.
Conv → Dropout → Dense → 조향각 출력 구조다.
import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Conv2D, Dropout, Flatten, Dense
from tensorflow.keras.optimizers import Adam
def nvidia_model():
model = Sequential(name='Nvidia_Model')
model.add(Conv2D(24, (5, 5), strides=(2, 2), input_shape=(66,200,3), activation='elu'))
model.add(Conv2D(36, (5, 5), strides=(2, 2), activation='elu'))
model.add(Conv2D(48, (5, 5), strides=(2, 2), activation='elu'))
model.add(Conv2D(64, (3, 3), activation='elu'))
model.add(Dropout(0.2))
model.add(Conv2D(64, (3, 3), activation='elu'))
model.add(Flatten())
model.add(Dropout(0.2))
model.add(Dense(100, activation='elu'))
model.add(Dense(50, activation='elu'))
model.add(Dense(10, activation='elu'))
model.add(Dense(1)) # 조향각 예측
model.compile(loss='mse', optimizer=Adam(lr=1e-3))
return model
model = nvidia_model()
model.summary()

메모리에 모든 이미지를 올리기 어려우므로, 배치 단위로 데이터를 불러오는 제너레이터를 작성하였다.
import numpy as np
import random
def image_data_generator(image_paths, steering_angles, batch_size):
while True:
batch_images, batch_steering = [], []
for i in range(batch_size):
idx = random.randint(0, len(image_paths)-1)
image = img_preprocess(my_imread(image_paths[idx]))
angle = steering_angles[idx]
batch_images.append(image)
batch_steering.append(angle)
yield np.asarray(batch_images), np.asarray(batch_steering)
from tensorflow.keras.callbacks import ModelCheckpoint
checkpoint = ModelCheckpoint("lane_navigation_check.h5", save_best_only=True, verbose=1)
history = model.fit_generator(
image_data_generator(X_train, y_train, batch_size=100),
steps_per_epoch=300,
epochs=10,
validation_data=image_data_generator(X_valid, y_valid, batch_size=100),
validation_steps=200,
callbacks=[checkpoint],
verbose=1
)
model.save("lane_navigation_final.h5")
손실 시각화:
plt.plot(history.history['loss'], color='blue')
plt.plot(history.history['val_loss'], color='red')
plt.legend(["Training Loss", "Validation Loss"])
plt.show()

👉 Epoch이 진행될수록 손실이 안정적으로 감소하는 모습을 확인.
검증 데이터 일부를 예측하여 실제 값과 비교한다.
from sklearn.metrics import mean_squared_error, r2_score
from tensorflow.keras.models import load_model
def summarize_prediction(Y_true, Y_pred):
mse = mean_squared_error(Y_true, Y_pred)
r2 = r2_score(Y_true, Y_pred)
print(f"MSE: {mse:.2f}, R²: {r2:.2%}")
model = load_model("lane_navigation_check.h5")
X_test, y_test = next(image_data_generator(X_valid, y_valid, 100))
y_pred = model.predict(X_test)
summarize_prediction(y_test, y_pred)

출력:
MSE: 1.20
R²: 93.5%
End-to-End 자율주행 모델이 카메라 입력으로부터 조향각을 안정적으로 예측할 수 있음을 확인하였다.
한계:
👉 향후에는 Data Augmentation, ResNet 기반 모델, 시뮬레이터(Carla) 연동 등을 통해 성능을 개선할 수 있다.