上一节《从Numpy到PaddlePaddle1》,讲述了用PaddlePaddle实现多项式拟合,并手动完成梯度计算和反向传播算法。
对于小型神经网络来说,手动完成梯度计算和反向传播算法,并不是什么大问题。但是对于大型的复杂网络来说,就变得非常复杂。
PaddlePaddle有自动微分功能,本节介绍用自动微分功能改写前述程序。
飞桨中的Tensor对象在动态图中的数据结果定义如下:
class OpBase;
class VarBase {
Variable var_;
std::shared_ptr<VarBase> grad_var_;
std::vector<std::shared_ptr<OpBase>> grad_ops_;
};
-
var_: 用于存储运行时的Tensor信息。例如,当用户在Python端调用 tensor.numpy() 接口时会返回 var_ 中存储的Tensor数值。
-
grad_var_: 用于存储该变量对应的反向梯度变量。 VarBase 存储 grad_var_ 的目的是便于根据前向变量找到一次反向梯度变量,根据一次反向梯度变量找到二次反向梯度变量,依此类推。例如,当用户在Python端调用 tensor.gradient() 接口时会返回 grad_var_ ;若变量不需要计算梯度,则 grad_var_ 为空。若某个变量存在二次反向梯度,则用户可在Python端调用 tensor.gradient().gradient() 获得之(即返回C++端的grad_var_->grad_var_)。
-
grad_ops_: 用于存储以变量为输入的反向算子列表,仅对反向梯度变量有效,对于前向变量此域为空。grad_ops_ 的目的是在计算前向算子的同时,辅助构建反向计算图。
借助PaddlePaddle自动微分功能实现程序如下:
import paddle
import math
# 创建训练数据和标签值 y = 2*x + 10
x_data = paddle.to_tensor([[1.], [3.0], [5.0], [9.0], [10.0], [20.0]])
y_data = paddle.to_tensor([[12.], [16.0], [20.0], [28.0], [30.0], [50.0]])
linear = paddle.nn.Linear(in_features=1, out_features=1)
w_before_opt = linear.weight.numpy().item()
b_before_opt = linear.bias.numpy().item()
print(f"w before optimize: {w_before_opt}")
print(f"b before optimize: {b_before_opt}")
print(linear.parameters())
mse_loss = paddle.nn.MSELoss()
opt = paddle.optimizer.SGD(learning_rate=0.001, parameters = linear.parameters())
EPOCH = 10000
for i in range(EPOCH):
y_predict = linear(x_data)
loss = mse_loss(y_predict, y_data)
loss.backward()
opt.step()
opt.clear_grad()
if i%1000 == 0:
print(f"epoch {i}: loss {loss.numpy()}")
w_after_opt = linear.weight.numpy().item()
b_after_opt = linear.bias.numpy().item()
print(f"w after optimize: {w_after_opt}")
print(f"b after optimize: {b_after_opt}")
运行结果(y = 2*x + 10):
epoch 9000: loss [4.976395e-05]
w after optimize: 2.0004260540008545
b after optimize: 9.994553565979004
网友评论