Softmax 함수와 MNIST 이미지 분류
이번 포스팅에서는 Softmax 함수를 이용한 다중분류(Multi-Class Classification)의 원리와, 실제 MNIST 손글씨 데이터셋을 사용한 이미지 분류 실습을 함께 다룹니다.
Ⅰ. Softmax 함수 개요
Softmax 함수는 여러 클래스 중 하나를 예측해야 하는 다중분류 문제에서 사용되는 활성함수입니다. 입력값을 0~1 사이의 확률로 변환하며, 각 클래스의 확률 합이 항상 1이 되도록 정규화합니다.
Softmax 공식
Softmax(zᵢ) = e^(zᵢ) / Σ e^(zⱼ)
- zᵢ : i번째 클래스의 로짓(logit, 선형결합 결과)
- Σ e^(zⱼ) : 모든 클래스의 지수 합
- 결과 : 각 클래스에 대한 확률 (0 ≤ p ≤ 1, Σp=1)
본 글은 4개의 실습 파일을 통해 Softmax 기반 다중분류의 기본 개념부터 MNIST(손글씨)·Fashion-MNIST(의류) 데이터셋 분류까지의 흐름을 정리합니다. 각 파일의 역할, 전처리(평탄화/정규화), 모델 설계(은닉층/활성화/손실), 학습·평가·시각화를 간단 주석과 함께 살펴봅니다.
- Ex01_softmax.py: 난수 데이터로 Softmax 다중분류 최소 예제
- Ex02_mnist_이미지분류.py: MNIST 구조 확인 및 샘플 시각화
- Ex03_mnist.py: MLP로 MNIST 분류(평탄화+정규화+원-핫)
- Ex04_mnist.py: Fashion-MNIST 분류(128×2 은닉층) & 시각화
softmax + 손실함수 categorical_crossentropy 조합(원-핫 라벨 기준)이 정석입니다.Ⅱ. Ex01 – Softmax 다중분류 기본 예제
핵심 포인트
- 라벨을
to_categorical로 원-핫 인코딩 →categorical_crossentropy사용 - 출력층
Dense(3, activation='softmax')→ 클래스 확률 합=1 validation_split=0.3으로 검증셋 자동 분리
import numpy as np
from keras import Sequential
from keras.src.layers import Dense, Input
from tensorflow.python.keras.utils.np_utils import to_categorical
# 1️⃣ 입력 데이터 (랜덤 생성)
x_data = np.random.random((10, 4)) # 10개 샘플, 4개의 피처
y_labels = np.random.randint(0,3,size=(10,)) # 0~2 클래스 중 랜덤 레이블
print(f'x_data: {x_data}')
print(f'y_labels: {y_labels}')
# 2️⃣ 원-핫 인코딩 (to_categorical)
# → 클래스 0,1,2를 [1,0,0], [0,1,0], [0,0,1] 형태로 변환
y_data = to_categorical(y_labels, num_classes=3)
print(f'y_data: {y_data}')
print()
# 3️⃣ 모델 구성: 입력(4) → 은닉(30, ReLU) → 출력(3, Softmax)
model = Sequential([
Dense(30, activation='relu', input_shape=(4,)),
Dense(3, activation='softmax')
])
# 4️⃣ 모델 설정 및 학습
# 손실함수: categorical_crossentropy (다중분류용)
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])
model.fit(x_data, y_data, epochs=10, validation_split=0.3, verbose=2)
# accuracy : 훈련 정확도 / val_accuracy : 검증 정확도
Ⅲ. MNIST 데이터셋 소개
MNIST는 0~9까지의 손글씨 숫자 이미지를 담은 대표적인 머신러닝 데이터셋입니다. 각 이미지는 28×28 픽셀의 흑백 이미지로, 총 70,000개(훈련 60,000 / 테스트 10,000)로 구성됩니다.
Ⅳ. Ex02 – MNIST 이미지 분류(EDA)
from keras.src.datasets import mnist
from matplotlib import pyplot as plt
# 1️⃣ 데이터 로드
(x_train, y_train), (x_test, y_test) = mnist.load_data()
print(x_train.shape) # (60000, 28, 28)
print(y_train.shape) # (60000,)
print(x_test.shape) # (10000, 28, 28)
print(y_test.shape) # (10000,)
print(x_train[0], y_train[0]) # 첫 번째 이미지와 정답 출력
# 2️⃣ 이미지 시각화(2×3 그리드)
nrow, ncol = 2, 3
fig, axes = plt.subplots(nrow, ncol, figsize=[10, 6])
for idx in range(nrow * ncol):
ax = axes[idx // ncol, idx % ncol]
ax.imshow(x_train[idx], cmap='gray')
ax.axis('off')
ax.set_title(f'label: {y_train[idx]}')
plt.show()
설명
mnist.load_data(): 자동으로 훈련/테스트 분리x_train: 이미지 (28×28),y_train: 라벨(0~9)- 간단한 시각화로 데이터 감 잡기
Ⅴ. Softmax와 MNIST의 관계
- MNIST는 10개 클래스(0~9) → 출력 뉴런 수=10
- Softmax는 각 숫자 클래스 확률을 계산 → 가장 큰 확률의 클래스를 예측
- 예: [0.01, 0.02, 0.93, …] → “2” 예측
Ⅵ. 추가 구현 아이디어(간단 MLP)
from keras import Sequential
from keras.src.layers import Dense, Flatten
from tensorflow.python.keras.utils.np_utils import to_categorical
# 1️⃣ 전처리(정규화/원-핫)
x_train = x_train / 255.0 # 0~255 → 0~1
x_test = x_test / 255.0
y_train = to_categorical(y_train, 10)
y_test = to_categorical(y_test, 10)
# 2️⃣ 모델
model = Sequential([
Flatten(input_shape=(28,28)), # 2D → 1D
Dense(128, activation='relu'),
Dense(10, activation='softmax')
])
# 3️⃣ 학습/평가
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])
model.fit(x_train, y_train, epochs=5, batch_size=32, validation_split=0.1)
test_loss, test_acc = model.evaluate(x_test, y_test)
print(f"테스트 정확도: {test_acc:.4f}")
Ex03_mnist.py — MLP로 MNIST 다중분류
핵심 포인트
input_shape=(784,)평탄화 벡터 입력- 은닉 512(ReLU) → 출력 10(Softmax)
validation_split=0.2로 검증
import numpy as np
from keras import Sequential
from keras.src.datasets import mnist
from keras.src.layers import Dense
from keras.src.utils import to_categorical
from matplotlib import pyplot as plt
# 1) 데이터 적재
(x_train, y_train), (x_test, y_test) = mnist.load_data()
# 2) 입력 차원 계산(28*28=784)
x_column = x_train.shape[1] * x_train.shape[2]
print('x_column:', x_column)
# 3) 평탄화 + 정규화
x_train = x_train.reshape(x_train.shape[0], x_column).astype('float32')/255.0
x_test = x_test.reshape(x_test.shape[0], x_column).astype('float32')/255.0
# 4) 원-핫 라벨
CLASSES = 10
y_train = to_categorical(y_train, num_classes=CLASSES)
y_test = to_categorical(y_test, num_classes=CLASSES)
# 5) 모델
model = Sequential()
model.add(Dense(units=512, activation="relu", input_shape=(x_column,)))
model.add(Dense(units=CLASSES, activation="softmax"))
# 6) 컴파일 & 학습
model.compile(loss="categorical_crossentropy", optimizer="rmsprop", metrics=["accuracy"])
model.fit(x_train, y_train, epochs=10, batch_size=64, validation_split=0.2, verbose=1)
# 7) 평가 & 예측
score = model.evaluate(x_test, y_test)
print(f'score : {score}') # [loss, acc]
prediction = model.predict(x_test)
predict_class = np.argmax(prediction[0])
print(f'prediction class : {predict_class}')
Ex04_mnist.py — Fashion-MNIST 분류 & 시각화
핵심 포인트 & 주의
- 전처리: 평탄화 + 정규화(0~1)
class_names로 시각화 라벨 가독성- 주의:
np.argmax(prediction)은 전체 배열 기준 → 개별 샘플은np.argmax(prediction[i])사용
import numpy as np
from keras import Sequential
from keras.src.datasets import fashion_mnist
from keras.src.layers import Dense
from keras.src.utils import to_categorical
from matplotlib import pyplot as plt
# 1) 데이터 적재(Fashion-MNIST)
(x_train, y_train), (x_test, y_test) = fashion_mnist.load_data()
print(x_train.shape, y_train.shape, x_test.shape, y_test.shape)
# 2) 평탄화 + 정규화
x_column = x_train.shape[1] * x_train.shape[2]
x_train = x_train.reshape(x_train.shape[0], x_column).astype('float32')/255.0
x_test = x_test.reshape(x_test.shape[0], x_column).astype('float32')/255.0
# 3) 클래스 라벨
class_names = ['T-shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat',
'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle boot']
# 4) 원-핫 라벨
CLASSES = 10
y_train = to_categorical(y_train, num_classes=CLASSES)
y_test = to_categorical(y_test, num_classes=CLASSES)
# 5) 모델: 128-128 ReLU → 10 Softmax
model = Sequential()
model.add(Dense(units=128, activation='relu', input_shape=(x_column,)))
model.add(Dense(units=128, activation='relu'))
model.add(Dense(units=CLASSES, activation='softmax'))
# 6) 컴파일 & 학습
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
model.fit(x_train, y_train, batch_size=10, epochs=10, validation_data=(x_test, y_test))
# 7) 예측 & 평가
prediction = model.predict(x_test)
score = model.evaluate(x_test, y_test)
print(f'손실률 : {score[0]}, 정확도 {score[1]}')
# (권장) 개별 샘플의 argmax
print('예측')
print('y_test', np.argmax(y_test[0]))
print('y_pred', np.argmax(prediction[0]))
# 8) 4x4 그리드 시각화
nrow, ncol = 4, 4
fig, axes = plt.subplots(nrow, ncol, figsize=[10, 6])
for idx in range(nrow * ncol):
ax = axes[idx//ncol, idx%ncol]
ax.imshow(x_train[idx].reshape(28,28))
ax.axis('off')
ax.set_title(f'label: {class_names[np.argmax(y_train[idx])]}')
plt.show()
Ⅶ. 요약
| 개념 | 역할 | 특징 |
|---|---|---|
| Softmax | 다중분류 확률 계산 | 출력 합=1, 각 클래스 확률 |
| MNIST 데이터 | 0~9 손글씨 이미지 | 28×28 픽셀 흑백 |
| Flatten | 2D→1D 변환 | Dense 입력에 사용 |
| categorical_crossentropy | 다중분류 손실 | Softmax와 함께 사용 |
Ⅷ. 결론
Softmax 함수는 다중분류 모델의 핵심 구성요소로, 모든 클래스의 예측 결과를 확률의 형태로 표현하게 합니다.
MNIST 예제처럼 출력층 뉴런 수를 클래스 수(10개)에 맞추고 Softmax를 적용하면, 모델은 각 숫자(0~9)에 대한 확률을 계산해 가장 높은 확률의 클래스를 예측할 수 있습니다.
실행 메모
- 환경: TensorFlow/Keras, matplotlib 필요
- 전처리 체크리스트:
reshape→astype('float32')→/255.0→to_categorical - 예측 인덱스: 개별 샘플은
np.argmax(prediction[i])형태로 확인
'🟡데이터 분석 > 머신러닝' 카테고리의 다른 글
| 08_머신러닝_로지스틱 회귀(Logistic Regression)_이진분류 (0) | 2025.11.12 |
|---|---|
| 07_머신러닝_나이브 베이즈(Naive Bayes) (0) | 2025.11.12 |
| 06_머신러닝_엔트로피(Entropy) 와 정보이득(Information Gain) (0) | 2025.11.11 |
| 05_머신러닝_K-최근접 이웃(K-Nearest Neighbors, KNN) (0) | 2025.11.11 |
| 04_머신러닝_SVM (Support Vector Machine) (0) | 2025.11.05 |












