[DE 프로젝트: 코로나 확진자 수 예측 앱 'CO-THER 19'] 2. LightGBM 회귀 모델

CSV 변환

  • 이 전에 PostgreSQL로 저장한 데이터를 머신 러닝 모델링을 위해 CSV 파일로 변환한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import requests
import psycopg2
import logging
import sys
import json
import csv

host = "covid.cjwptwa04yyi.ap-northeast-2.rds.amazonaws.com"
port = 5432
username = "sixmini"
database = "postgres"
password = ""


def main():
    # postgreSQL 연결
    try:
        conn = psycopg2.connect(
            host=host,
            database=database,
            user=username,
            password=password)
        cursor = conn.cursor()
    except:
        logging.error("could not connect to rds")
        sys.exit()

    sql = "COPY (SELECT * FROM weather) TO STDOUT WITH CSV DELIMITER ','"
    with open("weather.csv", "w") as file:
        cursor.copy_expert(sql, file)


if __name__=='__main__':
    main()

LightGBM 회귀 모델링

1
2
3
4
5
6
7
8
9
10
11
12
# 라이브러리 불러오기
from sklearn.metrics import mean_squared_error, r2_score
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import OneHotEncoder
from sklearn.externals import joblib
import lightgbm as lgb
import seaborn as sns
import pandas as pd
import numpy as np
import joblib
import shap
  • PostgreSQL에서 CSV로 변환할 시 컬럼명이 지정되어 있지 않으므로 헤더를 지정한다.
  • 모든 컬럼을 사용하고 싶지만, 예측에는 예보 기상이 활용되어야 하기 때문에 예보에서 확인할 수 있는 컬럼으로만 모델링을 진행한다.
    • avgRhm: 습도
    • ddMes: 적설량
    • sumSsHr: 일조시간
    • avgPs: 해면기압
    • avgTca: 전운량
    • minTa: 최저기온
    • maxTa: 최고기온
    • avgWs: 풍속
    • sumRn: 강수량
    • avgTa: 평균기온
    • avgTd: 평균 이슬점온도
1
2
3
4
weather = pd.read_csv('weather.csv', names=['date', 'avgTa', 'minTa', 'minTaHrmt', 'maxTa', 'maxTaHrmt', 'mi10MaxRn', 'mi10MaxRnHrmt', 'hr1MaxRn', 'hr1MaxRnHrmt', 'sumRnDur', 'sumRn', 'maxInsWs', 'maxInsWsWd', 'maxInsWsHrmt', 'maxWs', 'maxWsWd', 'maxWsHrmt', 'avgWs', 'hr24SumRws', 'maxWd', 'avgTd', 'minRhm', 'minRhmHrmt', 'avgRhm', 'avgPv', 'avgPa', 'maxPs', 'maxPsHrmt', 'minPs', 'minPsHrmt', 'avgPs', 'ssDur', 'sumSsHr', 'hr1MaxIcsrHrmt', 'hr1MaxIcsr', 'sumGsr', 'ddMefs', 'ddMefsHrmt', 'ddMes', 'ddMesHrmt', 'sumDpthFhsc', 'avgTca', 'avgLmac', 'avgTs', 'minTg', 'avgCm5Te', 'avgCm10Te', 'avgCm20Te', 'avgCm30Te', 'avgM05Te', 'avgM10Te', 'avgM15Te', 'avgM30Te', 'avgM50Te', 'sumLrgEv', 'sumSmlEv', 'n99Rn', 'iscs', 'sumFogDur'])
weather.set_index('date', inplace=True)
weather = weather[['avgRhm', 'ddMes', 'sumSsHr', 'avgPs', 'avgTca', 'avgTd', 'minTa', 'maxTa', 'avgWs', 'sumRn', 'avgTa']]
weather.head(1)

스크린샷 2021-10-10 06 42 23

1
2
3
confirmed = pd.read_csv('covid-confirmed-in-seoul.csv', names=['date', 'confirmed'])
confirmed.set_index('date', inplace=True)
confirmed.head(1)

스크린샷 2021-10-10 06 43 00

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# index 기준 merge
df = pd.merge(weather, confirmed, how='inner', on='date')
train, test= train_test_split(df, random_state=42)
train.shape, test.shape
'''
((275, 12), (92, 12))
'''

target = 'confirmed' 
features = df.columns.drop('confirmed')

X_train = train[features]
y_train = train[target]
X_test = test[features]
y_test = test[target]

train_ds = lgb.Dataset(X_train, label = y_train) 
test_ds = lgb.Dataset(X_test, label = y_test)

params = {'learning_rate': 0.01, 
          'max_depth': 10,
          'objective': 'regression', 
          'metric': 'mse', 
          'is_training_metric': True, 
          'num_leaves': 144, 
          'feature_fraction': 0.9, 
          'bagging_fraction': 0.7, 
          'bagging_freq': 5, 
          'seed':2018}

model = lgb.train(params, train_ds, 1000, test_ds, verbose_eval=100, early_stopping_rounds=100)
'''
Training until validation scores don't improve for 100 rounds.
[100]	valid_0's l2: 31347.2
[200]	valid_0's l2: 27725.6
[300]	valid_0's l2: 26771
[400]	valid_0's l2: 26554.9
[500]	valid_0's l2: 26491.7
[600]	valid_0's l2: 25947.4
[700]	valid_0's l2: 25973.2
Early stopping, best iteration is:
[620]	valid_0's l2: 25752
'''

predict_train = model.predict(X_train)
predict_test = model.predict(X_test)

mse = mean_squared_error(y_test, predict_test)
r2 = r2_score(y_test, predict_test)
print('Mean squared error: ', mse)
print('R2 score: ', r2)
'''
Mean squared error:  25752.024720112582
R2 score:  0.5061622922193673
'''
  • 사실 모든 컬럼을 사용했을 때보다 평가 점수는 만족할 수 없지만, 0.5가 넘으므로 이 모델 사용 예정이다.
  • 날씨와 코로나의 상관관계가 적고 데이터 또한 적기 때문에 과적합의 위험이 굉장히 크다.
  • 여러모로 마음에 들진 않지만, 웹 구현이 목적이므로 그대로 진행한다.
1
2
3
final_result = pd.concat([y_test.reset_index(drop=True), pd.DataFrame(predict_test)], axis = 1)
final_result.columns = ['label','predict']
sns.regplot(x = 'label', y = 'predict', data = final_result);

스크린샷 2021-10-10 06 48 44

  • LightGBM을 처음 사용했는데 별 설정을 안해줘도 엄청난 학습 능력을 자랑하는 것 같다.

SHAP

  • 대략적인 특성 중요도를 확인해본다.
1
2
3
4
explainer = shap.TreeExplainer(model)

shap_values = explainer.shap_values(X_test)
shap.summary_plot(shap_values, X_test)

스크린샷 2021-10-10 06 51 01

피클(Pickle)

  • 플라스크에서 사용하기 위해 joblib을 이용하여 모델을 피클 형태로 저장한다.
1
2
3
4
5
6
7
8
9
# save model
joblib.dump(model, 'lgb.pkl')
 
# load model
load_model = joblib.load('lgb.pkl')
load_model
'''
<lightgbm.basic.Booster at 0x7fe75b464c90>
'''
0%