【tensorflow2.0】處理時間序列資料
國內的新冠肺炎疫情從發現至今已經持續3個多月了,這場起源於吃野味的災難給大家的生活造成了諸多方面的影響。
有的同學是收入上的,有的同學是感情上的,有的同學是心理上的,還有的同學是體重上的。
那麼國內的新冠肺炎疫情何時結束呢?什麼時候我們才可以重獲自由呢?
本篇文章將利用TensorFlow2.0建立時間序列RNN模型,對國內的新冠肺炎疫情結束時間進行預測。
一,準備資料
本文的資料集取自tushare,獲取該資料集的方法參考了以下文章。
《https://zhuanlan.zhihu.com/p/109556102》
首先看下資料是什麼樣子的:
有時間、確診人數、治癒人數、死亡人數這些列。
然後是建立資料集:
import numpy as np import pandas as pd import matplotlib.pyplot as plt import tensorflow as tf from tensorflow.keras import models,layers,losses,metrics,callbacks %matplotlib inline %config InlineBackend.figure_format = 'svg' df = pd.read_csv("./data/covid-19.csv",sep = "\t") df.plot(x = "date",y = ["confirmed_num","cured_num","dead_num"],figsize=(10,6)) plt.xticks(rotation=60) dfdata = df.set_index("date") dfdiff = dfdata.diff(periods=1).dropna() dfdiff = dfdiff.reset_index("date") dfdiff.plot(x = "date",y = ["confirmed_num","cured_num","dead_num"],figsize=(10,6)) plt.xticks(rotation=60) dfdiff = dfdiff.drop("date",axis = 1).astype("float32") # 用某日前8天視窗資料作為輸入預測該日資料 WINDOW_SIZE = 8 def batch_dataset(dataset): dataset_batched = dataset.batch(WINDOW_SIZE,drop_remainder=True) return dataset_batched ds_data = tf.data.Dataset.from_tensor_slices(tf.constant(dfdiff.values,dtype = tf.float32)) \ .window(WINDOW_SIZE,shift=1).flat_map(batch_dataset) ds_label = tf.data.Dataset.from_tensor_slices( tf.constant(dfdiff.values[WINDOW_SIZE:],dtype = tf.float32)) # 資料較小,可以將全部訓練資料放入到一個batch中,提升效能 ds_train = tf.data.Dataset.zip((ds_data,ds_label)).batch(38).cache()
二,定義模型
使用Keras介面有以下3種方式構建模型:使用Sequential按層順序構建模型,使用函式式API構建任意結構模型,繼承Model基類構建自定義模型。
此處選擇使用函式式API構建任意結構模型。
# 考慮到新增確診,新增治癒,新增死亡人數資料不可能小於0,設計如下結構 class Block(layers.Layer): def __init__(self, **kwargs): super(Block, self).__init__(**kwargs) def call(self, x_input,x): x_out = tf.maximum((1+x)*x_input[:,-1,:],0.0) return x_out def get_config(self): config = super(Block, self).get_config() return config tf.keras.backend.clear_session() x_input = layers.Input(shape = (None,3),dtype = tf.float32) x = layers.LSTM(3,return_sequences = True,input_shape=(None,3))(x_input) x = layers.LSTM(3,return_sequences = True,input_shape=(None,3))(x) x = layers.LSTM(3,return_sequences = True,input_shape=(None,3))(x) x = layers.LSTM(3,input_shape=(None,3))(x) x = layers.Dense(3)(x) # 考慮到新增確診,新增治癒,新增死亡人數資料不可能小於0,設計如下結構 # x = tf.maximum((1+x)*x_input[:,-1,:],0.0) x = Block()(x_input,x) model = models.Model(inputs = [x_input],outputs = [x]) model.summary()
三,訓練模型
訓練模型通常有3種方法,內建fit方法,內建train_on_batch方法,以及自定義訓練迴圈。此處我們選擇最常用也最簡單的內建fit方法。
注:迴圈神經網路除錯較為困難,需要設定多個不同的學習率多次嘗試,以取得較好的效果。
# 自定義損失函式,考慮平方差和預測目標的比值 class MSPE(losses.Loss): def call(self,y_true,y_pred): err_percent = (y_true - y_pred)**2/(tf.maximum(y_true**2,1e-7)) mean_err_percent = tf.reduce_mean(err_percent) return mean_err_percent def get_config(self): config = super(MSPE, self).get_config() return config import datetime optimizer = tf.keras.optimizers.Adam(learning_rate=0.01) model.compile(optimizer=optimizer,loss=MSPE(name = "MSPE")) logdir = "./data/keras_model/" + datetime.datetime.now().strftime("%Y%m%d-%H%M%S") tb_callback = tf.keras.callbacks.TensorBoard(logdir, histogram_freq=1) # 如果loss在100個epoch後沒有提升,學習率減半。 lr_callback = tf.keras.callbacks.ReduceLROnPlateau(monitor="loss",factor = 0.5, patience = 100) # 當loss在200個epoch後沒有提升,則提前終止訓練。 stop_callback = tf.keras.callbacks.EarlyStopping(monitor = "loss", patience= 200) callbacks_list = [tb_callback,lr_callback,stop_callback] history = model.fit(ds_train,epochs=500,callbacks = callbacks_list)
部分結果:
...... Epoch 491/500 1/1 [==============================] - 0s 11ms/step - loss: 0.2643 - lr: 0.0050 Epoch 492/500 1/1 [==============================] - 0s 12ms/step - loss: 0.2625 - lr: 0.0050 Epoch 493/500 1/1 [==============================] - 0s 12ms/step - loss: 0.2628 - lr: 0.0050 Epoch 494/500 1/1 [==============================] - 0s 11ms/step - loss: 0.2633 - lr: 0.0050 Epoch 495/500 1/1 [==============================] - 0s 12ms/step - loss: 0.2619 - lr: 0.0050 Epoch 496/500 1/1 [==============================] - 0s 11ms/step - loss: 0.2627 - lr: 0.0050 Epoch 497/500 1/1 [==============================] - 0s 11ms/step - loss: 0.2622 - lr: 0.0050 Epoch 498/500 1/1 [==============================] - 0s 12ms/step - loss: 0.2618 - lr: 0.0050 Epoch 499/500 1/1 [==============================] - 0s 12ms/step - loss: 0.2624 - lr: 0.0050 Epoch 500/500 1/1 [==============================] - 0s 12ms/step - loss: 0.2616 - lr: 0.0050
四,評估模型
評估模型一般要設定驗證集或者測試集,由於此例資料較少,我們僅僅視覺化損失函式在訓練集上的迭代情況。
%matplotlib inline %config InlineBackend.figure_format = 'svg' import matplotlib.pyplot as plt def plot_metric(history, metric): train_metrics = history.history[metric] epochs = range(1, len(train_metrics) + 1) plt.plot(epochs, train_metrics, 'bo--') plt.title('Training '+ metric) plt.xlabel("Epochs") plt.ylabel(metric) plt.legend(["train_"+metric]) plt.show() plot_metric(history,"loss")
五,使用模型
此處我們使用模型預測疫情結束時間,即 新增確診病例為0 的時間。
# 使用dfresult記錄現有資料以及此後預測的疫情資料 dfresult = dfdiff[["confirmed_num","cured_num","dead_num"]].copy() dfresult.tail()
# 預測此後100天的新增走勢,將其結果新增到dfresult中 for i in range(100): arr_predict = model.predict(tf.constant(tf.expand_dims(dfresult.values[-38:,:],axis = 0))) dfpredict = pd.DataFrame(tf.cast(tf.floor(arr_predict),tf.float32).numpy(), columns = dfresult.columns) dfresult = dfresult.append(dfpredict,ignore_index=True) dfresult.query("confirmed_num==0").head()
# 第55天開始新增確診降為0,第45天對應3月10日,也就是10天后,即預計3月20日新增確診降為0 # 注:該預測偏樂觀 dfresult.query("cured_num==0").head()
# 第164天開始新增治癒降為0,第45天對應3月10日,也就是大概4個月後,即7月10日左右全部治癒。 # 注: 該預測偏悲觀,並且存在問題,如果將每天新增治癒人數加起來,將超過累計確診人數。 dfresult.query("dead_num==0").head() # 第60天開始,新增死亡降為0,第45天對應3月10日,也就是大概15天后,即20200325 # 該預測較為合理
六,儲存模型
推薦使用TensorFlow原生方式儲存模型。
model.save('./data/tf_model_savedmodel', save_format="tf") print('export saved model.') model_loaded = tf.keras.models.load_model('./data/tf_model_savedmodel',compile=False) optimizer = tf.keras.optimizers.Adam(learning_rate=0.001) model_loaded.compile(optimizer=optimizer,loss=MSPE(name = "MSPE")) model_loaded.predict(ds_train)
參考:
開源電子書地址:https://lyhue1991.github.io/eat_tensorflow2_in_30_days/
GitHub 專案地址:https://github.com/lyhue1991/eat_tensorflow2_in_30_