본문 바로가기

파이썬/머신러닝

랜덤 포레스트 원리와 구현 사이킷런 예제로 코드 실습해보기

오늘은 앙상블 기법 중 하나인 랜덤포레스트에 대해 공부해봤다. 랜덤포레스트를 배우려면 일단 결정트리(Decision Tree)가 뭔지부터 알아야 한다. 결정 트리에 대한 자세한 내용은 아래 포스팅을 참고!

 

2021.06.13 - [파이썬/머신러닝] - 결정트리(Decision Tree) , 엔트로피 개념과 시각화까지

 

결정트리(Decision Tree) , 엔트로피 개념과 시각화까지

Decision Tree 모델은 가장 많이 쓰이는 지도학습 ML 모델 중 하나이다. 추후 학습할 앙상블 학습의 배깅 방법인 Random Forest는 Decision Tree 여러개를 모아 모델을 학습시키는 방식이다. 그렇기에 그 기

for-my-wealthy-life.tistory.com

 

랜덤포레스트는 결정트리 여러개로 만들어진 모델이다. 결정트리는 훈련 데이터에 오버피팅된다는 치명적인 단점이 있기 때문에 보통은 결정트리 하나를 단독으로 사용하기 보다는 랜덤포레스트를 사용한다.

 

 

랜덤포레스트 개념

 

하나의 결정트리가 모든 feature를 변수로 사용해서 y값을 예측한다면 앞서 말한대로 오버피팅 문제가 발생한다. 그래서 랜덤포레스트는 feature를 무작위로 뽑거나, 데이터를 무작위로 뽑아서 여러개의 작은 트리를 만들고 그 트리들을 결합한다.

 

 

예를 들어 feature가 30개가 있다면 그 중에 랜덤하게 5개만 뽑아서 트리를 하나 만들고, 또 다시 랜덤하게 5개의 feature를 뽑아서 두번째 트리를 만들고, 이런 식으로 트리를 여러 개 만드는 것이 바로 랜덤포레스트이다. 

 

 

트리를 여러 개 만들면 트리 개수만큼 예측 결과값이 생성되는데 voting을 통해 결과값을 채택하게 된다. 이게 바로 앙상블 학습이다.

 

 

랜덤포레스트는 기본적으로 결정트리의 단점을 보완하는 모델이라 오버피팅이 적고 성능이 뛰어나다. n_estimators, max_features, max_depth 정도와 같은 몇몇 파라미터만 잘 튜닝해주면 튜닝을 많이 하지 않아도 높은 성능을 보인다.

 

 

*앙상블: 의견을 통합하거나 여러 결과를 합치는 것 

-분류모델: 가장 많이 나온 값

-회귀모델: 평균값

 

 

랜덤포레스트 주요 파라미터

 

n_estimators: 트리를 몇 개 만들 것인지 (int, default=100), 값이 클수록 오버피팅 방지

criterion: gini 또는 entropy 중 선택

max_depth: 트리의 깊이 (int, default=None)

bootstrap: True이면 전체 feature에서 복원추출해서 트리 생성 (default=True)

max_features: 선택할 feature의 개수, 보통 default값으로 씀 (default='auto')

출처: scikit-learn

 

랜덤포레스트 예제(실습 코드)

 

사이킷런의 breast_cancer 데이터로 간단하게 예제 코드를 실행해보자.

 

from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split

cancer = load_breast_cancer()
print(cancer.keys())
>>>
dict_keys(['data', 'target', 'frame', 'target_names', 'DESCR', 'feature_names', 'filename'])

print(cancer.target_names)
print(cancer.feature_names)
>>>
['malignant' 'benign']
['mean radius' 'mean texture' 'mean perimeter' 'mean area'
 'mean smoothness' 'mean compactness' 'mean concavity'
 'mean concave points' 'mean symmetry' 'mean fractal dimension'
 'radius error' 'texture error' 'perimeter error' 'area error'
 'smoothness error' 'compactness error' 'concavity error'
 'concave points error' 'symmetry error' 'fractal dimension error'
 'worst radius' 'worst texture' 'worst perimeter' 'worst area'
 'worst smoothness' 'worst compactness' 'worst concavity'
 'worst concave points' 'worst symmetry' 'worst fractal dimension']

 

breast_cacer데이터의 기본 구성 확인 후 train, test 데이터 분리

X_train, X_test, y_train, y_test = train_test_split(cancer.data, cancer.target, test_size=0.2, random_state=42 )

 

최적의 max_depth 찾기

from sklearn.model_selection import KFold

cv = KFold(n_splits=5)            # Desired number of Cross Validation folds  #n_splits값이 클수록 오래걸림
accuracies = list()
max_attributes = X_test.shape[1]
depth_range = range(1, max_attributes)

# Testing max_depths from 1 to max attributes
# Uncomment prints for details about each Cross Validation pass
for depth in depth_range:
    fold_accuracy = []
    rand_clf = RandomForestClassifier(max_depth = depth)
    # print("Current max depth: ", depth, "\n")
    for train_fold, valid_fold in cv.split(train):
        f_train = train.loc[train_fold] # Extract train data with cv indices
        f_valid = train.loc[valid_fold] # Extract valid data with cv indices

        model = rand_clf.fit(X_train, y_train)
        valid_acc = model.score(X_test, y_test)
        fold_accuracy.append(valid_acc)

    avg = sum(fold_accuracy)/len(fold_accuracy)
    accuracies.append(avg)
    # print("Accuracy per fold: ", fold_accuracy, "\n")
    # print("Average accuracy: ", avg)
    # print("\n")
    
# Just to show results conveniently
df = pd.DataFrame({"Max Depth": depth_range, "Average Accuracy": accuracies})
df = df[["Max Depth", "Average Accuracy"]]
print(df.to_string(index=False))

 

max_depth간에 accuracy 차이가 거의 없긴 하다. max_depth 6부터 정확도가 조금 떨어지므로 max_depth=5로 설정했다. 이제 랜덤포레스트 모델에 피팅을 시켜보자.

 

rand_clf = RandomForestClassifier(criterion='entropy', bootstrap=True, random_state=42, max_depth=5)

rand_clf.fit(X_train, y_train)
y_pred = rand_clf.predict(X_test)

print('훈련세트 정확도: {:.3f}' .format(rand_clf.score(X_train, y_train)))
print('테스트세트 정확도: {:.3f}' .format(rand_clf.score(X_test, y_test)))

>>>훈련세트 정확도: 0.993
>>>테스트세트 정확도: 0.965

train set 정확도는 99.3%, test set 정확도는 96.5%로 아주 높다. 조금 의심스럽긴 하지만 잘 맞췄다고 할 수 있다.

 

 

from sklearn.metrics import confusion_matrix, classification_report

print(confusion_matrix(y_test, y_pred))
print(classification_report(y_test, y_pred))

 

모델 훈련을 시켰으니 마지막으로 feature_importances를 확인해보자.

import seaborn as sns
import numpy as np
%matplotlib inline

# feature importance 추출 
print("Feature importances:\n{0}".format(np.round(rand_clf.feature_importances_, 3)))

# feature별 importance 매핑
for name, value in zip(cancer.feature_names , rand_clf.feature_importances_):
    print('{0} : {1:.3f}'.format(name, value))

# feature importance를 column 별로 시각화 하기 
sns.barplot(x=rand_clf.feature_importances_ , y=cancer.feature_names)

worst area, worst concave points, mean concave points 세 개 변수의 중요도가 가장 높다고 할 수 있다.