在回归问题中,我们的目标是预测连续值的输出,如价格或概率。 将此与分类问题进行对比,分类的目标是从类列表中选择一个类(例如,图片包含苹果或橙色,识别图片中的哪个水果)。
本章节采用了经典的Auto MPG 数据集,并建立了一个模型来预测20世纪70年代末和80年代初汽车的燃油效率。为此,我们将为该模型提供该时段内许多汽车的描述,此描述包括以下属性:气缸,排量,马力和重量。
此实例使用tf.keras API,有关信息信息,请参阅Keras指南。
1# 使用seaborn进行pairplot数据可视化,安装命令
2pip install seaborn
x1from __future__ import absolute_import, division, print_function, unicode_literals
2
3import pathlib
4
5import matplotlib.pyplot as plt
6import pandas as pd
7import seaborn as sns
8
9# tensorflow2 安装命令 pip install tensorflow==2.0.0-alpha0
10import tensorflow as tf
11
12from tensorflow import keras
13from tensorflow.keras import layers
14
15print(tf.__version__)
该数据集可从UCI机器学习库获得。
首先下载数据集:
xxxxxxxxxx
21dataset_path = keras.utils.get_file("auto-mpg.data", "http://archive.ics.uci.edu/ml/machine-learning-databases/auto-mpg/auto-mpg.data")
2dataset_path
用pandas导入数据
xxxxxxxxxx
81column_names = ['MPG','Cylinders','Displacement','Horsepower','Weight',
2 'Acceleration', 'Model Year', 'Origin']
3raw_dataset = pd.read_csv(dataset_path, names=column_names,
4 na_values = "?", comment='\t',
5 sep=" ", skipinitialspace=True)
6
7dataset = raw_dataset.copy()
8dataset.tail()
MPG | Cylinders | Displacement | Horsepower | Weight | Acceleration | Model Year | Origin | |
---|---|---|---|---|---|---|---|---|
393 | 27.0 | 4 | 140.0 | 86.0 | 2790.0 | 15.6 | 82 | 1 |
394 | 44.0 | 4 | 97.0 | 52.0 | 2130.0 | 24.6 | 82 | 2 |
395 | 32.0 | 4 | 135.0 | 84.0 | 2295.0 | 11.6 | 82 | 1 |
396 | 28.0 | 4 | 120.0 | 79.0 | 2625.0 | 18.6 | 82 | 1 |
397 | 31.0 | 4 | 119.0 | 82.0 | 2720.0 | 19.4 | 82 | 1 |
数据集包含一些未知值
xxxxxxxxxx
11dataset.isna().sum()
xxxxxxxxxx
91MPG 0
2Cylinders 0
3Displacement 0
4Horsepower 6
5Weight 0
6Acceleration 0
7Model Year 0
8Origin 0
9dtype: int64
这是一个入门教程,所以我们就简单地删除这些行。
xxxxxxxxxx
11dataset = dataset.dropna()
“Origin”这一列实际上是分类,而不是数字。 所以把它转换为独热编码:
xxxxxxxxxx
11origin = dataset.pop('Origin')
xxxxxxxxxx
41dataset['USA'] = (origin == 1)*1.0
2dataset['Europe'] = (origin == 2)*1.0
3dataset['Japan'] = (origin == 3)*1.0
4dataset.tail()
MPG | Cylinders | Displacement | Horsepower | Weight | Acceleration | Model Year | USA | Europe | Japan | |
---|---|---|---|---|---|---|---|---|---|---|
393 | 27.0 | 4 | 140.0 | 86.0 | 2790.0 | 15.6 | 82 | 1.0 | 0.0 | 0.0 |
394 | 44.0 | 4 | 97.0 | 52.0 | 2130.0 | 24.6 | 82 | 0.0 | 1.0 | 0.0 |
395 | 32.0 | 4 | 135.0 | 84.0 | 2295.0 | 11.6 | 82 | 1.0 | 0.0 | 0.0 |
396 | 28.0 | 4 | 120.0 | 79.0 | 2625.0 | 18.6 | 82 | 1.0 | 0.0 | 0.0 |
397 | 31.0 | 4 | 119.0 | 82.0 | 2720.0 | 19.4 | 82 | 1.0 | 0.0 | 0.0 |
现在将数据集拆分为训练集和测试集,我们将在模型的最终评估中使用测试集。
xxxxxxxxxx
21train_dataset = dataset.sample(frac=0.8,random_state=0)
2test_dataset = dataset.drop(train_dataset.index)
快速浏览训练集中几对列的联合分布:
xxxxxxxxxx
11sns.pairplot(train_dataset[["MPG", "Cylinders", "Displacement", "Weight"]], diag_kind="kde")
另外查看整体统计数据:
xxxxxxxxxx
41train_stats = train_dataset.describe()
2train_stats.pop("MPG")
3train_stats = train_stats.transpose()
4train_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 |
将目标值或“标签”与特征分开,此标签是您训练的模型进行预测的值:
xxxxxxxxxx
21train_labels = train_dataset.pop('MPG')
2test_labels = test_dataset.pop('MPG')
再看一下上面的train_stats
块,注意每个特征的范围有多么不同。
使用不同的比例和范围对特征进行标准化是一个很好的实践,虽然模型可能在没有特征标准化的情况下收敛,但它使训练更加困难,并且它使得最终模型取决于输入中使用的单位的选择。
注意:尽管我们仅从训练数据集中有意生成这些统计信息,但这些统计信息也将用于标准化测试数据集。我们需要这样做,将测试数据集投影到模型已经训练过的相同分布中。
xxxxxxxxxx
41def norm(x):
2 return (x - train_stats['mean']) / train_stats['std']
3normed_train_data = norm(train_dataset)
4normed_test_data = norm(test_dataset)
这个标准化数据是我们用来训练模型的数据。
注意:用于标准化输入的统计数据(平均值和标准偏差)需要应用于输入模型的任何其他数据,以及我们之前执行的独热编码。这包括测试集以及模型在生产中使用时的实时数据。
让我们建立我们的模型。在这里,我们将使用具有两个密集连接隐藏层的Sequential
模型,以及返回单个连续值的输出层。模型构建步骤包含在函数build_model
中,因为我们稍后将创建第二个模型。
xxxxxxxxxx
131def build_model():
2 model = keras.Sequential([
3 layers.Dense(64, activation='relu', input_shape=[len(train_dataset.keys())]),
4 layers.Dense(64, activation='relu'),
5 layers.Dense(1)
6 ])
7
8 optimizer = tf.keras.optimizers.RMSprop(0.001)
9
10 model.compile(loss='mse',
11 optimizer=optimizer,
12 metrics=['mae', 'mse'])
13 return model
xxxxxxxxxx
11model = build_model()
使用.summary
方法打印模型的简单描述
xxxxxxxxxx
11model.summary()
xxxxxxxxxx
141Model: "sequential"
2_________________________________________________________________
3Layer (type) Output Shape Param #
4=================================================================
5dense (Dense) (None, 64) 640
6_________________________________________________________________
7dense_1 (Dense) (None, 64) 4160
8_________________________________________________________________
9dense_2 (Dense) (None, 1) 65
10=================================================================
11Total params: 4,865
12Trainable params: 4,865
13Non-trainable params: 0
14_________________________________________________________________
现在试试这个模型。从训练数据中取出一批10个样本数据并在调用model.predict
函数。
xxxxxxxxxx
31example_batch = normed_train_data[:10]
2example_result = model.predict(example_batch)
3example_result
xxxxxxxxxx
101 array([[ 0.3297699 ],
2 [ 0.25655937],
3 [-0.12460149],
4 [ 0.32495883],
5 [ 0.50459725],
6 [ 0.10887371],
7 [ 0.57305855],
8 [ 0.57637435],
9 [ 0.12094647],
10 [ 0.6864784 ]], dtype=float32)
这似乎可以工作,它产生预期的shape和类型的结果。
训练模型1000个周期,并在history
对象中记录训练和验证准确性:
xxxxxxxxxx
121# 通过为每个完成的周期打印单个点来显示训练进度
2class PrintDot(keras.callbacks.Callback):
3 def on_epoch_end(self, epoch, logs):
4 if epoch % 100 == 0: print('')
5 print('.', end='')
6
7EPOCHS = 1000
8
9history = model.fit(
10 normed_train_data, train_labels,
11 epochs=EPOCHS, validation_split = 0.2, verbose=0,
12 callbacks=[PrintDot()])
使用存储在history
对象中的统计数据可视化模型的训练进度。300
xxxxxxxxxx
31hist = pd.DataFrame(history.history)
2hist['epoch'] = history.epoch
3hist.tail()
loss | mae | mse | val_loss | val_mae | val_mse | epoch | |
---|---|---|---|---|---|---|---|
995 | 2.556746 | 0.988013 | 2.556746 | 10.210531 | 2.324411 | 10.210530 | 995 |
996 | 2.597973 | 1.039339 | 2.597973 | 11.257273 | 2.469266 | 11.257273 | 996 |
997 | 2.671929 | 1.040886 | 2.671929 | 10.604957 | 2.446257 | 10.604958 | 997 |
998 | 2.634858 | 1.001898 | 2.634858 | 10.906935 | 2.373279 | 10.906935 | 998 |
999 | 2.741717 | 1.035889 | 2.741717 | 10.698320 | 2.342703 | 10.698319 | 999 |
xxxxxxxxxx
271def plot_history(history):
2 hist = pd.DataFrame(history.history)
3 hist['epoch'] = history.epoch
4
5 plt.figure()
6 plt.xlabel('Epoch')
7 plt.ylabel('Mean Abs Error [MPG]')
8 plt.plot(hist['epoch'], hist['mae'],
9 label='Train Error')
10 plt.plot(hist['epoch'], hist['val_mae'],
11 label = 'Val Error')
12 plt.ylim([0,5])
13 plt.legend()
14
15 plt.figure()
16 plt.xlabel('Epoch')
17 plt.ylabel('Mean Square Error [$MPG^2$]')
18 plt.plot(hist['epoch'], hist['mse'],
19 label='Train Error')
20 plt.plot(hist['epoch'], hist['val_mse'],
21 label = 'Val Error')
22 plt.ylim([0,20])
23 plt.legend()
24 plt.show()
25
26
27plot_history(history)
该图表显示在约100个周期之后,验证误差几乎没有改进,甚至降低。让我们更新model.fit
调用,以便在验证分数没有提高时自动停止训练。我们将使用EarlyStopping
回调来测试每个周期的训练状态。如果经过一定数量的周期而没有显示出改进,则自动停止训练。
您可以了解此回调的更多信息 连接.
xxxxxxxxxx
91model = build_model()
2
3# “patience”参数是检查改进的周期量
4early_stop = keras.callbacks.EarlyStopping(monitor='val_loss', patience=10)
5
6history = model.fit(normed_train_data, train_labels, epochs=EPOCHS,
7 validation_split = 0.2, verbose=0, callbacks=[early_stop, PrintDot()])
8
9plot_history(history)
上图显示在验证集上平均误差通常约为+/-2MPG,这个好吗?我们会把这个决定留给你。
让我们看一下使用测试集来看一下泛化模型效果,我们在训练模型时没有使用测试集,这告诉我们,当我们在现实世界中使用模型时,我们可以期待模型预测。
xxxxxxxxxx
31loss, mae, mse = model.evaluate(normed_test_data, test_labels, verbose=0)
2
3print("Testing set Mean Abs Error: {:5.2f} MPG".format(mae))
Testing set Mean Abs Error: 2.09 MPG
最后,使用测试集中的数据预测MPG值:
xxxxxxxxxx
111test_predictions = model.predict(normed_test_data).flatten()
2
3plt.scatter(test_labels, test_predictions)
4plt.xlabel('True Values [MPG]')
5plt.ylabel('Predictions [MPG]')
6plt.axis('equal')
7plt.axis('square')
8plt.xlim([0,plt.xlim()[1]])
9plt.ylim([0,plt.ylim()[1]])
10_ = plt.plot([-100, 100], [-100, 100])
11
看起来我们的模型预测得相当好,我们来看看错误分布:
xxxxxxxxxx
41error = test_predictions - test_labels
2plt.hist(error, bins = 25)
3plt.xlabel("Prediction Error [MPG]")
4_ = plt.ylabel("Count")
上图看起来不是很高斯(正态分布),很可能是因为样本数据非常少。
本章节介绍了一些处理回归问题的技巧:
最新版本:https://www.mashangxue123.com/tensorflow/tf2-tutorials-keras-basic_regression.html 英文版本:https://tensorflow.google.cn/beta/tutorials/keras/basic_regression 翻译建议PR:https://github.com/mashangxue/tensorflow2-zh/edit/master/r2/tutorials/keras/basic_regression.md