머신러닝&딥러닝

머신러닝 & 딥러닝 기초 31편 | 딥러닝 실습 완전 정리 (이진 분류, 다중 분류, MNIST)

SecLogs YJ 2026. 3. 20. 23:23
목차

0. TensorFlow / Keras 간단 정리
1. 이진 분류(Binary Classification)
2. 다중 분류(Multi-class Classification)

 

 

이전 글에서는 딥러닝의 구조와 학습 과정을 이론적으로 정리했다.

 

머신러닝 & 딥러닝 기초 30편 | 딥러닝 완전 정리 (개념부터 구조, 학습까지)

목차1. 딥러닝(Deep Learning)이란2. 인공신경망(ANN)3. 퍼셉트론(Perceptron)4. 다층 퍼셉트론(MLP)5. 활성화 함수6. 딥러닝의 학습 과정7. 딥러닝 활용 분야8. 머신러닝 vs 딥러닝9. 인공지능(AI) 1. 딥러닝(Deep

security-logs.tistory.com

 

이번 글에서는 실제 코드와 함께 다음 3가지 문제를 해결해본다.

  • 이진 분류 (Binary Classification)
  • 다중 분류 (Multi-class Classification)

 

0. TensorFlow / Keras 간단 정리

딥러닝 구현을 위해 가장 많이 사용되는 라이브러리는 TensorFlow와 Keras이다.

 

  • TensorFlow 
    • 구글에서 개발한 딥러닝 프레임워크
    • 연산과 모델 학습을 담당한다.
  • Keras
    • Tensorflow 위에서 동작하는 고수준 API
    • 딥러닝 모델(CNN, RNN 등)을 쉽게 설계할 수 있도록 도와준다.

즉, TensorFlow는 연산을 수행하는 엔진, Keras는 모델을 쉽게 만드는 인터페이스이다.

※ 과거에는 Keras가 다양한 벡엔드(Theano, CNTK 등)를 지원했지만, 현재는 TensorFlow기반으로 통합되어 사용되는 것이 일반적이다.

 

 

1. 이진 분류(Binary Classification)

이진 분류는 데이터를 두 개의 클래스(범주) 중 하나로 구분하는 과정이다.

 

예시

  • 스팸 / 정상 메일
  • 합격 / 불합격
  • 질병 있음 / 없음

 

출력층

  • 활성화 함수 : Sigmoid
  • 손실 함수 : Binary Cross Entropy
  • 최적화 함수 : Adam 또는 SGD
  • 평가지표 : 정확도(Accuracy), F1-Score, Precision, Recall 등
항목 개념
Sigmoid 출력 값을 0~1 사이로 변환하여 확률로 표현
Binary Cross Entropy 예측 확률과 실제 값(0/1)의 차이를 계산하는 손실 함수
Adam 학습률을 자동 조절하며 빠르고 안정적으로 학습하는 옵티마이저
SGD 일부 데이터로 빠르게 가중치를 업데이트하는 경사하강법
Accuracy 전체 중 맞춘 비율
Precision 양성이라고 예측한 것 중 실제 양성 비율
Recall 실제 양성 중 맞춘 비율
F1-score Precision과 Recall의 균형 지표

 

 

예제 1) 라이브러리 불러오기

import numpy as np
import tensorflow as tf
import matplotlib.pyplot as plt

from tensorflow.keras import Sequential
from tensorflow.keras.layers import Dense, Input, Dropout
from tensorflow.keras.callbacks import EarlyStopping

from sklearn.model_selection import train_test_split
from sklearn.datasets import make_classification
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import classification_report

 

 

numpy 수치 계산용 라이브러리
tensorflow 딥러닝 모델 생성 및 학습
matplotlib.pyplot 학습 결과 시각화
Sequential 층을 순서대로 쌓는 신경망 모델
Dense 완전 연결층
Input 입력층 정의
Dropout 과적합 방지용 층
EarlyStopping 성능 향상이 멈추면 학습 조기 종료
train_test_split 데이터 분할
make_classification 분류용 가상 데이터 생성
StandardScaler 데이터 표준화
classification_report 정밀도, 재현율, F1-score 출력

 

 

예제 2) 데이터 생성

X, y = make_classification(
    n_samples=1200,
    n_features=18,
    n_informative=12,
    n_redundant=4,
    n_classes=2,
    random_state=21
)

 

 

n_samples 전체 데이터 개수
n_features 전체 feature 개수
n_informative 실제 분류에 중요한 feature 개수
n_redundant 중복되거나 덜 중요한 feature 개수
n_classes 클래스 개수, 이진 분류이므로 2
random_state 난수 고정

 

 

예제 3) 데이터 분할

훈련 / 검증 / 테스트 = 70 : 15 : 15로 나누었다.

X_train, X_temp, y_train, y_temp = train_test_split(
    X,
    y,
    test_size=0.3,
    random_state=21
)

X_val, X_test, y_val, y_test = train_test_split(
    X_temp,
    y_temp,
    test_size=0.5,
    random_state=21
)

 

X_train, y_train 훈련 데이터 (70%)
X_val, y_val 검증 데이터 (15%)
X_test, y_test 테스트 데이터 (15%)
X_temp, y_temp 검증/테스트용 임시 데이터
test_size=0.3 먼저 전체의 30%를 분리
test_size=0.5 남은 30%를 다시 반으로 나눔

 

* 데이터 분할이 필요한 이유

  • 과적합 방지
  • 모델 성능 평가
  • 데이터 유출 방지

일반적으로 다음과 같은 비율로 데이터를 나눈다.

  • Train : 70~80% 
    • 모델을 실제로 학습시키는 용도
  • Validation : 10~15%
    • 학습 중 성능을 확인하고 Early Stopping에 사용하는 용도
  • Test : 10~15%
    • 학습이 끝난 뒤 최종 성능을 평가하는 용도

 

예제 4) 데이터 표준화

데이터의 값 범위가 서로 다르면 학습이 불안정해질 수 있기 때문에, 평균과 분산을 기준으로 데이터를 맞춰주는 과정이 필요하다.

scaler = StandardScaler()

X_train = scaler.fit_transform(X_train)
X_val = scaler.transform(X_val)
X_test = scaler.transform(X_test)

 

 

StandardScaler 평균 0, 표준편차 1 기준으로 데이터 스케일 조정
fit_transform 훈련 데이터 기준으로 평균/표준편차를 학습하고 변환
transform 같은 기준으로 검증/테스트 데이터 변환

 

 

fit_transform을 처음에만 사용하는 이유

표준화의 기준이 되는 평균과 표준편차는 반드시 훈련 데이터로만 계산해야 한다.
검증 데이터나 테스트 데이터까지 함께 기준을 계산하면, 학습 과정에서 사용하면 안 되는 정보가 포함되어 데이터 누수가 발생할 수 있다.

따라서

  • 훈련 데이터: fit_transform()으로 기준 학습 + 변환
  • 검증/테스트 데이터: transform()으로 동일 기준 적용

 

예제 5) 모델 생성

딥러닝 모델은 입력층 → 은닉층 → 출력층으로 구성된다.

각 층을 순서대로 쌓아 데이터를 점점 더 잘 분류할 수 있도록 만든다.

 

특히 은닉층에서는 비선형 활성화 함수(ReLU)를 사용하여 복잡한 패턴을 학습하며, 과적합을 방지하기 위해 Dropout을 함께 사용하는 경우가 많다.

model = Sequential([
    Input(shape=(X_train.shape[1],)),
    Dense(64, activation='relu'),
    Dropout(0.4),
    Dense(32, activation='relu'),
    Dense(1, activation='sigmoid')
])

 

Input 입력층, feature 개수 지정
Dense(64, relu) 첫 번째 은닉층
Dropout(0.4) 학습 중 일부 뉴런을 무작위로 제외하여 과적합 방지
Dense(32, relu) 두 번째 은닉층
Dense(1, sigmoid) 출력층, 이진 분류이므로 1개 뉴런 사용

 

 

<Dropout이 필요한 이유>

과적합은 모델이 특정 데이터에만 지나치게 맞춰져 일반화 성능이 떨어지는 현상이다.
이때 모델이 특정 뉴런이나 경로에 과도하게 의존하는 경우가 많다.

Dropout은 학습 과정에서 일부 뉴런을 무작위로 제거하여 특정 뉴런에 대한 의존도를 낮추고, 다양한 패턴을 학습하도록 만든다.

  • 매 학습마다 다른 구조의 네트워크를 사용하는 효과가 있음
  • 모델의 일반화 성능을 향상함
  • 과적합을 완화하는 대표적인 방법

 

예제 6) 모델 컴파일

모델 학습 전에 학습 방법, 손실 함수, 평가 기준을 설정하는 단계이다.

model.compile(
    optimizer='adam',
    loss='binary_crossentropy',
    metrics=['accuracy']
)

 

optimizer='adam' 가중치를 효율적으로 업데이트하는 최적화 알고리즘
loss='binary_crossentropy' 이진 분류에 적합한 손실 함수
metrics=['accuracy'] 학습 중 정확도를 함께 출력

 

 

예제 7) Early Stopping 설정

과적합(Overfitting)은 모델이 학습 데이터에만 지나치게 맞춰져, 새로운 데이터에서는 성능이 떨어지는 현상이다.

이를 방지하기 위해 보통 다음과 같은 방법을 사용한다.

  • 훈련 / 검증 / 테스트 데이터 분리
  • 데이터 스케일링(정규화 또는 표준화)
  • Early Stopping
  • Dropout

Early Stopping은 학습 중 검증 성능이 더 이상 좋아지지 않으면 학습을 조기에 종료하여 과적합을 방지하는 방법이다.

 

early_stopping = EarlyStopping(
    monitor='val_loss',
    patience=5,
    restore_best_weights=True
)

 

monitor='val_loss' 검증 손실을 기준으로 학습 상태 확인
patience=5 5번 동안 개선이 없으면 중단
restore_best_weights=True 가장 성능이 좋았던 가중치 복원

 

 

예제 8) 모델 훈련

fit()은 모델이 데이터를 학습하도록 하는 함수이다.
앞에서 사용한 fit_transform()은 데이터 전처리용 함수이고, 여기서는 모델을 학습하는 단계이기 때문에 fit()을 사용한다.

history = model.fit(
    X_train,
    y_train,
    validation_data=(X_val, y_val),
    epochs=50,
    batch_size=32,
    callbacks=[early_stopping],
    verbose=1
)
fit 모델 학습 수행
validation_data 검증 데이터로 성능 확인
epochs=50 최대 50번 학습
batch_size=32 한 번에 32개씩 학습
callbacks EarlyStopping 적용
verbose=1 학습 로그 출력

 

 

예제 8) 출력 결과

학습 과정에서 출력되는 로그는 모델이 얼마나 잘 학습되고 있는지를 보여준다.

각 항목의 의미를 이해하면 학습 상태, 성능, 과적합 여부를 판단할 수 있다.

Epoch 1/50
27/27 ━━━━━━━━━━━━━━━━━━━━ 1s 8ms/step - accuracy: 0.5524 - loss: 0.6900 - val_accuracy: 0.7389 - val_loss: 0.5915
Epoch 2/50
27/27 ━━━━━━━━━━━━━━━━━━━━ 0s 3ms/step - accuracy: 0.7119 - loss: 0.5792 - val_accuracy: 0.8444 - val_loss: 0.4981
... 생략

 

 항목의미해석 방법대응 방법

로그 항목 의미 해석 방법 대응 방법
Epoch 1/50 전체 학습 횟수 중 현재 단계 학습 초반 단계이므로 성능 판단은 이르다 여러 epoch 진행 후 판단
27/27 전체 배치 중 현재 진행 상황 모든 데이터가 정상적으로 학습됨 별도 조치 필요 없음
1s / 8ms/step 학습 시간 및 속도 학습 속도가 정상인지 확인 느리면 batch size, GPU 환경 조정
accuracy 훈련 데이터 정확도 초기에는 낮은 것이 정상, 점차 증가해야 함 증가하지 않으면 모델 구조/학습률 점검
loss 훈련 데이터 손실 값 학습이 진행될수록 감소해야 함 감소하지 않으면 학습 문제 가능성
val_accuracy 검증 데이터 정확도 훈련 정확도와 비슷하거나 약간 낮으면 정상 크게 차이나면 과적합 의심
val_loss 검증 데이터 손실 값 훈련 loss와 유사하게 감소하면 정상 증가하면 과적합 가능성

 

 

예제 9) 모델 평가

test_loss, test_acc = model.evaluate(X_test, y_test)
print(f"Test Loss: {test_loss:.4f}")
print(f"Test Accuracy: {test_acc:.4f}")
evaluate 테스트 데이터로 최종 성능 평가
test_loss 테스트 손실 값
test_acc 테스트 정확도

 

 

예제 10) 예측 및 분류 리포트 출력

predictions = model.predict(X_test)
predicted_classes = (predictions > 0.5).astype(int)

print("\nClassification Report")
print(classification_report(y_test, predicted_classes))

 

 

predict 테스트 데이터 예측
predictions > 0.5 0.5 기준으로 0 또는 1 분류
astype(int) 정수형으로 변환
classification_report Precision, Recall, F1-score 출력

 

 

예제 11) 학습 결과 시각화

 

손실 함수 변화 시각화

plt.plot(history.history['loss'], label='Training Loss')
plt.plot(history.history['val_loss'], label='Validation Loss')
plt.title('Loss over Epochs')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()
plt.show()

 

history.history['loss'] 훈련 손실 값
history.history['val_loss'] 검증 손실 값
plt.plot() 선 그래프 생성
plt.legend() 범례 표시
plt.show() 그래프 출력

 

 

손실 함수 변화 시각화 결과

손실 함수 변화 시각화 결과

 

손실(Loss)은 모델의 오차를 의미하며, 값이 낮을수록 좋은 모델이다.
그래프에서는 보통 Training Loss(훈련 손실)와 Validation Loss(검증 손실)를 함께 확인한다.

 

정상적인 경우

  • Training Loss ↓, Validation Loss ↓
    → 모델이 안정적으로 학습 중이며 일반화 성능도 좋음

과적합 발생

  • Training Loss ↓, Validation Loss ↑
    → 훈련 데이터에는 잘 맞지만, 새로운 데이터에는 성능이 떨어짐
    → 과적합 발생

학습 부족 (Underfitting)

  • Training Loss가 높고, Validation Loss도 높음
    → 모델이 데이터를 충분히 학습하지 못함
    → 모델 구조 또는 학습 부족 문제

핵심 정리

  • Loss는 낮을수록 좋다
  • Training과 Validation의 차이가 커지면 과적합을 의심한다

 

 

정확도 변화 시각화

plt.plot(history.history['accuracy'], label='Training Accuracy')
plt.plot(history.history['val_accuracy'], label='Validation Accuracy')
plt.title('Accuracy over Epochs')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.legend()
plt.show()
history.history['accuracy'] 훈련 정확도
history.history['val_accuracy'] 검증 정확도
plt.plot() 선 그래프 생성
plt.legend() 범례 표시
plt.show() 그래프 출력

 

 

정확도 변화 시각화 결과

정확도 변화 시각화 결과

 

정확도(Accuracy)는 모델이 얼마나 맞게 예측했는지를 나타낸다.
마찬가지로 Training Accuracy와 Validation Accuracy를 함께 본다.

 

정상적인 경우

  • Training Accuracy ↑, Validation Accuracy ↑
    → 모델이 잘 학습되고 있으며 일반화도 잘 됨

과적합 발생

  • Training Accuracy ↑, Validation Accuracy ↓ 또는 정체
    → 훈련 데이터에는 잘 맞지만 실제 성능은 떨어짐
    → 과적합

학습 부족 (Underfitting)

  • Training Accuracy와 Validation Accuracy 모두 낮음
    → 모델이 충분히 학습되지 않음

핵심 정리

  • Accuracy는 높을수록 좋다
  • Training과 Validation 차이가 크면 과적합 가능성이 높다

 

2. 다중 분류

다중 분류는 데이터를 세 개 이상의 클래스 중 하나로 구분하는 문제이다.
이진 분류와 달리 여러 개의 범주 중에서 하나를 선택해야 한다.

 

예를 들어 다음과 같은 문제에서 사용된다.

  • 숫자 이미지 분류 (0 ~ 9)
  • 동물 분류 (고양이 / 개 / 말 / 새 등)
  • 감정 분류 (기쁨 / 슬픔 / 분노 등)

출력층

  • 활성화 함수 : Softmax
  • 손실 함수 : Categorical Cross Entropy
  • 최적화 함수 : Adam 또는 SGD
  • 평가지표 : 정확도(Accuracy), F1-Score, Precision, Recall 등

 

예제 1) 라이브러리 불러오기

from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from tensorflow.keras.utils import to_categorical
from tensorflow.keras.datasets import mnist

 

Sequential 층을 순서대로 쌓는 모델
Dense 완전 연결층
to_categorical 원-핫 인코딩
mnist 손글씨 숫자 데이터셋

 

 

예제 2) 데이터 로드 및 전처리

MNIST 데이터셋은 0~9까지의 숫자 이미지를 포함하고 있다.

(x_train, y_train), (x_test, y_test) = mnist.load_data()

x_train = x_train.reshape(-1, 784) / 255.0
x_test = x_test.reshape(-1, 784) / 255.0

y_train = to_categorical(y_train, num_classes=10)
y_test = to_categorical(y_test, num_classes=10)
reshape(-1, 784) 28×28 이미지를 1차원 벡터로 변환
/255.0 픽셀 값을 0~1 범위로 정규화
to_categorical 정답을 원-핫 벡터로 변환
num_classes=10 전체 클래스 개수 (0~9, 총 10개)

 

mnist.load_data()는 데이터를 (훈련 데이터, 테스트 데이터) 형태로 반환한다.

또한 각 데이터는

  • 입력 데이터(x)
  • 정답 레이블(y)

로 구성되어 있기 때문에 다음과 같이 구조화되어 있다.

  • (x_train, y_train) : 학습용 데이터
  • (x_test, y_test) : 평가용 데이터

따라서 두 개의 튜플을 한 번에 받아오는 형태로 사용한다.

 

 

예제 3) 모델 생성

다중 분류에서는 출력층의 뉴런 수를 클래스 개수와 동일하게 설정한다.

model = Sequential([
    Input(shape=(784,)),
    Dense(128, activation='relu'),
    Dense(64, activation='relu'),
    Dense(10, activation='softmax')
])

 

Dense(128, relu) 첫 번째 은닉층
Dense(64, relu) 두 번째 은닉층
Dense(10, softmax) 출력층, 10개 클래스 확률 출력

 

 

예제 4) 모델 컴파일

model.compile(
    optimizer='adam',
    loss='categorical_crossentropy',
    metrics=['accuracy']
)

 

optimizer='adam' 가중치 업데이트 방식
loss='categorical_crossentropy' 다중 분류 손실 함수
metrics=['accuracy'] 정확도 출력

 

 

예제 5) 모델 훈련

history = model.fit(
    x_train,
    y_train,
    epochs=10,
    batch_size=32,
    validation_split=0.2
)

 

fit 모델 학습 수행
epochs 반복 학습 횟수
batch_size 한 번에 학습할 데이터 수
validation_split 일부 데이터를 검증용으로 사용

 

 

예제 6) 모델 평가

test_loss, test_accuracy = model.evaluate(x_test, y_test)
print(f"Test Accuracy: {test_accuracy}")
evaluate 테스트 데이터 성능 평가
test_accuracy 최종 정확도

 

 

예제 7) 예측 결과 확인

predictions = model.predict(x_test[:1])

print(predictions[0])
print(predictions.argmax())

 

predict 클래스별 확률 출력
argmax 가장 높은 확률의 클래스 선택

 

<argmax를 사용하는 이유>

Softmax를 사용한 출력 결과는 하나의 값이 아니라, 각 클래스에 대한 확률 배열이다.

예 :  [0.01, 0.02, 0.85, 0.03, ..., 0.01]

이 값은 각 인덱스가 클래스(0~9)를 의미하고, 각 값은 해당 클래스일 확률을 의미한다.

하지만 실제로 우리가 원하는 것은 “어느 클래스인지” 하나의 결과이다.

이때 argmax()를 사용하면 가장 큰 확률을 가진 위치(인덱스)* 반환한다.

 

 

예제 8) 학습 결과 시각화

 

손실 변화

import matplotlib.pyplot as plt

plt.plot(history.history['loss'], label='Training Loss')
plt.plot(history.history['val_loss'], label='Validation Loss')
plt.legend()
plt.show()

 

정확도 변화

plt.plot(history.history['accuracy'], label='Training Accuracy')
plt.plot(history.history['val_accuracy'], label='Validation Accuracy')
plt.legend()
plt.show()

 

 

 


정리

구분 이진 분류 다중 분류
클래스 수 2개 3개 이상
출력층 Dense(1) Dense(클래스 수)
활성화 함수 Sigmoid Softmax
출력 형태 하나의 확률 값 (0~1) 각 클래스별 확률
손실 함수 Binary Cross Entropy Categorical Cross Entropy
예측 방식 0.5 기준으로 분류 argmax로 가장 높은 확률 선택

 

 

학습 시 고려해야 할 요소

1. Epoch (학습 횟수)

Epoch는 전체 데이터를 몇 번 반복해서 학습할지를 의미한다.

  • 너무 적으면
    → 모델이 충분히 학습되지 않음 (Underfitting)
  • 너무 많으면
    → 훈련 데이터에만 맞춰짐 (Overfitting)

따라서 적절한 epoch를 찾거나, Early Stopping을 통해 자동으로 학습을 중단하는 것이 중요하다.

 

2. Batch Size

Batch Size는 한 번에 학습할 데이터의 개수를 의미한다.

  • 너무 작으면
    → 학습이 불안정하지만 일반화 성능은 좋아질 수 있음
  • 너무 크면
    → 학습은 빠르지만 최적해를 놓칠 가능성이 있음

일반적으로 32, 64, 128 등의 값을 많이 사용하며, 데이터 크기와 환경에 따라 조정이 필요하다.

 

3. Epoch와 Batch Size의 관계

  • Epoch와 Batch Size는 함께 조정해야 한다.
  • Batch Size가 크면 Epoch를 줄이고
  • Batch Size가 작으면 Epoch를 늘리는 방식으로 균형을 맞춘다.

또한 검증 데이터의 성능 변화를 함께 보면서 과적합 여부를 판단하는 것이 중요하다.

 


 👉 머신러닝 & 딥러닝 기초 32편 | 딥러닝 - CNN, RNN, Transformer