목차
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를 늘리는 방식으로 균형을 맞춘다.
또한 검증 데이터의 성능 변화를 함께 보면서 과적합 여부를 판단하는 것이 중요하다.
'머신러닝&딥러닝' 카테고리의 다른 글
| 머신러닝 & 딥러닝 기초 33편 | 모델 저장과 로드 (joblib, pickle, Keras) (0) | 2026.03.21 |
|---|---|
| 머신러닝 & 딥러닝 기초 32편 | 딥러닝 - CNN, RNN, Transformer (0) | 2026.03.20 |
| 머신러닝 & 딥러닝 기초 30편 | 딥러닝 완전 정리 (개념부터 구조, 학습까지) (0) | 2026.03.20 |
| 머신러닝 & 딥러닝 기초 29편 | t-SNE(t-distributed Stochastic Neighbor Embedding) (0) | 2026.03.19 |
| 머신러닝 & 딥러닝 기초 28편 | PCA(Principal Component Analysis, 주성분 분석) (0) | 2026.03.19 |