MNIST를 CNN으로 학습하고, 평가/그래프/저장을 한 번에 연결해보기
이미지를 CNN이 받을 수 있는 4차원 모양으로 바꾸고, 라벨을 원-핫으로 바꾼 뒤, 값 범위를 맞춰요.
from tensorflow.keras.datasets import mnist
from tensorflow.keras.utils import to_categorical
(train_images, train_labels), (test_images, test_labels) = mnist.load_data()
# MNIST(손글씨 숫자) 데이터를 훈련/테스트로 한 번에 불러와요.
train_images = train_images.reshape((60000, 28, 28, 1)) # CNN 입력에 맞게 (60000,28,28,1)로 모양을 바꿔요(채널 1개=흑백).
train_images = train_images.astype("float32") / 255 # 픽셀 값을 0~255에서 0~1로 정규화해서 학습이 안정적으로 되게 해요.
test_images = test_images.reshape((10000, 28, 28, 1)) # 테스트 이미지도 같은 방식으로 4차원 모양으로 바꿔요.
test_images = test_images.astype("float32") / 255 # 테스트 이미지도 0~1 범위로 정규화해요.
train_labels = to_categorical(train_labels) # 라벨(0~9)을 원-핫 벡터로 바꿔요(softmax 출력 10개와 맞추기).
test_labels = to_categorical(test_labels) # 테스트 라벨도 원-핫으로 바꿔요.
print(train_labels[:2]) # 원-핫 라벨이 어떻게 생겼는지 2개만 찍어 확인해요.
(예시) train_labels[:2] 원-핫 라벨 2개가 출력돼요. [[0. 0. 0. 0. 0. 1. 0. 0. 0. 0.] [1. 0. 0. 0. 0. 0. 0. 0. 0. 0.]]
라벨을 원-핫으로 바꾸는 이유는, 모델이 10개 확률(softmax)을 내기 때문이에요. 입력도 0~1로 맞춰야 학습이 안정적이에요.
Conv/Pooling/Dropout을 순서대로 쌓아서 ‘특징 추출 → 크기 줄이기 → 과적합 줄이기’ 흐름을 만들어요.
#################################
from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Input, Dropout # MNIST(손글씨 숫자) 데이터를 훈련/테스트로 한 번에 불러와요.
model = Sequential() # 픽셀 값을 0~255에서 0~1로 정규화해서 학습이 안정적으로 되게 해요.
model.add(Input(shape=(28, 28, 1))) # 테스트 이미지도 같은 방식으로 4차원 모양으로 바꿔요.
model.add(Conv2D(32, (3, 3), activation="relu")) # 테스트 이미지도 0~1 범위로 정규화해요.
model.add(MaxPooling2D(2, 2)) # 라벨(0~9)을 원-핫 벡터로 바꿔요(softmax 출력 10개와 맞추기).
model.add(Dropout(0.25)) # 테스트 라벨도 원-핫으로 바꿔요.
model.add(Conv2D(64, (3, 3), activation="relu"))
model.add(MaxPooling2D(2, 2))
model.add(Dropout(0.25))
model.add(Conv2D(64, (3, 3), activation="relu"))
model.add(Flatten()) # Sequential 모델(레이어를 순서대로 쌓는 방식)을 시작해요.
model.add(Dense(64, activation="relu")) # 입력 이미지 한 장의 모양(28x28x1)을 모델에 알려줘요.
model.add(Dropout(0.5)) # 첫 합성곱: 3x3 필터 32개로 간단한 특징(선/모서리)을 뽑아요.
model.add(Dense(10, activation="softmax")) # 풀링: 특징맵을 2칸씩 줄여서 계산량을 줄이고 중요한 것만 남겨요.
model.compile(optimizer="adam", loss="categorical_crossentropy", metrics=["accuracy"])
model.summary() # 두 번째 합성곱: 더 많은 필터(64개)로 더 복잡한 특징을 뽑아요.
(예시) model.summary()가 레이어 구조/출력 크기/파라미터 수를 표로 보여줘요.
모델을 쌓을 때는 ‘입력 모양이 맞는지’, ‘레이어 순서가 의도대로인지’를 summary로 확인하는 게 핵심이에요.
fit은 ‘훈련 데이터를 보고 가중치를 업데이트’하는 단계예요.
#################################
hist = model.fit(train_images, train_labels, epochs=10)
(예시) Epoch마다 loss/accuracy 로그가 출력돼요.
fit 출력은 진행 상황(손실이 줄고 정확도가 오르는지)을 확인하기 위한 로그예요.
evaluate는 ‘처음 보는 테스트 데이터’에서 성능을 숫자로 확인하는 단계예요.
#################################
test_loss, test_acc = model.evaluate(test_images, test_labels)
test_loss, test_acc
(예시) test_loss, test_acc 값이 (손실, 정확도)로 나와요.
evaluate는 훈련이 끝난 뒤 ‘테스트에서 성능이 어느 정도인지’를 숫자로 확정하는 단계예요.
loss/accuracy가 학습하면서 어떻게 변하는지 그래프로 확인해요.
#################################
import matplotlib.pyplot as plt
plt.figure(figsize=(12, 8)) # MNIST(손글씨 숫자) 데이터를 훈련/테스트로 한 번에 불러와요.
plt.plot(hist.history['loss']) # CNN 입력에 맞게 (60000,28,28,1)로 모양을 바꿔요(채널 1개=흑백).
plt.plot(hist.history['accuracy']) # 픽셀 값을 0~255에서 0~1로 정규화해서 학습이 안정적으로 되게 해요.
plt.legend(['loss', 'accuracy']) # 테스트 이미지도 같은 방식으로 4차원 모양으로 바꿔요.
plt.grid() # 테스트 이미지도 0~1 범위로 정규화해요.
plt.show() # 라벨(0~9)을 원-핫 벡터로 바꿔요(softmax 출력 10개와 맞추기).
(그래프 창) loss/accuracy 선 그래프가 표시돼요.
그래프는 숫자만 봤을 때 놓치기 쉬운 과적합/학습정체를 한눈에 보게 해줘요.
최종 정확도를 보기 좋은 문장으로 출력하고, 모델을 파일로 저장해요.
#################################
test_loss, test_acc = model.evaluate(test_images, test_labels, verbose=0)
print("Test accuracy:", test_acc)
(예시) Test accuracy: 0.98xx 그리고 mnist.keras 파일이 저장돼요.
마지막 print는 정확도를 문장 형태로 보기 좋게 만들고, save는 다음에 모델을 재사용하려는 목적이에요.
최종 정확도를 보기 좋은 문장으로 출력하고, 모델을 파일로 저장해요.
#################################
model.save('c:/data/mnist/mnist.keras')
(예시) Test accuracy: 0.98xx 그리고 mnist.keras 파일이 저장돼요.
마지막 print는 정확도를 문장 형태로 보기 좋게 만들고, save는 다음에 모델을 재사용하려는 목적이에요.