前兩篇文章,咱們分析分別用了prophet 和 seasonal_decompose 對信號進行了分解。機器學習中一直流程的一段話:模型和特徵工程決定告終果的極限,調參只是逼近這個極限。python
因此本人通常不喜歡簡單粗暴的調參,而是喜歡在模型上多試幾回,或者在特徵上變一下。git
今天咱們來用lightgbm 進行該數據集的預測。github
爲何是ligbtgbm呢? 個人理由有如下幾點:算法
既然實戰,咱們就假設你聽過lightgbm。若是是純新手,能夠看看官方文檔。 據說有中文版的翻譯,不過裏面有少量的坑,好比由於更新不及時,致使缺乏一些metrics的翻譯等。 咱們以前有用ligtgbm 硬train 不平衡數據集。有興趣的能夠在閱讀完本文去翻閱。兩個文章屬於不一樣的用法,一個是分類,一個是迴歸。bash
首先導入一些庫。這幾個庫都是必須的, lightgbm 能夠創造模型,pandas處理數據,pyplot 畫圖,metrics 用來評判咱們的模型。機器學習
import lightgbm as lgb
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.metrics import mean_absolute_error
複製代碼
首先讀入數據,而且將時間字符串轉換爲Timestamp 格式。ide
file = 'data/PJME_hourly.csv'
raw_df = pd.read_csv(file)
dt_format = '%Y-%m-%d %H:%M:%S'
raw_df['Datetime']= pd.to_datetime(raw_df['Datetime'],format=dt_format)
print(raw_df.head())
複製代碼
預覽數據:函數
Datetime PJME_MW
0 2002-12-31 01:00:00 26498.0
1 2002-12-31 02:00:00 25147.0
2 2002-12-31 03:00:00 24574.0
3 2002-12-31 04:00:00 24393.0
4 2002-12-31 05:00:00 24860.0
複製代碼
咱們能夠看到特徵其實只有一個Datetime,PJME_MW 是咱們要預測的目標(target)。很明顯這樣的特徵個數沒法達到咱們的理想要求。 因此咱們要提取特徵。 對於時間序列的特徵,咱們通常的處理方法就是從中提取其餘週期的信息,好比只要小時,只要日期,只要星期。 爲何呢?post
咱們回想以前prophet 和seasonal_decompose的算法,這兩個算法都是想在原始信號中分解出某個特定週期的信號,可是對於樹類型的模型,它是基於分割點的,沒法提取到週期信息。學習
因此咱們須要顯式的告訴數模型,可能有哪些週期,而後讓樹對這個週期特徵進行分割。
這裏重點須要介紹pandas 庫中給的series.dt 類,這個類其實基本類是CombinedDatetimelikeProperties, 它綜合了三大類屬性:DatetimeProperties, TimedeltaProperties, PeriodProperties。 今天咱們主要用到的PeriodProperties(週期屬性)。 由於這個數據集只有一個特徵,因此這裏咱們儘量多的創造更多的特徵,好讓lightgbm發揮他的特長。數據集最小的採集頻率是每小時,因此咱們能夠依次建立:
具體能夠參考代碼,咱們們直接寫了一個小函數。輸入dataframe,返回帶有更多特徵的dataframe。
# create time series period features
def time_period_features(df,time_col):
# in hour level
df['hour'] = df[time_col].dt.hour
# in day level
df['dayofweek'] = df[time_col].dt.dayofweek
df['days_in_month'] = df[time_col].dt.days_in_month
df['dayofyear'] = df[time_col].dt.dayofyear
# week levels
df['weekday'] = df[time_col].dt.weekday
df['week'] = df[time_col].dt.week
df['weekofyear'] = df[time_col].dt.weekofyear
# month level
df['month'] = df[time_col].dt.month
df['is_month_start'] = df[time_col].dt.is_month_start
df['is_month_end'] = df[time_col].dt.is_month_end
# quarter level
df['quarter'] = df[time_col].dt.quarter
# year level
df['year'] = df[time_col].dt.year
return df
複製代碼
而後咱們調用該函數進行特徵工程。
raw_df = time_period_features(raw_df,'Datetime')
raw_df = raw_df.sort_values(by=['Datetime'])
複製代碼
這個和以前文章的思路一致,直接上代碼。
split_dt = pd.Timestamp('2015-01-01 00:00:00')
train_df = raw_df[raw_df['Datetime']< split_dt]
test_df = raw_df[raw_df['Datetime']>= split_dt]
train_Y = train_df['PJME_MW'].copy()
test_Y = test_df['PJME_MW'].copy()
train_df.drop(['Datetime','PJME_MW'],axis=1,inplace=True)
test_df.drop(['Datetime','PJME_MW'],axis=1,inplace=True)
複製代碼
咱們幾乎採用模型全部默認的參數,來「硬train一發」。 爲何?
由於我會用這個做爲該模型的baseline。以後咱們能夠有方向的調整參數達到更好的效果。
這裏僅僅要注意的是objective 參數,值爲regression,由於咱們要作的是一個迴歸模型。 另外,metric 我會採用l1, 由於l1 其實就是mean_absolute_error(MAE)。 採用這個metric,咱們能夠和其餘模型的結果進行對比。
代碼就是經典的lightgbm代碼示例,這裏不作更多解釋,詳細官方文檔會寫的更清楚。
# initial trial
dtrain = lgb.Dataset(train_df.values, label=train_Y.values)
dtest = lgb.Dataset(test_df.values, label=test_Y.values)
param = {
'max_depth': 6,
'eta': 0.05,
'objective': 'regression',
'verbose': 0,
'metric': ['l1'],
}
evals_result = {}
valid_sets = [dtrain, dtest]
valid_name = ['train', 'eval']
feature_name = list(train_df.columns)
model = lgb.train(param, dtrain, num_boost_round=500, feature_name=feature_name,
valid_sets=valid_sets, valid_names=valid_name, evals_result=evals_result)
print(f'fitting done')
y_hat = model.predict(test_df.values)
print(mean_absolute_error(test_Y.values,y_hat))
fig,ax = plt.subplots(2,1)
ax[0].plot(y_hat)
ax[1].plot(test_Y.values)
plt.show()
metric = 'l1'
fig, ax = plt.subplots()
ax.plot(evals_result['train'][metric], label='Train')
ax.plot(evals_result['eval'][metric], label='Test')
ax.legend()
plt.ylabel(f'{metric}')
plt.title(f'XGBoost {metric}')
plt.show()
複製代碼
結果展現:
fitting done
3032.011693891576
複製代碼
這幾乎是讓我震驚的結果,由於它比藍老大(facebook)的模型結果5183.653998193845 提升了好多。
咱們在plot如下訓練和測試的error歷史曲線。
咱們用了默認的參數,結果已經很出乎個人意料了。主要是由於baseline弱爆了!! lightgbm咱們還能夠再提升一下: 很明顯從趨勢來看,咱們出現了過擬合訓練偏差還有降低的趨勢,可是測試偏差已經反彈了。 下降過擬合,最快的方法就是調整max_depth。 另一個方法,就是花點時間看看lightgbm官方文檔的tuning 指南。 咱們不是用來參賽或者提升給最終客戶的成品,這裏咱們簡單調參。
param = {
'boosting':'dart',
'max_depth': 4,
'num_leaves':32,
'eta': 0.05,
'objective': 'regression',
'verbose': 0,
'metric': ['l1'],
}
複製代碼
結果果真好於默認值,2666.2777024354377這個值是什麼水平呢?咱們一樣對比kaggle上的「大神」的結果。kaggle上kernals 第一名分享的結果是採用xgboost ( xgboost 通常會比lightgbm結果要更好),它的預測結果是2848.891429322955。
看來咱們的結果還不錯。並且還666。。。 做爲一篇學習類的文章,咱們不就用繼續調參了。
2666.2777024354377
複製代碼
咱們貼上主要特徵圖,也能夠看到天天影響很大,再次是每小時的耗電量。
本文中咱們採用lightgbm預測能源消耗數據集,取得了相對不錯的效果。相比於prophet,lightgbm的結果更好一些。這主要是由於咱們顯式的提取了一些特徵。
藍老大說: 我能夠再試一試!(請見下一篇,救活prohet)。