내맘대로 코딩
[ASAC] ML 머신러닝 - K-fold, KNN, RF(Random Forest), RGS, GS 본문
ASAC 빅데이터 분석가/ML
[ASAC] ML 머신러닝 - K-fold, KNN, RF(Random Forest), RGS, GS
Yoonjung 2024. 5. 9. 08:56
데이터 셋 분리 (train / test) - train_test_split 모듈
# train 데이터를 가지고... 바로 kaggle 의 test를 풀기에는....
# 좀 그래서...
# ==> 자체적으로 train, test를 나눠서 하려고 함! train을 가지고!
# FM : train (train / validation), test
# validation : 내가 정답을 알고 있음!--> 자체 평가가 가능!!
# test : 내가 정답을 가지고 있지 않음! --> kaggle에 업로드 해야만 확인!!
# ==> 데이터 셋을 뭔가 분리할 떄 : train_test_split 모듈!
from sklearn.model_selection import train_test_split
X_train, X_val, y_train, y_val = train_test_split(X_titanic, #처리가 된 문제지
y_titanic, # 처리가 된 정답지 : 인코딩!
test_size=0.2, # 얼마나 셋을 분리할지 결정하는 비율 (평가셋이 많아지면,학습데이터가 적어질 수 있으므로 개수를 잘 고려하기)
random_state=1234, # 재현성을 위해서.. 공통된 기준
### 정답지의 비율을 바탕으로 셋을 분리해줘!
# --> 최대한 원본을 유지하면서 자체 평가 셋을 구성할 때
stratify = y_titanic
)
- 비율 고려
y_train.value_counts(normalize=True)
=> Survived
0 0.616573
1 0.383427
Name: proportion, dtype: float64
y_val.value_counts(normalize=True)
=> Survived
0 0.614525
1 0.385475
Name: proportion, dtype: float64
# --> y_titanic 의 원본 비율을 유지하면서...
# 학습용과 평가용도 비율을 고려해서 샘플링을 함!
y_titanic.value_counts(normalize=True)
=> Survived
0 0.616162
1 0.383838
Name: proportion, dtype: float64
K-fold
- 기본 기능 : Training Data Set을 다시 K개로 분할을 하여서, 이것들을 돌려가면서 검증하는 것!
# train 데이터를 가지고...
# HPT을 위해서 여러 모델을 할 떄 동일한 평가 기준을 적용하기 위해서
# k-fold 의 구성을 재현성을 가지고 동일한 셋으로 평가
# ==> Random_sample 을 재현성을 구현하기 위해서 세팅!!
- K-fold 유형
# 1) 일반적인 k-fold
from sklearn.model_selection import KFold
kfold = KFold(n_splits=5, random_state=1234, shuffle=True ) # 5개로 분리,
# --> 내가 언제 어디서라도 cv = 5 대신에 cv=kflod를 부르면
# train이 동일한 상태라면.. 동일한 샘플들이 동일한 셋으로
# 2) 비율을 유지하는 strafied k-fold
from sklearn.model_selection import StratifiedKFold
str_kflod= StratifiedKFold(n_splits=5, random_state=1234, shuffle=True )
# 3) 복원추출을 사용하는 repeated k-fold
# --> 샘플은 적어서 .. 일반적인 k-fold를 하면.. 셋당 샘플이 적어져서
# 학습이 제대로 안 될 수 있다!!
# --> 복원추출을 활용해서.. 데이터의 수를 펌핑!!!
# --> 굳이 데이터가 많은 경우에서는 사용을 하지는 않음.....
# ==> 데이터의 수가 좀 부족한데... k-fold를 사용하고 싶을 때 주로 쓰임
from sklearn.model_selection import RepeatedKFold
repeat_kfold = RepeatedKFold(n_splits=5, n_repeats=10, random_state=1234) # n_repeats : 몇번 복원추출할지
# 4) 비율 유지 & 반복 repeated strafied k-fold
from sklearn.model_selection import RepeatedStratifiedKFold
rskold = RepeatedStratifiedKFold(n_splits=5, n_repeats=10, random_state=1234)
# --> 위의 목적은 성능 비교를 위해서 cv=5 하면.. 돌릴 때 마다 셋의 구성이 달라져서 비교가 애매
# ==> 재현성을 위해서 & 공통 비교 셋을 구성
# cv = kfold
# ++ 데이터셋만 동일하면 동일한 것들로 구성이 됨!
- 모델의 성능체크
# 앞으로 할 과정은
# 1) 개별 모델에 대한 대략적인 성능 체크 : 기준이 되는 Baseline 체크
# 2) 개별 모델에 대한 성능을 끌어올려야 함 HPT
# ==> RandomGridSerchCV : 크게 추리는 것 (좋은 후보군을 추림)
# ==> GridSerchCV : 좀 더 디테일하게 탐색!!
# 최근) Bayseian opt --> GridSearchCV 최적을 찾기도 함...
# +++ **Bayseian opt 이 훨씬 효율적 => Bayseian에서 큰 범위로 세팅!
# +++ 여러 플랫폼이 존재를 함
# 3) 실제 모의 평가를 진행해서 모델의 성능 체크!! --> val
# --> 개별 모델에 대한 성능 최적화가 잘 되었는지 체크!
# --> 만약에... 성능이 별로인 경우 : 다시 2-3번 돌아가면서 체크...
# ==> 최적의 f를 디테일하게 선택!!! (최적의 성능을 끌어내겠다)
# ----> knn_best, rf_best, xgboost_best etc...
# 4) 최종 모델을 선택하는 과정!!
# --> 4-1) 시도한 여러 모델 중에서 제일 best를 선정!!
# --> 4-2) 했던 여러 모델을 종합해서 voting 으로 최종!!
# (Hard voting, Soft voting)
# ++ Many & Diversity
# 4-3) Stacking (대회 한정) 다시 재학습!!
# etc.............
# ++++ 옵션) 주어진 셋을 컬럼/셋을 랜덤 샘플링을 하거나,
# 주요 변수 중심으로 셋을 Bootstraping + 여러 모델 + 최종 결론
# +++ 다양한 시도적인 방법들이 대회에서는 있음!
# (일반적인 논문에서 쓰는 스타일 FM,
# -- 실제 현업 ( 그 중간쯤 입장.... 성능이 더 좋아야 할 수 도 있고, 해석이 더 중요할 수도 있음 + new)
# 대회에서는 뭐라도 다 사용을 함 : 제약은 없음)
# 5) --> 진짜 최종 실전 문제를 풀어야 함 test.csv를 풀어야 함
# +++ kaggle 에 직접 업로드
# 주의사항 : train 과 동일한 전처리!!!
# ** 기준 : train 셋의 기준으로...
# ** test 셋에서 unseen data 처리!! (인코딩)
# 모델을 학습하는 과정에서 필요한 모듈
# 1) base-line : cross-val, 통으로 할 수도 있고...
from sklearn.model_selection import cross_val_score
# 2) 평가 : 채점기 --> 출제자의 의도에 맞는 평가 방식으로 채점...
# ==> 분류의 정확성 : accuracy 를 평가지표로 사용하겠다!!
# ++ 목적에 따라서 다양하게 존재를 함
# 참고) sklearn의 metrics 참조하여 평가지표를 쓰면 됨
from sklearn.metrics import accuracy_score
# 3) HPT 을 통해 f 성능 극대화 (시간적인 부분이 많이 소요되는 과정!)
# ==> 직접 다 체크 : gridsearch + CV
# ==> 랜덤하게 조합을 통해서 하자 : RandomGridSearch + CV
# (원래 원본 논문은 분포를 이용해서 랜덤값을 찾는데... 이제는 내가 주어진 값들의 조합에서만 랜덤하게 테스트함)
# --> 주로 optuna (Bayesian opt) 로 사용 : *단 외부 패키지임
from sklearn.model_selection import GridSearchCV
from sklearn.model_selection import RandomizedSearchCV
# ++ 왜 GS/RGS 아직까지 쓰는지?
# optuna 같은 외부 패키지 같은 경우는 HPT를 하는 것과 최종 모델은 다시 학습시켜야함!
# Sklearn GS/RGS 찾는 과정에서 제일 좋았던 모델이 저장이 되어서
# 좋은 파라미터로 재학습을 하지 않고, 불러다 쓰면 됨!!
1번 f(x)의 후보 : knn 모델
# 1번 f(x)의 후보 : knn 모델
# 매뉴얼을 보고 진행필요
# <https://scikit-learn.org/stable/modules/generated/sklearn.neighbors.KNeighborsClassifier.html>
from sklearn.neighbors import KNeighborsClassifier
knn = KNeighborsClassifier(n_jobs = -1 )
# 아무 세팅을 안하는 기본 모델을 가지고 baseline을 체크...
- Baseline 체크
scores = cross_val_score( knn,
X_train, y_train,
cv = kfold, scoring="accuracy")
scores
#array([0.72727273, 0.70629371, 0.65492958, 0.68309859, 0.78169014])
for iter_count, acc in enumerate(scores):
print(f"KNN의 {iter_count}시도 acc:{acc}")
print("KNN의 모델의 Acc Mean:", scores.mean())
print("KNN의 모델의 Acc std:", scores.std())
KNN의 0시도 acc:0.7272727272727273
KNN의 1시도 acc:0.7062937062937062
KNN의 2시도 acc:0.6549295774647887
KNN의 3시도 acc:0.6830985915492958
KNN의 4시도 acc:0.7816901408450704
KNN의 모델의 Acc Mean: 0.7106569486851178
KNN의 모델의 Acc std: 0.042908832008865715
# ---> 주어진 데이터와 전처리에 한해서...
# 대충 knn 을 돌려보면 대략 0.71정도는 나오겠구나(acc)
# --> 이제 할 일은 : KNN 더 디테일한 파라미터들을 세팅을 통해서
# 성능이 0.71 보다는 좋게 나오는 것을 찾아보자!!
# + 0.78 정도는 넘으면 좋지 않을까....
# ==> HPT 에 Baseline
# 참고/질문사항 정리)
# random_state : 1234그냥 정의한 것임 (아무의미 없음) 학습시킬 떄 그때끄때 셋이 다르기 떄문에
# 셋을 고정하기 위해서 번호로 정의해주는 것
# -> 나중에 어떤 다른 사람이 학습할 때 1234로 학습하라고 하면, 그 셋으로 학습할 수 있음 / 재현성을 유지할 수 있음
# 1234를 하면 뭐를 할지 정해짐
- HPT - RGS/GS
# HPT 에 사용할 수 있는 수단들....
# RGS/GS --> 매번 시행이 독립적!!
# Bayesian --> 과거 시행의 결과를 활용해서 next!!!
- RGS
# RGS : 1) 테스트할 파라미터의 조합들 중에서, 임의로 선택!
# ==> 테스트할 종류가 엄청 많을 때 사용이 가능함!
# ==> 범위를 좀 좁힐 때 사용할 수 있음
# 참고) scikitlearn에서 변질이 되었음...
# ==> 내가 직접 테스트할 파라미터들을 세팅!! + 매뉴얼!! (매뉴얼에 맞춰서 하기!)
parameters = {
# test 할 값들을 나열 : 직접 하거나 / range / 수학함수 math / 리스트 컴프리핸션
"n_neighbors" : [1,3,5,7,9, 11, 13, 15, 17, 19], # 10개
"algorithm" : ["auto", "ball_tree", "kd_tree"], # 3개
# => 30개 테스트
}
# RGS
# 1) 사용할 모델 : HW 성능 관련된 것은 기본으로 설정!
knn = KNeighborsClassifier(n_jobs = -1)
# 2) 몇번을 탐색할 것인가!!
n_iter = 10 # 10개만 샘플링해서 찾아보겠다! (테스트할 파라미터 조합보다 경우의 수가 작으면 에러남)
# 3) RGSCV 세팅 : 마치 모델!!
knn_kf_rgs = RandomizedSearchCV(knn, # 모델,
# 테스트할 파라미터들.. dict로 테스트할 파라미터 조합들을 넘겨주면 됨
param_distributions = parameters,
cv = kfold, # 5개로 분할해서 테스트
scoring = "accuracy",
n_jobs = -1,
random_state = 1234,
n_iter = n_iter)
# 4) 실제 데이터를 밀어넣어서 학습
knn_kf_rgs.fit(X_train, y_train)
- RGS 결과
# RGSCV에 실제 수행 결과 : 로그표
knn_kf_rgs.cv_results_
# 30가지 조합에서 10가지만 표기 params : [{'n_neighbors': 15, 'algorithm': 'auto'} ~~~~~
# 했던 조합들 중에서 제일 좋았던 조합은 뭐냐??
# --> 선정 기준은 오로지 cv에 대한 평균 성능 지표의 순서!
knn_kf_rgs.best_params_
-> {'n_neighbors': 9, 'algorithm': 'auto'}
# 했던 조합들 중에서 제일 좋은 조합의 평균 성능??
knn_kf_rgs.best_score_
--> 0.7191273515217178
- 실제 val로 자체 평가 및 채점
# --> 이게 괜찮네 라고 판단을 하면...
# 실제 val으로 자체 평가!!!
knn_kf_rgs_best = knn_kf_rgs.best_estimator_ # 다시 재학습을 할 필요가 없는 부분!
# val에서 대한 문제를 풀어보자!
knn_kf_rgs_ypred = knn_kf_rgs_best.predict(X_val)
# val 에 대한 채점
knn_kf_rgs_acc = accuracy_score(y_val, knn_kf_rgs_ypred)
print("KNN Fkold RGS Acc:", knn_kf_rgs_acc)
-> KNN Fkold RGS Acc: 0.6871508379888268
- GS (좀 더 디테일하게 탐색)
### --> RGS에서 제일 좋다고 하는 조합을 봤더니...
# 좀더 디테일하게 탐색을 하고 싶다!! : GS
# 가정 : RGS / Bayseian opt 찾은 후보들 중에서 디테일하게 탐색...
# 1) 테스트할 파라미터 조합 : 몇 가지로 좀 줄여서...
parameters = {
"n_neighbors" : [7, 9, 11, 13] #overfit, 기준, underfit 2개 을 선택
}
# 2) 사용할 모델을 선택
knn = KNeighborsClassifier(n_jobs = -1)
# 3) GSCV : 내가 생각한 파라미터 조합은 다 수행!
knn_kf_gs = GridSearchCV(knn,
param_grid = parameters,
cv = kfold,
scoring = "accuracy",
n_jobs = -1)
# 4) 실제 데이터를 밀어넣어서 수행
knn_kf_gs.fit(X_train, y_train)
--> knn_kf_gs.cv_results_
- GS 결과
knn_kf_gs.best_params_
--> {'n_neighbors': 9}
knn_kf_gs.best_score_
--> 0.7191273515217178
# ==> 아까 RGS로 찾은 n_neighbors = 9 가 제일 최적이구나!! 라고 결론을 지을 수 있음!
# KNN의 최종 best 모델은 knn_kf_rgs_best_
# ==> 기본 설정 + n=9 였을 떄임!!
2번 F(x)의 후보 모델 : RF (Random Forest)
# F(x)의 후보 모델2 : RF
# ref) <https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.RandomForestClassifier.html>
from sklearn.ensemble import RandomForestClassifier
- Baseline 체크
# RF의 BaseLine을 체크
rf = RandomForestClassifier(n_jobs=-1, random_state=1234 )
scores = cross_val_score( rf,
X_train, y_train,
cv = kfold, scoring="accuracy")
scores
--> array([0.81118881, 0.84615385, 0.78873239, 0.76056338, 0.83098592])
for iter_count, acc in enumerate(scores):
print(f"RF의 {iter_count}시도 acc:{acc}")
print("RF의 모델의 Acc Mean:", scores.mean())
print("RF의 모델의 Acc std:", scores.std())
-->
RF의 0시도 acc:0.8111888111888111
RF의 1시도 acc:0.8461538461538461
RF의 2시도 acc:0.7887323943661971
RF의 3시도 acc:0.7605633802816901
RF의 4시도 acc:0.8309859154929577
RF의 모델의 Acc Mean: 0.8075248694967005
RF의 모델의 Acc std: 0.030379537772527453
- HPT - RGS/GS
# RF으로 한다면... 대략 0.80 ~ 0.81 정도를 기준으로
# --> 이 값보다 높은 성능이 나오면 좋을 디테일할 파라미터값을 찾아보자 HPT
- RGS
# RGS
# 1) 모델
rf = RandomForestClassifier(n_jobs=-1, random_state=1234 )
# 2) 변경하고자 하는 파라미터들의 조합 세팅 : dict
parameters = {
# RF : Bagging --> 데이터 증강 & Many
# 제일 중요한 파라미터 : 몇 개의 모델의 의견을 종합
# --> 너무 큰 수를 사용하면 시간이 오래걸림!!
"n_estimators" : [10, 30, 50, 70, 100, 200, 300, 500, 1000, 2000],
# 사용할 컬럼의 수 : 주어진 컬럼은 8개 이기 때문에 -> 테스트 3-7
"max_features" : [3,4,5,6,7],
# 구체적인 개별 모델 : dt --> overfit
# ==> 사용하는 데이터의 수와 컬럼의 수
"max_depth" : [2,3,4,5,6,7,8,10,20],
"min_samples_split" : [1,3,5,7,9]
# ==> 개별 모델을 overfit 하게 되면...
# Bias는 줄어들고, variance는 클 수 있음... -> 적당히 처리하며 컨트롤해야함!
}
# 3) RGS 에 얼마나 샘플링을 해서 시도할지
n_iter = 20
# 4) RGS -> 세팅
rf_kfold_rgs = RandomizedSearchCV(rf,
param_distributions = parameters,
cv = kfold,
scoring = "accuracy", #평가지표 설정
n_jobs = -1,
random_state = 1234,
n_iter = n_iter)
# 5) 실제 데이터로 점검
rf_kfold_rgs.fit(X_train, y_train)
# 참고) 중간 진행 상황이런 부분 볼 떄 verbose, verbosity etc
- RGS 결과
rf_kfold_rgs.best_score_
--> 0.8299911356249385
rf_kfold_rgs.best_params_
--> {'n_estimators': 50, 'min_samples_split': 7, 'max_features': 6, 'max_depth': 6}
- 실제 val로 자체 평가 및 채점 (중간 평가)
# RGS 실제 어느정도인지 중간 평가 val
rf_kf_rgs_best = rf_kfold_rgs.best_estimator_
rf_kf_rgs_ypred = rf_kf_rgs_best.predict(X_val)
rf_kf_rgs_acc = accuracy_score( y_val, rf_kf_rgs_ypred)
rf_kf_rgs_acc
--> 0.8324022346368715
X_val.shape
--> (179, 8)
# --> Val의 셋의 숫자가 적어서... 몇 개로 성능이 제대로 나오지 않을 수 있음
# ==> test 로 실제 적용을 합니다.
# 현실적으로는 val, test 많은 양의 데이터로 평가를 합니다.
# kaggle train 100, test 100 + 숨겨진 셋...
- GS
# 혹시 조금 더 좋은 파라미터 값은 없을까?
# --> RGS에서 찾은 best 주변을 좀 더 디테일하게 탐색 : GS
# rgs로 할 때,
rf_kfold_rgs.best_params_
--> {'n_estimators': 50, 'min_samples_split': 7, 'max_features': 6, 'max_depth': 6}
rf = RandomForestClassifier(n_jobs=-1, random_state=1234 )
parameters = {
"n_estimators" : [50, 100, 500],
"max_features" : [6, 5, 7], # 6을 중심으로 5,7
"max_depth" : [6, 5,7],
# "min_samples_split" : [7, 6, 8]
} # --> 3*3*3 = 27개 시도 & cv=5 ==> 135개가 돌아가게 됨
rf_kf_gs = GridSearchCV(rf,
param_grid = parameters,
cv=kfold, scoring = "accuracy", n_jobs=-1)
rf_kf_gs.fit(X_train, y_train)
- GS 결과
rf_kf_gs.best_score_
--> 0.8313798877179159
rf_kf_gs.best_params_
--> {'max_depth': 7, 'max_features': 6, 'n_estimators': 50}
- 실제 val로 자체 평가 및 채점
# val으로 다시 체크해보기!!
rf_kf_gs_best = rf_kf_gs.best_estimator_
rf_kf_gs_ypred = rf_kf_gs_best.predict(X_val)
rf_kf_gs_acc = accuracy_score(y_val, rf_kf_gs_ypred)
rf_kf_gs_acc
—> 랜덤포레스트로 하는 걸로 할지, 그리드 서치로 할지 최종 선택 필요
모델 저장 및 불러오기
# 팀 프로젝트들을 하다보면....,
# 실제 활용을 할 때에는 역할별로 맡은 모델을 작성하고, 공유!!!
## --> 내가 만든 모델에 대한 저장 & 불러오는 과정!!
## model save, model load
## --> DL같은 경우도 모델에 대한 저장/불러오는 것은 중요!!!
- Joblib 패키지 사용하여 모델 저장 및 불러오기
# anaconda를 사용해서 하면 기본 패키지 중에 joblib
import joblib # 이걸 기준으로 해야 문제가 없음
# 1) 모델을 저장 : joblib.dump(어떤 모델, 어디다가 저장할지)
# -- 어떤 모델을 세이브 할건지 지정하고, 어디다 저장할지 기록
# 참고) 파이썬에서는 주로 모델을 저장하는 파일 양식 pickle 타입
# ~~ .pkl
# 참고) 모델이 큰 경우는 몇 백메가 ~ 몇 십 기가 ~~ 이상....
joblib.dump(rf_kf_gs_best, "rf_kf_gs_best.pkl") #피클파일로 세이브
# 2) 받은 모델을 불러와서 사용 : joblib.load(불러올 모델 파일 경로)
model_path = '/content/rf_kf_gs_best.pkl'
rf_backup = joblib.load(model_path)
rf_backup
# ==> 체크
(rf_kf_gs_best.predict(X_val) != rf_backup.predict(X_val)).sum()
--> 0
- pickle 패키지 사용하여 모델 저장 및 불러오기
# 참고) 조원분들의 각기 머신들의 사양이 다 다름!!
# ==> OS에 차이가 발생을 함..... 모델 공유에 문제가 있을 수 있음...
# 체크사항 1) scikit-learn 버전을 맞춰야 함!
# --> 안 맞으면 경고가 발생할 수도 있음!
# 체크사항 2) 명확하게 pickle 패키지를 불러서 사용을 해야함!!!
# ==> joblib가 아니라, pickle 패키지를 불러서 사용
# 체크사항 3) 불러오거나 저장하거나 할 때 : python의 파일 입출 IO open() 함수 사용필요
# 서로 다른 OS 상 모델 저장)
import pickle
file_name = "my_model.pkl"
pickle.dump(rf_kf_gs_best, open(file_name, "wb"))
# 서로 다른 OS 상 모델을 받을 때)
import pickle
path = "/content/my_model.pkl"
new_model = pickle.load(open(path, "rb"))