[KOGAS 빅스타 경진대회] LNG탱크 압력 예측 모델(우수상) 3편 모델 학습, 추론, 제출
카테고리: Project
[KOGAS 빅스타 경진대회] LNG탱크 압력 예측 모델(우수상) 1편 링크
[KOGAS 빅스타 경진대회] LNG탱크 압력 예측 모델(우수상) 2편 링크
상관관계 확인, 변수 최종 선택
모델링을 하기에 앞서 상관관계를 분석했다.
def draw_corr_heatmap(data):
colormap = plt.cm.PuBu
plt.figure(figsize=(11,14))
plt.title("Correlation of Features", y=1.05, size=15)
sns.heatmap(data.corr().loc[:,['LAG1_PIA205B-02A_MIN','LAG1_PIA205B-02A_MAX']], cmap=colormap, linewidths=0.03, linecolor="white", annot=True)
draw_corr_heatmap(x)
또한 최종적으로 사용할 변수 25개를 채택했다. 이유는 아래 사진과 같다.
x = x.drop(["LAG1_ZIH120-02", "LAG1_HOUR", "LAG1_PRESSURE-S", "LAG1_MONTH", "LAG1_WEEK", "LAG1_DAY",
'LAG1_PRESS', 'LAG1_VAPOR', 'LAG1_HUMID', 'LAG1_FY_SUM', 'LAG1_FIA_SUM', 'LAG1_TI_MEAN', 'LAG1_LP_TOTAL'], axis=1)
test_data = test_data.drop(["LAG1_ZIH120-02", "LAG1_HOUR", "LAG1_PRESSURE-S", "LAG1_MONTH", "LAG1_WEEK",
"LAG1_DAY", 'LAG1_PRESS', 'LAG1_VAPOR', 'LAG1_HUMID', 'LAG1_FY_SUM', 'LAG1_FIA_SUM', 'LAG1_TI_MEAN', 'LAG1_LP_TOTAL'], axis=1)
하이퍼파라미터 튜닝 -그리드서치-
이제 본격적으로 모델링을 해볼 것이다. 사용한 알고리즘은 xgboost, 랜덤포레스트, lgbm, extratree 총 네가지였다. 이 네 개의 모델들을 스태킹해보기도 하였으나 최종 성적은 xgboost 단일 모델이 가장 우수했다.
우선 하이퍼파라미터를 튜닝하기 위해 gridsearch를 진행했다. 예시로 랜덤포레스트 그리드서치만 코드를 첨부한다.
#랜덤포레스트 그리드서치
start = time.time()
kfold = KFold(n_splits=2, shuffle=True)
rf = RandomForestRegressor(n_jobs=-1)
params = {"n_estimators":[1000],
"max_depth":(24,28,32),
"min_samples_split":(3,5)}
grid_cv = GridSearchCV(rf,
param_grid=params,
cv=kfold,
scoring = 'neg_mean_absolute_error')
grid_cv.fit(x_train, y_train)
grid_cv.score(x_train, y_train)
print(grid_cv.best_estimator_)
print(grid_cv.best_params_)
print("runtime:", time.time()-start)
>>> RandomForestRegressor(max_depth=32, min_samples_split=3, n_estimators=1000,
n_jobs=-1)
{'max_depth': 32, 'min_samples_split': 3, 'n_estimators': 1000}
교차검증
모델링을 하는 방법은 교차검증을 n회 수행하여 이 n번의 평균값을 제출값으로 만드는 방식을 택했다. 아래 발표자료를 보면 이해를 도울 수 있을 것이다.
start = time.time()
def kfold_RF(n):
kfold = KFold(n_splits=n, shuffle=True, random_state=42)
folds = []
for train_idx, val_idx in kfold.split(x,y):
folds.append((train_idx, val_idx))
RF_model_dict = {}
MAE = []
for f in range(n):
print("----------{}회째 folds-----------".format(f+1))
train_idx, val_idx = folds[f]
x_train, x_val, y_train, y_val = x.iloc[train_idx], x.iloc[val_idx], y.iloc[train_idx], y.iloc[val_idx]
RF_model = RandomForestRegressor(max_depth=32,
n_estimators=2000,
min_samples_leaf=3,
min_samples_split=3,
n_jobs=-1)
RF_model.fit(x_train, y_train)
y_pred = RF_model.predict(x_val)
mae = mean_absolute_error(y_val, y_pred)
MAE.append(mae)
print("{} fold MAE score: {:.8f}".format(f+1,mae))
RF_model_dict[f] = RF_model
MAE_mean = np.array(MAE)
print("---------------------------")
print("k-fold MAE mean: {:.8f}".format(np.mean(MAE_mean)))
RF_y_train = pd.DataFrame(np.zeros((x_train.shape[0],2)),columns=['min', 'max'])
RF_y_test = pd.DataFrame(np.zeros((x_val.shape[0],2)),columns=['min', 'max'])
RF_y_sub = pd.DataFrame(np.zeros((test_data.shape[0],2)),columns=['min', 'max'])
for fold in tqdm(range(n)):
RF_y_train += RF_model_dict[fold].predict(x_train)/n
RF_y_test += RF_model_dict[fold].predict(x_val)/n
RF_y_sub += RF_model_dict[fold].predict(test_data)/n
print("runtime:", time.time()-start)
return RF_y_train, RF_y_test, RF_y_sub
위 함수에서 생성된 세 개의 변수는 스태킹에 사용될 데이터들이다.
RF_y_train, RF_y_test, RF_y_sub = kfold_RF(10)
----------1회째 folds-----------
1 fold MAE score: 0.01033327
----------2회째 folds-----------
2 fold MAE score: 0.01058179
----------3회째 folds-----------
3 fold MAE score: 0.01017598
----------4회째 folds-----------
4 fold MAE score: 0.01030806
----------5회째 folds-----------
5 fold MAE score: 0.01051287
----------6회째 folds-----------
6 fold MAE score: 0.01028156
----------7회째 folds-----------
7 fold MAE score: 0.01041308
----------8회째 folds-----------
8 fold MAE score: 0.01044887
----------9회째 folds-----------
9 fold MAE score: 0.01040414
----------10회째 folds-----------
10 fold MAE score: 0.01038963
---------------------------
k-fold MAE mean: 0.01038493
100%|██████████████████████████████████████████████████████████████████████████████████| 10/10 [01:40<00:00, 10.05s/it]
스태킹
위에선 랜덤포레스트만 코드를 작성했지만, 실제로는 xgboost, lgbm, extretree 모두 동일한 방식으로 모델링을 하여 예측값을 만들었다. 이제 네 개의 알고리즘을 통해 만든 예측값을 토대로 스태킹을 시도할 것이다. 스태킹에 쓰인 메타모델은 랜덤포레스트이다.
#stacking 전에 각 모델들의 예측값을 훈련데이터로 만들어주는 작업
dataframe = [RF_y_train, EXTRA_y_train, XGB_y_train, RF_y_test, EXTRA_y_test, XGB_y_test, RF_y_sub, EXTRA_y_sub, XGB_y_sub]
dfs = []
for i in tqdm(dataframe):
df = pd.DataFrame(i)
dfs.append(df)
y_train_concat = pd.concat((dfs[0], dfs[1], dfs[2]), axis=1)
y_test_concat = pd.concat((dfs[3], dfs[4], dfs[5]), axis=1)
y_sub_concat = pd.concat((dfs[6], dfs[7], dfs[8]), axis=1)
y_train_concat.columns = ['min_RF', 'max_RF', 'min_EXTRA', 'max_EXTRA', 'min_XGB', 'max_XGB']
y_test_concat.columns = ['min_RF', 'max_RF', 'min_EXTRA', 'max_EXTRA', 'min_XGB', 'max_XGB']
y_sub_concat.columns = ['min_RF', 'max_RF', 'min_EXTRA', 'max_EXTRA', 'min_XGB', 'max_XGB']
#x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.3, random_state=42)
#앞서 세 가지 level1 model에서 train과 test로 나눴던 것을 재병합 시키고, 이를 kfold로 다시 무작위로 섞어서 새로운 train test를 만들 것임
x_ens = pd.concat((y_train_concat,y_test_concat),axis=0)
y_ens = pd.concat((y_train,y_test),axis=0)
kfold = KFold(n_splits=10, shuffle=True, random_state=42)
folds = []
for train_idx, val_idx in kfold.split(x,y):
folds.append((train_idx, val_idx))
STACK_RF_model_dict = {}
MAE = []
for f in range(10):
print("----------{}회째 folds-----------".format(f+1))
train_idx, val_idx = folds[f]
x_ens_train, x_ens_val, y_ens_train, y_ens_val = x_ens.iloc[train_idx], x_ens.iloc[val_idx], y_ens.iloc[train_idx], y_ens.iloc[val_idx]
STACK_RF_model = RandomForestRegressor(max_depth=30,
n_estimators=2000,
min_samples_leaf=3,
min_samples_split=3,
n_jobs=-1)
STACK_RF_model.fit(x_ens_train, y_ens_train)
y_ens_pred = STACK_RF_model.predict(x_ens_val)
mae = mean_absolute_error(y_ens_val, y_ens_pred)
MAE.append(mae)
print("{} fold MAE score: {:.8f}".format(f+1,mae))
STACK_RF_model_dict[f] = STACK_RF_model
MAE_mean = np.array(MAE)
print("---------------------------")
print("k-fold MAE mean: {:.8f}".format(np.mean(MAE_mean)))
submission.iloc[:,1:]=0
for fold in range(10):
submission.iloc[:,1:] += STACK_RF_model_dict[fold].predict(y_sub_concat)/10
print("runtime:", time.time()-start)
submission.to_csv('STACK_RF_answer_11232117.csv', index=False)
하지만 위처럼 스태킹을 통해 만든 결과물은 overfitting이 되어서인지 리더보드 상 점수는 더 안좋아졌다. 때문에 단일 모델 중 가장 점수가 좋았던 xgboost 단일 모델로 최종 제출을 하였다.
xgbosst 최종 제출
# 10 fold로 교차검증을 수행
kfold = KFold(n_splits=10, shuffle=True, random_state=42)
folds = []
# train과 validation의 index를 나누기
for train_idx, val_idx in kfold.split(x,y):
folds.append((train_idx, val_idx))
# 10번의 교차검증을 통해 만들 10개의 모델을 딕셔너리에 저장해놓을 것.
XGB_model_dict = {}
#10번에 걸친 MAE 점수 역시도 리스트에 저장
MAE = []
for f in range(10):
print("----------{}회째 folds-----------".format(f+1))
train_idx, val_idx = folds[f]
x_train, x_val, y_train, y_val = x.iloc[train_idx], x.iloc[val_idx], y.iloc[train_idx], y.iloc[val_idx]
XGB_model = XGBRegressor(n_estimators=2000,
min_child_weight=4,
learning_rate=0.02,
max_depth=20,
n_jobs=-1,
alpha=0.1,
colsample_bytree=0.9,
subsample=1.0,
eval_metric='mae')
# 모델 훈련
XGB_model.fit(x_train, y_train)
y_pred = XGB_model.predict(x_val)
mae = mean_absolute_error(y_val, y_pred)
MAE.append(mae)
# K번째 교차검증의 MAE 스코어 출력
print("{} fold MAE score: {:.8f}".format(f+1,mae))
# K번째 모델을 딕셔너리에 저장
XGB_model_dict[f] = XGB_model
MAE_mean = np.array(MAE)
print("---------------------------")
print("k-fold MAE mean: {:.8f}".format(np.mean(MAE_mean)))
submission.iloc[:,1:]=0
#총 10개의 모델을 호출해가며 그 평균값을 제출파일에 저장하는 과정
for fold in range(10):
submission.iloc[:,1:] += XGB_model_dict[fold].predict(test_data)/10
print("runtime:", time.time()-start)
# 제출 파일 csv로 저장
submission.to_csv('XGB_answer_11231700.csv', index=False)
마지막으로, 모델을 dump하여 언제든 동일한 값을 불러올 수 있도록 한다.
joblib.dump(XGB_model_dict, './XGB_1123_0263_best.pkl')
final_model_dict = joblib.load('./XGB_1123_02597_best.pkl')
submission.iloc[:,1:] = 0
for fold in range(10):
submission.iloc[:,1:] += final_model_dict[fold].predict(test_data)/10
변수 중요도 확인
xgboost에는 plot_importance
라는 중요도 함수가 내장되어 있다. importance_type을 뭐로 하냐에 따라 중요도의 기준이 달라지는데, 아래 블로그에 정리가 잘 되어있다.
#변수 중요도
XGB_model = final_model_dict[0]
from xgboost import plot_importance
fig, axes = plt.subplots(nrows=2, ncols=2, figsize=(25,20))
axes = [ax for row_axes in axes for ax in row_axes]
plot_importance(XGB_model, importance_type='gain', title='gain', ax=axes[0])
plot_importance(XGB_model, importance_type='weight', title='weight', ax=axes[1])
plot_importance(XGB_model, importance_type='cover', title='cover', ax=axes[2])
발표자료
이로써 대회에 사용한 모든 코드를 소개해보았다. 이후에 진행된 발표평가에서는 기술적인 부분보다도 분석이 얼만큼 논리적으로 기획되었는지와 그 이후의 사업화까지도 고려해야 했다. 발표에 쓰인 ppt 자료를 첨부함으로써 글을 마치려 한다.