【深度学习】使用飞桨paddle实现波士顿房价预测任务

使用飞桨实现波士顿房价预测任务

由于开始学习深度学习,因此每次开始都熟悉一下深度学习模型的基本步骤:

在之前的学习中,我们学习了使用Python和NumPy实现波士顿房价预测任务的方法,本章我们将尝试使用飞桨paddle重写房价预测任务,体会二者的异同。在数据处理之前,需要先加载飞桨框架的相关类库。

1. 数据处理

数据处理的代码不依赖飞桨框架实现,与使用Python构建房价预测任务的代码相同,因此独立为单独的代码。

python 复制代码
# 导入需要用到的package
import numpy as np
from sklearn.preprocessing import MinMaxScaler


def load_data():
    # 从文件导入数据
    datafile = './work/housing.data'
    data = np.fromfile(datafile, sep=' ')
    # 每条数据包括14项,其中前面13项是影响因素,第14项是相应的房屋价格中位数
    feature_names = [ 'CRIM', 'ZN', 'INDUS', 'CHAS', 'NOX', 'RM', 'AGE',
                      'DIS', 'RAD', 'TAX', 'PTRATIO', 'B', 'LSTAT', 'MEDV' ]
    feature_num = len(feature_names)
    # 将原始数据进行Reshape,变成[N, 14]这样的形状
    data = data.reshape([data.shape[0] // feature_num, feature_num])
    # 将原数据集拆分成训练集和测试集
    # 这里使用80%的数据做训练,20%的数据做测试
    # 测试集和训练集必须是没有交集的
    ratio = 0.8
    offset = int(data.shape[0] * ratio)
    train_data, test_data = data[:offset], data[offset:]
    # # 计算训练集的最大值,最小值
    # maximums, minimums = training_data.max(axis=0), \
    #                         training_data.min(axis=0)
    #
    # # 对数据进行归一化处理
    # for i in range(feature_num):
    #     data[:, i] = (data[:, i] - minimums[i]) / (maximums[i] - minimums[i])
    # 使用训练集计算最大值和最小值
    scaler = MinMaxScaler()
    # 只在训练集上拟合
    scaler.fit(train_data)
    data = scaler.transform(data)
    # 训练集和测试集的划分比例
    train_data = data[:offset]
    test_data = data[offset:]
    return train_data, test_data

在后续的使用通过引用即可

python 复制代码
from load_data import load_data
train_data, test_data = load_data()

2.模型设计

模型设计的实质是定义线性回归的网络结构,建议通过创建Python类的方式构建模型,该类需要继承paddle.nn.Layer父类,并且在类中定义init函数和forward函数。forward是飞桨前向计算逻辑的函数,在调用模型实例时会自动执行,其使用的网络层需要在init中声明。

init函数:在类的初始化函数中声明每一层网络的实现函数。在房价预测任务中,只需要定义一层全连接层。

forward函数:在构建神经网络时实现前向计算过程,并返回预测结果,在本任务中返回的是房价预测结果。

python 复制代码
#加载飞桨、NumPy和相关类库
import paddle
from paddle.nn import Linear
import paddle.nn.functional as F
import numpy as np
import os
import random
from load_data import load_data


class Regressor(paddle.nn.Layer):

    # self代表类的实例自身
    def __init__(self):
        # 初始化父类中的一些参数
        super(Regressor, self).__init__()

        # 定义一层全连接层,输入维度是13,输出维度是1
        self.fc = Linear(in_features=13, out_features=1)

    # 网络的前向计算
    def forward(self, inputs):
        x = self.fc(inputs)
        return x

3.训练配置

  • 声明定义好的回归模型实例为Regressor,并将模型的状态设置为train。
  • 使用load_data函数加载训练数据和测试数据。
  • 设置优化算法和学习率,优化算法采用随机梯度下降,学习率设置为0.01。

训练配置的代码实现如下:

python 复制代码
# 声明定义好的线性回归模型
model = Regressor()
# 开启模型训练模式,模型的状态设置为train
model.train()
# 使用load_data加载训练集数据和测试集数据
train_data, test_data = load_data()
# 定义优化算法,采用随机梯度下降SGD
# 学习率设置为0.01
opt = paddle.optimizer.SGD(learning_rate=0.005, parameters=model.parameters())

4.训练过程

由于model.train()已经被Regressor用来设置模型的状态,因此新增了一个train_model来作为训练过程方法。

python 复制代码
    def train_model(self, train_data, num_epochs, batch_size=10, eta=0.01):
        # 定义模型训练轮次epoch(外层循环)
        for epoch_id in range(num_epochs):
            # 在每轮迭代开始之前,对训练集数据进行样本乱序
            np.random.shuffle(train_data)
            # 对训练集数据进行拆分,batch_size设置为10
            mini_batches = [train_data[k:k + batch_size] for k in range(0, len(train_data), batch_size)]
            # 定义模型训练(内层循环)
            for iter_id, mini_batch in enumerate(mini_batches):
                x = np.array(mini_batch[:, :-1])  # 将当前批的房价影响因素的数据转换为np.array格式
                y = np.array(mini_batch[:, -1:])  # 将当前批的标签数据(真实房价)转换为np.array格式
                # 将np.array格式的数据转为张量tensor格式
                house_features = paddle.to_tensor(x, dtype='float32')
                prices = paddle.to_tensor(y, dtype='float32')

                # 前向计算
                predicts = model(house_features)

                # 计算损失,损失函数采用平方误差square_error_cost
                loss = F.square_error_cost(predicts, label=prices)
                avg_loss = paddle.mean(loss)
                if iter_id % 20 == 0:
                    print("epoch: {}, iter: {}, loss is: {}".format(epoch_id, iter_id, avg_loss.numpy()))

                # 反向传播,计算每层参数的梯度值
                avg_loss.backward()
                # 更新参数,根据设置好的学习率迭代一步
                opt.step()
                # 清空梯度变量,进行下一轮计算
                opt.clear_grad()

5.numpy和python构建深度学习模型和飞桨的比较

5.1 前向计算forward

python 复制代码
	# paddle
    def forward(self, inputs):
        x = self.fc(inputs)
        return x
    # numpy+python
    def forward(self, x):
      z = np.dot(x, self.w) + self.b
      return z

在Python310\Lib\site-packages\paddle\nn\functional\common.py路径下,可以看到paddle底层封装的方法和numpy+python是一致的

5.2.计算损失函数 loss

python 复制代码
	import paddle.nn.functional as F
	# 计算损失,损失函数采用平方误差square_error_cost
    loss = F.square_error_cost(predicts, label=prices)
    avg_loss = paddle.mean(loss)

    def loss(self, z, y):
        error = z - y
        num_samples = error.shape[0]
        cost = error * error
        cost = np.sum(cost) / num_samples
        return cost

在Python310\Lib\site-packages\paddle\nn\functional\loss.py路径下,可以看到paddle封装了均方误差square_error_cost的方法,该方法中并没有/N,因此还计算了avg_loss = paddle.mean(loss)

5.3.梯度计算gradient

python 复制代码
# paddle
	# 定义优化算法,采用随机梯度下降SGD
    # 学习率设置为0.01
    opt = paddle.optimizer.SGD(learning_rate=0.005, parameters=model.parameters())
	# 反向传播,计算每层参数的梯度值
	avg_loss.backward()
	# 更新参数,根据设置好的学习率迭代一步
	opt.step()
	# 清空梯度变量,进行下一轮计算
	opt.clear_grad()

# numpy + python
    def gradient(self, x, y):
        z = self.forward(x)
        N = x.shape[0]
        gradient_w = 1. / N * np.sum((z - y) * x, axis=0)
        gradient_w = gradient_w[:, np.newaxis]
        gradient_b = 1. / N * np.sum(z - y)
        return gradient_w, gradient_b

    def update(self, gradient_w, gradient_b, eta=0.01):
        self.w = self.w - eta * gradient_w
        self.b = self.b - eta * gradient_b

6.模型保存和推理

6.1.模型保存

python 复制代码
# 保存模型参数,文件名为LR_model.pdparams
paddle.save(model.state_dict(), 'LR_model.pdparams')
print("模型保存成功, 模型参数保存在LR_model.pdparams中")

6.2.模型推理

需要注意的是在模型推理后,需要做反向的归一化处理,这里会用到max_values, min_values,这里的最大最小值是用训练数据在归一化之前获取的,为了避免重新load data,因此在返回数据时,将这两个数据一并返回。

python 复制代码
#加载飞桨、NumPy和相关类库
import paddle
import numpy as np
from load_data import load_data
from train_paddle import Regressor
train_data, test_data, max_values, min_values = load_data()


def load_one_example():
    # 从测试集中随机选择一条作为推理数据
    # 从测试集中随机选择一条作为推理数据
    idx = np.random.randint(0, test_data.shape[0])
    idx = -10
    one_data, label = test_data[idx, :-1], test_data[idx, -1]
    # 将数据格式修改为[1,13]
    one_data = one_data.reshape([1, -1])

    return one_data, label


if __name__ == '__main__':
    # 将模型参数保存到指定路径中
    model_dict = paddle.load('LR_model.pdparams')
    model = Regressor()
    model.load_dict(model_dict)
    # 将模型状态修改为.eval
    model.eval()

    one_data, label = load_one_example()
    # 将数据格式转换为张量
    one_data = paddle.to_tensor(one_data,dtype="float32")
    predict = model(one_data)

    # 对推理结果进行后处理
    print(predict.numpy(), max_values[-1], min_values[-1])
    predict = predict * (max_values[-1] - min_values[-1]) + min_values[-1]
    # 对label数据进行后处理
    label = label * (max_values[-1] - min_values[-1]) + min_values[-1]

    print("Inference result is {}, the corresponding label is {}".format(predict.numpy(), label))

这里的label是原始数据,predict是预测数据。

6.3 用plt绘制test_data曲线图

python 复制代码
	N = y.shape[0]  # 数据点的数量
    # 由于 x 在这个例子中是为了生成 y 而存在的,并且我们实际上不会用它来绘图(因为我们只有 y 和 predict),
    # 我们可以简单地使用 range(N) 来作为 x 轴的索引,但这在绘制曲线图时通常不是必需的,因为 Matplotlib 会自动处理。
    # 然而,为了演示目的,我们将创建一个与 y 和 predict 相同长度的 x_index 数组。
    x_index = np.arange(N)
    # 绘制原始数据 y
    plt.plot(x_index, y.flatten(), color='blue', label='Original Data (y)', alpha=0.6, linewidth=1)
    # 绘制预测数据 predict
    plt.plot(x_index, predict.numpy().flatten(), color='red', label='Predicted Data (predict)', linewidth=2)
    # 添加标题和标签(注意:这里我们没有使用实际的 x 值作为横轴标签,因为只有 y 和 predict)
    plt.title('Comparison of Original Data and Predicted Data')
    plt.xlabel('Index')  # 或者你可以使用 'Sample Number'、'Data Point' 等标签
    plt.ylabel('Value')
    plt.legend()

    # 显示图表
    plt.grid(True)
    plt.show()

可以看到,整体预测得并不是很好。

7 使用飞桨高层API实现波士顿房价预测任务

如上代码使用飞桨的基础API完成了波士顿房价预测任务,是否有更加快捷地实现方法呢?答案是肯定的。下面使用飞桨高层API实现波士顿房价预测任务,代码实现如下:

python 复制代码
#加载飞桨、NumPy和相关类库
import paddle
from paddle.nn import Linear
paddle.set_default_dtype("float32")
import paddle.nn.functional as F
import numpy as np
import matplotlib.pyplot as plt

class Regressor(paddle.nn.Layer):

    # self代表类的实例自身
    def __init__(self):
        # 初始化父类中的一些参数
        super(Regressor, self).__init__()

        # 定义一层全连接层,输入维度是13,输出维度是1
        self.fc = Linear(in_features=13, out_features=1)

    # 网络的前向计算
    def forward(self, inputs):
        x = self.fc(inputs)
        return x

if __name__ == '__main__':
    # 使用飞桨高层API加载波士顿房价预测数据集,包括训练集和测试集
    # paddle.text:用于加载文本领域数据集。
    train_dataset = paddle.text.datasets.UCIHousing(mode='train')
    eval_dataset = paddle.text.datasets.UCIHousing(mode='test')
    # 模型训练
    model = paddle.Model(Regressor())
    # model.prepare:用于定义模型训练参数,如优化器paddle.optimizer.SGD、损失函数paddle.nn.MSELoss等。
    model.prepare(paddle.optimizer.SGD(learning_rate=0.005, parameters=model.parameters()),
                  paddle.nn.MSELoss())
    # model.fit:用于模型训练,并指定相关参数,如训练轮次epochs,批大小batch_size,可视化的模型方式verbose。
    model.fit(train_dataset, eval_dataset, epochs=10, batch_size=10, verbose=1)
    # model.evaluate:用于在测试集上评估模型的损失函数值和评价指标。由于本实践没有定义模型评价指标,因此只输出损失函数值。本实践使用均方误差损失(Mean Squared Error,MSE)。
    result = model.evaluate(eval_dataset, batch_size=10)
    print("result:", result)
    test_data = eval_dataset.data
    x = test_data[:, :-1]  # 所有行,列从第 0 列到倒数第 2 列
    # 提取最后一列作为 Y
    y = test_data[:, -1].reshape([-1, 1])
    # model.predict:用于模型推理。
    x = paddle.to_tensor(x, dtype="float32")
    result_pred = model.predict(x, batch_size=1) # result_pred是一个list,元素数目对应模型的输出数目
    result_pred = result_pred[0] # tuple,其中第一个值是array
    predict = np.vstack(result_pred)
    # y = y * (max_values[-1] - min_values[-1]) + min_values[-1]
    print("Inference result is {}, the corresponding label is {}".format(predict, y))

    N = y.shape[0]  # 数据点的数量
    # 由于 x 在这个例子中是为了生成 y 而存在的,并且我们实际上不会用它来绘图(因为我们只有 y 和 predict),
    # 我们可以简单地使用 range(N) 来作为 x 轴的索引,但这在绘制曲线图时通常不是必需的,因为 Matplotlib 会自动处理。
    # 然而,为了演示目的,我们将创建一个与 y 和 predict 相同长度的 x_index 数组。
    x_index = np.arange(N)
    # 绘制原始数据 y
    plt.plot(x_index, y.flatten(), color='blue', label='Original Data (y)', alpha=0.6, linewidth=1)
    # 绘制预测数据 predict
    plt.plot(x_index, predict.flatten(), color='red', label='Predicted Data (predict)', linewidth=2)
    # 添加标题和标签(注意:这里我们没有使用实际的 x 值作为横轴标签,因为只有 y 和 predict)
    plt.title('Comparison of Original Data and Predicted Data')
    plt.xlabel('Index')  # 或者你可以使用 'Sample Number'、'Data Point' 等标签
    plt.ylabel('Value')
    plt.legend()

    # 显示图表
    plt.grid(True)
    plt.show()

学习总结

1、numpy+python和paddle训练过程是相同的,paddle对前向计算、计算损失和反向传播梯度进行了封装,不再需要逐一编写代码,这就是使用飞桨框架的威力!但是通过numpy+python比较容易理解深度学习的过程。

2、模型推理时,需要将测试数据转换为张量x = paddle.to_tensor(x, dtype="float32")

3、将一行数据的x,y分离出来的代码

python 复制代码
x = test_data[:, :-1]
y = test_data[:, -1].reshape([-1, 1])
如果只需要分离部分记录:
x = test_data[0:3][:, :-1]
y = test_data[0:3][:, -1].reshape([-1, 1])
# 这是Numpy库的广播功能

4、用plt绘制曲线图时,需要用到y和predict,其中y的shap是(N,1),需要用y.flatten()降维到(N,)。predict还是张量,需要用predict.numpy()先转换为numpy格式,再通过flatten()降维。

5、使用飞桨高层API加载波士顿房价预测数据集是下载下来的,也可以指定数据集路径。

6、目前还没有定义模型评价指标,后续再学习

相关推荐
thinkMoreAndDoMore1 小时前
深度学习(2)-深度学习关键网络架构
人工智能·深度学习·机器学习
紫雾凌寒2 小时前
计算机视觉基础|从 OpenCV 到频域分析
深度学习·opencv·计算机视觉·傅里叶变换·频域分析
B站计算机毕业设计超人2 小时前
计算机毕业设计hadoop+spark旅游景点推荐 旅游推荐系统 旅游可视化 旅游爬虫 景区客流量预测 旅游大数据 大数据毕业设计
大数据·hadoop·爬虫·深度学习·机器学习·数据可视化·推荐算法
mengyoufengyu2 小时前
DeepSeek04-导出导入模型文件
人工智能·深度学习·deepseek
大数据追光猿3 小时前
【深度学习】Pytorch的深入理解和研究
人工智能·pytorch·python·深度学习·机器学习·ai编程
go54631584653 小时前
基于深度学习进行呼吸音检测的详细示例
人工智能·深度学习
真智AI4 小时前
使用AI创建流程图和图表的 3 种简单方法
人工智能·深度学习·神经网络·机器学习·自然语言处理·流程图
IT古董4 小时前
【深度学习】自然语言处理(NLP)-语音识别-WaveNet
深度学习·自然语言处理·语音识别
cufewxy20184 小时前
Transformer解析——(四)Decoder
人工智能·深度学习·transformer·decoder
9命怪猫11 小时前
DeepSeek底层揭秘——微调
人工智能·深度学习·神经网络·ai·大模型