<배깅(Bagging) - 랜덤 포레스트(Random Forest)>

 

●배깅의 대표적인 알고리즘은 랜덤 포레스트이다.

●랜덤 포레스트는 다재 다능한 알고리즘으로 앙상블 알고리즘 중 비교적 빠른 수행 속도를 가지고 있으며, 다양한 영역에서 높은 예측 성능을 보이고 있다.

●랜덤 포레스트는 여러 개의 결정 트리 분류기가 전체 데이터에서 배깅 방식으로 각자의 데이터를 샘플링해 개별적으로 학습을 수행한 뒤 최종적으로 모든 분류기가 보팅을 통해 예측 결정을 한다.

●랜덤 포레스트뿐만 아니라 부스팅 기반의 다양한 앙상블 알고리즘은 대부분 결정 트리 알고리즘을 기반 알고리즘으로 채택하고 있다.

 

 

 

 

-랜덤 포레스트의 부트스트래핑 분할

●랜덤 포레스트는 개별적인 분류기의 기반 알고리즘은 결정 트리이지만 개별 트리가 학습하는 데이터 세트는 전체 데이터에서 일부가 중첩되게 샘플링된 데이터 세트이다. 이렇게 여러 개의 데이터 세트를 중첩되게 분리하는 것을 부트스트래핑(bootstrapping) 분할 방식이라고 한다.(그래서 배깅(Bagging)이 bootstrap aggregating의 줄임말이다.)

●랜덤 포레스트의 서브세트 데이터는 이러한 부트스트래핑으로 데이터가 임의로 만들어지고, 서브세트의 데이터 건수는 전체 데이터 건수와 동일하지만, 개별 데이터가 중첩되어 만들어진다.

원본 데이터의 건수가 10개인 학습 데이터 세트에 랜덤 포레스트를 3개의 결정 트리 기반으로 학습하려고 n_estimators=3으로 하이퍼 파라미터를 부여하면 위와 같이 데이터 서브세트가 만들어진다.

 

→이렇게 데이터가 중첩된 개별 데이터 세트에 결정 트리 분류기를 각각 적용하여 학습을 수행한 뒤 최종적으로 모든 분류기가 보팅을 통해 예측 결정을 하는 것이 랜덤 포레스트이다.

 

<랜덤 포레스트 하이퍼 파라미터>

사이킷런은 랜덤 포레스트 분류를 위해 RandomForestClassifier 클래스를 제공한다.(회귀의 경우 RandomForestRegressor)

●n_estimators: 랜덤 포레스트에서 결정 트리의 개수를 지정한다. 디폴트는 100개이다. 많이 설정할수록 좋은 성능을 기대할 수 있지만 계속 증가시킨다고 성능이 무조건 향상되는 것은 아니다. 또한 늘릴수록 학습 수행 시간이 오래 걸리는 것도 감안해야 한다.

●max_features는 결정 트리에 사용된 max_features 파라미터와 같다. 하지만 RandomForestClassifier의 기본 max_features는 'None'이 아니라 'auto', 즉 'sqrt'와 같다. 따라서 랜덤 포레스트의 트리를 분할하는 피처를 참조할 때 전체 피처가 아니라 sqrt만큼 참조한다.(전체 피처가 16개라면 분할을 위해 4개 참조-각각의 결정 트리는 이 4개의 피처가 다를 수 있음) 

●max_depth나 min_samples_leaf, min_samples_split과 같이 결정 트리에서 과적합을 개선하기 위해 사용되는 파라미터가 랜덤 포레스트에도 똑같이 적용될 수 있다.


이전에 사용자 행동 인식 데이터 세트를 RandomForestClassifier를 이용해 예측해 보자. 재 수행 할 때마다 동일한 예측 결과를 출력하기 위해 RandomForestClassifier의 random_state는 0으로 설정한다.

(해당 데이터 세트는 자주 사용되서 DataFrame을 생성하는 로직을 함수로 만들어났음)

import pandas as pd

feature_name_df = pd.read_csv(r'human_acttivity\features.txt', sep='\s+',
                             header=None, names=['column_index', 'column_name'])
def get_new_feature_name_df(old_feature_name_df):
    feature_dup_df = pd.DataFrame(data=feature_name_df.groupby(['column_name']).cumcount(),
                                                              columns=['dup_cnt'])
    feature_dup_df = feature_dup_df.reset_index()
    new_feature_name_df = pd.merge(old_feature_name_df.reset_index(), feature_dup_df, on='index', how='outer')
    new_feature_name_df['column_name'] = new_feature_name_df[['column_name', 'dup_cnt']].apply(lambda x : x[0]+'_'+str(x[1])
                                                                                               if x[1] > 0 else x[0], axis=1)
    new_feature_name_df = new_feature_name_df.drop(['index'], axis=1)
    return new_feature_name_df

def get_human_dataset():
    # 각 데이터 파일은 공백으로 분리되어 있으므로 read_csv에서 공백 문자를 sep로 할당
    feature_name_df = pd.read_csv(r'human_acttivity\features.txt', sep='\s+',
                             header=None, names=['column_index', 'column_name'])
    
    # 중복된 피처명을 수정하는 get_new_feature_name_df()를 이용, 신규 피처명 DataFrame 생성
    new_feature_name_df = get_new_feature_name_df(feature_name_df)
    # DataFrame에 피처명을 컬럼으로 부여하기 위해 리스트 객체로 다시 변환(values로 ndarray 변환 후 tolist()로 리스트 만듬)
    feature_name = new_feature_name_df.iloc[:, 1].values.tolist()
    
    # 학습 피처 데이터와 테스트 피처 데이터를 DataFrame으로 로딩. 컬럼명은 feature_name 적용
    X_train = pd.read_csv(r'human_acttivity\train\X_train.txt', sep='\s+', names=feature_name)
    X_test = pd.read_csv(r'human_acttivity\test\X_test.txt', sep='\s+', names=feature_name)
    
    # 학습 레이블 데이터와 테스트 레이블 데이터를 DataFrame으로 로딩. 컬럼명은 action으로 부여
    # (pd.read_csv() 할때 names를 할당했기 때문에 굳이 header=None 입력안해도 무방한듯)
    y_train = pd.read_csv(r'human_acttivity\train\y_train.txt', sep='\s+', header=None, names=['action']) 
    y_test = pd.read_csv(r'human_acttivity\test\y_test.txt', sep='\s+', header=None, names=['action']) 
    
    # 로드된 학습/테스트용 DataFrame을 모두 반환
    return X_train, X_test, y_train, y_test
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score
import warnings
warnings.filterwarnings('ignore')

X_train, X_test, y_train, y_test = get_human_dataset()

# 랜덤 포레스트 학습 및 별도의 테스트 데이터 세트로 예측 성능 평가
rf_clf = RandomForestClassifier(n_estimators=100, random_state=0, max_depth=8) # n_esitmator 디폴트 값은 100
rf_clf.fit(X_train, y_train)
pred = rf_clf.predict(X_test)
accuracy = accuracy_score(y_test, pred)
print('랜덤 포레스트 정확도:{0:.4f}'.format(accuracy))

이번에는 GridSearchCV를 이용해 랜덤 포레스트의 하이퍼 파라미터를 튜닝해보자. 튜닝 시간을 절약하기 위해 n_estimators는 100, cv는 2로만 설정해 최적의 하이퍼 파라미터를 구함.

그리고 n_jobs=-1 파라미터를 추가하면 모든 CPU 코어를 이용해 학습할 수 있다.

from sklearn.model_selection import GridSearchCV

params = {'max_depth':[8, 16, 24],
         'min_samples_leaf':[1, 6, 12],
         'min_samples_split':[2, 8, 16]
}
# RandomForestClassifier 객체 생성 후 GridSearchCV 수행
rf_clf = RandomForestClassifier(n_estimators=100, random_state=0, n_jobs=-1)
grid_cv = GridSearchCV(rf_clf, param_grid=params, cv=2, n_jobs=-1)
grid_cv.fit(X_train, y_train)

print('최적 하이퍼 파라미터:\n', grid_cv.best_params_)
print('최고 예측 정확도:{0:.4f}'.format(grid_cv.best_score_))

max_depth:16, min_samples_leaf:6, min_samples_split:2일 때 2개의 cv 세트에서 91.65%의 평균 정확도가 측정됐다.(검증용 데이터 세트에서 예측 성능 측정)

이렇게 추출된 최적의 하이퍼 파라미터로 다시 RandomForestClassifier를 학습시킨 뒤에 이번에는 별도의 테스트 데이터 세트에서 예측 성능을 측정해보자.

rf_clf1 = RandomForestClassifier(n_estimators=100, max_depth=16, min_samples_leaf=6,
                                min_samples_split=2, random_state=0)
rf_clf1.fit(X_train, y_train)
pred = rf_clf1.predict(X_test)
print('예측 정확도:{0:.4f}'.format(accuracy_score(y_test, pred)))

별도의 테스트 데이터 세트에서 수행한 예측 정확도는 92.60%이다. 

RandomForestClassifier도 DecisionTreeClassifier와 똑같이 feature_importances_ 속성을 이용해 알고리즘이 선택한 피처의 중요도를 알 수 있다.(Tree 기반 알고리즘은 feature_importances_ 속성을 가짐)

import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline

# 피처 중요도가 높은 상위 20개
ftr_importances_values = rf_clf1.feature_importances_
ftr_importances = pd.Series(ftr_importances_values, index=X_train.columns)
ftr_top20 = ftr_importances.sort_values(ascending=False)[:20]

plt.figure(figsize=(8,6))
plt.title('Feature importances Top 20')
sns.barplot(x=ftr_top20, y=ftr_top20.index)
plt.show()

+ Recent posts