李沐- - -动手学深度学习笔记(第二章)
预备知识
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.])
第二章相关知识完结,整个预备知识部分完结!!
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐

所有评论(0)