這篇文章的內容來自B站UP主唐國樑Tommy老師的視頻
TensorFlow 2.0 基於LSTM多變量_共享單車使用量預測: https://www.bilibili.com/video/BV1y5411K7NR
案例實現思路:
- 模塊導入
- 加載數據集、預處理
- 數據可視化
- 數據預處理
- 特徵工程
- 模型編譯、訓練、驗證
- 模型驗證
1、模塊導入
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import os
import datetime
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import MinMaxScaler
from sklearn.metrics import r2_score
import tensorflow as tf
from tensorflow.keras import Sequential, layers, utils, losses
from tensorflow.keras.callbacks import ModelCheckpoint, TensorBoard
import warnings
warnings.filterwarnings('ignore')
2、加載數據集、預處理
共享單車使用量數據集
# 加載數據集
dataset = pd.read_csv("BikeShares.csv", parse_dates=['timestamp'], index_col=['timestamp'])
# (17414, 9)
# 默認顯示前5行
dataset.head()
timestamp | cnt | t1 | t2 | hum | wind_speed | weather_code | is_holiday | is_weekend | season |
---|---|---|---|---|---|---|---|---|---|
2015-01-04 00:00:00 | 182 | 3.0 | 2.0 | 93.0 | 6.0 | 3.0 | 0.0 | 1.0 | 3.0 |
2015-01-04 01:00:00 | 138 | 3.0 | 2.5 | 93.0 | 5.0 | 1.0 | 0.0 | 1.0 | 3.0 |
2015-01-04 02:00:00 | 134 | 2.5 | 2.5 | 96.5 | 0.0 | 1.0 | 0.0 | 1.0 | 3.0 |
2015-01-04 03:00:00 | 72 | 2.0 | 2.0 | 100.0 | 0.0 | 1.0 | 0.0 | 1.0 | 3.0 |
2015-01-04 04:00:00 | 47 | 2.0 | 0.0 | 93.0 | 6.5 | 1.0 | 0.0 | 1.0 | 3.0 |
字段說明:
- timestamp : 時間戳
- cnt : 租用共享單車的數量(目標值)
- t1 : 氣溫
- t2 : 體感溫度
- hum : 溼度
- wind_speed : 風速
- weather_code : 天氣的類別(1=乾淨,2 =很少的雲,3=碎雲,4=多雲,7=雨/小雨,10=有雷雨,26=降雪,94=冰霧)分類類型
- is_holiday : 是否爲假期(1:假期 / 0:工作日)
- is_weekend : 是否爲週末(1:週末 / 0:工作日)
- season : 季節(0:春天 ; 1:夏天 ; 2:秋天 ; 3:冬天)
2、數據可視化
字段t1(氣溫)與字段cnt(單車使用量)之間的關係
plt.figure(figsize=(16,8))
sns.pointplot(x='t1', y='cnt', data=dataset)
plt.show()
字段t2(體感溫度)與字段cnt(單車使用量)之間的關係
plt.figure(figsize=(16,8))
sns.lineplot(x='t2', y='cnt', data=dataset)
plt.show()
字段hum(溼度)與字段cnt(單車使用量)之間的關係
plt.figure(figsize=(16,8))
sns.lineplot(x='hum', y='cnt', data=dataset)
plt.xticks([])
plt.show()
字段weather_code : 天氣的類別與字段cnt(單車使用量)之間的關係
# weather_code : 天氣的類別(1=乾淨,2 =很少的雲,3=碎雲,4=多雲,7=雨/小雨,10=有雷雨,26=降雪,94=冰霧
plt.figure(figsize=(16,8))
sns.pointplot(x='weather_code', y='cnt', data=dataset)
plt.show()
注意:創建時間字段,用於分析數據
# 創建hour字段
dataset['hour'] = dataset.index.hour
# 創建year字段
dataset['year'] = dataset.index.year
# 創建month字段
dataset['month'] = dataset.index.month
基於is_holiday 統計 hour 與 cnt 之間的分佈
# 1:假期 / 0:工作日
plt.figure(figsize=(16,8))
sns.lineplot(x='hour', y='cnt', data=dataset, hue='is_holiday')
plt.xticks(list(range(24)))
plt.show()
基於 season 統計 hour 與 cnt 之間的分佈
# 0:春天 ; 1:夏天 ; 2:秋天 ; 3:冬天
plt.figure(figsize=(16,8))
sns.pointplot(x='hour', y='cnt', data=dataset, hue='season')
plt.xticks(list(range(24)))
plt.show()
基於 is_holiday 統計 hour 與 cnt 之間的分佈
# 1:假期 / 0:工作日
plt.figure(figsize=(16,8))
sns.lineplot(x='month', y='cnt', data=dataset, hue='is_holiday')
plt.show()
3、數據預處理
# 刪除多餘的列 hour, year, month
# axis=1 刪列
dataset.drop(columns=['hour', 'year', 'month'], axis=1, inplace=True)
timestamp | cnt | t1 | t2 | hum | wind_speed | weather_code | is_holiday | is_weekend | season |
---|---|---|---|---|---|---|---|---|---|
2015-01-04 00:00:00 | 182 | 3.0 | 2.0 | 93.0 | 6.0 | 3.0 | 0.0 | 1.0 | 3.0 |
2015-01-04 01:00:00 | 138 | 3.0 | 2.5 | 93.0 | 5.0 | 1.0 | 0.0 | 1.0 | 3.0 |
2015-01-04 02:00:00 | 134 | 2.5 | 2.5 | 96.5 | 0.0 | 1.0 | 0.0 | 1.0 | 3.0 |
2015-01-04 03:00:00 | 72 | 2.0 | 2.0 | 100.0 | 0.0 | 1.0 | 0.0 | 1.0 | 3.0 |
2015-01-04 04:00:00 | 47 | 2.0 | 0.0 | 93.0 | 6.5 | 1.0 | 0.0 | 1.0 | 3.0 |
- 注意事項:
- cnt : 是標籤;
- t1, t2, hum, wind_speed : 是數值類型字段;
- weather_code, is_holiday, is_weekend, season : 是分類類型字段;
# 分別對字段t1, t2, hum, wind_speed進行歸一化
columns = ['cnt', 't1', 't2', 'hum', 'wind_speed']
for col in columns:
scaler = MinMaxScaler()
dataset[col] = scaler.fit_transform(dataset[col].values.reshape(-1,1))
timestamp | cnt | t1 | t2 | hum | wind_speed | weather_code | is_holiday | is_weekend | season |
---|---|---|---|---|---|---|---|---|---|
2015-01-04 00:00:00 | 0.023155 | 0.126761 | 0.2000 | 0.911950 | 0.106195 | 3.0 | 0.0 | 1.0 | 3.0 |
2015-01-04 01:00:00 | 0.017557 | 0.126761 | 0.2125 | 0.911950 | 0.088496 | 1.0 | 0.0 | 1.0 | 3.0 |
2015-01-04 02:00:00 | 0.017048 | 0.112676 | 0.2125 | 0.955975 | 0.000000 | 1.0 | 0.0 | 1.0 | 3.0 |
2015-01-04 03:00:00 | 0.009160 | 0.098592 | 0.2000 | 1.000000 | 0.000000 | 1.0 | 0.0 | 1.0 | 3.0 |
2015-01-04 04:00:00 | 0.005980 | 0.098592 | 0.1500 | 0.911950 | 0.115044 | 1.0 | 0.0 | 1.0 | 3.0 |
4、特徵工程
# 特徵數據集
X = dataset.drop(columns=['cnt'], axis=1)
# X.shape (17414, 8)
# 標籤數據集
y = dataset['cnt']
# y.shape (17414,)
1 數據集分離: X_train, X_test
# shuffle=False 不能打亂 因爲是時序預測
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, shuffle=False, random_state=666)
# X_train.shape (13931, 8)
# y_train.shape (13931,)
# X_test.shape (3483, 8)
# y_test.shape (3483,)
2 構造特徵數據集
def create_dataset(X, y, seq_len=10):
features = []
targets = []
for i in range(0, len(X) - seq_len, 1):
data = X.iloc[i:i+seq_len] # 序列數據
label = y.iloc[i+seq_len] # 標籤數據
# 保存到features和labels
features.append(data)
targets.append(label)
# 返回
return np.array(features), np.array(targets)
# ① 構造訓練特徵數據集
train_dataset, train_labels = create_dataset(X_train, y_train, seq_len=10)
# 有13921個滑動窗口 每個滑動窗口有10條數據 每條數據有8個特徵
# train_dataset.shape (13921, 10, 8)
# train_labels.shape (13921,)
# ② 構造測試特徵數據集
test_dataset, test_labels = create_dataset(X_test, y_test, seq_len=10)
# test_dataset.shape (3473, 10, 8)
# test_labels.shape (3473,)
3 構造批數據
def create_batch_dataset(X, y, train=True, buffer_size=1000, batch_size=128):
batch_data = tf.data.Dataset.from_tensor_slices((tf.constant(X), tf.constant(y))) # 數據封裝,tensor類型
if train: # 訓練集
return batch_data.cache().shuffle(buffer_size).batch(batch_size)
else: # 測試集
return batch_data.batch(batch_size)
# 訓練批數據
train_batch_dataset = create_batch_dataset(train_dataset, train_labels)
# 測試批數據
test_batch_dataset = create_batch_dataset(test_dataset, test_labels, train=False)
5、模型搭建、編譯、訓練
# 模型搭建--版本1
model = Sequential([#(10,8)
layers.LSTM(units=256, input_shape=train_dataset.shape[-2:], return_sequences=True),
layers.Dropout(0.4),
layers.LSTM(units=256, return_sequences=True),
layers.Dropout(0.3),
layers.LSTM(units=128, return_sequences=True),
layers.LSTM(units=32),
layers.Dense(1)
])
# 模型編譯
model.compile(optimizer='adam',loss='mse')
checkpoint_file = "best_model.hdf5"
checkpoint_callback = ModelCheckpoint(filepath=checkpoint_file,
monitor='loss',
mode='min',
save_best_only=True,
save_weights_only=True)
# 模型訓練
history = model.fit(train_batch_dataset,
epochs=30,
validation_data=test_batch_dataset,
callbacks=[checkpoint_callback])
# 顯示訓練結果
plt.figure(figsize=(16,8))
plt.plot(history.history['loss'], label='train loss')
plt.plot(history.history['val_loss'], label='val loss')
plt.legend(loc='best')
plt.show()
6、模型驗證
# test_dataset.shape (3473, 10, 8)
test_preds = model.predict(test_dataset, verbose=1)
# test_preds.shape (3473, 1)
# 真值shape
# test_labels.shape (3473,)
# 計算r2值
score = r2_score(test_labels, test_preds)
print("r^2 值爲: ", score)
r^2 值爲: 0.4916024135223812
# 繪製 預測與真值結果
plt.figure(figsize=(16,8))
plt.plot(test_labels[:300], label="True value")
plt.plot(test_preds[:300], label="Pred value")
plt.legend(loc='best')
plt.show()