안녕하세요!
오늘은 머신러닝 분류(Classification) 알고리즘의 기초이자 강력한 모델인 결정트리(Decision Tree)를 활용해
독버섯과 식용버섯을 분류하는 프로젝트를 진행해 보겠습니다.
특히 머신러닝 입문자들이 가장 많이 헤매는 '문자형(Categorical) 데이터를
어떻게 모델이 이해할 수 있는 숫자형으로 바꾸는가(원-핫 인코딩)'에 초점을 맞추어
한 줄 한 줄 쉽게 풀어보겠습니다.
그 전에 다른 실무 인사이트가 궁금하다면?
1. 프로젝트 목표 및 라이브러리 로드
이번 프로젝트의 목표는 버섯의 갓 모양, 냄새, 색상 등 다양한 외형적 특성 데이터를 활용해
이 버섯이 독버섯(p)인지 식용버섯(e)인지 분류하는 모델을 만드는 것입니다.
먼저 분석에 필요한 핵심 라이브러리들을 불러옵니다.
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
# 데이터 분리 및 모델링을 위한 사이킷런 패키지
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier
2. 데이터 수집 및 탐색 (EDA)
준비된 mushroom.csv 데이터를 판다스로 읽어와 데이터의 형태와 기술통계량을 확인해 봅니다.
data = pd.read_csv("data/mushroom.csv")
data.info()
data.describe()
data.info()와 data.describe()를 확인해 보면, 이 데이터셋의 모든 컬럼이 숫자가 아닌
문자형(Object) 데이터로 이루어져 있음을 알 수 있습니다.
이것이 바로 이번 포스팅의 핵심 전처리 포인트입니다.
또한 Seaborn의 countplot과 hue 옵션을 사용하면
특정 특성에 따라 독버섯과 식용버섯이 어떻게 분포하는지 한눈에 볼 수 있습니다.
# 독성 여부(poisonous)의 전체 비율 확인
sns.countplot(data=data, x="poisonous");
# 갓 모양(cap-shape)에 따른 독성 여부 비교
sns.countplot(data=data, x="cap-shape", hue="poisonous");
hue="poisonous"를 지정해 주면 갓 모양별 막대그래프 안에서 식용과 독버섯이 색상별로 분리되어 표현됩니다.
3. 머신러닝의 핵심: 문제집(X)과 정답지(y) 분리
인공지능을 학습시키기 위해 규칙을 찾을 재료인 문제집(X)과 맞춰야 할 정답지(y)로 데이터를 쪼갭니다.
# cap-shape 컬럼부터 끝까지 모든 특성을 문제집 X로 설정
X = data.loc[:, "cap-shape":]
# 독성 여부 컬럼만 정답지 y로 설정
y = data["poisonous"]
print(X.shape) # (8124, 22) -> 8124개의 데이터와 22개의 특징
print(y.shape) # (8124,) -> 8124개의 정답 리스트
4. 문자형 데이터 전처리: 원-핫 인코딩(One-Hot Encoding)
컴퓨터는 '글자'를 계산할 수 없습니다. 오직 '숫자'만 이해하죠.
따라서 문자형 특성들을 숫자로 변환해야 합니다.
왜 레이블 인코딩 대신 원-핫 인코딩인가?
LabelEncoder()를 쓰면 글자마다 0, 1, 2... 같은 숫자가 부여됩니다.
하지만 버섯의 갓 모양에는 우열이 없습니다.
컴퓨터가 2가 0보다 크다고 착각하여 모델이 왜곡될 수 있죠.
그래서 우리는 데이터 종류만큼 새로운 열을 만들고 해당하는 곳에만 1, 나머지는 0을 채우는
원-핫 인코딩(One-Hot Encoding)을 적용합니다.
# 판다스의 get_dummies를 활용해 한 번에 원-핫 인코딩 적용
X_one_hot = pd.get_dummies(X, dtype=int)
X_one_hot.head()
이 작업을 거치면 기존에 22개였던 X의 컬럼이 컴퓨터가 완벽히 이해할 수 있는
117개의 숫자 컬럼으로 안전하게 확장됩니다.
(참고: 정답지 y는 모델이 내부적으로 알아서 처리하므로 그대로 두어도 괜찮습니다).
5. 학습용(Train) vs 평가용(Test) 데이터 분리
모델의 정확한 성능 평가를 위해 데이터를 7:3 비율로 분리합니다.
X_train, X_test, y_train, y_test = train_test_split(
X_one_hot, y,
test_size=0.3, # 30%를 평가용으로 분리
random_state=99, # 난수 고정
stratify=y # 원본 데이터의 정답 비율을 유지하며 분리
)
print(X_train.shape) # (5686, 117) -> 학습용 문제집
print(X_test.shape) # (2438, 117) -> 시험용 문제집
6. 결정트리 모델 생성, 학습 및 평가
이제 모델을 생성하고 학습을 진행합니다. 이때 너무 깊게 나무가 자라나면 학습 데이터에만 과대적합(Overfitting)될 수 있으므로 하이퍼파라미터로 제동을 걸어줍니다.
# 과대적합 제어를 위한 하이퍼파라미터 설정
tree_model = DecisionTreeClassifier(
max_depth=3, # 나무의 최대 깊이를 3단계로 제한
max_leaf_nodes=5, # 최종 정답 방의 개수를 최대 5개로 제한
min_samples_leaf=50 # 하나의 방에 최소 50개 이상의 데이터가 있어야 인정
)
# 모델 학습
tree_model.fit(X_train, y_train)
# 정확도(Accuracy) 결과 확인
print("학습 데이터 정확도:", tree_model.score(X_train, y_train)) # 약 96.9%
print("평가 데이터 정확도:", tree_model.score(X_test, y_test)) # 약 96.8%
학습 데이터와 평가 데이터의 점수가 둘 다 96% 대를 기록하며,
과대적합 없이 안정적으로 새로운 버섯 데이터도 분류할 수 있는 훌륭한 일반화 모델이 완성되었습니다!
7. 하이라이트: 결정트리 시각화 파일 추출 (Graphviz)
결정트리 모델의 가장 큰 장점은 "인공지능이 왜 이런 판단을 내렸는지 눈으로 직접 화이트박스 모델을 볼 수 있다"는 점입니다. 내부 로직을 시각화하기 위해 tree.dot 파일을 생성해 보겠습니다.
import graphviz
from sklearn.tree import export_graphviz
export_graphviz(
tree_model,
out_file="data/tree.dot",
class_names=["식용", "독"],
feature_names=X_one_hot.columns, # 숫자 대신 실제 컬럼명 표기
impurity=True, # 지니 불순도 출력
filled=True # 클래스 비율에 따라 색상 채우기
)
이렇게 생성된 .dot 파일을 Graphviz 뷰어나 주피터 내에서 시각화하면,
모델이 가장 먼저 어떤 특성(예: 특정 냄새나 서식지 등)을 기준으로 독버섯을 쪼개 나갔는지 그 알고리즘의 지도를 직관적으로 확인할 수 있습니다.