![]() |
![]() |
![]() |
![]() |
在迴歸問題中,目標是預測連續值的輸出,例如價格或機率。這與分類問題形成對比,分類問題的目標是從類別清單中選取類別 (例如,圖片包含蘋果或橘子,辨識圖片中的水果)。
本教學課程使用經典的 Auto MPG 資料集,並示範如何建構模型來預測 1970 年代末期和 1980 年代初期汽車的燃油效率。若要執行這項操作,您將提供模型當時許多汽車的描述。此描述包括汽缸數、排氣量、馬力和重量等屬性。
本範例使用 Keras API。(如需瞭解詳情,請造訪 Keras 教學課程和指南。)
# Use seaborn for pairplot.
pip install -q seaborn
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns
# Make NumPy printouts easier to read.
np.set_printoptions(precision=3, suppress=True)
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
print(tf.__version__)
Auto MPG 資料集
資料集可從 UCI Machine Learning Repository 取得。
取得資料
首先使用 pandas 下載並匯入資料集
url = 'http://archive.ics.uci.edu/ml/machine-learning-databases/auto-mpg/auto-mpg.data'
column_names = ['MPG', 'Cylinders', 'Displacement', 'Horsepower', 'Weight',
'Acceleration', 'Model Year', 'Origin']
raw_dataset = pd.read_csv(url, names=column_names,
na_values='?', comment='\t',
sep=' ', skipinitialspace=True)
dataset = raw_dataset.copy()
dataset.tail()
清理資料
資料集包含一些不明值
dataset.isna().sum()
捨棄這些列以保持本入門教學課程的簡潔性
dataset = dataset.dropna()
"Origin"
欄是類別資料,而非數值資料。因此,下一步是使用 pd.get_dummies 對欄中的值進行 one-hot 編碼。
dataset['Origin'] = dataset['Origin'].map({1: 'USA', 2: 'Europe', 3: 'Japan'})
dataset = pd.get_dummies(dataset, columns=['Origin'], prefix='', prefix_sep='')
dataset.tail()
將資料分割為訓練集和測試集
現在,將資料集分割為訓練集和測試集。您將在模型的最終評估中使用測試集。
train_dataset = dataset.sample(frac=0.8, random_state=0)
test_dataset = dataset.drop(train_dataset.index)
檢查資料
檢閱訓練集中幾對欄的聯合分佈。
頂端列表示燃油效率 (MPG) 是所有其他參數的函數。其他列表示它們是彼此的函數。
sns.pairplot(train_dataset[['MPG', 'Cylinders', 'Displacement', 'Weight']], diag_kind='kde')
我們也來檢查整體統計資料。請注意每個特徵涵蓋的範圍差異很大
train_dataset.describe().transpose()
從標籤分割特徵
從特徵中分離目標值 (「標籤」)。此標籤是您將訓練模型預測的值。
train_features = train_dataset.copy()
test_features = test_dataset.copy()
train_labels = train_features.pop('MPG')
test_labels = test_features.pop('MPG')
正規化
在統計資料表中,很容易看出每個特徵的範圍差異有多大
train_dataset.describe().transpose()[['mean', 'std']]
最好將使用不同比例和範圍的特徵正規化。
這項作業之所以重要,原因之一是特徵會乘以模型權重。因此,輸出的比例和梯度的比例會受到輸入比例的影響。
雖然模型可能在沒有特徵正規化的情況下收斂,但正規化可讓訓練更穩定。
正規化層
tf.keras.layers.Normalization
是將特徵正規化新增至模型的簡潔方法。
第一步是建立層
normalizer = tf.keras.layers.Normalization(axis=-1)
接著,呼叫 Normalization.adapt
,將預先處理層的狀態擬合到資料
normalizer.adapt(np.array(train_features))
計算平均值和變異數,並將其儲存在層中
print(normalizer.mean.numpy())
呼叫層時,它會傳回輸入資料,且每個特徵都會獨立正規化
first = np.array(train_features[:1])
with np.printoptions(precision=2, suppress=True):
print('First example:', first)
print()
print('Normalized:', normalizer(first).numpy())
線性迴歸
在建構深度神經網路模型之前,先從使用單一變數和多個變數的線性迴歸開始。
單一變數線性迴歸
從單一變數線性迴歸開始,以根據 'Horsepower'
預測 'MPG'
。
使用 tf.keras
訓練模型通常從定義模型架構開始。使用 tf.keras.Sequential
模型,此模型代表一系列步驟。
您的單一變數線性迴歸模型中有兩個步驟
- 使用
tf.keras.layers.Normalization
預先處理層正規化'Horsepower'
輸入特徵。 - 套用線性轉換 (\(y = mx+b\)),使用線性層 (
tf.keras.layers.Dense
) 產生 1 個輸出。
輸入數量可以透過 input_shape
引數設定,也可以在模型首次執行時自動設定。
首先,建立由 'Horsepower'
特徵組成的 NumPy 陣列。然後,例項化 tf.keras.layers.Normalization
,並將其狀態擬合到 horsepower
資料
horsepower = np.array(train_features['Horsepower'])
horsepower_normalizer = layers.Normalization(input_shape=[1,], axis=None)
horsepower_normalizer.adapt(horsepower)
建構 Keras Sequential 模型
horsepower_model = tf.keras.Sequential([
horsepower_normalizer,
layers.Dense(units=1)
])
horsepower_model.summary()
此模型將根據 'Horsepower'
預測 'MPG'
。
在最初 10 個 'Horsepower' 值上執行未訓練的模型。輸出結果不會太好,但請注意,其具有預期的 (10, 1)
形狀
horsepower_model.predict(horsepower[:10])
模型建構完成後,請使用 Keras Model.compile
方法設定訓練程序。編譯最重要的引數是 loss
和 optimizer
,因為這些引數定義了將最佳化的內容 (mean_absolute_error
) 和方式 (使用 tf.keras.optimizers.Adam
)。
horsepower_model.compile(
optimizer=tf.keras.optimizers.Adam(learning_rate=0.1),
loss='mean_absolute_error')
使用 Keras Model.fit
執行 100 個週期的訓練
%%time
history = horsepower_model.fit(
train_features['Horsepower'],
train_labels,
epochs=100,
# Suppress logging.
verbose=0,
# Calculate validation results on 20% of the training data.
validation_split = 0.2)
使用儲存在 history
物件中的統計資料,視覺化模型的訓練進度
hist = pd.DataFrame(history.history)
hist['epoch'] = history.epoch
hist.tail()
def plot_loss(history):
plt.plot(history.history['loss'], label='loss')
plt.plot(history.history['val_loss'], label='val_loss')
plt.ylim([0, 10])
plt.xlabel('Epoch')
plt.ylabel('Error [MPG]')
plt.legend()
plt.grid(True)
plot_loss(history)
收集測試集上的結果以供稍後使用
test_results = {}
test_results['horsepower_model'] = horsepower_model.evaluate(
test_features['Horsepower'],
test_labels, verbose=0)
由於這是單一變數迴歸,因此很容易將模型的預測視為輸入的函數
x = tf.linspace(0.0, 250, 251)
y = horsepower_model.predict(x)
def plot_horsepower(x, y):
plt.scatter(train_features['Horsepower'], train_labels, label='Data')
plt.plot(x, y, color='k', label='Predictions')
plt.xlabel('Horsepower')
plt.ylabel('MPG')
plt.legend()
plot_horsepower(x, y)
多個輸入的線性迴歸
您可以使用幾乎相同的設定,根據多個輸入進行預測。此模型仍然執行相同的 \(y = mx+b\) 運算,只是 \(m\) 是矩陣,而 \(x\) 是向量。
再次建立雙步驟 Keras Sequential 模型,第一個步驟是您先前定義並調整為整個資料集的 normalizer
(tf.keras.layers.Normalization(axis=-1)
)
linear_model = tf.keras.Sequential([
normalizer,
layers.Dense(units=1)
])
當您在輸入批次上呼叫 Model.predict
時,它會為每個範例產生 units=1
個輸出
linear_model.predict(train_features[:10])
當您呼叫模型時,其權重矩陣將會建構,請檢查 kernel
權重 (即 \(y=mx+b\) 中的 \(m\)) 是否具有 (9, 1)
的形狀
linear_model.layers[1].kernel
使用 Keras Model.compile
設定模型,並使用 Model.fit
訓練 100 個週期
linear_model.compile(
optimizer=tf.keras.optimizers.Adam(learning_rate=0.1),
loss='mean_absolute_error')
%%time
history = linear_model.fit(
train_features,
train_labels,
epochs=100,
# Suppress logging.
verbose=0,
# Calculate validation results on 20% of the training data.
validation_split = 0.2)
在此迴歸模型中使用所有輸入,比具有單一輸入的 horsepower_model
獲得更低的訓練和驗證錯誤
plot_loss(history)
收集測試集上的結果以供稍後使用
test_results['linear_model'] = linear_model.evaluate(
test_features, test_labels, verbose=0)
深度神經網路 (DNN) 迴歸
在上一節中,您實作了適用於單一輸入和多個輸入的兩個線性模型。
在此,您將實作單一輸入和多個輸入 DNN 模型。
程式碼基本上相同,只是擴充了模型以包含一些「隱藏」的非線性層。「隱藏」在此僅表示未直接連接到輸入或輸出。
這些模型將包含比線性模型更多的層
- 與之前相同的正規化層 (單一輸入模型使用
horsepower_normalizer
,多個輸入模型使用normalizer
)。 - 兩個隱藏的非線性
Dense
層,具有 ReLU (relu
) 啟動函數非線性。 - 線性
Dense
單一輸出層。
這兩個模型都將使用相同的訓練程序,因此 compile
方法包含在下方的 build_and_compile_model
函數中。
def build_and_compile_model(norm):
model = keras.Sequential([
norm,
layers.Dense(64, activation='relu'),
layers.Dense(64, activation='relu'),
layers.Dense(1)
])
model.compile(loss='mean_absolute_error',
optimizer=tf.keras.optimizers.Adam(0.001))
return model
使用 DNN 和單一輸入進行迴歸
建立 DNN 模型,僅使用 'Horsepower'
作為輸入,並使用 horsepower_normalizer
(先前定義) 作為正規化層
dnn_horsepower_model = build_and_compile_model(horsepower_normalizer)
此模型具有比線性模型更多的可訓練參數
dnn_horsepower_model.summary()
使用 Keras Model.fit
訓練模型
%%time
history = dnn_horsepower_model.fit(
train_features['Horsepower'],
train_labels,
validation_split=0.2,
verbose=0, epochs=100)
此模型的效能比線性單一輸入 horsepower_model
略好
plot_loss(history)
如果您將預測繪製為 'Horsepower'
的函數,您應該會注意到此模型如何利用隱藏層提供的非線性
x = tf.linspace(0.0, 250, 251)
y = dnn_horsepower_model.predict(x)
plot_horsepower(x, y)
收集測試集上的結果以供稍後使用
test_results['dnn_horsepower_model'] = dnn_horsepower_model.evaluate(
test_features['Horsepower'], test_labels,
verbose=0)
使用 DNN 和多個輸入進行迴歸
使用所有輸入重複先前的程序。模型的效能在驗證資料集上略有提升。
dnn_model = build_and_compile_model(normalizer)
dnn_model.summary()
%%time
history = dnn_model.fit(
train_features,
train_labels,
validation_split=0.2,
verbose=0, epochs=100)
plot_loss(history)
收集測試集上的結果
test_results['dnn_model'] = dnn_model.evaluate(test_features, test_labels, verbose=0)
效能
由於所有模型都已訓練完成,您可以檢閱其測試集效能
pd.DataFrame(test_results, index=['Mean absolute error [MPG]']).T
這些結果與訓練期間觀察到的驗證錯誤相符。
進行預測
您現在可以使用 Keras Model.predict
在測試集上使用 dnn_model
進行預測,並檢閱損失
test_predictions = dnn_model.predict(test_features).flatten()
a = plt.axes(aspect='equal')
plt.scatter(test_labels, test_predictions)
plt.xlabel('True Values [MPG]')
plt.ylabel('Predictions [MPG]')
lims = [0, 50]
plt.xlim(lims)
plt.ylim(lims)
_ = plt.plot(lims, lims)
模型似乎預測得相當好。
現在,檢查錯誤分佈
error = test_predictions - test_labels
plt.hist(error, bins=25)
plt.xlabel('Prediction Error [MPG]')
_ = plt.ylabel('Count')
如果您對模型感到滿意,請使用 Model.save
儲存模型以供日後使用
dnn_model.save('dnn_model.keras')
如果您重新載入模型,它會提供相同的輸出
reloaded = tf.keras.models.load_model('dnn_model.keras')
test_results['reloaded'] = reloaded.evaluate(
test_features, test_labels, verbose=0)
pd.DataFrame(test_results, index=['Mean absolute error [MPG]']).T
結論
本筆記本介紹了一些處理迴歸問題的技巧。以下是一些可能有幫助的訣竅
- 均方誤差 (MSE) (
tf.keras.losses.MeanSquaredError
) 和平均絕對誤差 (MAE) (tf.keras.losses.MeanAbsoluteError
) 是迴歸問題常用的損失函數。MAE 對離群值較不敏感。分類問題使用不同的損失函數。 - 同樣地,迴歸使用的評估指標與分類不同。
- 當數值輸入資料特徵的值具有不同範圍時,每個特徵都應獨立縮放到相同範圍。
- 過度擬合是 DNN 模型的常見問題,雖然這在本教學課程中不是問題。請造訪過度擬合與擬合不足教學課程以取得更多相關協助。
# MIT License
#
# Copyright (c) 2017 François Chollet
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.