Ensemble 학습의 Bagging 의 대표적인 방식
앙상블이랑 여러개의 알고리즘을 사용하여, 그 예측을 결정함으로써 보다 정확한 예측을 도출하는 기법.
기존의 Decision Tree에서 그 트리가 엄청많아져서, 그것들의 최종 값의 비율로 계산하자!
각각 부트스트랩으로 만들어진 데이터셋으로 학습하게되는데, 이를 Bagging (Bootstrap AGGregatING) 이라고 한다.
오리지널 데이터에서 복원추출하는데, 샘플을 뽑고, 다시 원본에서 샘플을 뽑는다. (중복될 수 있다는것)
원본 데이터셋에서 복원추출하는 것을 Bootstrap Sampling이라 한다.
그렇게 samples들을 뽑아서 원본과 같은 수의 sample을 구성하면 그게 부트스트랩샘플을 하나 만들었다고 한다.
각각의 부트스트랩 샘플의 Training set 에서 뽑히지 않은 샘플들을 test set으로 분류하고 이를 OOB Sample이라고 한다.
→ 100 개 데이터 중 70개로 트레인하고, 나머지 30개로 validation을 매 트리 생성시마다 한다고 보면 됨.
앙상블 기법은 한 종류의 데이터로 여러 학습모델을 만들어, 그 모델들의 예측결과를 다수결이나 펴균을 내어 예측하는 방법을 말한다.
랜덤포레스트는 Decision Tree를 기본모델로 사용한다.
부트스트랩 세트의 크기가 n일 때, 한번의 추출에서 어느 한 샘플이 추출되지 않을 확률은 ${n-1} \over n$ 이다.
그것을 n번 반복였을 때에도 그 샘플이 추출되지 않았을 확률은 $ ({{n-1}\over n})^2$ 이다.
이게 이제 무한히 커지면 어느 한 점으로 수렴하게 되는데.
결과적으로 데이터가 충분할 때 한 부트스트랩 세트는 63.2%의 샘플을 갖고,
여기에 속하지 않은 36.8% 의 샘플이 Out-of_Bag 샘플이며, 이것이 검증에 사용된다.
트리모델에서는 One-hot Encoding보다 Ordinal Encoding이 좀더 성능이 좋을 때가 많다.
원핫으로 피쳐가 추가되었을 때, 트리에서는 상위노드에서 중요한 피처가 사용되므로, 하나의 피쳐가 여러개의 피쳐로 쪼개진 One-Hot에서 범주 종류가 많은 (High Cardinality) 피쳐였을 경우 상위노드에서 선택될 가능성이 적어진다.
- n_estimators = 트리의 갯수 | number of trees in the foreset
- max_features = 노드들 구분할 때 고려할 최대 피쳐수 | max number of features considered for splitting a node
- max_depth = 각 트리의 최대 깊이 | max number of levels in each decision tree
- min_samples_split = 최소 분할 샘플 갯수 | min number of data points placed in a node before the node is split
- min_samples_leaf = 최소 잎 노드 데이터 수 | min number of data points allowed in a leaf node
- bootstrap = 복원 추출 | method for sampling data points (with or without replacement)
랜덤 포레스트의 Pseudo Code
S는 트레이닝셋, Feature 는 F, 랜덤포레스트 안의 트리는 B 라고 한다.
H를 초기화하고,
1부터 B까지 (트리들을 순회하면서),
$S^(i)$ 에 S로 부터 만든 부트스트랩 샘플을 넣고,
$h_i$ 에 트리를 학습시키고(트리의 학습은, 각각의 노드에서 feature의 subset을 최적으로 쪼개면서 학습시킨다. ),
이후, 학습된 트리들 하나하나를 H에 합친 후, 최종 H를 리턴.
from sklearn.model_selection import train_test_split
train, val = train_test_split(train, train_size=0.8, test_size=0.2,
stratify=train[target], random_state=2)
##############################################
from category_encoders import OrdinalEncoder
from sklearn.pipeline import make_pipeline
pipe_ordinal_simple=make_pipeline(verbose=0,
OrdinalEncoder(verbose=0),
SimpleImputer(),
RandomForestClassifier(n_jobs=-1, random_state=10, oob_score=True)
)
pipe_ordinal_simple.fit(X_train,y_train)
pipe_ordinal_simple.score(X_val,y_val) #0.8258806784485826
ㅇ 이렇게 파이프라인속에 넣어서 사용할 수도 있다.
X_train으로 fit(학습)시키고, X_val로 분리해놓은 데이터를 통하여 점수를 매겨보는 등의 작업을 하면된다.
하이퍼파라미터 튜닝
- n_estimators = 트리의 갯수 | number of trees in the foreset
- max_features = 노드들 구분할 때 고려할 최대 피쳐수 | max number of features considered for splitting a node
- max_depth = 각 트리의 최대 깊이 | max number of levels in each decision tree
- min_samples_split = 최소 분할 샘플 갯수 | min number of data points placed in a node before the node is split
- min_samples_leaf = 최소 잎 노드 데이터 수 | min number of data points allowed in a leaf node
- bootstrap = 복원 추출 | method for sampling data points (with or without replacement)
- class_weight = sample의 라벨 비율 -> 기존의 분류문제에서 라벨비율이 비슷하지 않으면, 좋지 않은 학습결과를 가져올 수 있는데, 이를 처리해줌.
class_weight 설정
어떠한경우 target 의 class 비율이 다를 수 있다. 이는 모델 학습에 악영향을 끼치는데, 이 부분에 대하여 RandomForest에서는 class_weight라는 파라미터를 준다.
from sklearn.utils.class_weight import compute_class_weight
print(y_train.unique(),'\n',y_train.value_counts())
compute_class_weight(class_weight='balanced', classes = y_train.unique(), y=y_train)
# [0 1]
# 0 29269
# 1 3681
# Name: y, dtype: int64
# array([0.56288223, 4.47568595])
## _____________________ ##
from sklearn.ensemble import RandomForestClassifier
rfc = RandomForestClassifier(n_estimators=250, class_weight={1:0.56288223 , 0:4.47568595})
rfc.fit(X_train, y_train)
rfcpred = rfc.predict(X_val)
print(confusion_matrix(y_val, rfcpred))
print('Accuracy Score : ',accuracy_score(y_val, rfcpred))
print('f1 Score : ',f1_score(y_val, rfcpred))
print('\nRandomForest Regression Matrix\n' , classification_report(y_val,rfcpred))
이를 통하여 불균형한 데이터 셋에서 Upsampling과 Downsampling하지 않고도 주어진 데이터를 최선으로 활용하여 학습을 진행할 수 있다.
Max_depth 튜닝
def pipe_ordinal_simple_elbow(howmany,X_train, y_train, X_val, y_val):
trainAccuracy=[]
valAccuracy=[]
for depth in range(1,howmany):
pipe_ordinal_simple=make_pipeline(
OrdinalEncoder(verbose=0),
SimpleImputer(),
RandomForestClassifier(max_depth=depth,n_jobs=-1, random_state=10, oob_score=True)
)
pipe_ordinal_simple.fit(X_train,y_train)
trainAccuracy.append(pipe_ordinal_simple.score(X_train,y_train))
valAccuracy.append(pipe_ordinal_simple.score(X_val,y_val))
return trainAccuracy, valAccuracy
ta_msl , tv_msl = pipe_ordinal_simple_elbow(howmany=20 ,
X_train=X_train,
y_train=y_train,
X_val=X_val,
y_val=y_val)
pd.DataFrame({'msl_training': ta_msl, 'msl_val':tv_msl}, index=range(1,20)).plot(figsize=(10,8))
plt.grid()
depth 가 7~8을 넘어가는 부분부터 training과 validation의 accuracy score가 확연히 달라진다.
깊어질수록 training은 정확도가 높아지는반면, val의 정확도는 거의 변하지않는다. 이는 과적합되는 부분이라고 봐도 될것이다.
n_estimator
n_estimators = 트리의 갯수 | number of trees in the foreset
한마디로 투표용지의 개수가 얼마나 많아지는가? 라고 생각하면 된다.
def pipe_ordinal_simple_elbow_estimators(howmany,X_train, y_train, X_val, y_val):
trainAccuracy=[]
valAccuracy=[]
for depth in range(10,howmany, 20):
pipe_ordinal_simple=make_pipeline(
OrdinalEncoder(verbose=0),
SimpleImputer(),
RandomForestClassifier(n_estimators=depth,n_jobs=-1, random_state=10, oob_score=True)
)
pipe_ordinal_simple.fit(X_train,y_train)
trainAccuracy.append(pipe_ordinal_simple.score(X_train,y_train))
valAccuracy.append(pipe_ordinal_simple.score(X_val,y_val))
return trainAccuracy, valAccuracy
ta_msl , tv_msl = pipe_ordinal_simple_elbow_estimators(howmany=200 ,
X_train=X_train,
y_train=y_train,
X_val=X_val,
y_val=y_val)
pd.DataFrame({'msl_training': ta_msl, 'msl_val':tv_msl}, index=range(10,200,20)).plot(figsize=(10,8))
plt.grid()
시작을 10부터 잡아서 그런지, 거의 변화가 너무 적다. 하지만 확실하게 1부터 시작한다면 증가하는 폭을 볼 수 있을것이다.
GridSearchCV 를 통한 하이퍼파라미터 튜닝
from sklearn.model_selection import GridSearchCV
params = { 'randomforestclassifier__n_estimators' : [50, 100, 150],
'randomforestclassifier__max_depth' : [6, 9, 12],
'randomforestclassifier__min_samples_leaf' : [8, 12],
'randomforestclassifier__min_samples_split' : [8, 16]
}
pipe_ordinal_simple=make_pipeline(
OrdinalEncoder(verbose=0),
SimpleImputer(),
RandomForestClassifier(n_jobs=-1, random_state=10, oob_score=True)
)
grid=GridSearchCV(pipe_ordinal_simple , param_grid=params , cv=5, verbose=0)
grid.fit(X_train,y_train)
grid.best_estimator_
Pipeline(steps=[('ordinalencoder',
OrdinalEncoder(cols=['opinion_h1n1_vacc_effective',
'opinion_h1n1_risk',
'opinion_h1n1_sick_from_vacc',
'opinion_seas_vacc_effective',
'opinion_seas_risk',
'opinion_seas_sick_from_vacc', 'agegrp',
'employment_status', 'census_msa',
'state'],
mapping=[{'col': 'opinion_h1n1_vacc_effective',
'data_type': dtype('O'),
'mapping': Somew...
TENNESSEE 39
FLORIDA 40
NEW HAMPSHIRE 41
IDAHO 42
MAINE 43
ALASKA 44
WISCONSIN 45
OKLAHOMA 46
MASSACHUSETTS 47
NORTH DAKOTA 48
WASHINGTON 49
NEBRASKA 50
HAWAII 51
NaN -2
dtype: int64}])),
('simpleimputer', SimpleImputer()),
('randomforestclassifier',
RandomForestClassifier(max_depth=12, min_samples_leaf=12,
min_samples_split=8, n_estimators=150,
n_jobs=-1, oob_score=True,
random_state=10))])
이러한 출력을 볼 수 있다.
이제 이 파라미터들의 상태를 시각화해본다
cols='param_randomforestclassifier__max_depth param_randomforestclassifier__min_samples_leaf param_randomforestclassifier__min_samples_split param_randomforestclassifier__n_estimators mean_test_score'.split()
griddf=pd.DataFrame(grid.cv_results_)
fig,axs=plt.subplots(2,2, figsize=(10,10))
sns.pointplot(ax=axs[0][0], data=griddf, x=cols[0],y=cols[-1])
sns.pointplot(ax=axs[0][1], data=griddf, x=cols[1],y=cols[-1])
sns.pointplot(ax=axs[1][0], data=griddf, x=cols[2],y=cols[-1])
sns.pointplot(ax=axs[1][1], data=griddf, x=cols[3],y=cols[-1])
max_depth의 변화에 가장 크게 증가하였으며, 그외에는 오히려 조금 떨어진 경우가 많았다.
Feature Importance
plt.figure(figsize=(4,8), dpi=110)
sns.barplot(data=
pd.DataFrame(grid.best_estimator_['randomforestclassifier'].feature_importances_,
index=X_train.columns, columns=['feature_importances_']).reset_index().sort_values('feature_importances_',ascending=False)
, x='feature_importances_', y='index')
'Machine Learning > Tree Based Models' 카테고리의 다른 글
Decision Tree (결정트리) (0) | 2021.02.10 |
---|