이미지 로딩 중...

머신러닝 완벽 가이드 초급부터 실전까지 - 슬라이드 1/11
A

AI Generated

2025. 11. 21. · 6 Views

머신러닝 완벽 가이드 초급부터 실전까지

머신러닝의 핵심 개념과 워크플로우를 실무 중심으로 배워봅니다. 데이터 준비부터 모델 배포까지, 실제 프로젝트에서 바로 활용할 수 있는 머신러닝 전 과정을 단계별로 이해하고 구현해봅니다.


목차

  1. 머신러닝이란
  2. 지도학습
  3. 비지도학습
  4. 데이터_전처리
  5. 특성_엔지니어링
  6. 모델_학습
  7. 모델_평가
  8. 과적합과_과소적합
  9. 교차_검증
  10. 하이퍼파라미터_튜닝

1. 머신러닝이란

시작하며

여러분이 스팸 메일을 필터링하거나, 넷플릭스에서 영화 추천을 받거나, 음성으로 스마트폰을 제어할 때 뒤에서 무엇이 작동하는지 궁금하신 적 있나요? 바로 머신러닝입니다.

전통적인 프로그래밍에서는 개발자가 모든 규칙을 하나하나 코드로 작성해야 했습니다. 예를 들어 스팸 메일을 걸러내려면 "제목에 '무료'가 들어있으면 스팸", "발신자가 수상하면 스팸" 같은 규칙을 수백 개 만들어야 했죠.

하지만 스팸은 계속 진화하고, 모든 경우를 예측하기란 불가능합니다. 머신러닝은 이런 문제를 완전히 다른 방식으로 접근합니다.

우리가 규칙을 만드는 대신, 컴퓨터에게 수많은 예시를 보여주고 스스로 패턴을 찾도록 합니다. 마치 아이가 사과를 여러 번 보고 "둥글고 빨간 과일"을 사과라고 배우는 것처럼요.

개요

간단히 말해서, 머신러닝은 컴퓨터가 데이터로부터 스스로 학습하여 문제를 해결하는 기술입니다. 실무에서는 규칙으로 정의하기 어려운 복잡한 문제들을 해결할 때 필수적입니다.

예를 들어, 고객이 다음 달에 서비스를 해지할지 예측하거나, 제품 사진만 보고 불량품을 찾아내거나, 주가를 예측하는 등 수많은 변수가 복잡하게 얽혀있는 문제들을 다룰 수 있습니다. 기존에는 전문가가 수년간의 경험으로 판단했다면, 이제는 머신러닝 모델이 수백만 건의 과거 데이터를 분석해서 패턴을 찾아냅니다.

더 빠르고, 더 정확하며, 24시간 쉬지 않고 작동합니다. 머신러닝의 핵심 특징은 세 가지입니다.

첫째, 데이터 기반입니다. 많은 양의 데이터가 있을수록 더 정확해집니다.

둘째, 자동화된 학습입니다. 한 번 설정하면 스스로 패턴을 찾습니다.

셋째, 지속적인 개선이 가능합니다. 새로운 데이터로 계속 업데이트할 수 있습니다.

이러한 특징들이 현대 비즈니스에서 머신러닝이 필수가 된 이유입니다.

코드 예제

# 간단한 머신러닝 예제: 집 가격 예측
from sklearn.linear_model import LinearRegression
import numpy as np

# 데이터 준비: 집 크기(평)와 가격(억원)
sizes = np.array([[20], [30], [40], [50], [60]])  # 집 크기
prices = np.array([2.0, 3.0, 4.0, 5.0, 6.0])      # 실제 가격

# 모델 생성 및 학습
model = LinearRegression()
model.fit(sizes, prices)  # 데이터로부터 패턴 학습

# 새로운 집 가격 예측
new_size = np.array([[45]])  # 45평 집
predicted_price = model.predict(new_size)
print(f"45평 집의 예상 가격: {predicted_price[0]:.1f}억원")

설명

이것이 하는 일: 이 코드는 집 크기와 가격의 관계를 학습하여, 새로운 집의 가격을 예측하는 간단한 머신러닝 모델을 만듭니다. 첫 번째로, 데이터 준비 단계에서 실제 집들의 크기와 가격 정보를 numpy 배열로 만듭니다.

여기서는 20평짜리 집이 2억원, 30평이 3억원 식으로 5개의 예시 데이터를 준비했습니다. 이것이 바로 머신러닝의 첫 단계인 '학습 데이터'입니다.

실제로는 수천, 수만 개의 데이터를 사용하지만, 개념을 이해하기 위해 간단히 5개만 사용했습니다. 그 다음으로, LinearRegression 모델을 생성하고 fit() 메서드로 학습을 시작합니다.

이 순간 모델은 데이터를 분석해서 "집 크기가 10평 증가하면 가격이 약 1억원 증가한다"는 패턴을 스스로 찾아냅니다. 우리는 이런 규칙을 직접 코딩하지 않았습니다.

모델이 데이터만 보고 알아서 발견한 것입니다. 마지막으로, predict() 메서드로 새로운 집의 가격을 예측합니다.

45평짜리 집의 데이터를 주면, 모델이 학습한 패턴을 바탕으로 "약 4.5억원"이라고 예측합니다. 이것이 바로 머신러닝의 핵심입니다.

한번 학습하면 이전에 본 적 없는 새로운 데이터에 대해서도 예측할 수 있습니다. 여러분이 이 코드를 사용하면 단 몇 줄로 복잡한 예측 시스템을 만들 수 있습니다.

실무에서는 더 많은 변수(방 개수, 위치, 건축 연도 등)를 추가하고, 더 많은 데이터로 학습시켜서 정확도를 높입니다. 같은 원리로 주가 예측, 고객 이탈 예측, 제품 추천 등 다양한 문제를 해결할 수 있습니다.

실전 팁

💡 처음 시작할 때는 데이터를 많이 모으는 것보다 깨끗한 데이터를 준비하는 것이 더 중요합니다. 잘못된 데이터로 학습하면 모델도 잘못된 패턴을 배웁니다.

💡 scikit-learn 라이브러리는 머신러닝 입문에 가장 좋습니다. 문서화가 잘 되어있고, 예제가 풍부하며, 대부분의 기본 알고리즘을 제공합니다.

💡 항상 데이터를 시각화해서 확인하세요. matplotlib이나 seaborn으로 그래프를 그려보면 데이터의 패턴과 이상치를 쉽게 발견할 수 있습니다.

💡 모델을 학습시킬 때는 전체 데이터의 80%는 학습용, 20%는 테스트용으로 나누세요. 학습에 사용한 데이터로 평가하면 실제 성능을 알 수 없습니다.

💡 복잡한 모델부터 시작하지 마세요. 간단한 선형 회귀나 로지스틱 회귀로 시작해서 기본을 다지고, 필요할 때만 복잡한 모델로 발전시키세요.


2. 지도학습

시작하며

여러분이 학교에서 수학 문제를 풀 때를 생각해보세요. 선생님이 문제와 정답을 함께 보여주고, 여러분은 그것을 보면서 "아, 이런 문제는 이렇게 푸는구나"라고 배우죠.

지도학습도 똑같습니다. 실무에서 가장 많이 사용되는 머신러닝 방식이 바로 지도학습입니다.

왜냐하면 우리는 대부분 "이런 입력에는 이런 결과가 나온다"는 과거 데이터를 가지고 있기 때문입니다. 예를 들어, 과거 고객 정보와 그들이 제품을 구매했는지 안 했는지의 기록이 있다면, 이것으로 새로운 고객의 구매 가능성을 예측할 수 있습니다.

지도학습은 정답이 있는 데이터로 학습한다는 점에서 가장 직관적이고 이해하기 쉬운 방법입니다. 그래서 머신러닝을 처음 시작한다면 지도학습부터 공부하는 것을 강력히 추천합니다.

개요

간단히 말해서, 지도학습은 입력 데이터와 그에 대응하는 정답(레이블)을 함께 주고 학습시키는 방법입니다. 실무에서 활용도가 매우 높은 이유는 대부분의 비즈니스 문제가 "예측"과 관련되어 있기 때문입니다.

고객이 이탈할지, 대출을 갚을지, 광고를 클릭할지, 제품이 고장날지 등 과거 데이터가 있는 모든 문제에 적용할 수 있습니다. 실제로 구글, 넷플릭스, 아마존 같은 기업들이 추천 시스템을 만들 때 지도학습을 핵심으로 사용합니다.

기존에는 전문가가 경험과 직관으로 판단했다면, 이제는 지도학습 모델이 수백만 건의 과거 사례를 학습해서 더 정확하게 예측합니다. 더 놀라운 것은 사람이 놓치는 미묘한 패턴까지 찾아낸다는 점입니다.

지도학습은 크게 두 가지로 나뉩니다. 첫째, 회귀(Regression)는 연속적인 숫자를 예측합니다(집값, 매출액, 온도 등).

둘째, 분류(Classification)는 카테고리를 예측합니다(스팸/정상, 구매/미구매, 질병/정상 등). 이 두 가지만 잘 이해해도 실무의 80%는 해결할 수 있습니다.

코드 예제

# 지도학습 예제: 고객 구매 예측 (분류)
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
import numpy as np

# 데이터 준비: [나이, 소득(만원)] -> 구매여부(1:구매, 0:미구매)
X = np.array([[25, 3000], [45, 7000], [35, 5000],
              [50, 8000], [23, 2500], [48, 7500]])
y = np.array([0, 1, 1, 1, 0, 1])  # 정답 레이블

# 데이터 분할: 학습용과 테스트용
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3)

# 모델 학습
model = RandomForestClassifier(n_estimators=100)
model.fit(X_train, y_train)  # 입력과 정답으로 학습

# 새로운 고객 예측
new_customer = np.array([[30, 4000]])  # 30세, 소득 4000만원
prediction = model.predict(new_customer)
probability = model.predict_proba(new_customer)
print(f"구매 예측: {'구매' if prediction[0] == 1 else '미구매'}")
print(f"구매 확률: {probability[0][1]*100:.1f}%")

설명

이것이 하는 일: 이 코드는 고객의 나이와 소득 정보를 바탕으로 제품 구매 여부를 예측하는 분류 모델을 만듭니다. 첫 번째로, 데이터를 준비합니다.

X는 입력 특성(나이와 소득)이고, y는 정답 레이블(구매 여부)입니다. 예를 들어 25세에 소득 3000만원인 고객은 구매하지 않았고(0), 45세에 소득 7000만원인 고객은 구매했습니다(1).

이렇게 입력과 정답을 쌍으로 만드는 것이 지도학습의 핵심입니다. 실제로는 수천 명의 고객 데이터를 사용합니다.

그 다음으로, train_test_split으로 데이터를 나눕니다. 70%는 학습용, 30%는 테스트용으로 분할합니다.

왜 나눌까요? 학습에 사용한 데이터로 평가하면 "시험문제를 미리 본 것"과 같아서 실제 성능을 알 수 없기 때문입니다.

RandomForestClassifier는 의사결정나무 100개를 조합한 강력한 모델입니다. fit() 메서드로 입력 데이터와 정답을 함께 학습시킵니다.

마지막으로, 학습이 끝난 모델로 새로운 고객의 구매 가능성을 예측합니다. predict()는 구매/미구매를 예측하고, predict_proba()는 구매할 확률을 퍼센트로 알려줍니다.

"30세, 소득 4000만원" 고객이 들어오면 모델이 과거 패턴을 바탕으로 "70% 확률로 구매할 것 같아요"라고 답합니다. 여러분이 이 코드를 사용하면 마케팅 타겟팅, 신용 평가, 의료 진단 등 정답이 있는 모든 예측 문제를 해결할 수 있습니다.

실무에서는 더 많은 특성(웹사이트 방문 횟수, 이전 구매 이력, 거주 지역 등)을 추가하고, 수만 건의 데이터로 학습시켜서 정확도를 90% 이상까지 높입니다. 이것이 바로 이커머스 기업들이 "맞춤형 추천"을 할 수 있는 비결입니다.

실전 팁

💡 분류 문제에서는 RandomForestClassifier나 XGBoost가 일반적으로 좋은 성능을 보입니다. 특별한 이유가 없다면 이 두 가지부터 시도해보세요.

💡 클래스 불균형 문제를 조심하세요. 만약 구매 고객이 5%, 미구매가 95%라면 모델이 제대로 학습하지 못합니다. SMOTE 같은 오버샘플링 기법을 사용하세요.

💡 정확도(Accuracy)만 보지 말고 정밀도(Precision), 재현율(Recall), F1-score도 함께 확인하세요. 특히 중요한 케이스를 놓치면 안 되는 경우(의료, 금융) 재현율이 중요합니다.

💡 피처 중요도(Feature Importance)를 확인하면 어떤 변수가 예측에 가장 큰 영향을 미치는지 알 수 있습니다. model.feature_importances_로 확인할 수 있습니다.

💡 실제 서비스에 적용하기 전에 항상 교차 검증(Cross-validation)으로 모델의 안정성을 확인하세요. 한 번의 테스트로는 운이 좋았을 수도 있습니다.


3. 비지도학습

시작하며

여러분이 방 청소를 할 때를 생각해보세요. 물건들을 종류별로 정리하죠.

책은 책대로, 옷은 옷대로, 장난감은 장난감대로. 누가 정답을 알려주지 않아도 비슷한 것끼리 자연스럽게 묶습니다.

비지도학습이 바로 이렇게 작동합니다. 실무에서 우리는 종종 정답이 없는 데이터를 다룹니다.

예를 들어, 수백만 명의 고객이 있는데 이들을 어떻게 그룹화해야 효과적인 마케팅을 할 수 있을까요? 정답이 미리 정해져 있지 않습니다.

이럴 때 비지도학습으로 고객들의 행동 패턴을 분석해서 자연스러운 그룹을 찾아낼 수 있습니다. 비지도학습의 가장 큰 장점은 사람이 생각하지 못한 새로운 인사이트를 발견할 수 있다는 점입니다.

우리가 "이런 그룹이 있을 거야"라고 미리 정하지 않아도, 데이터 자체가 숨겨진 패턴을 드러내줍니다.

개요

간단히 말해서, 비지도학습은 정답 없이 데이터만 주고 스스로 패턴이나 구조를 찾도록 하는 학습 방법입니다. 실무에서는 고객 세분화, 이상 탐지, 추천 시스템의 기반 등에 널리 사용됩니다.

예를 들어, 넷플릭스는 사용자들의 시청 패턴을 비지도학습으로 분석해서 비슷한 취향의 사용자 그룹을 찾아내고, 같은 그룹 사람들이 좋아하는 콘텐츠를 추천합니다. 신용카드 회사는 비정상적인 거래 패턴을 찾아내서 사기를 방지합니다.

기존에는 마케팅 팀이 "20대 남성", "30대 여성" 같은 인구통계학적 기준으로 고객을 나눴다면, 이제는 비지도학습이 실제 구매 행동, 웹사이트 탐색 패턴, 제품 선호도 등을 종합해서 훨씬 더 의미 있는 그룹을 자동으로 찾아냅니다. "주말 저녁에 프리미엄 제품을 구매하는 그룹", "할인 상품만 노리는 그룹" 같은 실용적인 세분화가 가능합니다.

비지도학습의 주요 기법은 세 가지입니다. 첫째, 군집화(Clustering)는 비슷한 데이터끼리 그룹으로 묶습니다.

둘째, 차원 축소(Dimensionality Reduction)는 복잡한 데이터를 간단하게 압축합니다. 셋째, 이상 탐지(Anomaly Detection)는 정상에서 벗어난 특이한 데이터를 찾습니다.

이 중 군집화가 가장 많이 사용되며, 실무에서 즉시 활용 가능한 인사이트를 제공합니다.

코드 예제

# 비지도학습 예제: 고객 세분화 (군집화)
from sklearn.cluster import KMeans
import numpy as np
import matplotlib.pyplot as plt

# 데이터 준비: [월 구매 횟수, 평균 구매 금액(만원)]
customers = np.array([[1, 5], [2, 8], [1, 4], [10, 50],
                      [11, 55], [9, 48], [2, 7], [10, 52],
                      [20, 100], [22, 105], [21, 98]])

# K-Means 군집화: 3개 그룹으로 나누기
kmeans = KMeans(n_clusters=3, random_state=42)
kmeans.fit(customers)  # 정답 없이 패턴만으로 학습

# 각 고객이 어느 그룹에 속하는지 확인
labels = kmeans.labels_
centers = kmeans.cluster_centers_  # 각 그룹의 중심점

print("고객 그룹 레이블:", labels)
print("그룹 중심점:\n", centers)

# 새로운 고객이 어느 그룹인지 예측
new_customer = np.array([[15, 70]])
group = kmeans.predict(new_customer)
print(f"새 고객은 그룹 {group[0]}에 속합니다")

설명

이것이 하는 일: 이 코드는 고객들의 구매 패턴을 분석해서 자동으로 비슷한 고객끼리 3개 그룹으로 묶어줍니다. 첫 번째로, 고객 데이터를 준비합니다.

각 고객의 월 구매 횟수와 평균 구매 금액을 배열로 만듭니다. 지도학습과 다른 점을 눈치채셨나요?

정답 레이블(y)이 없습니다. 우리는 이 고객들이 어떤 그룹에 속하는지 미리 정의하지 않습니다.

데이터만 주고 모델이 알아서 그룹을 찾도록 합니다. 그 다음으로, KMeans 알고리즘으로 군집화를 수행합니다.

n_clusters=3은 "3개 그룹으로 나눠줘"라는 의미입니다. fit() 메서드를 실행하면 모델이 데이터를 분석해서 비슷한 고객끼리 자동으로 묶습니다.

구매 횟수와 금액이 비슷한 사람들이 같은 그룹이 됩니다. 결과적으로 "가끔 적게 구매하는 그룹", "중간 구매 그룹", "자주 많이 구매하는 VIP 그룹" 같은 의미 있는 세분화가 자동으로 생깁니다.

마지막으로, labels로 각 고객이 어느 그룹에 속하는지 확인하고, cluster_centers_로 각 그룹의 대표 특성을 봅니다. 새로운 고객이 들어오면 predict()로 어느 그룹에 가장 가까운지 판단합니다.

"월 15회, 평균 70만원 구매" 고객이 들어오면 VIP 그룹으로 자동 분류되어, 그 그룹에 맞는 마케팅을 할 수 있습니다. 여러분이 이 코드를 사용하면 수천, 수만 명의 고객을 의미 있는 그룹으로 자동 분류할 수 있습니다.

실무에서는 더 많은 변수(접속 시간대, 관심 카테고리, 이탈 위험도 등)를 추가해서 더 정교한 세분화를 만듭니다. 이렇게 찾은 그룹별로 맞춤형 이메일, 쿠폰, 상품 추천을 보내면 전환율이 크게 올라갑니다.

실제로 많은 이커머스 기업들이 매출의 30~40%를 이런 세분화 마케팅에서 얻습니다.

실전 팁

💡 적절한 클러스터 개수를 찾기 위해 엘보우 방법(Elbow Method)을 사용하세요. 여러 개수로 테스트해보고 inertia 값의 변화를 그래프로 그려서 결정합니다.

💡 비지도학습 전에 반드시 데이터 정규화(Scaling)를 하세요. 변수들의 단위가 다르면(구매 횟수 vs 금액) 큰 값이 결과를 지배합니다. StandardScaler를 사용하세요.

💡 군집 결과를 시각화하면 훨씬 이해하기 쉽습니다. matplotlib나 seaborn으로 산점도를 그려서 각 클러스터가 얼마나 잘 분리되었는지 확인하세요.

💡 DBSCAN은 클러스터 개수를 미리 정하지 않아도 되고, 이상치도 자동으로 처리합니다. 복잡한 형태의 클러스터를 찾을 때 유용합니다.

💡 실무에서는 군집 결과에 비즈니스적인 이름을 붙이세요. "클러스터 0, 1, 2"보다 "절약형 고객", "중간 고객", "VIP 고객"이 팀원들이 이해하기 쉽습니다.


4. 데이터_전처리

시작하며

여러분이 요리를 할 때를 생각해보세요. 재료를 손질하는 시간이 실제 요리 시간보다 더 오래 걸리죠.

야채를 씻고, 껍질을 벗기고, 적당한 크기로 자르는 과정이 음식 맛을 좌우합니다. 머신러닝도 똑같습니다.

실무에서 데이터 과학자들은 시간의 70~80%를 데이터 전처리에 쏟는다고 합니다. 실제 세계의 데이터는 결측값이 있고, 이상치가 있고, 형식이 제각각이기 때문입니다.

이런 지저분한 데이터를 그대로 모델에 넣으면 쓰레기 같은 결과가 나옵니다. "쓰레기를 넣으면 쓰레기가 나온다(Garbage In, Garbage Out)"는 격언이 있을 정도입니다.

좋은 데이터 전처리는 모델의 성능을 2배, 3배로 향상시킬 수 있습니다. 최신 알고리즘을 쓰는 것보다 데이터를 깨끗하게 만드는 것이 훨씬 더 큰 영향을 미칩니다.

그래서 전처리를 잘하는 것이 머신러닝 실력의 핵심입니다.

개요

간단히 말해서, 데이터 전처리는 raw 데이터를 머신러닝 모델이 학습할 수 있는 깨끗한 형태로 변환하는 과정입니다. 실무에서 만나는 데이터는 완벽하지 않습니다.

고객 정보 데이터베이스에서 나이가 빠진 행이 있거나, 센서 데이터에서 이상하게 튀는 값이 있거나, 텍스트 데이터가 대소문자가 섞여있는 등 무수히 많은 문제가 있습니다. 이런 문제들을 해결하지 않으면 모델이 잘못된 패턴을 학습하거나, 아예 에러가 발생합니다.

기존에는 데이터 엔지니어들이 SQL이나 스크립트로 수동으로 처리했다면, 이제는 pandas와 scikit-learn 같은 라이브러리가 강력한 전처리 도구를 제공합니다. 몇 줄의 코드로 복잡한 전처리를 자동화할 수 있습니다.

데이터 전처리의 주요 작업은 다섯 가지입니다. 첫째, 결측값 처리(평균값으로 채우기, 행 삭제 등).

둘째, 이상치 제거(정상 범위를 벗어난 값 탐지). 셋째, 스케일링(변수들의 범위를 동일하게 조정).

넷째, 인코딩(텍스트 카테고리를 숫자로 변환). 다섯째, 데이터 분할(학습/검증/테스트 세트로 나누기).

이 다섯 가지만 제대로 해도 모델 성능이 크게 향상됩니다.

코드 예제

# 데이터 전처리 종합 예제
import pandas as pd
import numpy as np
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split

# 지저분한 실제 데이터 (결측값, 이상치 포함)
data = pd.DataFrame({
    'age': [25, np.nan, 35, 200, 45],  # 결측값, 이상치(200) 포함
    'income': [3000, 5000, np.nan, 7000, 8000],
    'city': ['Seoul', 'Busan', 'Seoul', 'Seoul', 'Busan']
})

# 1. 결측값 처리: 평균값으로 채우기
data['age'].fillna(data['age'].median(), inplace=True)
data['income'].fillna(data['income'].mean(), inplace=True)

# 2. 이상치 제거: IQR 방법
Q1 = data['age'].quantile(0.25)
Q3 = data['age'].quantile(0.75)
IQR = Q3 - Q1
data = data[(data['age'] >= Q1 - 1.5*IQR) & (data['age'] <= Q3 + 1.5*IQR)]

# 3. 범주형 데이터 인코딩
data['city_encoded'] = data['city'].map({'Seoul': 0, 'Busan': 1})

# 4. 스케일링
scaler = StandardScaler()
data[['age', 'income']] = scaler.fit_transform(data[['age', 'income']])

print("전처리된 데이터:\n", data)

설명

이것이 하는 일: 이 코드는 실제 세계의 지저분한 데이터를 깨끗하게 정리하여 머신러닝 모델에 바로 사용할 수 있도록 만듭니다. 첫 번째로, 결측값을 처리합니다.

fillna() 메서드로 빠진 값들을 채웁니다. 나이는 중앙값(median)으로, 소득은 평균값(mean)으로 채우는데, 왜 다를까요?

나이는 이상치(200같은 값)의 영향을 덜 받는 중앙값이 안전하고, 소득은 평균값이 더 대표성이 있기 때문입니다. 결측값을 그냥 두면 대부분의 머신러닝 알고리즘이 에러를 일으킵니다.

그 다음으로, 이상치를 제거합니다. 나이가 200세인 것은 명백한 입력 오류입니다.

IQR(사분위수 범위) 방법을 사용해서 통계적으로 비정상적인 값들을 자동으로 찾아냅니다. Q1(25%) - 1.5IQR부터 Q3(75%) + 1.5IQR 범위를 벗어나면 이상치로 판단합니다.

이런 값들이 모델을 혼란스럽게 만들기 때문에 제거하는 것이 좋습니다. 세 번째로, 범주형 데이터를 숫자로 변환합니다.

머신러닝 모델은 숫자만 이해하기 때문에 'Seoul', 'Busan' 같은 텍스트를 0, 1로 바꿔야 합니다. map() 함수로 간단히 매핑할 수 있습니다.

카테고리가 많으면 pd.get_dummies()나 OneHotEncoder를 사용합니다. 마지막으로, 스케일링을 합니다.

나이(0~100)와 소득(수천만원)은 범위가 완전히 다릅니다. StandardScaler는 모든 변수를 평균 0, 표준편차 1로 만들어서 공정하게 만듭니다.

스케일링을 안 하면 큰 값(소득)이 작은 값(나이)을 압도해서 모델이 나이를 무시하게 됩니다. 여러분이 이 전처리 기법들을 사용하면 모델의 정확도가 60%에서 85%로 향상되는 것을 경험할 수 있습니다.

실무에서는 더 복잡한 전처리(텍스트 정제, 날짜 처리, 파생 변수 생성 등)를 추가하지만, 이 네 가지가 가장 중요한 기본입니다. 좋은 요리는 좋은 재료 손질에서 시작되듯, 좋은 모델은 좋은 전처리에서 시작됩니다.

실전 팁

💡 결측값을 무조건 평균으로 채우지 마세요. 데이터의 의미를 고려하세요. 예를 들어, "구매 금액"의 결측은 "구매 안 함"을 의미할 수 있어서 0으로 채우는 게 맞을 수 있습니다.

💡 이상치를 무조건 제거하지 마세요. 때로는 이상치가 중요한 정보입니다. 신용카드 사기 탐지에서는 이상치가 바로 사기 거래일 수 있습니다. 도메인 지식이 중요합니다.

💡 스케일링은 트리 기반 모델(RandomForest, XGBoost)에는 필요 없지만, 선형 모델이나 신경망에는 필수입니다. 모델에 따라 달리 적용하세요.

💡 전처리 파이프라인을 sklearn.pipeline으로 만들어두면 새로운 데이터에 동일한 전처리를 자동으로 적용할 수 있어 편리합니다.

💡 항상 학습 데이터로 fit한 전처리 객체를 테스트 데이터에 transform해야 합니다. 테스트 데이터로 fit하면 데이터 누수(Data Leakage)가 발생해서 결과가 왜곡됩니다.


5. 특성_엔지니어링

시작하며

여러분이 수능 시험을 준비한다고 생각해보세요. 교과서 내용을 그대로 외우는 것보다, 핵심 개념을 뽑아내고, 연결고리를 찾고, 암기 팁을 만들면 훨씬 효율적이죠.

특성 엔지니어링도 비슷합니다. 실무에서 최고의 데이터 과학자와 평범한 데이터 과학자를 가르는 것이 바로 특성 엔지니어링 능력입니다.

같은 데이터, 같은 알고리즘을 써도 좋은 특성을 만들어내는 사람의 모델이 압도적으로 우수한 성능을 냅니다. 캐글 같은 머신러닝 대회에서도 우승자들의 비법은 대부분 창의적인 특성 엔지니어링입니다.

놀랍게도, 복잡한 알고리즘을 쓰는 것보다 좋은 특성을 만드는 것이 10배는 더 효과적입니다. "간단한 모델 + 좋은 특성"이 "복잡한 모델 + 나쁜 특성"을 압도합니다.

그래서 많은 전문가들이 "특성 엔지니어링이 머신러닝의 80%"라고 말합니다.

개요

간단히 말해서, 특성 엔지니어링은 원본 데이터로부터 모델 성능을 향상시킬 수 있는 새로운 의미 있는 변수를 만들어내는 과정입니다. 실무에서는 도메인 지식과 창의성이 결합되어야 합니다.

예를 들어, 신용카드 사기 탐지에서 단순히 "거래 금액"만 보는 것보다 "평소 평균 대비 거래 금액 비율", "최근 1시간 내 거래 횟수", "거래 시간대" 같은 파생 변수를 만들면 사기를 훨씬 잘 잡아냅니다. 이런 특성들은 데이터베이스에 없지만, 우리가 만들어낼 수 있습니다.

기존에는 데이터를 있는 그대로 모델에 넣었다면, 이제는 데이터 안에 숨어있는 의미 있는 패턴을 찾아서 명시적으로 드러내줍니다. 마치 원석을 가공해서 다이아몬드를 만드는 것처럼, raw 데이터를 가공해서 더 가치 있는 특성으로 만듭니다.

특성 엔지니어링의 주요 기법은 네 가지입니다. 첫째, 변환(로그, 제곱근 등으로 분포 개선).

둘째, 조합(기존 변수들을 더하거나 곱하기). 셋째, 집계(그룹별 평균, 합계 등 통계).

넷째, 시간 특성 추출(날짜에서 요일, 월, 시즌 등 추출). 이 네 가지를 상황에 맞게 적용하면 모델 성능이 비약적으로 향상됩니다.

코드 예제

# 특성 엔지니어링 예제: 쇼핑몰 데이터
import pandas as pd
import numpy as np

# 원본 데이터
data = pd.DataFrame({
    'user_id': [1, 1, 2, 2, 3],
    'purchase_amount': [50000, 30000, 100000, 80000, 20000],
    'purchase_date': ['2024-01-15', '2024-01-20', '2024-01-10',
                      '2024-01-25', '2024-01-05'],
    'product_category': ['Fashion', 'Electronics', 'Fashion', 'Fashion', 'Food']
})
data['purchase_date'] = pd.to_datetime(data['purchase_date'])

# 1. 집계 특성: 사용자별 총 구매 금액, 평균, 횟수
user_stats = data.groupby('user_id').agg({
    'purchase_amount': ['sum', 'mean', 'count']
}).reset_index()
user_stats.columns = ['user_id', 'total_amount', 'avg_amount', 'purchase_count']

# 2. 시간 특성: 요일, 월 추출
data['day_of_week'] = data['purchase_date'].dt.dayofweek  # 0=월요일
data['month'] = data['purchase_date'].dt.month

# 3. 조합 특성: 평균 대비 구매 금액 비율
data = data.merge(user_stats, on='user_id')
data['amount_vs_avg'] = data['purchase_amount'] / data['avg_amount']

# 4. 범주형 특성: 카테고리별 빈도 인코딩
category_counts = data['product_category'].value_counts()
data['category_frequency'] = data['product_category'].map(category_counts)

print("엔지니어링된 특성들:\n", data[['user_id', 'amount_vs_avg',
                                     'day_of_week', 'category_frequency']])

설명

이것이 하는 일: 이 코드는 단순한 쇼핑 거래 데이터에서 모델 성능을 크게 향상시킬 수 있는 다양한 파생 변수들을 생성합니다. 첫 번째로, 집계 특성을 만듭니다.

groupby()로 사용자별로 총 구매 금액, 평균, 횟수를 계산합니다. 왜 이게 유용할까요?

단일 거래만 보면 "5만원 구매"가 큰지 작은지 알 수 없지만, "이 사용자의 평균이 10만원인데 5만원을 샀다"는 정보는 "오늘은 적게 샀네"라는 의미를 담습니다. 이런 맥락 정보가 모델의 예측력을 크게 높입니다.

그 다음으로, 시간 특성을 추출합니다. 날짜 데이터에서 요일과 월을 뽑아냅니다.

실무에서 "주말에 구매가 많다", "12월에 구매가 급증한다" 같은 시간 패턴이 매우 중요합니다. dt.dayofweek는 월요일을 0, 일요일을 6으로 변환해서 모델이 요일 패턴을 학습할 수 있게 합니다.

시간, 분기, 공휴일 여부 등도 유용한 특성이 됩니다. 세 번째로, 조합 특성을 만듭니다.

"평균 대비 이번 구매 금액 비율"은 단순한 금액보다 훨씬 의미 있습니다. 비율이 2.0이면 "평소보다 2배 많이 샀다"는 뜻으로, 이상 거래나 특별한 이벤트를 나타낼 수 있습니다.

이런 상대적 지표가 절대값보다 패턴을 더 잘 포착합니다. 마지막으로, 빈도 인코딩을 적용합니다.

단순히 카테고리를 0, 1, 2로 바꾸는 것보다, "이 카테고리가 얼마나 자주 등장하는지"를 숫자로 만들면 더 많은 정보를 담습니다. Fashion이 3번, Food가 1번 등장한다는 정보가 인기도를 나타내서 모델에 도움이 됩니다.

여러분이 이런 특성 엔지니어링을 적용하면 모델 정확도가 보통 5~15% 향상됩니다. 실무에서는 수십 개의 파생 변수를 만들고, A/B 테스트로 어떤 특성이 가장 효과적인지 실험합니다.

좋은 특성 하나가 모델 알고리즘을 업그레이드하는 것보다 더 큰 임팩트를 만듭니다. 이것이 바로 데이터 과학자의 핵심 역량입니다.

실전 팁

💡 특성을 만들 때는 비즈니스 도메인 지식이 핵심입니다. 데이터만 보지 말고, 현업 담당자와 대화하며 "어떤 정보가 의사결정에 중요한지" 물어보세요.

💡 너무 많은 특성을 만들지 마세요. 특성이 많아질수록 과적합 위험이 커집니다. 특성 중요도(Feature Importance)를 확인해서 효과 없는 특성은 과감히 제거하세요.

💡 시간 관련 특성은 거의 모든 문제에서 유용합니다. 요일, 시간대, 월, 분기, 공휴일 여부, 주말 여부 등을 적극 활용하세요.

💡 타겟 인코딩(Target Encoding)은 강력하지만 조심해야 합니다. 각 카테고리의 평균 타겟값을 특성으로 쓰면 효과적이지만, 데이터 누수를 방지하려면 교차 검증과 함께 써야 합니다.

💡 자동 특성 엔지니어링 도구(Featuretools, AutoFeat)도 있지만, 처음에는 수동으로 만들어보면서 감을 익히세요. 도구가 만든 특성을 이해하지 못하면 디버깅이 어렵습니다.


6. 모델_학습

시작하며

여러분이 운전을 배울 때를 떠올려보세요. 처음엔 이론을 공부하고, 연습장에서 수십 번 연습하고, 피드백을 받으면서 점점 나아지죠.

머신러닝 모델 학습도 정확히 같은 과정입니다. 실무에서 모델 학습은 단순히 fit() 함수를 호출하는 것이 아닙니다.

적절한 알고리즘을 선택하고, 데이터를 올바르게 나누고, 학습 과정을 모니터링하고, 문제가 생기면 조정하는 전 과정을 포함합니다. 이 과정을 잘 이해해야 실전에서 제대로 작동하는 모델을 만들 수 있습니다.

많은 초보자들이 모델을 학습시킨 후 "왜 정확도가 낮지?"라고 고민합니다. 대부분의 문제는 데이터 준비나 파라미터 설정에 있습니다.

학습 과정을 제대로 이해하면 이런 문제들을 쉽게 진단하고 해결할 수 있습니다.

개요

간단히 말해서, 모델 학습은 준비된 데이터를 사용해서 머신러닝 알고리즘이 패턴을 찾도록 훈련시키는 과정입니다. 실무에서는 여러 알고리즘을 시도해보고 가장 좋은 것을 선택합니다.

선형 회귀부터 시작해서, 랜덤 포레스트, XGBoost, 신경망까지 단계적으로 복잡도를 높여갑니다. 처음부터 복잡한 모델을 쓰면 디버깅이 어렵고, 과적합 위험도 커집니다.

"간단한 것부터 시작해서 필요하면 복잡하게"가 원칙입니다. 기존에는 알고리즘의 수학적 원리를 완벽히 이해해야 했다면, 이제는 scikit-learn 같은 라이브러리가 쉬운 인터페이스를 제공합니다.

하지만 각 알고리즘이 언제 잘 작동하는지는 알아야 합니다. 예를 들어, 데이터가 선형이면 선형 회귀가, 비선형이면 트리 기반 모델이 적합합니다.

모델 학습의 핵심 단계는 다섯 가지입니다. 첫째, 데이터 분할(학습/검증/테스트).

둘째, 알고리즘 선택. 셋째, 모델 학습(fit).

넷째, 검증 데이터로 성능 확인. 다섯째, 문제 발견시 하이퍼파라미터 조정 후 재학습.

이 사이클을 반복하면서 점진적으로 성능을 개선합니다.

코드 예제

# 모델 학습 전체 과정
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score, classification_report
import numpy as np

# 1. 데이터 로드
iris = load_iris()
X, y = iris.data, iris.target

# 2. 데이터 분할: 학습(70%), 검증(15%), 테스트(15%)
X_train, X_temp, y_train, y_temp = train_test_split(X, y, test_size=0.3, random_state=42)
X_val, X_test, y_val, y_test = train_test_split(X_temp, y_temp, test_size=0.5, random_state=42)

# 3. 모델 생성 및 학습
model = RandomForestClassifier(n_estimators=100, max_depth=5, random_state=42)
model.fit(X_train, y_train)  # 학습 데이터로 패턴 학습

# 4. 성능 평가
train_acc = model.score(X_train, y_train)  # 학습 정확도
val_acc = model.score(X_val, y_val)        # 검증 정확도
test_acc = model.score(X_test, y_test)     # 최종 테스트 정확도

print(f"학습 정확도: {train_acc:.3f}")
print(f"검증 정확도: {val_acc:.3f}")
print(f"테스트 정확도: {test_acc:.3f}")

# 5. 예측 및 상세 리포트
y_pred = model.predict(X_test)
print("\n상세 분류 리포트:\n", classification_report(y_test, y_pred))

설명

이것이 하는 일: 이 코드는 머신러닝 모델을 제대로 학습시키고 평가하는 전체 과정을 보여줍니다. 실무에서 사용하는 베스트 프랙티스를 담고 있습니다.

첫 번째로, 데이터를 세 부분으로 나눕니다. 학습 데이터(70%)로 모델을 훈련시키고, 검증 데이터(15%)로 하이퍼파라미터를 튜닝하며, 테스트 데이터(15%)로 최종 성능을 평가합니다.

왜 세 개로 나눌까요? 테스트 데이터는 완전히 숨겨두고 마지막에만 사용해야 "실전 성능"을 정확히 알 수 있기 때문입니다.

검증 데이터로 계속 조정하다 보면 거기에 과적합될 수 있어서, 테스트는 별도로 보관합니다. 그 다음으로, RandomForestClassifier를 생성하고 학습시킵니다.

n_estimators=100은 의사결정나무 100개를 사용한다는 뜻이고, max_depth=5는 나무의 깊이를 제한해서 과적합을 방지합니다. fit() 메서드가 실행되는 순간 모델이 학습 데이터를 분석해서 패턴을 찾기 시작합니다.

데이터가 많으면 이 과정에 시간이 걸릴 수 있습니다. 세 번째로, 세 개의 정확도를 모두 확인합니다.

이것이 매우 중요합니다. 학습 정확도가 95%인데 검증 정확도가 70%라면 과적합입니다.

학습과 검증 정확도가 모두 낮으면 과소적합입니다. 학습, 검증, 테스트 정확도가 비슷하면 건강한 모델입니다.

이 세 숫자를 비교해서 모델 상태를 진단합니다. 마지막으로, classification_report로 상세한 성능 지표를 확인합니다.

정확도만으로는 부족합니다. 정밀도(Precision), 재현율(Recall), F1-score를 클래스별로 보면 어떤 클래스를 잘 못 맞추는지 알 수 있습니다.

예를 들어, 질병 진단에서는 "병이 있는데 없다고 하는 것"이 치명적이므로 재현율이 중요합니다. 여러분이 이 과정을 따라하면 체계적으로 모델을 개발할 수 있습니다.

실무에서는 여러 알고리즘을 시도하고, 파라미터를 조정하고, 교차 검증을 추가해서 더 정교하게 만듭니다. 하지만 이 기본 흐름은 변하지 않습니다.

데이터 과학자들이 매일 반복하는 바로 이 과정입니다.

실전 팁

💡 처음에는 기본 파라미터로 시작하세요. RandomForestClassifier()만 써도 됩니다. 기본 성능을 확인하고, 필요하면 그때 파라미터를 조정하세요.

💡 학습 시간이 너무 오래 걸리면 샘플링을 사용하세요. 전체 데이터의 10%로 먼저 실험하고, 방향이 맞으면 전체 데이터로 학습시키세요.

💡 random_state를 항상 설정하세요. 재현 가능성이 중요합니다. 같은 코드를 실행할 때마다 결과가 바뀌면 디버깅이 불가능합니다.

💡 여러 알고리즘을 빠르게 비교하려면 lazypredict 라이브러리를 사용해보세요. 한 번에 수십 개 알고리즘을 테스트해서 가장 좋은 것을 찾아줍니다.

💡 모델을 학습시킨 후 joblib이나 pickle로 저장하세요. 학습에 오래 걸린 모델을 매번 재학습하는 것은 비효율적입니다. 저장해두고 불러와서 사용하세요.


7. 모델_평가

시작하며

여러분이 시험을 본 후 점수를 확인하듯이, 머신러닝 모델도 얼마나 잘 작동하는지 평가해야 합니다. 하지만 단순히 "정답률"만 보면 큰 함정에 빠질 수 있습니다.

실무에서 "정확도 95% 모델"이라고 자랑하다가 실제로 배포했더니 엉망인 경우가 많습니다. 왜 그럴까요?

예를 들어, 사기 거래가 전체의 1%만 차지하는데 모델이 모든 거래를 "정상"이라고만 판단해도 정확도가 99%가 나옵니다. 하지만 사기를 하나도 못 잡았으니 완전히 쓸모없는 모델이죠.

올바른 평가 지표를 선택하는 것은 모델을 잘 만드는 것만큼 중요합니다. 문제의 특성과 비즈니스 목표에 맞는 지표를 써야 실전에서 제대로 작동하는 모델을 만들 수 있습니다.

개요

간단히 말해서, 모델 평가는 다양한 지표를 사용해서 모델이 실제로 얼마나 잘 작동하는지 정량적으로 측정하는 과정입니다. 실무에서는 문제 유형에 따라 다른 평가 지표를 사용합니다.

분류 문제에서는 정확도, 정밀도, 재현율, F1-score, ROC-AUC를 보고, 회귀 문제에서는 MAE, RMSE, R²를 봅니다. 각 지표가 무엇을 의미하는지 정확히 이해해야 올바른 판단을 할 수 있습니다.

기존에는 한 가지 지표만 보고 판단했다면, 이제는 여러 지표를 종합적으로 고려합니다. 예를 들어, 의료 진단 모델은 재현율(병을 놓치지 않기)이 중요하고, 스팸 필터는 정밀도(정상 메일을 스팸으로 분류하지 않기)가 중요합니다.

비즈니스 맥락이 지표 선택을 결정합니다. 평가의 핵심 원칙은 세 가지입니다.

첫째, 여러 지표를 함께 봅니다. 둘째, 테스트 데이터는 학습에 절대 사용하지 않습니다.

셋째, 혼동 행렬(Confusion Matrix)로 어떤 실수를 하는지 구체적으로 분석합니다. 이 세 가지만 지켜도 신뢰할 수 있는 평가를 할 수 있습니다.

코드 예제

# 모델 평가 종합 예제
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import (accuracy_score, precision_score, recall_score,
                             f1_score, confusion_matrix, classification_report, roc_auc_score)
import numpy as np

# 불균형 데이터 생성 (사기 탐지 시뮬레이션)
X, y = make_classification(n_samples=1000, n_classes=2, weights=[0.95, 0.05],
                           n_features=10, random_state=42)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3)

# 모델 학습
model = RandomForestClassifier(random_state=42)
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
y_proba = model.predict_proba(X_test)[:, 1]

# 다양한 평가 지표
print("=== 평가 지표 ===")
print(f"정확도: {accuracy_score(y_test, y_pred):.3f}")
print(f"정밀도: {precision_score(y_test, y_pred):.3f}")
print(f"재현율: {recall_score(y_test, y_pred):.3f}")
print(f"F1 점수: {f1_score(y_test, y_pred):.3f}")
print(f"ROC-AUC: {roc_auc_score(y_test, y_proba):.3f}")

# 혼동 행렬
print("\n=== 혼동 행렬 ===")
cm = confusion_matrix(y_test, y_pred)
print(f"True Negative: {cm[0,0]}, False Positive: {cm[0,1]}")
print(f"False Negative: {cm[1,0]}, True Positive: {cm[1,1]}")

설명

이것이 하는 일: 이 코드는 불균형 데이터(사기 거래 5%)에서 모델을 평가할 때 정확도만 보면 안 되는 이유를 보여주고, 올바른 평가 방법을 제시합니다. 첫 번째로, make_classification으로 현실적인 불균형 데이터를 만듭니다.

weights=[0.95, 0.05]는 정상이 95%, 사기가 5%라는 뜻입니다. 실제 사기 탐지, 의료 진단, 이탈 예측 같은 문제들이 이런 불균형을 가지고 있습니다.

이런 상황에서 정확도는 오해를 불러일으킵니다. 그 다음으로, 여러 평가 지표를 계산합니다.

정확도는 전체 중 맞춘 비율입니다. 정밀도는 "사기라고 예측한 것 중 실제 사기의 비율"이고, 재현율은 "실제 사기 중 잡아낸 비율"입니다.

정밀도가 높으면 오탐이 적고, 재현율이 높으면 놓치는 게 적습니다. F1-score는 둘의 조화평균으로 균형을 봅니다.

ROC-AUC는 임계값에 관계없이 전반적인 분류 능력을 측정합니다. 세 번째로, 혼동 행렬을 확인합니다.

이것이 가장 직관적입니다. True Negative(정상을 정상으로), False Positive(정상을 사기로), False Negative(사기를 정상으로), True Positive(사기를 사기로) 네 가지 경우를 모두 보여줍니다.

실무에서는 이 네 숫자를 보고 "우리 모델이 사기 100건 중 80건을 잡았고, 15건을 놓쳤네. 정상 거래 1000건 중 50건을 잘못 걸렀네"라고 구체적으로 이해합니다.

마지막으로, 비즈니스 관점에서 해석합니다. 만약 False Negative(사기를 놓침)의 비용이 크다면 재현율을 높여야 합니다.

임계값을 낮춰서 더 많은 거래를 의심하면 됩니다. 반대로 False Positive(정상을 사기로 오판)가 고객 불만을 일으킨다면 정밀도를 높여야 합니다.

이렇게 평가 지표와 비즈니스를 연결해서 생각해야 합니다. 여러분이 이런 다각적 평가를 하면 모델의 진짜 실력을 알 수 있습니다.

실무에서는 A/B 테스트로 실제 서비스에서의 성능도 확인합니다. 오프라인 평가 지표가 좋아도 온라인에서 안 좋을 수 있기 때문입니다.

하지만 오프라인 평가가 첫 번째 관문이고, 이것을 제대로 하는 것이 신뢰할 수 있는 모델의 시작입니다.

실전 팁

💡 불균형 데이터에서는 정확도를 절대 주 지표로 쓰지 마세요. F1-score나 ROC-AUC를 사용하세요. 또는 클래스별 성능을 따로 평가하세요.

💡 임계값(threshold)을 조정하면 정밀도와 재현율의 균형을 바꿀 수 있습니다. predict_proba()로 확률을 얻고, 0.5 대신 0.3이나 0.7 같은 값을 임계값으로 써보세요.

💡 혼동 행렬을 시각화하면 이해가 쉽습니다. seaborn의 heatmap으로 그려보세요. sns.heatmap(cm, annot=True, fmt='d')

💡 cross_val_score로 교차 검증 점수를 여러 번 계산하면 모델의 안정성을 확인할 수 있습니다. 한 번의 평가는 운이 좋았을 수도 있습니다.

💡 비즈니스 지표와 연결하세요. "재현율 5% 향상"이 "연간 사기 피해 10억원 감소"를 의미한다면 설득력이 커집니다. 경영진에게 보고할 때 필수입니다.


8. 과적합과_과소적합

시작하며

여러분이 시험 공부를 할 때를 생각해보세요. 기출문제만 달달 외우면 기출은 100점이지만 새로운 문제는 못 풀죠.

반대로 너무 대충 공부하면 기출도 못 풀고 새 문제도 못 풉니다. 머신러닝의 과적합과 과소적합이 바로 이것입니다.

실무에서 가장 흔한 문제가 과적합입니다. 모델이 학습 데이터에 너무 맞춰져서 새로운 데이터에는 잘 작동하지 않는 현상입니다.

개발할 때는 정확도가 95%인데 실제 서비스에 올리면 60%로 떨어지는 참사가 벌어집니다. 이것을 방지하는 것이 모델 개발의 핵심입니다.

과적합과 과소적합의 균형을 잡는 것이 머신러닝의 핵심 기술입니다. 이것을 "바이어스-분산 트레이드오프"라고 부릅니다.

너무 단순해도 안 되고, 너무 복잡해도 안 됩니다. 적절한 중간 지점을 찾는 것이 마스터의 경지입니다.

개요

간단히 말해서, 과적합은 학습 데이터에만 지나치게 맞춰진 상태고, 과소적합은 학습 데이터조차 제대로 학습하지 못한 상태입니다. 실무에서 과적합을 진단하는 가장 쉬운 방법은 학습 정확도와 검증 정확도를 비교하는 것입니다.

학습이 95%인데 검증이 70%라면 전형적인 과적합입니다. 모델이 학습 데이터의 노이즈까지 외워버려서 새로운 데이터에는 일반화가 안 된 상태입니다.

반대로 둘 다 60%로 낮으면 과소적합입니다. 기존에는 복잡한 수학으로 설명했다면, 이제는 직관적으로 이해할 수 있습니다.

과적합은 "암기"이고, 적절한 학습은 "이해"입니다. 우리는 모델이 데이터를 암기하는 게 아니라 패턴을 이해하길 원합니다.

그래야 처음 보는 데이터에도 잘 작동합니다. 과적합을 방지하는 방법은 다섯 가지입니다.

첫째, 더 많은 데이터를 모읍니다. 둘째, 모델을 단순화합니다(파라미터 수 줄이기).

셋째, 정규화(Regularization)를 적용합니다. 넷째, 드롭아웃이나 조기 종료를 사용합니다.

다섯째, 교차 검증으로 안정성을 확인합니다. 이 다섯 가지를 조합하면 대부분의 과적합을 해결할 수 있습니다.

코드 예제

# 과적합 vs 적절한 학습 비교
from sklearn.datasets import make_regression
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeRegressor
from sklearn.ensemble import RandomForestRegressor
from sklearn.metrics import mean_squared_error
import numpy as np

# 데이터 생성
X, y = make_regression(n_samples=100, n_features=10, noise=10, random_state=42)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3)

# 1. 과적합 모델: 제한 없는 깊은 트리
overfit_model = DecisionTreeRegressor(max_depth=None)  # 제한 없음
overfit_model.fit(X_train, y_train)

train_mse_overfit = mean_squared_error(y_train, overfit_model.predict(X_train))
test_mse_overfit = mean_squared_error(y_test, overfit_model.predict(X_test))

# 2. 적절한 모델: 깊이 제한 + 앙상블
good_model = RandomForestRegressor(max_depth=5, n_estimators=100, random_state=42)
good_model.fit(X_train, y_train)

train_mse_good = mean_squared_error(y_train, good_model.predict(X_train))
test_mse_good = mean_squared_error(y_test, good_model.predict(X_test))

# 3. 과소적합 모델: 너무 단순함
underfit_model = DecisionTreeRegressor(max_depth=1)  # 너무 얕음
underfit_model.fit(X_train, y_train)

train_mse_underfit = mean_squared_error(y_train, underfit_model.predict(X_train))
test_mse_underfit = mean_squared_error(y_test, underfit_model.predict(X_test))

# 결과 비교
print("=== 과적합 모델 ===")
print(f"학습 MSE: {train_mse_overfit:.2f}, 테스트 MSE: {test_mse_overfit:.2f} (차이 큼!)")
print("\n=== 적절한 모델 ===")
print(f"학습 MSE: {train_mse_good:.2f}, 테스트 MSE: {test_mse_good:.2f} (차이 작음)")
print("\n=== 과소적합 모델 ===")
print(f"학습 MSE: {train_mse_underfit:.2f}, 테스트 MSE: {test_mse_underfit:.2f} (둘 다 나쁨)")

설명

이것이 하는 일: 이 코드는 과적합, 과소적합, 적절한 학습의 세 가지 경우를 직접 만들어서 비교하여, 어떤 차이가 있는지 명확히 보여줍니다. 첫 번째로, 과적합 모델을 만듭니다.

max_depth=None인 의사결정나무는 제한이 없어서 학습 데이터를 완벽하게 외울 수 있습니다. 모든 데이터 포인트를 정확히 맞추려고 나무가 매우 복잡해집니다.

결과는? 학습 MSE는 거의 0에 가깝지만 테스트 MSE는 매우 높습니다.

학습 데이터의 노이즈까지 학습해서 새로운 데이터에는 엉뚱한 예측을 하게 됩니다. 그 다음으로, 적절한 모델을 만듭니다.

RandomForest에 max_depth=5로 나무 깊이를 제한하고, 100개의 나무를 앙상블합니다. 이렇게 하면 개별 나무가 과적합하는 것을 방지하고, 여러 나무의 평균을 내서 안정적인 예측을 합니다.

결과는? 학습 MSE와 테스트 MSE가 비슷합니다.

이것이 건강한 모델의 신호입니다. 학습 데이터에서도 잘 작동하고, 새 데이터에서도 비슷하게 작동합니다.

세 번째로, 과소적합 모델을 만듭니다. max_depth=1인 나무는 너무 단순해서 데이터의 복잡한 패턴을 포착할 수 없습니다.

단 한 번만 분기하므로 거의 평균값만 예측합니다. 결과는?

학습 MSE와 테스트 MSE가 둘 다 높습니다. 학습 데이터조차 제대로 학습하지 못한 상태입니다.

모델이 너무 단순해서 데이터의 진짜 패턴을 놓치고 있습니다. 여러분이 이 실험을 직접 해보면 세 가지 상태의 차이를 몸으로 느낄 수 있습니다.

실무에서는 학습 곡선(Learning Curve)을 그려서 시각적으로 확인합니다. 학습 데이터 크기를 늘려가며 학습/검증 성능의 변화를 보면 과적합인지 과소적합인지 명확히 알 수 있습니다.

그리고 그에 맞는 해결책을 적용합니다. 과적합이면 정규화를 강화하고, 과소적합이면 모델을 복잡하게 만듭니다.

실전 팁

💡 학습 곡선을 그려보세요. sklearn.model_selection.learning_curve를 사용하면 데이터 양에 따른 성능 변화를 볼 수 있습니다. 과적합/과소적합 진단에 최고입니다.

💡 과적합이 의심되면 데이터 증강(Data Augmentation)을 시도하세요. 이미지라면 회전/반전, 텍스트라면 동의어 치환 등으로 데이터를 인위적으로 늘릴 수 있습니다.

💡 정규화 파라미터(alpha, lambda)를 조정하세요. 선형 모델에서 Ridge(L2)나 Lasso(L1) 정규화를 쓰면 과적합을 효과적으로 방지합니다.

💡 조기 종료(Early Stopping)는 신경망 학습에서 필수입니다. 검증 성능이 더 이상 개선되지 않으면 학습을 멈춰서 과적합을 방지합니다.

💡 앙상블(배깅, 부스팅)은 과적합을 줄이는 강력한 방법입니다. 여러 모델의 예측을 결합하면 개별 모델의 과적합이 평균화되어 사라집니다.


9. 교차_검증

시작하며

여러분이 운전 실력을 평가받는다고 생각해보세요. 한 코스에서만 테스트하면 우연히 잘 할 수도 있습니다.

여러 다른 코스에서 테스트해야 진짜 실력을 알 수 있죠. 교차 검증이 바로 이것입니다.

실무에서 한 번의 학습/테스트 분할만으로는 모델을 신뢰할 수 없습니다. 운 좋게 쉬운 테스트 데이터를 만났을 수도 있고, 운 나쁘게 어려운 데이터를 만났을 수도 있습니다.

교차 검증은 여러 번 다른 방식으로 나눠서 평가함으로써 모델의 진짜 실력과 안정성을 확인합니다. 특히 데이터가 적을 때 교차 검증이 필수적입니다.

데이터를 학습/테스트로 한 번 나누면 이미 데이터가 부족한데 더 적어집니다. 교차 검증은 모든 데이터를 학습과 테스트에 돌아가면서 사용하므로 데이터를 효율적으로 활용할 수 있습니다.

개요

간단히 말해서, 교차 검증은 데이터를 여러 번 다르게 나눠서 평균 성능을 계산함으로써 모델의 실제 성능을 더 정확하게 평가하는 기법입니다. 실무에서 가장 많이 사용하는 것은 K-Fold 교차 검증입니다.

데이터를 K개(보통 5개나 10개)로 나누고, 한 조각씩 돌아가며 테스트로 쓰고 나머지로 학습합니다. 이렇게 K번 반복해서 K개의 점수를 얻고 평균을 냅니다.

단 한 번의 평가보다 훨씬 신뢰할 수 있습니다. 기존에는 수동으로 여러 번 나눠서 평가했다면, 이제는 scikit-learn의 cross_val_score 함수가 한 줄로 해결해줍니다.

내부적으로 데이터를 나누고, 학습하고, 평가하는 전 과정을 자동으로 여러 번 반복합니다. 교차 검증의 장점은 세 가지입니다.

첫째, 더 신뢰할 수 있는 성능 추정치를 얻습니다. 둘째, 모든 데이터를 학습과 평가에 활용합니다.

셋째, 모델의 분산(얼마나 들쑥날쑥한지)을 알 수 있습니다. 평균이 높아도 분산이 크면 불안정한 모델이므로 주의가 필요합니다.

코드 예제

# K-Fold 교차 검증 예제
from sklearn.datasets import load_wine
from sklearn.model_selection import cross_val_score, cross_validate, KFold
from sklearn.ensemble import RandomForestClassifier
import numpy as np

# 데이터 로드
wine = load_wine()
X, y = wine.data, wine.target

# 모델 생성
model = RandomForestClassifier(n_estimators=100, random_state=42)

# 1. 간단한 교차 검증: 정확도만
scores = cross_val_score(model, X, y, cv=5)  # 5-Fold
print("=== 5-Fold 교차 검증 정확도 ===")
print(f"각 Fold 점수: {scores}")
print(f"평균: {scores.mean():.3f}{scores.std():.3f})")

# 2. 상세한 교차 검증: 여러 지표
scoring = ['accuracy', 'precision_macro', 'recall_macro']
scores_detail = cross_validate(model, X, y, cv=5, scoring=scoring)
print("\n=== 상세 교차 검증 ===")
print(f"정확도 평균: {scores_detail['test_accuracy'].mean():.3f}")
print(f"정밀도 평균: {scores_detail['test_precision_macro'].mean():.3f}")
print(f"재현율 평균: {scores_detail['test_recall_macro'].mean():.3f}")

# 3. 커스텀 KFold
kfold = KFold(n_splits=10, shuffle=True, random_state=42)
scores_10fold = cross_val_score(model, X, y, cv=kfold)
print(f"\n=== 10-Fold 교차 검증 ===")
print(f"평균: {scores_10fold.mean():.3f}{scores_10fold.std():.3f})")

설명

이것이 하는 일: 이 코드는 교차 검증으로 모델을 더 신뢰할 수 있게 평가하는 여러 방법을 보여줍니다. 첫 번째로, cross_val_score로 가장 간단한 5-Fold 교차 검증을 수행합니다.

cv=5는 데이터를 5조각으로 나눈다는 의미입니다. 내부에서 자동으로 5번 반복합니다.

1번째: 1조각을 테스트, 나머지를 학습. 2번째: 2조각을 테스트, 나머지를 학습...

이렇게 5번 하면 모든 데이터가 한 번씩 테스트로 쓰입니다. 결과는 5개의 점수 배열로 나옵니다.

평균이 모델의 예상 성능이고, 표준편차가 안정성 지표입니다. 그 다음으로, cross_validate로 여러 지표를 동시에 평가합니다.

정확도만 보면 부족하니까 정밀도와 재현율도 함께 봅니다. scoring 파라미터에 리스트로 넣으면 한 번에 계산됩니다.

결과는 딕셔너리 형태로 나오며, 각 지표별로 5개의 점수가 있습니다. 이렇게 여러 각도에서 평가하면 모델의 전체적인 모습을 파악할 수 있습니다.

세 번째로, 커스텀 KFold 객체를 만들어서 10-Fold를 수행합니다. shuffle=True는 데이터를 섞어서 나누라는 의미입니다.

만약 데이터가 정렬되어 있으면 섞지 않으면 편향된 평가가 될 수 있습니다. 10-Fold는 5-Fold보다 더 많이 반복하므로 더 정확한 추정치를 주지만, 계산 시간이 2배 걸립니다.

데이터가 적으면 10-Fold, 충분하면 5-Fold가 일반적입니다. 여러분이 교차 검증을 사용하면 "우리 모델은 평균 92% 정확도, 표준편차 2%"라고 자신 있게 말할 수 있습니다.

실무에서는 하이퍼파라미터 튜닝할 때도 교차 검증을 사용합니다. GridSearchCV는 내부적으로 각 파라미터 조합을 교차 검증으로 평가해서 가장 좋은 것을 찾아줍니다.

이렇게 하면 테스트 데이터를 전혀 건드리지 않고도 최적의 모델을 찾을 수 있습니다.

실전 팁

💡 시계열 데이터는 일반 K-Fold를 쓰면 안 됩니다. 미래 데이터로 학습하고 과거 데이터로 테스트하는 이상한 상황이 됩니다. TimeSeriesSplit을 사용하세요.

💡 불균형 데이터에서는 StratifiedKFold를 사용하세요. 각 Fold에 클래스 비율을 동일하게 유지해서 공정한 평가를 합니다.

💡 데이터가 매우 적으면 Leave-One-Out 교차 검증(LOOCV)을 고려하세요. 샘플 하나씩 테스트로 쓰는 극단적인 방법이지만, 데이터를 최대한 활용할 수 있습니다.

💡 교차 검증은 시간이 오래 걸립니다. 개발 초기에는 간단한 분할로 빠르게 실험하고, 최종 평가할 때만 교차 검증을 사용하세요.

💡 교차 검증 결과의 표준편차가 크면 모델이 불안정하다는 신호입니다. 정규화를 강화하거나 앙상블을 사용해서 안정성을 높이세요.


10. 하이퍼파라미터_튜닝

시작하며

여러분이 라디오를 튠한다고 생각해보세요. 주파수를 조금씩 조정하면서 가장 선명한 소리를 찾죠.

머신러닝의 하이퍼파라미터 튜닝도 똑같습니다. 최적의 설정을 찾는 과정입니다.

실무에서 하이퍼파라미터 튜닝은 모델 성능을 10~20% 향상시킬 수 있는 강력한 기법입니다. 같은 알고리즘도 파라미터 설정에 따라 정확도가 70%에서 90%까지 달라질 수 있습니다.

많은 초보자들이 기본 설정만 쓰다가 "이 알고리즘은 별로네"라고 포기하는데, 제대로 튜닝하면 놀라운 성능을 낼 수 있습니다. 하지만 무작정 모든 조합을 시도하면 시간이 너무 오래 걸립니다.

랜덤 포레스트만 해도 수십 개의 파라미터가 있고, 각각 수십 개의 값이 가능합니다. 효율적인 탐색 전략이 필요합니다.

Grid Search, Random Search, Bayesian Optimization 같은 자동화된 방법들이 이 문제를 해결합니다.

개요

간단히 말해서, 하이퍼파라미터 튜닝은 모델의 설정값들을 체계적으로 바꿔가며 최고의 성능을 내는 조합을 찾는 과정입니다. 실무에서는 GridSearchCV나 RandomizedSearchCV를 주로 사용합니다.

GridSearchCV는 지정한 모든 조합을 빠짐없이 시도하고, RandomizedSearchCV는 무작위로 샘플링해서 빠르게 탐색합니다. 데이터가 크고 파라미터가 많으면 Random이 효율적입니다.

최근에는 Optuna 같은 Bayesian Optimization 라이브러리도 인기입니다. 기존에는 수동으로 파라미터를 하나씩 바꿔가며 실험했다면, 이제는 탐색 공간만 정의하면 알고리즘이 자동으로 최적값을 찾아줍니다.

심지어 교차 검증까지 내부에서 수행해서 과적합 없는 최적 파라미터를 리턴합니다. 튜닝의 핵심 전략은 세 가지입니다.

첫째, 중요한 파라미터부터 튜닝합니다(트리 깊이, 학습률 등). 둘째, 처음엔 넓은 범위로 대략적으로 찾고, 좁은 범위로 정밀하게 찾습니다.

셋째, 시간이 부족하면 RandomSearch로 빠르게 탐색합니다. 이렇게 단계적으로 접근하면 효율적으로 최적값을 찾을 수 있습니다.

코드 예제

# 하이퍼파라미터 튜닝 예제
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split, GridSearchCV, RandomizedSearchCV
from sklearn.ensemble import RandomForestClassifier
import numpy as np

# 데이터 로드
cancer = load_breast_cancer()
X_train, X_test, y_train, y_test = train_test_split(cancer.data, cancer.target, test_size=0.2)

# 1. Grid Search: 모든 조합 시도
param_grid = {
    'n_estimators': [50, 100, 200],
    'max_depth': [5, 10, 15, None],
    'min_samples_split': [2, 5, 10]
}

grid_search = GridSearchCV(
    RandomForestClassifier(random_state=42),
    param_grid,
    cv=5,  # 5-Fold 교차 검증
    scoring='accuracy',
    n_jobs=-1  # 모든 CPU 사용
)
grid_search.fit(X_train, y_train)

print("=== Grid Search 결과 ===")
print(f"최적 파라미터: {grid_search.best_params_}")
print(f"최적 점수: {grid_search.best_score_:.3f}")
print(f"테스트 정확도: {grid_search.score(X_test, y_test):.3f}")

# 2. Random Search: 무작위 샘플링
param_dist = {
    'n_estimators': [50, 100, 150, 200, 250],
    'max_depth': [5, 10, 15, 20, None],
    'min_samples_split': [2, 5, 10, 15],
    'min_samples_leaf': [1, 2, 4]
}

random_search = RandomizedSearchCV(
    RandomForestClassifier(random_state=42),
    param_dist,
    n_iter=20,  # 20개 조합만 시도
    cv=5,
    random_state=42,
    n_jobs=-1
)
random_search.fit(X_train, y_train)

print("\n=== Random Search 결과 ===")
print(f"최적 파라미터: {random_search.best_params_}")
print(f"최적 점수: {random_search.best_score_:.3f}")

설명

이것이 하는 일: 이 코드는 Grid Search와 Random Search를 사용해서 랜덤 포레스트의 최적 하이퍼파라미터를 자동으로 찾습니다. 첫 번째로, GridSearchCV로 모든 조합을 체계적으로 탐색합니다.

param_grid 딕셔너리에 탐색할 파라미터와 값들을 정의합니다. n_estimators(트리 개수), max_depth(나무 깊이), min_samples_split(분할 최소 샘플) 세 가지를 탐색합니다.

3 × 4 × 3 = 36개 조합이 생기고, cv=5이므로 36 × 5 = 180번 모델을 학습합니다. 시간이 오래 걸리지만 n_jobs=-1로 모든 CPU를 활용해서 병렬 처리합니다.

그 다음으로, fit()을 호출하면 내부에서 마법이 일어납니다. 각 파라미터 조합마다 5-Fold 교차 검증을 수행해서 평균 점수를 계산합니다.

모든 조합을 시도한 후 best_params_에 최고 점수를 낸 조합을 저장합니다. best_estimator_로 최적 모델 자체도 얻을 수 있어서 바로 예측에 사용할 수 있습니다.

세 번째로, RandomizedSearchCV로 더 효율적으로 탐색합니다. param_dist에 더 많은 파라미터와 값을 정의했지만, n_iter=20으로 무작위로 20개 조합만 시도합니다.

Grid Search는 전체 5 × 5 × 4 × 3 = 300개를 다 해야 하지만, Random은 20개만 하므로 15배 빠릅니다. 놀랍게도 연구에 따르면 Random Search가 Grid Search보다 더 효율적인 경우가 많습니다.

마지막으로, 최적 파라미터를 확인하고 테스트 데이터로 최종 평가합니다. 주의할 점은 best_score_는 교차 검증 점수이고, 실제 테스트 성능은 score()로 따로 확인해야 한다는 것입니다.

때로는 교차 검증 점수가 90%인데 테스트가 85%일 수 있습니다. 이것이 정상입니다.

여러분이 이런 자동 튜닝을 사용하면 수동 실험에 비해 10배 빠르고 더 좋은 결과를 얻을 수 있습니다. 실무에서는 Optuna나 Hyperopt 같은 고급 라이브러리로 Bayesian Optimization을 하면 더 적은 시도로 최적값을 찾습니다.

하지만 GridSearchCV와 RandomizedSearchCV만 잘 써도 대부분의 상황에서 충분합니다. 중요한 것은 파라미터가 무엇을 의미하는지 이해하고 적절한 탐색 범위를 설정하는 것입니다.

실전 팁

💡 처음엔 넓은 범위로 RandomSearch하고, 최적값 근처에서 좁은 범위로 GridSearch하세요. 이 2단계 접근이 가장 효율적입니다.

💡 가장 영향력 있는 파라미터부터 튜닝하세요. 랜덤 포레스트에서는 n_estimators, max_depth, min_samples_split이 핵심입니다. 나머지는 나중에 미세 조정하세요.

💡 튜닝 시간이 너무 오래 걸리면 데이터 샘플링을 고려하세요. 전체 데이터의 20%로 튜닝하고, 최적 파라미터로 전체 데이터를 학습시키면 됩니다.

💡 Optuna는 더 스마트합니다. 이전 시도 결과를 학습해서 다음 시도를 똑똑하게 선택합니다. 파라미터가 많고 시간이 부족하면 Optuna를 추천합니다.

💡 튜닝 결과를 시각화하세요. cv_results_를 DataFrame으로 만들어서 파라미터별 성능 변화를 그래프로 그리면 어떤 파라미터가 중요한지 한눈에 보입니다.


#Python#MachineLearning#DataScience#sklearn#ModelTraining#Data Science

댓글 (0)

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

함께 보면 좋은 카드 뉴스

데이터 증강과 정규화 완벽 가이드

머신러닝 모델의 성능을 극대화하는 핵심 기법인 데이터 증강과 정규화에 대해 알아봅니다. 실무에서 바로 활용할 수 있는 다양한 기법과 실전 예제를 통해 과적합을 방지하고 모델 성능을 향상시키는 방법을 배웁니다.

ResNet과 Skip Connection 완벽 가이드

딥러닝 모델이 깊어질수록 성능이 떨어지는 문제를 해결한 혁신적인 기법, ResNet과 Skip Connection을 초급자도 이해할 수 있도록 쉽게 설명합니다. 실제 구현 코드와 함께 배워보세요.

CNN 아키텍처 완벽 가이드 LeNet AlexNet VGGNet

컴퓨터 비전의 기초가 되는 세 가지 핵심 CNN 아키텍처를 배웁니다. 손글씨 인식부터 이미지 분류까지, 딥러닝의 발전 과정을 따라가며 각 모델의 구조와 특징을 실습 코드와 함께 이해합니다.

CNN 기초 Convolution과 Pooling 완벽 가이드

CNN의 핵심인 Convolution과 Pooling을 초급자도 쉽게 이해할 수 있도록 설명합니다. 이미지 인식의 원리부터 실제 코드 구현까지, 실무에서 바로 활용 가능한 내용을 담았습니다.

TensorFlow와 Keras 완벽 입문 가이드

머신러닝과 딥러닝의 세계로 들어가는 첫걸음! TensorFlow와 Keras 프레임워크를 처음 접하는 분들을 위한 친절한 가이드입니다. 실무에서 바로 활용할 수 있는 핵심 개념과 예제를 통해 AI 모델 개발의 기초를 탄탄히 다져보세요.