Tensorflow2.*教程之使用Auto MPG数据集构建回归模型预测燃油率(4)

 学习和理解本文章的要求:

  • 有数理统计的知识
  • 有矩阵论的知识
  • 有图像分析和处理知识
  • 有Python编程基础
  • 有机器学习和深度学习理论支撑

本教程适合有机器学习,深度学习基础的人员学习。如果没有上面储备知识,可能学起来会吃力,有些概念可能听起来很玄学。

Keras 机器学习基础知识文章链接:

  1. 对服装图像进行分类
  2. 使用Tensorflow Hub对未处理的电影评论数据集IMDB进行分类
  3. Keras 机器学习基础知识之对预处理的电影评论文本分类

前几天文章用tensorflow2.x的API进行了分类问题的解决。今天来一篇使用tensorflow2.x解决回归问题。回归 (regression) 问题的目的是预测出如价格,股票或概率这样连续值的输出。而分类(classification) 问题,目的是从一系列的分类出选择出一个分类 (如,给出一张包含苹果或橘子的图片,识别出图片中是哪种水果),是离散值。

数据集介绍

本文使用 Auto MPG 数据集,文章基于此数据集构建了一个用来预测70年代末到80年代初汽车燃油效率的模型。数据集提供了汽车相关描述,包含:气缸数,排量,马力以及重量等(这些统称为属性)。

引入使用到API库

import tensorflow as tf

import matplotlib.pyplot as plt

import pathlib

import seaborn as sns  # 使用 seaborn 绘制矩阵图 (pairplot)

import pandas as pd

from tensorflow import keras

from tensorflow.keras import layers

print(tf.__version__)

数据集获取

1.下载数据集AUTO_MPG,数据文件为CSV格式文件

# 返回文件下载完之后保存路径
data_path = keras.utils.get_file("auto_mpg","http://archive.ics.uci.edu/ml/machine-learning-databases/auto-mpg/auto-mpg.data")

2.使用pandas解析下载好的数据文件

# 各个属性的名称
column_names = ['MPG','Cylinders','Displacement','Horsepower','Weight',
                'Acceleration', 'Model Year', 'Origin']
'''
sep: 指定分割符,默认是’,’C引擎不能自动检测分隔符,但Python解析引擎可以
names: 指定列名,如果文件中不包含header的行,应该显性表示header=None
na_values: 默认None NaN包含哪些情况,默认情况下, ‘#N/A’, ‘#N/A N/A’, ‘#NA’, ‘-1.#IND’, ‘-1.#QNAN’, ‘-NaN’, ‘-nan’, ‘1.#IND’, ‘1.#QNAN’, ‘N/A’, ‘NA’, ‘NULL’, ‘NaN’, ‘n/a’, ‘nan’, ‘null’. 都表现为NAN
skipinitialspace: 忽略分隔符后的空格,默认false
标识着多余的行不被解析。如果该字符出现在行首,这一行将被全部忽略。这个参数只能是一个字符,空行(就像skip_blank_lines=True)注释行被header和skiprows忽略一样。例如如果指定comment='#' 解析‘#empty\na,b,c\n1,2,3’ 以header=0 那么返回结果将是以’a,b,c'作为header。

'''
raw_dataset = pd.read_csv(data_path,name=column_names,na_values="?",comment="\t",sep=" ",skipinitialspace=True)

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

       MPG   Cylinders  Displacement  ...  Acceleration  Model Year  Origin
393  27.0          4         140.0  ...         15.6          82       1
394  44.0          4          97.0  ...          24.6          82       2
395  32.0          4         135.0  ...          11.6          82       1
396  28.0          4         120.0  ...          18.6          82       1
397  31.0          4         119.0  ...          19.4          82       1 

数据清洗

对含有非数值型或者属性值为类别型的记录进行处理。

1.统计非数值型记录的个数

dataset.isna().sum()

MPG             0
Cylinders       0
Displacement    0
Horsepower      6
Weight          0
Acceleration    0
Model Year      0
Origin          0

为了保证这个初始示例的简单性,删除这些行。

dataset = dataset.dropna()

2. 对类别型属性进行one-hot编码

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

拆分训练数据集和测试数据集

将数据集拆分为训练数据集和测试数据集。最后将使用测试数据集对模型进行评估。

# pandas.DataFrame.sample 随机选取若干行
# frac:抽取行的比例,例如frac=0.8,就是抽取其中80%。
train_dataset = dataset.sample(frac=0.8,random_state=0)
# 删除DataFrame的行:
test_dataset = dataset.drop(train_dataset.index)

数据检查

快速查看训练集中几对列的联合分布。

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

Tensorflow2.*教程之使用Auto MPG数据集构建回归模型预测燃油率(4)_第1张图片

查看总体的数据统计:

train_stats = train_dataset.describe()
train_stats.pop("MPG")
train_stats = train_stats.transpose()
train_stats
  count mean std min 25% 50% 75% max
Cylinders 314.0 5.477707 1.699788 3.0 4.00 4.0 8.00 8.0
Displacement 314.0 195.318471 104.331589 68.0 105.50 151.0 265.75 455.0
Horsepower 314.0 104.869427 38.096214 46.0 76.25 94.5 128.00 225.0
Weight 314.0 2990.251592 843.898596 1649.0 2256.50 2822.5 3608.00 5140.0
Acceleration 314.0 15.559236 2.789230 8.0 13.80 15.5 17.20 24.8
Model Year 314.0 75.898089 3.675642 70.0 73.00 76.0 79.00 82.0
USA 314.0 0.624204 0.485101 0.0 0.00 1.0 1.00 1.0
Europe 314.0 0.178344 0.383413 0.0 0.00 0.0 0.00 1.0
Japan 314.0 0.197452 0.398712 0.0 0.00 0.0 0.00 1.0

将特征值从目标值或者"标签"中分离。 这个标签是你使用训练模型进行预测的值。

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

数据归一化

再次上面的 train_stats 部分,并注意每个特征的范围有什么不同。

使用不同的尺度和范围对特征归一化是好的实践。尽管模型可能 在没有特征归一化的情况下收敛,它会使得模型训练更加复杂,并会造成生成的模型依赖输入所使用的单位选择。

注意:尽管我们仅仅从训练集中有意生成这些统计数据,但是这些统计信息也会用于归一化的测试数据集。我们需要这样做,将测试数据集放入到与已经训练过的模型相同的分布中。

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

构建模型

我们使用一个“顺序”模型,其中包含两个紧密相连的隐藏层,以及返回单个连续值得输出层。模型的构建步骤包含于一个名叫 '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()

现在试用下这个模型。从训练数据中批量获取‘10’条例子并对这些例子调用 model.predict 。

example_batch = normed_train_data[:10]
example_result = model.predict(example_batch)
example_result

 训练模型

对模型进行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()
loss mae mse val_loss val_mae val_mse epoch
995 2.377308 0.972564 2.377308 9.812141 2.277914 9.812140 995
996 2.089346 0.881897 2.089346 11.054218 2.500603 11.054217 996
997 2.168902 0.915549 2.168902 9.853918 2.314816 9.853918 997
998 2.340033 0.944049 2.340033 10.559951 2.421101 10.559950 998
999 2.254203 0.926744 2.254203 10.530814 2.451076 10.530814 999
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)

 Tensorflow2.*教程之使用Auto MPG数据集构建回归模型预测燃油率(4)_第2张图片

Tensorflow2.*教程之使用Auto MPG数据集构建回归模型预测燃油率(4)_第3张图片

该图表显示在约100个 epochs 之后误差非但没有改进,反而出现恶化。 让我们更新 model.fit 调用,当验证值没有提高上是自动停止训练。 我们将使用一个 EarlyStopping callback 来测试每个 epoch 的训练条件。如果经过一定数量的 epochs 后没有改进,则自动停止训练。

model = build_model()

# patience 值用来检查改进 epochs 的数量
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)

 Tensorflow2.*教程之使用Auto MPG数据集构建回归模型预测燃油率(4)_第4张图片

Tensorflow2.*教程之使用Auto MPG数据集构建回归模型预测燃油率(4)_第5张图片

模型评估 

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

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

绘制图像

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

Tensorflow2.*教程之使用Auto MPG数据集构建回归模型预测燃油率(4)_第6张图片

这看起来我们的模型预测得相当好。我们来看下误差分布。

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

Tensorflow2.*教程之使用Auto MPG数据集构建回归模型预测燃油率(4)_第7张图片

 总结:

  • 均方误差(MSE)是用于回归问题的常见损失函数(分类问题中使用不同的损失函数)。
  • 类似的,用于回归的评估指标与分类不同。 常见的回归指标是平均绝对误差(MAE)。
  • 当数字输入数据特征的值存在不同范围时,每个特征应独立缩放到相同范围。
  • 如果训练数据不多,一种方法是选择隐藏层较少的小网络,以避免过度拟合。
  • 早期停止是一种防止过度拟合的有效技术。

你可能感兴趣的:(Tensorflow2.0,Keras,机器学习基础知识,人工智能,机器学习,深度学习)