预备知识
2.4微积分
标量导数
亚导数
将导数扩展到不可微的函数,例如 y=|x| ,在x=0处斜率不唯一
将|x|导数分为三种情况:
梯度
我们可以连结一个多元函数对其所有变量的偏导数,以得到该函数的梯度(gradient)向量。
将导数扩展到向量
其中,当x是标量,y是标量,求导也为标量,即Y1
当y是向量,x是标量,求导为向量,即Y3
当y是标量,x是向量,求导为向量,即Y2
当y是向量, x是向量,求导为矩阵,即Y4
以Y2举个例子:
其中,x 为列向量,对其求的导数是Y2为行向量,上图圆形线为等高线,在(x1,x2)切线上,做一个正交方向出来,方向值为(2,4),它和梯度一样,梯度和等高线是正交的,而且指向值变化最大的方向。
举个例子:
其中,sum(x)求导,则为全1的向量;<u,v> 内积;
以Y3举个例子:
以Y4举个例子:
其中,I为对角矩阵;
假设x为n维向量,在微分多元函数时经常使用以下规则:
2.5自动求导
链式法则
标量链式法则
向量链式法则
其中, (1,)(1,n)标量Xn维向量=n维向量;(1,k)(k,n)k维向量Xk*n矩阵=n维量;(m,k)(k,n)m*k矩阵Xk*n矩阵=m*n矩阵。
举个例子:
自动求导
自动求导计算一个函数在指定值上的导数
其中,符号求导:对给定的一个函数,可以将其导数求出来;
数值求导:任意给一个函数,不需要知道函数样子,通过数值去拟合导数。
计算图
将代码分解成操作子,即将其一步一步展开
将计算表示成一个无环图
计算图实际上等价于用链式法则求导的过程,上图每个圈可以表示一个操作或输入
显示构造:Tensorflow/Theano/MXNet
隐式构造:PyTorch/MXNet
自动求导的两种模式
反向累积
其中,先计算z关于b的导数,将b带入再计算z关于a的导数,最后计算z关于w的导数。
总结:
前向:执行图,存储中间结果
反向:从相反方向去执行图,去除不需要的枝(一些值的导数不需要的话),在计算中,需要拿来中间结果去用。
假设我们想对函数y=2,关于列向量x求导
import torch
x=torch.arange(4.0)
x
在我们计算y关于x的梯度之前,我们需要一个地方来存储梯度。
x.requires_grad_(True)#等价于‘x=torch.arange(4.0,require_grad=True)’
x.grad #默认值是None
将 x
的 requires_grad
属性设置为 True
,这意味着 x
将参与梯度计算
现在来计算y
y=2*torch.dot(x,x)
y
输出结果:tensor(28., grad_fn=<MulBackward0>)
通过调用反向传播函数来自动计算y关于x的每个分量的梯度
y.backward()
x.grad
输出结果:tensor([ 0., 4., 8., 12.])
x.grad==4*x
输出结果:tensor([True, True, True, True])
计算x的另一个函数
#在默认情况下,pytorch会累积梯度,我们需要清除之前的值
x.grad.zero_()
y=x.sum()
y.backward()
x.grad
输出结果:tensor([1., 1., 1., 1.])
深度学习中,我们的目的不是计算微分矩阵,而是批量中每个样本单独计算的偏导数之和。
#对非标量调用‘backward’需要传入一个‘gradient’参数,该参数指定微分函数
x.grad.zero_()
y=x*x
#等价于y.backward(torch.ones(len(x)))
y.sum().backward()
x.grad
输出结果:tensor([0., 2., 4., 6.])
y = x * x
:对于 x = [0., 1., 2., 3.]
,y
变成 [0^2, 1^2, 2^2, 3^2] = [0., 1., 4.,9]
y.sum()
是对 y
的所有元素求和,结果为 0 + 1 + 4 + 9 = 14
。调用 backward()
来计算 y
对 x
的梯度。对 y = x * x
求导数,∂y/∂x = 2 * x
,所以 x.grad
应该为 2 * x
。
x.grad
:反向传播后,x.grad
会存储 2 * x
的结果。
将某些计算移动到记录的计算图之外
x.grad.zero_()
y=x*x
u=y.detach()
z=u*x
z.sum().backward()
x.grad==u
使用 detach()
从计算图中分离出张量 y
,这意味着 u
不会参与梯度计算。u
的值与 y
相同,但它不具有与 x
相关的梯度信息。
输出结果:tensor([True, True, True, True])
x.grad.zero_()
y.sum().backward()
x.grad==2*x
输出结果:tensor([True, True, True, True])
即使构建函数的计算图需要通过Python控制流(例如,条件、循环或任意函数调用),我们任然可以计算得到的变量的梯度。
import torch
# 定义函数 f
def f(a):
b = a * 2
while b.norm() < 1000:
b = b * 2
if b.sum() > 0:
c = b
else:
c = 100 * b
return c
# 创建一个随机张量 a
a = torch.randn(3, requires_grad=True) # 假设 a 是 1D 张量
# 调用函数 f
d = f(a)
# 如果 d 是非标量,先对其求和,反向传播只能用于标量张量。
d.sum().backward()
# 打印梯度
print(a.grad)
输出结果:tensor([2048., 2048., 2048.])
第二章相关知识完结,整个预备知识部分完结!!