[2024.11.06] 필수 온라인 강의 Part16 Machine Learning Advanced CH06 심화 ML 모델
이론
Bagging vs Boosting
- Bagging (Recap.) - 부트스트랩을 통해 표본을 여러번 뽑아 모델을 학습시키고, 결과를 집계(Aggregation) 하는 앙상블 방법
- Boosting - Boosting은 성능이 약한 모델의 예측값에 대한 오차를 이용해 모델을 더욱 최적값으로 보완하며 성능을 높이는 방법 - 이전 모델이 잘 예측하지 못한 부분에 집중하여 찾아낸 오차를 최적화에 사용하는 점이 가장 큰 차이
Boosting Algorithms - 이전 모델의 예측 결과를 다음 모델 구축에 어떻게 활용하는지에 따라 아래의 두방법이 존재 ○ AdaBoost (Adaptive Boosting) - 이전 모델이 틀리게 예측한 값에 가중치를 부여하여, 다음 모델의 조정에 사용 - 매 스텝마다 오차가 큰 데이터에 대해서는 큰 가중치를, 오차가 작은 데이터에 대해서는 낮은 가중치를 부여
○ GBM (Gradient Boosting Model) - 오차를 계산하는 함수 (Loss function)을 두고, 함수의 Gradient를 사용하여 오차가 최소가 되도록 다음 모델의 조정에 사용 -정답과 예측값의 차이를 통해 데이터에 가중치를 두어 다음 모델이 조정되었던 이전의 Adaboost와는 다르게, 정답과 이전 모델의 예측값으로 Gradient를 계산하여 다음 모델을 조정하는 Boosting 알고리즘 - GBM을 기반으로 훈련 및 추론 속도와 메모리 효율성, 정확도를 향상시킨 다양한 알고리즘 - 목적에 따라 오차(Loss)를 계산하는 손실함수(Loss function)을 두고, 손실 함수로부터 Gradient를 계산하여 모델을 업데이트하는 Gradient Descent를 사용 - 대표적으로 XGBoost, LightGBM, CatBoost으로 구분
- Gradient Descent (with Regression) - 오차함수의 최소값을 찾기 위한 반복 최적화 알고리즘 - 오른쪽 그림은 몸무게를 통해 키를 예측하는 예시 데이터의 모습 - 녹색 선은 현재 모델의 예측 값 (Prediction Value) - 파란 점은 관측 값 (True Value) - 실제 값과 예측 값의 차이를 잔차 (Residual)라고 하며 아래와 같이 표현 - “실제 값” - “현재 모델의 예측값” = “다음 모델이 예측해야 할 값” - “실제 값” = “현재 모델의 예측값” + “다음 모델의 예측값” - 점진적으로 잔차를 줄이는 방향으로 학습되며, 실제 값에 최적화 - 앞선 잔차를 계산하는데에 목적에 맞는 다양한 오차함수(Loss Function)이 존재 (아래 예시는 MSE Loss를 사용) - 초기 모델의 결과가 아래 왼쪽의 그림과 같을때, 오른쪽 그림의 MSE Loss에서 빨간점이 현재 모델의 Loss값으로 적용
- 모든 모델의 변화에 대한 Loss의 값을 집계하게 된다면 Loss값이 최소가 되는 지점을 찾아내는 것이 가능 - Loss값이 최소가 되는 모델은 최적 - 하지만 사전에 모든 모델을 찾아낼 수 없을 뿐만 아니라, 모든 모델에 대한 Loss를 계산해야하는 점이 비효율 - 앞선 문제를 오차함수에서 해당 지점 접선의 기울기(Gradient)를 이용한 경사하강법(Gradient Descent)으로 해결 - 즉, 접선의 기울기는 “X값인 함수 F(x)가 변할 때, Y값인 Loss가 얼마나 변하는지”로 표현 - 따라서 오차 함수 미분을 통한 기울기(Gradient)를 계산한다면, 다음의 모델이 어느 방향(+, -)으로 얼마나(값) 이동해야 하는지를 추측 가능 - 현재 모델의 Negative Gradient를 통해 다음의 모델을 추론하는 과정을 Loss가 최소가 되는 지점이 될때까지 반복
Regularization : 과적합을 줄이는 방법 - Overfitting - 학습 데이터셋으로 학습한 모델의 예측값을 통한 잔차가 0이 되며, 학습세트에 과하게 적합(Overfitting)되는 문제 발생 - 과적합을 완화시켜주는 Regularization방법으로 Subsampling, Shrinkage, Early Stopping을 제안
- Subsampling - 원본 데이터의 일부분만을 추출해서 다음 학습에 사용 ○ Sample A -> 첫번째 모델 학습 ○ Sample B -> 두번째 모델 학습 ○ Sample C -> 세번째 모델 학습 - 데이터를 sampling하면서 조금씩 다른 분포의 데이터에 대해 학습이 이루어지도록 하여 과적합을 방지
- Shrinkage - 추가로 추정되어지는 모델의 영향력을 줄이는 방법 - Learning rate (학습률) 파라미터를 주어 강도를 조절 - 즉, 모델을 어느 정도로 Update할 것인지를 결정하여 과적합을 방지하는 방법
- Early stopping - 학습 데이터에 대한 모델학습이 더 이상 검증 세트에 대해 개선되어지지 않을 때, 훈련을 중지하는 정규화 기술 - 모델 학습을 진행하면서 검증 세트에 대해 최고성능을 가지는 때의 모델의 상태를 저장해두어, 일정 기준이상 성능 개선이 이루지지 않을 경우 저장해두었던 모델을 마지막 최상의 모델로 사용
LightGBM - GBM은 속도가 너무 느려서, GBM을 기반으로 성능의 감소없이 더욱 빠른 학습을 이루는 LightGBM 사용 - 훈련 속도, 효율성과 메모리 사용량을 줄이면서 성능을 높인 Gradient Boosting Machine - GBM보다 대규모 데이터 처리 및 학습에 적합 - LightGBM: A Highly Efficient Gradient Boosting Decision Tree (2017)
GBM의 문제점 - 앞선 GBM에서는 모든 Feature, 모든 Data instance의 Scan을 통해 Gradient를 측정 - 계산복잡성이 Feature, Instance의 수에 비례하게 되어 데이터의 크기가 커질수록 시간이 많이 소요 - 위 문제들을 GOSS, EFB라는 방법을 통해 개선 - 전체 데이터를 사용하는데에서 발생하는 시간/연산적인 비용을 효율적으로 개선
- GOSS : Gradient-based One-Side Sampling - Gradient를 기준으로 인스턴스를 정렬 후, down sampling하는 방법 - Gradient가 작은 인스턴스 = 잘 훈련된 것 - Gradient가 큰 인스턴스 = 덜 훈련된 것 : 학습에 더 참여 시켜야함 - topSet과 randSet을 합쳐서 다음의 학습에 사용 - topSet : k개의 큰 Gradient를 가지는 인스턴스 - randSet : 나머지 (n-k)개의 인스턴스에서 무작위로 샘플링
- EFB : Exclusive Feature Bundling -입력 Feature의 수를 줄이는 방법 - Feature bundling을 통해 복잡도를 더욱 낮추는 방법 - GOSS를 통해 Data관점의 개선으로 O(Down sampled data * feature)의 복잡도 - GOSS+EFB를 통해 O(Down sampled data * feature bundle)으로 복잡도를 낮추어 더욱 빠른 계산 가능 - 번들로 묶을 수 있는 Feature를 식별하는 단계와 Feature들을 병합하는 단계, 총 2단계로 구성
- Part 1 of EFB : 번들로 묶을 수 있는 Feature 식별 - 오른쪽 그림에서 각 차원은 Feature, 각 점들은 데이터가 되며 차원이 늘어남에 따라 전체 공간에서의 빈 공간이 늘어나는 형태 - 빈 공간은 수치적으로 0을 의미 - 일반적으로 Feature가 많을 수록 차원이 높아지면서 0의 값들이 많아지게되고 Sparse한 데이터가 구성 - 각 Feature들이 서로 동시에 0이 아닌 값을 가질 확률이 낮아짐 - 여러 Feature들을 하나의 Bundle로 묶었을 때에 모두 0의 값을 가질 확률이 높음 - Feature의 값이 모두 0일 경우 Bundle의 값은 손실없이 0으로 둘 수있기에, 원본 데이터의 손실이 적은 접근이 가능 - 따라서, Bundling을 위해서는 각 Feature간에 0이 아닌 값을 가지는 경우에 집중한 효율적인 방법이 필요 - 각 Bundle에서 허용하는 최대 Conflict count를 정의 - 최대 Conflict count가 클수록 더욱 많은 Feature가 묶이고 Bundle은 줄어들면서, 학습시간은 감소하지만 값의 손실은 증가
- Part 2 of EFB : Feature 병합 - Bundle내에서 큰 Degree를 가진 Feature가 기준 Feature로 선정 - 다른 Feature와 가장 많은 Conflict를 발생한 Feature - Bundle내의 값들을 조합해 하나의 값으로 변환하는데에 있어 가장 큰 손실을 초래할 수 있음 - 해당 feature를 기준으로 최소한의 손실로 값을 변환할 수 있는 방법을 사용 - 기준 Feature의 가장 많은 Conflict가 발생한 경우(최댓값)를 찾고, Bundle내 값의 변환에 사용할 Offset으로 지정 - 기준 Feature가 0의 값을 가지고 Bundle내의 다른 Feature들이 0이 아닐 경우, 다른 Feature의 값에 기준 Feature가 가질 수 있는 최대 Offset을 추가하면서 사용 - 해당 열의 Data에서는 Conflict가 0이지만, 다른 데이터에 대해서 Offset만큼 기준 Feature에서 Conflict가 발생할 수 있기 때문
Control Strategy - boosting Tree - 구축 방식을 정하는 Parameter. - Default값은 gbdt로, gbdt(gradient boosting decision tree), rf(random forest), dart(dropouts meet multiple additive regression trees) 중 선택 가능 - data_sample_strategy - data sampling의 방식을 정하는 Parameter. - default값은 bagging으로, bagging과 goss중 선택가능 - objective - 학습하고자 하는 Task를 정하는 Parameter. - default값은 regression으로, regression, regression_l1, huber, fair, poisson, quantile, mape, gamma, tweedie, binary, multiclass, multiclassova, cross_entropy, cross_entropy_lambda, lambdarank, rank_xendcg 중 선택 가능
Control Overfitting - max_depth - 트리의 최대 깊이를 제어하는 Parameter로 Default값은 20 max_depth를 낮추면서 모델의 과적합을 줄일 수 있음 - num_leaves - 트리의 leaf 수를 조절하는 Parameter로 Default값은 31 - 2^(max_depth)보다 낮은 수를 사용가능하며 복잡성을 제어하여 과적합을 줄일 수 있음 - min_data_in_leaf - 트리의 leaf가 가질 수 있는 최소 인스턴스 수를 조절하는 Parameter로 Default값은 20 - 큰값을 주면 너무 깊은 Tree가 구성되는 것을 피하면서 과적합을 방지할 수 있지만, 과소적합(Underfitting)이 발생 - feature_fraction - 1.0보다 작은경우 매 iteration에서 Feature들에 대한 하위집합을 Parameter의 비율로 무작위로 선택 - Default값은 1.0이며, 매 Iteration마다 학습에 사용할 데이터의 양을 조절하여 학습속도를 향상 - bagging_fraction - feature_fraction 과 유사한 Parameter지만, Feature가 아닌 Data(row)에 대한 하위집합을 무작위로 선택한다는 차이가 존재 - Default값은 1.0 - device_type - 학습을 진행할 장비를 정하는 parameter. default는 cpu로 cpu, gpu, cuda 중 선택 가능 - cuda옵션 선택시 gpu와 cpu의 학습보다 빠르며, CUDA가 지원되는 gpu환경에서만 사용 가능 - gpu옵션 선택시 cpu보다 빠르며, CUDA가 지원되지 않는 gpu환경까지 조더 넓은 범위에서 사용 가능 - Note: 더 빠른 학습을 위해서는 max_bin값을 더욱 낮춰주는것이 필요
실습
1. 실습 데이터 준비 데이터 불러오기 및 전처리
2. LightGBM 실습 LightGBM Training API를 활용한 학습/저장/불러오기/추론
# Training API를 사용하기 위해 LightGBM에서 제공하는 Dataset으로 변환
lgb_train = lgb.Dataset(X_train, Y_train)
lgb_valid = lgb.Dataset(X_valid, Y_valid)
# Training API에서는 Parameter를 별도의 Dictionary 형태로 입력
# 간단한 실습을 위해, 가장 기본적인 Parameter들을 사용
params = {
'boosting_type': 'gbdt', # boosting 방법을 지정합니다. gbdt, rf(random forest), dart중 선택할 수 있습니다.
'objective': 'regression', # 학습의 목적을 지정합니다. regression, binary, multiclass, lambdarank 등 학습의 목적에 따라 지정할 수 있습니다..
'metric': {'rmse'}, # 평가과정에서의 평가함수를 지정합니다. l1, l2, rmse, auc 등 다양한 metric을 제공합니다.
'metric_freq': 10, # 몇번의 반복마다 평가를 진행할지 지정합니다.
'verbosity': 0 # 학습 중 출력할 로그의 레벨을 지정합니다. "< 0"은 Fatal, "0"은 Error(Warning), "1"은 Info, "> 1"은 Debug 레벨의 로그를 출력합니다.
}
# 위에서 선언한 데이터셋과 parameter를 학습을 위한 함수에 적용
%%time
gbm = lgb.train(
params,
lgb_train,
valid_sets=[lgb_train, lgb_valid], # 평가과정을 진행할 데이터셋을 지정
callbacks=[lgb.log_evaluation(period=params['metric_freq'], show_stdv=True)] # 평가과정을 parameter의 반복마다 진행하고 출력
)
- 특정 반복마다 평가점수를 확인하며 모델의 학습이 잘 이루어지는지 확인
- rmse로그를 보면, training rmse는 지속적으로 감소하지만, valid rmse는 감소하다가 다시 증가하는 모습
- Parameter 튜닝에서 해당 부분을 해결
# 학습한 모델을 저장
joblib.dump(gbm, 'lightgbm_training_api.pkl')
# 저장한 모델
gbm_trained = joblib.load('lightgbm_training_api.pkl')
# 불러온 모델을 통해 추론을 진행
predicts = gbm_trained.predict(X_valid)
display(f"추론 결과 샘플 : {predicts[:4]}")
# 학습에서 평가셋으로 사용한 세트를 이용한 추론성능 평가를 진행
%%time
RMSE = mean_squared_error(Y_valid, predicts)**0.5
display(f"추론 결과 rmse : {RMSE}")
LightGBM Scikit-learn API를 활용한 학습/저장/불러오기/추론
# Regression모델을 지정
gbm = lgb.LGBMRegressor(n_estimators=100) # 총 반복 횟수를 지정
# 학습을 진행.
# scikit-learn api를 사용할 시, 별도의 lightgbm Dataset으로 변환을 거치지 않고 원데이터 형태로 사용
%%time
gbm.fit(
X_train, Y_train, # 학습 데이터를 입력
eval_set=[(X_train, Y_train), (X_valid, Y_valid)], # 평가셋을 지정
eval_metric ='rmse', # 평가과정에서 사용할 평가함수를 지정
callbacks=[lgb.log_evaluation(period=10, show_stdv=True)] # 앞서 지정했던 callback함수와 동일하게 지정.
)
# 학습한 모델을 저장
joblib.dump(gbm, 'lightgbm_sklearn_api.pkl')
# 저장한 모델
gbm_trained = joblib.load('lightgbm_sklearn_api.pkl')
# 불러온 모델을 통해 추론을 진행
predicts = gbm_trained.predict(X_valid)
display(f"추론 결과 샘플 : {predicts[:4]}")
# 학습에서 평가셋으로 사용한 세트를 이용한 추론성능 평가를 진행
%%time
RMSE = mean_squared_error(Y_valid, predicts)**0.5
display(f"추론 결과 rmse : {RMSE}")
Parameters - LightGBM에서는 100개가 넘는 수 많은 Parameter를 지정 : 이 중 주요 parameter을 적용
# 이전의 LightGBM 학습의 로그에서 확인했던 과적합을 방지하기 위한 튜닝을 진행
# num_leaves, min_data_in_leaf 모두 기존보다 높은 값을 주고, max_depth는 기존보다 낮은 값
# early stopping을 적용하여 학습의 종료 조건을 주고, n_estimators는 가능한 큰 수
%%time
gbm = lgb.LGBMRegressor(n_estimators=100000, # early stopping을 적용하기에 적당히 많은 반복 횟수를 지정.
metric="rmse",
data_sample_strategy='goss', # sampling 방법을 goss로 적용합니다.
max_depth=12, # default값인 20에서 12로 변경합니다.
num_leaves=62, # default값인 31에서 62으로 변경합니다.
min_data_in_leaf=40 # default값인 20에서 40으로 변경합니다.
)
gbm.fit(X_train, Y_train,
eval_set=[(X_train, Y_train), (X_valid, Y_valid)],
eval_metric ='rmse',
categorical_feature="auto",
callbacks=[lgb.early_stopping(stopping_rounds=50), # early stopping을 적용합니다. 50번동안 metirc의 개선이 없다면 학습을 중단
lgb.log_evaluation(period=10, show_stdv=True)] # 10번의 반복마다 평가점수를 로그에 나타남
)
# 학습한 모델을 저장
joblib.dump(gbm, 'tuning_lightgbm_sklearn_api.pkl')
# 저장한 모델을 불러옴
gbm_trained = joblib.load('tuning_lightgbm_sklearn_api.pkl')
# 불러온 모델을 통해 추론을 진행
predicts = gbm_trained.predict(X_valid)
print(f"추론 결과 샘플 : {predicts[:5]}")
# 학습에서 평가셋으로 사용한 세트를 이용한 추론성능 평가를 진행
%%time
RMSE = mean_squared_error(Y_valid, predicts)**0.5
print(f"추론 결과 rmse : {RMSE}")
시각화
# 학습된 gbm의 parameter들을 불러옴
gbm_params = gbm_trained.get_params()
print(json.dumps(gbm_params, indent=4)) # 보기 편하게 나타내기 위해, json 형태로 변환
# 학습결과를 시각화
fig = plt.figure(figsize=(8, 8))
plt.subplot(1, 1, 1)
plt.plot(np.arange(len(list(gbm_trained.evals_result_['training']['rmse']))), list(gbm_trained.evals_result_['training']['rmse']), 'b-',
label='Train Set')
plt.plot(np.arange(len(list(gbm_trained.evals_result_['training']['rmse']))), list(gbm_trained.evals_result_['valid_1']['rmse']), 'r-',
label='Valid Set')
plt.legend(loc='upper right')
plt.xlabel('Boosting Iterations')
plt.ylabel('Rmse')
fig.tight_layout()
plt.show()
- 종목별 rmse 시각화
# 종목별로 valid set에 대한 rmse를 측정
code_li = list(X_valid['LEncodedCode'].unique()) # 종목리스트를 만듬.
valid_rmse_li = [] # 종목별 rmse를 저장할 리스트를 선언
for code in code_li: # 종목별로 rmse측정을 진행
valid_sets = X_valid.loc[X_valid['LEncodedCode']==code] # 해당 종목의 input
valid_predicts = gbm_trained.predict(valid_sets, verbosity=-1) # 추론을 진행.
valid_rmse_li.append(mean_squared_error(Y_valid[valid_sets.index], valid_predicts)**0.5) # 추론결과로 rmse를 계산하고 저장.
# 종목별 rmse결과를 dataframe형태로 바꾸고, 오름차순으로 정렬.
valid_rmse_df = pd.DataFrame({'code':code_li, 'rmse':valid_rmse_li})
valid_rmse_df = valid_rmse_df.sort_values(by='rmse', ascending=True).reset_index(drop=True)
# 시각화를 진행
plt.figure(figsize=(15,10))
barplot = sns.barplot(data=valid_rmse_df, # 종목별 rmse를 barplot으로 시각화합니다.
x="code",
y="rmse",
order=valid_rmse_df['code'])
barplot.set(xticklabels=[]) # 종목명을 그래프에 모두 표현하기에는 너무 좁기에, 종목명은 시각화에서 제외합니다.
barplot.set(xlabel=None)
sns.lineplot(x=valid_rmse_df['code'].index, y=[RMSE] * len(valid_rmse_df), label="Total_Valid_RMSE") # 전체 validset에 대한 rmse를 lineplot으로 시각화합니다.
plt.legend(loc='upper center')
plt.grid(True, axis='y')
plt.show()
- 종목 별 예측을 시각화
# 높게 나타난 3개의 종목을 추출.
high_rmse_sample = valid_rmse_df.tail(3) # 이전의 rmse기준으로 오름차순으로 정렬된 dataframe에서 끝부분 3개
# 각 종목별로 예측결과와 실제결과를 함께 시각화.
for idx, row in high_rmse_sample.iterrows():
code_input = X_valid.index[X_valid['LEncodedCode'] == row['code']].tolist() # 검증세트의 입력에서 해당 종목의 인덱스
plt.figure(figsize=(15,6))
sns.lineplot(x=range(0, len(code_input)), y=np.asarray(Y_valid)[code_input], label="Actual", alpha=1.0) # 실제 결과들 중 앞선 인덱스를 통해 해당 종목의 실제값만 가져옵니다.
sns.lineplot(x=range(0, len(code_input)), y=predicts[code_input], label="Predict", alpha=1.0, linestyle='--') # 예측 결과들 중 앞선 인덱스를 통해 해당 종목의 예측값만 가져옵니다.
origin_code = code_label_encoder.inverse_transform([int(row['code'])])[0] # 종목을 label encoding하기 전의 원본코드값으로 변환합니다.
code_name = company_data.loc[company_data['code']==origin_code]['company'].values[0] # company data에서 원본코드값에 매칭되는 종목명을 가져옵니다.
plt.title(f"Code : {code_name}, RMSE : {row['rmse']:.2f} - Prediction Close Price")
plt.ylabel('Close')
plt.legend()
plt.grid(True)
plt.show()
# 낮게 나타난 3개의 종목을 추출
low_rmse_sample = valid_rmse_df.head(3) # 이전의 rmse기준으로 오름차순으로 정렬된 dataframe에서 앞부분 3개.
# 각 종목별로 예측결과와 실제결과를 함께 시각화.
for idx, row in low_rmse_sample.iterrows():
code_input = X_valid.index[X_valid['LEncodedCode'] == row['code']].tolist() # 검증세트의 입력에서 해당 종목의 인덱스.
plt.figure(figsize=(15,6))
sns.lineplot(x=range(0, len(code_input)), y=np.asarray(Y_valid)[code_input], label="Actual", alpha=1.0) # 실제 결과들 중 앞선 인덱스를 통해 해당 종목의 실제값.
sns.lineplot(x=range(0, len(code_input)), y=predicts[code_input], label="Predict", alpha=1.0, linestyle='--') # 예측 결과들 중 앞선 인덱스를 통해 해당 종목의 예측값.
origin_code = code_label_encoder.inverse_transform([int(row['code'])])[0] # 종목을 label encoding하기 전의 원본코드값으로 변환.
code_name = company_data.loc[company_data['code']==origin_code]['company'].values[0] # company data에서 원본코드값에 매칭되는 종목명을 가져옴.
plt.title(f"Code : {code_name}, RMSE : {row['rmse']:.2f} - Prediction Close Price")
plt.ylabel('Close')
plt.legend()
plt.grid(True)
plt.show()
- 산업군 별 rmse 시각화
# 산업군별로 valid set에 대한 rmse를 측정
industry_li = list(X_valid['LEncodedIndustry'].unique()) # 산업군리스트.
valid_rmse_li = [] # 산업군별 rmse를 저장할 리스트를 선언.
for industry in industry_li: # 산업군별로 rmse측정을 진행
valid_sets = X_valid.loc[X_valid['LEncodedIndustry']==industry] # 해당 산업군의 input
valid_predicts = gbm_trained.predict(valid_sets, verbosity=-1) # 추론을 진행
valid_rmse_li.append(mean_squared_error(Y_valid[valid_sets.index], valid_predicts)**0.5) # 추론결과로 rmse를 계산하고 저장.
# 산업군별 rmse결과를 dataframe형태로 바꾸고, 오름차순으로 정렬.
valid_rmse_df = pd.DataFrame({'industry':industry_li, 'rmse':valid_rmse_li})
valid_rmse_df = valid_rmse_df.sort_values(by='rmse', ascending=True).reset_index(drop=True)
# 시각화를 진행
plt.figure(figsize=(15,10))
barplot = sns.barplot(data=valid_rmse_df, # 산업군별 rmse를 barplot으로 시각화.
x="industry",
y="rmse",
order=valid_rmse_df['industry'])
barplot.set(xticklabels=[]) # 산업군명을 그래프에 모두 표현하기에는 너무 좁기에, 산업군명은 시각화에서 제외
barplot.set(xlabel=None)
sns.lineplot(x=valid_rmse_df['industry'].index, y=[RMSE] * len(valid_rmse_df), label="Total_Valid_RMSE") # 전체 validset에 대한 rmse를 lineplot으로 시각화.
plt.legend(loc='upper center')
plt.grid(True, axis='y')
plt.show()
- 산업군 별 예측 시각화
# 높게 나타난 3개의 산업군을 추출
high_rmse_sample = valid_rmse_df.tail(3) # 이전의 rmse기준으로 오름차순으로 정렬된 dataframe에서 끝부분 3개
# 각 산업군별로 예측결과와 실제결과를 함께 시각화.
for idx, row in high_rmse_sample.iterrows():
Industry_input = X_valid.loc[X_valid['LEncodedIndustry'] == row['industry']].sort_values(by=['LEncodedCode'], ascending=True).index.tolist()
plt.figure(figsize=(15,5))
sns.lineplot(x=range(0, len(Industry_input)), y=np.asarray(Y_valid)[Industry_input], label="Actual", alpha=1.0) # 실제 결과들 중 앞선 인덱스를 통해 해당 산업군의 실제값.
sns.lineplot(x=range(0, len(Industry_input)), y=predicts[Industry_input], label="Predict", alpha=1.0, linestyle='--') # 예측 결과들 중 앞선 인덱스를 통해 해당 산업군의 예측값.
origin_industry = industry_label_encoder.inverse_transform([int(row['industry'])])[0] # 산업군을 label encoding하기 전의 원본산업군으로 변환
plt.title(f"Industry : {origin_industry}, RMSE : {row['rmse']:.2f} - Prediction Close Price")
plt.ylabel('Close')
plt.legend()
plt.grid(True)
plt.show()
# 낮게 나타난 3개의 산업군을 추출
low_rmse_sample = valid_rmse_df.head(3) # 이전의 rmse기준으로 오름차순으로 정렬된 dataframe에서 앞부분 3개.
# 각 산업군별로 예측결과와 실제결과를 함께 시각화.
for idx, row in low_rmse_sample.iterrows():
Industry_input = X_valid.loc[X_valid['LEncodedIndustry'] == row['industry']].sort_values(by=['LEncodedCode'], ascending=True).index.tolist()
plt.figure(figsize=(15,5))
sns.lineplot(x=range(0, len(Industry_input)), y=np.asarray(Y_valid)[Industry_input], label="Actual", alpha=1.0) # 실제 결과들 중 앞선 인덱스를 통해 해당 산업군의 실제값만 가져옵니다.
sns.lineplot(x=range(0, len(Industry_input)), y=predicts[Industry_input], label="Predict", alpha=1.0, linestyle='--') # 예측 결과들 중 앞선 인덱스를 통해 해당 산업군의 예측값만 가져옵니다.
origin_industry = industry_label_encoder.inverse_transform([int(row['industry'])])[0] # 산업군을 label encoding하기 전의 원본산업군으로 변환합니다.
plt.title(f"Industry : {origin_industry}, RMSE : {row['rmse']:.2f} - Prediction Close Price")
plt.ylabel('Close')
plt.legend()
plt.grid(True)
plt.show()
- Feature Importance
# 학습된 모델에서 feature importance를 불러옴.
feat_imp = gbm_trained.feature_importances_
print(f"GBM Feature Importance \n\n {feat_imp}")
# 불러온 feature importance를 feature의 이름과 함께 dataframe의 형태로 저장.
# 저장 후, 가장 영향력이 강한 feature순으로 정렬
sorted_feat_imp = pd.Series(feat_imp, input_cols).sort_values(ascending=False)
print(sorted_feat_imp)
# 구성된 feature importance 결과를 시각화.
plt.figure(figsize=(16,6))
sorted_feat_imp.plot(kind='bar', title='Feature Importances')
plt.ylabel('Feature Importance Score')
- plot tree - Tree계열의 모델학습에 있어서, Tree의 각 노드가 어떻게 연결되어 있는지 한눈에 확인 - 각 분기점들의 Feature와 수치, Tree의 깊이와 넓이 등을 보다 쉽게 파악할 수 있으며, 이를 통해 추가적인 Feature Engineering이나 max depth의 조절과 같은 Parameter 튜닝의 방향으로 모델의 다음 개선과정을 탐색