3 분 소요


분류

앙상블 (Ensemble Learning)

여러개의 분류기(Classifier)를 생성하고 그 예측을 결합함으로써 보다 정확한 최종 예측을 도출하는 기법이다.
단일 분류기보다 신뢰성이 높은 예측값을 얻는 것이다.
정형 데이터 분류 시에 뛰어난 성능을 나타내고 있다.

  • 앙상블 학습의 유형
    1. 보팅(Voting): 서로 다른 알고리즘을 가진 분류기를 결합, 같은 Data Set을 대상으로 한다. 최종 예측결과는 투표를 통해 결정한다.
    2. 배깅(Bagging): 각각의 분류기가 같은 유형의 알고리즘 기반이지만 데이터 샘플링을 서로 다르게 가져가 학습을 수행한다. 최종 예측결과는 투표를 통해 결정한다. 대표적으로 랜덤 포레스트 알고리즘이 있다.
  • 부트스트래핑(Bootstrapping) 분할방식: 개별 분류기에게 데이터를 샘플링해서 추출하는 방식이다. 데이터 세트 간의 중첩을 허용한다.
    1. 부스팅(Boosting): 여러 개의 분류기가 순차적으로 학습을 수행하되, 앞서 학습한 분류기가 예측이 틀린 데이터에 대해 올바르게 예측할 수 있도록 다음 분류기에게 가중치(weight)를 부여하면서 학습과 예측을 진행한다.

img

보팅 유형

img

  • 하드 보팅(Hard Voting)
    다수결의 원칙 - 다수의 분류기가 결정한 예측값을 최종 보팅 결괏값을 선정한다.

  • 소프트 보팅(Soft Voting)
    분류기들의 레이블 값 결정 확률을 모두 더하고 이를 평균해서 이들 중 확률이 가장 높은 레이블 값을 최종 보팅 결괏값으로 선정한다.

일반적으로 소프트 보팅 방법을 적용한다.

보팅 분류기(Voting Classifier)

위스콘신 유방암 데이터 세트를 예측 분석한다.
유방암의 악성종양, 양성종양 여부를 결정하는 이진 분류 데이터 세트이며 종양의 크기, 모양 등의 형태와 관련한 많은 피처를 가지고 있다.
로지스틱 회귀와 KNN을 기반으로 보팅 분류기를 만든다.

In [1]:
import pandas as pd
from sklearn.ensemble import VotingClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.neighbors import KNeighborsClassifier
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
import warnings
warnings.filterwarnings('ignore')
In [2]:
cancer = load_breast_cancer(as_frame=True)
In [3]:
cancer.data.head(3)
Out [3]:
mean radius mean texture mean perimeter mean area mean smoothness mean compactness mean concavity mean concave points mean symmetry mean fractal dimension ... worst radius worst texture worst perimeter worst area worst smoothness worst compactness worst concavity worst concave points worst symmetry worst fractal dimension
0 17.99 10.38 122.8 1001.0 0.11840 0.27760 0.3001 0.14710 0.2419 0.07871 ... 25.38 17.33 184.6 2019.0 0.1622 0.6656 0.7119 0.2654 0.4601 0.11890
1 20.57 17.77 132.9 1326.0 0.08474 0.07864 0.0869 0.07017 0.1812 0.05667 ... 24.99 23.41 158.8 1956.0 0.1238 0.1866 0.2416 0.1860 0.2750 0.08902
2 19.69 21.25 130.0 1203.0 0.10960 0.15990 0.1974 0.12790 0.2069 0.05999 ... 23.57 25.53 152.5 1709.0 0.1444 0.4245 0.4504 0.2430 0.3613 0.08758

3 rows × 30 columns

In [4]:
cancer.target.head(3)
Out [4]:
0    0
1    0
2    0
Name: target, dtype: int32
In [5]:
cancer.target_names # malignant: 악성, benign: 양성
Out [5]:
array(['malignant', 'benign'], dtype='<U9')
In [6]:
# 로지스틱 회귀와 KNN 모델 생성
lr_clf = LogisticRegression(solver='liblinear')
knn_clf = KNeighborsClassifier(n_neighbors=8) # n_neighbors: 기준점의 수

# 개별 모델을 소프트 보팅 기반의 앙상블 모델로 구현한 분류기
vo_clf = VotingClassifier([('LR', lr_clf), ('KNN', knn_clf)], voting='soft') # estimators=[(str, estimator), ...]

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

# 앙상블 모델 학습/예측/평가
vo_clf.fit(X_train, y_train)
pred = vo_clf.predict(X_test)
print(f'Voting 분류기 정확도: {accuracy_score(y_test, pred):.4f}')
Out [6]:
Voting 분류기 정확도: 0.9561

In [7]:
# 개별 모델 학습/예측/평가
models = [lr_clf, knn_clf]
for model in models:
    model.fit(X_train, y_train)
    pred = model.predict(X_test)
    model_name = model.__class__.__name__
    print(f'{model_name} 정확도: {accuracy_score(y_test, pred):.4f}')
Out [7]:
LogisticRegression 정확도: 0.9474
KNeighborsClassifier 정확도: 0.9386

개별 모델보다 보팅한 앙상블 모델의 정확도가 높게 나왔다.
보편적으로 앙상블 모델이 잘 나오는 경우가 많으나 무조건 기반 분류기보다 예측 성능이 향상되지는 않는다.

랜덤 포레스트

배깅의 대표적인 알고리즘이다.
같은 알고리즘으로 여러 개의 분류기를 만들어서 보팅으로 최종 결정한다. 여러 개의 결정트리 분류기가 전체 데이터에서 배깅 방식으로 각자의 데이터를 샘플링해 개별적으로 학습을 수행한 뒤 최종적으로 모든 분류기가 보팅을 통해 예측 결정을 하게 된다.

In [8]:
from sklearn.ensemble import RandomForestClassifier
In [9]:
# 앞서 작성한 함수 가져오기
def get_new_df(old_df):
    dup_df = pd.DataFrame(data=old_df.groupby('column_name').cumcount(), columns=['dup_cnt']) # cumcount 그룹별로 묶어 같은값에 대해 번호를 매김
    dup_df = dup_df.reset_index() # 기존의 인덱스값을 하나의 컬럼으로 추가
    new_df = pd.merge(old_df.reset_index(), dup_df, how='outer') # reset_index하여 추가된 컬럼을 기준으로 병합
    new_df['column_name'] = new_df[['column_name', 'dup_cnt']].apply(lambda x: x[0]+'_'+str(x[1]) if x[1]>0 else x[0], axis=1)
    new_df.drop(columns=['index'], inplace=True)
    return new_df

def get_human_dataset():
    feature_name_df = pd.read_csv('human_activity/features.txt', sep='\s+',
                              header=None, names=['column_index', 'column_name'])
    name_df = get_new_df(feature_name_df)
    feature_name = name_df.iloc[:, 1].values.tolist()
    
    X_train = pd.read_csv('human_activity/train/X_train.txt', sep='\s+', names=feature_name)
    X_test = pd.read_csv('human_activity/test/X_test.txt', sep='\s+', names=feature_name)
    
    y_train = pd.read_csv('human_activity/train/y_train.txt', sep='\s+', names=['action'])
    y_test = pd.read_csv('human_activity/test/y_test.txt', sep='\s+', names=['action'])
    
    return X_train, y_train, X_test, y_test
In [10]:
X_train, y_train, X_test, y_test = get_human_dataset()
In [11]:
rf_clf = RandomForestClassifier(random_state=0) # DecisionTree의 묶음이기에 같은 파라미터를 가짐
rf_clf.fit(X_train, y_train)
pred = rf_clf.predict(X_test)
print(f'랜덤 포레스트 정확도: {accuracy_score(y_test, pred):.4f}')
Out [11]:
랜덤 포레스트 정확도: 0.9253

하이퍼 파라미터 튜닝

In [12]:
from sklearn.model_selection import GridSearchCV
In [13]:
params = {
    'max_depth':[8, 16, 24],
    'min_samples_leaf':[1, 6, 12],
    'min_samples_split':[2, 8, 16]
}
In [14]:
# RandomForestClassifier 객체 생성 후 GridSearchCV 수행
rf_clf = RandomForestClassifier(random_state=0, n_jobs=-1) # n_jobs: 모든 프로세스를 사용
grid_cv = GridSearchCV(rf_clf, param_grid=params, cv=2, n_jobs=-1) # 3*3*3*2=54 경우의 수
In [15]:
%%time
grid_cv.fit(X_train, y_train)
Out [15]:
Wall time: 38.3 s

GridSearchCV(cv=2, estimator=RandomForestClassifier(n_jobs=-1, random_state=0),
             n_jobs=-1,
             param_grid={'max_depth': [8, 16, 24],
                         'min_samples_leaf': [1, 6, 12],
                         'min_samples_split': [2, 8, 16]})
In [16]:
print(f'최적 하이퍼 파라미터: {grid_cv.best_params_}')
print(f'최고 예측 정확도: {grid_cv.best_score_:.4f}')
Out [16]:
최적 하이퍼 파라미터: {'max_depth': 16, 'min_samples_leaf': 6, 'min_samples_split': 2}
최고 예측 정확도: 0.9165

In [17]:
rf_clf = RandomForestClassifier(max_depth=16, min_samples_leaf=6, min_samples_split=2, random_state=0)
rf_clf.fit(X_train, y_train)
pred = rf_clf.predict(X_test)
print(f'튜닝 후 랜덤 포레스트 정확도: {accuracy_score(y_test, pred):.4f}')
Out [17]:
튜닝 후 랜덤 포레스트 정확도: 0.9260

In [18]:
# 피처 중요도 시각화
import matplotlib.pyplot as plt
import seaborn as sns
In [19]:
data = pd.Series(rf_clf.feature_importances_, index=X_train.columns)
top10 = data.sort_values(ascending=False)[:10]

plt.figure(figsize=(8, 6))
plt.title('Top 10 피처 중요도')
sns.barplot(x=top10, y=top10.index, palette='hls')
plt.show()
Out [19]:

img

Reference

댓글남기기