利用TensorFlow2.0為膽固醇、血脂、血壓資料構建時序深度學習模型(python完整原始碼)
背景資料描述
膽固醇、高血脂、高血壓是壓在廣大中年男性頭上的三座大山,如何有效的監控他們,做到早發現、早預防、早治療尤為關鍵,趁著這個假期我就利用TF2.0構建了一套時序預測模型,一來是可以幫我預發疾病,二來也可以體驗下TF2.0的特性。
先來看下資料結構:
-
date表示的是測量日期
-
cholesterol代表膽固醇數值
-
blood_fat代表血脂
-
blood_pressure代表血壓
整個的建模思路就是將這三個數值一起構建時序模型,因為這三個指標不能獨立來看,他們是相互有影響的,互為特徵和目標值。
程式碼詳解
廢話不多說,我們先看下完整的程式碼,程式碼比較長,我把整個程式碼分為資料探查、網路構建、模型訓練、模型儲存和預測這4個模組進行拆分並講解,可以參見程式碼中的註釋進行模組區分。環境使用的是python3.7、TensorFlow2.0版本。(完整程式碼見文末)
1 資料探查
在資料探查模組使用了pandas將資料讀取進來,然後用diff函式構建了時序資料的增長率曲線圖,因為做時序資料預測,更多地是去看資料的增長或者降低趨勢。通過matplotlib可以把資料的成長曲線畫出來:
2 網路結構構建
使用的是標準的lstm網路結構,可以通過model.summary函式將深度學習網路結構打印出來,如下圖所示:
3 模型訓練
在模型訓練環節主要是構建了收斂函式MSPE,MSPE是一種殘差收斂演算法,具體計算公式比較簡單:
(y_true - y_pred)**2/(tf.maximum(y_true**2,1e-7))
4 模型儲存和預測
第四部分先使用model.save這個TF的官方模型保持函式將模型儲存到本地,建議儘量使用這種官方的模型保持方案。
然後load模型物件,用model.predict函式對下一階段的3個指標資料做一個預測。最終的預測結果存放在arr_predict物件中,預測結果為:
[[0.26552328,0.33151102,0]]
以上預測的是資料的增長率,假設最後一階段的三個指標的資料分別為4.5、3.2、119,那麼最終下一階段的預測值就是:
[[4.5+0.26552328,3.2+0.33151102,119+0]]
完整程式碼如下,有興趣的同學可以跑一跑玩一玩。
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
import os
import datetime
#1.資料探查-------------------------------------
df = pd.read_csv("/Users/garvin/Downloads/data.txt",sep = "\t")
print(df)
dfdata = df.set_index("date")
dfdiff = dfdata.diff(periods=1).dropna()
dfdiff = dfdiff.reset_index("date")
dfdiff.plot(x = "date",y = ["cholesterol","blood_fat","blood_pressure"],figsize=(10,6))
plt.xticks(rotation=60)
dfdiff = dfdiff.drop("date",axis = 1).astype("float32")
plt.show()
WINDOW_SIZE = 1
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))
#We put all data into one batch for better efficiency since the data volume is small.
ds_train = tf.data.Dataset.zip((ds_data,ds_label)).batch(38).cache()
#2.構建網路結構-------------------------------------
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)
x = Block()(x_input,x)
model = models.Model(inputs = [x_input],outputs = [x])
model.summary()
#Customized loss function, consider the ratio between square error and the prediction
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
#3.模型訓練-------------------------------------
optimizer = tf.keras.optimizers.Adam(learning_rate=0.01)
model.compile(optimizer=optimizer,loss=MSPE(name = "MSPE"))
stamp = datetime.datetime.now().strftime("%Y%m%d-%H%M%S")
logdir = os.path.join('data', 'autograph', stamp)
tb_callback = tf.keras.callbacks.TensorBoard(logdir, histogram_freq=1)
#Half the learning rate if loss is not improved after 100 epoches
lr_callback = tf.keras.callbacks.ReduceLROnPlateau(monitor="loss",factor = 0.5, patience = 100)
#Stop training when loss is not improved after 200 epoches
stop_callback = tf.keras.callbacks.EarlyStopping(monitor = "loss", patience= 200)
callbacks_list = [tb_callback,lr_callback,stop_callback]
history = model.fit(ds_train,epochs=10,callbacks = callbacks_list)
#4.模型儲存和預測-------------------------------------
model.save('tf_model_savedmodel', save_format="tf")
print('export saved model.')
dfresult = dfdiff[["cholesterol","blood_fat","blood_pressure"]].copy()
dfresult.tail()
#print(dfresult.values[-1:,:])
arr_predict = model.predict(tf.constant(tf.expand_dims(dfresult.values[-1:,:],axis = 0)))
print(arr_predict)