百度人工项目实训1--多变量线性回归之房价预测

目录

1.实验介绍:

2.实验环境

3.实验内容

3.1.基本内容

3.1.1.数据集介绍:

3.1.2训练

3.1.3测试

3.2扩展


1.实验介绍:

       给定一个大小为𝑛n的数据集 {𝑦𝑖,𝑥𝑖1,...,𝑥𝑖𝑑}𝑛𝑖=1{yi,xi1,...,xid}i=1n,其中𝑥𝑖1,…,𝑥𝑖𝑑xi1,…,xid是第𝑖i个样本𝑑d个属性上的取值,𝑦𝑖yi是该样本待预测的目标。线性回归模型假设目标𝑦𝑖yi可以被属性间的线性组合描述,即

                                        𝑦𝑖=𝜔1𝑥𝑖1+𝜔2𝑥𝑖2+…+𝜔𝑑𝑥𝑖𝑑+𝑏,𝑖=1,…,𝑛yi=ω1xi1+ω2xi2+…+ωdxid+b,i=1,…,n

       例如,在我们将要建模的房价预测问题里,𝑥𝑖𝑗xij是描述房子𝑖i的各种属性(比如房间的个数、周围学校和医院的个数、交通状况等),而 𝑦𝑖yi是房屋的价格。

2.实验环境

      实验系统环境:Window10,百度云学习平台

      软件:Anaconda3、Jupyter notebook

      框架:PaddlePaddle(飞桨(PaddlePaddle)以百度多年的深度学习技术研究和业务应用为基础,是中国首个开源开放、技术领先、功能完备的产业级深度学习平台,集深度学习核心训练和推理框架、基础模型库、端到端开发套件和丰富的工具组件于一体。)

3.实验内容

3.1.基本内容

3.1.1.数据集介绍:

数据集包括13个变量,具体参数如下图所示

 

       观察一下数据,我们的第一个发现是:所有的13维属性中,有12维的连续值和1维的离散值。所以通常对一个有d个可能取值的离散属性,我们会将它们转为d个取值为0或1的二值属性或者将每个可能取值映射为一个多维向量。不过就这里而言,因为CHAS本身就是一个二值属性,就省去了这个麻烦。还可以观察到,各位属性的取值范围差别很大,解决这个问题的操作是归一化。常见的方法有去均值:减掉均值,然后除以原取值范围。

做归一化(或 Feature scaling)至少有以下3个理由:

  1. 过大或过小的数值范围会导致计算时的浮点上溢或下溢。
  2. 不同的数值范围会导致不同属性对模型的重要性不同(至少在训练的初始阶段如此),而这个隐含的假设常常是不合理的。这会对优化的过程造成困难,使训练时间大大的加长。
  3. 很多的机器学习技巧/模型(例如L1,L2正则项,向量空间模型-Vector Space Model)都基于这样的假设:所有的属性取值都差不多是以0为均值且取值范围相近的。

       除了对数据的属性进行分析外,数据集的大小也是我们要考虑的内容:为了是实验更加有说服力,在大量数据支持的前提下,通常把数据集分为三类:训练集,测试集和验证集。训练集顾名思义是,用于训练的数据的集合,测试集是用于测试的数据集合,一般将数据集按比例划分为训练集和数据集,例如在本实验中,训练集与测试集的分割比例为8:2。验证集是从训练集中划分出来的。因为复杂的模型中常常还有一些超参数(Hyperparameter)需要调节,所以我们会尝试多种超参数的组合来分别训练多个模型,然后对比它们在验证集上的表现选择相对最好的一组超参数,最后才使用这组参数下训练的模型在测试集上评估测试误差。

3.1.2训练

①配置数据提供器

  首先引入必要的库

from __future__ import print_function
import paddle
import paddle.fluid as fluid
import numpy
import math
import sys

接下来我们定义了用于训练的数据提供器。提供器每次读入一个大小为BATCH_SIZE的数据批次。如果用户希望加一些随机性,它可以同时定义一个批次大小和一个缓存大小。这样的话,每次数据提供器会从缓存中随机读取批次大小那么多的数据。

BATCH_SIZE = 20#批处理大小
#数据预处理已完成后,进行的读取数据的过程
train_reader = paddle.batch(
    paddle.reader.shuffle(#读训练集
        paddle.dataset.uci_housing.train(), buf_size=500),#buf_size缓存大小
        batch_size=BATCH_SIZE)

test_reader = paddle.batch(#读测试集
    paddle.reader.shuffle(
        paddle.dataset.uci_housing.test(), buf_size=500),
        batch_size=BATCH_SIZE)

②配置训练程序

训练程序的目的是定义一个训练模型的网络结构。对于线性回归来讲,它就是一个从输入到输出的简单的全连接层。更加复杂的结果,比如卷积神经网络,递归神经网络等会在随后的章节中介绍。训练程序必须返回平均损失作为第一个返回值,因为它会被后面反向传播算法所用到。

x = fluid.layers.data(name='x', shape=[13], dtype='float32') # 定义输入的形状和数据类型
y = fluid.layers.data(name='y', shape=[1], dtype='float32') # 定义输出的形状和数据类型
y_predict = fluid.layers.fc(input=x, size=1, act=None) # 连接输入和输出的全连接层
main_program = fluid.default_main_program() # 获取默认/全局主函数(假设函数)
startup_program = fluid.default_startup_program() # 获取默认/全局启动程序
cost = fluid.layers.square_error_cost(input=y_predict, label=y) # 利用标签数据和输出的预测数据估计方差(损失函数)
avg_loss = fluid.layers.mean(cost) # 对方差求均值,得到平均损失

③Optimizer Function 配置(优化函数)

在下面的 SGD optimizer,learning_rate 是学习率,与网络的训练收敛速度有关系。

#克隆main_program得到test_program
#有些operator在训练和测试之间的操作是不同的,例如batch_norm,使用参数for_test来区分该程序是用来训练还是用来测试
#该api不会删除任何操作符,请在backward和optimization之前使用
test_program = main_program.clone(for_test=True)

sgd_optimizer = fluid.optimizer.SGD(learning_rate=0.001)
sgd_optimizer.minimize(avg_loss)

④定义运算场所

我们可以定义运算是发生在CPU还是GPU

use_cuda = False
place = fluid.CUDAPlace(0) if use_cuda else fluid.CPUPlace() # 指明executor的执行场所

###executor可以接受传入的program,并根据feed map(输入映射表)和fetch list(结果获取表)向program中添加数据输入算子和结果获取算子。使用close()关闭该executor,调用run(...)执行program。
exe = fluid.Executor(place)

⑤创建训练过程

训练需要有一个训练程序和一些必要参数,并构建了一个获取训练过程中测试误差的函数。必要参数有executor,program,reader,feeder,fetch_list,executor表示之前创建的执行器,program表示执行器所执行的program,是之前创建的program,如果该项参数没有给定的话则默认使用default_main_program,reader表示读取到的数据,feeder表示前向输入的变量,fetch_list表示用户想得到的变量或者命名的结果

num_epochs = 100

def train_test(executor, program, reader, feeder, fetch_list):
    accumulated = 1 * [0]
    count = 0
    for data_test in reader():
        outs = executor.run(program=program,
                            feed=feeder.feed(data_test),
                            fetch_list=fetch_list)
        accumulated = [x_c[0] + x_c[1][0] for x_c in zip(accumulated, outs)] # 累加测试过程中的损失值
        count += 1 # 累加测试集中的样本数量
    return [x_d / count for x_d in accumulated] # 计算平均损失

⑥训练主循环

给出需要存储的目录名,并初始化一个执行器。

%matplotlib inline
params_dirname = "fit_a_line.inference.model"
feeder = fluid.DataFeeder(place=place, feed_list=[x, y])
exe.run(startup_program)
train_prompt = "train cost"
test_prompt = "test cost"
from paddle.utils.plot import Ploter
plot_prompt = Ploter(train_prompt, test_prompt)
step = 0

exe_test = fluid.Executor(place)

paddlepaddle提供了reader机制来读取训练数据。reader会一次提供多列数据,因此我们需要一个python的列表来定义读取顺序。我们构建一个循环来进行训练,直到训练结果足够好或者循环次数足够多。 如果训练迭代次数满足参数保存的迭代次数,可以把训练参数保存到params_dirname。 设置训练主循环

for pass_id in range(num_epochs):
    for data_train in train_reader():
        avg_loss_value, = exe.run(main_program,
                                  feed=feeder.feed(data_train),
                                  fetch_list=[avg_loss])
        if step % 10 == 0: # 每10个批次记录并输出一下训练损失
            plot_prompt.append(train_prompt, step, avg_loss_value[0])
            plot_prompt.plot()
            print("%s, Step %d, Cost %f" %
	                  (train_prompt, step, avg_loss_value[0]))
        if step % 100 == 0:  # 每100批次记录并输出一下测试损失
            test_metics = train_test(executor=exe_test,
                                     program=test_program,
                                     reader=test_reader,
                                     fetch_list=[avg_loss.name],
                                     feeder=feeder)
            plot_prompt.append(test_prompt, step, test_metics[0])
            plot_prompt.plot()
            print("%s, Step %d, Cost %f" %
	                  (test_prompt, step, test_metics[0]))
            if test_metics[0] < 10.0: # 如果准确率达到要求,则停止训练
                break

        step += 1

        if math.isnan(float(avg_loss_value[0])):
            sys.exit("got NaN loss, training failed.")

        #保存训练参数到之前给定的路径中
        if params_dirname is not None:
            fluid.io.save_inference_model(params_dirname, ['x'], [y_predict], exe)

到此,训练的过程基本结束,通过训练,我们可以得到一个模型,通过这个模型,我们就可以实现对房价的预测了。

3.1.3测试

需要构建一个使用训练好的参数来进行预测的程序,训练好的参数位置在params_dirname。

准备预测环境

类似于训练过程,预测器需要一个预测程序来做预测。我们可以稍加修改我们的训练程序来把预测值包含进来。

infer_exe = fluid.Executor(place)
inference_scope = fluid.core.Scope()

预测,保存图片

def save_result(points1, points2):
    import matplotlib
    matplotlib.use('Agg')
    import matplotlib.pyplot as plt
    x1 = [idx for idx in range(len(points1))]
    y1 = points1
    y2 = points2
    l1 = plt.plot(x1, y1, 'r--', label='predictions')
    l2 = plt.plot(x1, y2, 'g--', label='GT')
    plt.plot(x1, y1, 'ro-', x1, y2, 'g+-')
    plt.title('predictions VS GT')
    plt.legend()
    plt.savefig('./image/prediction_gt.png')

通过fluid.io.load_inference_model,预测器会从params_dirname中读取已经训练好的模型,来对从未遇见过的数据进行预测。

with fluid.scope_guard(inference_scope):
    [inference_program, feed_target_names,
     fetch_targets] = fluid.io.load_inference_model(params_dirname, infer_exe) # 载入预训练模型
    batch_size = 10

    infer_reader = paddle.batch(
        paddle.dataset.uci_housing.test(), batch_size=batch_size) # 准备测试集

    infer_data = next(infer_reader())
    infer_feat = numpy.array(
        [data[0] for data in infer_data]).astype("float32") # 提取测试集中的数据
    infer_label = numpy.array(
        [data[1] for data in infer_data]).astype("float32") # 提取测试集中的标签

    assert feed_target_names[0] == 'x'
    results = infer_exe.run(inference_program,
                            feed={feed_target_names[0]: numpy.array(infer_feat)},
                            fetch_list=fetch_targets) # 进行预测
    #打印预测结果和标签并可视化结果
    print("infer results: (House Price)")
    for idx, val in enumerate(results[0]):
        print("%d: %.2f" % (idx, val)) # 打印预测结果

    print("\nground truth:")
    for idx, val in enumerate(infer_label):
        print("%d: %.2f" % (idx, val)) # 打印标签值

    save_result(results[0], infer_label) # 保存图片

由于每次都是随机选择一个minibatch的数据作为当前迭代的训练数据,所以每次得到的预测结果会有所不同。

3.2扩展

接下来就是扩展的内容了。

1.本次实验中我们采用了PaddlePaddle封装好的数据准备过程,尝试直接从txt文件中读取数据的话。这次的数据格式是这样的,前两列是变量,最后一列是房价。由于数据不是官方的,所以数据预处理的过程需要我们自己动手。

       数据预处理按照前面的介绍,首先,我们要定义类型名,好可以与数据对应,然后读取文件,为减少数据范围对实验的影响,对数据进行归一化处理,然后划分数据集。关键代码如下:

BATCH_SIZE = 5#批处理大小
feature_names = [#类名
    'CRIM', 'ZN', 'INDUS'
]
feature_num = len(feature_names)
datafile = './ex1data2.txt'  # 原始文件
data = numpy.fromfile(datafile, sep=' ')  #一行数据显示
data = data.reshape(data.shape[0] // feature_num, feature_num)
#归一化操作:去均值
maximums, minimums, avgs = data.max(axis=0), data.min(axis=0), data.sum(axis=0) / data.shape[0]

for i in six.moves.range(feature_num - 1):
    data[:, i] = (data[:, i] - avgs[i]) / (maximums[i] - minimums[i])  # six.moves可以兼容python2和python3

ratio = 0.8  # 训练集和验证集的划分比例
offset = int(data.shape[0] * ratio)
train_data = data[:offset]
test_data = data[offset:]

配置数据提供器:

def reader_creator(train_data):
    def reader():
        for d in train_data:
            yield d[:-1], d[-1:]

    return reader
#读取训练集
train_reader = paddle.batch(
    paddle.reader.shuffle(
        reader_creator(train_data), buf_size=500),
    batch_size=BATCH_SIZE)
#读取测试集
test_reader = paddle.batch(
    paddle.reader.shuffle(
        reader_creator(test_data), buf_size=500),
    batch_size=BATCH_SIZE)

到此数据预处理过程结束。成功将新的数据读入功训练与测试使用。

2.调整batch size重新训练,查看对训练速度和训练效果的影响‘

可以通过如上图箭头所指的变量来控制batch size的大小。

初始参数:epoch=100,learning rate=0.001,不变,batch size=3或5或7或10。

测试结果如下:

结论:在一定条件下,当batch size不断的增大,训练的次数不断变少,速度也有所加快。

3.调整learning rate重新训练,查看对训练速度和训练效果的影响

可以通过图上图箭头所指的变量修改learning rate的值。

初始参数:batch size=5,epoch=100,learning rate=0.001或0.002或0.003

测试结果如下

结论:在一定条件下,学习率能够影响模型精度,对训练的时间没有影响。

4.调整num_epochs重新训练,查看对训练速度和训练效果的影响

可以通过修改如上图箭头所指出数值来修改epoch的数量

初始参数:batch size=5,learning rate=0.001,epoch=100或200或300。测试结果如下:

结论:在一定条件下,epoch的大小能够影响模型精度,当epoch增大,训练时间也会增加,epoch大小与训练时间成正相关。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值