在 TensorFlow.org 上查看
|
在 Google Colab 中运行
|
在 GitHub 上查看源码
|
下载笔记本
|
在回归问题中,目标是预测连续值的输出,例如价格或概率。这与分类问题形成对比,在分类问题中,目标是从一系列类别中选择一个类别(例如,识别图片中包含的是苹果还是橘子)。
本教程使用经典的 Auto MPG 数据集,并演示如何构建模型来预测 20 世纪 70 年代末和 80 年代初汽车的燃油效率。为此,您将向模型提供该时期许多汽车的描述信息。这些描述包括气缸数、排量、马力和重量等属性。
本示例使用 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 机器学习存储库 获取。
获取数据
首先,使用 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']]
对使用不同尺度和范围的特征进行归一化是一种良好的实践。
之所以重要,原因之一是特征会与模型权重相乘。因此,输出的尺度和梯度的尺度都会受到输入尺度的影响。
尽管模型可能在没有特征归一化的情况下收敛,但归一化会使训练过程更加稳定。
Normalization 层
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 个“马力”值上运行未经训练的模型。输出结果不会很好,但请注意它具有预期的 (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 和单输入进行回归
创建一个仅以 'Horsepower' 为输入,并以 horsepower_normalizer(之前定义)作为归一化层的 DNN 模型
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.
在 TensorFlow.org 上查看
在 Google Colab 中运行
在 GitHub 上查看源码
下载笔记本