教程 | TensorFlow 2.0 教程 —— 機器學習基礎 —— 迴歸(2)

譯自:TensorFlow 官方教程

由於 TensorFlow 2.0 的迴歸教程使用了另一個數據集,因此保留之前版本中使用的波士頓住房數據集。

傳送門


在迴歸問題中,我們希望的預測輸出是連續值,例如價格或者概率。

這個教程使用經典的 Auto MPG 數據集(MPG,每加侖汽油可行駛英里數)並且建立模型來預測十九世紀七十年代末及八十年代初的汽車燃油效率。我們提供了那個時間段的汽車的描述,包括汽缸、排量、馬力和重量等屬性。

from __future__ import absolute_import, division, print_function, unicode_literals

import pathlib

import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns

import tensorflow as tf

from tensorflow import keras
from tensorflow.keras import layers

Auto MPG 數據集

獲取數據

首先下載數據集。

dataset_path = keras.utils.get_file("auto-mpg.data", "http://archive.ics.uci.edu/ml/machine-learning-databases/auto-mpg/auto-mpg.data")
dataset_path
Downloading data from http://archive.ics.uci.edu/ml/machine-learning-databases/auto-mpg/auto-mpg.data
32768/30286 [================================] - 0s 0us/step

'/root/.keras/datasets/auto-mpg.data'

使用 pandas 導入數據集。

column_names = ['MPG','Cylinders','Displacement','Horsepower','Weight',
                'Acceleration', 'Model Year', 'Origin']
raw_dataset = pd.read_csv(dataset_path, names=column_names,
                      na_values = "?", comment='\t',
                      sep=" ", skipinitialspace=True)

dataset = raw_dataset.copy()
dataset.tail()

在這裏插入圖片描述

清洗數據

數據集包含了一些未知的值。

dataset.isna().sum()
MPG             0
Cylinders       0
Displacement    0
Horsepower      6
Weight          0
Acceleration    0
Model Year      0
Origin          0
dtype: int64

爲使教程簡潔,直接丟棄包含這些值的行。

dataset = dataset.dropna()

“Origin” 列實際上是類別,而不是數值。因此將其轉換爲 one-hot 形式。

origin = dataset.pop('Origin')
dataset['USA'] = (origin == 1)*1.0
dataset['Europe'] = (origin == 2)*1.0
dataset['Japan'] = (origin == 3)*1.0
dataset.tail()

在這裏插入圖片描述

將數據劃分爲訓練集和測試集

我們將使用測試集評估我們的最終模型。

train_dataset = dataset.sample(frac=0.8,random_state=0)
test_dataset = dataset.drop(train_dataset.index)

檢查數據

快速查看一下訓練集中幾對列的聯合分佈。

sns.pairplot(train_dataset[["MPG", "Cylinders", "Displacement", "Weight"]], diag_kind="kde")

在這裏插入圖片描述
查看整體統計信息。

train_stats = train_dataset.describe()
train_stats.pop("MPG")
train_stats = train_stats.transpose()
train_stats

在這裏插入圖片描述

從標籤中分離特徵

標籤是我們將要訓練模型預測的值。

train_labels = train_dataset.pop('MPG')
test_labels = test_dataset.pop('MPG')

歸一化數據

從上面的統計數據中可以看出,每個特徵都在不同的範圍。

將不同尺度和範圍的特徵歸一化在大多數情況下都是重要的一步。雖然模型在不進行特徵歸一化的情況下可能會收斂,但這會增加訓練的難度,並使生成的模型依賴於輸入中使用的單元的選擇。

def norm(x):
  return (x - train_stats['mean']) / train_stats['std']
normed_train_data = norm(train_dataset)
normed_test_data = norm(test_dataset)

注意: 這裏用來歸一化輸入的統計值(均值和標準差)需要應用於任意送入模型的數據,並且要包含上面編碼的 one-hot 變量。


模型

建立模型

我們將使用 Sequential 建立由兩個密集連接隱藏層組成的模型,輸出層返回一個單個且連續的值。建立模型的步驟包裝在 build_model 函數裏,因爲我們後面會創建另一個模型。

def build_model():
  model = keras.Sequential([
    layers.Dense(64, activation='relu', input_shape=[len(train_dataset.keys())]),
    layers.Dense(64, activation='relu'),
    layers.Dense(1)
  ])

  optimizer = tf.keras.optimizers.RMSprop(0.001)

  model.compile(loss='mse',
                optimizer=optimizer,
                metrics=['mae', 'mse'])
  return model
model = build_model()

檢查模型

使用 .summary 方法可以打印模型的描述信息。

model.summary()
Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense (Dense)                (None, 64)                640       
_________________________________________________________________
dense_1 (Dense)              (None, 64)                4160      
_________________________________________________________________
dense_2 (Dense)              (None, 1)                 65        
=================================================================
Total params: 4,865
Trainable params: 4,865
Non-trainable params: 0
_________________________________________________________________

現在試試這個模型。從訓練集中選出 10 個樣本,並且調用 model.predict

example_batch = normed_train_data[:10]
example_result = model.predict(example_batch)
example_result
array([[ 0.3297699 ],
       [ 0.25655937],
       [-0.12460149],
       [ 0.32495883],
       [ 0.50459725],
       [ 0.10887371],
       [ 0.57305855],
       [ 0.57637435],
       [ 0.12094647],
       [ 0.6864784 ]], dtype=float32)

訓練模型

訓練模型 1000 個週期,並在 history 對象中記錄訓練和驗證準確率。

# 在每個週期完成時打印一個點來顯示訓練進度
class PrintDot(keras.callbacks.Callback):
  def on_epoch_end(self, epoch, logs):
    if epoch % 100 == 0: print('')
    print('.', end='')

EPOCHS = 1000

history = model.fit(
  normed_train_data, train_labels,
  epochs=EPOCHS, validation_split = 0.2, verbose=0,
  callbacks=[PrintDot()])

使用保存在 history 對象中的統計數據可視化模型的訓練進度。

hist = pd.DataFrame(history.history)
hist['epoch'] = history.epoch
hist.tail()

在這裏插入圖片描述

def plot_history(history):
  hist = pd.DataFrame(history.history)
  hist['epoch'] = history.epoch

  plt.figure()
  plt.xlabel('Epoch')
  plt.ylabel('Mean Abs Error [MPG]')
  plt.plot(hist['epoch'], hist['mae'],
           label='Train Error')
  plt.plot(hist['epoch'], hist['val_mae'],
           label = 'Val Error')
  plt.ylim([0,5])
  plt.legend()

  plt.figure()
  plt.xlabel('Epoch')
  plt.ylabel('Mean Square Error [$MPG^2$]')
  plt.plot(hist['epoch'], hist['mse'],
           label='Train Error')
  plt.plot(hist['epoch'], hist['val_mse'],
           label = 'Val Error')
  plt.ylim([0,20])
  plt.legend()
  plt.show()


plot_history(history)

在這裏插入圖片描述
在這裏插入圖片描述
這張圖顯示,在大約 100 個週期之後,驗證誤差幾乎沒有改善,甚至有所上升。讓我們更新 model.fit 使得模型在驗證分數沒有改善時自動停止訓練。我們將使用 EarlyStopping 回調,如果一段週期中沒有顯示出性能改善,則自動停止訓練。

這裏有更多關於 回調 的信息。

model = build_model()

# patience 參數是檢查性能改善的週期數
early_stop = keras.callbacks.EarlyStopping(monitor='val_loss', patience=10)

history = model.fit(normed_train_data, train_labels, epochs=EPOCHS,
                    validation_split = 0.2, verbose=0, callbacks=[early_stop, PrintDot()])

plot_history(history)

在這裏插入圖片描述
在這裏插入圖片描述
從圖中可以看出,在驗證集上,平均誤差通常在 +/- 2 MPG 左右。

讓我們使用測試集來看看模型的泛化能力如何。

loss, mae, mse = model.evaluate(normed_test_data, test_labels, verbose=0)

print("Testing set Mean Abs Error: {:5.2f} MPG".format(mae))
Testing set Mean Abs Error:  2.09 MPG

做出預測

最後,使用測試集中的數據預測 MPG 值。

test_predictions = model.predict(normed_test_data).flatten()

plt.scatter(test_labels, test_predictions)
plt.xlabel('True Values [MPG]')
plt.ylabel('Predictions [MPG]')
plt.axis('equal')
plt.axis('square')
plt.xlim([0,plt.xlim()[1]])
plt.ylim([0,plt.ylim()[1]])
_ = plt.plot([-100, 100], [-100, 100])

在這裏插入圖片描述
看起來我們模型的預測很合理。再看一下誤差的分佈。

error = test_predictions - test_labels
plt.hist(error, bins = 25)
plt.xlabel("Prediction Error [MPG]")
_ = plt.ylabel("Count")

在這裏插入圖片描述
它不是高斯分佈的,但我們可以預料到因爲樣本的數量很小。


結論

本教程介紹了一些處理迴歸問題的技巧:

  • 均方誤差(MSE)是迴歸問題中常用的損失函數(不同於分類問題中的損失函數)
  • 類似地,評估指標也不同於分類問題。常用的迴歸指標是平均絕對誤差(MAE)。
  • 當數值輸入數據特徵值有不同的範圍時,應單獨縮放每個特徵到相同範圍。
  • 如果訓練數據不多,一個技巧就是使用更少隱藏層的小網絡來避免過擬合。
  • 提前終止是預防過擬合的一個很有效的技巧。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章