<jupyter_start><jupyter_text>PaddlePaddle实现线性回归-房价预测模型 基本概念线性回归是机器学习中最简单也是最重要的模型之一，其模型建立同样遵循上图流程：获取数据、数据预处理、训练模型、应用模型。回归模型可以理解为：存在一个点集，用一条曲线去拟合它分布的过程。如果拟合曲线是一条直线，则称为线性回归。如果是一条二次曲线，则被称为二次回归。线性回归是回归模型中最简单的一种。读者可以看到图中成本在刚开始收敛较快，随着迭代次数变多，收敛速度变慢，最终收敛到一个较小值。在线性回归中有几个基本的概念需要掌握：- 假设函数（Hypothesis Function）：用数学的方法描述自变量和因变量之间的关系，它们之间可以是一个线性函数或非线性函数- 损失函数（Loss Function）：用数学的方法衡量假设函数预测结果与真实值之间的误差。这个差距越小预测越准确，而算法的任务就是使这个差距越来越小。- 优化算法（Optimization Algorithm)：它决定了一个模型的精度和运算速度。本章的线性回归实例中主要使用了梯度下降法进行优化。** 损失函数 ** 对于某个具体样本(x(i),y(i))，算法通过不断调整参数值和，最终使得预测值和真实值尽可能相似，即 。整个训练的过程可以表述为通过调整参数值和最小化损失函数。因此，损失函数也是衡量算法优良性的方法。这里涉及到两个值：预测值和真实值。预测值是算法给出的值（用来表示概率）。而真实值是训练集中预先包含的，是事先准备好的。形式上，可以表示为：$$\{(x^{(1)}, y^{(1)}), (x^{(2)}, y^{(2)}), ... ,(x^{(m)}, y^{(m)})\}$$其中，x(i)表示属于第i个样本的特征向量，y(i)表示属于第i个样本的分类标签，也就是真实值。损失函数的选择需要具体问题具体分析，在不同问题场景下采用不同的函数。通常情况下，会将损失函数定义为平方损失函数（Quadratic Loss Function）。在本次线性回归中，使用的是均方差（Mean Squared Error）来衡量。$$L(\hat{y} , y) = -(ylog{ \hat{y} } + (1-y)log(1 - \hat{y}))$$** 梯度下降算法 **梯度下降是深度学习中非常重要的概念，值得庆幸的是它也十分容易理解。损失函数J(w,b)可以理解为变量w和b的函数。观察图，垂直轴表示损失函数的值，两个水平轴分别表示变量w和b。实际上，w可能是更高维的向量，但是为了方便说明，在这里假设w和b都是一个实数。算法的最终目标是找到损失函数J(w,b)的最小值。而这个寻找过程就是不断地微调变量w和b的值，一步一步地的试出这个最小值。而试的方法就是沿着梯度方向逐步移动。本例中让图中的圆点表示J(w,b)的某个值，那么梯度下降就是让圆点沿着曲面下降，直到J(w,b)取到最小值或逼近最小值。了解了以上几个关键概念后，我们以预测房价为背景，介绍线性回归的PaddlePaddle实现过程。数据集使用了某地区的房价分布，为了简化模型数据只有两维，分别是房屋面积与房屋价格。可以看到房价与房屋面积之间存在一种关系，这种关系究竟是什么，就是本次预测想要得到的结论。  1 - 引用库在进行网络配置之前，首先需要加载相应的Python库，并进行初始化操作。- numpy：一个python的基本库，用于科学计算- matplotlib.pyplot：用于生成图，在验证模型准确率和展示成本变化趋势时会使用到- paddle.v2：paddle深度学习平台<jupyter_code>import numpy as np
import paddle.v2 as paddle
import matplotlib
import matplotlib.pyplot as plt

%matplotlib inline<jupyter_output><empty_output><jupyter_text>2 - 数据预处理本次数据集使用的是2016年12月份某市某地区的房价分布。为了简化模型，假设影响房价的因素只有房屋面积，因此数据集只有两列。下述代码展示了数据处理的全部过程，包括数据装载和归一化。** 初始化全局变量 **<jupyter_code># 训练数据、原始数据、测试数据
CODEMASTER_TRAIN_DATA = None
X_RAW = None
CODEMASTER_TEST_DATA = None<jupyter_output><empty_output><jupyter_text>** 载入数据并进行预处理 **<jupyter_code># 载入数据
def load_data(filename, feature_num=2, ratio=0.8):
    """
    载入数据并进行数据预处理

    Args:
        filename -- 数据存储文件，从该文件读取数据
        feature_num -- 数据特征数量
        ratio -- 训练集占总数据集比例
    Return:
    """
    # 如果测试数据集和训练数据集都不为空，就不再载入数据load_data
    global CODEMASTER_TRAIN_DATA, CODEMASTER_TEST_DATA, X_RAW
    if CODEMASTER_TRAIN_DATA is not None and CODEMASTER_TEST_DATA is not None:
        return
    # data = np.loadtxt()表示将数据载入后以矩阵或向量的形式存储在data中
    # delimiter=',' 表示以','为分隔符
    data = np.loadtxt(filename, delimiter=',')
    X_RAW = data.T[0].copy()
    # axis=0 表示按列计算
    # data.shape[0]表示data中一共多少列
    maximums, minimums, avgs = data.max(axis=0), data.min(axis=0), data.sum(
        axis=0) / data.shape[0]

    # 归一化，data[:, i] 表示第i列的元素
    for i in xrange(feature_num - 1):
        data[:, i] = (data[:, i] - avgs[i]) / (maximums[i] - minimums[i])

    # offset用于划分训练数据集和测试数据集，例如0.8表示训练集占80%
    offset = int(data.shape[0] * ratio)
    CODEMASTER_TRAIN_DATA = data[:offset].copy()
    CODEMASTER_TEST_DATA = data[offset:].copy()<jupyter_output><empty_output><jupyter_text>完成数据集的加载和分割后，PaddlePaddle中通过reader来加载数据。 Reader返回的数据可以包括多列，利用reader可以使训练组合特征变得更容易。本次的数据只有两维，reader优点不明显，当数据多起来时就会发现利用多个reader训练模型会比传统方法方便许多。<jupyter_code># 读取训练数据或测试数据，服务于train()和test()
def read_data(data_set):
    """
        一个reader
        Args:
            data_set -- 要获取的数据集
        Return:
            reader -- 用于获取训练数据集及其标签的生成器generator
    """
    def reader():
        """
        一个reader
        Args:
        Return:
            data[:-1], data[-1:] -- 使用yield返回生成器(generator)，
                    data[:-1]表示前n-1个元素，也就是训练数据，data[-1:]表示最后一个元素，也就是对应的标签
        """
        for data in data_set:
            yield data[:-1], data[-1:]
    return reader
# 获取训练数据集
def train():
    """
    定义一个reader来获取训练数据集及其标签

    Args:
    Return:
        read_data -- 用于获取训练数据集及其标签的reader
    """
    global CODEMASTER_TRAIN_DATA
    load_data('data.txt')

    return read_data(CODEMASTER_TRAIN_DATA)

# 获取测试数据集
def test():
    """
    定义一个reader来获取测试数据集及其标签

    Args:
    Return:
        read_data -- 用于获取测试数据集及其标签的reader
    """
    global CODEMASTER_TEST_DATA
    load_data('data.txt')

    return read_data(CODEMASTER_TEST_DATA)<jupyter_output><empty_output><jupyter_text>3 - 配置网络结构线性回归的模型其实就是一个采用线性激活函数（linear activation，LinearActivation）的全连接层（fully-connected layer，fc_layer），因此在Peddlepeddle中利用全连接层模型构造线性回归，这样一个全连接层就可以看做是一个简单的神经网络。本次的模型由于只有一个影响参数，因此输入只含一个。搭建神经网络就像使用积木搭建宝塔一样。在PaddlePaddle中，网络层（layer）是我们的积木，而神经网络是我们要搭建的宝塔。我们使用不同的layer进行组合，来搭建神经网络。 宝塔的底端需要坚实的基座来支撑，同样，神经网络也需要一些特定的layer作为输入接口，来完成网络的训练。下述代码定义了一个layer组合，其中，x与y为之前描述的输入层；y_predict接收x作为输入，接上一个全连接层；cost接收y_predict与y作为输入，接上均方误差层。最后一层cost中记录了神经网络的所有拓扑结构，通过组合不同的layer，我们即可完成神经网络的搭建。其中x表示输入数据是一个维度为1的稠密向量，y表示输入数据是一个维度为1的稠密向量。<jupyter_code>#配置网络结构
def netconfig():
    """
    配置网络结构
    Args:
    Return:
        image -- 输入层，DATADIM维稠密向量
        y_predict -- 输出层，Linear作为激活函数
        y_label -- 标签数据，1维稠密向量
        cost -- 损失函数
        parameters -- 模型参数
        optimizer -- 优化器
        feeding -- 数据映射，python字典
    """
    # 输入层，paddle.layer.data表示数据层,name=’x’：名称为x,
    # type=paddle.data_type.dense_vector(1)：数据类型为1维稠密向量
    x = paddle.layer.data(name='x', type=paddle.data_type.dense_vector(1))

    # 输出层，paddle.layer.fc表示全连接层，input=x: 该层输入数据为x
    # size=1：神经元个数，act=paddle.activation.Linear()：激活函数为Linear()
    y_predict = paddle.layer.fc(input=x, size=1, act=paddle.activation.Linear())

    # 标签数据，paddle.layer.data表示数据层，name=’y’：名称为y
    # type=paddle.data_type.dense_vector(1)：数据类型为1维稠密向量
    y = paddle.layer.data(name='y', type=paddle.data_type.dense_vector(1))

    # 定义成本函数为均方差损失函数square_error_cost
    cost = paddle.layer.square_error_cost(input=y_predict, label=y)

    # 利用cost创建parameters
    parameters = paddle.parameters.create(cost)

    # 创建optimizer，并初始化momentum
    optimizer = paddle.optimizer.Momentum(momentum=0)

    # 数据层和数组索引映射，用于trainer训练时喂数据
    feeding = {'x': 0, 'y': 1}

    data = [x, y_predict, y, cost, parameters, optimizer, feeding]

    return data<jupyter_output><empty_output><jupyter_text>4 - 训练过程在完成神经网络结构配置之后，首先需要根据神经网络结构来创建所需要优化的参数集合（parameters），并创建优化器（optimizer）。之后，接下来可以创建训练器（trainer）来对网络进行训练。其中，trainer接收三个参数， 配置三个参数cost、parameters、update_equation，它们分别表示成本函数、参数和更新公式。<jupyter_code># 初始化，设置是否使用gpu，trainer数量
paddle.init(use_gpu=False, trainer_count=1)

x, y_predict, y, cost, parameters, optimizer, feeding = netconfig()

# 记录成本cost
costs = []

# 创建trainer
trainer = paddle.trainer.SGD(cost=cost, parameters=parameters, update_equation=optimizer)<jupyter_output>[INFO 2017-12-25 06:32:39,183 networks.py:1482] The input order is [x, y]
[INFO 2017-12-25 06:32:39,190 networks.py:1488] The output order is [__mse_cost_0__]
[INFO 2017-12-25 06:32:39,197 networks.py:1482] The input order is [x, y]
[INFO 2017-12-25 06:32:39,202 networks.py:1488] The output order is [__mse_cost_0__]<jupyter_text>在配置神经网络结构的过程中，仅仅对神经网络的输入进行了描述。而trainer需要读取训练数据进行训练，PaddlePaddle中通过reader来加载数据。 Reader返回的数据可以包括多列，一个Python dict可以把列序号映射到网络里的数据层。此外，PaddlePaddle还提供事件管理机制event handler，可以用来打印训练的进度及对模型训练效果进行监控。<jupyter_code># 打印训练过程信息
def event_handler(event):
    """
    事件处理器，可以根据训练过程的信息作相应操作
    Args:
        event -- 事件对象，包含event.pass_id, event.batch_id, event.cost等信息
    Return:
    """
    if isinstance(event, paddle.event.EndIteration):
        if event.pass_id % 100 == 0:
            print "Pass %d, Batch %d, Cost %f" % (
                event.pass_id, event.batch_id, event.cost)
            costs.append(event.cost)

    if isinstance(event, paddle.event.EndPass):
        result = trainer.test(
            reader=paddle.batch(test(), batch_size=2),
            feeding=feeding)
        print "Test %d, Cost %f" % (event.pass_id, result.cost)<jupyter_output><empty_output><jupyter_text>** 模型训练 **上述内容进行了初始化并配置了网络结构，接下来利用上述配置进行模型训练。首先定义一个随机梯度下降trainer，配置三个参数cost、parameters、update_equation，它们分别表示成本函数、参数和更新公式。再利用trainer.train()即可开始真正的模型训练：- paddle.reader.shuffle(train(), buf_size=500)表示trainer从train()这个reader中读取了buf_size=500大小的数据并打乱顺序- paddle.batch(reader(), batch_size=256)表示从打乱的数据中再取出batch_size=256大小的数据进行一次迭代训练- 参数feeding用到了之前定义的feeding索引，将数据层image和label输入trainer，也就是训练数据的来源。- 参数event_handler是事件管理机制，读者可以自定义event_handler，根据事件信息作相应的操作。- 参数num_passes=300表示迭代训练300次后停止训练。<jupyter_code># 训练
trainer.train(
    reader=paddle.batch(
        paddle.reader.shuffle(train(), buf_size=500),
        batch_size=256),
    feeding=feeding,
    event_handler=event_handler,
    num_passes=300)<jupyter_output>Pass 0, Batch 0, Cost 451396.781250
Pass 0, Batch 1, Cost 161123.718750
Pass 0, Batch 2, Cost 95150.760870
Test 0, Cost 78716.032743
Test 1, Cost 64055.805154
Test 2, Cost 58688.981138
Test 3, Cost 54197.818933
Test 4, Cost 50216.046274
Test 5, Cost 46687.523248
Test 6, Cost 43556.274846
Test 7, Cost 40789.440595
Test 8, Cost 38342.586017
Test 9, Cost 36154.617022
Test 10, Cost 34250.378032
Test 11, Cost 32576.608462
Test 12, Cost 31059.964123
Test 13, Cost 29719.393458
Test 14, Cost 28565.036137
Test 15, Cost 27484.750906
Test 16, Cost 26589.802841
Test 17, Cost 25768.127287
Test 18, Cost 25128.457994
Test 19, Cost 24412.014256
Test 20, Cost 23834.376012
Test 21, Cost 23442.690983
Test 22, Cost 22993.790680
Test 23, Cost 22570.143308
Test 24, Cost 22251.150047
Test 25, Cost 21959.284203
Test 26, Cost 21634.785514
Test 27, Cost 21432.838187
Test 28, Cost 21252.535045
Test 29, Cost 21123.863543
Test 30, Cost 20935.006401
Test 31, Cost 20836.804354
Test 32, Cost 20737.280186
Test 33, Cos[...]<jupyter_text>训练完成后我们可将结果参数打印出来：<jupyter_code>print("Result Parameters as below:")
a = parameters.get('___fc_layer_0__.w0')[0]
b = parameters.get('___fc_layer_0__.wbias')[0]
print(a, b)

x0 = X_RAW[0]
y0 = a * CODEMASTER_TRAIN_DATA[0][0] + b

x1 = X_RAW[1]
y1 = a * CODEMASTER_TRAIN_DATA[1][0] + b

a = (y0 - y1) / (x0 - x1)
b = (y1 - a * x1)

print 'a = ', a
print 'b = ', b<jupyter_output>Result Parameters as below:
(1137.4955, 608.8786)
a =  7.11512781259
b =  -64.529399425<jupyter_text>5 - 学习曲线输出成本的变化情况，也就是学习曲线，对模型进行分析，使用matplotlib展示学习曲线：<jupyter_code>costs = np.squeeze(costs)
plt.plot(costs)
plt.ylabel('cost')
plt.xlabel('iterations (per hundreds)')
plt.title("House Price Distributions ")
plt.show()<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.12104028157997654, 'fraction_numerical': 0.06804849432929214, 'mean_word_length': 5.2754601226993865, 'pattern_counts': {'":': 0, '<': 38, '<?xml version=': 0, '>': 38, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '10', 'dir': 'jupyter-structured-clean-dedup', 'id': '134717', 'license': 'Apache-2.0', 'n_tokens_mistral': 6080, 'n_tokens_neox': 6213, 'n_words': 582, 'path': 'jupyter/2.linear_regression/.ipynb_checkpoints/LiniearRegression-checkpoint.ipynb', 'repo_name': 'BaiduOSS/PaddleTutorial', 'size': '10228'}