LightGBMとOptunaをお試しで使って気温を予想してみる
機械学習で明日の気温をざっくり予想できたら面白そうだなと思ってRandomForestでちょっと作ってみたもの http://narow613.hatenadiary.jp/entry/2019/01/27/175803 に
LightGBMとOptunaを使ったらどれくらい精度があがるのかを試してみました。
この2つのライブラリは本当にすごいんですが、それに関しては皆様ググってください。
Optunaの実装はここを参考にしました。http://www.algo-fx-blog.com/xgboost-optuna-hyperparameter-tuning/
まずはコンソールでOptunaとLightGBMをインストール。
pip install lightgbm
pip install optuna
以上、準備終わり。
#import
import numpy as np
import pandas as pd
import os
import csv
import datetime
import matplotlib.pyplot as plt
%matplotlib inline
from lightgbm import LGBMRegressor
import optuna
from sklearn.metrics import make_scorer, r2_score
from sklearn.preprocessing import StandardScaler
天候データは気象庁からダウンロードしましょう https://www.data.jma.go.jp/gmd/risk/obsdl/index.php
過去一週間の気温の推移をざっくりとDataFrameにぶちこみます
def week_dataset(df):
tmp=np.zeros((2,8,len(df)))
for i in range(1,8):
for j in range(len(df)):
tmp[0][i][j]=df.Max_temp.iloc[j-i]
tmp[1][i][j]=df.Min_temp.iloc[j-i]
df[str("Max -")+str(i)]=tmp[0][i]
df[str("Min -")+str(i)]=tmp[1][i]
return df
df=week_dataset(df)
df.head()
Max-1というのは、一日前の最高気温を表します。 Min-6だったら、6日前の最低気温を表しています。
X=df.drop(["Max_temp","Min_temp"],axis=1)
Y_Max=df["Max_temp"]
Y_Min=df["Min_temp"]
X_train=X[:-365]
X_test=X[-365:]
Y_Max_train=Y_Max[:-365]
Y_Max_test=Y_Max[-365:]
Y_Min_train=Y_Min[:-365]
Y_Min_test=Y_Min[-365:]
XとYに分けます。Yには当日の最低気温と最高気温をそれぞれ切り出します。
Xには一日前までの時点での7日間の気温の推移が詰まりますね。
forest_Max = LGBMRegressor(min_samples_leaf=3, random_state=42)
forest_Max.fit(X_train, Y_Max_train)
forest_Min = LGBMRegressor(min_samples_leaf=3, random_state=42)
forest_Min.fit(X_train, Y_Min_train)
Y_Max_pred=forest_Max.predict(X_test)
Y_Min_pred=forest_Min.predict(X_test)
print("MaxTemp pred r2score",r2_score(Y_Max_test, Y_Max_pred))
print("MinTemp pred r2score",r2_score(Y_Min_test, Y_Min_pred))
前の記録が、
MaxTemp pred r2score 0.8336991228198057
MinTemp pred r2score 0.9558456419510862
だったことを考えると、むしろ劣化してる感じですね。
ここでOptunaの出番です。
def opt(trial):
n_estimators = trial.suggest_int('n_estimators', 0, 1000)
max_depth = trial.suggest_int('max_depth', 1, 20)
min_child_weight = trial.suggest_int('min_child_weight', 1, 20)
subsample = trial.suggest_discrete_uniform('subsample', 0.5, 0.9, 0.1)
colsample_bytree = trial.suggest_discrete_uniform('colsample_bytree', 0.5, 0.9, 0.1)
model_opt = LGBMRegressor(
random_state=42,
n_estimators = n_estimators,
max_depth = max_depth,
min_child_weight = min_child_weight,
subsample = subsample,
colsample_bytree = colsample_bytree,
)
model_opt.fit(X_train,Y_Max_train)
opt_pred = model_opt.predict(X_test)
return (1.0 - (model_opt.score(X_test, Y_Max_test)))
model_opt=LGBMRegressor()
study = optuna.create_study()
study.optimize(opt, n_trials=100)
結果を見ます。
print(study.best_params)
print(1-study.best_value)
どうやらこのパラメータが適切みたいですね。 精度もちょびっと上がってます。
最低気温にも適用して、予想してみましょう。
def opt(trial):
n_estimators = trial.suggest_int('n_estimators', 0, 1000)
max_depth = trial.suggest_int('max_depth', 1, 20)
min_child_weight = trial.suggest_int('min_child_weight', 1, 20)
subsample = trial.suggest_discrete_uniform('subsample', 0.5, 0.9, 0.1)
colsample_bytree = trial.suggest_discrete_uniform('colsample_bytree', 0.5, 0.9, 0.1)
model_opt = LGBMRegressor(
random_state=42,
n_estimators = n_estimators,
max_depth = max_depth,
min_child_weight = min_child_weight,
subsample = subsample,
colsample_bytree = colsample_bytree,
)
model_opt.fit(X_train,Y_Min_train)
opt_pred = model_opt.predict(X_test)
return (1.0 - (model_opt.score(X_test, Y_Min_test)))
model_opt=LGBMRegressor()
study = optuna.create_study()
study.optimize(opt, n_trials=100)
print(study.best_params)
print(1-study.best_value)
最低気温と最高気温で結構パラメータ違いますね。
Optunaを使えば結構高速にわかってしまいます。便利ですね。
ではお楽しみの予想をしてみましょう。
forest_Max = LGBMRegressor(n_estimators= 572,max_depth= 1, min_child_weight= 14, subsample= 0.9, colsample_bytree= 0.7,random_state=42)
forest_Max.fit(X_train, Y_Max_train)
forest_Min = LGBMRegressor(n_estimators= 69, max_depth= 4, min_child_weight= 20, subsample= 0.8, colsample_bytree=0.8 ,random_state=42)
forest_Min.fit(X_train, Y_Min_train)
Y_Max_pred=forest_Max.predict(X_test)
Y_Min_pred=forest_Min.predict(X_test)
print("MaxTemp pred r2score",r2_score(Y_Max_test, Y_Max_pred))
print("MinTemp pred r2score",r2_score(Y_Min_test, Y_Min_pred))
result=pd.DataFrame([Y_Max_pred,Y_Min_pred],index=["MaxTemp_pred","MinTemp_pred"]).T
result.index=X[-len(X_test):].index
result["MaxTemp_act"]=Y_Max_test
result["MinTemp_act"]=Y_Min_test
結果を整理。
result.plot(figsize=(16,6))
XX_predが予測値、XX_actが実測値です。
前回と比べると…良くなったのか…なぁ?スコアは良くなってますけどね。。
1/27の気温を題材に、実際に比較してみましょう。
前回との比較ということで、1/26のデータから1/27の予測を行ってみます。
df_pred=pd.DataFrame()
dfp=pd.read_csv("data2.csv",encoding="shiftjis",header=3).drop([0,1],axis=0).reset_index(drop=True)
df_pred["datetime"]=pd.to_datetime(dfp["年月日"])
df_pred["Max_temp"]=dfp["最高気温(℃)"]
df_pred["Min_temp"]=dfp["最低気温(℃)"]
df_pred=df_pred.set_index("datetime",drop=True)
df_pred=week_dataset(df_pred)
pred=pd.DataFrame()
pred["datetime"]=pd.to_datetime(["2019-01-27"])
pred.index=pred.datetime
pred=pred.drop("datetime",axis=1)
pred["Max -1"]=df_pred.iloc[-1]["Max_temp"]
pred["Min -1"]=df_pred.iloc[-1]["Min_temp"]
for i in range(2,8):
pred["Max -"+str(i)]=df_pred.iloc[-1]["Max -"+str(i-1)]
pred["Min -"+str(i)]=df_pred.iloc[-1]["Min -"+str(i-1)]
Y_Max_pred=forest_Max.predict(pred)
Y_Min_pred=forest_Min.predict(pred)
print("Max temp",Y_Max_pred)
print("Min temp",Y_Min_pred)
さて、予測したところ、1/27の最高気温は9.09度、最低気温は-1.41度のようです。
実際の1/27は最高気温10.2度、最低気温-1.5度だったようです。
前の結果を確認してみましょう。
>Max temp [11.14383333]
>Min temp [-1.28191667]
最低気温はだいぶ近づきましたね。最高気温は精度変わらずくらいです。上振れか下振れかの違いです。 でもこれでもGoogleに表示される天気予報と同じくらいに当てられています。(この日に限りですが)
このスクリプトは1時間かからないくらいで作成できたので、そのくらいのコストでこの精度ならかなり良いような気がしますね。
これから言えることは、おそらくこの程度の誤差が勾配Boostingによる数値ベース予想の限界ってことですね。(XGBとかCatBoostを使ってアンサンブルしても目に見えて精度が向上しそうにない)
気象予報士とかは雲の形とか太陽の活動度とか使って予想をしているそうなので、そういうデータを適切に追加すれば、もっと精度の良い予想ができそうです。