2019年1月11日 星期五

深度學習 - boston_housing 波士頓房價數據集練習 - 回歸問題

深度學習  - boston_housing 波士頓房價數據集練習 - 回歸問題

from keras.datasets import boston_housing
(train_data, train_targets), (test_data, test_targets) = boston_housing.load_data()

將訓練數據&測試數據標準化(規一化)
mean = train_data.mean(axis=0)
train_data -= mean
std = train_data.std(axis=0)
train_data /= std

test_data -= mean
test_data /= std

建立網絡訓練模型,一樣使用Sequential()

from keras import models
from keras import layers

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))

選擇優化器"rmsprop",損失函數"mse",指標"mae"

model.compile(optimizer='rmsprop', loss='mse', metrics=['mae'])
這邊選用"mse",mean_squared_error(y_true, y_pred)

Ling之博客 :
損失函數: mean_squared_error
特點:
1)常用於回歸問題
2)MSE均方誤差損失

指標 mean_absolute_error(y_true, y_pred)
特點:
1)MAE平均絕對值誤差損失

指標選平均絕對值誤差,可以評估訓練出來的模型,在未見過的資料預測出的結果與實際結果的絕對差值,並將所有結果取平均。例如mae=0.5 (千美元) 則表示預測結果與實際金額差了500美元的誤差,所以mae越小表示預測越精準。

監督學習 - validation_data

留出驗證資料 取前100組做驗證資料
val_data = train_data[:100]
partial_train_data = train_data[100:]

val_targets = train_targets[:100]
partial_train_targets = train_targets[100:]

丟進fit學習,並將學習過程繪製出來
history = model.fit(partial_train_data,
                    partial_train_targets,
                    validation_data=(val_data, val_targets),
                    epochs=200,
                    batch_size=1, verbose=0)

import matplotlib.pyplot as plt

loss = history.history['loss']
val_loss = history.history['val_loss']

epochs = range(1, len(loss) + 1)

plt.plot(epochs, loss, 'bo', label='Training loss')
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('Training and validation loss')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()

plt.show()
發現前10個點誤差太大,將前10個點略過,不要畫出。修改成以下:
loss = history.history['loss'][10:]
val_loss = history.history['val_loss'][10:]
mae_data = history.history['mean_absolute_error'][10:]
val_mae_data = history.history['val_mean_absolute_error'][10:]

plt.plot(epochs, mae_data, 'bo', label='mean_absolute_error')
plt.plot(epochs, val_mae_data, 'b', label='val_mean_absolute_error')
plt.title('Training and validation mean_absolute_error')
plt.xlabel('Epochs')
plt.ylabel('MAE')
plt.legend()

plt.show()
可以從圖看出,因為訓練資料太少,導致模型在新數據上表現不佳
>>>test_targets.mean()
23.07843137254902
>>>train_targets.mean()
22.395049504950492
可以從targets資料來看,平均房價價格大概在22~23(千美元),而測試絕對誤差竟然在2~2.5(千美元),誤差高達10%,以A.I.應用來看,這是不及格的...所以要利用以下方法,對於較少的數據,做更多的訓練。

K-Fold訓練驗證

將訓練資料拆成K等分,然後再將訓練資料監督學習validation_data分成K次驗證,validation_data每次在不同區域,然後將K次驗證得到的MAE再取平均值。
見書內文解釋:
為了在調節網絡參數(比如訓練的輪數)的同時對網絡進行評估,你可以將數據劃分為訓練集和驗證集。
但由於數據點很少,驗證集會非常小(比如大約 100 個樣本)。因此,驗證分數可能會有很大波動,
這取決於你所選擇的驗證集和訓練集。也就是說,驗證集的劃分方式可能會造成驗證分數上有很大的方差,
這樣就無法對模型進行可靠的評估。
在這種情況下,最佳做法是使用K折交叉驗證(見圖)。這種方法將可用數據劃分為K 個分區(K 通常取4 或5),
實例化K 個相同的模型,將每個模型在K-1 個分區上訓練,並在剩下的一個分區上進行評估。
模型的驗證分數等於 K 個驗證分數的平均值。這種方法的代碼實現很簡單。

這邊因為會用到K次model,將它寫成函數,之前某個insite文章作者說過,一段程式會用到3次以上,就把它寫成函式
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(1))
    model.compile(optimizer='rmsprop', loss='mse', metrics=['mae'])
    return model

import numpy as np

k = 4
num_val_samples = len(train_data) // k
num_epochs = 100
all_scores = []
將train_data 分成K等份,這邊K取4。
每折迭代100次
將每折訓練驗證的MAE存到all_scores裡

for i in range(k):
    print('processing fold #', i)
    val_data = train_data[i * num : (i + 1) * num]
    val_targets = train_targets[i * num : (i + 1) * num]

    partial_train_data = np.concatenate(
        [train_data[:i * num],
         train_data[(i + 1) * num:]],
        axis=0)
    partial_train_targets = np.concatenate(
        [train_targets[:i * num],
         train_targets[(i + 1) * num:]],
        axis=0)

最後,每折裡面都丟進fit訓練&驗證(迭代num_epochs次),會得到一個MAE,將其丟進all_scores,迭代K次後,all_scores會有K個data。verbose:日誌顯示模式。 0 = 安靜模式, 1 = 進度條, 2= 每輪一行。
    model = build_model()
    model.fit(partial_train_data, partial_train_targets,
              epochs=num_epochs, batch_size=1, verbose=0)
    val_mse, val_mae = model.evaluate(val_data, val_targets, verbose=0)
    all_scores.append(val_mae)

print(all_scores)

得到以下結果,
[2.0645172407131382, 2.153722616705564, 2.9730089938286506, 2.4351987738420466]
平均為2.40661190627235

修改一下,將驗證結果全部記錄在all_mae_histories = []
num_epochs = 500
import numpy as np

k = 4
num_val_samples = len(train_data) // k
num_epochs = 500
all_mae_histories = []
all_scores = []
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,
              epochs=num_epochs, batch_size=1, verbose=2)
    mae_history = history.history['mean_absolute_error']
    all_mae_histories.append(mae_history)

將收集K次的數據做平均,用np.mean可以將每折迭代做平均
average_mae_history = np.mean(all_mae_histories,axis=0)
#average_mae_history = [np.mean([x[i] for x in all_mae_histories]) for i in range(num_epochs)]
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()
一樣前10個data差據太大看不出變化,略過前10個data。
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()
可以看到在大約Epochs=80時,所得到的MAE最低,故最後調整訓練模型。
完整程式碼:
from keras.datasets import boston_housing

(train_data, train_targets), (test_data, test_targets) = boston_housing.load_data()

mean = train_data.mean(axis=0)
train_data -= mean
std = train_data.std(axis=0)
train_data /= std

test_data -= mean
test_data /= std


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(1))
    model.compile(optimizer='rmsprop', loss='mse', metrics=['mae'])
    return model

model = build_model()
model.fit(train_data, train_targets,
              epochs=80 , batch_size=1, verbose=2)

final_test_data = model.evaluate(test_data, test_targets, verbose=0)
print(final_test_data)

[11.513197506175322, 2.2164718123043285]
最後MAE為2.2,在這樣少的data中,這預測平均絕對誤差已經是極限了。

沒有留言:

張貼留言