🤖

본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.

⚠️

본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.

이미지 로딩 중...

머신러닝 모델 평가 완벽 가이드 - 슬라이드 1/8
A

AI Generated

2025. 12. 17. · 5 Views

머신러닝 모델 평가 완벽 가이드

머신러닝 모델의 성능을 제대로 측정하는 방법을 배웁니다. 정확도부터 F1 Score까지, 실무에서 꼭 필요한 평가 지표들을 쉽게 이해할 수 있습니다. 상황에 맞는 평가 지표를 선택하는 기준도 함께 배웁니다.


목차

  1. 정확도_이해
  2. 혼동_행렬_이해
  3. 정밀도_계산
  4. 재현율_계산
  5. F1_Score_이해
  6. classification_report_활용
  7. 평가_지표_선택_기준

1. 정확도 이해

김개발 씨가 첫 번째 머신러닝 모델을 완성했습니다. 이메일 스팸 필터를 만들었는데, 과연 얼마나 잘 작동하는지 확인하고 싶습니다.

선배 박시니어 씨가 물었습니다. "모델의 정확도는 얼마나 나왔어요?"

**정확도(Accuracy)**는 전체 예측 중에서 올바르게 예측한 비율을 나타냅니다. 가장 직관적인 평가 지표로, 모델이 얼마나 정확하게 맞췄는지 한눈에 알 수 있습니다.

하지만 모든 상황에서 완벽한 지표는 아닙니다.

다음 코드를 살펴봅시다.

from sklearn.metrics import accuracy_score

# 실제 값과 예측 값
y_true = [1, 0, 1, 1, 0, 1, 0, 0, 1, 0]
y_pred = [1, 0, 1, 0, 0, 1, 0, 0, 1, 1]

# 정확도 계산
accuracy = accuracy_score(y_true, y_pred)
print(f"정확도: {accuracy:.2f}")  # 0.80 (80%)

# 직접 계산해보면
correct = sum(t == p for t, p in zip(y_true, y_pred))
total = len(y_true)
manual_accuracy = correct / total
print(f"수동 계산 정확도: {manual_accuracy:.2f}")

김개발 씨는 주니어 데이터 과학자입니다. 회사에서 첫 번째 프로젝트로 이메일 스팸 필터를 개발했습니다.

모델이 완성되자 뿌듯한 마음에 선배인 박시니어 씨에게 달려갔습니다. "선배님!

제 모델이 완성됐어요!" 김개발 씨가 신이 나서 말했습니다. 박시니어 씨는 미소를 지으며 물었습니다.

"좋아요. 그런데 모델이 얼마나 잘 작동하는지는 확인했나요?" 김개발 씨는 잠시 멈칫했습니다.

모델을 만들었지만, 성능을 어떻게 측정해야 하는지는 생각하지 못했던 것입니다. **정확도(Accuracy)**는 머신러닝 모델을 평가하는 가장 기본적인 지표입니다.

쉽게 비유하자면, 정확도는 마치 시험 점수와 같습니다. 전체 문제 10개 중에서 8개를 맞췄다면 80점이죠.

마찬가지로 모델이 100개의 예측 중 80개를 올바르게 맞췄다면 정확도는 80%입니다. 매우 직관적이고 이해하기 쉬운 지표입니다.

모델을 만들고 나서 가장 먼저 확인하고 싶은 것은 "제대로 작동하는가?"입니다. 정확도는 이 질문에 명확하게 답해줍니다.

계산 방법도 간단합니다. 올바르게 예측한 개수를 전체 예측 개수로 나누면 됩니다.

복잡한 수학이 필요하지 않습니다. 누구나 쉽게 이해하고 계산할 수 있습니다.

하지만 박시니어 씨는 조심스럽게 말했습니다. "정확도는 좋은 지표지만, 항상 완벽하지는 않아요.

특히 데이터가 불균형할 때는 주의해야 합니다." 김개발 씨는 고개를 갸우뚱했습니다. "불균형이요?" 예를 들어 설명해보겠습니다.

이메일 1000개 중에서 스팸이 10개, 정상 메일이 990개라고 가정해봅시다. 만약 모델이 모든 이메일을 "정상"이라고 예측한다면 어떻게 될까요?

정확도는 99%가 나옵니다. 하지만 이 모델은 스팸을 하나도 찾아내지 못합니다.

완전히 쓸모없는 모델이지만 정확도는 매우 높게 나오는 것입니다. 위의 코드를 살펴보겠습니다.

accuracy_score 함수는 sklearn에서 제공하는 가장 기본적인 평가 함수입니다. 실제 값(y_true)과 예측 값(y_pred)을 입력하면 자동으로 정확도를 계산해줍니다.

수동으로 계산하는 부분도 함께 보여드렸는데, 원리를 이해하는 데 도움이 됩니다. 실제 현업에서는 어떻게 활용할까요?

뉴스 기사를 카테고리별로 분류하는 서비스를 개발한다고 가정해봅시다. 각 카테고리에 기사가 골고루 분포되어 있다면 정확도는 훌륭한 지표입니다.

정치, 경제, 스포츠, 연예 기사가 비슷한 비율로 있을 때, 정확도 90%는 모델이 정말 잘 작동한다는 의미입니다. 하지만 주의할 점이 있습니다.

초보 개발자들이 흔히 하는 실수는 정확도만 보고 모델을 평가하는 것입니다. 희귀 질병 진단 시스템을 만든다고 생각해보세요.

1000명 중 1명만 병에 걸린 상황에서 모두 "건강함"이라고 예측하면 정확도는 99.9%입니다. 하지만 정작 중요한 환자는 한 명도 찾지 못합니다.

김개발 씨는 이제 이해가 되었습니다. "아, 그래서 정확도만으로는 부족하군요.

다른 지표들도 함께 봐야겠네요." 박시니어 씨가 고개를 끄덕였습니다. "맞아요.

정확도는 출발점이에요. 여기서부터 시작해서 다른 지표들도 함께 살펴봐야 합니다." 정확도를 제대로 이해하면 모델의 기본 성능을 빠르게 파악할 수 있습니다.

하지만 데이터의 특성을 함께 고려해야 올바른 평가가 가능합니다. 여러분도 모델을 평가할 때 데이터의 분포를 먼저 확인하는 습관을 들이세요.

실전 팁

💡 - 데이터가 균형 잡혀 있을 때는 정확도가 좋은 지표입니다

  • 클래스 불균형이 있다면 정확도만으로 평가하지 마세요
  • 항상 데이터 분포를 먼저 확인하고 적절한 지표를 선택하세요

2. 혼동 행렬 이해

김개발 씨가 모델의 정확도를 확인했더니 85%가 나왔습니다. 꽤 높은 수치라 만족스러웠습니다.

그런데 박시니어 씨가 말했습니다. "좋네요.

그런데 어떤 부분을 틀렸는지는 알아요?" 김개발 씨는 당황했습니다.

**혼동 행렬(Confusion Matrix)**은 모델이 어떻게 예측했는지를 상세하게 보여주는 표입니다. 단순히 맞았는지 틀렸는지만이 아니라, 어떤 방식으로 틀렸는지까지 알 수 있습니다.

모델의 강점과 약점을 한눈에 파악할 수 있는 핵심 도구입니다.

다음 코드를 살펴봅시다.

from sklearn.metrics import confusion_matrix
import numpy as np

# 실제 값과 예측 값
y_true = [1, 0, 1, 1, 0, 1, 0, 0, 1, 0]
y_pred = [1, 0, 1, 0, 0, 1, 0, 0, 1, 1]

# 혼동 행렬 생성
cm = confusion_matrix(y_true, y_pred)
print("혼동 행렬:")
print(cm)

# 각 요소 추출
tn, fp, fn, tp = cm.ravel()
print(f"\nTrue Negative (TN): {tn}")   # 올바르게 0으로 예측
print(f"False Positive (FP): {fp}")   # 잘못 1로 예측
print(f"False Negative (FN): {fn}")   # 잘못 0으로 예측
print(f"True Positive (TP): {tp}")    # 올바르게 1로 예측

김개발 씨는 정확도 85%라는 결과에 만족했습니다. 하지만 박시니어 씨의 질문은 예상 밖이었습니다.

"어떤 부분을 틀렸는지"는 생각해보지 않았던 것입니다. "정확도만 보면 전체적인 성능은 알 수 있어요.

하지만 모델이 어떤 실수를 하는지는 알 수 없죠." 박시니어 씨가 설명했습니다. "예를 들어 스팸 메일을 정상으로 분류하는 실수와, 정상 메일을 스팸으로 분류하는 실수는 완전히 다른 문제예요." 김개발 씨는 고개를 끄덕였습니다.

확실히 그 둘은 다른 문제였습니다. 혼동 행렬은 모델의 예측을 네 가지 경우로 분류해서 보여줍니다.

마치 학생이 시험을 본 후 오답 노트를 작성하는 것과 같습니다. 단순히 "80점을 맞았다"로 끝나는 것이 아니라, "어떤 유형의 문제를 틀렸는지" 분석하는 것이죠.

혼동 행렬도 마찬가지입니다. 모델이 어떤 종류의 실수를 얼마나 했는지 명확하게 보여줍니다.

혼동 행렬이 없던 시절에는 어떤 문제가 있었을까요? 개발자들은 모델이 틀렸다는 사실만 알 수 있었습니다.

하지만 왜 틀렸는지, 어떤 패턴으로 틀렸는지는 일일이 데이터를 확인해야만 알 수 있었습니다. 100개의 예측 중 20개가 틀렸다면, 그 20개를 하나씩 살펴봐야 했습니다.

매우 비효율적이었죠. 혼동 행렬은 이런 문제를 해결합니다.

네 가지 경우를 자동으로 분류해줍니다. **True Positive(TP)**는 실제로 양성이고 양성으로 예측한 경우입니다.

**True Negative(TN)**는 실제로 음성이고 음성으로 예측한 경우입니다. 이 두 가지는 올바른 예측입니다.

그런데 실수도 두 가지 종류가 있습니다. **False Positive(FP)**는 실제로는 음성인데 양성으로 잘못 예측한 경우입니다.

스팸 필터로 비유하면 정상 메일을 스팸으로 분류한 것이죠. **False Negative(FN)**는 반대로 실제로는 양성인데 음성으로 잘못 예측한 경우입니다.

스팸 메일을 정상으로 분류한 것입니다. 위의 코드를 자세히 살펴보겠습니다.

confusion_matrix 함수는 자동으로 2x2 행렬을 만들어줍니다. 첫 번째 행은 실제 음성 데이터의 예측 결과이고, 두 번째 행은 실제 양성 데이터의 예측 결과입니다.

ravel() 함수로 네 개의 값을 추출하면 각 경우의 개수를 쉽게 확인할 eventHandler. 실무에서는 어떻게 활용할까요?

의료 진단 시스템을 개발한다고 가정해봅시다. False Negative가 많다는 것은 실제 환자를 놓치고 있다는 뜻입니다.

매우 위험한 상황이죠. 반대로 이메일 스팸 필터에서 False Positive가 많다면 중요한 메일이 스팸함으로 가버립니다.

사용자 경험이 나빠집니다. 혼동 행렬을 보면 이런 문제를 즉시 파악할 수 있습니다.

어떤 종류의 실수가 많은지 알면 모델을 개선하는 방향도 명확해집니다. FP가 많다면 양성으로 판단하는 기준을 더 엄격하게 만들어야 합니다.

FN이 많다면 반대로 기준을 완화해야 하죠. 초보 개발자들이 자주 하는 실수가 있습니다.

혼동 행렬의 행과 열을 헷갈리는 것입니다. 행은 실제 값(True Label)이고, 열은 예측 값(Predicted Label)입니다.

이것을 반대로 이해하면 FP와 FN을 거꾸로 해석하게 됩니다. 항상 "실제로는 어땠고, 예측은 어떻게 했는가"의 순서로 생각하세요.

김개발 씨는 자신의 모델 혼동 행렬을 확인했습니다. "아, 제 모델은 스팸을 정상으로 분류하는 실수가 많네요." 박시니어 씨가 말했습니다.

"맞아요. 이제 그 부분을 집중적으로 개선하면 됩니다.

혼동 행렬이 개선 방향을 알려준 거예요." 혼동 행렬을 제대로 이해하면 모델의 문제점을 정확히 진단할 수 있습니다. 단순히 전체 정확도만 보는 것이 아니라, 어떤 종류의 실수를 하는지 파악할 수 있습니다.

실전 팁

💡 - 혼동 행렬은 모델 개선의 출발점입니다

  • FP와 FN 중 어느 것이 더 심각한지는 도메인에 따라 다릅니다
  • 시각화 라이브러리(seaborn)를 사용하면 더 보기 좋게 만들 수 있습니다

3. 정밀도 계산

김개발 씨의 스팸 필터가 어떤 메일을 스팸으로 분류했습니다. 하지만 그중 절반이 실제로는 정상 메일이었습니다.

사용자들이 중요한 메일을 놓치게 된 것입니다. 박시니어 씨가 말했습니다.

"모델의 정밀도를 확인해봐야겠어요."

**정밀도(Precision)**는 모델이 양성으로 예측한 것 중에서 실제로 양성인 비율입니다. "모델이 YES라고 한 것이 얼마나 믿을 만한가?"를 측정합니다.

거짓 양성이 문제가 되는 상황에서 특히 중요한 지표입니다.

다음 코드를 살펴봅시다.

from sklearn.metrics import precision_score, confusion_matrix

# 실제 값과 예측 값
y_true = [1, 0, 1, 1, 0, 1, 0, 0, 1, 0]
y_pred = [1, 0, 1, 0, 0, 1, 0, 1, 1, 0]

# 정밀도 계산
precision = precision_score(y_true, y_pred)
print(f"정밀도: {precision:.2f}")

# 혼동 행렬로 수동 계산
cm = confusion_matrix(y_true, y_pred)
tn, fp, fn, tp = cm.ravel()
manual_precision = tp / (tp + fp)
print(f"수동 계산 정밀도: {manual_precision:.2f}")
print(f"\n양성 예측: {tp + fp}개 중 실제 양성: {tp}개")

김개발 씨의 스팸 필터가 문제를 일으켰습니다. 사용자 한 분이 고객센터에 전화를 걸어 항의했습니다.

"중요한 업무 메일이 스팸함에 들어가 있었어요!" 김개발 씨는 당황했습니다. 모델이 스팸으로 분류한 메일 100개를 확인해보니, 그중 50개가 실제로는 정상 메일이었습니다.

정확도는 괜찮았는데 왜 이런 문제가 생긴 걸까요? 박시니어 씨가 설명했습니다.

"정확도는 전체적인 성능을 보는 거예요. 하지만 '스팸이라고 판단한 것이 정말 스팸인가?'를 확인하려면 **정밀도(Precision)**를 봐야 합니다." 김개발 씨는 새로운 개념에 귀를 기울였습니다.

정밀도는 쉽게 말해 "모델의 신뢰도"입니다. 마치 경찰이 범죄자를 체포하는 상황과 비슷합니다.

10명을 체포했는데 그중 8명만 실제 범죄자라면, 체포의 정밀도는 80%입니다. 2명은 무고한 사람을 잘못 잡은 것이죠.

마찬가지로 모델이 "이것은 양성입니다"라고 판단한 것 중에서 실제로 양성인 비율이 정밀도입니다. 정밀도가 낮으면 어떤 문제가 생길까요?

스팸 필터의 경우 정상 메일이 스팸함으로 가버립니다. 사용자는 중요한 메일을 놓치게 됩니다.

신용카드 사기 탐지 시스템이라면 어떨까요? 정밀도가 낮으면 정상 거래를 사기로 판단해서 카드를 차단합니다.

고객이 당황하고 화가 나겠죠. 정밀도를 높이면 이런 문제를 줄일 수 있습니다.

모델이 양성이라고 판단할 때 더 신중해집니다. 확실한 경우에만 양성으로 분류하는 것이죠.

물론 트레이드오프가 있습니다. 너무 신중하면 실제 양성을 놓칠 수 있습니다.

하지만 거짓 양성이 심각한 문제를 일으키는 상황에서는 정밀도가 매우 중요합니다. 위의 코드를 분석해보겠습니다.

precision_score 함수는 자동으로 정밀도를 계산해줍니다. 하지만 수동 계산 부분도 함께 보여드렸습니다.

정밀도는 TP를 (TP + FP)로 나눈 값입니다. 즉, 양성으로 예측한 전체 개수 중에서 실제로 양성인 개수의 비율입니다.

실무에서 정밀도가 특히 중요한 경우가 있습니다. 유튜브의 추천 시스템을 생각해보세요.

사용자에게 영상을 추천할 때, 추천한 영상을 실제로 시청할 확률이 중요합니다. 정밀도가 낮으면 사용자가 관심 없는 영상만 추천받게 됩니다.

사용자 경험이 나빠지고 서비스를 떠나게 됩니다. 또 다른 예시를 들어보겠습니다.

채용 시스템에서 이력서를 자동으로 선별한다고 가정해봅시다. 합격으로 분류된 이력서 중 실제로 적합한 후보의 비율이 정밀도입니다.

정밀도가 낮으면 인사팀이 적합하지 않은 후보를 면접하느라 시간을 낭비합니다. 주의할 점이 있습니다.

초보 개발자들은 정밀도를 높이기 위해 무조건 기준을 엄격하게 만듭니다. 하지만 이렇게 하면 실제 양성을 많이 놓치게 됩니다.

예를 들어 확률이 99% 이상일 때만 양성으로 분류하면 정밀도는 높아지지만, 많은 양성 샘플을 음성으로 분류해버립니다. 김개발 씨는 자신의 모델을 다시 확인했습니다.

정밀도가 60%밖에 되지 않았습니다. 스팸으로 분류한 메일 10개 중 4개가 실제로는 정상 메일이었던 것입니다.

"이래서 사용자 불만이 많았구나." 김개발 씨가 중얼거렸습니다. 박시니어 씨가 조언했습니다.

"정밀도를 개선하려면 모델이 양성으로 판단하는 기준을 조정해야 해요. 임계값을 높이거나, 특성을 더 추가해서 판단 근거를 강화하는 방법이 있습니다." 김개발 씨는 고개를 끄덕이며 노트에 적었습니다.

정밀도를 제대로 이해하면 모델의 신뢰성을 파악할 수 있습니다. 거짓 양성이 큰 문제가 되는 상황에서는 정밀도를 우선적으로 개선해야 합니다.

실전 팁

💡 - 거짓 양성(False Positive)이 심각한 문제를 일으킬 때 정밀도를 중시하세요

  • 정밀도만 높이면 재현율이 낮아질 수 있으니 균형을 고려하세요
  • 임계값(threshold)을 조정해서 정밀도를 제어할 수 있습니다

4. 재현율 계산

김개발 씨가 정밀도를 높이기 위해 모델을 수정했습니다. 이제 스팸으로 분류한 것은 거의 확실히 스팸이었습니다.

하지만 새로운 문제가 생겼습니다. 실제 스팸 메일이 받은편지함에 넘쳐났습니다.

박시니어 씨가 말했습니다. "이번엔 재현율이 문제네요."

**재현율(Recall)**은 실제 양성 중에서 모델이 올바르게 찾아낸 비율입니다. "실제 양성을 얼마나 놓치지 않았는가?"를 측정합니다.

거짓 음성이 문제가 되는 상황에서 특히 중요한 지표입니다.

다음 코드를 살펴봅시다.

from sklearn.metrics import recall_score, confusion_matrix

# 실제 값과 예측 값
y_true = [1, 0, 1, 1, 0, 1, 0, 0, 1, 0]
y_pred = [1, 0, 1, 0, 0, 1, 0, 0, 0, 0]

# 재현율 계산
recall = recall_score(y_true, y_pred)
print(f"재현율: {recall:.2f}")

# 혼동 행렬로 수동 계산
cm = confusion_matrix(y_true, y_pred)
tn, fp, fn, tp = cm.ravel()
manual_recall = tp / (tp + fn)
print(f"수동 계산 재현율: {manual_recall:.2f}")
print(f"\n실제 양성: {tp + fn}개 중 찾아낸 양성: {tp}개")
print(f"놓친 양성: {fn}개")

김개발 씨는 정밀도를 개선하는 데 성공했습니다. 이제 스팸으로 분류되는 메일은 거의 확실히 스팸이었습니다.

사용자들의 불만도 줄어들었습니다. 그런데 며칠 후 또 다른 문제가 발생했습니다.

"받은편지함에 스팸이 너무 많아요!" 사용자들이 항의하기 시작했습니다. 김개발 씨는 데이터를 확인해보고 깜짝 놀랐습니다.

실제 스팸 메일 100개 중에서 모델이 찾아낸 것은 겨우 30개였습니다. 박시니어 씨가 설명했습니다.

"정밀도를 높이는 데만 집중하다 보니 **재현율(Recall)**이 낮아진 거예요. 모델이 너무 신중해져서 실제 스팸을 많이 놓치고 있는 겁니다." 김개발 씨는 한숨을 쉬었습니다.

한 가지 문제를 해결하니 다른 문제가 생긴 것입니다. 재현율은 "포괄성"을 측정하는 지표입니다.

마치 그물로 물고기를 잡는 상황과 비슷합니다. 연못에 물고기가 100마리 있는데 그물로 70마리를 잡았다면 재현율은 70%입니다.

30마리는 놓친 것이죠. 재현율은 "실제로 있는 것 중에서 얼마나 찾아냈는가"를 나타냅니다.

재현율이 왜 중요할까요? 의료 진단 시스템을 생각해보세요.

암 환자 100명 중에서 20명만 찾아낸다면 재현율은 20%입니다. 나머지 80명은 치료받지 못하고 병이 악화될 수 있습니다.

생명과 직결된 문제입니다. 이런 상황에서는 재현율이 매우 중요합니다.

재현율과 정밀도는 trade-off 관계입니다. 재현율을 높이려면 모델이 더 적극적으로 양성이라고 판단해야 합니다.

의심스러운 것도 양성으로 분류하는 것이죠. 그러면 실제 양성을 놓치는 경우는 줄어듭니다.

하지만 거짓 양성은 늘어납니다. 반대로 정밀도를 높이려면 확실한 경우만 양성으로 분류해야 하는데, 그러면 재현율이 낮아집니다.

위의 코드를 살펴보겠습니다. recall_score 함수는 재현율을 자동으로 계산합니다.

수동 계산을 보면 TP를 (TP + FN)으로 나눕니다. 즉, 실제 양성 전체 개수 중에서 올바르게 찾아낸 개수의 비율입니다.

FN이 많으면 재현율이 낮아집니다. 실무에서 재현율이 특히 중요한 경우를 알아보겠습니다.

사기 거래 탐지 시스템을 개발한다고 가정해봅시다. 실제 사기 거래를 놓치면 금전적 피해가 발생합니다.

고객도 피해를 입고 회사의 신뢰도도 떨어집니다. 이런 상황에서는 재현율을 높여야 합니다.

정상 거래를 사기로 오인하는 것보다 사기를 놓치는 것이 더 큰 문제입니다. 또 다른 예시는 검색 엔진입니다.

사용자가 "머신러닝 튜토리얼"을 검색했을 때, 관련된 문서 1000개 중에서 100개만 보여준다면 재현율은 10%입니다. 나머지 900개의 유용한 문서를 놓친 것입니다.

검색 엔진은 재현율이 높아야 사용자 만족도가 올라갑니다. 초보 개발자들이 자주 하는 실수가 있습니다.

재현율 100%를 목표로 하는 것입니다. 모든 것을 양성으로 분류하면 재현율은 100%가 됩니다.

하지만 정밀도는 바닥을 칩니다. 무의미한 모델이 되는 것이죠.

재현율과 정밀도의 균형을 찾는 것이 중요합니다. 김개발 씨는 모델의 재현율을 확인했습니다.

30%밖에 되지 않았습니다. 실제 스팸 10개 중 3개만 찾아내고 7개는 놓친 것입니다.

"정밀도를 높이려다 보니 너무 보수적으로 예측하게 만들었네요." 김개발 씨가 반성했습니다. 박시니어 씨가 조언했습니다.

"임계값을 낮춰보세요. 확률이 0.8 이상일 때만 스팸으로 분류하던 것을 0.5 이상으로 낮추는 겁니다.

그러면 더 많은 스팸을 잡아낼 수 있어요. 물론 정밀도는 조금 낮아지겠지만요." 김개발 씨는 고개를 끄덕이며 수정 작업에 들어갔습니다.

재현율을 제대로 이해하면 모델이 놓치는 케이스를 파악할 수 있습니다. 거짓 음성이 심각한 문제를 일으키는 상황에서는 재현율을 우선적으로 개선해야 합니다.

실전 팁

💡 - 거짓 음성(False Negative)이 심각한 문제를 일으킬 때 재현율을 중시하세요

  • 재현율과 정밀도는 trade-off 관계이므로 둘 사이의 균형을 찾아야 합니다
  • 민감도(Sensitivity)라고도 부르며, 의료 분야에서 자주 사용됩니다

5. F1 Score 이해

김개발 씨는 고민에 빠졌습니다. 정밀도를 높이면 재현율이 낮아지고, 재현율을 높이면 정밀도가 낮아졌습니다.

둘 다 중요한데 어떻게 평가해야 할까요? 박시니어 씨가 미소를 지으며 말했습니다.

"F1 Score를 사용하면 됩니다."

F1 Score는 정밀도와 재현율의 조화 평균입니다. 두 지표를 균형 있게 평가할 수 있습니다.

정밀도와 재현율이 모두 높아야 F1 Score도 높아지므로, 종합적인 모델 성능을 하나의 숫자로 표현할 수 있습니다.

다음 코드를 살펴봅시다.

from sklearn.metrics import f1_score, precision_score, recall_score

# 실제 값과 예측 값
y_true = [1, 0, 1, 1, 0, 1, 0, 0, 1, 0]
y_pred = [1, 0, 1, 0, 0, 1, 0, 0, 1, 0]

# F1 Score 계산
f1 = f1_score(y_true, y_pred)
print(f"F1 Score: {f1:.2f}")

# 정밀도와 재현율로 수동 계산
precision = precision_score(y_true, y_pred)
recall = recall_score(y_true, y_pred)
manual_f1 = 2 * (precision * recall) / (precision + recall)
print(f"\n정밀도: {precision:.2f}")
print(f"재현율: {recall:.2f}")
print(f"수동 계산 F1: {manual_f1:.2f}")

김개발 씨는 딜레마에 빠졌습니다. 정밀도를 80%로 올리면 재현율이 60%로 떨어졌습니다.

반대로 재현율을 80%로 올리면 정밀도가 65%가 되었습니다. 어떤 모델이 더 나은 걸까요?

"둘 다 중요한데 어떻게 비교하죠?" 김개발 씨가 답답해하며 물었습니다. 박시니어 씨는 화이트보드에 "F1 Score"라고 적었습니다.

"이 지표가 바로 그 고민을 해결해줍니다." F1 Score는 정밀도와 재현율을 하나로 합친 지표입니다. 마치 학생의 종합 성적표와 비슷합니다.

수학 점수와 영어 점수를 따로 보는 것도 좋지만, 종합 평균을 보면 전체적인 실력을 한눈에 파악할 수 있죠. F1 Score도 마찬가지입니다.

정밀도와 재현율을 종합해서 하나의 숫자로 보여줍니다. F1 Score가 필요한 이유는 무엇일까요?

단순 평균을 사용하면 문제가 생깁니다. 정밀도가 90%이고 재현율이 10%라면 단순 평균은 50%입니다.

그런데 재현율이 10%밖에 안 되는 모델은 사실상 쓸모가 없습니다. F1 Score는 이런 극단적인 경우를 방지합니다.

조화 평균을 사용하기 때문입니다. 조화 평균은 작은 값에 더 큰 가중치를 줍니다.

위의 예시에서 F1 Score를 계산하면 약 18%가 나옵니다. 단순 평균 50%보다 훨씬 낮죠.

이것이 조화 평균의 특징입니다. 정밀도와 재현율 중 하나라도 낮으면 F1 Score도 크게 낮아집니다.

두 지표가 모두 높아야만 F1 Score도 높아집니다. 위의 코드를 자세히 분석해보겠습니다.

f1_score 함수는 자동으로 F1 Score를 계산합니다. 수동 계산 공식을 보면 2 * (precision * recall) / (precision + recall)입니다.

이것이 조화 평균의 공식입니다. 정밀도와 재현율을 곱하고, 더한 값으로 나눈 후 2를 곱합니다.

실무에서 F1 Score는 언제 사용할까요? 정밀도와 재현율 중 어느 것이 더 중요한지 명확하지 않을 때 사용합니다.

예를 들어 고객 이탈 예측 모델을 만든다고 가정해봅시다. 이탈할 고객을 놓치는 것(재현율)도 문제고, 이탈하지 않을 고객을 이탈 예정으로 분류하는 것(정밀도)도 문제입니다.

둘 다 중요하므로 F1 Score로 평가하는 것이 적절합니다. 모델을 비교할 때도 유용합니다.

A 모델은 정밀도 75%, 재현율 85%입니다. B 모델은 정밀도 80%, 재현율 78%입니다.

어떤 모델이 더 나을까요? F1 Score를 계산하면 명확해집니다.

A 모델은 79.7%, B 모델은 78.9%가 나옵니다. A 모델이 근소하게 더 우수합니다.

주의할 점이 있습니다. F1 Score는 클래스 불균형 상황에서 특히 유용합니다.

하지만 모든 상황에 완벽한 지표는 아닙니다. 만약 정밀도가 재현율보다 2배 더 중요하다면 어떻게 해야 할까요?

이럴 때는 F2 Score나 F0.5 Score 같은 변형을 사용할 수 있습니다. 김개발 씨는 두 모델의 F1 Score를 비교했습니다.

첫 번째 모델은 F1 Score 72%, 두 번째 모델은 68%였습니다. "첫 번째 모델이 더 균형 잡혀 있네요." 박시니어 씨가 말했습니다.

"정밀도와 재현율이 모두 적절한 수준이에요." 김개발 씨는 이제 모델을 평가하는 체계가 잡혔습니다. 정확도로 전체 성능을 확인하고, 혼동 행렬로 어떤 실수를 하는지 파악하고, 정밀도와 재현율로 구체적인 성능을 측정하고, F1 Score로 종합 평가를 합니다.

각 지표가 어떤 의미를 가지는지 이제 명확히 이해했습니다. F1 Score를 제대로 이해하면 정밀도와 재현율을 균형 있게 평가할 수 있습니다.

하나의 숫자로 모델의 종합 성능을 표현할 수 있어서 모델 비교가 쉬워집니다.

실전 팁

💡 - 정밀도와 재현율이 모두 중요할 때 F1 Score를 사용하세요

  • 하나의 지표라도 낮으면 F1 Score가 크게 낮아집니다
  • 가중치를 조정하려면 F-beta Score를 사용할 수 있습니다

6. classification report 활용

김개발 씨는 매번 정밀도, 재현율, F1 Score를 따로 계산하는 것이 번거로웠습니다. 세 개의 함수를 각각 호출하고 출력하는 코드가 길어졌습니다.

박시니어 씨가 말했습니다. "한 번에 모든 지표를 볼 수 있는 방법이 있어요."

classification_report는 모든 주요 평가 지표를 한 번에 계산해서 보기 좋게 출력해줍니다. 정밀도, 재현율, F1 Score를 클래스별로 보여주며, 전체 평균도 함께 제공합니다.

실무에서 가장 자주 사용하는 평가 도구입니다.

다음 코드를 살펴봅시다.

from sklearn.metrics import classification_report

# 다중 클래스 분류 예제
y_true = [0, 1, 2, 2, 1, 0, 1, 2, 0, 1]
y_pred = [0, 1, 2, 1, 1, 0, 2, 2, 0, 1]

# 전체 리포트 출력
print(classification_report(y_true, y_pred))

# 딕셔너리로 받기
report_dict = classification_report(y_true, y_pred, output_dict=True)
print(f"\n클래스 0 F1 Score: {report_dict['0']['f1-score']:.2f}")
print(f"전체 정확도: {report_dict['accuracy']:.2f}")
print(f"Macro 평균 F1: {report_dict['macro avg']['f1-score']:.2f}")

김개발 씨의 코드는 점점 길어졌습니다. 정밀도를 계산하고, 재현율을 계산하고, F1 Score를 계산하고, 각각 출력하는 코드가 반복되었습니다.

클래스가 3개, 4개로 늘어나면 코드는 더욱 복잡해졌습니다. "매번 이렇게 하는 건 너무 비효율적이에요." 김개발 씨가 푸념했습니다.

박시니어 씨는 웃으며 말했습니다. "sklearn에는 이미 완벽한 해결책이 있어요.

classification_report라는 함수입니다." classification_report는 모든 평가 지표를 한 방에 보여주는 강력한 도구입니다. 마치 건강검진 결과지와 비슷합니다.

혈압, 혈당, 콜레스테롤을 따로따로 검사받는 것이 아니라, 한 번에 모든 검사를 하고 결과를 하나의 표로 받는 것이죠. classification_report도 마찬가지입니다.

한 줄의 코드로 모든 주요 지표를 계산하고 예쁘게 정리해서 보여줍니다. 이 함수가 없던 시절에는 어땠을까요?

개발자들은 precision_score, recall_score, f1_score를 각각 호출했습니다. 클래스가 5개라면 15번의 함수 호출이 필요했습니다.

그리고 결과를 보기 좋게 포맷팅하는 코드도 따로 작성해야 했습니다. 시간이 많이 걸리고 실수하기도 쉬웠습니다.

classification_report는 이 모든 과정을 자동화합니다. 클래스별로 정밀도, 재현율, F1 Score를 계산합니다.

또한 support라는 값도 보여주는데, 이것은 각 클래스의 실제 샘플 개수입니다. 마지막에는 전체 평균도 계산해줍니다.

Macro average, Weighted average, 그리고 전체 accuracy까지 한눈에 볼 수 있습니다. 위의 코드를 자세히 살펴보겠습니다.

기본 사용법은 매우 간단합니다. 실제 값과 예측 값만 넣으면 됩니다.

출력은 표 형태로 나오는데, 각 클래스별로 성능을 한눈에 비교할 수 있습니다. output_dict=True 옵션을 사용하면 딕셔너리로 받을 수도 있어서 프로그램에서 특정 값을 추출하기 쉽습니다.

Macro average와 Weighted average의 차이는 무엇일까요? Macro average는 각 클래스의 지표를 단순 평균낸 것입니다.

클래스 0의 F1이 80%, 클래스 1의 F1이 60%라면 macro average는 70%입니다. 클래스의 샘플 개수와 상관없이 모든 클래스를 동등하게 취급합니다.

Weighted average는 각 클래스의 샘플 개수를 가중치로 사용합니다. 클래스 0이 100개 샘플, 클래스 1이 10개 샘플이라면, 클래스 0의 성능에 더 큰 가중치를 줍니다.

실무에서는 weighted average가 더 실질적인 성능을 나타내는 경우가 많습니다. 하지만 클래스 불균형이 심하면 소수 클래스의 성능을 제대로 반영하지 못할 수 있습니다.

실무에서 어떻게 활용할까요? 모델 개발 중에는 매번 classification_report를 출력해서 성능을 확인합니다.

어떤 클래스의 성능이 낮은지 즉시 파악할 수 있습니다. 예를 들어 클래스 2의 재현율이 유독 낮다면, 그 클래스의 특성을 더 추가하거나 샘플을 더 수집해야 한다는 신호입니다.

모델 비교에도 매우 유용합니다. 여러 모델을 학습시킨 후 각 모델의 classification_report를 비교합니다.

어떤 모델이 어떤 클래스를 더 잘 예측하는지 한눈에 보입니다. A 모델은 클래스 0을 잘 예측하지만 클래스 2를 못하고, B 모델은 반대라면, 앙상블 기법을 고려할 수도 있습니다.

주의할 점이 있습니다. 초보 개발자들은 리포트를 출력만 하고 자세히 읽지 않습니다.

표가 복잡해 보여서 대충 넘어가는 것이죠. 하지만 각 숫자가 중요한 의미를 담고 있습니다.

특히 support 열을 확인해서 클래스 불균형이 있는지 파악해야 합니다. 김개발 씨는 classification_report를 실행했습니다.

표가 깔끔하게 출력되었습니다. "오, 클래스 1의 재현율이 낮네요.

여기를 집중적으로 개선해야겠어요." 박시니어 씨가 만족스러운 표정으로 말했습니다. "맞아요.

이제 문제점을 정확히 파악했으니 개선 방향도 명확해졌습니다." 김개발 씨는 이제 모델 평가가 훨씬 쉬워졌습니다. 한 줄의 코드로 모든 정보를 얻을 수 있었습니다.

시간도 절약되고 실수할 가능성도 줄어들었습니다. 무엇보다 각 클래스의 성능을 한눈에 비교할 수 있어서 모델 개선이 빨라졌습니다.

classification_report를 제대로 활용하면 모델 평가 시간을 크게 단축할 수 있습니다. 모든 주요 지표를 한 번에 확인하고, 문제점을 빠르게 파악할 수 있습니다.

실전 팁

💡 - 모델 평가 시 가장 먼저 사용하는 함수입니다

  • output_dict=True로 프로그래밍 방식으로 결과를 활용하세요
  • support 열을 확인해서 클래스 불균형 여부를 파악하세요

7. 평가 지표 선택 기준

김개발 씨는 이제 다양한 평가 지표를 알게 되었습니다. 그런데 새로운 프로젝트를 시작할 때마다 고민이 생겼습니다.

"이 프로젝트에는 어떤 지표를 중점적으로 봐야 하죠?" 박시니어 씨가 말했습니다. "상황에 따라 다릅니다.

도메인의 특성을 이해해야 해요."

평가 지표는 프로젝트의 목적과 도메인 특성에 따라 선택해야 합니다. 거짓 양성과 거짓 음성 중 무엇이 더 치명적인지, 클래스 분포는 어떤지, 비즈니스 목표는 무엇인지를 고려해야 합니다.

올바른 지표 선택이 성공적인 모델 개발의 시작입니다.

다음 코드를 살펴봅시다.

from sklearn.metrics import classification_report, confusion_matrix
import numpy as np

# 예제: 암 진단 시스템 (재현율 중요)
y_true_medical = [1, 1, 1, 0, 0, 1, 1, 0, 1, 0]
y_pred_medical = [1, 1, 0, 0, 0, 1, 1, 1, 1, 0]

print("=== 의료 진단 시스템 ===")
print(classification_report(y_true_medical, y_pred_medical))
cm = confusion_matrix(y_true_medical, y_pred_medical)
tn, fp, fn, tp = cm.ravel()
print(f"놓친 환자(FN): {fn}개 <- 가장 중요!")

# 예제: 스팸 필터 (정밀도 중요)
print("\n=== 스팸 필터 시스템 ===")
print("정밀도가 낮으면 중요한 메일을 놓칩니다")

김개발 씨는 이제 평가 지표의 전문가가 되었습니다. 정확도, 정밀도, 재현율, F1 Score를 모두 이해했습니다.

그런데 새로운 프로젝트를 시작할 때마다 같은 질문이 떠올랐습니다. "어떤 지표를 가장 중요하게 봐야 하지?" 박시니어 씨는 이것이 매우 중요한 질문이라고 말했습니다.

"평가 지표를 잘못 선택하면 방향을 잘못 잡게 됩니다. 재현율을 높여야 하는 상황에서 정밀도만 높이면 문제가 생기죠." 김개발 씨는 고개를 끄덕이며 노트를 펼쳤습니다.

평가 지표 선택의 첫 번째 기준은 오류의 비용입니다. 거짓 양성(False Positive)과 거짓 음성(False Negative) 중 어느 것이 더 치명적인가를 생각해야 합니다.

마치 보험에 가입할 때 어떤 위험을 더 우선적으로 보장받을지 선택하는 것과 비슷합니다. 의료 진단 시스템을 예로 들어보겠습니다.

암 진단 모델을 만든다고 가정합시다. 실제 환자를 건강하다고 판단하는 실수(FN)는 생명과 직결됩니다.

반대로 건강한 사람을 환자로 판단하는 실수(FP)는 추가 검사를 받는 불편함은 있지만 생명에는 지장이 없습니다. 따라서 재현율을 최우선으로 해야 합니다.

스팸 필터는 정반대입니다. 정상 메일을 스팸으로 분류하는 실수(FP)가 더 치명적입니다.

중요한 업무 메일이나 면접 일정 메일이 스팸함으로 가버리면 큰 문제가 발생합니다. 반면 스팸이 받은편지함에 들어오는 것(FN)은 불편하지만 치명적이지는 않습니다.

따라서 정밀도를 중요하게 봐야 합니다. 두 번째 기준은 클래스 분포입니다.

데이터가 균형 잡혀 있다면 정확도만으로도 충분합니다. 고양이와 강아지 사진을 분류하는데 각각 5000장씩 있다면 정확도가 좋은 지표입니다.

하지만 고양이 9000장, 강아지 1000장처럼 불균형하다면 정확도는 의미가 없습니다. 클래스 불균형이 심한 경우 어떻게 해야 할까요?

소수 클래스의 재현율정밀도를 집중적으로 봐야 합니다. 사기 거래 탐지의 경우 정상 거래가 99%, 사기가 1%입니다.

이때는 사기 클래스의 재현율이 가장 중요합니다. classification_report에서 클래스별 지표를 확인하는 것이 필수입니다.

세 번째 기준은 비즈니스 목표입니다. 추천 시스템을 만든다고 가정해봅시다.

사용자에게 상품을 추천하는데, 추천한 상품을 실제로 구매할 확률이 중요합니다. 이것은 정밀도에 해당합니다.

반면 사용자가 관심 있을 만한 모든 상품을 빠짐없이 찾아내는 것이 목표라면 재현율이 중요합니다. 검색 엔진의 경우 둘 다 중요합니다.

관련 있는 문서를 빠짐없이 찾아야 하고(재현율), 동시에 검색 결과에 관련 없는 문서가 많으면 안 됩니다(정밀도). 이런 경우 F1 Score를 사용하는 것이 적절합니다.

두 지표를 균형 있게 평가할 수 있습니다. 네 번째 기준은 사용자 경험입니다.

유튜브 추천 알고리즘을 생각해봅시다. 추천한 영상을 보지 않는 경우(FP)가 많으면 사용자가 추천을 신뢰하지 않게 됩니다.

반면 사용자가 좋아할 만한 영상을 추천하지 못하는 경우(FN)도 문제입니다. 둘 다 사용자 경험에 영향을 미치므로 F1 Score로 균형을 맞춰야 합니다.

위의 코드를 살펴보겠습니다. 의료 진단 시스템 예제에서는 False Negative를 강조했습니다.

실제 환자를 놓치는 것이 가장 치명적이기 때문입니다. 스팸 필터 예제에서는 정밀도의 중요성을 강조했습니다.

도메인에 따라 중요한 지표가 다르다는 것을 명확히 보여줍니다. 실무에서의 의사결정 과정을 알아보겠습니다.

프로젝트를 시작할 때 팀 전체가 모여 논의합니다. "FP와 FN 중 어느 것이 더 치명적인가?" "비즈니스적으로 어떤 오류가 더 큰 손실을 가져오는가?" "사용자 경험 측면에서 무엇이 더 중요한가?" 이런 질문들에 답하면서 핵심 지표를 결정합니다.

주의할 점이 있습니다. 한 가지 지표만 맹목적으로 추구하면 안 됩니다.

재현율을 100%로 만들려고 모든 것을 양성으로 분류하면 정밀도가 바닥을 칩니다. 쓸모없는 모델이 되는 것이죠.

항상 다른 지표들도 함께 모니터링하면서 균형을 유지해야 합니다. 김개발 씨는 이제 자신감이 생겼습니다.

새로운 프로젝트를 시작할 때마다 도메인 특성을 먼저 분석합니다. 오류의 비용을 따져보고, 클래스 분포를 확인하고, 비즈니스 목표를 명확히 합니다.

그런 다음 적절한 평가 지표를 선택합니다. 박시니어 씨가 마지막 조언을 했습니다.

"평가 지표는 도구일 뿐입니다. 도구를 선택하는 기준은 문제의 본질을 이해하는 것이에요.

숫자에만 집중하지 말고, 그 숫자가 실제로 무엇을 의미하는지 항상 생각하세요." 김개발 씨는 깊이 공감하며 고개를 끄덕였습니다. 평가 지표 선택을 제대로 하면 모델 개발의 방향이 명확해집니다.

도메인 지식과 비즈니스 이해를 바탕으로 적절한 지표를 선택하고, 균형을 유지하면서 최적의 모델을 만들어갈 수 있습니다.

실전 팁

💡 - 프로젝트 시작 전에 팀과 함께 핵심 지표를 합의하세요

  • FP와 FN의 비용을 정량적으로 계산해보면 도움이 됩니다
  • 한 가지 지표만 보지 말고 여러 지표를 함께 모니터링하세요

이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!

#Python#MachineLearning#ModelEvaluation#Classification#Metrics

댓글 (0)

댓글을 작성하려면 로그인이 필요합니다.