故事app怎么制作,信息流优化师工作内容,国外产品推广是怎么做的,wordpress博客防红跳转插件前向与反向 这里我们从 一次计算 开始比如 zf(x,y) 讨论若我们把任意对于tensor的计算都看为函数#xff08;如将 a*b#xff08;数值#xff09; 看为 mul(a,b)#xff09;#xff0c;那么都可以将其看为2个过程#xff1a;forward-前向#xff0c;backward-反向在pyto…前向与反向 这里我们从 一次计算 开始比如 zf(x,y) 讨论若我们把任意对于tensor的计算都看为函数如将 a*b数值 看为 mul(a,b)那么都可以将其看为2个过程forward-前向backward-反向在pytorch中我们通过继承torch.autograd.Function来实现这2个过程详细的用法和扩展参考https://pytorch.org/docs/stable/notes/extending.html【例子】比如我们要实现一个数值乘法z ( x ∗ y ) 2 (x*y)^2 (x∗y)2
前向
在前向过程中我们主要干的事情为1. 通过输入计算得到输出。2.保存反向传播求导数所需要的tensor到ctx在反向传播的时候会对应的传入【例子】1.为了反向传播求梯度保存xy因为我们知道 d z / d x 2 ∗ x ∗ y 2 , d z / d y 2 ∗ y ∗ x 2 dz/dx2*x*y^2, dz/dy2*y*x^2 dz/dx2∗x∗y2,dz/dy2∗y∗x2, 2.return ( x ∗ y ) 2 (x*y)^2 (x∗y)2
反向
在反向传播的时候我们干的事情就是将传入的导数梯度和我们在前向过程中保存的tensor进行加工最终返回每个输入变量的梯度【例子】此时反向时应该返回 2 ∗ x ∗ x ∗ y 与 2 ∗ x ∗ y ∗ y 2*x*x*y与2*x*y*y 2∗x∗x∗y与2∗x∗y∗y分别对应 d z / d x , d z / d y dz/dx, dz/dy dz/dx,dz/dy
计算图 那么对于一次计算的讨论完了现在我们来讨论多次计算即梯度导数是如何一步步的从最终的zf(a), ag(b), bw©… 一层层的 传回x的。那么在pytorch中其使用了图的数据结构在一开始 z x ∗ x ∗ y ∗ y zx*x*y*y zx∗x∗y∗y的例子中z会指向x与y方便反向传播求梯度导数现在若 w z ∗ z wz*z wz∗z关于z的函数那么w会指向z那么 d w / d x d w / d z ∗ d z / d x dw/dx dw/dz * dz/dx dw/dxdw/dz∗dz/dx
需要梯度require_grad
但是很多tensor在计算时是不需要梯度的而保存上面那种梯度图又很费空间pytroch默认你创建的tensor是不需要梯度的如当你使用线性层时实际上是 w ∗ x b w*xb w∗xb但是其实这里传入x是需要梯度的它又不需要学习而w与b是模型的参数是nn.Parameters所以他需要学习自然需要梯度而这里pytorch就使用一个bool标记来说明这个tensor需要梯度嘛若他需要梯度那么基于他的计算才会有指针指向它比如 z x ∗ y zx*y zx∗y若xy都require_gradFalse,则根本不会建立计算图若x的requires_gradTrue,则该计算会建立z-x的计算图
梯度函数grad_fn
当你进行了建立了计算图的计算比如x.requires_gradTrue, z x ∗ y zx*y zx∗y, 那么z.grad_fn就会有函数指针指向反向传播的计算这里就是这个 x ∗ y x*y x∗y在上图中一个节点虽然向回指向多个变量但其实对应函数指针其实是指向一个函数 x ∗ x ∗ y ∗ y x*x*y*y x∗x∗y∗y2个箭头对应的是2个返回值 ( d z / d x , d z / d y ) (dz/dx,dz/dy) (dz/dx,dz/dy)函数指针可以在运算完了后在tensor.grad_fn看到
梯度grad
当你在正向计算时构建完了上述的这样一个计算图你就可以对最终得到的tensor调用backward函数那么整个计算图就会从最后一个变量还是反向一步一步的将梯度传给每个需要保存梯度的tensor这时可以在tensor.grad中看到此时默认情况tensor.grad_fn会被清空。
torch.autograd.grad 大部分情况下我们都是得到loss然后loss.backward()模型的参数对应的每个tensor就会的到梯度这个时候opt.step()就会根据学习率优化参数但有时候我们需要手动求导可以使用 torch.autograd.grad函数
自变量input
x即对什么求导当然该tensor必须requires_gradTrue在因变量的同一梯度图的后继
因变量output
y即被求导的变量这里结合x相当于求dy/dx
加权grad_outputs
pytorch中求导的因变量必须是一个shape为[1]的tensor所以比如当backward时我们往往取loss.sum() 或者mean(), 那么这里y是个大小不定的tensor那么这个参数就是和y的shape一样先令g代表grad_outputs L ∑ g i j ∗ y i j L∑g_{ij}*y_{ij} L∑gij∗yij, 然后L在对x求导这里求和往往我们取gtorch.ones_like(y), 相当于y.sum()一般情况下不同batch之间的计算是独立的所以得到的y就算sum后每个x的得到的梯度其实是batch独立的但是batch_norm除外因为batch_norm不同batch的x会与整个batch的均值做运算 除非你手写batch_norm并将数据均值对应的tensor mu detach掉此时mu对于整个梯度图就是一个常数否则mu会指向不同batch的x导致每个x的得到的梯度其实不是batch独立的
输出
输出的shape和x一样即最终的L对于x每个位置的梯度这也同样解释了为什么必须要对y求和得L否则每个x中的每个位置其实对应整个tensory
二阶求导create_graph
那么若想二阶求导我们举个例子z f(x,y) , z1 ∂f/∂x z2 ∂z1 / ∂y在代码里其实x对应X[:,0], y对应X[:,1]也可以其他对应,其实还是一个tensor那么首先把torch.autograd.grad整个过程又看为一次新的计算在反向求导求z1时程序会按照上述过程一步一步反向传播而反向传播得到梯度时其实这里又可以形成新的梯度图z1处于整个新的梯度图顶端那么对应代码里使用函数参数create_graphTrue来告诉autograd我这次得到的tensor是需要产生梯度图的因为可能进一步求导那么在代码里我们相当于 z1 torch.autograd.grad(outputy,inputX,...)[0][:, 0]z2 torch.autograd.grad(outputz1,inputX,...)[0][:, 1]若你的输入X是将x与y合成一体的如xX[:,0], yX[:,1],那么你求导也只能将整体X作为输入再从答案中获得对应的列
若你直接在求导时inputX[:,i] 这里实际上你创建了一个新tensor X[:,i] - X, 而Z-X, 并未指向X[:,i], 故不行补充 画2阶导数的梯度图 将一阶导数的梯度图的每一条边作为新的一个节点例如s-[q]- e, q作为一个新节点指向e然后与其他同样从边引申出来的节点进行相乘链接导数链式法则一下是 Q ( x ∗ x ∗ y ) 2 Q(x*x*y)^2 Q(x∗x∗y)2的例子