示例:训练线性函数 y = 2 * x - 0.5

加载相关的库:

  1. import paddle.fluid as fluid
  2. import paddle
  3. import numpy as np
  4. import matplotlib.pyplot as plt
  5. %matplotlib inline

其中的一些说明:

  • paddle.fluid:飞桨的主库,目前大部分的实用函数均在paddle.fluid包内。

定义一个单层线性全连接输出层的神经网络结构,不指定激活函数,损失函数采用均方差(square_error_cost)、优化方法使用随机梯度下降(SGDOptimizer),然后初始化参数。

这里输入数据本身只有一个参数,但为了演示输入多个参数对训练结果的影响,指定输入的参数为13个,输出的参数数量不变,仍然是1。

  1. # 定义一个简单的线性网络
  2. x = fluid.layers.data(name='x', shape=[13], dtype='float32')
  3. hidden = fluid.layers.fc(input=x, size=100, act='relu')
  4. net = fluid.layers.fc(input=hidden, size=1, act=None)
  5. # 定义损失函数
  6. y = fluid.layers.data(name='y', shape=[1], dtype='float32')
  7. cost = fluid.layers.square_error_cost(input=net, label=y)
  8. avg_cost = fluid.layers.mean(cost)
  9. # 复制一个主程序,方便之后使用
  10. test_program = fluid.default_main_program().clone(for_test=True)
  11. # 定义优化方法
  12. optimizer = fluid.optimizer.SGDOptimizer(learning_rate=0.01)
  13. opts = optimizer.minimize(avg_cost)
  14. # 创建一个使用CPU的解释器
  15. place = fluid.CPUPlace()
  16. exe = fluid.Executor(place)
  17. # 进行参数初始化
  18. exe.run(fluid.default_startup_program())

准备数据,只在第一个参数赋值,以消除其他参数的影响:

  1. # 定义训练和测试数据
  2. x_data = np.array([[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
  3. [1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
  4. [2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
  5. [3.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
  6. [4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
  7. [5.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]]).astype('float32')
  8. y_data = np.array([[-0.5], [1.5], [3.5], [5.5], [7.5], [9.5]]).astype('float32')
  9. test_data = np.array([[6.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]]).astype('float32')

开始训练100个pass

  1. for pass_id in range(10):
  2. train_cost = exe.run(program=fluid.default_main_program(),
  3. feed={'x': x_data, 'y': y_data},
  4. fetch_list=[avg_cost])
  5. print("Pass:%d, Cost:%0.5f" % (pass_id, train_cost[0]))

输出训练结果:

  1. Pass:0, Cost:27.77786
  2. Pass:1, Cost:12.39720
  3. Pass:2, Cost:4.35448
  4. Pass:3, Cost:1.09518
  5. Pass:4, Cost:0.35478
  6. Pass:5, Cost:0.25425
  7. Pass:6, Cost:0.23851
  8. Pass:7, Cost:0.23030
  9. Pass:8, Cost:0.22285
  10. Pass:9, Cost:0.21566

通过训练好的模型预测测试集的数据:

  1. # 开始预测
  2. result = exe.run(program=test_program,
  3. feed={'x': test_data, 'y': np.array([[0.0]]).astype('float32')},
  4. fetch_list=[net])
  5. print("当x为6.0时,y为:%0.5f" % result[0][0][0])

输出:

  1. x6.0时,y为:10.94277

可以看到,将6带入函数 y = 2 * x - 0.5,正确的输出结果应该是11.5,预测结果为 10.94277,虽然有些差距,但已经比较接近了。

示例:摄氏度转化为华氏度

摄氏度转华氏度 f = c * 1.8 + 32

目标:通过训练让模型能够实现从摄氏度到华氏度的转化。

常规方式

引入相关库:

  1. import paddle.fluid as fluid
  2. import paddle
  3. import numpy as np
  4. import matplotlib.pyplot as plt
  5. %matplotlib inline

定义一个简单的三层线性网络结构,包括两个隐藏层和一个输出层:

  1. x = fluid.layers.data(name='x', shape=[1], dtype='float32')
  2. hidden1 = fluid.layers.fc(input=x, size=10, act='relu')
  3. hidden2 = fluid.layers.fc(input=hidden1, size=10, act='relu')
  4. net = fluid.layers.fc(input=hidden2, size=1, act=None)

定义损失函数,线性回归任务通常使用均方差(square_error_cost)作为其损失函数。

  1. y = fluid.layers.data(name='y', shape=[1], dtype='float32') # 定义输入的形状和类型
  2. cost = fluid.layers.square_error_cost(input=net, label=y) # 计算每次训练的损失值
  3. avg_cost = fluid.layers.mean(cost) # 取损失的平均值

复制一个主程序,方便之后使用:

  1. test_program = fluid.default_main_program().clone(for_test=True)

定义优化方法,这里可选的优化方法有很多,通过测试,在此模型中使用Adam效果比较明显。
通过learning_rate设置学习率,值为小于1的浮点数,可以多次尝试 0.1、0.01、0.001等,直到达到比较满意的效果。

  1. optimizer = fluid.optimizer.AdamOptimizer(learning_rate=0.1)
  2. opts = optimizer.minimize(avg_cost)

创建一个使用CPU的解释器:

  1. place = fluid.CPUPlace()
  2. exe = fluid.Executor(place)
  3. exe.run(fluid.default_startup_program()) # 进行参数初始化

初始化输入数据:

  1. c = np.array([[-40], [-10], [0], [8], [15], [22], [38]]).astype('float32')
  2. f = np.array([[-40], [14], [32], [46], [59], [72], [100]]).astype('float32')

开始训练,训练次数设置为200次:

  1. losses = []
  2. for pass_id in range(200):
  3. train_cost = exe.run(program=fluid.default_main_program(),
  4. feed={'x': c, 'y': f},
  5. fetch_list=[avg_cost])
  6. if pass_id % 20 == 0:
  7. print("Pass:%d, Cost:%0.5f" % (pass_id, train_cost[0]))

输出每次训练的损失值:

  1. Pass:0, Cost:3719.45874
  2. Pass:20, Cost:227.31641
  3. Pass:40, Cost:120.61336
  4. Pass:60, Cost:54.93145
  5. Pass:80, Cost:4.02490
  6. Pass:100, Cost:0.78429
  7. Pass:120, Cost:0.14203
  8. Pass:140, Cost:0.09770
  9. Pass:160, Cost:0.04135
  10. Pass:180, Cost:0.02964

通过训练好的模型,预测未知的值:

  1. test_data = np.array([[100.0]]).astype('float32') # 正确的输出应该为212
  2. # 开始预测
  3. result = exe.run(program=test_program,
  4. feed={'x': test_data, 'y': np.array([[0.0]]).astype('float32')},
  5. fetch_list=[net])
  6. print("当x为6.0时,y为:%0.5f" % result[0])

输出:

  1. x6.0时,y为:213.89677

可以看到,输入6时,输出为213.89677,与真实值212相差不算太大,说明模型训练还是有效果的。

网络模型

上面的程序,比较松散,通常我们会将网络结构进行封装,方便使用。

  1. import paddle
  2. import paddle.fluid as fluid
  3. import paddle.fluid.dygraph as dygraph
  4. from paddle.fluid.dygraph import Linear
  5. import numpy as np
  6. import os
  7. import random
  8. import matplotlib.pyplot as plt
  9. %matplotlib inline

其中的一些说明:

  • dygraph:动态图的类库。
  • Linear:神经网络的全连接层函数,即包含所有输入权重相加和激活函数的基本神经元结构。在房价预测任务中,使用只有一层的神经网络(全连接层)来实现线性回归模型。

准备数据:

  1. c = np.array([[-40], [-10], [0], [8], [15], [22], [38]]).astype('float32')
  2. f = np.array([[-40], [14], [32], [46], [59], [72], [100]]).astype('float32')

定义网络结构模型,只需要将上面的程序稍加改动即可:

  1. class Regressor(fluid.dygraph.Layer):
  2. def __init__(self):
  3. super(Regressor, self).__init__()
  4. # 定义两层全连接隐含层,输出维度是10,激活函数为relu
  5. self.fc1 = Linear(input_dim=1, output_dim=10, act='relu') # 隐含层节点为10,可根据任务调整
  6. self.fc2 = Linear(input_dim=10, output_dim=10, act='relu')
  7. # 定义一层全连接输出层,输出维度是1,不使用激活函数
  8. self.fc3 = Linear(input_dim=10, output_dim=1, act=None)
  9. # 网络的前向计算函数
  10. def forward(self, inputs):
  11. outputs1 = self.fc1(inputs)
  12. outputs2 = self.fc2(outputs1)
  13. outputs_final = self.fc3(outputs2)
  14. return outputs_final

开始训练,将use_gpu设置为False指定在CPU上进行,若为True则是在GPU上进行。
通过save_dygraph将训练好的模型保存到文件,以便下次调用。
其他部分跟上面的程序差不多,只需稍加改动即可:

  1. use_gpu = False
  2. place = fluid.CUDAPlace(0) if use_gpu else fluid.CPUPlace()
  3. with fluid.dygraph.guard(place):
  4. # 声明定义好的线性回归模型
  5. model = Regressor()
  6. # 开启模型训练模式状态
  7. model.train()
  8. # 定义优化算法,这里使用随机梯度下降SGD,学习率设置为0.1
  9. optimizer = fluid.optimizer.AdamOptimizer(learning_rate=0.1, parameter_list=model.parameters())
  10. losses = []
  11. for pass_id in range(200):
  12. inputs = fluid.dygraph.to_variable(c)
  13. outputs = fluid.dygraph.to_variable(f)
  14. #前向计算的过程
  15. predict = model(inputs)
  16. #计算损失,取一个批次样本损失的平均值
  17. loss = fluid.layers.square_error_cost(predict, label=outputs)
  18. avg_loss = fluid.layers.mean(loss)
  19. losses.append(avg_loss.numpy())
  20. if pass_id % 20 == 0:
  21. print("num: {}, avg_loss: {}".format(pass_id, avg_loss.numpy()))
  22. avg_loss.backward()
  23. optimizer.minimize(avg_loss)
  24. model.clear_gradients()
  25. #保存模型参数
  26. fluid.save_dygraph(model.state_dict(), 'c2f')
  27. # 绘制损失图像
  28. plt.plot(losses)
  29. plt.show()

guard函数指定运行训练的机器资源,表明在with作用域下的程序均执行在本机的CPU资源上。dygraph.guard表示在with作用域下的程序会以飞桨动态图的模式执行(实时执行)。

模型实例有两种模式状态:训练状态.train()和预测状态.eval()。训练时要执行正向计算和反向传播梯度两个过程,而预测时只需要执行正向计算。为模型指定运行状态,有两点原因:
(1)部分高级的算子(例如Drop out和Batch Normalization)在两个状态执行的逻辑不同。
(2)从性能和存储空间的考虑,预测状态时更节省内存,性能更好。

输出训练结果:

  1. num: 0, avg_loss: [2592.1245]
  2. num: 20, avg_loss: [206.15218]
  3. num: 40, avg_loss: [48.400196]
  4. num: 60, avg_loss: [1.9362496]
  5. num: 80, avg_loss: [0.69255096]
  6. num: 100, avg_loss: [0.12821685]
  7. num: 120, avg_loss: [0.07444369]
  8. num: 140, avg_loss: [0.04959141]
  9. num: 160, avg_loss: [0.03748916]
  10. num: 180, avg_loss: [0.03136524]

同时打印出了损失函数的图像:
📃 线性回归模型 - 图1
通过加载模型,预测未知数据:

  1. with dygraph.guard(place):
  2. # 参数为保存模型参数的文件地址
  3. model_dict, _ = fluid.load_dygraph('c2f')
  4. model.load_dict(model_dict)
  5. model.eval()
  6. # 参数为数据集的文件地址
  7. test_data = np.array([[100.0]]).astype('float32') # 正确的输出应该为212
  8. label = 212.0
  9. # 将数据转为动态图的variable格式
  10. test_data = dygraph.to_variable(test_data)
  11. results = model(test_data)
  12. # 对结果做反归一化处理
  13. print("Inference result is {}, the corresponding label is {}".format(results.numpy(), label))

输出预测结果:

  1. Inference result is [[210.89426]], the corresponding label is 212.0

可以看到,预测值与真实值还是有些差距,不过已经比较接近了。