소란한 블로그
[TIL-210204] Decision Tree 본문
1. 사이킷런 파이프라인 (Sklearn.pipeline.Pipeline)
사이킷런의 파이프라인 라이브러리를 이용하면 머신러닝 모델을 구현할 때 중복 코드를 최소화하여 쉽게 구현할 수 있다. 파이프라인을 이용하면 하나의 전처리 프로세스에 여러 ML 모델을 쉽게 적용시켜 볼 수 있다. 또한 그리드서치(grid search)를 통해 여러 하이퍼파라미터를 쉽게 연결할 수 있다.
# 파이프라인 사용하지 않았을 때
from category_encoders import OneHotEncoder
from sklearn.impute import SimpleImputer
from sklearn.tree import DecisionTreeClassifier
encoder = OneHotEncoder()
imputer = SimpleImputer()
scaler = StandardScaler()
model = LogisticRegression(n_jobs=-1)
X_train_encoded = encoder.fit_transform(X_train)
X_train_imputed = imputer.fit_transform(X_train_encoded)
X_train_scaled = scaler.fit_transform(X_train_imputed)
model.fit(X_train_scaled, y_train)
X_val_encoded = encoder.transform(X_val)
X_val_imputed = imputer.transform(X_val_encoded)
X_val_scaled = scaler.transform(X_val_imputed)
# 파이프라인을 사용했을 때
from category_encoders import OneHotEncoder
from sklearn.impute import SimpleImputer
from sklearn.tree import DecisionTreeClassifier
from sklearn.pipeline import make_pipeline # 파이프라인
pipe = make_pipeline(
OneHotEncoder(use_cat_names=True),
SimpleImputer(),
DecisionTreeClassifier(random_state=2, criterion='entropy')
)
pipe.fit(X_train, y_train)
파이프라인을 사용했을 때와 사용하지 않았을 때를 비교해보면 훨씬 코드가 간결해진 것을 볼 수 있다. Encoder와 Imputer, Scaler, ML 모델을 일일이 선언해주고 데이터 셋에 일일이 fit해주어야 했던 전과 비교했을 때 가독성도 좋고 간결해졌다.
파이프라인의 named_steps 속성을 이용하면, 파이프라인의 각 스텝에 접근할 수 있다.
pipe.named_steps
# 출력 결과
{'onehotencoder': OneHotEncoder(cols=['opinion_h1n1_vacc_effective', 'opinion_h1n1_risk', 'opinion_h1n1_sick_from_vacc', 'agegrp', 'census_msa']),
'simpleimputer': SimpleImputer(),
'standardscaler': StandardScaler(),
'logisticregression': LogisticRegression(n_jobs=-1)}
출력 결과를 보면 파이프라인을 이용해서 전처리한 내용들이 어떻게 구성되어있는지 알 수 있다. 각 스텝들은 딕셔너리 형태로 출력된다. 따라서 named_steps은 유사 딕셔너리 객체(dictionary-like object)로, 파이프라인 내 과정에 접근할 수 있다.
2. 결정트리(Decision Tree) 모델
결정트리 모델은 선형모델과는 다른 트리기반(tree-based) 머신러닝 모델이다.
아래 링크를 참조하면 결정트리모델에 대한 직관적인 이해를 할 수 있다.
A visual introduction to machine learning
What is machine learning? See how it works with our animated data visualization.
www.r2d3.us
자료구조 트리와 비슷하게 노드(node), 에지(edge), 루트노드(root node) 등을 가지며 특성들의 수치를 통해 정답 클래스를 찾아가도록 한다.

결정 트리의 가장 큰 장점은 분류와 회귀문제 모두 적용가능하다는 것이다. 또한 이런 분류과정을 직관적으로 확인 가능하여 왜 그런 결과가 나왔는지 설명하기 용이하다.
노드를 어떤 기준으로 분류하는가에 따라 다른 모양의 트리구조가 만들어지는데, 트리구조에 따라 분류하고자하는 모델의 정확도가 달라질 것이다. 이때 불순도 개념이 사용된다. 불순도는 간단히 말해 어떤 분류가 가장 좋은지 결정하기 위해 사용된다. 아래에서 좀 더 자세히 설명한다.
사이킷런의 결정트리모델은 불순도를 각 노드에서 모두 계산하고 불순도가 가장 낮아지는 특성을 선택하고 분할 지점을 선택해주면서 학습한다. Greedy algoritm 처럼 선택마다 최적의 선택을 하지만, 그것이 전체로 보았을 때 최적의 선택이라고 볼 수는 없다. 또한 Random State를 정해주지 않으면 할 때 마다 다른 결과가 나온다.
- 결정 트리의 기준모델 (baseline model of decision tree)
결정트리의 기준모델은 모든 노드를 사용해서 분리하여 학습데이터를 99%이상 맞추도록 한다. 즉, 훈련 정확도를 99% 이상으로 만든다.
# 타겟의 클래스 비율 알아보기
train[target].value_counts(normalize=True)
# 출력결과
0 0.760565
1 0.239435
Name: vacc_h1n1_f, dtype: float64
타겟에서 0과 1의 비율이 얼마정도 되는지를 보면, 0이 76%를 차지하는 불균형한 분류문제임을 알 수 있다.
from category_encoders import OneHotEncoder
from sklearn.impute import SimpleImputer
from sklearn.tree import DecisionTreeClassifier
from sklearn.pipeline import make_pipeline # 파이프라인
import warnings
warnings.simplefilter(action='ignore', category=FutureWarning) # FutureWarning 제거
pipe = make_pipeline(
OneHotEncoder(use_cat_names=True),
SimpleImputer(),
DecisionTreeClassifier(random_state=2, criterion='entropy')
)
pipe.fit(X_train, y_train)
print('[entropy] 훈련 정확도: ', pipe.score(X_train, y_train))
print('[entropy] 검증 정확도: ', pipe.score(X_val, y_val))
y_pred = pipe.predict(X_val)
print('[F1-score] 검증 정확도: ', f1_score(y_val, y_pred))
# 출력 결과
[entropy] 훈련 정확도: 0.9885721269899471
[entropy] 검증 정확도: 0.7318504033243706
[F1-score] 검증 정확도: 0.4473551637279597
출력 결과를 보면 훈련 정확도는 1에 수렴하는데에 비해 검증 정확도는 0.7로, 과적합이 일어난 것을 볼 수 있다. 검증 셋을 결정트리 기준 모델로 예측했을 때 위에서 살펴본 타겟 클래스의 다수 범주 (0)이 차지하는 비율과 비슷한 정확도가 나온다. 이 결과의 의미는 이 모델이 어떤 데이터를 예측할 때 모두 다수 클래스이 0으로 예측했다는 것이다.
따라서 하이퍼 파라미터를 조절하여 기준모델 보다 더 성능이 좋은 결정트리 모델을 만들어야 한다.
- 결정 트리 모델의 하이퍼파라미터(Hyperparameter)
1) min_samples_leaf
: 마지막 노드(leaf node)가 가지는 샘플의 개수를 정해주어 과적합을 방지할 수 있다.
2) max_depth
: 만들어지는 트리의 깊이를 제한한다.
3) min_samples_split
: 노드를 분할 할 때 사용되는 최소한의 샘플의 수를 정해준다. 작게 설정할수록 분할되는 노드가 많아서 과적합의 가능성이 증가한다.
pipe = make_pipeline(
OneHotEncoder(use_cat_names=True),
SimpleImputer(),
DecisionTreeClassifier(max_depth=5, random_state=2)
)
pipe.fit(X_train, y_train)
3. 불순도(impurity)
불순도란, 여러 범주가 섞여있는 정도를 말한다. 예를 들어 한 노드(질문)을 기준으로 나뉘는 샘플의 개수가 비슷하다면 불순도가 높은 것이고, 비슷하지 않고 한 쪽의 샘플 개수가 월등이 많고 다른 한 쪽은 적다면 불순도가 낮은 것이다. 샘플을 나눌때 더 명확하게 나뉠 수록 불순도가 낮다고 볼 수 있다.
불순도를 계산하는 방법은 여러가지 인데, 지니 불순도의 경우 1 - (yes 가능성)^2 - (no 가능성)^2 을 각 노드마다 계산한다. 만약 불순도가 가장 낮은 노드가 있다면, 이 노드에서는 더이상 나누어지지 않고 leaf node가 된다.
1) 지니 불순도 (Gini Impurity)

2) 엔트로피 (Entropy)

사이킷런(sklearn)에서 불순도의 디폴트값은 지니이다. 지니는 조금 더 빠르고, 엔트로피는 좀 더 정확하다고 한다.
불순도가 낮을수록, 지니불순도 또는 엔트로피 값이 낮다.
질문(노드)을 정할 때는 분할에 사용할 특성이나 분할 지점(이 특성의 수치)을 정해야한다. 이때 특성이나 분할 지점은 예측하고자 하는 타겟 변수를 가장 잘 구별해주는 것을 선택해야하는데, 이를 판단할 때 불순도의 감소가 최대가 되는 것을 기준으로 선택한다. 즉, 불순도의 감소를 통해 이 노드를 이용해서 분할 할 때 얻을 수 있는 정보 획득(information gain)의 양을 알 수 있다.
정보획득(Information Gain)은 특정한 특성을 사용해 분할했을 때 엔트로피의 감소량을 말한다.

4. 특성상호작용
결정트리모델은 선형모델과는 달리, 비선형, 비단조(non-monotonic), 특성상호작용(feature interactions)을 특징으로하는 데이터 분석에 용이하다.
특성상호작용이란, 특성들끼리 서로 상관성이 있는 경우를 말한다. 회귀분석에서는 서로 상관관계가 높은 특성들이 있으면 회귀 계수를 해석하는데 어려움이 있고 학습이 올바르게 되지 않을 수 있다. 하지만 트리모델은 이런 상호작용을 자동으로 걸러낸다.
5. 캐글 Competition
캐글 competition에 참여하기 위해서는 만든 모델을 가지고 test를 예측한 결과를 csv 파일로 저장해야한다.
# test set에 모델 적용
y_pred = pipe.predict(X_test)
sample_submission = pd.read_csv('submission.csv')
sample_submission['vacc_h1n1_f'] = y_pred
sample_submission = sample_submission.set_index('Id')
sample_submission
# csv 파일로 저장
sample_submission.to_csv("submission.csv")
'Today I Learned' 카테고리의 다른 글
[TIL-210406] 역전파(Backpropagation) (0) | 2021.04.06 |
---|---|
[TIL-210205] Random Forest (0) | 2021.02.05 |
[TIL-210202] Logistic Regression (0) | 2021.02.02 |
[TIL-210201] Ridge Regression (0) | 2021.02.01 |