의사결정 트리(Decision Tree)는 주로 데이터 분류 문제에 활용되는 대표적인 머신러닝 알고리즘입니다. 트리 구조에서 각 노드(node)는 특성(feature)을 나타내고, 각 엣지(edge)는 해당 특성에 대해 내려진 판단(결정)을 의미합니다.
루트 노드(root node)에서 출발해 각 특성을 순서대로 평가하면서 어떤 엣지를 따라갈지 결정합니다. 새로운 데이터가 입력되면 동일한 과정이 반복적으로 적용되며, 분류에 필요한 모든 특성이 검토된 후 최종 결론이 도출됩니다.
즉, 의사결정 트리는 여러 개의 학습 변수(training variables)를 기반으로 종속 변수(dependent variable)를 예측하는 지도 학습(supervised learning) 모델이라고 할 수 있습니다.
1. 실습 데이터 소개
이번 글에서는 Kaggle에서 공개된 약물 처방(drug) 데이터셋을 사용합니다. 먼저 pandas를 이용해 CSV 파일을 읽어 들인 뒤, 데이터의 내용과 구조를 확인해 보겠습니다.
import pandas as pd
datainput = pd.read_csv("drug.csv", delimiter=",") # https://www.kaggle.com/gangliu/drugsets
print(datainput)
위 코드를 실행하면 다음과 같은 결과가 출력됩니다.
Age Sex BP Cholesterol Na_to_K Drug 0 23 F HIGH HIGH 25.355 drugY 1 47 M LOW HIGH 13.093 drugC 2 47 M LOW HIGH 10.114 drugC 3 28 F NORMAL HIGH 7.798 drugX 4 61 F LOW HIGH 18.043 drugY .. ... .. ... ... ... ... 195 56 F LOW HIGH 11.567 drugC 196 16 M LOW HIGH 12.006 drugC 197 52 M NORMAL HIGH 9.894 drugX [200 rows x 6 columns]
데이터는 총 200개의 행과 6개의 열(Age, Sex, BP, Cholesterol, Na_to_K, Drug)로 구성되어 있으며, 나이·성별·혈압 등의 정보를 바탕으로 어떤 약물을 처방해야 하는지를 분류하는 것이 목표입니다.
2. 데이터 전처리(Pre-processing)
다음 단계는 전처리입니다. 데이터에는 'F', 'M', 'LOW', 'HIGH' 같은 텍스트 값이 포함되어 있는데, scikit-learn의 모델은 숫자형 입력만 처리할 수 있으므로 이를 수치 값으로 변환해야 합니다. 이 과정을 통해 나이, 성별, 혈압 등의 값이 주어졌을 때 어떤 약물을 사용할지에 대한 학습 및 평가가 가능해집니다.
예제 코드
import numpy as np
import pandas as pd
from sklearn.metrics import confusion_matrix
datainput = pd.read_csv("drug.csv", delimiter=",")
X = datainput[['Age', 'Sex', 'BP', 'Cholesterol', 'Na_to_K']].values
from sklearn import preprocessing
label_gender = preprocessing.LabelEncoder()
label_gender.fit(['F','M'])
X[:,1] = label_gender.transform(X[:,1])
label_BP = preprocessing.LabelEncoder()
label_BP.fit([ 'LOW', 'NORMAL', 'HIGH'])
X[:,2] = label_BP.transform(X[:,2])
label_Chol = preprocessing.LabelEncoder()
label_Chol.fit([ 'NORMAL', 'HIGH'])
X[:,3] = label_Chol.transform(X[:,3])
# 첫 6개 레코드 출력
print(X[0:6])
위 코드를 실행하면 다음과 같은 결과를 얻을 수 있습니다.
[[23 0 0 0 25.355] [47 1 1 0 13.093] [47 1 1 0 10.113999999999999] [28 0 2 0 7.797999999999999] [61 0 1 0 18.043] [22 0 2 0 8.607000000000001]]
LabelEncoder를 사용하면 범주형 텍스트가 자동으로 정수로 매핑됩니다. 예를 들어 성별은 F→0, M→1로, 혈압(BP)은 LOW→1, NORMAL→2, HIGH→0처럼 변환됩니다.
3. 종속 변수(Dependent Variable) 변환
독립 변수뿐만 아니라 예측 대상인 종속 변수 역시 수치화해야 학습 데이터와 평가 데이터 양쪽에서 일관되게 사용할 수 있습니다. 여기서는 'Drug' 열을 그대로 추출해 타깃(target) 값으로 사용합니다.
예제 코드
import pandas as pd
datainput = pd.read_csv("drug.csv", delimiter=",")
X = datainput[['Age', 'Sex', 'BP', 'Cholesterol', 'Na_to_K']].values
y = datainput["Drug"]
print(y[0:6])
실행 결과
0 drugY 1 drugC 2 drugC 3 drugX 4 drugY 5 drugX Name: Drug, dtype: object
4. 데이터셋 학습(Training)
이제 전체 데이터 중 70%를 학습용(train)으로 사용하고, 나머지 30%를 테스트용(test)으로 분리합니다. 학습 데이터는 분류 모델을 만드는 기반이 되고, 테스트 데이터는 만들어진 모델의 성능을 검증하는 데 사용됩니다.
예제 코드
import pandas as pd
from sklearn.model_selection import train_test_split
datainput = pd.read_csv("drug.csv", delimiter=",")
X = datainput[['Age', 'Sex', 'BP', 'Cholesterol', 'Na_to_K']].values
y = datainput["Drug"]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=3)
print(X_train.shape)
print(X_test.shape)
print(y_train.shape)
print(y_test.shape)
실행 결과
(140, 5) (60, 5) (140,) (60,)
결과를 보면 학습 데이터 140건, 테스트 데이터 60건으로 나뉜 것을 확인할 수 있습니다. random_state=3을 지정하면 실행할 때마다 동일한 방식으로 데이터가 분할되어 재현성이 보장됩니다.
5. 학습된 모델로 결과 확인하기
마지막으로 의사결정 트리 모델을 적용해 학습 결과를 확인합니다. 여기서는 입력 데이터를 기반으로 트리를 생성하며, 분할 기준(criterion)으로 엔트로피(entropy)를 사용합니다. 트리의 깊이(max_depth)는 4로 제한해 과적합(overfitting)을 방지합니다. 모델 학습 후에는 테스트 데이터에 대한 예측을 수행하고, 정확도(accuracy)를 계산해 성능을 평가합니다.
예제 코드
import pandas as pd
from sklearn.tree import DecisionTreeClassifier
from sklearn import metrics
datainput = pd.read_csv("drug.csv", delimiter=",")
X = datainput[['Age', 'Sex', 'BP', 'Cholesterol', 'Na_to_K']].values
# 데이터 전처리
from sklearn import preprocessing
label_gender = preprocessing.LabelEncoder()
label_gender.fit(['F', 'M'])
X[:, 1] = label_gender.transform(X[:, 1])
label_BP = preprocessing.LabelEncoder()
label_BP.fit(['LOW', 'NORMAL', 'HIGH'])
X[:, 2] = label_BP.transform(X[:, 2])
label_Chol = preprocessing.LabelEncoder()
label_Chol.fit(['NORMAL', 'HIGH'])
X[:, 3] = label_Chol.transform(X[:, 3])
y = datainput["Drug"]
# train_test_split
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=3)
drugTree = DecisionTreeClassifier(criterion="entropy", max_depth=4)
drugTree.fit(X_train, y_train)
predicted = drugTree.predict(X_test)
print(predicted)
print("\nDecisionTrees's Accuracy: ", metrics.accuracy_score(y_test, predicted))
실행 결과
['drugY' 'drugX' 'drugX' 'drugX' 'drugX' 'drugC' 'drugY' 'drugA' 'drugB' 'drugA' 'drugY' 'drugA' 'drugY' 'drugY' 'drugX' 'drugY' 'drugX' 'drugX' 'drugB' 'drugX' 'drugX' 'drugY' 'drugY' 'drugY' 'drugX' 'drugB' 'drugY' 'drugY' 'drugA' 'drugX' 'drugB' 'drugC' 'drugC' 'drugX' 'drugX' 'drugC' 'drugY' 'drugX' 'drugX' 'drugX' 'drugA' 'drugY' 'drugC' 'drugY' 'drugA' 'drugY' 'drugY' 'drugY' 'drugY' 'drugY' 'drugB' 'drugX' 'drugY' 'drugX' 'drugY' 'drugY' 'drugA' 'drugX' 'drugY' 'drugX'] DecisionTrees's Accuracy: 0.9833333333333333
마무리
실행 결과, 의사결정 트리 모델의 정확도는 약 98.33%로 매우 높게 나타났습니다. 이처럼 의사결정 트리는 전처리 → 데이터 분할 → 모델 학습 → 평가라는 명확한 파이프라인을 따르며, 해석이 직관적이고 분류 성능도 우수하기 때문에 실무에서 널리 활용되는 알고리즘입니다.
추가로 plot_tree() 함수를 사용하면 학습된 트리 구조를 시각화할 수 있으니, 어떤 특성이 어떤 순서로 분류에 기여했는지 직접 확인해 보시기 바랍니다.