官术网_书友最值得收藏!

1.3 基于Keras實現房價預測

前面我們使用scikit-learn機器學習庫實現了房價預測。本節將使用神經網絡模型來實現房價預測,使用的是TensorFlow下的Keras的API代碼。我們將使用Keras創建模型、訓練模型、預測和對比圖預覽。

Keras封裝了一套高級的API用于構建深度學習模型,常用于快速實現原型設計和高級搜索。Keras對開發者的友好性、模塊化、可組合性和易于擴展的關鍵特征,使其成為目前使用較為廣泛的深度學習開源框架,它的后端之一就是TensorFlow。Keras在2015年12月份就將其以TensorFlow為后端的部分API融合到了TensorFlow框架中,所以目前我們可以通過tf.keras或者獨立的Keras庫來使用它。本書后面的章節會大量使用TensorFlow和Keras來構建深度學習模型。

1.3.1 數據準備

我們通過TensorFlow提供的Keras接口下的datasets模塊來加載數據集。數據集由卡內基梅隆大學維護,是波士頓近郊房價數據。讀者也可以通過前言中的鏈接去查看后續更新的數據集。在tf.keras.datasets下加載的數據集和我們在鏈接中看到的數據集的格式是一樣的,只是年份可能不同。加載數據集的代碼如下。

  import tensorflow as tf
  # 從TensorFlow導入Keras模塊
  from tensorflow import keras
  import numpy as np
  # 加載波士頓房價數據集
  (train_data, train_labels), (test_data, test_labels) = \
  keras.datasets.boston_housing.load_data()
  # 清洗訓練集數據
  # np.random.random()表示返回在0.0到1.0之間指定個數的隨機浮點數
  # np.argsort()表示返回對數組進行排序的索引
  order = np.argsort(np.random.random(train_labels.shape))
  train_data = train_data[order]
  train_labels = train_labels[order]
  # 歸一化處理數據
  # 對不同的范圍和比例進行歸一化處理,并且每個元素都要減去均值后再除以標準差
  # 雖然模型在沒有特征歸一化時也可以得到收斂,但是這會讓訓練更加困難,
  # 而且會導致結果模型依賴于訓練數據
  mean = train_data.mean(axis=0)
  std = train_data.std(axis=0)
  train_data = (train_data - mean) / std
  test_data = (test_data - mean) / std
  print("train_data.shape: {}, train_labels.shape: {}."
      .format(train_data.shape, train_labels.shape)) 
  print("test_data.shape: {}, test_labels.shape: {}."
      .format(test_data.shape, test_labels.shape))

輸出如下。

  train_data.shape:(404,13),train_labels.shape:(404,).
  test_data.shape:(102,13),test_labels.shape:(102,).

1.3.2 創建神經網絡模型

在Keras中,我們創建神經網絡模型,就是使用Sequential類來創建Keras模型。本次創建的模型比較簡單,通過Dense類來創建神經網絡層。對于輸入層,層的深度是64個units,輸入層必須傳入input_shape參數,表示輸入數據的特征維度的大小。

激活函數(activation),我們指定的是修正線性單元(ReLU),它是神經網絡中最常用的激活函數。當輸入的值為正數時,導數不為0,返回它本身,這就允許在訓練模型時進行基于梯度的學習,也會使計算變得更快;當輸入的值為負數時,學習速度可能會變得很慢,甚至會使神經元直接失效,這是因為輸入的值是小于0的值,計算它的梯度也為0,從而使其權重無法得到更新,因此在傳播到下一個神經網絡層的時候,返回的值為0就沒有什么意義了。

然后我們再添加一個隱藏層,這一層不需要input_shape參數,因為在輸入層時已經指定了。我們仍然設置層的深度是64個units,激活函數是ReLu。

輸出層只有一個unit,代碼如下。

  # 定義創建模型函數
  def build_model():
    model = keras.Sequential([
      keras.layers.Dense(64, activation=tf.nn.relu,
                         input_shape=(train_data.shape[1],)),
      keras.layers.Dense(64, activation=tf.nn.relu),
      keras.layers.Dense(1)
    ])
    # 使用RMSProp(均方根傳播)優化器,它可以加速梯度下降,其中學習速度適用于每個參數
    optimizer = tf.train.RMSPropOptimizer(0.001)
    # mse(均方差)一般用于回歸問題的損失函數
    # mae(平均絕對誤差)一般用于回歸問題的測量/評估
    model.compile(loss='mse', optimizer=optimizer, metrics=['mae'])
    return model
  model = build_model()
  # 查看模型的架構
  model.summary()

輸出的模型架構如圖1.12所示。全部參數有5121個,模型中的每一層參數都可以在表格的Param列看到。每一層的輸出大小在Output Shape列中顯示,其中None表示是可變的batch size,它會在訓練模型或者模型預測時被自動填充上具體的值。

圖1.12 一個簡單的Keras模型架構圖

1.3.3 訓練網絡模型

訓練這個模型500次,并且將訓練精確度和驗證精確度記錄在history對象中,以便繪圖預覽。我們自定義一個回調對象類,重寫on_epoch_end()函數,在每次epoch結束時會調用該函數。

  # 自定義一個回調對象類,在每次epoch(代)結束時都會調用該函數
  class PrintDot(keras.callbacks.Callback):
    def on_epoch_end(self, epoch, logs):
        if epoch % 100 == 0: print('')
        print('.', end='')
  EPOCHS = 500
  # 訓練模型
  # 參數1:房屋特征數據
  # 參數2:房屋價格數據
  # 參數3:迭代次數
  # 參數4:驗證集分割比例,0.2表示20%的數據用于驗證,80%的數據用于訓練
  # 參數5:輸出打印日志信息,0表示不輸出打印日志信息
  # 參數6:回調對象,這里我們使用自定義的回調類PrintDot
  history = model.fit(train_data, train_labels, epochs=EPOCHS,
                    validation_split=0.2, verbose=0,
                    callbacks=[PrintDot()])

1.3.4 可視化模型的結果

通過history對象,我們可以讀取該模型訓練時的誤差數值,以便于觀察何時是最佳模型,何時應該停止訓練。

  import matplotlib.pyplot as plt
  # 繪制圖來顯示訓練時的accuracy和loss
  def plot_history(history):
    plt.figure()
    plt.xlabel('Epoch')
    plt.ylabel('Mean Abs Error [1000$]')
    plt.plot(history.epoch, np.array(history.history['mean_absolute_error']),
             label='Train Loss')
    plt.plot(history.epoch, np.array(history.history['val_mean_absolute_error']),
             label='Val loss')
    plt.legend()
    plt.ylim([0, 5])
    plt.show()
  plot_history(history)

輸出如圖1.13所示。

圖1.13 模型訓練時的平均絕對誤差表現圖

可以發現,大約在150到200次迭代時,訓練損失值就沒怎么降低了。所以,這里我們要用到一個降低過擬合技術:早期停止(Early Stopping)。它是指在指定的迭代次數內,如果依舊沒有損失降低、模型性能提升的話,就自動終止訓練。

我們重新構建和訓練該模型,在重新構建模型前,請先清除Keras的內存狀態。最簡單的辦法就是重新運行Jupyter Notebook;如果讀者使用的是終端,就重新啟動該腳本程序。

  # 重新構建模型
  model = build_model()
  # 設置早期停止,如果20次的迭代依舊沒有降低驗證損失,則自動停止訓練
  early_stop = keras.callbacks.EarlyStopping(monitor='val_loss', patience=20)
  # 重新訓練模型,此時的callbacks有兩個回調函數,所以我們使用數組的形式傳入它們
  history = model.fit(train_data, train_labels, epochs=EPOCHS,
                         validation_split=0.2, verbose=0,
                         callbacks=[early_stop, PrintDot()])
  # 打印輸出歷史記錄的曲線圖
  plot_history(history)

輸出如圖1.14所示。

圖1.14 訓練和驗證模型時的平均絕對誤差圖(使用早期停止技術)

1.3.5 評估和預測模型

接下來,我們來測試模型在測試集下的表現,這就需要對模型進行評估了。通過evaluate()函數,傳入測試房屋特征數據集,測試房屋價格數據,計算測試集的平均絕對誤差。

  [loss, mae] = model.evaluate(test_data, test_labels, verbose=0)
  print("Testing set Mean Abs Error: ${:7.2f}".format(mae * 1000))

輸出日志如下。

  Testing set Mean Abs Error:$2930.86

然后預測模型,通過predict()函數,傳入測試房屋特征數據集,返回預測房價;最后將返回的數據通過flatten()函數進行扁平化處理,以便于繪制散點圖。

  # 使用測試數據集預測模型
  test_predictions = model.predict(test_data).flatten()
  plt.scatter(test_labels, test_predictions)
  plt.xlabel('True Values [1000$]')
  plt.ylabel('Predictions [1000$]')
  plt.axis('equal')
  plt.xlim(plt.xlim())
  plt.ylim(plt.ylim())
  plt.plot([-100, 100], [-100, 100])
  plt.show()

輸出結果如圖1.15所示。

圖1.15 預測房價的回歸模型圖

1.3.6 預測可視化顯示

接下來,我們來看下將真實房價和預測房價進行對比,從而形成的價格差直方圖。我們先計算預測房價和真實房價的差價,代碼如下。

  error = test_predictions - test_labels
  plt.hist(error, bins=50)
  plt.xlabel("Prediction Error [1000$]")
  plt.ylabel("Count")
  plt.show()

輸出如圖1.16所示。

圖1.16 預測房價和真實房價的價格差直方圖

最后,我們通過真實房價和預測房價生成一張更直觀的圖。函數plotVersusFigure()在上面的代碼中已經定義過,這里就不再介紹。

  plotVersusFigure(test_labels,test_predictions)

輸出如圖1.17所示。

圖1.17 真實房價與預測房價的對比圖

通過以上分析可知,不管是用scikit-learn的機器學習庫來預測房價,還是使用Keras的神經網絡模型來預測房價,真實房價和預測房價總是有些誤差。所以我們能控制的就是在訓練神經網絡模型時,調整訓練的超參數、迭代次數、網絡層數和優化器等參數,以得到更好的、適用于該房屋數據的預測模型。這里的數據量比較小,如果數據量更大一些,模型效果會更好。

主站蜘蛛池模板: 舟曲县| 雷波县| 光山县| 衡南县| 湘西| 南京市| 浦北县| 承德县| 建德市| 老河口市| 夏河县| 峨眉山市| 民丰县| 黄龙县| 阿图什市| 吕梁市| 长丰县| 乐平市| 德安县| 承德县| 旬阳县| 美姑县| 襄城县| 德钦县| 垫江县| 鹰潭市| 达尔| 武定县| 云阳县| 嘉定区| 贡山| 山丹县| 阳东县| 福安市| 油尖旺区| 利川市| 新平| 兴宁市| 雷山县| 滨海县| 哈巴河县|