Decision Tree는 "질문을 던지고 답변에 따라 분류하는 과정"을 트리 구조로 표현한 머신러닝 알고리즘이에요.
이를 쉽게 이해하기 위해 간단한 예제를 들어볼게요.
예제: 오늘 우산을 가져갈까?
우리는 매일 아침 우산을 가져갈지 말지 고민할 수 있어요. 이를 Decision Tree 방식으로 해결해볼게요.
- 첫 번째 질문: "오늘 비가 올 확률이 50% 이상인가?"
- YES → 우산을 가져간다. ✅
- NO → 다음 질문으로 이동
- 두 번째 질문: "오늘 외출 시간이 길어질 가능성이 있는가?"
- YES → 우산을 가져간다. ✅
- NO → 우산을 가져가지 않는다. ❌
(비 올 확률 ≥ 50%?)
/ \
Yes (✅) No
/ \
(외출 시간 길어질 가능성?)
/ \
Yes(✅) No(❌)
위와 같이 질문(노드)을 던지고, 조건(가지)에 따라 분기를 나누는 것이
Decision Tree의 핵심 원리예요.
핵심 정리
✔ 의사결정 트리는 질문을 던지고 가지를 나누어 결정을 내리는 구조
✔ 데이터를 분류(우산 여부)하거나 예측(집값, 판매량 등)에 사용
✔ 사람이 의사결정을 내리는 과정과 유사해 직관적 이해가 쉬움
이제 Decision Tree의 개념이 조금 더 쉽게 다가오죠?
Decision Tree (의사결정나무) 개념과 수식 이해하기
1. 데이터 설명
아래와 같은 데이터를 기반으로 의사결정을 내립니다:
비 올 확률 (%) | 외출 시간 길이 (시간) | 출력 변수 (y) : 우산 여부 (1: O, 0: X) |
80 | 1 | 1 |
10 | 5 | 0 |
40 | 2 | 0 |
60 | 3 | 1 |
20 | 6 | 0 |
90 | 1 | 1 |
50 | 4 | 1 |
30 | 2 | 0 |
- 입력 변수 (X): "비 올 확률 (%)"과 "외출 시간 길이 (시간)"
- 출력 변수 (y): 우산을 가져갈지 여부 (1 = 가져감, 0 = 안 가져감)
2. Decision Tree 모델 해석
- 가장 먼저 비 올 확률 (%)을 기준으로 분기합니다.
- 만약 비 올 확률이 높으면 우산을 가져갈 확률이 높습니다.
- 그렇지 않다면 다음 기준으로 넘어갑니다.
- 다음으로 외출 시간 길이 (시간)을 기준으로 또 한 번 나눕니다.
- 외출 시간이 길다면 우산을 가져갈 가능성이 커집니다.
즉, "비 올 확률이 높거나, 외출 시간이 길다면 우산을 가져간다."라는 논리를 트리 구조로 나타낸 것입니다.
3. 핵심 개념
✔ 결정 기준(feature importance): "비 올 확률"이 가장 중요한 기준
✔ 질문을 반복하며 조건에 맞게 데이터를 분류
✔ 직관적이고 쉽게 해석할 수 있는 머신러닝 모델
이제 Decision Tree의 개념이 데이터 기반으로 더 명확해졌을 거예요!
Decision Tree (의사결정나무) 개념과 수식 이해하기
이제 실제로 정보이득(Information Gain)과 엔트로피(Entropy)를 계산하는 과정을 살펴보겠습니다. 간단한 데이터를 이용해 **어떤 기준(feature)**으로 분기하는 것이 좋은지 수식을 통해 이해해봅시다.
1. 엔트로피 (Entropy)와 정보 이득 (Information Gain)
1. 개념 소개
1) 엔트로피 (Entropy)
엔트로피는 데이터의 "불확실성(혼잡도)"을 측정하는 값입니다. 값이 클수록 데이터가 균등하게 분포되어 있어 예측이 어렵고, 값이 작을수록 한쪽으로 치우쳐 있어 예측이 쉬워집니다.
엔트로피 공식은 다음과 같습니다:
- = 클래스 i 의 확률
- 엔트로피 값이 0이면 모든 데이터가 같은 클래스
- 엔트로피 값이 1에 가까울수록 데이터가 섞여 있음
2. 예제 데이터
우리는 다음 데이터를 사용하여 우산을 가져갈지 예측하는 트리를 생성합니다.
ID | 비 올 확률 (%) | 외출 시간 (시간) | 우산 여부 (1: O, 0: X) |
1 | 80 | 1 | 1 |
2 | 10 | 5 | 0 |
3 | 40 | 2 | 0 |
4 | 60 | 3 | 1 |
5 | 20 | 6 | 0 |
6 | 90 | 1 | 1 |
7 | 50 | 4 | 1 |
8 | 30 | 2 | 0 |
우산 여부의 분포:
- 우산 가져감(1): 4개
- 우산 안 가져감(0): 4개
=> 총 8개의 데이터에서 50%가 우산을 가져가고, 50%는 가져가지 않음
=> 엔트로피 계산:
즉, 초기 데이터의 엔트로피는 1 (완전히 섞여 있는 상태).
3. 정보 이득 (Information Gain) 계산
정보이득(IG, Information Gain)은 특정 기준으로 데이터를 나누었을 때 엔트로피가 얼마나 줄어드는지 측정하는 값입니다.
(1) "비 올 확률 ≥ 50%"로 데이터 분리
데이터를 비 올 확률 ≥ 50% 기준으로 나눠봅시다.
그룹 1 (비 올 확률 ≥ 50%)
ID | 비 올 확률 (%) | 우산 여부 |
1 | 80 | 1 |
4 | 60 | 1 |
6 | 90 | 1 |
7 | 50 | 1 |
→ 우산 O: 4개, 우산 X: 0개
→ 엔트로피 = 0 (모두 같은 클래스)
그룹 2 (비 올 확률 < 50%)
ID | 비 올 확률 (%) | 우산 여부 |
2 | 10 | 0 |
3 | 40 | 0 |
5 | 20 | 0 |
8 | 30 | 0 |
→ 우산 O: 0개, 우산 X: 4개
→ 엔트로피 = 0 (모두 같은 클래스)
(2) 정보이득 계산
각 그룹의 크기는 같으므로 정보이득 계산:
즉, "비 올 확률 ≥ 50%" 기준으로 분기하면 정보이득 1을 얻고, 모든 데이터가 완전히 분류됨!
따라서 이 기준이 가장 좋은 분류 기준이 됩니다.
4. 결정 트리 모델 결과
위 계산을 바탕으로 만든 결정 트리는 다음과 같습니다.
- 비 올 확률 ≥ 50%이면 → 무조건 우산을 가져감 ✅
- 비 올 확률 < 50%이면 → 무조건 우산을 가져가지 않음 ❌
5. 핵심 요약
✔ 엔트로피: 데이터의 혼잡도를 측정하는 값
✔ 정보이득 (IG): 특정 기준으로 데이터를 나누었을 때 엔트로피가 줄어드는 정도
✔ 최적의 분기 기준 찾기: 정보이득이 가장 높은 feature 선택
✔ 결과: "비 올 확률 ≥ 50%"가 가장 좋은 기준
2. 지니계수(Gini Index)와 지니불순도(Gini Impurity) 이해하기
1. 개념 소개
1) 지니불순도(Gini Impurity)란?
지니불순도(Gini Impurity)는 한 그룹 내에서 클래스(예: 우산을 가져가는지 여부)가 섞여 있을 확률을 의미합니다.
(1) 공식
- = 클래스 i의 확률 (예: 우산을 가져갈 확률과 가져가지 않을 확률)
- 값이 0이면 불순도가 낮고(= 모든 데이터가 같은 클래스)
- 값이 1에 가까울수록 불순도가 높음(= 데이터가 균등하게 섞여 있음)
2. 예제 데이터
우리는 다음 데이터를 사용하여 우산을 가져갈지 예측하는 트리를 생성합니다.
ID | 비 올 확률 (%) | 외출 시간 (시간) | 우산 여부 (1: O, 0: X) |
1 | 80 | 1 | 1 |
2 | 10 | 5 | 0 |
3 | 40 | 2 | 0 |
4 | 60 | 3 | 1 |
5 | 20 | 6 | 0 |
6 | 90 | 1 | 1 |
7 | 50 | 4 | 1 |
8 | 30 | 2 | 0 |
우산 여부의 분포:
- 우산 가져감(1): 4개
- 우산 안 가져감(0): 4개
1) 초기 지니불순도 계산
(1) 전체 데이터의 지니불순도
총 8개의 데이터 중:
- 우산을 가져간 경우(1): 4개 → p1 = 4/8 = 0.5
- 우산을 안 가져간 경우(0): 4개 → p0 = 4/8= 0.5
즉, 초기 데이터의 지니불순도(Gini Impurity) = 0.5입니다.
3. "비 올 확률 ≥ 50%"로 데이터 분리
데이터를 비 올 확률 ≥ 50% 기준으로 나누었을 때, 각 그룹에서 지니불순도를 계산해보겠습니다.
1) 그룹 1 (비 올 확률 ≥ 50%)
(1) 지니불순도 계산
ID | 비 올 확률 (%) | 우산 여부 |
1 | 80 | 1 |
4 | 60 | 1 |
6 | 90 | 1 |
7 | 50 | 1 |
모두 "우산을 가져간다(1)" → 완전히 분류됨
즉, 불순도 0 (완전히 깨끗한 분류)
2) 그룹 2 (비 올 확률 < 50%)
(1) 지니불순도 계산
ID | 비 올 확률 (%) | 우산 여부 |
2 | 10 | 0 |
3 | 40 | 0 |
5 | 20 | 0 |
8 | 30 | 0 |
모두 "우산을 가져가지 않는다(0)" → 완전히 분류됨
즉, 불순도 0 (완전히 깨끗한 분류)
4. 정보이득(Gini Gain) 계산
지니계수를 사용하여 정보이득(Gini Gain)을 계산하는 공식은 다음과 같습니다.
1) 공식
(1) 정보이득(Gini Gain)
즉, "비 올 확률 ≥ 50%" 기준으로 분기하면 지니불순도가 0으로 감소하며, 가장 좋은 분류 기준이 됨.
5. 엔트로피(Entropy) vs. 지니불순도(Gini Impurity)
1) 비교 표
(1) 엔트로피(Entropy) vs. 지니불순도(Gini Impurity)
비교항목 | 엔트로피(Entropy) | 지니불순도(Gini Impurity) |
공식 | ||
계산 속도 | 상대적으로 느림 | 빠름 |
값의 범위 | 0 ~ 1 | 0 ~ 0.5 |
의미 | 불확실성(혼잡도) 측정 | 분류 오류 가능성 측정 |
결정트리에서 사용 | 보통 criterion="entropy" | 보통 criterion="gini" |
- 지니불순도(Gini Impurity)는 계산이 더 빠르고 직관적이기 때문에 CART(Classification And Regression Tree) 알고리즘에서 기본값으로 사용됩니다.
- 엔트로피(Entropy)는 로그 연산이 포함되어 있어 상대적으로 느리지만, 정보이득이 더 세밀하게 계산될 수도 있습니다.
3. 코드 설명
아래 코드는 Decision Tree(의사결정 나무)를 이용해 "우산을 가져갈지 여부"를 예측하는 모델을 만들고 시각화하는 과정입니다
1) 데이터 준비
data = {
"비 올 확률 (%)": [80, 10, 40, 60, 20, 90, 50, 30],
"외출 시간 길이 (시간)": [1, 5, 2, 3, 6, 1, 4, 2],
"우산 여부": [1, 0, 0, 1, 0, 1, 1, 0]
}
df = pd.DataFrame(data)
(1) 설명
- **비 올 확률 (%)**과 **외출 시간 길이 (시간)**을 독립 변수로 사용
- **우산 여부(1: 가져감, 0: 안 가져감)**을 종속 변수로 설정
- pd.DataFrame(data)를 사용하여 데이터프레임으로 변환
2) 독립 변수(X)와 종속 변수(y) 설정
X = df[["비 올 확률 (%)", "외출 시간 길이 (시간)"]]
y = df["우산 여부"]
(1) 설명
- X: 입력(특징) 데이터
- y: 정답(목표) 데이터
- 모델이 학습할 때 "비 올 확률"과 "외출 시간 길이"를 이용해 "우산 여부"를 예측하도록 구성
3) 모델 학습
model = DecisionTreeClassifier(criterion="gini", max_depth=2, random_state=42) model.fit(X, y)
(1) 설명
- DecisionTreeClassifier()를 사용하여 결정 트리 모델 생성
- fit(X, y)를 호출하여 모델을 학습시킴
- 주요 하이퍼파라미터는 다음과 같음:
- criterion="gini" → 지니 불순도를 사용하여 트리를 분할
- max_depth=2 → 트리의 최대 깊이를 2로 제한하여 과적합 방지
- random_state=42 → 랜덤 시드를 고정하여 동일한 결과를 얻을 수 있도록 설정
4) 트리 시각화
plt.figure(figsize=(8, 5))
tree.plot_tree(model, feature_names=["비 올 확률 (%)", "외출 시간 길이 (시간)"],
class_names=["우산 X", "우산 O"], filled=True)
plt.show()
(1) 설명
- tree.plot_tree(model, ...)을 사용하여 결정 트리를 시각화
- feature_names: X에서 사용된 변수 이름 설정
- class_names: 예측할 클래스(우산 O, 우산 X)를 명시
- filled=True를 설정하면 노드 색상으로 분류 확률을 직관적으로 표현 가능
- plt.show()를 사용해 그래프 출력
4. 하이퍼파라미터 설명
model = DecisionTreeClassifier(
criterion="entropy",
splitter="best",
max_depth=3,
min_samples_split=2,
min_samples_leaf=1,
min_weight_fraction_leaf=0,
max_features=None,
random_state=42,
max_leaf_nodes=None,
min_impurity_decrease=0.0,
class_weight=None
)
위 코드에서 사용된 DecisionTreeClassifier의 하이퍼파라미터를 초보자가 이해하기 쉽게 설명하겠습니다.
1. criterion="entropy"
- 결정 트리가 데이터를 분할하는 기준을 설정합니다.
- "gini"를 선택하면 지니 불순도를 기준으로 데이터를 나누고,
- "entropy"를 선택하면 정보이득(Information Gain)을 기준으로 나눕니다.
- "entropy"는 불순도가 많이 줄어드는 방향으로 분기하는 방식이라 좀 더 정확한 기준을 찾을 수 있습니다.
하지만 로그 연산이 필요해 계산이 조금 더 느릴 수 있습니다. - "gini"는 빠르게 작동하지만 분할 기준이 다소 단순할 수 있습니다.
✅ 초보자 팁:
- 속도가 중요하다면 "gini" 사용
- 정보이득을 더 정확하게 계산하고 싶다면 "entropy" 사용
2. splitter="best"
- 데이터를 분할할 때 어떤 방식으로 나눌지 결정하는 옵션입니다.
- "best"를 선택하면 각 노드에서 최적의 분할 기준을 자동으로 찾습니다.
- "random"을 선택하면 데이터를 무작위로 분할합니다.
✅ 초보자 팁:
- 보통 "best"가 성능이 좋습니다.
- "random"은 특성이 많을 때 과적합을 방지하는 데 유용합니다
3. max_depth=3
- 트리의 최대 깊이를 설정하는 옵션입니다.
- 결정 트리는 **너무 깊어지면 과적합(overfitting)**될 위험이 있습니다.
- 예를 들어 max_depth=3이면 트리의 깊이가 3 이하로 제한되어 지나치게 세부적인 분류를 방지할 수 있습니다.
✅ 초보자 팁:
- None으로 설정하면 트리가 최대한 깊어질 수 있습니다.
- 데이터가 작다면 3~5 정도가 적절한 값입니다.
- 과적합이 의심되면 max_depth 값을 줄여보세요.
4. min_samples_split=2
- 노드를 분할할 최소 샘플 수를 의미합니다.
- 기본값은 2로 설정되어 있어, 샘플이 2개 이상이면 분할을 허용합니다.
- 값이 커지면 너무 작은 그룹으로 분할되는 것을 방지할 수 있습니다.
✅ 초보자 팁:
- min_samples_split=4로 설정하면 최소 4개의 데이터가 있어야 분할됩니다.
- 데이터가 크다면 기본값(2)보다 높은 값을 주는 것이 좋습니다.
5. min_samples_leaf=1
- 리프(Leaf) 노드에 최소한으로 남아야 하는 샘플 수입니다.
- 1이면 각 리프 노드에 최소한 1개의 샘플이 있어도 괜찮다는 의미입니다.
- 값이 커질수록 과적합을 방지하는 데 도움이 됩니다.
✅ 초보자 팁:
- 데이터가 크다면 min_samples_leaf=2 또는 min_samples_leaf=5로 설정하는 것이 좋습니다.
- 작은 값일수록 모델이 데이터에 과하게 적응(과적합)할 가능성이 있습니다.
6. min_weight_fraction_leaf=0
- min_samples_leaf와 비슷하지만, 데이터 가중치를 고려하여 최소 리프 샘플 비율을 설정하는 옵션입니다.
- 0이면 이 기능을 사용하지 않는다는 의미입니다.
✅ 초보자 팁:
- 데이터에 가중치(weight)가 부여된 경우가 아니라면 기본값 0을 유지하세요.
7. max_features=None
- 분할할 때 사용할 최대 특성(feature) 개수를 정하는 옵션입니다.
- None이면 모든 특성을 사용합니다.
- 숫자를 입력하면 해당 개수만큼의 특성(feature)만 사용해서 분할합니다.
✅ 초보자 팁:
- 데이터에 많은 특성이 있을 때 max_features="sqrt" 또는 max_features="log2"로 설정하면 계산 속도를 높일 수 있습니다.
- 보통 분류 문제에서는 기본값(None)을 많이 사용합니다.
8. random_state=42
- 랜덤 시드를 설정하는 옵션입니다.
- random_state를 지정하면 매번 같은 결과를 얻을 수 있습니다.
- 42는 관습적으로 자주 사용되는 숫자일 뿐, 아무 숫자나 넣어도 됩니다.
✅ 초보자 팁:
- random_state를 설정하지 않으면 실행할 때마다 모델이 달라질 수 있습니다.
- 항상 동일한 결과를 원한다면 random_state=42로 설정하세요
9. max_leaf_nodes=None
- 리프(Leaf) 노드의 최대 개수를 제한하는 옵션입니다.
- None이면 리프 노드 개수에 제한이 없습니다.
✅ 초보자 팁:
- 과적합이 의심되면 적절한 리프 노드 개수를 설정하는 것이 좋습니다.
- 예를 들어 max_leaf_nodes=10으로 하면 최대 10개의 리프 노드까지만 허용됩니다.
10. min_impurity_decrease=0.0
- 분할을 수행하기 위해 최소한으로 감소해야 하는 불순도(impurity) 값입니다.
- 0.0이면 모든 분할이 허용됩니다.
- 값이 커질수록 불순도가 충분히 감소할 때만 분할이 수행됩니다.
✅ 초보자 팁:
- 과적합 방지를 위해 min_impurity_decrease=0.01 정도로 설정하는 것도 좋습니다.
11. class_weight=None
- 클래스 간의 가중치를 조정하는 옵션입니다.
- None이면 모든 클래스에 동일한 가중치를 부여합니다.
- "balanced"를 선택하면 데이터의 클래스 비율에 따라 자동으로 가중치를 부여합니다.
✅ 초보자 팁:
- 데이터가 불균형한 경우(0이 90%, 1이 10%라면) "balanced"를 설정하는 것이 좋습니다.
✅ 초보자를 위한 정리
💡 만약 처음부터 설정해야 한다면?
초보자가 가장 쉽게 적용할 수 있는 추천 설정은 다음과 같습니다.
model = DecisionTreeClassifier(
criterion="entropy",
max_depth=3,
min_samples_split=4,
min_samples_leaf=2,
random_state=42
)
이렇게 설정하면:
- 엔트로피 기준으로 최적의 분할을 찾고,
- 최대 깊이를 3으로 제한하여 과적합을 방지하며,
- 최소 샘플 개수를 늘려(4개 이상이면 분할) 작은 그룹이 생기는 것을 방지하고,
- 리프 노드에 최소 2개 이상의 샘플이 남도록 설정하여 일반화 성능을 높이며,
- 랜덤 시드를 고정하여 항상 같은 결과를 얻을 수 있습니다.
이제 결정 트리의 모든 하이퍼파라미터를 쉽게 조정할 수 있습니다! 🚀