Posts 앙상블(Ensemble)
Post
Cancel

앙상블(Ensemble)

1. 앙상블 기법


1.1 앙상블 기법

  • 앙상블 학습을 통한 분류 : 여러 개의 분류기를 생성하고 그 예측을 결합하여 정확한 최종 예측을 기대하는 기법
  • 앙상블 학습의 목표 : 다양한 분류기의 예측 결과를 결합함으로써 단일 분류기보다 신뢰성이 높은 예측 값을 얻는 것
  • 정형 데이터를 대상으로 하는 분류기에서는 앙상블 기법이 뛰어난 성과를 보여줌


1.2 Voting

  • 하나의 데이터셋을 여러개의 모델이 사용하여 투표를 최종 결정


1.3 Bagging

  • 데이터를 중복을 허용해서 샘플링하고 각각의 데이터에 같은 알고리즘을 적용하여 결과를 투표로 결정함
  • 각각의 분류기에 데이터를 각각 샘플링해서 추출하는 방식을 부트스트래핑방식이라고 함


1.4 Boosting

  • 여러개의 알고리즘이 순차적으로 학습을 하되 앞에 학습한 알고리즘 예측이 틀린 데이터에 대해 올바르게 예측할수 있도록 그 다음번 알고리즘에 가중치를 부여하여 학습과 예측을 진행하는 방식


1.5 Stacking

  • 여러가지 다른 모델의 예측 결과값을 다시 학습 데이터로 만들어 다른 모델로 재학습시켜 결과를 예측 하는 방법


1.6 Hard voting

  • 다수결의 원칙으로 투표와 비슷함


1.7 Soft voting

  • 각 알고리즘이 레이블 값 결정 확률을 예측해서, 이것을 평균하여 이들 중 가장 확률이 높은 레이블 값을 최종값으로 예측


1.8 Random Forest

  • 같은 알고리즘으로 구현하는 배깅의 대표적인 방법
  • 앙상블 방법 중에서 비교적 속도가 빠르며 다양한 영역에서 높은 성능을 보여줌
    • 부트스트래핑은 여러개의 작은 데이터셋을 중첩을 허용하여 만드는 것
  • 랜덤포레스트는 결정 나무를 기본으로 함
    • 부트스트래핑으로 샘플링된 데이터마다 결정나무가 예측한 결과를 소프트 보팅으로 최종 예측 결론을 얻음


2. HAR, Human Activity Recognition


2.1 IMU 센서를 활용해서 사람의 행동을 인식하는 실험

  • UCI HAR 데이터 셋은 스마트폰을 장착한 사람의 행동을 관찰한 데이터
  • 허리에 스마트폰을 착용하여 50Hz의 주파수로 데이터를 얻음
  • 6가지 활동(걷기, 계단 오르기, 계단 내려가기, 앉기, 일어서기, 눕기)을 수행
  • 내장 된 가속도계와 자이로 스코프를 사용하여 50Hz의 일정한 속도로 3축 선형 가속 및 3축 각속도를 갭처
  • 중력 및 신체 운동 성분을 갖는 센서 가속 신호는 버터 워스 저역 통과 필터를 사용하여 신체 가속 및 중력으로 분리


2.2 데이터의 특성

  • 가속도계로부터의 3축 가속도 및 추정 된 신체 가속도
  • 자이로 스코프의 3축 가속도
  • 시간 및 주파수 영역 변수가 포함된 561 기능 백터
  • 활동 라벨
  • 실험을 수행 한 대상의 식별자


2.3 데이터의 시간 영역

  • 실제 시간 영역의 데이터를 직접 사용하는것은 어려움
  • 해당 행동도 시간의 영역임 (움직였다가 멈췄다가 함)
  • 시간 영역 데이터를 머신러닝에 적용하기 위해 여러 통계적 데이터로 변환함
  • 시간 영역의 평균, 분산, 피크, 중간 값, 주파수 영역의 평균, 분산 등으로 변환한 수리를 가지고 있음


2.4 머신러닝의 행동 인식 연구

  • 센서신호 -> 특징추출 -> 모델학습 -> 행동추론
  • 센서신호를 받아, 시간 영역과 주파수 영역에서 특징을 추출하고, 모델을 학습, 행동 추론


2.5 데이터 로드

1
2
3
4
5
6
import pandas as pd
import matplotlib.pyplot as plt
url = 'https://raw.githubusercontent.com/hmkim312/datas/main/HAR/features.txt'

feature_name_df = pd.read_csv(url, sep = '\s+', header = None, names = ['column_index','column_name'])
feature_name_df.head()


column_indexcolumn_name
01tBodyAcc-mean()-X
12tBodyAcc-mean()-Y
23tBodyAcc-mean()-Z
34tBodyAcc-std()-X
45tBodyAcc-std()-Y


  • (https://github.com/hmkim312/datas/blob/main/HAR){: target=”_blank”}
  • 데이터는 깃헙에 올려둠
  • 지금은 데이터의 특성이름만 load한것임


2.6 특성의 갯수

1
print(len(feature_name_df))
1
561
  • 전체 특성 (feature)만 561개로 엄청 많음


2.7 특성의 종류

1
2
feature_name = feature_name_df.iloc[:,1].values.tolist()
feature_name[:5]
1
2
3
4
5
['tBodyAcc-mean()-X',
 'tBodyAcc-mean()-Y',
 'tBodyAcc-mean()-Z',
 'tBodyAcc-std()-X',
 'tBodyAcc-std()-Y']
  • 평균, 표준편차, 최대값 등으로 이루어져있음


2.8 X데이터 불러오기

1
2
3
4
5
6
X_train = pd.read_csv('https://raw.githubusercontent.com/hmkim312/datas/main/HAR/X_train.txt', sep = '\s+',  header = None)
X_test = pd.read_csv('https://raw.githubusercontent.com/hmkim312/datas/main/HAR/X_test.txt', sep = '\s+',  header = None)

X_train.columns = feature_name
X_test.columns = feature_name
X_train.head()
tBodyAcc-mean()-XtBodyAcc-mean()-YtBodyAcc-mean()-ZtBodyAcc-std()-XtBodyAcc-std()-YtBodyAcc-std()-ZtBodyAcc-mad()-XtBodyAcc-mad()-YtBodyAcc-mad()-ZtBodyAcc-max()-X...fBodyBodyGyroJerkMag-meanFreq()fBodyBodyGyroJerkMag-skewness()fBodyBodyGyroJerkMag-kurtosis()angle(tBodyAccMean,gravity)angle(tBodyAccJerkMean),gravityMean)angle(tBodyGyroMean,gravityMean)angle(tBodyGyroJerkMean,gravityMean)angle(X,gravityMean)angle(Y,gravityMean)angle(Z,gravityMean)
00.288585-0.020294-0.132905-0.995279-0.983111-0.913526-0.995112-0.983185-0.923527-0.934724...-0.074323-0.298676-0.710304-0.1127540.030400-0.464761-0.018446-0.8412470.179941-0.058627
10.278419-0.016411-0.123520-0.998245-0.975300-0.960322-0.998807-0.974914-0.957686-0.943068...0.158075-0.595051-0.8614990.053477-0.007435-0.7326260.703511-0.8447880.180289-0.054317
20.279653-0.019467-0.113462-0.995380-0.967187-0.978944-0.996520-0.963668-0.977469-0.938692...0.414503-0.390748-0.760104-0.1185590.1778990.1006990.808529-0.8489330.180637-0.049118
30.279174-0.026201-0.123283-0.996091-0.983403-0.990675-0.997099-0.982750-0.989302-0.938692...0.404573-0.117290-0.482845-0.036788-0.0128920.640011-0.485366-0.8486490.181935-0.047663
40.276629-0.016570-0.115362-0.998139-0.980817-0.990482-0.998321-0.979672-0.990441-0.942469...0.087753-0.351471-0.6992050.1233200.1225420.693578-0.615971-0.8478650.185151-0.043892

5 rows × 561 columns


  • 데이터는 깃헙에 따로 올려주었음
  • 데이터 용량이 생각보다 커서 오래걸림
  • 앞에서 불러온 feature_name을 컬럼으로 사용함

2.9 Y데이터 불러오기

1
2
3
y_train = pd.read_csv('https://raw.githubusercontent.com/hmkim312/datas/main/HAR/y_train.txt', sep = '\s+',  header = None, names = ['action'])
y_test = pd.read_csv('https://raw.githubusercontent.com/hmkim312/datas/main/HAR/y_test.txt', sep = '\s+',  header = None, names = ['action'])
X_train.shape, X_test.shape, y_train.shape, y_test.shape
1
((7352, 561), (2947, 561), (7352, 1), (2947, 1))
  • 마찬가지로 y 데이터를 불러옴
  • 전체 데이터는 약 1만개가 넘음


2.10 액션별 데이터의 수

1
y_train['action'].value_counts()
1
2
3
4
5
6
7
6    1407
5    1374
4    1286
1    1226
2    1073
3     986
Name: action, dtype: int64
  • 총 6개의 액션 (앉기, 서기, 걷기, 계단 오르기, 계단 내려가기, 눕기)의 갯수
  • 1 = Walking
  • 2 = Walkling Upstairs
  • 3 = Walking Downstairs
  • 4 = Sitting
  • 5 = Standing
  • 6 = Laying


2.11 Decision Tree로 하기

1
2
3
4
5
6
7
8
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import accuracy_score

dt_clf = DecisionTreeClassifier(random_state=13, max_depth=4)
dt_clf.fit(X_train, y_train)
pred = dt_clf.predict(X_test)

accuracy_score(y_test, pred)
1
0.8096369189005769
  • 딱히 하이퍼파라미터 튜닝을 하지 않고 결정나무를 돌려봤을때 Accuracy는 0.8정도가 나옴


2.12 Gridsearch로 Max_depth 설정

1
2
3
4
5
6
7
8
from sklearn.model_selection import GridSearchCV

params = {'max_depth': [6, 8, 10, 12, 16, 20, 24]}

grid_cv = GridSearchCV(estimator=dt_clf, param_grid=params,
                       scoring='accuracy', cv=5, return_train_score=True)

grid_cv.fit(X_train, y_train)
1
2
3
4
GridSearchCV(cv=5,
             estimator=DecisionTreeClassifier(max_depth=4, random_state=13),
             param_grid={'max_depth': [6, 8, 10, 12, 16, 20, 24]},
             return_train_score=True, scoring='accuracy')
  • GridSearchCV를 이용하여 6,8,10,12,16,20,24의 max_depth를 조절해서 가장 높은 Accuracy가 나오는 것을 구함


2.13 best score 및 param은?

1
grid_cv.best_score_
1
0.8543335321892183


1
grid_cv.best_params_
1
{'max_depth': 8}
  • 가장 좋은 Accuracy는 0.85가 나왔고, depth를 8로 했을때가 가장 좋음


2.14 Max_depth 별로 표로 성능을 정리

1
2
cv_result_df = pd.DataFrame(grid_cv.cv_results_)
cv_result_df[['param_max_depth', 'mean_test_score', 'mean_train_score']]
param_max_depthmean_test_scoremean_train_score
060.8434440.944879
180.8543340.982692
2100.8471250.993369
3120.8419580.997212
4160.8419580.999660
5200.8423650.999966
6240.8418211.000000


  • train과 test score에 차이가 있음 과적합일수도 있음


2.15 실제 Test 데이터에서의 결과

1
2
3
4
5
6
7
8
max_depths = [6, 8, 10, 12, 16, 20, 24]

for depth in max_depths:
    dt_clf = DecisionTreeClassifier(max_depth=depth, random_state=13)
    dt_clf.fit(X_train, y_train)
    pred = dt_clf.predict(X_test)
    accuracy = accuracy_score(y_test, pred)
    print(f'Max_depth = {depth}, Accuracy = {accuracy}')
1
2
3
4
5
6
7
Max_depth = 6, Accuracy = 0.8554462164913471
Max_depth = 8, Accuracy = 0.8734306073973532
Max_depth = 10, Accuracy = 0.8615541228367831
Max_depth = 12, Accuracy = 0.8595181540549711
Max_depth = 16, Accuracy = 0.8669833729216152
Max_depth = 20, Accuracy = 0.8652867322701052
Max_depth = 24, Accuracy = 0.8652867322701052
  • 실제 Test 데이터로 해본 결과 가장 높은 0.87 Accuracy가 나왔다.


2.16 베스트 모델의 결과

1
2
3
4
best_df_clf = grid_cv.best_estimator_
pred1 = best_df_clf.predict(X_test)

accuracy_score(y_test, pred1)
1
0.8734306073973532
  • 생각보다 잘 나오지만 과적합 의심을 계속 해야함


2.17 랜덤포레스트에 적용

1
2
3
4
5
6
7
8
9
10
11
12
13
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import GridSearchCV

params = {
    'max_depth': [6, 8, 10],
    'n_estimators' : [50, 100, 200],
    'min_samples_leaf' : [6, 12],
    'min_samples_split' : [6, 12]
}

rf_clf = RandomForestClassifier(random_state= 13, n_jobs= -1)
grid_cv = GridSearchCV(rf_clf, param_grid= params, cv = 2, n_jobs= -1)
grid_cv.fit(X_train, y_train)
1
2
3
4
5
GridSearchCV(cv=2, estimator=RandomForestClassifier(n_jobs=-1, random_state=13),
             n_jobs=-1,
             param_grid={'max_depth': [6, 8, 10], 'min_samples_leaf': [6, 12],
                         'min_samples_split': [6, 12],
                         'n_estimators': [50, 100, 200]})
  • 똑같이 GridSearchCV를 이용하여 찾았으며 Cross Validation은 2로 설정하였다.

  • max_depth : int or None, optional (default=None)
    • 트리의 깊이
    • None 이면 최대한 깊게 (불순도 혹은 복잡도가 0일 때까지)
    • 클수록 정확 (과대적합)
    • 작을수록 가지치기 (과대적합 방지)
  • max_leaf_nodes : int or None, optional (default=None)
    • 최대 몇개 잎 노드가 만들어 질때 까지 split(하위 (잎) 노드로 분리) 될지
    • 클수록 정확 (과대적합)
    • 작을수록 가지치기 (과대적합 방지)
  • min_samples_split : int, float, optional (default=2)
    • 샘플이 최소한 몇개 이상이어야 split(하위 (잎) 노드로 분리) 할것인지
    • int일 경우 주어진 값을 그대로 사용, float일 경우 0에서 1사이의 값을 줄 수 있으며 전체 데이터 수 * min_sample_split의 값을 사용
    • 클수록 가지치기 (과대적합 방지)
    • 작을수록 정확 (과대적합)
  • min_samples_leaf : int, float, optional (default=1)
    • (잎) 노드가 되려면 가지고 있어야할 최소 샘플 수
    • 클수록 가지치기 (과대적합 방지)
    • 작을수록 정확 (과대적합)


2.18 결과 정리를 위한 작업

1
2
cv_result_df = pd.DataFrame(grid_cv.cv_results_)
cv_result_df.columns
1
2
3
4
5
6
Index(['mean_fit_time', 'std_fit_time', 'mean_score_time', 'std_score_time',
       'param_max_depth', 'param_min_samples_leaf', 'param_min_samples_split',
       'param_n_estimators', 'params', 'split0_test_score',
       'split1_test_score', 'mean_test_score', 'std_test_score',
       'rank_test_score'],
      dtype='object')
  • 결과를 데이터프레임으로 생성


2.19 랜덤포레스트 성능 확인

1
2
3
4
target_col = ['rank_test_score', 'mean_test_score',
              'param_n_estimators', 'param_max_depth']

cv_result_df[target_col].sort_values('rank_test_score').head()
rank_test_scoremean_test_scoreparam_n_estimatorsparam_max_depth
2310.9128132008
2010.9128132008
3530.91254120010
3230.91254120010
2950.91172520010
  • Max_depth는 8과 분류기는 200개를 사용하는게 가장 높은 결과를 가져옴
  • 의사결정나무보다 성능이 좋게 나옴


2.20 랜덤 포레스트의 Best 모델

1
grid_cv.best_params_
1
2
3
4
{'max_depth': 8,
 'min_samples_leaf': 12,
 'min_samples_split': 6,
 'n_estimators': 200}


1
grid_cv.best_estimator_
1
2
RandomForestClassifier(max_depth=8, min_samples_leaf=12, min_samples_split=6,
                       n_estimators=200, n_jobs=-1, random_state=13)


1
grid_cv.best_score_
1
0.9128128400435256


  • 랜덤포레스트의 베스트 모델은
  • Max_depth가 8이고 최소 노드가 되기위한 샘플 수는 6개, 최소 노드는 12개의 파라미터가 나왔음


2.21 test 데이터에 적용

1
2
3
4
5
6
rf_clf_best = grid_cv.best_estimator_
rf_clf_best.fit(X_train, y_train)

pred1 = rf_clf_best.predict(X_test)

accuracy_score(y_test, pred1)
1
0.9121140142517815
  • 베스트 모델을 Test 데이터에 적용해도 의사결정 나무보다 더 높은 Accuracy를 보여줌


2.22 중요 특성 확인

1
2
3
4
best_cols_values = rf_clf_best.feature_importances_
best_cols = pd.Series(best_cols_values, index = X_train.columns)
top20_cols = best_cols.sort_values(ascending = False)[:20]
top20_cols
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
tGravityAcc-max()-X                0.033146
angle(X,gravityMean)               0.031783
tGravityAcc-mean()-X               0.031114
angle(Y,gravityMean)               0.028749
tGravityAcc-min()-X                0.028376
tGravityAcc-mean()-Y               0.027525
tGravityAcc-max()-Y                0.026787
tGravityAcc-energy()-X             0.024104
tGravityAcc-min()-Y                0.023466
tGravityAcc-energy()-Y             0.019362
tBodyAcc-max()-X                   0.013909
tGravityAcc-mean()-Z               0.013876
tBodyAccMag-std()                  0.013222
tGravityAcc-max()-Z                0.012954
fBodyAccJerk-bandsEnergy()-1,8     0.011679
fBodyAccJerk-bandsEnergy()-1,24    0.011569
tGravityAcc-min()-Z                0.010980
tBodyAccJerk-entropy()-X           0.010875
tBodyAccMag-mad()                  0.010858
tGravityAcc-arCoeff()-Z,2          0.010688
dtype: float64
  • feature_importances_ 메서드로 결과에 영향을 미친 특성을 볼수있음
  • 전체 특성이 561개나 되기때문에 각 개의 특성은 큰 값을 가지진 못함


2.23 주요 특성 그래프

1
2
3
4
5
import seaborn as sns

plt.figure(figsize=(8, 8))
sns.barplot(x=top20_cols, y=top20_cols.index)
plt.show()

  • 주요 특성 20개를 그래프화 함
  • tGravityAcc-max()-X 특성이 제일 중요하게 나옴


2.24 주요 20개 특성만 가지고 다시 성능 확인

1
2
3
4
5
6
7
8
9
X_train_re = X_train[top20_cols.index]
X_test_re = X_test[top20_cols.index]

rf_clf_best_re = grid_cv.best_estimator_
rf_clf_best_re.fit(X_train_re, y_train.values.reshape(-1, ))

pred1_re = rf_clf_best_re.predict(X_test_re)

accuracy_score(y_test, pred1_re)
1
0.8096369189005769
  • 561개 특성보다 20개 특성으로하니 모델 학습 및 예측에는 오래걸리진 않지만, acc가 떨어짐
This post is licensed under CC BY 4.0 by the author.