示例:训练线性函数 y = 2 * x - 0.5
加载相关的库:
import paddle.fluid as fluid
import paddle
import numpy as np
import 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.77786
Pass:1, Cost:12.39720
Pass:2, Cost:4.35448
Pass:3, Cost:1.09518
Pass:4, Cost:0.35478
Pass:5, Cost:0.25425
Pass:6, Cost:0.23851
Pass:7, Cost:0.23030
Pass:8, Cost:0.22285
Pass: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 fluid
import paddle
import numpy as np
import 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.45874
Pass:20, Cost:227.31641
Pass:40, Cost:120.61336
Pass:60, Cost:54.93145
Pass:80, Cost:4.02490
Pass:100, Cost:0.78429
Pass:120, Cost:0.14203
Pass:140, Cost:0.09770
Pass:160, Cost:0.04135
Pass: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 paddle
import paddle.fluid as fluid
import paddle.fluid.dygraph as dygraph
from paddle.fluid.dygraph import Linear
import numpy as np
import os
import random
import 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,激活函数为relu
self.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 = False
place = fluid.CUDAPlace(0) if use_gpu else fluid.CPUPlace()
with fluid.dygraph.guard(place):
# 声明定义好的线性回归模型
model = Regressor()
# 开启模型训练模式状态
model.train()
# 定义优化算法,这里使用随机梯度下降SGD,学习率设置为0.1
optimizer = 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') # 正确的输出应该为212
label = 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
可以看到,预测值与真实值还是有些差距,不过已经比较接近了。