๊ฒฐ์ ํธ๋ฆฌ Decision Tree
๐ณ ๊ฒฐ์ ํธ๋ฆฌ(Decision Tree)์ ๋ถ๋ฅ ์๋ฆฌ
**๊ฒฐ์ ํธ๋ฆฌ(Decision Tree)**๋ ๋ฐ์ดํฐ๋ฅผ ํน์ฑ(feature)์ ์กฐ๊ฑด์ ๋ฐ๋ผ ๋ถํ ํ๋ฉด์ ์ต์ ์ ์์ธก์ ์ํํ๋ ์๊ณ ๋ฆฌ์ฆ์
๋๋ค.
๋ถ๋ฅ(Classification)์ ํ๊ท(Regression) ๋ชจ๋ ๊ฐ๋ฅํ์ง๋ง, ์ฌ๊ธฐ์๋ ๋ถ๋ฅ ๊ธฐ์ค์ ์ง์คํด์ ์ค๋ช
ํ ๊ฒ์!
1๏ธโฃ ๊ฒฐ์ ํธ๋ฆฌ๋ ์ด๋ป๊ฒ ์๋ํ ๊น?
๊ฒฐ์ ํธ๋ฆฌ๋ ํธ๋ฆฌ ๊ตฌ์กฐ๋ฅผ ๊ฐ์ง๊ณ ์์ผ๋ฉฐ, ๋ฃจํธ ๋ ธ๋์์ ์์ํด์ ๊ฐ์ง(branch)๋ฅผ ๋ฐ๋ผ๊ฐ๋ฉด์ ๋ฐ์ดํฐ๋ฅผ ๋๋๋ ๋ฐฉ์์ ๋๋ค.
๐ฅ ํต์ฌ ๊ฐ๋ :
- ๊ฐ ๋ ธ๋(Node) → ํ๋์ ์กฐ๊ฑด(์: x > 5?)
- ๊ฐ ๋ถ๊ธฐ(Branch) → ์กฐ๊ฑด์ ๋ฐ๋ผ ๋ฐ์ดํฐ๊ฐ ๋ถํ ๋จ
- ๋ฆฌํ ๋ ธ๋(Leaf Node) → ์ต์ข ์ ์ผ๋ก ๊ฒฐ์ ๋ ํด๋์ค
2๏ธโฃ ๊ฒฐ์ ํธ๋ฆฌ๊ฐ ๋ถ๋ฅ๋ฅผ ์ํํ๋ ๋ฐฉ๋ฒ
(1) ๋ฐ์ดํฐ ๋ถํ (Splitting)
- ํธ๋ฆฌ๋ ํน์ฑ์ ํ๋ ์ ํํด์ ๋ฐ์ดํฐ๋ฅผ ๋ถํ ํฉ๋๋ค.
- ๊ฐ์ฅ ์ข์ ๋ถํ ์ ์ฐพ๊ธฐ ์ํด ์ฌ๋ฌ ๊ธฐ์ค(์ง๋ ๋ถ์๋, ์ํธ๋กํผ ๋ฑ)์ ์ฌ์ฉํฉ๋๋ค.
(2) ๋ถ์๋ ์ธก์ (Impurity)
- ๋ถํ ์ด ์ ๋์๋์ง ํ๋จํ๋ ๋ฐฉ๋ฒ์ ๋๋ค.
- ๋ํ์ ์ธ ๋ ๊ฐ์ง ๋ฐฉ๋ฒ:
- ์ง๋ ๋ถ์๋(Gini Impurity)
- p_i๋ ํด๋์ค ii์ ์ํ ํ๋ฅ
- ๊ฐ์ด ์์์๋ก ๋ ๊นจ๋ํ๊ฒ ๋ถ๋ฅ๋จ
- ์ํธ๋กํผ(Entropy)Entropy=−∑pilogโก2piEntropy = - \sum p_i \log_2 p_i
- ์ ๋ณด ์ด๋(Information Gain)์ ์ต๋ํํ๋ ๋ฐฉํฅ์ผ๋ก ๋ถํ ์ํ
- ์ง๋ ๋ถ์๋(Gini Impurity)
#์ง๋ ๋ถ์๋
import numpy as np
def gini_impurity(y):
_, counts = np.unique(y, return_counts=True) # ํด๋์ค๋ณ ๊ฐ์ ๊ณ์ฐ
probabilities = counts / counts.sum() # ํ๋ฅ ๊ณ์ฐ
gini = 1 - np.sum(probabilities ** 2) # ์ง๋ ๋ถ์๋ ๊ณต์ ์ ์ฉ
return gini
# ์์ ๋ฐ์ดํฐ (ํด๋์ค 0, 1๋ก ๊ตฌ์ฑ)
y1 = np.array([0, 0, 1, 1, 1]) # ํด๋์ค 0: 2๊ฐ, ํด๋์ค 1: 3๊ฐ
y2 = np.array([0, 0, 0, 1, 1]) # ํด๋์ค 0: 3๊ฐ, ํด๋์ค 1: 2๊ฐ
print("Gini ๋ถ์๋ (y1):", gini_impurity(y1))
print("Gini ๋ถ์๋ (y2):", gini_impurity(y2))
#์ํธ๋กํผ
def entropy(y):
_, counts = np.unique(y, return_counts=True) # ํด๋์ค๋ณ ๊ฐ์ ๊ณ์ฐ
probabilities = counts / counts.sum() # ํ๋ฅ ๊ณ์ฐ
entropy = -np.sum(probabilities * np.log2(probabilities + 1e-9)) # ๋ก๊ทธ 0 ๋ฐฉ์ง์ฉ ์์ ๊ฐ ์ถ๊ฐ
return entropy
print("์ํธ๋กํผ (y1):", entropy(y1))
print("์ํธ๋กํผ (y2):", entropy(y2))
(3) ๋ฐ๋ณต์ ์ผ๋ก ๋ถํ ํ์ฌ ํธ๋ฆฌ ์์ฑ
- ๋ฐ์ดํฐ๊ฐ ๋ ์ด์ ๋๋ ํ์๊ฐ ์์ ๋๊น์ง ์ ๊ณผ์ ์ ๋ฐ๋ณตํฉ๋๋ค.
- ๋ฉ์ถ๋ ์กฐ๊ฑด:
- ๋ ธ๋ ๋ด ๋ฐ์ดํฐ๊ฐ ๋๋ฌด ์์์ง ๊ฒฝ์ฐ (๊ณผ์ ํฉ ๋ฐฉ์ง)
- ์ถ๊ฐ ๋ถํ ์ด ์๋ฏธ ์์ ๊ฒฝ์ฐ (๋ถ์๋๊ฐ ์ถฉ๋ถํ ๋ฎ์)
3๏ธโฃ ๊ฒฐ์ ํธ๋ฆฌ ๊ตฌํ (Python)
from sklearn.tree import DecisionTreeClassifier
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
# ๋ฐ์ดํฐ ๋ก๋
iris = load_iris()
X_train, X_test, y_train, y_test = train_test_split(iris.data, iris.target, test_size=0.2, random_state=42)
# ๊ฒฐ์ ํธ๋ฆฌ ๋ชจ๋ธ ์์ฑ
clf = DecisionTreeClassifier(criterion='gini', max_depth=3, random_state=42)
# ๋ชจ๋ธ ํ์ต
clf.fit(X_train, y_train)
# ์์ธก
y_pred = clf.predict(X_test)
# ์ ํ๋ ํ๊ฐ
from sklearn.metrics import accuracy_score
print("์ ํ๋:", accuracy_score(y_test, y_pred))
- criterion='gini' → ์ง๋ ๋ถ์๋ ๊ธฐ์ค์ผ๋ก ๋ถ๋ฅ
- max_depth=3 → ๊ณผ์ ํฉ ๋ฐฉ์ง๋ฅผ ์ํด ์ต๋ ๊น์ด๋ฅผ 3์ผ๋ก ์ ํ
4๏ธโฃ ์ฅ์ ๊ณผ ๋จ์
โ ์ฅ์
- ์ง๊ด์ ์ด๊ณ ํด์์ด ์ฌ์ (ํธ๋ฆฌ๋ฅผ ๋ฐ๋ผ๊ฐ๋ฉด ๊ฒฐ์ ๊ณผ์ ์ ์ ์ ์์)
- ๋น์ ํ ๋ฐ์ดํฐ์๋ ์ ์ฉ ๊ฐ๋ฅ
- ์ ์ฒ๋ฆฌ๊ฐ ๊ฑฐ์ ํ์ ์์ (์ค์ผ์ผ ์กฐ์ ๋ถํ์)
โ ๋จ์
- ๊ณผ์ ํฉ(Overfitting) ๊ฐ๋ฅ์ฑ ํผ
- ๋ฐ์ดํฐ๊ฐ ์์ ๋๋ ์์ ์ฑ์ด ๋จ์ด์ง
- ์์ ๋ณํ์๋ ๊ตฌ์กฐ๊ฐ ํฌ๊ฒ ๋ฐ๋ ์ ์์
โก ํด๊ฒฐ ๋ฐฉ๋ฒ:
- max_depth, min_samples_split ๋ฑ์ ํ์ดํผํ๋ผ๋ฏธํฐ ํ๋
- ๋๋ค ํฌ๋ ์คํธ(Random Forest)๋ ๋ถ์คํ (XGBoost, LightGBM) ์ฌ์ฉ
๐ฏ ๊ฒฐ๋ก
๊ฒฐ์ ํธ๋ฆฌ๋ ๋ฐ์ดํฐ๋ฅผ ์กฐ๊ฑด๋ณ๋ก ๋ถํ ํ๋ฉด์ ์ต์ ์ ๋ถ๋ฅ ๊ฒฝ๋ก๋ฅผ ์ฐพ์๊ฐ๋ ์๊ณ ๋ฆฌ์ฆ์
๋๋ค.
๐ ํ์ง๋ง ๋จ์ํ ๊ฒฐ์ ํธ๋ฆฌ๋ ๊ณผ์ ํฉ๋๊ธฐ ์ฌ์ฐ๋ฏ๋ก, ๋๋ค ํฌ๋ ์คํธ๋ ๋ถ์คํ
๋ชจ๋ธ์ ํ์ฉํ๋ ๊ฒ์ด ์ค๋ฌด์์ ๋ ์์ ์ ์ด์์! ๐