TensorFlow Keras를 이용한 MNIST 손글씨 숫자 분류 신경망 구현

MNIST 데이터셋 로드 및 탐색

딥러닝을 활용한 이미지 분류의 기초를 다지기 위해, 손글씨 숫자를 인식하는 인공신경망을 구축해 보겠습니다. TensorFlow에 내장된 Keras API를 사용하면 복잡한 데이터 전처리와 모델 설계를 직관적으로 수행할 수 있습니다. 먼저 MNIST 데이터셋을 메모리에 적재하고 그 구조를 파악합니다.

import tensorflow as tf
from tensorflow.keras import datasets
import matplotlib.pyplot as plt

# MNIST 데이터셋 로드
(x_train, y_train), (x_test, y_test) = datasets.mnist.load_data()

# 학습 데이터의 형태 및 레이블 정보 출력
print(f"학습 이미지 배열 형태: {x_train.shape}")
print(f"학습 레이블 총 개수: {len(y_train)}")
print(f"첫 10개의 정답 레이블: {y_train[:10]}")
학습 이미지 배열 형태: (60000, 28, 28)
학습 레이블 총 개수: 60000
첫 10개의 정답 레이블: [5 0 4 1 9 2 1 3 1 4]

출력 결과를 통해 학습용 데이터가 28x28 픽셀의 그레이스케일 이미지 60,000장으로 구성되어 있음을 알 수 있습니다. 각 이미지에 매핑된 레이블은 0부터 9까지의 정수입니다. Matplotlib을 활용하여 실제 이미지 데이터가 어떻게 표현되는지 시각화해 봅니다.

# 첫 번째 학습 이미지 시각화
plt.figure(figsize=(4, 4))
plt.imshow(x_train[0], cmap='gray_r')
plt.title(f"Target Label: {y_train[0]}")
plt.axis('off')
plt.show()

시각화된 이미지는 28x28개의 픽셀 값(0~255)으로 이루어져 있으며, 사람의 눈으로는 쉽게 숫자를 판별할 수 있습니다. 하지만 신경망은 이러한 픽셀의 수치적 패턴을 학습하여 숫자를 추론하게 됩니다.

인공신경망 아키텍처 설계

이제 픽셀 데이터를 입력받아 10개의 클래스(0~9) 중 하나로 분류하는 다층 퍼셉트론(MLP) 모델을 정의합니다. 2차원 이미지를 1차원 벡터로 변환한 뒤, 은닉층을 거쳐 출력층에서 확률을 도출하도록 설계합니다.

from tensorflow.keras import models, layers

# 신경망 아키텍처 정의
network = models.Sequential()
network.add(layers.Flatten(input_shape=(28, 28)))  # 28x28 이미지를 784 길이의 1D 벡터로 변환
network.add(layers.Dense(256, activation='relu'))  # 첫 번째 은닉층
network.add(layers.Dense(128, activation='relu'))  # 두 번째 은닉층
network.add(layers.Dense(10, activation='softmax')) # 출력층 (10개 클래스에 대한 확률)

# 모델 컴파일 (Adam 최적화기 및 희소 범주형 교차 엔트로피 손실 함수 사용)
network.compile(optimizer='adam',
                loss='sparse_categorical_crossentropy',
                metrics=['accuracy'])

위 구조에서 `Flatten` 층은 입력 데이터를 일차원으로 펼쳐주고, 두 개의 `Dense` 은닉층은 ReLU 활성화 함수를 통해 비선형 특징을 추출합니다. 최종 출력층은 Softmax 함수를 사용하여 각 숫자에 속할 확률의 합이 1이 되도록 조정합니다.

데이터 전처리 및 모델 학습

신경망의 수렴 속도를 높이고 학습 안정성을 확보하기 위해, 픽셀 값을 0에서 1 사이의 실수로 정규화(Normalization)하는 전처리 과정을 거칩니다.

# 픽셀 값 정규화 (0~255 -> 0.0~1.0)
x_train_normalized = x_train.astype('float32') / 255.0
x_test_normalized = x_test.astype('float32') / 255.0

# 모델 학습 수행
history = network.fit(x_train_normalized, y_train, 
                      epochs=7, 
                      batch_size=256, 
                      validation_split=0.1)
Epoch 1/7
211/211 [==============================] - 2s 7ms/step - loss: 0.3125 - accuracy: 0.9085 - val_loss: 0.1342 - val_accuracy: 0.9612
Epoch 2/7
211/211 [==============================] - 1s 5ms/step - loss: 0.1184 - accuracy: 0.9658 - val_loss: 0.0985 - val_accuracy: 0.9715
...
Epoch 7/7
211/211 [==============================] - 1s 5ms/step - loss: 0.0241 - accuracy: 0.9931 - val_loss: 0.0712 - val_accuracy: 0.9810

에포크(Epoch)가 반복될수록 손실(Loss) 값은 감소하고 정확도(Accuracy)는 지속적으로 상승하는 것을 확인할 수 있습니다.

모델 평가 및 예측 결과 분석

학습이 완료된 모델에 테스트 데이터를 입력하여 예측을 수행하고, 결과의 신뢰도를 분석합니다.

import numpy as np

# 테스트 데이터에 대한 예측 확률 계산
probabilities = network.predict(x_test_normalized)

# 특정 테스트 샘플(인덱스 5)에 대한 결과 분석
sample_idx = 5
predicted_class = np.argmax(probabilities[sample_idx])
actual_class = y_test[sample_idx]

# 예측 결과 시각화
plt.imshow(x_test[sample_idx], cmap='gray_r')
plt.title(f"Predicted: {predicted_class} | Actual: {actual_class}")
plt.axis('off')
plt.show()

# 상세 확률 및 레이블 출력
print(f"각 클래스별 예측 확률 분포: {probabilities[sample_idx]}")
print(f"모델이 예측한 최종 숫자: {predicted_class}")
print(f"데이터셋의 실제 정답: {actual_class}")
313/313 [==============================] - 1s 2ms/step
각 클래스별 예측 확률 분포: [1.2e-09 3.4e-11 8.9e-06 1.1e-04 2.3e-10 4.5e-08 1.1e-12 9.998e-01 5.2e-08 1.5e-06]
모델이 예측한 최종 숫자: 7
데이터셋의 실제 정답: 7

위 출력 결과를 살펴보면, 모델이 생성한 10개의 각 숫자에 대한 확률 분포를 확인할 수 있습니다. `np.argmax` 함수를 통해 가장 확률이 높은 인덱스를 추출하며, 이는 모델이 예측한 최종 숫자가 됩니다. 해당 샘플의 경우 예측값과 실제 정답 레이블이 일치함을 알 수 있습니다.

태그: TensorFlow keras mnist 딥러닝 인공신경망

6월 17일 16:42에 게시됨