系列文章:
一、搭建屬於你的第一個神經網絡
二、訓練完的網絡去哪裏找
之前mnist的例子是一個分類問題,今天來講的是迴歸問題,所謂迴歸就是根據過去的點來擬合曲線,然後根據曲線來預測以後的值。
from keras.datasets import boston_housing
(train_data, train_targets), (test_data, test_targets) = boston_housing.load_data()
這個數據集和之前的mnist不同,只有506個數據,而且每一個特徵的取值範圍也不盡相同。
>>> train_data.shape
(404, 13)
>>> test_data.shape
(102, 13)
我們可以看到訓練數據包含404個點,每個數據點包含13個特徵。
這十三個特徵包括:
1.人均犯罪率
2.在25000平方英尺上住房的比例
3.每個鎮上平均的非零售企業比例
4.Charles River dummy variable(= 1 if tract bounds river; 0 otherwise))
5.一氧化氮的濃度,單位是千萬分之一
6.每個住所的平均房間數
7.1940年以前自己修的建築比例
8.波士頓五個就業中心的加權距離
9.徑向公路適應性指標
10.每10000的稅率的完全價值
11.鎮上的學生-老師比例
12.其中Bk是黑人比例
13.低地位人口比例
而標籤則是有房子人的房價的中位數。
數據準備
mean = train_data.mean(axis=0)
train_data -= mean
std = train_data.std(axis=0)
train_data /= std
test_data -= mean
test_data /= std
有必要解釋一下axis,這個代表的是運算的方向,我們的train_data是兩個維度的,爲(404,13),由於python從0開始計數,axis=0的意思就是考慮404這個維度上的數據,這裏mean就是求這404個數據的均值,std則求這404個數據的標準差,然後剩下的就是減去均值除以標準差,這個過程叫做歸一化,屬於正則化的一種,目的是防止不收斂,這個之前提到過。
搭建網絡
由於數據量小,我們搭建的網絡的量級也小,才能夠防止過擬合
from keras import models
from keras import layers
def build_model():
# Because we will need to instantiate
# the same model multiple time,
# we use a function to construct it.
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(1))
model.compile(optimizer='rmsprop', loss='mse', metrics=['mae'])
return model
由於我們要實例化教學,這個模型將會用很多次,所以這裏定義成一個函數來調用。
在這裏有三點需要說明:
- 最後一次直接輸出了一個值,這樣就能夠預測任意範圍的值了,如果調用了什麼激活函數,例如sigmoid,會導致最後得到的值限制在0,1之間,那麼這不是我們想要的。
- 這裏的loss採用了mse,均方誤差,這在迴歸問題中很常用。
- metrics採用mae,代表絕對誤差,即預測值和目標值的差值的絕對值。
k重驗證集
數據量小導致了我們在進行驗證的時候很可能結果會依賴於我們選取的驗證集,爲了解決這個問題,我們就把測試集劃成幾部分,這幾部分輪流當驗證集,分別訓練驗證,得到結果,最後平均一下,得到的結果就更具有說服力,這就叫做k-fold。
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)
# Prepare the validation data: data from partition # 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]
# Prepare the training data: data from all other partitions
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)
# Build the Keras model (already compiled)
model = build_model()
# Train the model (in silent mode, verbose=0)
model.fit(partial_train_data, partial_train_targets,
epochs=num_epochs, batch_size=1, verbose=0)
# Evaluate the model on the validation data
val_mse, val_mae = model.evaluate(val_data, val_targets, verbose=0)
all_scores.append(val_mae)
代碼本身沒什麼難度,最難的可能也就是concatenate沒見過,這個就是把兩個數組連在一起。
>>> all_scores
[2.588258957792037, 3.1289568449719116, 3.1856116051248984, 3.0763342615401386]
>>> np.mean(all_scores)
2.9947904173572462
最後得到了結果,預測接近於3.0
保存每一次交換訓練的結果
把訓練批次調到500以後查看預測均值和預測的變換趨勢
num_epochs = 500
all_mae_histories = []
for i in range(k):
print('processing fold #', i)
# Prepare the validation data: data from partition # 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]
# Prepare the training data: data from all other partitions
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)
# Build the Keras model (already compiled)
model = build_model()
# Train the model (in silent mode, verbose=0)
history = model.fit(partial_train_data, partial_train_targets,
validation_data=(val_data, val_targets),
epochs=num_epochs, batch_size=1, verbose=0)
mae_history = history.history['val_mean_absolute_error']
all_mae_histories.append(mae_history)
計算訓練後的均值:
average_mae_history = [
np.mean([x[i] for x in all_mae_histories]) for i in range(num_epochs)]
繪出預測變化:
由於樣本數量小,造成了方差大,波動比較大。
爲了便於觀察,選取十個點,滑動平均:
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[10:])
plt.plot(range(1, len(smooth_mae_history) + 1), smooth_mae_history)
plt.xlabel('Epochs')
plt.ylabel('Validation MAE')
plt.show()
這幅圖我們能比較 清楚的看到,在80epochs以後誤差又開始上升,這說明開始過擬合了。
調整epochs再來一遍
# Get a fresh, compiled model.
model = build_model()
# Train it on the entirety of the data.
model.fit(train_data, train_targets,
epochs=80, batch_size=16, verbose=0)
test_mse_score, test_mae_score = model.evaluate(test_data, test_targets)
最後我們得到結果
>>> test_mae_score
2.5532484335057877