示例:训练线性函数 y = 2 * x - 0.5
加载相关的库:
import paddle.fluid as fluidimport paddleimport numpy as npimport matplotlib.pyplot as plt%matplotlib inline
其中的一些说明:
- paddle.fluid:飞桨的主库,目前大部分的实用函数均在paddle.fluid包内。
定义一个单层线性全连接输出层的神经网络结构,不指定激活函数,损失函数采用均方差(square_error_cost)、优化方法使用随机梯度下降(SGDOptimizer),然后初始化参数。
这里输入数据本身只有一个参数,但为了演示输入多个参数对训练结果的影响,指定输入的参数为13个,输出的参数数量不变,仍然是1。
# 定义一个简单的线性网络x = fluid.layers.data(name='x', shape=[13], dtype='float32')hidden = fluid.layers.fc(input=x, size=100, act='relu')net = fluid.layers.fc(input=hidden, size=1, act=None)# 定义损失函数y = fluid.layers.data(name='y', shape=[1], dtype='float32')cost = fluid.layers.square_error_cost(input=net, label=y)avg_cost = fluid.layers.mean(cost)# 复制一个主程序,方便之后使用test_program = fluid.default_main_program().clone(for_test=True)# 定义优化方法optimizer = fluid.optimizer.SGDOptimizer(learning_rate=0.01)opts = optimizer.minimize(avg_cost)# 创建一个使用CPU的解释器place = fluid.CPUPlace()exe = fluid.Executor(place)# 进行参数初始化exe.run(fluid.default_startup_program())
准备数据,只在第一个参数赋值,以消除其他参数的影响:
# 定义训练和测试数据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],[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],[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],[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],[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],[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')y_data = np.array([[-0.5], [1.5], [3.5], [5.5], [7.5], [9.5]]).astype('float32')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
for pass_id in range(10):train_cost = exe.run(program=fluid.default_main_program(),feed={'x': x_data, 'y': y_data},fetch_list=[avg_cost])print("Pass:%d, Cost:%0.5f" % (pass_id, train_cost[0]))
输出训练结果:
Pass:0, Cost:27.77786Pass:1, Cost:12.39720Pass:2, Cost:4.35448Pass:3, Cost:1.09518Pass:4, Cost:0.35478Pass:5, Cost:0.25425Pass:6, Cost:0.23851Pass:7, Cost:0.23030Pass:8, Cost:0.22285Pass:9, Cost:0.21566
通过训练好的模型预测测试集的数据:
# 开始预测result = exe.run(program=test_program,feed={'x': test_data, 'y': np.array([[0.0]]).astype('float32')},fetch_list=[net])print("当x为6.0时,y为:%0.5f" % result[0][0][0])
输出:
当x为6.0时,y为:10.94277
可以看到,将6带入函数 y = 2 * x - 0.5,正确的输出结果应该是11.5,预测结果为 10.94277,虽然有些差距,但已经比较接近了。
示例:摄氏度转化为华氏度
摄氏度转华氏度 f = c * 1.8 + 32
目标:通过训练让模型能够实现从摄氏度到华氏度的转化。
常规方式
引入相关库:
import paddle.fluid as fluidimport paddleimport numpy as npimport matplotlib.pyplot as plt%matplotlib inline
定义一个简单的三层线性网络结构,包括两个隐藏层和一个输出层:
x = fluid.layers.data(name='x', shape=[1], dtype='float32')hidden1 = fluid.layers.fc(input=x, size=10, act='relu')hidden2 = fluid.layers.fc(input=hidden1, size=10, act='relu')net = fluid.layers.fc(input=hidden2, size=1, act=None)
定义损失函数,线性回归任务通常使用均方差(square_error_cost)作为其损失函数。
y = fluid.layers.data(name='y', shape=[1], dtype='float32') # 定义输入的形状和类型cost = fluid.layers.square_error_cost(input=net, label=y) # 计算每次训练的损失值avg_cost = fluid.layers.mean(cost) # 取损失的平均值
复制一个主程序,方便之后使用:
test_program = fluid.default_main_program().clone(for_test=True)
定义优化方法,这里可选的优化方法有很多,通过测试,在此模型中使用Adam效果比较明显。
通过learning_rate设置学习率,值为小于1的浮点数,可以多次尝试 0.1、0.01、0.001等,直到达到比较满意的效果。
optimizer = fluid.optimizer.AdamOptimizer(learning_rate=0.1)opts = optimizer.minimize(avg_cost)
创建一个使用CPU的解释器:
place = fluid.CPUPlace()exe = fluid.Executor(place)exe.run(fluid.default_startup_program()) # 进行参数初始化
初始化输入数据:
c = np.array([[-40], [-10], [0], [8], [15], [22], [38]]).astype('float32')f = np.array([[-40], [14], [32], [46], [59], [72], [100]]).astype('float32')
开始训练,训练次数设置为200次:
losses = []for pass_id in range(200):train_cost = exe.run(program=fluid.default_main_program(),feed={'x': c, 'y': f},fetch_list=[avg_cost])if pass_id % 20 == 0:print("Pass:%d, Cost:%0.5f" % (pass_id, train_cost[0]))
输出每次训练的损失值:
Pass:0, Cost:3719.45874Pass:20, Cost:227.31641Pass:40, Cost:120.61336Pass:60, Cost:54.93145Pass:80, Cost:4.02490Pass:100, Cost:0.78429Pass:120, Cost:0.14203Pass:140, Cost:0.09770Pass:160, Cost:0.04135Pass:180, Cost:0.02964
通过训练好的模型,预测未知的值:
test_data = np.array([[100.0]]).astype('float32') # 正确的输出应该为212# 开始预测result = exe.run(program=test_program,feed={'x': test_data, 'y': np.array([[0.0]]).astype('float32')},fetch_list=[net])print("当x为6.0时,y为:%0.5f" % result[0])
输出:
当x为6.0时,y为:213.89677
可以看到,输入6时,输出为213.89677,与真实值212相差不算太大,说明模型训练还是有效果的。
网络模型
上面的程序,比较松散,通常我们会将网络结构进行封装,方便使用。
import paddleimport paddle.fluid as fluidimport paddle.fluid.dygraph as dygraphfrom paddle.fluid.dygraph import Linearimport numpy as npimport osimport randomimport matplotlib.pyplot as plt%matplotlib inline
其中的一些说明:
- dygraph:动态图的类库。
- Linear:神经网络的全连接层函数,即包含所有输入权重相加和激活函数的基本神经元结构。在房价预测任务中,使用只有一层的神经网络(全连接层)来实现线性回归模型。
准备数据:
c = np.array([[-40], [-10], [0], [8], [15], [22], [38]]).astype('float32')f = np.array([[-40], [14], [32], [46], [59], [72], [100]]).astype('float32')
定义网络结构模型,只需要将上面的程序稍加改动即可:
class Regressor(fluid.dygraph.Layer):def __init__(self):super(Regressor, self).__init__()# 定义两层全连接隐含层,输出维度是10,激活函数为reluself.fc1 = Linear(input_dim=1, output_dim=10, act='relu') # 隐含层节点为10,可根据任务调整self.fc2 = Linear(input_dim=10, output_dim=10, act='relu')# 定义一层全连接输出层,输出维度是1,不使用激活函数self.fc3 = Linear(input_dim=10, output_dim=1, act=None)# 网络的前向计算函数def forward(self, inputs):outputs1 = self.fc1(inputs)outputs2 = self.fc2(outputs1)outputs_final = self.fc3(outputs2)return outputs_final
开始训练,将use_gpu设置为False指定在CPU上进行,若为True则是在GPU上进行。
通过save_dygraph将训练好的模型保存到文件,以便下次调用。
其他部分跟上面的程序差不多,只需稍加改动即可:
use_gpu = Falseplace = fluid.CUDAPlace(0) if use_gpu else fluid.CPUPlace()with fluid.dygraph.guard(place):# 声明定义好的线性回归模型model = Regressor()# 开启模型训练模式状态model.train()# 定义优化算法,这里使用随机梯度下降SGD,学习率设置为0.1optimizer = fluid.optimizer.AdamOptimizer(learning_rate=0.1, parameter_list=model.parameters())losses = []for pass_id in range(200):inputs = fluid.dygraph.to_variable(c)outputs = fluid.dygraph.to_variable(f)#前向计算的过程predict = model(inputs)#计算损失,取一个批次样本损失的平均值loss = fluid.layers.square_error_cost(predict, label=outputs)avg_loss = fluid.layers.mean(loss)losses.append(avg_loss.numpy())if pass_id % 20 == 0:print("num: {}, avg_loss: {}".format(pass_id, avg_loss.numpy()))avg_loss.backward()optimizer.minimize(avg_loss)model.clear_gradients()#保存模型参数fluid.save_dygraph(model.state_dict(), 'c2f')# 绘制损失图像plt.plot(losses)plt.show()
以guard函数指定运行训练的机器资源,表明在with作用域下的程序均执行在本机的CPU资源上。dygraph.guard表示在with作用域下的程序会以飞桨动态图的模式执行(实时执行)。
模型实例有两种模式状态:训练状态.train()和预测状态.eval()。训练时要执行正向计算和反向传播梯度两个过程,而预测时只需要执行正向计算。为模型指定运行状态,有两点原因:
(1)部分高级的算子(例如Drop out和Batch Normalization)在两个状态执行的逻辑不同。
(2)从性能和存储空间的考虑,预测状态时更节省内存,性能更好。
输出训练结果:
num: 0, avg_loss: [2592.1245]num: 20, avg_loss: [206.15218]num: 40, avg_loss: [48.400196]num: 60, avg_loss: [1.9362496]num: 80, avg_loss: [0.69255096]num: 100, avg_loss: [0.12821685]num: 120, avg_loss: [0.07444369]num: 140, avg_loss: [0.04959141]num: 160, avg_loss: [0.03748916]num: 180, avg_loss: [0.03136524]
同时打印出了损失函数的图像:
通过加载模型,预测未知数据:
with dygraph.guard(place):# 参数为保存模型参数的文件地址model_dict, _ = fluid.load_dygraph('c2f')model.load_dict(model_dict)model.eval()# 参数为数据集的文件地址test_data = np.array([[100.0]]).astype('float32') # 正确的输出应该为212label = 212.0# 将数据转为动态图的variable格式test_data = dygraph.to_variable(test_data)results = model(test_data)# 对结果做反归一化处理print("Inference result is {}, the corresponding label is {}".format(results.numpy(), label))
输出预测结果:
Inference result is [[210.89426]], the corresponding label is 212.0
可以看到,预测值与真实值还是有些差距,不过已经比较接近了。
