Auto MPG 汽车油耗预测(tensorflow深度学习龙书笔记)

利用全连接网络模型来完成汽车的效能指标 MPG(Mile Per Gallon,每加仑燃油英里数)的预测问题实战

数据集

我们采用 Auto MPG 数据集,它记录了各种汽车效能指标与气缸数、重量、马力等其
它因子的真实数据,查看数据集的前 5 项,如表 6.1 所示,其中每个字段的含义列在表6.2 中。除了产地的数字字段表示类别外,其他字段都是数值类型。对于产地地段,1 表示美国,2 表示欧洲,3 表示日本

Auto MPG 汽车油耗预测(tensorflow深度学习龙书笔记)_第1张图片

# 在线下载汽车效能数据集
dataset_path = keras.utils.get_file("auto-mpg.data", 
"http://archive.ics.uci.edu/ml/machine-learning-databases/auto-mpg/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()

pd.read_csv()

pd.read_csv(filepath_or_buffer, sep=',',
 delimiter=None, header='infer', names=None, index_col=None, usecols=None, squeeze=False, prefix=None, mangle_dupe_cols=True, 
 dtype=None, engine=None, converters=None, true_values=None, false_values=None, skipinitialspace=False, skiprows=None, nrows=None, na_values=None, keep_default_na=True, 
 na_filter=True, verbose=False, skip_blank_lines=True, parse_dates=False, infer_datetime_format=False, keep_date_col=False, 
 date_parser=None, dayfirst=False, iterator=False, chunksize=None, compression='infer', thousands=None, 
 decimal=b'.', lineterminator=None, quotechar='"', quoting=0, escapechar=None, comment=None, encoding=None, dialect=None, tupleize_cols=False, error_bad_lines=True, 
 warn_bad_lines=True, skipfooter=0, skip_footer=0, doublequote=True, delim_whitespace=False, as_recarray=False, compact_ints=False, use_unsigned=False, low_memory=True, buffer_lines=None, memory_map=False, float_precision=None)

重要参数

1.filepath_or_buffer:(这是唯一一个必须有的参数,其它都是按需求选用的) 文件所在处的路径

2.sep: 指定分隔符,默认为逗号’,’

3.delimiter : str, default None 定界符,备选分隔符(如果指定该参数,则sep参数失效)

4.header:int or list of ints, default ‘infer’ 指定哪一行作为表头。默认设置为0(即第一行作为表头),如果没有表头的话,要修改参数,设置header=None

5.names: 指定列的名称,用列表表示。一般我们没有表头,即header=None时,这个用来添加列名就很有用啦!

6.index_col: 指定哪一列数据作为行索引,可以是一列,也可以多列。多列的话,会看到一个分层索引

7.prefix: 给列名添加前缀。如prefix=“x”,会出来"x1"、“x2”、"x3"酱纸

8.nrows : int, default None 需要读取的行数(从文件头开始算起)

9.encoding: 乱码的时候用这个就是了,官网文档看看用哪个: https://docs.python.org/3/library/codecs.html#standard-encodings

10.skiprows : list-like or integer, default None 需要忽略的行数(从文件开始处算起),或需要跳过的行号列表(从0开始)。

# 查看部分数据
dataset.head()

原始表格中的数据可能含有空字段(缺失值)的数据项,需要清除这些记录项:

dataset.isna().sum() # 统计空白数据
dataset = dataset.dropna() # 删除空白数据项
dataset.isna().sum() # 再次统计空白数据

由于 Origin 字段为类别类型数据,我们将其移除,并转换为新的 3 个字段:USA、Europe 和 Japan,分别代表是否来自此产地:

# 处理类别型数据,其中 origin 列代表了类别 1,2,3,分布代表产地:美国、欧洲、日本
# 先弹出(删除并返回)origin 这一列
origin = dataset.pop('Origin')
# 根据 origin 列来写入新的 3 个列
dataset['USA'] = (origin == 1)*1.0
dataset['Europe'] = (origin == 2)*1.0
dataset['Japan'] = (origin == 3)*1.0
dataset.tail() # 查看新表格的后几项
按着 8:2 的比例切分数据集为训练集和测试集:
# 切分为训练集和测试集
train_dataset = dataset.sample(frac=0.8,random_state=0)
test_dataset = dataset.drop(train_dataset.index) 
将 MPG 字段移出为标签数据:
# 移动 MPG 油耗效能这一列为真实标签 Y
train_labels = train_dataset.pop('MPG')
test_labels = test_dataset.pop('MPG')
统计训练集的各个字段数值的均值和标准差,并完成数据的标准化,通过 norm()函数
实现,代码如下:
# 查看训练集的输入 X 的统计数据
train_stats = train_dataset.describe()
train_stats.pop("MPG") # 仅保留输入 X
train_stats = train_stats.transpose() # 转置
# 标准化数据
def norm(x): # 减去每个字段的均值,并除以标准差
	return (x - train_stats['mean']) / train_stats['std']
normed_train_data = norm(train_dataset) # 标准化训练集
normed_test_data = norm(test_dataset) # 标准化测试集

#打印出训练集和测试集的大小:

print(normed_train_data.shape,train_labels.shape)
print(normed_test_data.shape, test_labels.shape)
(314, 9) (314,) # 训练集共 314 行,输入特征长度为 9,标签用一个标量表示
(78, 9) (78,) # 测试集共 78 行,输入特征长度为 9,标签用一个标量表示

#利用切分的训练集数据构建数据集对象:

train_db = tf.data.Dataset.from_tensor_slices((normed_train_data.values, 
train_labels.values)) # 构建 Dataset 对象
train_db = train_db.shuffle(100).batch(32) # 随机打散,批量化

创建网络

创建一个 3 层的全连接网络来完成 MPG值的预测任务。输入的特征共有 9 种,因此第一层的输入节点数为 9。第一层、第二层的输出节点数设计为64和64,由于只有一种预测值,输出层输出节点设计为 1。考虑MPG ∈+,因此输出层的激活函数可以不加,也可以添加 ReLU 激活函数。

将网络实现为一个自定义网络类,只需要在初始化函数中创建各个子网络层,并
在前向计算函数 call 中实现自定义网络类的计算逻辑即可。自定义网络类继承自
keras.Model 基类,这也是自定义网络类的标准写法,以方便地利用 keras.Model 基类提供的 trainable_variables、save_weights 等各种便捷功能。网络模型类实现如下:

#  创建网络
class Network(keras.Model):
    # 回归网络模型
    def __init__(self):
        super(Network, self).__init__()
        # 创建 3 个全连接层
        self.fc1 = layers.Dense(64, activation='relu')

        self.fc2 = layers.Dense(64, activation='relu')
        self.fc3 = layers.Dense(1)

    def call(self, inputs, training=None, mask=None):
        # 依次通过 3 个全连接层
        x = self.fc1(inputs)
        x = self.fc2(x)
        x = self.fc3(x)

        return x

训练与测试

在完成主网络模型类的创建后,我们来实例化网络对象和创建优化器,代码如下:

model = Network() # 创建网络类实例
# 通过 build 函数完成内部张量的创建,其中 4 为任意设置的 batch 数量,9 为输入特征长度
model.build(input_shape=(4, 9))
model.summary() # 打印网络信息
optimizer = tf.keras.optimizers.RMSprop(0.001) # 创建优化器,指定学习率

接下来实现网络训练部分。通过 Epoch 和 Step 组成的双层循环训练网络,共训练 200个 Epoch,代码如下:

for epoch in range(200): # 200 个 Epoch
    for step, (x,y) in enumerate(train_db): # 遍历一次训练集
        # 梯度记录器,训练时需要使用它
        with tf.GradientTape() as tape:
            out = model(x) # 通过网络获得输出
            loss = tf.reduce_mean(tf.keras.losses.MSE(y, out)) # 计算 MSE
            mae_loss = tf.reduce_mean(tf.keras.losses.MAE(y, out)) # 计算 MAE
        if step % 10 == 0: # 间隔性地打印训练误差
            print(epoch, step, float(loss))
        # 计算梯度,并更新
        grads = tape.gradient(loss, model.trainable_variables)
        optimizer.apply_gradients(zip(grads, model.trainable_variables))

完整代码

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, losses

def load_dataset():

    dataset_path = keras.utils.get_file("auto-mpg.data", "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(dataset_path, names=column_names, na_values="?", comment='\t', sep=" ", skipinitialspace=True)
    dataset = raw_dataset.copy()
    return  dataset

dataset = load_dataset()
# 查看部分数据
dataset.head()

def preprocess_dataset(dataset):
    dataset = dataset.copy()

    dataset = dataset.dropna() #  删除空白数据项


    # 处理类别型数据,其中 origin 列代表了类别 1,2,3,分布代表产地:美国、欧洲、日本
    # 先弹出(删除并返回)origin 这一列
    origin = dataset.pop('Origin')

    # 根据 origin 列来写入新的 3 个列
    dataset['USA'] = (origin == 1)*1.0
    dataset['Europe'] = (origin == 2)*1.0
    dataset['Japan'] = (origin == 3)*1.0
    dataset.tail() # 查看新表格的后几项

    #  按着 8:2 的比例切分数据集为训练集和测试集:
    # 切分为训练集和测试集
    train_dataset = dataset.sample(frac=0.8,random_state=0)
    test_dataset = dataset.drop(train_dataset.index)

    return train_dataset, test_dataset

train_dataset, test_dataset = preprocess_dataset(dataset)

# 统计数据
sns_plot = sns.pairplot(train_dataset[["Cylinders", "Displacement", "Weight", "MPG"]], diag_kind="kde")

# 查看训练集的输入X的统计数据
train_stats = train_dataset.describe()
train_stats.pop("MPG")
train_stats = train_stats.transpose()
train_stats



# 标准化数据
def norm(x): # 减去每个字段的均值,并除以标准差
	return (x - train_stats['mean']) / train_stats['std']


# 移动MPG油耗效能这一列为真实标签Y
train_labels = train_dataset.pop('MPG')
test_labels = test_dataset.pop('MPG')


normed_train_data = norm(train_dataset) # 标准化训练集
normed_test_data = norm(test_dataset) # 标准化测试集

#打印出训练集和测试集的大小:

print(normed_train_data.shape,train_labels.shape)
print(normed_test_data.shape, test_labels.shape)

#利用切分的训练集数据构建数据集对象:

#  创建网络
class Network(keras.Model):
    # 回归网络模型
    def __init__(self):
        super(Network, self).__init__()
        # 创建 3 个全连接层
        self.fc1 = layers.Dense(64, activation='relu')

        self.fc2 = layers.Dense(64, activation='relu')
        self.fc3 = layers.Dense(1)

    def call(self, inputs, training=None, mask=None):
        # 依次通过 3 个全连接层
        x = self.fc1(inputs)
        x = self.fc2(x)
        x = self.fc3(x)

        return x

def build_model():

    model = Network() # 创建网络类实例

    # 通过 build 函数完成内部张量的创建,其中 4 为任意设置的 batch 数量,9 为输入特征长度
    model.build(input_shape=(4, 9))
    model.summary() # 打印网络信息
    return model

model = build_model()

optimizer = tf.keras.optimizers.RMSprop(0.001) # 创建优化器,指定学习率
train_db = tf.data.Dataset.from_tensor_slices((normed_train_data.values, train_labels.values))
train_db = train_db.shuffle(100).batch(32)


def train(model, train_db, optimizer, normed_test_data, test_labels):
    train_mae_losses = []
    test_mae_losses = []
    for epoch in range(200):
        for step, (x, y) in enumerate(train_db):

            with tf.GradientTape() as tape:
                out = model(x)
                loss = tf.reduce_mean(losses.MSE(y, out))
                mae_loss = tf.reduce_mean(losses.MAE(y, out))

            if step % 10 == 0:
                print("epoch:", epoch, "loss:", float(loss))

            grads = tape.gradient(loss, model.trainable_variables)
            optimizer.apply_gradients(zip(grads, model.trainable_variables))

        train_mae_losses.append(float(mae_loss))
        out = model(tf.constant(normed_test_data.values))
        test_mae_losses.append(tf.reduce_mean(losses.MAE(test_labels, out)))

    return train_mae_losses, test_mae_losses

def plot(train_mae_losses, test_mae_losses):
    plt.figure()
    plt.xlabel('Epoch')
    plt.ylabel('MAE')
    plt.plot(train_mae_losses, label='Train')
    plt.plot(test_mae_losses, label='Test')
    plt.legend()
    # plt.ylim([0,10])
    plt.legend()
    plt.show()


train_mae_losses, test_mae_losses = train(model, train_db, optimizer, normed_test_data, test_labels)

plot(train_mae_losses, test_mae_losses)

你可能感兴趣的:(DL,深度学习,tensorflow,机器学习)