简 介: 初步分析了求解梯度的部分。详细的过程可以参见以下文献: pytorch自动求梯度—详解
关键词
: 自动求导, Paddle
01 自动微分
PaddlePaddle
的神经网络核心是自动微分,本篇文章主要为你介绍如何使用飞桨的自动微分,以及飞桨的自动微分机制,帮助你更好的使用飞桨进行训练。
一、背景
神经网络是由节点和节点间的相互连接组成的。网络中每层的每个节点代表一种特定的函数,来对输入进行计算。每个函数都是由不同参数(权重w
和偏置b
)组成。神经网络训练的过程,就是不断让这些函数的参数进行学习、优化,以能够更好的处理后面输入的过程。
为了让神经网络的判断更加准确,首先需要有衡量效果的工具,于是损失函数应运而生。如果你想要神经网络的效果好,那么就要让损失函数尽可能的小,于是深度学习引入了能够有效计算函数最小值的算法–梯度下降等优化算法,以及参数优化更新过程–反向传播。
- 前向传播是输入通过每一层节点计算后得到每层输出,上层输出又作为下一层的输入,最终达到输出层。然后通过损失函数计算得到
loss
值。 - 反向传播是通过
loss
值来指导前向节点中的函数参数如何改变,并更新每层中每个节点的参数,来让整个神经网络达到更小的loss
值。
自动微分机制就是让你只关注组网中的前向传播过程,然后飞桨框架来自动完成反向传播过程,从而来让你从繁琐的求导、求梯度的过程中解放出来。
二、应用Paddle自动微分机制
本文通过一个比较简单的模型来还原飞桨的自动微分过程。 本示例基于Paddle2.0
编写。
import paddle
from paddle.vision.models import vgg11
import paddle.nn.functional as F
import numpy as np
print(paddle.__version__)
2.2.1
1、定义模型
本案例首先定义网络。因为本示例着重展示如何使用飞桨进行自动微分,故组网部分不过多展开,直接使用高层API
中封装好的模型vgg11
。
然后随机初始化一个输入x
,和对应标签label
。
model = vgg11()
x = paddle.rand([1,3,224,224])
label = paddle.randint(0,1000)
label 取值为一个 0 - 1000之间的随机值:
Tensor(shape=[1], dtype=int64, place=CPUPlace, stop_gradient=True,
[578])
2、前向传播
然后将输入传入到模型中,进行前向传播过程。
predicts = model(x)
print(predicts)
Tensor(shape=[1, 1000], dtype=float32, place=CPUPlace, stop_gradient=False,
[[-5.86728096, 4.47268057, -0.95992291, 0.23020555, 1.00721347,
2.99545169, -2.75388002, -2.63786125, -3.75683165, 0.73450726,
0.85474956, -0.75945181, -5.11652756, 0.88256705, -4.56009960,
.....
1.58102369, -1.69987798, -1.06336939, -0.56181955, 0.71741229,
-3.75649190, 0.65584004, 0.91495293, -0.22558716, 0.40234384,
1.70077145, 1.04505992, -3.24514723, -0.23471458, -0.45503062]])
前向传播结束后,你就得到模型的预测结果predicts
,这时可以使用飞桨中的对应损失函数API
进行损失函数的计算。该例子中使用cross_entropy
来计算损失函数,来衡量模型的预测情况。
3、计算损失与反向传播
loss = F.cross_entropy(predicts, label)
print(loss)
Tensor(shape=[1], dtype=float32, place=CPUPlace, stop_gradient=False,
[12.34226704])
随后进行反向传播,在飞桨中你只需要调用backward()
即可自动化展开反向传播过程。各梯度保存在grad
属性中。
loss.backward()
4、优化器
然后来定义优化器,本例子中使用Adam
优化器,设置learning_rate
为0.001
,并把该模型的所有参数传入优化器中。
optim = paddle.optimizer.Adam(learning_rate=0.001, parameters=model.parameters())
最后通过step
来开始执行优化器,并进行模型参数的更新
optim.step()
通过以上步骤,你已经完成了一个神经网络前向传播、反向传播的所有过程。快自己动手试试吧!
三、自动微分使用说明
此章主要介绍飞桨中所有自动微分过程中会使用到的方法、属性等。属于第二部分的扩展阅读。
1、stop grandient属性
飞桨中的Tensor
有stop_gradient
属性,这个属性可以查看一个Tensor
是否计算并传播梯度。
- 如果为
True
,则该Tensor
不会计算梯度,并会阻绝Autograd
的梯度传播。 - 反之,则会计算梯度并传播梯度。用户自行创建的的
Tensor
,默认stop_gradient
为True
,即默认不计算梯度;模型参数的stop_gradient
默认都为False
,即默认计算梯度。
import paddle
a = paddle.to_tensor([1.0, 2.0, 3.0])
b = paddle.to_tensor([1.0, 2.0, 3.0], stop_gradient=False) # 将b设置为需要计算梯度的属性
print(a.stop_gradient)
print(b.stop_gradient)
True
False
a.stop_gradient = False
print(a.stop_gradient)
False
2、反向计算
接下来,本文用一个简单的计算图来了解如何调用backward()
函数。开始从当前Tensor
开始计算反向的神经网络,传导并计算计算图中Tensor
的梯度。
import paddle
x = paddle.to_tensor([1.0, 2.0, 3.0], stop_gradient=False)
y = paddle.to_tensor([4.0, 5.0, 6.0], stop_gradient=False)
z = x ** 2 + 4 * y
假设上面创建的x
和y
分别是神经网络中的参数,z
为神经网络的损失值loss
。
$${{\partial z} \over {\partial x}} = 2x,\space \space \space {{\partial z} \over {\partial y}} = 4$$
对z
调用backward()
,飞桨即可以自动计算x
和y
的梯度,并且将他们存进grad
属性中。
z.backward()
print("Tensor x's grad is: {}".format(x.grad))
print("Tensor y's grad is: {}".format(y.grad))
Tensor x's grad is: Tensor(shape=[3], dtype=float32, place=CPUPlace, stop_gradient=False,
[2., 4., 6.])
Tensor y's grad is: Tensor(shape=[3], dtype=float32, place=CPUPlace, stop_gradient=False,
[4., 4., 4.])
此外,飞桨默认会释放反向计算图。如果在backward()
之后继续添加OP
,需要将backward()
中的retain_graph
参数设置为True
,此时之前的反向计算图会保留。
温馨小提示:将其设置为False
会更加节省内存。因为他的默认值是False
,所以也可以直接不设置此参数。
import paddle
x = paddle.to_tensor([1.0, 2.0, 3.0], stop_gradient=False)
y = x + 3
y.backward(retain_graph=True) # 设置retain_graph为True,保留反向计算图
print("Tensor x's grad is: {}".format(x.grad))
Tensor x's grad is: Tensor(shape=[3], dtype=float32, place=CPUPlace, stop_gradient=False,
[1., 1., 1.])
3、梯度清除
因为backward()
会累积梯度,所以飞桨还提供了clear_grad()
函数来清除当前Tensor
的梯度。
import paddle
import numpy as np
x = np.ones([2, 2], np.float32)
inputs2 = []
for _ in range(10):
tmp = paddle.to_tensor(x)
tmp.stop_gradient = False
inputs2.append(tmp)
ret2 = paddle.add_n(inputs2)
loss2 = paddle.sum(ret2)
loss2.backward()
print("Before clear {}".format(loss2.gradient()))
loss2.clear_grad()
print("After clear {}".format(loss2.gradient()))
Before clear [1.]
After clear [0.]
(1)说明
如果不貂绒 backward()
则无法产生 gradient()
:
print("Before clear {}".format(loss2.gradient()))
print(ret2.gradient())
loss2.clear_grad()
ret2.clear_grad()
print("After clear {}".format(loss2.gradient()))
print(ret2.gradient())
只有貂绒 backward(),才可以获得对应的梯度:
Before clear [1.]
[[1. 1.]
[1. 1.]]
After clear [0.]
[[0. 0.]
[0. 0.]]
四、自动微分运行机制
本章主要介绍飞桨在实现反向传播进行自动微分计算时,内部是如何运行工作的。此部分为选读部分,更多是介绍飞桨内部实现机制,可以选择跳过,跳过不会影响你的正常使用。
1、自动微分
飞桨的自动微分是通过trace
的方式,记录前向OP
的执行,并自动创建反向var
和添加相应的反向OP
,然后来实现反向梯度计算的。
2、举例
下面本文用一些的例子,来模拟这个过程。
例子一:首先用一个比较简单的例子来让你了解整个过程。
(1)测试代码
import paddle
a = paddle.to_tensor(2.0, stop_gradient=False)
b = paddle.to_tensor(5.0, stop_gradient=True)
c = a * b
c.backward()
print("Tensor a's grad is: {}".format(a.grad))
print("Tensor b's grad is: {}".format(b.grad))
Tensor a's grad is: Tensor(shape=[1], dtype=float32, place=CPUPlace, stop_gradient=False,
[5.])
Tensor b's grad is: None
(2)分析
在上面代码中c.backward()
执行前,你可以理解整个计算图是这样的:
当创建Tensor
,Tensor
的stop_grad=False
时,会自动为此Tensor
创建一个反向Tensor
。在此例子中,a
的反向Tensor
就是a_grad
。在a_grad
中,会记录他的反向OP
,因为a
没有作为任何反向op
的输入,所以它的grad_op
为None
。
当执行OP
时,会自动创建反向OP
,不同的OP
创建反向OP
的方法不同,传的内容也不同。本文以这个乘法OP
为例:
- -乘法OP的反向OP,即MulBackward的输入是,正向OP的两个输入,以及正向OP的输出Tensor的反向Tensor。在此例子中就是,a、b、c_grad
- -乘法OP的反向OP,即MulBackward的输出是,正向OP的两个输入的反向Tensor(如果输入是stop_gradient=True,则即为None)。在此例子中就是,a_grad、None(b_grad)
- -乘法OP的反向OP,即MulBackward的grad_pending_ops是自动构建反向网络的时候,让这个反向op知道它下一个可以执行的反向op是哪一个,可以理解为反向网络中,一个反向op指向下一个反向op的边。
当c
通过乘法OP
被创建后,c
会创建一个反向Tensor
:c_grad,
他的grad_op
为该乘法OP
的反向OP
,即MulBackward
。
调用backward()
后,正式开始进行反向传播过程,开始自动计算微分。
例子二:用一个稍微复杂一点的例子让你深入了解这个过程。
import paddle
a = paddle.to_tensor(2.0, stop_gradient=False)
b = paddle.to_tensor(5.0, stop_gradient=False)
c = a * b
d = paddle.to_tensor(4.0, stop_gradient=False)
e = c * d
e.backward()
print("Tensor a's grad is: {}".format(a.grad))
print("Tensor b's grad is: {}".format(b.grad))
print("Tensor c's grad is: {}".format(c.grad))
print("Tensor d's grad is: {}".format(d.grad))
Tensor a's grad is: Tensor(shape=[1], dtype=float32, place=CPUPlace, stop_gradient=False,
[20.])
Tensor b's grad is: Tensor(shape=[1], dtype=float32, place=CPUPlace, stop_gradient=False,
[8.])
Tensor c's grad is: Tensor(shape=[1], dtype=float32, place=CPUPlace, stop_gradient=False,
[4.])
Tensor d's grad is: Tensor(shape=[1], dtype=float32, place=CPUPlace, stop_gradient=False,
[10.])
该例子的正向和反向图构建过程即:
※ 总 结 ※
初步分析了求解梯度的部分。详细的过程可以参见以下文献:
■ 相关文献链接:
● 相关图表链接:
★ 本文来自于CSDN文献:<
简 介: 初步分析了求解梯度的部分。详细的过程可以参见以下文献: pytorch自动求梯度—详解
关键词
: 自动求导, Paddle
01 自动微分
PaddlePaddle
的神经网络核心是自动微分,本篇文章主要为你介绍如何使用飞桨的自动微分,以及飞桨的自动微分机制,帮助你更好的使用飞桨进行训练。
一、背景
神经网络是由节点和节点间的相互连接组成的。网络中每层的每个节点代表一种特定的函数,来对输入进行计算。每个函数都是由不同参数(权重w
和偏置b
)组成。神经网络训练的过程,就是不断让这些函数的参数进行学习、优化,以能够更好的处理后面输入的过程。
为了让神经网络的判断更加准确,首先需要有衡量效果的工具,于是损失函数应运而生。如果你想要神经网络的效果好,那么就要让损失函数尽可能的小,于是深度学习引入了能够有效计算函数最小值的算法–梯度下降等优化算法,以及参数优化更新过程–反向传播。
- 前向传播是输入通过每一层节点计算后得到每层输出,上层输出又作为下一层的输入,最终达到输出层。然后通过损失函数计算得到
loss
值。 - 反向传播是通过
loss
值来指导前向节点中的函数参数如何改变,并更新每层中每个节点的参数,来让整个神经网络达到更小的loss
值。
自动微分机制就是让你只关注组网中的前向传播过程,然后飞桨框架来自动完成反向传播过程,从而来让你从繁琐的求导、求梯度的过程中解放出来。
二、应用Paddle自动微分机制
本文通过一个比较简单的模型来还原飞桨的自动微分过程。 本示例基于Paddle2.0
编写。
import paddle
from paddle.vision.models import vgg11
import paddle.nn.functional as F
import numpy as np
print(paddle.__version__)
2.2.1
1、定义模型
本案例首先定义网络。因为本示例着重展示如何使用飞桨进行自动微分,故组网部分不过多展开,直接使用高层API
中封装好的模型vgg11
。
然后随机初始化一个输入x
,和对应标签label
。
model = vgg11()
x = paddle.rand([1,3,224,224])
label = paddle.randint(0,1000)
label 取值为一个 0 - 1000之间的随机值:
Tensor(shape=[1], dtype=int64, place=CPUPlace, stop_gradient=True,
[578])
2、前向传播
然后将输入传入到模型中,进行前向传播过程。
predicts = model(x)
print(predicts)
Tensor(shape=[1, 1000], dtype=float32, place=CPUPlace, stop_gradient=False,
[[-5.86728096, 4.47268057, -0.95992291, 0.23020555, 1.00721347,
2.99545169, -2.75388002, -2.63786125, -3.75683165, 0.73450726,
0.85474956, -0.75945181, -5.11652756, 0.88256705, -4.56009960,
.....
1.58102369, -1.69987798, -1.06336939, -0.56181955, 0.71741229,
-3.75649190, 0.65584004, 0.91495293, -0.22558716, 0.40234384,
1.70077145, 1.04505992, -3.24514723, -0.23471458, -0.45503062]])
前向传播结束后,你就得到模型的预测结果predicts
,这时可以使用飞桨中的对应损失函数API
进行损失函数的计算。该例子中使用cross_entropy
来计算损失函数,来衡量模型的预测情况。
3、计算损失与反向传播
loss = F.cross_entropy(predicts, label)
print(loss)
Tensor(shape=[1], dtype=float32, place=CPUPlace, stop_gradient=False,
[12.34226704])
随后进行反向传播,在飞桨中你只需要调用backward()
即可自动化展开反向传播过程。各梯度保存在grad
属性中。
loss.backward()
4、优化器
然后来定义优化器,本例子中使用Adam
优化器,设置learning_rate
为0.001
,并把该模型的所有参数传入优化器中。
optim = paddle.optimizer.Adam(learning_rate=0.001, parameters=model.parameters())
最后通过step
来开始执行优化器,并进行模型参数的更新
optim.step()
通过以上步骤,你已经完成了一个神经网络前向传播、反向传播的所有过程。快自己动手试试吧!
三、自动微分使用说明
此章主要介绍飞桨中所有自动微分过程中会使用到的方法、属性等。属于第二部分的扩展阅读。
1、stop grandient属性
飞桨中的Tensor
有stop_gradient
属性,这个属性可以查看一个Tensor
是否计算并传播梯度。
- 如果为
True
,则该Tensor
不会计算梯度,并会阻绝Autograd
的梯度传播。 - 反之,则会计算梯度并传播梯度。用户自行创建的的
Tensor
,默认stop_gradient
为True
,即默认不计算梯度;模型参数的stop_gradient
默认都为False
,即默认计算梯度。
import paddle
a = paddle.to_tensor([1.0, 2.0, 3.0])
b = paddle.to_tensor([1.0, 2.0, 3.0], stop_gradient=False) # 将b设置为需要计算梯度的属性
print(a.stop_gradient)
print(b.stop_gradient)
True
False
a.stop_gradient = False
print(a.stop_gradient)
False
2、反向计算
接下来,本文用一个简单的计算图来了解如何调用backward()
函数。开始从当前Tensor
开始计算反向的神经网络,传导并计算计算图中Tensor
的梯度。
import paddle
x = paddle.to_tensor([1.0, 2.0, 3.0], stop_gradient=False)
y = paddle.to_tensor([4.0, 5.0, 6.0], stop_gradient=False)
z = x ** 2 + 4 * y
假设上面创建的x
和y
分别是神经网络中的参数,z
为神经网络的损失值loss
。
$${{\partial z} \over {\partial x}} = 2x,\space \space \space {{\partial z} \over {\partial y}} = 4$$
对z
调用backward()
,飞桨即可以自动计算x
和y
的梯度,并且将他们存进grad
属性中。
z.backward()
print("Tensor x's grad is: {}".format(x.grad))
print("Tensor y's grad is: {}".format(y.grad))
Tensor x's grad is: Tensor(shape=[3], dtype=float32, place=CPUPlace, stop_gradient=False,
[2., 4., 6.])
Tensor y's grad is: Tensor(shape=[3], dtype=float32, place=CPUPlace, stop_gradient=False,
[4., 4., 4.])
此外,飞桨默认会释放反向计算图。如果在backward()
之后继续添加OP
,需要将backward()
中的retain_graph
参数设置为True
,此时之前的反向计算图会保留。
温馨小提示:将其设置为False
会更加节省内存。因为他的默认值是False
,所以也可以直接不设置此参数。
import paddle
x = paddle.to_tensor([1.0, 2.0, 3.0], stop_gradient=False)
y = x + 3
y.backward(retain_graph=True) # 设置retain_graph为True,保留反向计算图
print("Tensor x's grad is: {}".format(x.grad))
Tensor x's grad is: Tensor(shape=[3], dtype=float32, place=CPUPlace, stop_gradient=False,
[1., 1., 1.])
3、梯度清除
因为backward()
会累积梯度,所以飞桨还提供了clear_grad()
函数来清除当前Tensor
的梯度。
import paddle
import numpy as np
x = np.ones([2, 2], np.float32)
inputs2 = []
for _ in range(10):
tmp = paddle.to_tensor(x)
tmp.stop_gradient = False
inputs2.append(tmp)
ret2 = paddle.add_n(inputs2)
loss2 = paddle.sum(ret2)
loss2.backward()
print("Before clear {}".format(loss2.gradient()))
loss2.clear_grad()
print("After clear {}".format(loss2.gradient()))
Before clear [1.]
After clear [0.]
(1)说明
如果不貂绒 backward()
则无法产生 gradient()
:
print("Before clear {}".format(loss2.gradient()))
print(ret2.gradient())
loss2.clear_grad()
ret2.clear_grad()
print("After clear {}".format(loss2.gradient()))
print(ret2.gradient())
只有貂绒 backward(),才可以获得对应的梯度:
Before clear [1.]
[[1. 1.]
[1. 1.]]
After clear [0.]
[[0. 0.]
[0. 0.]]
四、自动微分运行机制
本章主要介绍飞桨在实现反向传播进行自动微分计算时,内部是如何运行工作的。此部分为选读部分,更多是介绍飞桨内部实现机制,可以选择跳过,跳过不会影响你的正常使用。
1、自动微分
飞桨的自动微分是通过trace
的方式,记录前向OP
的执行,并自动创建反向var
和添加相应的反向OP
,然后来实现反向梯度计算的。
2、举例
下面本文用一些的例子,来模拟这个过程。
例子一:首先用一个比较简单的例子来让你了解整个过程。
(1)测试代码
import paddle
a = paddle.to_tensor(2.0, stop_gradient=False)
b = paddle.to_tensor(5.0, stop_gradient=True)
c = a * b
c.backward()
print("Tensor a's grad is: {}".format(a.grad))
print("Tensor b's grad is: {}".format(b.grad))
Tensor a's grad is: Tensor(shape=[1], dtype=float32, place=CPUPlace, stop_gradient=False,
[5.])
Tensor b's grad is: None
(2)分析
在上面代码中c.backward()
执行前,你可以理解整个计算图是这样的:
当创建Tensor
,Tensor
的stop_grad=False
时,会自动为此Tensor
创建一个反向Tensor
。在此例子中,a
的反向Tensor
就是a_grad
。在a_grad
中,会记录他的反向OP
,因为a
没有作为任何反向op
的输入,所以它的grad_op
为None
。
当执行OP
时,会自动创建反向OP
,不同的OP
创建反向OP
的方法不同,传的内容也不同。本文以这个乘法OP
为例:
- -乘法OP的反向OP,即MulBackward的输入是,正向OP的两个输入,以及正向OP的输出Tensor的反向Tensor。在此例子中就是,a、b、c_grad
- -乘法OP的反向OP,即MulBackward的输出是,正向OP的两个输入的反向Tensor(如果输入是stop_gradient=True,则即为None)。在此例子中就是,a_grad、None(b_grad)
- -乘法OP的反向OP,即MulBackward的grad_pending_ops是自动构建反向网络的时候,让这个反向op知道它下一个可以执行的反向op是哪一个,可以理解为反向网络中,一个反向op指向下一个反向op的边。
当c
通过乘法OP
被创建后,c
会创建一个反向Tensor
:c_grad,
他的grad_op
为该乘法OP
的反向OP
,即MulBackward
。
调用backward()
后,正式开始进行反向传播过程,开始自动计算微分。
例子二:用一个稍微复杂一点的例子让你深入了解这个过程。
import paddle
a = paddle.to_tensor(2.0, stop_gradient=False)
b = paddle.to_tensor(5.0, stop_gradient=False)
c = a * b
d = paddle.to_tensor(4.0, stop_gradient=False)
e = c * d
e.backward()
print("Tensor a's grad is: {}".format(a.grad))
print("Tensor b's grad is: {}".format(b.grad))
print("Tensor c's grad is: {}".format(c.grad))
print("Tensor d's grad is: {}".format(d.grad))
Tensor a's grad is: Tensor(shape=[1], dtype=float32, place=CPUPlace, stop_gradient=False,
[20.])
Tensor b's grad is: Tensor(shape=[1], dtype=float32, place=CPUPlace, stop_gradient=False,
[8.])
Tensor c's grad is: Tensor(shape=[1], dtype=float32, place=CPUPlace, stop_gradient=False,
[4.])
Tensor d's grad is: Tensor(shape=[1], dtype=float32, place=CPUPlace, stop_gradient=False,
[10.])
该例子的正向和反向图构建过程即:
※ 总 结 ※
初步分析了求解梯度的部分。详细的过程可以参见以下文献:
■ 相关文献链接:
● 相关图表链接: