dell公司网站设计特色,c#网站开发案例源码,手机优化对手机有影响吗,改号宝网站搭建自动微分
正如上节所说#xff0c;求导是几乎所有深度学习优化算法的关键步骤。 虽然求导的计算很简单#xff0c;只需要一些基本的微积分。 但对于复杂的模型#xff0c;手工进行更新是一件很痛苦的事情#xff08;而且经常容易出错#xff09;。
深度学习框架通过自动…自动微分
正如上节所说求导是几乎所有深度学习优化算法的关键步骤。 虽然求导的计算很简单只需要一些基本的微积分。 但对于复杂的模型手工进行更新是一件很痛苦的事情而且经常容易出错。
深度学习框架通过自动计算导数即自动微分automatic differentiation来加快求导。 实际中根据设计好的模型系统会构建一个计算图computational graph 来跟踪计算是哪些数据通过哪些操作组合起来产生输出。 自动微分使系统能够随后反向传播梯度。 这里反向传播backpropagate意味着跟踪整个计算图填充关于每个参数的偏导数。
一个简单的例子
作为一个演示例子(假设我们想对函数 y 2 x ⊤ x y2\mathbf{x}^{\top}\mathbf{x} y2x⊤x关于列向量 x \mathbf{x} x求导)。 首先我们创建变量x并为其分配一个初始值。
import torchx torch.arange(4.0)
x[在我们计算 y y y关于 x \mathbf{x} x的梯度之前需要一个地方来存储梯度。] 重要的是我们不会在每次对一个参数求导时都分配新的内存。 因为我们经常会成千上万次地更新相同的参数每次都分配新的内存可能很快就会将内存耗尽。 注意一个标量函数关于向量 x \mathbf{x} x的梯度是向量并且与 x \mathbf{x} x具有相同的形状。
x.requires_grad_(True) # 等价于xtorch.arange(4.0,requires_gradTrue)
x.grad # 默认值是None在 PyTorch 里requires_grad 是张量Tensor的一个属性用于表明是否要对该张量进行梯度计算。若 requires_grad 为 True那么在后续的计算中PyTorch 会自动追踪与该张量相关的所有运算并且可以通过反向传播算法计算其梯度。 (现在计算 y y y。)
y 2 * torch.dot(x, x)
y在 PyTorch 里torch.dot 函数用于计算两个一维张量也就是向量的点积。点积的计算规则是将两个向量对应位置的元素相乘然后把这些乘积相加。在代码里torch.dot(x, x) 计算的是向量 x 与自身的点积。假设 x [x₁, x₂, x₃, ..., xₙ]那么 torch.dot(x, x) 的结果就是 x 1 2 x 2 2 x 3 2 . . . x n 2 x_1^2 x_2^2 x_3^2 ... x_n^2 x12x22x32...xn2 grad_fnMulBackward0 表明 y 是经过乘法操作得到的并且可以进行反向传播来计算梯度。 x是一个长度为4的向量计算x和x的点积得到了我们赋值给y的标量输出。接下来[通过调用反向传播函数来自动计算y关于x每个分量的梯度]并打印这些梯度。
y.backward()#计算并存储 y 关于 x 的梯度
x.grad#访问梯度值y.backward() 这行代码的作用是执行反向传播算法。反向传播的核心目的是计算标量 y 关于所有具有requires_gradTrue 的输入张量这里就是 x的梯度。它会根据链式法则从 y 开始逆向计算每个中间变量和输入变量的梯度并将这些梯度存储在相应张量的 grad 属性中。 x.grad 用于获取张量 x 的梯度。在调用 y.backward() 之前x.grad 的值通常为 None。调用 y.backward() 之后PyTorch 会计算并存储 y 关于 x 的梯度此时通过 x.grad 就可以访问到这些梯度值。 函数 y 2 x ⊤ x y2\mathbf{x}^{\top}\mathbf{x} y2x⊤x关于 x \mathbf{x} x的梯度应为 4 x 4\mathbf{x} 4x。 让我们快速验证这个梯度是否计算正确。
x.grad 4 * x[现在计算x的另一个函数。]
# 在默认情况下PyTorch会累积梯度我们需要清除之前的值
x.grad.zero_()
y x.sum()
y.backward()
x.gradx.grad.zero_() 在 PyTorch 里当我们进行多次反向传播时梯度会累积在 x.grad 中。x.grad.zero_() 这行代码是一个原地操作其作用是将 x 的梯度清零以避免之前的梯度对当前计算产生影响。 非标量变量的反向传播
当y不是标量时向量y关于向量x的导数的最自然解释是一个矩阵。 对于高阶和高维的y和x求导的结果可以是一个高阶张量。
然而虽然这些更奇特的对象确实出现在高级机器学习中包括[深度学习中] 但当调用向量的反向计算时我们通常会试图计算一批训练样本中每个组成部分的损失函数的导数。 这里(我们的目的不是计算微分矩阵而是单独计算批量中每个样本的偏导数之和。)
# 对非标量调用backward需要传入一个gradient参数该参数指定微分函数关于self的梯度。
# 本例只想求偏导数的和所以传递一个1的梯度是合适的
x.grad.zero_()
y x * x
# 等价于y.backward(torch.ones(len(x)))
y.sum().backward()
x.grad分离计算
有时我们希望[将某些计算移动到记录的计算图之外]。 例如假设y是作为x的函数计算的而z则是作为y和x的函数计算的。 想象一下我们想计算z关于x的梯度但由于某种原因希望将y视为一个常数 并且只考虑到x在y被计算后发挥的作用。
这里可以分离y来返回一个新变量u该变量与y具有相同的值 但丢弃计算图中如何计算y的任何信息。 换句话说梯度不会向后流经u到x。 因此下面的反向传播函数计算zu*x关于x的偏导数同时将u作为常数处理 而不是zx*x*x关于x的偏导数。
x.grad.zero_()
y x * x
u y.detach()
z u * xz.sum().backward()
x.grad udetach() 方法用于从计算图中分离出一个张量。调用 y.detach() 会返回一个新的张量 u这个新张量和 y 具有相同的数据但它不会再与原计算图产生关联即不会再参与反向传播。也就是说在后续的计算中PyTorch 不会追踪 u 的梯度。 由于记录了y的计算结果我们可以随后在y上调用反向传播 得到yx*x关于的x的导数即2*x。
x.grad.zero_()
y.sum().backward()
x.grad 2 * xPython控制流的梯度计算
使用自动微分的一个好处是 [即使构建函数的计算图需要通过Python控制流例如条件、循环或任意函数调用我们仍然可以计算得到的变量的梯度]。 在下面的代码中while循环的迭代次数和if语句的结果都取决于输入a的值。
def f(a):b a * 2while b.norm() 1000:b b * 2if b.sum() 0:c belse:c 100 * breturn cb.norm() 若不指定参数默认计算的是 2 - 范数也被称作欧几里得范数。对于向量而言2 - 范数是向量各个元素平方和的平方根对于矩阵来说2 - 范数是矩阵的最大奇异值 让我们计算梯度。
a torch.randn(size(), requires_gradTrue)
d f(a)
d.backward()torch.randn 是 PyTorch 里用于生成服从标准正态分布均值为 0标准差为 1的随机数的函数。其语法格式通常为 torch.randn(*size, outNone, dtypeNone, layouttorch.strided, deviceNone, requires_gradFalse)其中 size 参数用于指定生成张量的形状。 我们现在可以分析上面定义的f函数。 请注意它在其输入a中是分段线性的。 换言之对于任何a存在某个常量标量k使得f(a)k*a其中k的值取决于输入a因此可以用d/a验证梯度是否正确。
a.grad d / a