본문 바로가기
Project/Convenience Store Location Analysis

[편의점 매출 예측] Streamlit 페이지 구현 2. 모델 pkl 저장

by skwkiix 2023. 12. 22.
728x90

 

이전 포스팅의 구현 순서에 맞게 코드를 작성한다


주요 아키텍쳐

Streamlit
 ┣ 📂data
 ┣ 📂models
 ┃ ┣ 📜gm_model.pkl # 모델 pkl
 ┃ ┗ 📜ngm_model.pkl # 모델 pkl
 ┣ 📂pages
 ┃ ┣ 📜main_page.py # 메인 페이지
 ┃ ┗ 📜sub_page.py # 서브 페이지
 ┣ 📜Home.py # 첫 페이지
 ┣ 📜models_gol.py # 골목상권 모델
 ┣ 📜models_ngol.py # 비골목상권 모델
 ┗ 📜requirements.txt

모델 pkl 형태로 저장

> models_gol.py, models_ngol.py 에서 각각 진행

  • 머신러닝 코드 작성 (예시. models_gol.py 골목상권 모델)
import numpy as np
import pandas as pd
import lightgbm as lgb
from sklearn.model_selection import KFold, train_test_split, RandomizedSearchCV
from sklearn.metrics import mean_squared_error, mean_absolute_error
import matplotlib.pyplot as plt
import seaborn as sns
import matplotlib.font_manager as fm
import joblib
import os

#font 오류 수정
font_list = fm.findSystemFonts()
font_name = None
for font in font_list:
    if 'AppleGothic' in font:
        font_name = fm.FontProperties(fname=font).get_name()
plt.rc('font', family=font_name)

# 데이터 불러오기 및 전처리
data = pd.read_csv('data/골목_model용.csv')
# 데이터 분할
# k폴드, 라이트gbm 베이스라인 코드

# 라이브러리 임포트
import lightgbm as lgb
import numpy as np
import pandas as pd
from lightgbm import LGBMRegressor
from sklearn.model_selection import train_test_split, KFold, RandomizedSearchCV
from sklearn.metrics import mean_squared_error, mean_absolute_error

# 데이터 로드(실제 데이터셋 가져오기)
X = data.iloc[:, 5:]
y = data.iloc[:, 0]

# 데이터를 훈련세트와 테스트 세트로 나눔(test_size 설정 필요, 임의로 0.2)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# k-폴드 교차 검증
num_folds = 10
kf = KFold(n_splits= num_folds, shuffle=True, random_state=42)


# LightGBM 모델 초기화
params = {
    'boosting_type': 'gbdt',
    'objective': 'regression',
    'metric': 'rmse',
    'num_leaves': 31,
    'learning_rate': 0.05,
    'feature_fraction': 0.9
}

# 특성 중요도 리스트 초기화
feature_importance_list = []

# 결과 스코어
rmse_scores = []  # RMSE 스코어를 저장할 리스트
mae_scores = []   # MAE 스코어를 저장할 리스트
best_params_list = []  # 각 fold에서의 최적 파라미터를 저장할 리스트

# 데이터 분할
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)


# 파라미터 범위 설정 (랜덤 서치용)
param_dist = {
    'objective': ['regression'],
    'metric': ['mse'],
    'num_leaves': list(range(7, 64)),              # 7부터 63까지
    'learning_rate': [0.01, 0.02, 0.03, 0.04, 0.05],  #0.01부터 0.05까지
    'n_estimators': list(range(200, 301)),         # 200부터 300까지
    'early_stopping_rounds': list(range(40, 51))  # 40부터 50까지
}


# K-Fold 교차 검증 수행
for train_index, val_index in kf.split(X_train):
    X_train_kf, X_val_kf = X.iloc[train_index], X.iloc[val_index]
    y_train_kf, y_val_kf = y.iloc[train_index], y.iloc[val_index]


    # 데이터셋
    train_data = lgb.Dataset(X_train_kf, label=y_train_kf)
    val_data = lgb.Dataset(X_val_kf, label=y_val_kf, reference=train_data)


    # 랜덤 서치를 사용한 LightGBM 모델 튜닝
    random_search = RandomizedSearchCV(
        lgb.LGBMRegressor(),
        param_distributions=param_dist,
        n_iter=10,
        scoring='neg_mean_squared_error',
        cv=kf,
        random_state=42,
        n_jobs=-1,
        verbose=1
    )

    evals = [(X_train_kf, y_train_kf),(X_val_kf, y_val_kf)]
    random_search.fit(X_train_kf, y_train_kf, eval_set = evals, eval_metric='rmse')
    best_params = random_search.best_params_

    bst = lgb.LGBMRegressor(**best_params)

    bst.fit(X_train_kf, y_train_kf,
            eval_set=evals,
            eval_metric='rmse',
            verbose=False)
    

    #Feature importance 계산
    feature_importance = bst.feature_importances_
    feature_importance_list.append(feature_importance)


    # 모델 평가 (RMSE)
    y_pred = bst.predict(X_val_kf)
    mse = mean_squared_error(y_val_kf, y_pred)
    rmse = np.sqrt(mean_squared_error(y_val_kf, y_pred))
    mae = mean_absolute_error(y_val_kf, y_pred)

    rmse_scores.append(rmse)
    mae_scores.append(mae)
    best_params_list.append(best_params)


# 교차 검증 결과 출력
mean_rmse = np.mean(rmse_scores)
mean_mae = np.mean(mae_scores)
print(f'평균 RMSE: {mean_rmse}')
print(f'평균 MAE: {mean_mae}')

# 특성 중요도 평균 계산
average_feature_importance = np.mean(feature_importance_list, axis=0)

# 특성 이름
feature_names = X.columns


# 중요도를 특성 이름과 함께 출력
feature_importance_df = pd.DataFrame({'Feature': feature_names, 'Importance': average_feature_importance})
feature_importance_df = feature_importance_df.sort_values(by='Importance', ascending=False)
print(feature_importance_df)

# 특성 중요도 시각화
plt.figure(figsize=(12, 8))
sns.barplot(x='Importance', y='Feature', data=feature_importance_df)
plt.title('특성 중요도')
plt.show()

# K-fold 교차 검증에서 얻은 최적 파라미터 출력
print("K-fold 교차 검증을 위한 최적 하이퍼파라미터:")
for i, params in enumerate(best_params_list):
    print(f'Fold {i + 1}: {params}')

 

 

  • 하단에 joblib 라이브러리로 모델을 pkl로 저장하는 코드 작성
# 모델 저장
if not os.path.exists("models"):
    os.mkdir("models")

model_file = open("models/gm_model.pkl", "wb")
joblib.dump(bst, model_file) # Export
model_file.close()

 

os 라이브러리 이용해서, models 폴더 생성 한다. ( models 폴더가 없는 경우 models 폴더를 생성)

베스트 모델을 bst로 저장했으므로, joblib 라이브러리를 이용해서 bst 모델을 pkl로 저장한다.

 

  • models 폴더에 gm_model.pkl,과 ngm_model.pkl 이 저장된 것을 확인
Streamlit
 ┣ 📂data
 ┣ 📂models
 ┃ ┣ 📜gm_model.pkl # 모델 pkl
 ┃ ┗ 📜ngm_model.pkl # 모델 pkl
 ┣ 📜models_gol.py # 골목상권 모델
 ┣ 📜models_ngol.py # 비골목상권 모델
 ┗ 📜requirements.txt