본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.
본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.
AI Generated
2025. 12. 4. · 50 Views
지도 학습 개요와 분류 기초 완벽 가이드
머신러닝의 핵심인 지도 학습과 분류의 기초를 다룹니다. 혼동 행렬부터 F1 스코어까지, 모델 평가의 모든 것을 실무 예제와 함께 쉽게 설명합니다.
목차
1. 지도 학습이란
김개발 씨는 회사에서 스팸 메일 필터를 만들라는 과제를 받았습니다. "어떻게 컴퓨터가 스팸인지 아닌지 구분할 수 있지?" 고민하던 그에게 박시니어 씨가 다가왔습니다.
"머신러닝 중에서도 지도 학습이라는 게 있어요. 마치 선생님이 학생을 가르치듯이 컴퓨터를 학습시키는 거죠."
**지도 학습(Supervised Learning)**은 정답이 있는 데이터로 모델을 훈련시키는 방법입니다. 마치 수학 문제집의 정답지를 보면서 공부하는 것과 같습니다.
입력 데이터와 그에 대응하는 정답(레이블)을 함께 제공하면, 모델은 이 관계를 학습하여 새로운 데이터에 대해서도 정답을 예측할 수 있게 됩니다.
다음 코드를 살펴봅시다.
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
# 지도 학습의 핵심: 입력(X)과 정답(y)이 함께 제공됨
X = [[1, 2], [2, 3], [3, 4], [4, 5]] # 입력 데이터
y = [0, 0, 1, 1] # 정답 레이블 (0: 스팸 아님, 1: 스팸)
# 훈련용과 테스트용 데이터 분리
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25)
# 모델 생성 및 학습 - 정답을 보며 패턴을 익힘
model = LogisticRegression()
model.fit(X_train, y_train) # 여기서 학습이 일어남
# 새로운 데이터에 대해 예측
prediction = model.predict(X_test)
김개발 씨는 입사 3개월 차 주니어 개발자입니다. 어느 날 팀장님이 다가와 말했습니다.
"우리 회사 메일 시스템에 스팸 필터를 추가해야 해요. 김개발 씨가 한번 해볼래요?" 김개발 씨는 고민에 빠졌습니다.
스팸 메일을 어떻게 구분할 수 있을까요? 단순히 특정 단어가 들어있으면 스팸이라고 판단하면 될까요?
하지만 스팸 메일은 점점 교묘해지고 있어서 단순한 규칙으로는 한계가 있습니다. 이때 선배 개발자 박시니어 씨가 다가와 조언을 해주었습니다.
"머신러닝을 사용해보는 건 어때요? 특히 지도 학습이라는 방법이 있는데, 이 문제에 딱 맞아요." 그렇다면 지도 학습이란 정확히 무엇일까요?
쉽게 비유하자면, 지도 학습은 마치 수학 문제집으로 공부하는 것과 같습니다. 문제집에는 문제와 함께 정답이 있잖아요.
학생은 문제를 풀고 정답을 확인하면서 어떤 유형의 문제에 어떤 풀이 방법을 적용해야 하는지 배웁니다. 충분히 연습하면 처음 보는 문제도 풀 수 있게 되죠.
지도 학습도 마찬가지입니다. 컴퓨터에게 입력 데이터와 그에 해당하는 정답을 함께 보여줍니다.
예를 들어 "이 메일은 스팸이야", "이 메일은 정상이야"라고 하나하나 알려주는 것입니다. 컴퓨터는 수천, 수만 개의 이런 예시를 보면서 스팸 메일의 특징을 스스로 찾아냅니다.
지도 학습이 없던 시절에는 어땠을까요? 개발자들은 직접 규칙을 만들어야 했습니다.
"제목에 '무료'가 들어가면 스팸", "발신자가 unknown이면 스팸" 같은 규칙을 수백 개씩 작성했습니다. 하지만 스패머들은 이런 규칙을 피해가는 새로운 방법을 계속 만들어냈고, 개발자들은 끝없는 쫓고 쫓기는 게임을 해야 했습니다.
지도 학습은 이 문제를 우아하게 해결합니다. 새로운 유형의 스팸이 나타나면?
그냥 새로운 데이터를 추가해서 다시 학습시키면 됩니다. 모델은 스스로 새로운 패턴을 찾아냅니다.
위의 코드를 살펴보겠습니다. 핵심은 fit 메서드입니다.
이 한 줄에서 모든 학습이 일어납니다. X_train은 입력 데이터이고, y_train은 정답입니다.
모델은 이 둘의 관계를 찾아내어 내부적으로 저장합니다. 학습이 끝난 후에는 predict 메서드로 새로운 데이터에 대해 예측할 수 있습니다.
모델이 학습 과정에서 발견한 패턴을 바탕으로 정답을 추측하는 것이죠. 실제 현업에서 지도 학습은 정말 다양한 곳에서 사용됩니다.
넷플릭스의 영화 추천, 은행의 대출 심사, 병원의 질병 진단까지. 정답 데이터만 충분히 있다면 지도 학습으로 해결할 수 있는 문제는 무궁무진합니다.
다시 김개발 씨의 이야기로 돌아가 봅시다. 박시니어 씨의 설명을 들은 김개발 씨는 눈이 반짝였습니다.
"아, 그러니까 과거의 스팸 메일 데이터를 모아서 학습시키면 되는 거군요!" 그렇습니다. 지도 학습의 시작은 바로 좋은 품질의 레이블된 데이터를 확보하는 것입니다.
실전 팁
💡 - 지도 학습의 성능은 데이터의 품질과 양에 크게 좌우됩니다
- 훈련 데이터와 테스트 데이터는 반드시 분리해야 과적합을 방지할 수 있습니다
- 레이블링 작업은 시간이 많이 들지만, 모델 성능에 결정적인 영향을 미칩니다
2. 분류와 회귀의 차이
스팸 필터 개발을 시작한 김개발 씨에게 새로운 과제가 추가되었습니다. "아, 그리고 사용자별로 예상 구매 금액도 예측해주세요." 김개발 씨는 혼란스러워졌습니다.
스팸인지 아닌지 구분하는 것과 금액을 예측하는 것, 둘 다 지도 학습인 것 같은데 뭔가 다른 느낌이 들었습니다.
**분류(Classification)**와 **회귀(Regression)**는 지도 학습의 두 가지 핵심 유형입니다. 분류는 데이터를 정해진 카테고리 중 하나로 구분하는 것이고, 회귀는 연속적인 숫자 값을 예측하는 것입니다.
마치 객관식 시험과 주관식 시험의 차이와 같습니다.
다음 코드를 살펴봅시다.
from sklearn.linear_model import LogisticRegression, LinearRegression
import numpy as np
# 분류 문제: 스팸(1)인지 정상(0)인지 구분
# 결과가 정해진 카테고리 중 하나
classifier = LogisticRegression()
X_email = [[100, 5], [20, 1], [150, 8]] # 단어수, 링크수
y_spam = [1, 0, 1] # 스팸 여부 (이산적 값)
classifier.fit(X_email, y_spam)
spam_prediction = classifier.predict([[80, 4]]) # 결과: 0 또는 1
# 회귀 문제: 구매 금액 예측
# 결과가 연속적인 숫자
regressor = LinearRegression()
X_user = [[25, 50000], [35, 80000]] # 나이, 연봉
y_purchase = [30000, 75000] # 예상 구매금액 (연속적 값)
regressor.fit(X_user, y_purchase)
amount_prediction = regressor.predict([[30, 65000]]) # 결과: 52500.0
김개발 씨는 두 가지 과제를 앞에 두고 고민했습니다. 스팸 필터와 구매 금액 예측.
둘 다 데이터를 가지고 무언가를 예측하는 건 같은데, 왜 다르게 느껴질까요? 박시니어 씨가 화이트보드 앞으로 다가갔습니다.
"자, 이렇게 생각해봐요. 스팸 필터는 어떤 결과를 내놓죠?" 김개발 씨가 대답했습니다.
"스팸이거나 스팸이 아니거나요. 둘 중 하나죠." "맞아요.
그럼 구매 금액 예측은요?" "음... 30,000원일 수도 있고, 52,347원일 수도 있고...
어떤 숫자든 될 수 있네요." 박시니어 씨가 고개를 끄덕였습니다. "바로 그게 분류와 회귀의 차이예요." 쉽게 비유하자면, 분류는 객관식 시험과 같습니다.
1번, 2번, 3번, 4번 중에서 하나를 고르는 것이죠. 정답은 반드시 주어진 선택지 중 하나입니다.
반면 회귀는 주관식 시험과 같습니다. "이 문제의 답은 몇일까요?"라고 물으면 3.14159...처럼 어떤 숫자든 답이 될 수 있습니다.
분류 문제의 대표적인 예시를 들어볼까요? 이메일이 스팸인지 아닌지, 사진 속 동물이 개인지 고양이인지, 고객이 이탈할 것인지 남을 것인지.
모두 정해진 카테고리 중 하나를 선택하는 문제입니다. 회귀 문제는 어떨까요?
내일 주식 가격, 이번 달 예상 매출, 환자의 생존 기간 예측. 모두 구체적인 숫자 값을 예측해야 합니다.
코드를 살펴보면 이 차이가 더 명확해집니다. 분류 모델인 LogisticRegression의 predict 결과는 0 또는 1입니다.
딱 떨어지는 정수죠. 반면 회귀 모델인 LinearRegression의 predict 결과는 52500.0처럼 연속적인 실수입니다.
재미있는 점은 같은 문제도 분류나 회귀로 다르게 접근할 수 있다는 것입니다. 예를 들어 "고객의 구매 금액"을 예측한다면 회귀 문제입니다.
하지만 "고객이 10만원 이상 구매할 것인가?"로 질문을 바꾸면 분류 문제가 됩니다. 어떤 방식을 선택할지는 비즈니스 목적에 따라 달라집니다.
정확한 금액이 필요하면 회귀를, VIP 고객을 선별하는 것이 목적이면 분류를 사용하면 됩니다. 실무에서 흔히 하는 실수 중 하나는 분류 문제에 회귀 모델을 사용하거나 그 반대로 하는 것입니다.
스팸 여부를 예측하는데 LinearRegression을 사용하면 -0.3이나 1.7 같은 이상한 값이 나올 수 있습니다. 문제 유형에 맞는 모델을 선택해야 합니다.
김개발 씨가 고개를 끄덕였습니다. "아, 이제 알겠어요.
스팸 필터는 분류 모델로, 구매 금액 예측은 회귀 모델로 만들면 되는 거군요!" 문제의 본질을 이해하면 해결책은 자연스럽게 따라옵니다.
실전 팁
💡 - 예측 결과가 카테고리면 분류, 연속적인 숫자면 회귀입니다
- 같은 문제도 질문 방식에 따라 분류 또는 회귀가 될 수 있습니다
- 이진 분류(2개 클래스)와 다중 분류(3개 이상 클래스)를 구분하세요
3. 분류 모델 과제 소개
김개발 씨는 본격적으로 스팸 필터 개발에 착수했습니다. 로지스틱 회귀 모델을 만들어 훈련까지 마쳤습니다.
"다 됐다!" 하고 기뻐하려는 순간, 박시니어 씨가 물었습니다. "그래서 이 모델 성능이 어때요?
얼마나 잘 맞추나요?" 김개발 씨는 멈칫했습니다. 성능을 어떻게 측정해야 할까요?
분류 모델은 단순히 만들기만 해서는 안 됩니다. 모델이 얼마나 잘 예측하는지 평가해야 합니다.
분류 모델의 성능을 평가하는 방법에는 여러 가지가 있으며, 각각 다른 관점에서 모델을 바라봅니다. 정확도, 정밀도, 재현율, F1 스코어 등이 대표적인 평가 지표입니다.
다음 코드를 살펴봅시다.
from sklearn.metrics import accuracy_score, classification_report
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
# 스팸 메일 분류 모델 훈련
X = [[100, 5, 1], [20, 1, 0], [150, 8, 1], [30, 2, 0], [120, 6, 1]]
y = [1, 0, 1, 0, 1] # 1: 스팸, 0: 정상
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.4)
model = LogisticRegression()
model.fit(X_train, y_train)
# 모델 평가 - 여기가 핵심!
y_pred = model.predict(X_test)
accuracy = accuracy_score(y_test, y_pred)
print(f"정확도: {accuracy:.2f}")
# 상세 평가 리포트
print(classification_report(y_test, y_pred, target_names=['정상', '스팸']))
김개발 씨는 뿌듯한 마음으로 모델을 완성했습니다. 하지만 박시니어 씨의 질문에 당황했습니다.
"성능이요? 음...
잘 되는 것 같은데요?" 박시니어 씨가 웃으며 말했습니다. "'것 같다'는 건 엔지니어의 언어가 아니에요.
숫자로 증명해야죠." 이것이 바로 분류 모델 평가의 핵심입니다. 감이 아닌 수치로 모델의 성능을 판단해야 합니다.
쉽게 비유하자면, 이것은 마치 학생의 실력을 평가하는 것과 같습니다. "공부 잘하는 것 같아요"라고 말하는 것과 "수학 95점, 영어 88점이에요"라고 말하는 것은 천지 차이입니다.
구체적인 점수가 있어야 어디가 부족한지, 얼마나 개선해야 하는지 알 수 있습니다. 분류 모델을 평가할 때 가장 먼저 떠오르는 것은 **정확도(Accuracy)**입니다.
전체 예측 중 맞힌 비율이죠. 100개를 예측해서 90개를 맞혔다면 정확도는 90%입니다.
직관적이고 이해하기 쉽습니다. 하지만 정확도만으로는 부족한 경우가 많습니다.
예를 들어 암 진단 모델을 만들었다고 가정해봅시다. 실제로 암 환자는 전체의 1%밖에 안 됩니다.
이 상황에서 모델이 무조건 "암 아님"이라고만 예측해도 정확도는 99%입니다. 하지만 이 모델은 완전히 쓸모없습니다.
암 환자를 하나도 찾아내지 못하니까요. 이런 문제를 해결하기 위해 다양한 평가 지표가 등장했습니다.
정밀도(Precision), 재현율(Recall), F1 스코어 등이 대표적입니다. 각각 다른 관점에서 모델을 평가합니다.
위의 코드에서 classification_report 함수를 사용하면 이 모든 지표를 한눈에 볼 수 있습니다. 정상 메일과 스팸 메일 각각에 대해 정밀도, 재현율, F1 스코어를 보여줍니다.
실무에서는 상황에 따라 중요한 지표가 달라집니다. 스팸 필터라면 정상 메일을 스팸으로 잘못 분류하는 것(거짓 양성)을 줄이는 게 중요할 수 있습니다.
중요한 업무 메일이 스팸함으로 가버리면 큰일이니까요. 반면 암 진단이라면 실제 암 환자를 놓치지 않는 것(거짓 음성 최소화)이 더 중요합니다.
모델을 평가할 때 한 가지 더 중요한 점이 있습니다. 반드시 테스트 데이터로 평가해야 합니다.
훈련에 사용한 데이터로 평가하면 좋은 점수가 나오겠지만, 이건 시험 문제를 미리 알고 본 시험과 같습니다. 새로운 데이터에 대한 진짜 실력을 알 수 없습니다.
김개발 씨는 classification_report의 결과를 보며 고개를 끄덕였습니다. "아, 정확도만 보면 안 되는 거군요.
상황에 따라 봐야 할 지표가 다르고요." 이제 각각의 지표가 무엇을 의미하는지 더 자세히 알아볼 시간입니다.
실전 팁
💡 - 정확도만으로는 불균형 데이터에서 모델 성능을 제대로 파악할 수 없습니다
- 항상 훈련 데이터와 테스트 데이터를 분리해서 평가하세요
- 비즈니스 목적에 따라 중요한 평가 지표가 달라집니다
4. 혼동 행렬
김개발 씨가 분류 리포트를 열심히 들여다보고 있었습니다. 하지만 숫자가 어떻게 계산되었는지 이해가 잘 안 됐습니다.
박시니어 씨가 A4 용지를 가져와 네 칸짜리 표를 그렸습니다. "이게 혼동 행렬이에요.
모든 분류 평가 지표의 기초가 되는 거죠."
**혼동 행렬(Confusion Matrix)**은 분류 모델의 예측 결과를 정리한 표입니다. 실제 값과 예측 값의 조합에 따라 네 가지 경우로 나눕니다.
참 양성(TP), 거짓 양성(FP), 참 음성(TN), 거짓 음성(FN). 이 네 가지 숫자만 알면 모든 평가 지표를 계산할 수 있습니다.
다음 코드를 살펴봅시다.
from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay
import matplotlib.pyplot as plt
# 실제 값과 예측 값
y_true = [1, 0, 1, 1, 0, 1, 0, 0, 1, 0] # 실제: 1=스팸, 0=정상
y_pred = [1, 0, 0, 1, 0, 1, 1, 0, 1, 0] # 예측 결과
# 혼동 행렬 생성
cm = confusion_matrix(y_true, y_pred)
print("혼동 행렬:")
print(cm)
# [[3, 1], <- 실제 정상: 3개 맞춤(TN), 1개 틀림(FP)
# [1, 5]] <- 실제 스팸: 1개 틀림(FN), 5개 맞춤(TP)
# 시각화
tn, fp, fn, tp = cm.ravel()
print(f"참 양성(TP): {tp}, 거짓 양성(FP): {fp}")
print(f"참 음성(TN): {tn}, 거짓 음성(FN): {fn}")
박시니어 씨가 그린 표는 단순했습니다. 가로 2칸, 세로 2칸.
총 4칸짜리 표였습니다. "자, 생각해봐요.
스팸 필터가 내릴 수 있는 결론은 '스팸이다' 또는 '스팸 아니다' 둘 중 하나예요. 그리고 실제로 그 메일이 스팸인지 아닌지는 따로 있죠.
이 조합이 바로 네 가지가 되는 거예요." 김개발 씨가 따라 그려봤습니다. 첫 번째 경우: 실제로 스팸인데, 모델도 스팸이라고 예측했다.
이걸 **참 양성(True Positive, TP)**이라고 합니다. "참"은 예측이 맞았다는 뜻이고, "양성"은 스팸(관심 있는 클래스)으로 예측했다는 뜻입니다.
두 번째 경우: 실제로 정상인데, 모델이 스팸이라고 잘못 예측했다. 이걸 **거짓 양성(False Positive, FP)**이라고 합니다.
정상 메일이 스팸함으로 가버리는 경우죠. 오탐이라고도 부릅니다.
세 번째 경우: 실제로 정상인데, 모델도 정상이라고 예측했다. **참 음성(True Negative, TN)**입니다.
네 번째 경우: 실제로 스팸인데, 모델이 정상이라고 잘못 예측했다. **거짓 음성(False Negative, FN)**입니다.
스팸이 받은 편지함에 들어오는 경우입니다. 미탐이라고도 하죠.
이 네 가지 숫자가 왜 중요할까요? 모든 분류 평가 지표가 이 숫자들의 조합으로 계산되기 때문입니다.
정확도는 (TP + TN) / 전체입니다. 맞힌 것의 비율이죠.
정밀도는 TP / (TP + FP)입니다. 스팸이라고 예측한 것 중 실제 스팸의 비율입니다.
재현율은 TP / (TP + FN)입니다. 실제 스팸 중 모델이 찾아낸 비율입니다.
코드를 보면 confusion_matrix 함수가 이 네 숫자를 2x2 배열로 반환합니다. ravel() 함수를 사용하면 TN, FP, FN, TP 순서로 풀어서 볼 수 있습니다.
실무에서 혼동 행렬을 볼 때는 대각선을 먼저 봅니다. 대각선(TN과 TP)은 올바른 예측이고, 대각선이 아닌 곳(FP와 FN)은 틀린 예측입니다.
좋은 모델이라면 대각선의 숫자가 크고, 나머지는 작아야 합니다. 한 가지 주의할 점이 있습니다.
어떤 클래스를 "양성(Positive)"으로 볼지는 상황에 따라 다릅니다. 스팸 필터에서는 보통 스팸을 양성으로 봅니다.
암 진단에서는 암을 양성으로 봅니다. 관심 있는 클래스, 찾고자 하는 클래스를 양성으로 설정합니다.
김개발 씨가 혼동 행렬을 다시 바라봤습니다. "이제 왜 '혼동' 행렬인지 알겠어요.
모델이 어디서 혼동했는지, 어디서 실수했는지 한눈에 볼 수 있으니까요!"
실전 팁
💡 - 대각선은 정답, 비대각선은 오답입니다
- FP와 FN 중 어느 것이 더 치명적인지 비즈니스 맥락에서 판단하세요
- 다중 분류에서는 NxN 행렬이 됩니다
5. 정밀도와 재현율
김개발 씨가 혼동 행렬을 이해하고 나니 다음 질문이 떠올랐습니다. "그래서 FP가 많으면 안 좋은 건가요, FN이 많으면 안 좋은 건가요?" 박시니어 씨가 대답했습니다.
"상황에 따라 달라요. 그래서 정밀도와 재현율이라는 개념이 필요한 거예요."
**정밀도(Precision)**는 양성으로 예측한 것 중 실제 양성의 비율입니다. "스팸이라고 한 것들 중 진짜 스팸이 얼마나 되나?"라는 질문에 답합니다.
**재현율(Recall)**은 실제 양성 중 모델이 찾아낸 비율입니다. "전체 스팸 중 얼마나 잡아냈나?"라는 질문에 답합니다.
두 지표는 서로 트레이드오프 관계에 있습니다.
다음 코드를 살펴봅시다.
from sklearn.metrics import precision_score, recall_score
# 실제 값과 예측 값
y_true = [1, 0, 1, 1, 0, 1, 0, 0, 1, 1] # 1=스팸
y_pred = [1, 1, 0, 1, 0, 1, 1, 0, 1, 1]
# 정밀도: 스팸이라고 예측한 것 중 진짜 스팸 비율
# TP / (TP + FP) = 5 / (5 + 2) = 0.714
precision = precision_score(y_true, y_pred)
print(f"정밀도: {precision:.3f}")
# 재현율: 실제 스팸 중 모델이 찾아낸 비율
# TP / (TP + FN) = 5 / (5 + 1) = 0.833
recall = recall_score(y_true, y_pred)
print(f"재현율: {recall:.3f}")
# 해석: 재현율이 높다 = 스팸을 놓치는 경우가 적다
# 정밀도가 낮다 = 정상 메일을 스팸으로 잘못 분류하는 경우가 있다
박시니어 씨가 질문했습니다. "김개발 씨, 스팸 필터에서 더 나쁜 상황은 뭘까요?
정상 메일이 스팸함으로 가는 것? 아니면 스팸이 받은편지함에 오는 것?" 김개발 씨가 잠시 생각했습니다.
"음... 중요한 업무 메일이 스팸함으로 가면 큰일이잖아요.
그게 더 나쁜 것 같아요." "맞아요. 그래서 스팸 필터에서는 정밀도가 중요해요.
반면에 암 진단에서는 어떨까요?" "암 환자를 정상이라고 잘못 판단하면... 치료 시기를 놓칠 수 있으니 그게 더 위험하겠네요." "그래서 암 진단에서는 재현율이 중요해요." 정밀도를 쉽게 비유하자면, 마치 골드 러시 시대의 금광 탐사와 같습니다.
"금이다!"라고 외친 것들 중에 진짜 금이 얼마나 되는지가 정밀도입니다. 거짓 경보가 많으면 정밀도가 낮아집니다.
재현율은 마치 범인 검거율과 같습니다. 전체 범인 중 경찰이 얼마나 잡았는지가 재현율입니다.
놓친 범인이 많으면 재현율이 낮아집니다. 수식으로 보면 더 명확합니다.
정밀도 = TP / (TP + FP) 분모를 보세요. TP + FP는 "모델이 양성이라고 예측한 모든 것"입니다.
그중에서 실제로 맞힌 것(TP)의 비율이 정밀도입니다. 재현율 = TP / (TP + FN) 분모가 다릅니다.
TP + FN은 "실제 양성인 모든 것"입니다. 그중에서 모델이 찾아낸 것(TP)의 비율이 재현율입니다.
두 지표는 시소처럼 트레이드오프 관계에 있습니다. 정밀도를 높이려면 확실한 것만 양성으로 예측해야 합니다.
그러면 놓치는 것이 많아져 재현율이 낮아집니다. 반대로 재현율을 높이려면 조금이라도 의심되면 다 양성으로 예측해야 합니다.
그러면 오탐이 많아져 정밀도가 낮아집니다. 코드의 결과를 해석해봅시다.
재현율 0.833은 실제 스팸 6개 중 5개를 잡았다는 뜻입니다. 정밀도 0.714는 스팸이라고 예측한 7개 중 5개가 진짜 스팸이라는 뜻입니다.
2개는 억울하게 스팸함에 간 정상 메일이죠. 실무에서는 이 균형점을 찾는 것이 핵심입니다.
모델의 임계값(threshold)을 조절해서 정밀도와 재현율의 균형을 맞춥니다. 임계값을 높이면 정밀도가 올라가고, 낮추면 재현율이 올라갑니다.
김개발 씨가 정리했습니다. "거짓 양성이 치명적이면 정밀도를, 거짓 음성이 치명적이면 재현율을 중시하면 되는 거군요!" 바로 그렇습니다.
실전 팁
💡 - 정밀도는 FP를 줄이고 싶을 때, 재현율은 FN을 줄이고 싶을 때 중요합니다
- 임계값(threshold)을 조절해서 두 지표의 균형을 맞출 수 있습니다
- 비즈니스 요구사항에 따라 어떤 지표를 우선할지 결정하세요
6. F1 스코어
김개발 씨는 고민에 빠졌습니다. 정밀도와 재현율, 둘 다 중요해 보입니다.
"두 개 다 보면 되잖아요?"라고 생각했지만, 모델을 비교할 때 숫자가 두 개면 애매해집니다. A 모델은 정밀도가 높고, B 모델은 재현율이 높으면 어떤 게 더 좋은 걸까요?
F1 스코어는 정밀도와 재현율의 조화 평균입니다. 두 지표를 하나의 숫자로 합쳐서 모델을 쉽게 비교할 수 있게 해줍니다.
조화 평균을 사용하기 때문에 두 지표가 균형 잡혀 있을 때 높은 점수가 나옵니다. 한쪽만 높고 다른 쪽이 낮으면 F1 스코어도 낮아집니다.
다음 코드를 살펴봅시다.
from sklearn.metrics import f1_score, precision_score, recall_score
# 모델 A: 정밀도 높음, 재현율 낮음
y_true = [1, 1, 1, 1, 0, 0, 0, 0]
y_pred_a = [1, 1, 0, 0, 0, 0, 0, 0] # 확실한 것만 예측
precision_a = precision_score(y_true, y_pred_a) # 1.0
recall_a = recall_score(y_true, y_pred_a) # 0.5
f1_a = f1_score(y_true, y_pred_a) # 0.667
# 모델 B: 균형 잡힌 예측
y_pred_b = [1, 1, 1, 0, 1, 0, 0, 0]
precision_b = precision_score(y_true, y_pred_b) # 0.75
recall_b = recall_score(y_true, y_pred_b) # 0.75
f1_b = f1_score(y_true, y_pred_b) # 0.75
print(f"모델 A - 정밀도: {precision_a}, 재현율: {recall_a}, F1: {f1_a:.3f}")
print(f"모델 B - 정밀도: {precision_b}, 재현율: {recall_b}, F1: {f1_b:.3f}")
# 모델 B가 더 균형 잡혀서 F1 스코어가 높음
박시니어 씨가 흥미로운 퀴즈를 냈습니다. "자, 두 모델이 있어요.
모델 A는 정밀도 100%, 재현율 50%예요. 모델 B는 정밀도 75%, 재현율 75%예요.
어떤 게 더 좋은 모델이에요?" 김개발 씨가 계산해봤습니다. "음...
A는 평균이 75%고, B도 평균이 75%네요. 같은 건가요?" "산술 평균으로 보면 그래요.
하지만 모델 A는 실제로 스팸 절반을 놓치고 있어요. 그게 정말 좋은 모델일까요?" 여기서 조화 평균의 개념이 등장합니다.
쉽게 비유하자면, 조화 평균은 마치 팀워크 점수와 같습니다. 축구팀에서 공격수가 아무리 잘해도 수비가 엉망이면 좋은 팀이 아닙니다.
조화 평균은 이런 "불균형"에 패널티를 줍니다. F1 스코어의 공식은 이렇습니다: F1 = 2 x (정밀도 x 재현율) / (정밀도 + 재현율) 이 공식의 핵심은 곱셈입니다.
정밀도가 1.0이고 재현율이 0.5면, 분자는 2 x 1.0 x 0.5 = 1.0입니다. 분모는 1.0 + 0.5 = 1.5입니다.
결과는 1.0 / 1.5 = 0.667입니다. 반면 정밀도 0.75, 재현율 0.75면, 분자는 2 x 0.75 x 0.75 = 1.125입니다.
분모는 0.75 + 0.75 = 1.5입니다. 결과는 1.125 / 1.5 = 0.75입니다.
산술 평균으로는 둘 다 75%였지만, F1 스코어로 보면 균형 잡힌 모델 B가 더 높습니다. 왜 조화 평균을 사용할까요?
극단적인 상황을 생각해봅시다. 모든 것을 양성으로 예측하면 재현율은 100%가 됩니다.
모든 양성을 다 잡았으니까요. 하지만 정밀도는 바닥을 칩니다.
이런 쓸모없는 모델에 높은 점수를 주면 안 되겠죠? 조화 평균은 한쪽이 0에 가까우면 결과도 0에 가깝게 만들어서 이런 문제를 방지합니다.
코드를 보면 모델 A는 확실한 것만 예측해서 정밀도 100%를 달성했지만, 재현율이 50%밖에 안 됩니다. F1 스코어는 0.667입니다.
모델 B는 둘 다 75%로 균형 잡혀 있고, F1 스코어도 0.75로 더 높습니다. 실무에서 F1 스코어는 "하나의 숫자로 모델을 비교하고 싶을 때" 유용합니다.
여러 모델을 실험하면서 "F1이 가장 높은 모델"을 선택하면 정밀도와 재현율 모두 어느 정도 보장된 모델을 고를 수 있습니다. 다만 주의할 점도 있습니다.
정밀도가 훨씬 중요하거나 재현율이 훨씬 중요한 상황에서는 F1 스코어만 보면 안 됩니다. 비즈니스 맥락을 항상 고려해야 합니다.
실전 팁
💡 - F1 스코어가 높다 = 정밀도와 재현율이 모두 적절히 높다
- 한쪽 지표가 극단적으로 중요하면 F1 대신 그 지표를 직접 보세요
- 불균형 데이터에서는 weighted F1이나 macro F1을 사용하기도 합니다
7. 모델 평가 지표 선택
김개발 씨는 스팸 필터 개발을 거의 마무리했습니다. 하지만 마지막 질문이 남았습니다.
"팀장님께 어떤 숫자를 보고해야 하죠? 정확도요?
F1 스코어요?" 박시니어 씨가 웃으며 대답했습니다. "그건 문제의 성격에 따라 달라요.
정답은 없어요. 하지만 선택의 기준은 있죠."
모델 평가 지표 선택은 정해진 정답이 없습니다. 비즈니스 목표, 데이터의 특성, 오류의 비용을 종합적으로 고려해야 합니다.
클래스 불균형이 심하면 정확도는 의미가 없고, 오탐이 치명적이면 정밀도를, 미탐이 치명적이면 재현율을 중시해야 합니다. 상황에 맞는 지표를 선택하는 것이 진정한 실력입니다.
다음 코드를 살펴봅시다.
from sklearn.metrics import (accuracy_score, precision_score,
recall_score, f1_score, roc_auc_score)
# 상황별 평가 지표 선택 가이드
def evaluate_model(y_true, y_pred, y_prob, context="general"):
results = {
"정확도": accuracy_score(y_true, y_pred),
"정밀도": precision_score(y_true, y_pred),
"재현율": recall_score(y_true, y_pred),
"F1": f1_score(y_true, y_pred),
"AUC": roc_auc_score(y_true, y_prob)
}
# 상황별 권장 지표
recommendations = {
"spam": "정밀도 (정상 메일 보호가 중요)",
"medical": "재현율 (환자 놓치면 안 됨)",
"fraud": "재현율 + AUC (사기 탐지 최우선)",
"balanced": "F1 또는 정확도 (균형 잡힌 평가)"
}
print(f"[{context}] 권장: {recommendations.get(context, 'F1')}")
return results
팀장님과의 미팅이 다가왔습니다. 김개발 씨는 발표 자료를 준비하며 박시니어 씨에게 마지막 조언을 구했습니다.
"박시니어 님, 결국 어떤 지표를 보여드려야 할까요?" 박시니어 씨가 잠시 생각하더니 화이트보드에 세 가지 질문을 적었습니다. 첫째, 데이터가 균형 잡혀 있나요? 만약 스팸이 전체의 1%밖에 안 된다면, 정확도는 의미가 없습니다.
무조건 "스팸 아님"이라고 해도 99%의 정확도가 나오니까요. 이런 불균형 상황에서는 정밀도, 재현율, F1 스코어, 또는 AUC-ROC를 사용해야 합니다.
둘째, 어떤 실수가 더 치명적인가요? 스팸 필터에서 정상 메일이 스팸함에 가면(FP) 중요한 메일을 놓칠 수 있습니다. 이 경우 정밀도를 중시합니다.
암 진단에서 암 환자를 정상으로 판정하면(FN) 치료 시기를 놓칩니다. 이 경우 재현율을 중시합니다.
사기 탐지에서는 사기를 놓치면 금전적 손실이 발생합니다. 역시 재현율이 중요하지만, 너무 많은 정상 거래를 막으면 고객 불만이 생깁니다.
두 지표의 균형이 필요합니다. 셋째, 단일 임계값인가요, 전체 성능인가요? 지금까지 본 지표들은 모두 특정 임계값(보통 0.5)에서의 성능입니다.
하지만 AUC-ROC는 모든 임계값에서의 성능을 종합적으로 평가합니다. 모델 자체의 잠재력을 보고 싶다면 AUC-ROC가 유용합니다.
실무에서 자주 사용하는 지표 선택 가이드를 정리해봅시다. 균형 잡힌 데이터에서 일반적인 분류: 정확도 또는 F1 불균형 데이터: F1, AUC-ROC, 또는 상황에 따라 정밀도/재현율 스팸 필터, 추천 시스템: 정밀도 (사용자 경험 중시) 의료 진단, 사기 탐지, 보안: 재현율 (놓치면 안 되는 상황) 모델 비교 및 선택: AUC-ROC (임계값 독립적) 코드를 보면 상황(context)에 따라 다른 지표를 권장하는 함수를 만들었습니다.
이렇게 상황별로 어떤 지표를 봐야 하는지 가이드를 만들어두면 팀원 모두가 일관된 기준으로 모델을 평가할 수 있습니다. 김개발 씨가 정리했습니다.
"결국 숫자 자체보다 그 숫자가 비즈니스에서 무엇을 의미하는지가 중요한 거군요." 박시니어 씨가 고개를 끄덕였습니다. "맞아요.
95% 정확도라는 숫자보다 '100통 중 5통의 중요한 메일이 스팸함에 갈 수 있다'는 해석이 팀장님께 더 와닿을 거예요." 김개발 씨는 자신감을 얻었습니다. 이제 숫자의 의미를 알고 있으니, 어떤 질문이 와도 대답할 수 있을 것 같습니다.
실전 팁
💡 - 지표를 보고할 때는 숫자와 함께 비즈니스 의미도 설명하세요
- 한 가지 지표만 보지 말고 여러 지표를 함께 봐야 전체 그림이 보입니다
- 모델 성능 목표는 비즈니스 팀과 함께 설정하세요
이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!
댓글 (0)
함께 보면 좋은 카드 뉴스
vLLM 통합 완벽 가이드
대규모 언어 모델 추론을 획기적으로 가속화하는 vLLM의 설치부터 실전 서비스 구축까지 다룹니다. PagedAttention과 연속 배칭 기술로 GPU 메모리를 효율적으로 활용하는 방법을 배웁니다.
Web UI Demo 구축 완벽 가이드
Gradio를 활용하여 머신러닝 모델과 AI 서비스를 위한 웹 인터페이스를 구축하는 방법을 다룹니다. 코드 몇 줄만으로 전문적인 데모 페이지를 만들고 배포하는 과정을 초급자도 쉽게 따라할 수 있도록 설명합니다.
Sandboxing & Execution Control 완벽 가이드
AI 에이전트가 코드를 실행할 때 반드시 필요한 보안 기술인 샌드박싱과 실행 제어에 대해 알아봅니다. 격리된 환경에서 안전하게 코드를 실행하고, 악성 동작을 탐지하는 방법을 단계별로 설명합니다.
Voice Design then Clone 워크플로우 완벽 가이드
AI 음성 합성에서 일관된 캐릭터 음성을 만드는 Voice Design then Clone 워크플로우를 설명합니다. 참조 음성 생성부터 재사용 가능한 캐릭터 구축까지 실무 활용법을 다룹니다.
Tool Use 완벽 가이드 - Shell, Browser, DB 실전 활용
AI 에이전트가 외부 도구를 활용하여 셸 명령어 실행, 브라우저 자동화, 데이터베이스 접근 등을 수행하는 방법을 배웁니다. 실무에서 바로 적용할 수 있는 패턴과 베스트 프랙티스를 담았습니다.