本文記錄了筆者用Keras框架編寫BP神經網絡,訓練並預測秦皇島未來煤價數據,共分爲三部分:訓練、驗證和預測。本文編寫於2020年5月19日,文中代碼全爲Python 3代碼,並在Jupyter中測試通過。
點擊跳轉至本文數據集下載鏈接
獻給新手!大家有疑問可以在評論區留言,一起進步~
1、數據讀入
數據集下載鏈接:戳這裏
訓練集一共62條,每條包含了9個屬性(A~I)和目標值target,我們將用他們來訓練模型:根據A-I的值來預測target的值。
以下是需要預測的數據,我們將它放置在數據集的64~94行:
(僅有A-I屬性的數據,無target值,我們將用訓練好的模型來預測其target)
首先用Pandas讀入全部數據——
import pandas as pd
data = pd.read_csv("C:/Users/LRK/Desktop/0518.csv")
注意,用Pandas讀取的數據會是一個Numpy數組,在這種數據集上非常好用!!
★ 手動劃分訓練集(train_data)、訓練目標(train_targets),他們都是Numpy數組。
train_data = data.loc[0:61, ['A','B','C','D','E','F','G','H','I']]
train_data.shape
# 讀取數據集的0~61行、A~I列的數據
Output:(62, 9)
train_targets = data.loc[0:61, ['target']]
train_targets.shape
# 讀取數據集的0~61行、target列的數據
Output:(62, 1)
用Pandas讀取train_data是這樣的,非常美觀:
★ 讀取需要預測的數據的A-I特徵:
test_data = data.loc[62:92, ['A','B','C','D','E','F','G','H','I']]
2、數據預處理
在這裏,我們採用“數據標準化處理”,即:每個數據減去該列平均值,再除以該列的標準差。這是機器學習中常見的數據處理方式,一定程度上縮小了A~I列不同屬性的數據大小範圍,方便神經網絡進行訓練。
(這裏不明白的可以參考Andrew Ng的視頻課程)
mean = train_data.mean(axis=0)
train_data -= mean
std = train_data.std(axis=0)
train_data /= std
test_data -= mean
test_data /= std
警告:test_data進行預處理的時候,減去的平均值和除以的標準差,都是在訓練集上得出的,而非測試集上!!否則會影響準確性。
3、編寫模型(類BP神經網絡)
實在不知道Dense層堆疊該叫啥模型,就先管他叫做類BP網絡吧~
import os
os.environ["CUDA_VISIBLE_DEVICES"] = "1" # GTX 1050 Ti
from keras import models
from keras import layers
def build_model():
model = models.Sequential()
model.add(layers.Dense(64, activation='relu',
input_shape=(train_data.shape[1],)))
model.add(layers.Dense(64, activation='relu'))
model.add(layers.Dense(64, activation='relu'))
model.add(layers.Dense(1))
model.compile(optimizer='rmsprop', loss='mse', metrics=['mae'])
return model
用MAE(平均絕對誤差)來衡量訓練效果。MAE是指實際值與預測值的差值大小。
4、劃分驗證集
考慮到訓練數據很少,我們採用K折交叉驗證(k=4)
import numpy as np
k = 4
num_val_samples = len(train_data) // k
num_epochs = 100
all_scores = []
for i in range(k):
print('processing fold #', i)
# 準備驗證數據:第 k 個分區的數據
val_data = train_data[i * num_val_samples: (i + 1) * num_val_samples]
val_targets = train_targets[i * num_val_samples: (i + 1) * num_val_samples]
# 準備訓練數據:其他所有分區的數據
partial_train_data = np.concatenate(
[train_data[:i * num_val_samples],
train_data[(i + 1) * num_val_samples:]],
axis=0)
partial_train_targets = np.concatenate(
[train_targets[:i * num_val_samples],
train_targets[(i + 1) * num_val_samples:]],
axis=0)
# 構建 Keras 模型(已編譯)
model = build_model()
model.fit(partial_train_data, partial_train_targets,
epochs=num_epochs, batch_size=1, verbose=1)
# 在驗證數據上評估模型
val_mse, val_mae = model.evaluate(val_data, val_targets, verbose=0)
all_scores.append(val_mae)
查看MAE的平均值:
np.mean(all_scores)
輸出:60.37001419067383
5、訓練模型
先練500輪看看。
from keras import backend as K
K.clear_session()
num_epochs = 500
all_mae_histories = []
for i in range(k):
print('processing fold #', i)
val_data = train_data[i * num_val_samples: (i + 1) * num_val_samples]
val_targets = train_targets[i * num_val_samples: (i + 1) * num_val_samples]
partial_train_data = np.concatenate(
[train_data[:i * num_val_samples],
train_data[(i + 1) * num_val_samples:]],
axis=0)
partial_train_targets = np.concatenate(
[train_targets[:i * num_val_samples],
train_targets[(i + 1) * num_val_samples:]],
axis=0)
model = build_model()
history = model.fit(partial_train_data, partial_train_targets,
validation_data=(val_data, val_targets),
epochs=num_epochs, batch_size=1, verbose=1)
mae_history = history.history['val_mean_absolute_error']
all_mae_histories.append(mae_history)
查看500輪中,每一輪的各個分區上的MAE的平均值:
average_mae_history = [
np.mean([x[i] for x in all_mae_histories]) for i in range(num_epochs)]
最後幾輪的MAE平均值如下:
可以看出,經過訓練,誤差已經縮小了很多!
我們再畫圖來看看——
import matplotlib.pyplot as plt
plt.plot(range(1, len(average_mae_history) + 1), average_mae_history)
plt.xlabel('Epochs')
plt.ylabel('Validation MAE')
plt.show()
刪除前40輪的數據,再重新畫圖,方便觀察:
def smooth_curve(points, factor=0.9):
smoothed_points = []
for point in points:
if smoothed_points:
previous = smoothed_points[-1]
smoothed_points.append(previous * factor + point * (1 - factor))
else:
smoothed_points.append(point)
return smoothed_points
smooth_mae_history = smooth_curve(average_mae_history[40:])
plt.plot(range(1, len(smooth_mae_history) + 1), smooth_mae_history)
plt.xlabel('Epochs')
plt.ylabel('Validation MAE')
plt.show()
可以看出,模型並沒有過擬合。
現在,我們用全部的訓練數據,對模型從頭開始重新訓練!
model = build_model()
# Train it on the entirety of the data.
model.fit(train_data, train_targets,
epochs=600, batch_size=16, verbose=1)
分享最後幾輪的訓練過程如下:
由於訓練集很小,效果可能沒有很好。大家可以分享自己的指標或建議在評論區裏~
6、用訓練好的模型來預測新數據
這一塊網上能搜到的代碼很少,作爲新手也是踩了不少坑~最後自己琢磨出來了怎麼用Keras的Predict。
★ 其實!就兩句話的事兒!用Keras真的非常方便了!!
predict = model.predict(test_data)
predict
接下來,就會打印出64—94行(即預測集)對應的target值,如下圖所示。
用Python可以寫代碼把預測數據寫入到指定csv文件的指定行列上,所以應用時不必一個一個複製粘貼!具體代碼自己網上一扒就有~
7、預測結果可視化
在數學建模中,經常會遇到需要以圖表呈現數據。即數據可視化。這裏我們順便也演示一下。
注意:我們剛剛預測的30個值是未來30天的煤炭價格。我們將其畫折線圖呈現出來。代碼如下。
from matplotlib import pyplot as plt
%matplotlib inline
plt.rcParams['font.sans-serif'] = [u'SimHei']
plt.rcParams['axes.unicode_minus'] = False
x = np.arange(1,31)
plt.title("未來30天煤價走勢預測")
plt.xlabel("未來一個月")
plt.ylabel("煤價")
plt.plot(range(0, 31, 1),predict)
plt.show()
效果如下圖。 注意:筆者一開始橫縱軸寫反了所以大家看到的橫縱座標標識是反的,但圖是對的。懶得改啦~
★ 既然說到python的matplotlib,那順便解釋兩個新手常見的問題吧!
- Jupyter中畫圖,寫標註時無法正常顯示中文。
解決辦法:加上這三條語句!別問爲什麼,加上去就是了!!
from matplotlib import pyplot as plt
plt.rcParams['font.sans-serif'] = [u'SimHei']
plt.rcParams['axes.unicode_minus'] = False
- 用matplotlib畫圖,顯示什麼<Figure …>,圖沒顯示出來。
解決辦法:加上下面的第一條語句。如果不行,把第二條也加上(加畫布用的)。
%matplotlib inline
plt.figure()