본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.
본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.
이미지 로딩 중...
AI Generated
2025. 12. 4. · 11 Views
나이브 베이즈와 회귀 알고리즘 완벽 가이드
머신러닝의 핵심 알고리즘인 나이브 베이즈와 회귀 분석을 초급 개발자도 이해할 수 있도록 풀어낸 가이드입니다. 확률 기반 분류부터 예측 모델까지, 실무에서 바로 활용할 수 있는 예제와 함께 배워봅니다.
목차
1. 베이즈 정리 이해
김개발 씨는 스팸 메일 필터를 개발하는 프로젝트에 투입되었습니다. "이 메일이 스팸일 확률을 어떻게 계산하죠?" 선배 박시니어 씨가 웃으며 대답했습니다.
"베이즈 정리를 알면 됩니다."
베이즈 정리는 새로운 증거가 주어졌을 때 기존 믿음을 업데이트하는 방법입니다. 마치 탐정이 새로운 단서를 발견할 때마다 범인에 대한 추측을 수정하는 것과 같습니다.
이 정리를 이해하면 불확실한 상황에서 합리적인 판단을 내릴 수 있습니다.
다음 코드를 살펴봅시다.
# 베이즈 정리: P(A|B) = P(B|A) * P(A) / P(B)
# 스팸 메일 확률 계산 예제
# 사전 확률 (Prior)
p_spam = 0.3 # 전체 메일 중 스팸 비율
p_ham = 0.7 # 전체 메일 중 정상 메일 비율
# 우도 (Likelihood): "무료"라는 단어가 포함될 확률
p_free_given_spam = 0.8 # 스팸에 "무료" 포함 확률
p_free_given_ham = 0.1 # 정상 메일에 "무료" 포함 확률
# 전체 확률
p_free = p_free_given_spam * p_spam + p_free_given_ham * p_ham
# 베이즈 정리 적용: "무료"가 있을 때 스팸일 확률
p_spam_given_free = (p_free_given_spam * p_spam) / p_free
print(f"'무료'가 포함된 메일이 스팸일 확률: {p_spam_given_free:.2%}")
김개발 씨는 입사 6개월 차 주니어 개발자입니다. 회사에서 스팸 필터 개선 프로젝트를 맡게 되었는데, 기존 규칙 기반 필터로는 한계가 있었습니다.
특정 단어가 포함되면 무조건 스팸으로 처리하는 방식은 너무 단순했기 때문입니다. 선배 박시니어 씨가 화이트보드 앞에 섰습니다.
"김개발 씨, 베이즈 정리를 들어본 적 있나요? 18세기 영국의 토마스 베이즈 목사가 만든 정리인데, 머신러닝의 기초가 됩니다." 그렇다면 베이즈 정리란 정확히 무엇일까요?
쉽게 비유하자면, 베이즈 정리는 마치 의사가 진단을 내리는 과정과 같습니다. 환자가 "기침을 합니다"라고 말했을 때, 의사는 기존에 알고 있던 감기 환자의 비율과 감기 환자가 기침을 하는 비율을 종합해서 "이 환자가 감기일 확률"을 계산합니다.
새로운 증상이라는 증거가 주어지면 진단 확률을 업데이트하는 것입니다. 베이즈 정리의 핵심 공식은 P(A|B) = P(B|A) * P(A) / P(B) 입니다.
여기서 P(A|B)는 B가 발생했을 때 A가 일어날 확률, 즉 사후 확률입니다. P(A)는 A가 발생할 기본 확률인 사전 확률이고, P(B|A)는 A가 참일 때 B가 발생할 확률인 우도입니다.
베이즈 정리가 없던 시절에는 어땠을까요? 개발자들은 단순한 규칙에 의존해야 했습니다.
"무료"라는 단어가 있으면 무조건 스팸, "안녕하세요"가 있으면 무조건 정상. 이런 흑백논리는 수많은 오탐을 만들어냈습니다.
위의 코드를 살펴보겠습니다. 먼저 사전 확률을 설정합니다.
전체 메일 중 30%가 스팸이라고 가정했습니다. 그 다음 우도를 정의합니다.
스팸 메일의 80%에는 "무료"라는 단어가 포함되어 있고, 정상 메일에서는 10%만 포함되어 있다고 설정했습니다. 핵심은 마지막 계산입니다.
"무료"라는 단어가 포함된 메일을 받았을 때, 그 메일이 스팸일 확률은 약 77%로 계산됩니다. 단순히 "무료가 있으니까 스팸"이 아니라, 확률적으로 판단하는 것입니다.
실제 현업에서는 이 원리를 더 복잡하게 적용합니다. 여러 단어의 출현 여부를 조합하고, 실제 데이터로 확률을 학습시킵니다.
이메일 서비스, 추천 시스템, 의료 진단 등 다양한 분야에서 베이즈 정리가 활용됩니다. 다시 김개발 씨의 이야기로 돌아가 봅시다.
박시니어 씨의 설명을 들은 김개발 씨는 고개를 끄덕였습니다. "아, 그래서 확률을 계산하는 거군요!" 베이즈 정리를 이해하면 불확실한 세상에서 데이터 기반의 합리적인 판단을 내릴 수 있습니다.
실전 팁
💡 - 사전 확률은 도메인 지식이나 과거 데이터에서 추정합니다
- 우도는 실제 데이터를 분석하여 계산해야 정확한 결과를 얻습니다
2. 조건부 확률 계산
김개발 씨는 베이즈 정리를 배우고 나서 한 가지 의문이 생겼습니다. "그런데 조건부 확률이 정확히 뭔가요?" 박시니어 씨가 커피를 한 모금 마시며 말했습니다.
"좋은 질문이에요. 이게 모든 확률 기반 머신러닝의 기초거든요."
조건부 확률은 어떤 사건이 이미 발생했다는 조건 하에서 다른 사건이 일어날 확률입니다. 마치 비가 오는 날에 우산을 가져온 사람의 비율을 구하는 것과 같습니다.
이 개념을 제대로 이해하면 데이터 간의 관계를 확률적으로 파악할 수 있습니다.
다음 코드를 살펴봅시다.
# 조건부 확률 계산 예제
# P(A|B) = P(A와 B가 동시에 발생) / P(B)
# 데이터: 100명의 고객 구매 패턴
total_customers = 100
bought_laptop = 30 # 노트북 구매자
bought_mouse = 40 # 마우스 구매자
bought_both = 20 # 둘 다 구매한 사람
# 기본 확률
p_laptop = bought_laptop / total_customers
p_mouse = bought_mouse / total_customers
p_both = bought_both / total_customers
# 조건부 확률: 노트북을 산 사람이 마우스도 살 확률
p_mouse_given_laptop = p_both / p_laptop
print(f"노트북 구매자가 마우스도 살 확률: {p_mouse_given_laptop:.2%}")
# 반대로: 마우스를 산 사람이 노트북도 살 확률
p_laptop_given_mouse = p_both / p_mouse
print(f"마우스 구매자가 노트북도 살 확률: {p_laptop_given_mouse:.2%}")
김개발 씨는 이커머스 회사의 추천 시스템 팀에서 일하고 있습니다. 어느 날 기획팀에서 요청이 왔습니다.
"노트북을 산 고객에게 어떤 상품을 추천하면 좋을까요?" 이 질문에 답하려면 조건부 확률을 이해해야 합니다. 박시니어 씨가 칠판에 벤 다이어그램을 그렸습니다.
"조건부 확률은 전체 세상을 특정 조건으로 좁혀서 보는 것입니다." 조건부 확률이란 무엇일까요? 비유하자면, 학교 전체 학생 중 안경을 쓴 학생의 비율과 수학 동아리 학생 중 안경을 쓴 학생의 비율은 다를 수 있습니다.
후자가 바로 조건부 확률입니다. "수학 동아리에 속한다"는 조건이 주어진 상태에서의 확률이기 때문입니다.
수학적으로 조건부 확률은 P(A|B) = P(A ∩ B) / P(B) 로 정의됩니다. 분모의 P(B)는 조건이 되는 사건의 확률이고, 분자의 P(A ∩ B)는 두 사건이 동시에 일어날 확률입니다.
결국 B라는 조건 안에서 A가 차지하는 비율을 구하는 것입니다. 조건부 확률의 중요한 특성은 비대칭성입니다.
P(A|B)와 P(B|A)는 일반적으로 다릅니다. 코드를 보면, 노트북 구매자가 마우스를 살 확률은 약 67%인 반면, 마우스 구매자가 노트북을 살 확률은 50%입니다.
조건이 바뀌면 결과도 달라지는 것입니다. 이 개념을 실무에서 어떻게 활용할까요?
추천 시스템에서는 "상품 A를 산 고객이 상품 B도 살 확률"을 계산하여 연관 상품을 추천합니다. 아마존의 "이 상품을 구매한 고객이 함께 구매한 상품" 기능이 대표적인 예입니다.
주의할 점도 있습니다. 조건부 확률과 인과관계를 혼동하면 안 됩니다.
"노트북을 사면 마우스를 산다"가 아니라 "노트북을 산 사람들 중 많은 비율이 마우스도 샀다"는 의미입니다. 상관관계와 인과관계의 차이를 명심해야 합니다.
김개발 씨는 조건부 확률을 이해하고 나서 추천 시스템의 원리가 눈에 들어오기 시작했습니다. "결국 데이터에서 조건부 확률을 계산하고 활용하는 거군요!" 이제 베이즈 정리의 각 항목이 무엇을 의미하는지 더 명확해졌습니다.
실전 팁
💡 - P(A|B)와 P(B|A)는 다르다는 점을 항상 기억하세요
- 실무에서는 충분한 데이터가 있어야 신뢰할 수 있는 조건부 확률을 계산할 수 있습니다
3. 나이브 베이즈 알고리즘
김개발 씨가 드디어 실제 머신러닝 모델을 구현할 차례가 되었습니다. "베이즈 정리는 이해했는데, 실제로 어떻게 분류기를 만들죠?" 박시니어 씨가 키보드를 두드리며 말했습니다.
"나이브 베이즈를 써봅시다. 단순하지만 강력해요."
나이브 베이즈는 베이즈 정리를 기반으로 한 분류 알고리즘입니다. "나이브(순진한)"라는 이름은 모든 특성이 서로 독립적이라고 가정하기 때문입니다.
마치 각 단서를 개별적으로 평가하는 단순한 탐정처럼 동작하지만, 놀라울 정도로 좋은 성능을 보여줍니다.
다음 코드를 살펴봅시다.
from sklearn.naive_bayes import MultinomialNB
from sklearn.feature_extraction.text import CountVectorizer
# 학습 데이터: 메일 내용과 레이블
emails = [
"무료 이벤트 당첨 축하드립니다",
"할인 쿠폰 지금 받으세요",
"회의 일정 공유드립니다",
"프로젝트 진행 상황 보고",
"100% 무료 경품 행사",
"내일 미팅 시간 변경"
]
labels = [1, 1, 0, 0, 1, 0] # 1: 스팸, 0: 정상
# 텍스트를 숫자 벡터로 변환
vectorizer = CountVectorizer()
X = vectorizer.fit_transform(emails)
# 나이브 베이즈 모델 학습
model = MultinomialNB()
model.fit(X, labels)
# 새로운 메일 분류
new_email = ["무료 할인 이벤트 안내"]
new_X = vectorizer.transform(new_email)
prediction = model.predict(new_X)
print(f"예측 결과: {'스팸' if prediction[0] == 1 else '정상'}")
김개발 씨는 이론을 배우고 나서 직접 코드를 작성해보기로 했습니다. 스팸 필터의 첫 번째 버전을 만들어야 하는데, 어떤 알고리즘을 선택해야 할지 고민이었습니다.
박시니어 씨가 제안했습니다. "처음에는 나이브 베이즈로 시작하는 게 좋아요.
구현이 간단하고, 학습 데이터가 적어도 잘 동작하거든요." 나이브 베이즈의 "나이브"는 왜 붙은 걸까요? 이 알고리즘은 모든 특성이 서로 독립적이라고 가정합니다.
예를 들어, "무료"라는 단어가 나올 확률과 "이벤트"라는 단어가 나올 확률이 서로 영향을 주지 않는다고 가정하는 것입니다. 현실에서는 이 가정이 거의 맞지 않지만, 실제로는 놀라울 정도로 잘 동작합니다.
비유하자면, 나이브 베이즈는 마치 투표 시스템과 같습니다. 각 단어가 "이 메일이 스팸이다"에 한 표씩 던지고, 최종적으로 표를 합산해서 다수결로 결정합니다.
"무료"는 스팸에 강하게 투표하고, "회의"는 정상에 투표하는 식입니다. 코드를 살펴보겠습니다.
먼저 CountVectorizer로 텍스트를 숫자 벡터로 변환합니다. 각 단어의 출현 횟수를 세어서 벡터로 만드는 것입니다.
그 다음 MultinomialNB 모델을 생성하고 데이터를 학습시킵니다. fit 메서드 한 줄로 학습이 완료됩니다.
학습이 끝나면 새로운 메일을 분류할 수 있습니다. "무료 할인 이벤트 안내"라는 메일을 입력하면, 모델은 각 단어의 스팸 확률을 계산하고 종합하여 최종 판단을 내립니다.
나이브 베이즈는 크게 세 가지 종류가 있습니다. 텍스트처럼 단어 빈도를 다룰 때는 MultinomialNB, 이진 특성에는 BernoulliNB, 연속적인 수치 데이터에는 GaussianNB를 사용합니다.
실무에서 나이브 베이즈는 텍스트 분류의 기본 베이스라인으로 자주 사용됩니다. 빠른 학습 속도, 적은 메모리 사용, 적은 데이터로도 괜찮은 성능을 보이기 때문입니다.
물론 더 복잡한 패턴을 잡아내려면 딥러닝 모델이 필요하지만, 첫 번째 시도로는 나이브 베이즈가 훌륭한 선택입니다. 김개발 씨는 첫 번째 스팸 필터 모델을 완성했습니다.
"이렇게 간단한데 실제로 잘 동작하다니!" 나이브 베이즈의 단순함과 효과에 감탄하며, 다음 단계인 모델 개선을 계획하기 시작했습니다.
실전 팁
💡 - 텍스트 분류에서는 MultinomialNB, 수치 데이터에서는 GaussianNB를 사용하세요
- 학습 데이터가 적을 때 특히 효과적인 알고리즘입니다
4. 단순 선형 회귀
프로젝트가 끝나고 김개발 씨는 새로운 팀으로 이동했습니다. 이번에는 부동산 가격 예측 서비스를 개발하는 팀입니다.
"집 크기로 가격을 예측하려면 어떻게 해야 하죠?" 박시니어 씨가 답했습니다. "회귀 분석을 배워야 할 때가 됐네요."
단순 선형 회귀는 하나의 독립 변수로 종속 변수를 예측하는 가장 기본적인 회귀 모델입니다. 마치 두 점을 연결하는 최적의 직선을 찾는 것과 같습니다.
이 직선을 통해 새로운 값을 예측할 수 있습니다.
다음 코드를 살펴봅시다.
import numpy as np
from sklearn.linear_model import LinearRegression
import matplotlib.pyplot as plt
# 데이터: 집 크기(평)와 가격(억원)
house_sizes = np.array([20, 25, 30, 35, 40, 45, 50]).reshape(-1, 1)
prices = np.array([2.0, 2.5, 3.2, 3.8, 4.5, 5.0, 5.8])
# 선형 회귀 모델 학습
model = LinearRegression()
model.fit(house_sizes, prices)
# 기울기와 절편 확인
print(f"기울기 (coefficient): {model.coef_[0]:.4f}")
print(f"절편 (intercept): {model.intercept_:.4f}")
# 새로운 집 가격 예측
new_size = np.array([[32]])
predicted_price = model.predict(new_size)
print(f"32평 집의 예측 가격: {predicted_price[0]:.2f}억원")
김개발 씨는 부동산 팀에 합류한 첫 날, 흥미로운 데이터를 받았습니다. 지난 1년간 거래된 아파트의 크기와 가격 정보였습니다.
"이 데이터로 새 매물의 적정 가격을 예측할 수 있을까요?" 박시니어 씨가 그래프를 그렸습니다. X축에 집 크기, Y축에 가격을 표시하니 점들이 대각선 방향으로 분포했습니다.
"이 점들 사이를 가장 잘 지나가는 직선을 찾으면 됩니다." 단순 선형 회귀는 이름 그대로 가장 단순한 형태의 회귀 분석입니다. 수학적으로는 y = ax + b 라는 1차 함수를 찾는 것입니다.
여기서 a는 기울기(coefficient)이고, b는 절편(intercept)입니다. 비유하자면, 집 크기와 가격의 관계를 표현하는 "공식"을 만드는 것입니다.
"집이 1평 커질 때마다 가격이 0.12억원 오른다"는 규칙을 데이터에서 발견하는 것이죠. 그렇다면 최적의 직선은 어떻게 찾을까요?
최소제곱법(Least Squares)을 사용합니다. 각 데이터 점과 직선 사이의 거리(오차)를 계산하고, 이 오차의 제곱합이 최소가 되는 직선을 찾습니다.
제곱을 사용하는 이유는 양수와 음수 오차가 상쇄되지 않도록 하기 위해서입니다. 코드를 살펴보면, sklearn의 LinearRegression 클래스를 사용합니다.
fit 메서드에 데이터를 넣으면 최적의 기울기와 절편을 자동으로 계산해줍니다. 결과를 보면 기울기가 약 0.12로, 1평당 0.12억원(1,200만원)씩 가격이 증가한다는 의미입니다.
학습된 모델로 예측도 간단합니다. 32평짜리 집의 가격을 알고 싶다면 predict 메서드에 32를 넣으면 됩니다.
모델은 y = 0.12 * 32 + b를 계산하여 예측값을 반환합니다. 실무에서 주의할 점은 외삽의 위험성입니다.
학습 데이터가 20~50평 범위였다면, 100평짜리 집의 가격을 예측하는 것은 위험합니다. 데이터 범위 밖에서는 선형 관계가 성립하지 않을 수 있기 때문입니다.
김개발 씨는 첫 번째 예측 모델을 완성했습니다. "생각보다 직관적이네요!" 하지만 박시니어 씨가 덧붙였습니다.
"집 가격은 크기만으로 결정되지 않아요. 위치, 층수, 연식도 고려해야죠.
그게 바로 다중 회귀입니다."
실전 팁
💡 - 데이터를 시각화해서 선형 관계가 있는지 먼저 확인하세요
- 학습 데이터 범위 밖의 예측은 신중하게 해석해야 합니다
5. 다중 회귀 분석
김개발 씨의 모델이 프로덕션에 배포되었지만, 예측 정확도가 기대에 못 미쳤습니다. "집 크기만으로는 한계가 있어요." 박시니어 씨가 말했습니다.
"역세권인지, 몇 층인지, 이런 변수들도 고려해야 해요."
다중 회귀는 여러 개의 독립 변수로 종속 변수를 예측하는 확장된 회귀 모델입니다. 마치 여러 요인을 종합적으로 고려하여 판단을 내리는 것과 같습니다.
현실 세계의 복잡한 관계를 모델링할 수 있습니다.
다음 코드를 살펴봅시다.
import numpy as np
from sklearn.linear_model import LinearRegression
import pandas as pd
# 데이터: 크기(평), 역까지 거리(분), 층수, 연식(년), 가격(억원)
data = {
'size': [25, 30, 35, 28, 40, 32, 38],
'distance_to_station': [5, 10, 3, 15, 7, 2, 8],
'floor': [3, 10, 15, 5, 20, 12, 8],
'age': [10, 5, 2, 15, 3, 1, 7],
'price': [3.5, 3.8, 5.2, 2.9, 5.5, 4.8, 4.5]
}
df = pd.DataFrame(data)
# 특성과 타겟 분리
X = df[['size', 'distance_to_station', 'floor', 'age']]
y = df['price']
# 다중 회귀 모델 학습
model = LinearRegression()
model.fit(X, y)
# 각 변수의 영향력 확인
for feature, coef in zip(X.columns, model.coef_):
print(f"{feature}: {coef:.4f}")
# 새로운 집 가격 예측 (30평, 역 5분, 10층, 5년차)
new_house = [[30, 5, 10, 5]]
print(f"예측 가격: {model.predict(new_house)[0]:.2f}억원")
김개발 씨는 단순 선형 회귀의 한계를 실감했습니다. 같은 30평이라도 강남역 도보 5분 거리와 외곽 버스 30분 거리의 가격은 천지차이였기 때문입니다.
하나의 변수만으로는 현실을 제대로 반영할 수 없었습니다. 박시니어 씨가 설명을 이어갔습니다.
"단순 선형 회귀가 y = ax + b였다면, 다중 회귀는 y = a1x1 + a2x2 + a3*x3 + ... + b 입니다.
변수가 여러 개일 뿐, 기본 원리는 같아요." 다중 회귀를 비유하자면, 집 가격을 매기는 감정평가사와 같습니다. 평가사는 크기만 보지 않습니다.
위치, 층수, 건물 연식, 주변 환경 등 여러 요소를 종합적으로 판단합니다. 다중 회귀도 마찬가지로 여러 변수의 영향을 동시에 고려합니다.
코드를 보면, 이제 독립 변수가 4개입니다. 크기, 역까지 거리, 층수, 연식.
모델을 학습시키면 각 변수의 계수(coefficient)를 얻을 수 있습니다. 이 계수는 해당 변수가 가격에 미치는 영향력을 나타냅니다.
결과를 해석해봅시다. size의 계수가 0.08이라면 "다른 조건이 동일할 때, 1평 증가하면 가격이 0.08억원 오른다"는 의미입니다.
distance_to_station의 계수가 음수라면 역에서 멀어질수록 가격이 떨어진다는 뜻입니다. 다중 회귀에서 주의해야 할 점은 다중공선성(Multicollinearity)입니다.
독립 변수들끼리 높은 상관관계가 있으면 계수 추정이 불안정해집니다. 예를 들어 "층수"와 "전망 점수"가 거의 같은 정보를 담고 있다면 문제가 됩니다.
또한 변수의 스케일도 고려해야 합니다. 크기는 2050 범위이고, 연식은 120 범위라면 계수의 크기만으로 중요도를 비교할 수 없습니다.
이럴 때는 표준화(Standardization)를 적용하여 공정한 비교를 해야 합니다. 실무에서 다중 회귀는 매우 널리 사용됩니다.
광고비가 매출에 미치는 영향, 날씨와 시간대가 배달 수요에 미치는 영향 등 다양한 비즈니스 문제에 적용할 수 있습니다. 김개발 씨는 개선된 모델을 배포했고, 예측 정확도가 크게 향상되었습니다.
"변수를 추가하니까 확실히 좋아지네요!" 이제 이 모델이 얼마나 좋은지 평가하는 방법을 배울 차례입니다.
실전 팁
💡 - 변수를 무작정 많이 추가하면 과적합의 위험이 있습니다
- 변수들 간의 상관관계를 확인하여 다중공선성을 체크하세요
6. 회귀 모델 평가
김개발 씨가 모델을 완성하고 뿌듯해하고 있을 때, QA 담당자가 질문했습니다. "이 모델이 얼마나 정확한가요?
수치로 보여주실 수 있나요?" 김개발 씨는 당황했습니다. 모델의 좋고 나쁨을 어떻게 객관적으로 측정할 수 있을까요?
회귀 모델의 성능은 MSE(평균제곱오차)와 R²(결정계수)로 평가합니다. MSE는 예측값과 실제값의 차이를 측정하고, R²는 모델이 데이터의 변동을 얼마나 설명하는지 나타냅니다.
이 지표들을 이해하면 모델을 객관적으로 비교하고 개선할 수 있습니다.
다음 코드를 살펴봅시다.
import numpy as np
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error, r2_score
from sklearn.model_selection import train_test_split
# 샘플 데이터 생성
np.random.seed(42)
X = np.random.rand(100, 2) * 10
y = 3 * X[:, 0] + 2 * X[:, 1] + np.random.randn(100) * 2
# 학습/테스트 데이터 분리
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
# 모델 학습
model = LinearRegression()
model.fit(X_train, y_train)
# 예측 및 평가
y_pred = model.predict(X_test)
# MSE: 평균제곱오차 (낮을수록 좋음)
mse = mean_squared_error(y_test, y_pred)
print(f"MSE: {mse:.4f}")
# RMSE: 루트 평균제곱오차 (원래 단위로 해석 가능)
rmse = np.sqrt(mse)
print(f"RMSE: {rmse:.4f}")
# R²: 결정계수 (1에 가까울수록 좋음)
r2 = r2_score(y_test, y_pred)
print(f"R² Score: {r2:.4f}")
김개발 씨는 QA 담당자의 질문에 답하기 위해 모델 평가 지표를 공부하기 시작했습니다. "그냥 눈으로 봤을 때 괜찮아 보여요"라는 대답은 프로페셔널하지 않으니까요.
박시니어 씨가 화이트보드에 수식을 적었습니다. "회귀 모델 평가의 핵심은 '예측값이 실제값과 얼마나 다른가'입니다." 첫 번째 지표는 MSE(Mean Squared Error, 평균제곱오차)입니다.
각 예측값과 실제값의 차이를 구하고, 제곱해서 평균을 낸 것입니다. 수식으로는 MSE = (1/n) * Σ(y_실제 - y_예측)²입니다.
제곱을 하는 이유는 양수와 음수 오차가 상쇄되지 않도록 하고, 큰 오차에 더 큰 페널티를 주기 위해서입니다. MSE의 단점은 단위가 제곱이라는 점입니다.
가격을 예측하는 모델에서 MSE가 4라면, 이게 얼마나 큰 오차인지 직관적으로 알기 어렵습니다. 그래서 RMSE(Root MSE)를 사용합니다.
MSE에 루트를 씌우면 원래 단위로 해석할 수 있습니다. RMSE가 2억이라면 "평균적으로 2억원 정도 오차가 난다"고 말할 수 있습니다.
두 번째 지표는 R²(R-squared, 결정계수)입니다. 이 값은 0과 1 사이인데, "모델이 데이터의 변동을 얼마나 설명하는가"를 나타냅니다.
R²가 0.85라면 데이터 변동의 85%를 모델이 설명한다는 의미입니다. R²를 비유하자면, 시험 점수와 같습니다.
100점 만점에 85점이면 꽤 좋은 성적입니다. 하지만 50점이면 모델이 데이터를 절반밖에 설명하지 못한다는 뜻이니 개선이 필요합니다.
코드에서 중요한 부분은 train_test_split입니다. 데이터를 학습용과 테스트용으로 나누어 평가하는 것이 핵심입니다.
학습 데이터로 평가하면 과적합 여부를 알 수 없습니다. 모델이 본 적 없는 테스트 데이터로 평가해야 실제 성능을 알 수 있습니다.
실무에서는 여러 모델을 만들고 이 지표들로 비교합니다. "A 모델의 R²가 0.78이고 B 모델은 0.85니까 B가 더 낫다"는 식으로 객관적인 판단이 가능해집니다.
김개발 씨는 자신의 모델이 R² 0.82를 기록했다는 것을 QA 담당자에게 보고했습니다. "데이터 변동의 82%를 설명하는 모델입니다." 이제 숫자로 이야기할 수 있게 되었습니다.
실전 팁
💡 - 학습 데이터가 아닌 테스트 데이터로 평가해야 실제 성능을 알 수 있습니다
- R²만 보지 말고 MSE도 함께 확인하여 실제 오차 크기를 파악하세요
7. 회귀 트리와 그라디언트 부스트
김개발 씨의 선형 회귀 모델은 잘 동작했지만, 특정 구간에서 예측이 빗나갔습니다. "선형 관계가 아닌 부분이 있어요." 박시니어 씨가 새로운 방법을 제안했습니다.
"트리 기반 회귀를 써보세요. 비선형 관계도 잡아낼 수 있어요."
회귀 트리는 데이터를 조건에 따라 분할하여 예측하는 모델입니다. 그라디언트 부스팅은 여러 개의 약한 모델을 순차적으로 학습시켜 강한 모델을 만드는 앙상블 기법입니다.
마치 여러 전문가의 의견을 종합하는 것처럼, 복잡한 패턴도 효과적으로 학습합니다.
다음 코드를 살펴봅시다.
from sklearn.tree import DecisionTreeRegressor
from sklearn.ensemble import GradientBoostingRegressor
from sklearn.model_selection import train_test_split
from sklearn.metrics import r2_score
import numpy as np
# 비선형 관계가 있는 데이터 생성
np.random.seed(42)
X = np.linspace(0, 10, 200).reshape(-1, 1)
y = np.sin(X).ravel() + np.random.randn(200) * 0.2
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
# 회귀 트리
tree_model = DecisionTreeRegressor(max_depth=5)
tree_model.fit(X_train, y_train)
tree_pred = tree_model.predict(X_test)
print(f"Decision Tree R²: {r2_score(y_test, tree_pred):.4f}")
# 그라디언트 부스팅
gb_model = GradientBoostingRegressor(n_estimators=100, max_depth=3)
gb_model.fit(X_train, y_train)
gb_pred = gb_model.predict(X_test)
print(f"Gradient Boosting R²: {r2_score(y_test, gb_pred):.4f}")
# 특성 중요도 (다중 변수일 경우)
# print("Feature Importances:", gb_model.feature_importances_)
김개발 씨는 선형 회귀의 한계에 부딪혔습니다. 집 가격이 역에서 5분 거리까지는 급격히 오르다가, 그 이후로는 완만하게 변하는 패턴이 있었습니다.
직선 하나로는 이런 곡선을 표현할 수 없었습니다. 박시니어 씨가 새로운 접근법을 소개했습니다.
"의사결정나무를 회귀에도 쓸 수 있어요. 그리고 이것들을 여러 개 조합하면 더 강력해집니다." 회귀 트리(Decision Tree Regressor)는 데이터를 질문으로 분할합니다.
"역까지 거리가 5분 이하인가?" → "예"라면 왼쪽, "아니오"라면 오른쪽. 이렇게 계속 분할하다가 최종 노드에서 해당 영역의 평균값을 예측값으로 사용합니다.
비유하자면, 회귀 트리는 마치 가격표와 같습니다. "역세권이면서 신축이면 5억, 역세권이지만 구축이면 4억, 역세권이 아니면서 신축이면 3.5억..." 이런 식으로 조건에 따라 가격이 정해지는 것입니다.
하지만 단일 트리는 과적합되기 쉽습니다. 학습 데이터에는 완벽하게 맞지만 새로운 데이터에는 잘 동작하지 않는 것이죠.
이를 해결하기 위해 앙상블 기법이 등장했습니다. 그라디언트 부스팅(Gradient Boosting)은 앙상블의 대표적인 방법입니다.
첫 번째 모델이 예측하고, 그 오차를 두 번째 모델이 학습합니다. 두 번째 모델의 오차를 세 번째 모델이 학습합니다.
이렇게 순차적으로 오차를 줄여나갑니다. 비유하자면, 팀 프로젝트에서 첫 번째 팀원이 초안을 작성하고, 두 번째 팀원이 부족한 부분을 보완하고, 세 번째 팀원이 다시 다듬는 것과 같습니다.
혼자서는 완벽하지 않지만 여러 명이 협력하면 훌륭한 결과물이 나옵니다. 코드를 보면, GradientBoostingRegressor의 n_estimators는 트리의 개수입니다.
100개의 트리가 순차적으로 학습됩니다. max_depth는 각 트리의 깊이를 제한하여 과적합을 방지합니다.
실무에서는 XGBoost, LightGBM, CatBoost 같은 최적화된 라이브러리를 많이 사용합니다. sklearn의 GradientBoostingRegressor보다 훨씬 빠르고 성능도 좋습니다.
Kaggle 대회에서도 이런 부스팅 모델들이 상위권을 휩쓸고 있습니다. 그라디언트 부스팅의 또 다른 장점은 특성 중요도(Feature Importance)를 알려준다는 것입니다.
어떤 변수가 예측에 가장 큰 영향을 미치는지 알 수 있어서 모델 해석에 유용합니다. 김개발 씨는 그라디언트 부스팅으로 모델을 교체했고, R² 점수가 0.82에서 0.91로 크게 향상되었습니다.
"비선형 패턴까지 잡아내니까 확실히 좋아졌네요!" 이제 복잡한 현실 세계의 데이터도 자신 있게 다룰 수 있게 되었습니다.
실전 팁
💡 - 단일 트리보다 앙상블 모델이 일반화 성능이 좋습니다
- 실무에서는 XGBoost나 LightGBM을 사용하면 더 빠르고 좋은 성능을 얻을 수 있습니다
이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!
댓글 (0)
함께 보면 좋은 카드 뉴스
Helm 마이크로서비스 패키징 완벽 가이드
Kubernetes 환경에서 마이크로서비스를 효율적으로 패키징하고 배포하는 Helm의 핵심 기능을 실무 중심으로 학습합니다. Chart 생성부터 릴리스 관리까지 체계적으로 다룹니다.
보안 아키텍처 구성 완벽 가이드
프로젝트의 보안을 처음부터 설계하는 방법을 배웁니다. AWS 환경에서 VPC부터 WAF, 암호화, 접근 제어까지 실무에서 바로 적용할 수 있는 보안 아키텍처를 단계별로 구성해봅니다.
AWS Organizations 완벽 가이드
여러 AWS 계정을 체계적으로 관리하고 통합 결제와 보안 정책을 적용하는 방법을 실무 스토리로 쉽게 배워봅니다. 초보 개발자도 바로 이해할 수 있는 친절한 설명과 실전 예제를 제공합니다.
AWS KMS 암호화 완벽 가이드
AWS KMS(Key Management Service)를 활용한 클라우드 데이터 암호화 방법을 초급 개발자를 위해 쉽게 설명합니다. CMK 생성부터 S3, EBS 암호화, 봉투 암호화까지 실무에 필요한 모든 내용을 담았습니다.
AWS Secrets Manager 완벽 가이드
AWS에서 데이터베이스 비밀번호, API 키 등 민감한 정보를 안전하게 관리하는 Secrets Manager의 핵심 개념과 실무 활용법을 배워봅니다. 초급 개발자도 쉽게 따라할 수 있도록 실전 예제와 함께 설명합니다.