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_index | column_name | |
---|---|---|
0 | 1 | tBodyAcc-mean()-X |
1 | 2 | tBodyAcc-mean()-Y |
2 | 3 | tBodyAcc-mean()-Z |
3 | 4 | tBodyAcc-std()-X |
4 | 5 | tBodyAcc-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()-X | tBodyAcc-mean()-Y | tBodyAcc-mean()-Z | tBodyAcc-std()-X | tBodyAcc-std()-Y | tBodyAcc-std()-Z | tBodyAcc-mad()-X | tBodyAcc-mad()-Y | tBodyAcc-mad()-Z | tBodyAcc-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) | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0.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.112754 | 0.030400 | -0.464761 | -0.018446 | -0.841247 | 0.179941 | -0.058627 |
1 | 0.278419 | -0.016411 | -0.123520 | -0.998245 | -0.975300 | -0.960322 | -0.998807 | -0.974914 | -0.957686 | -0.943068 | ... | 0.158075 | -0.595051 | -0.861499 | 0.053477 | -0.007435 | -0.732626 | 0.703511 | -0.844788 | 0.180289 | -0.054317 |
2 | 0.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.118559 | 0.177899 | 0.100699 | 0.808529 | -0.848933 | 0.180637 | -0.049118 |
3 | 0.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.012892 | 0.640011 | -0.485366 | -0.848649 | 0.181935 | -0.047663 |
4 | 0.276629 | -0.016570 | -0.115362 | -0.998139 | -0.980817 | -0.990482 | -0.998321 | -0.979672 | -0.990441 | -0.942469 | ... | 0.087753 | -0.351471 | -0.699205 | 0.123320 | 0.122542 | 0.693578 | -0.615971 | -0.847865 | 0.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_depth | mean_test_score | mean_train_score | |
---|---|---|---|
0 | 6 | 0.843444 | 0.944879 |
1 | 8 | 0.854334 | 0.982692 |
2 | 10 | 0.847125 | 0.993369 |
3 | 12 | 0.841958 | 0.997212 |
4 | 16 | 0.841958 | 0.999660 |
5 | 20 | 0.842365 | 0.999966 |
6 | 24 | 0.841821 | 1.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_score | mean_test_score | param_n_estimators | param_max_depth | |
---|---|---|---|---|
23 | 1 | 0.912813 | 200 | 8 |
20 | 1 | 0.912813 | 200 | 8 |
35 | 3 | 0.912541 | 200 | 10 |
32 | 3 | 0.912541 | 200 | 10 |
29 | 5 | 0.911725 | 200 | 10 |
- 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가 떨어짐