汽车油耗预测实战

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

一、加载数据集以及数据集的处理

官方数据集在下载时,出现链接超时的失败问题,可以使用百度云下载数据集,然后放在.keras文件夹下http://链接:https://pan.baidu.com/s/1GEKol03p9nraw-kscE7bXw   提取码:llkq

无论是否是去Auto MPG下载的数据集,还是网盘的数据集,都还是调用以下方法加载数据集本地路径

# 加载数据集
dataset_path = keras.utils.get_file("auto-mpg.data",
                                    "http://archive.ics.uci.edu/ml/machine-learning-databases/auto-mpg/autompg.data")

由于读取的数据是是一个data文件,类似于表格,所以需要为其读取前,设置好对应的列名-标签。DataFrame是Pandas中的一个表格型的数据结构,包含有一组有序的列,每列可以是不同的值类型(数值、字符串、布尔型等),DataFrame即有行索引也有列索引,可以被看做是由Series组成的字典

# 利用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)

使用pandas读取数据需要说明以下主要参数:依次为路径, 索引名,空(无效)值设置,指定注释行,分隔符,是否考虑为False的数据

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

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

 isna()会返回为包含无效值的表单,再追加一个sum()实现计算无效值的和;其次,对无效数据进行处理:dropna()清除无效数据后返回一个新数据表单

将需要的产源地分类序号从表中提取出来,使用pop(‘Origin’)提取Origin列的数据,将Origin进行one-hot转换——即Origin不同的值仅对应一个数据有效:

# 根据 origin 列来写入新的 3 个列
dataset['USA'] = (origin == 1) * 1.0
dataset['Europe'] = (origin == 2) * 1.0
dataset['Japan'] = (origin == 3) * 1.0

仅当对应的标号时,某一列中的数据才为1,满足one-hot定义:[0,0,1,0,0……1,0],只有有效数据为1,其余为零

 

# 数据集 = 训练集 + 测试集
train_dataset = dataset.sample(frac=0.8, random_state=0)
test_dataset = dataset.drop(train_dataset.index)


# 查看训练集的输入x的统计数据
train_stats = train_dataset.describe()
train_stats.pop("MPG")  # 仅保留输入x
train_stats = train_stats.transpose()  # 转置

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

然后利用sample函数将数据集拆分为0.8的数据集和0.2的测试集,并且移除已经取出的数据下标包含的数据并返回作为测试集,describe()用于观察一系列数据的范围,统计训练集的各个字段数值的均值和标准差,大小、波动趋势等。从获得的数据中获得训练数据的标签,即移除的MPG列数据

最近完成数据的标准化防止出现梯度弥散,切分训练集,分为几个batch,加速计算

# 切分训练集,分为batch = 32
train_db = tf.data.Dataset.from_tensor_slices((normed_train_data.values, train_labels.values))
train_db = train_db.shuffle(100).batch(32)  # 随机大赛,批处理
sample = next(iter(train_db))

二、网络模型构建

考虑到 Auto MPG 数据集规模较小,只创建一个 3 层的全连接网络来完成 MPG值的预测任务。输入𝑿的特征共有 9 种,因此第一层的输入节点数为 9。第一层、第二层的输出节点数设计为64和64,由于只有一种预测值,输出层输出节点设计为 1。考虑MPG ∈𝑅+,因此输出层的激活函数可以不加,也可以添加 ReLU 激活函数。将网络实现为一个自定义网络类,只需要在初始化函数中创建各个子网络层,并在前向计算函数 call 中实现自定义网络类的计算逻辑即可

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):
        # 依次通过前三层
        out = self.fc1(inputs)
        out = self.fc2(out)
        out = self.fc3(out)
        return out

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

在完成主网络模型类的创建后,来实例化网络对象,通过build函数初始化网络的权值和维度

三、网络装配

在训练网络时,一般的流程是通过前向计算获得网络的输出值, 再通过损失函数计算网络误差,然后通过自动求导工具计算梯度并更新,同时间隔性地测试网络的性能。

所以,在完成网络模型的搭建后,需要指定网络使用的优化器对象、 损失函数类型, 评价指标等设定,这一步称为装配。这里只指定网络使用的优化器对象,损失函数在梯度求导时在指定

# 创建优化器,指定学习率
optimizer = tf.keras.optimizers.RMSprop(1e-3)

四、计算梯度和代价函数以及更新参数

通过 Epoch 和 Step 组成的双层循环训练网络,共训练 200个 Epoch

for epoch in range(200):  # 训练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, step, float(loss), float(mae_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)))

对于回归问题,除了 MSE 均方差可以用来模型的测试性能,还可以用平均绝对误差(Mean Absolute Error,简称 MAE)来衡量模型的性能,程序运算时记录每个 Epoch 结束时的训练和测试 MAE 数据,并绘制变化曲线
 

最后训练结果:

 调了输入值在不同的区间,测试集MAE基本不变,但是,训练集还在波动,和其他大佬的有很大不同。

五、完整程序

仅供参考

# -*- codeing = utf-8 -*-
# @Time : 13:48
# @Author:Paranipd
# @File : 汽车油耗预测.py
# @Software:PyCharm

import os
import pandas as pd
import seaborn as sns
import tensorflow as tf
import matplotlib.pyplot as plt
from tensorflow import keras
from tensorflow.keras import datasets, layers, optimizers, Sequential, losses  

os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'  # 去掉不必要的报错


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

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

# 处理类别型数据,其中 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() # 查看新表格的最后几项

# 数据集 = 训练集 + 测试集
train_dataset = dataset.sample(frac=0.8, random_state=0)
test_dataset = dataset.drop(train_dataset.index)

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



# 查看训练集的输入x的统计数据
train_stats = train_dataset.describe()
train_stats.pop("MPG")  # 仅保留输入x
train_stats = train_stats.transpose()  # 转置

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

# 标准化数据
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,标签用一个标量表示'''

# 切分训练集,分为batch = 32
train_db = tf.data.Dataset.from_tensor_slices((normed_train_data.values, train_labels.values))
train_db = train_db.shuffle(100).batch(32)  # 随机大赛,批处理
sample = next(iter(train_db))

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

        self.fc3 = layers.Dense(1)

    def call(self, inputs, training=None, mask=None):
        # 依次通过前三层
        out = self.fc1(inputs)
        out = self.fc2(out)
        out = self.fc3(out)
        return out

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

# 创建优化器,指定学习率
optimizer = tf.keras.optimizers.RMSprop(1e-3)

train_mae_losses = []
test_mae_losses = []


for epoch in range(200):  # 训练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, step, float(loss), float(mae_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)))

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.savefig('auto.svg')
plt.show()

  • 6
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Super.Bear

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值