当前位置: 首页> 游戏> 攻略 > 邢台seo外包_马鞍山网站建设哪里有_百度seo白皮书_百度网络推广怎么做

邢台seo外包_马鞍山网站建设哪里有_百度seo白皮书_百度网络推广怎么做

时间:2025/7/10 11:12:07来源:https://blog.csdn.net/m0_67656158/article/details/146974122 浏览次数:0次
邢台seo外包_马鞍山网站建设哪里有_百度seo白皮书_百度网络推广怎么做

🌈 个人主页:十二月的猫-CSDN博客
🔥 系列专栏: 🏀《PyTorch科研加速指南:即插即用式模块开发》CSDN博客

💪🏻 十二月的寒冬阻挡不了春天的脚步,十二点的黑夜遮蔽不住黎明的曙光 

目录

1. 前言

2. 动态图与静态图

静态图(如 TensorFlow 1.x)

动态图(如 PyTorch)

3. PyTorch中的计算图(动态图)

3.1 动态图的初步推导​编辑

3.2 动态图的叶子节点

3.2.1 retain_grad()​编辑

3.2.2 grad_fn

3.3  张量求导属性总结

4. 求解梯度方式

4.1 backward求解

4.2 autograd.grad()求解

5 二阶求导

5.1 计算图、中间变量梯度和高阶求导

5.1.1 计算图、中间变量梯度

5.1.2 高阶求导

5.2 二阶求导

6. 向量求导

7. 梯度清零

8. 总结 


1. 前言

正在更新中💹💹

🚨项目运行环境:

  • 👑《PyTorch科研加速指南:即插即用式模块开发》专栏持续更新中,未来最少文章数量为60篇。由于专栏刚刚建立,目前免费,后续将慢慢恢复原价至99.9🍉。
  • 👑《PyTorch科研加速指南:即插即用式模块开发》专栏主要针对零基础入门的小伙伴。不需要Python基础,不需要深度学习基础,只要你愿意学,这一个专栏将真正让你做到零基础入门。
  • 🔥每例项目都包括理论讲解、数据集、源代码
  • 平台:Window11
  • 语言环境:Python3.8
  • 运行环境1:PyCharm 2021.3
  • 运行环境2:Jupyter Notebook 7.3.2
  • 框架:PyTorch 2.5.1(CUDA11.8)

2. 动态图与静态图

        PyTorch是动态图,即计算图的搭建和运算是同时的,随时可以输出结果;而TensorFlow是静态图,即先搭建计算图,后将需要求解梯度的数值代入得到结果

静态图(如 TensorFlow 1.x)

优点

  • 性能优化:静态图在构建时就可以进行优化,例如内存优化和计算图融合,提高了运行效率。
  • 部署稳定性:由于计算图在运行前已完全定义,部署时的行为更可预测,适用于生产环境。

缺点

  • 开发复杂性:调试和开发更为复杂,因为需要先定义整个计算图,然后执行。这可能会使得错误难以定位和修复。
  • 灵活性差:一旦定义了计算图,动态改变图结构较困难,不适合需要动态变化的计算任务。

动态图(如 PyTorch)

优点

  • 灵活性高:图在运行时动态创建,使得模型结构可以根据输入动态调整,适合需要动态计算的任务。
  • 调试友好:因为图是在运行时动态生成的,调试时可以逐步执行和检查中间结果,使得错误定位更为直观。

缺点

  • 性能优化难度:动态图在运行时生成,因此可能难以进行一些图级优化,影响性能。
  • 部署挑战:相对于静态图,动态图在生产环境中的稳定性和优化程度可能不如静态图。

3. PyTorch中的计算图(动态图)

3.1 动态图的初步推导

        在pytorch的计算图里只有两种元素:数据(tensor)运算(operation)。运算包括:加减乘除、开方、幂指对、三角函数等可求导运算;数据可分为:叶子节点(leaf node)和非叶子节点

  • 结点表示数据 :如向量、矩阵、张量;
  • 边表示运算 :如加减乘除卷积等; 

上图是用计算图表示:

y=(x+w)∗(w+1)y=(x+w)∗(w+1)

其中呢,a=x+w ,b=w+1 , y=a∗b. (a和b是类似于中间变量的那种感觉。)

        Pytorch在计算的时候,就会把计算过程用上面那样的动态图存储起来。现在我们计算一下y关于w的梯度:

\frac{\partial y}{\partial w}=\frac{\partial y}{\partial a} \frac{\partial a}{\partial w}+\frac{\partial y}{\partial b} \frac{\partial b}{\partial w}=2 \times w+x+1=5

(上面的计算中,w=1,x=2)现在我们用Pytorch的代码来实现这个过程:

import torch
w = torch.tensor([1.],requires_grad = True)
x = torch.tensor([2.],requires_grad = True)a = w+x
b = w+1
y = a*by.backward()
print(w.grad)

结果为:

3.2 动态图的叶子节点

3.2.1 retain_grad()

        叶子节点是用户创建的节点,不依赖其它节点;它们表现出来的区别在于反向传播结束之后,非叶子节点的梯度会被释放掉,只保留叶子节点的梯度,这样就节省了内存。如果想要保留非叶子节点的梯度,可以使用retain_grad()方法。

        这个图中的叶子节点,是w和x,是整个计算图的根基。之所以用叶子节点的概念,是为了减少内存,在反向传播结束之后,非叶子节点的梯度会被释放掉 , 我们依然用上面的例子解释:

import torch
w = torch.tensor([1.],requires_grad = True)
x = torch.tensor([2.],requires_grad = True)a = w+x
b = w+1
y = a*by.backward()
print(w.is_leaf,x.is_leaf,a.is_leaf,b.is_leaf,y.is_leaf)
print(w.grad,x.grad,a.grad,b.grad,y.grad)

 运行结果是:

        可以看到只有x和w是叶子节点,然后反向传播计算完梯度后(.backward()之后),只有叶子节点的梯度保存下来了。当然也可以通过.retain_grad()来保留非任意节点的梯度值:

import torch
w = torch.tensor([1.],requires_grad = True)
x = torch.tensor([2.],requires_grad = True)a = w+x
a.retain_grad()
b = w+1
y = a*by.backward()
print(w.is_leaf,x.is_leaf,a.is_leaf,b.is_leaf,y.is_leaf)
print(w.grad,x.grad,a.grad,b.grad,y.grad)

运行结果:

3.2.2 grad_fn

        torch.tensor有一个属性grad_fn,grad_fn的作用是记录创建该张量时所用的函数,这个属性反向传播的时候会用到。例如在上面的例子中,y.grad_fn=MulBackward0,表示y是通过乘法得到的。所以求导的时候就是用乘法的求导法则。同样的,a.grad=AddBackward0表示a是通过加法得到的,使用加法的求导法则。

import torch
w = torch.tensor([1.],requires_grad = True)
x = torch.tensor([2.],requires_grad = True)a = w+x
a.retain_grad()
b = w+1
y = a*by.backward()
print(y.grad_fn)
print(a.grad_fn)
print(w.grad_fn)

 运行结果是:

3.3  张量求导属性总结

torch.tensor 具有如下属性:

  • 查看 是否可以求导requires_grad
  • 查看 运算名称 grad_fn
  • 查看 是否为叶子节点 is_leaf
  • 查看 导数值 grad

        requires_grad 是 PyTorch 中一个重要的属性,用于指定一个张量是否需要计算梯度。设置 requires_grad=True 使得该张量在执行操作时会记录操作历史,以便在调用反向传播 backward() 方法时计算梯度。通常,输入数据的张量设置为 requires_grad=True,以便在训练过程中自动进行反向传播和梯度更新。

        注意:非叶子结点使用requires_grad=True后也会计算梯度但是不会保存其梯度值grad。最后通过.grad找不到值,但是在计算中会计算其梯度。

        当我们想要对某个Tensor变量求梯度时,需要先指定requires_grad属性为True,指定方式主要有两种:

x = torch.tensor(1.).requires_grad_() # 第一种x = torch.tensor(1., requires_grad=True) # 第二种

4. 求解梯度方式

4.1 backward求解

x = torch.tensor(2., requires_grad=True)a = torch.add(x, 1)
b = torch.add(x, 2)
y = torch.mul(a, b)y.backward()
print(x.grad)
>>>tensor(7.)

结果:

print("requires_grad: ", x.requires_grad, a.requires_grad, b.requires_grad, y.requires_grad)
print("is_leaf: ", x.is_leaf, a.is_leaf, b.is_leaf, y.is_leaf)
print("grad: ", x.grad, a.grad, b.grad, y.grad)>>>requires_grad:  True True True True
>>>is_leaf:  True False False False
>>>grad:  tensor(7.) None None None

        使用backward()函数反向传播计算tensor的梯度时,并不计算所有tensor的梯度,而是只计算满足这几个条件的tensor的梯度:

  1. 类型为叶子节点
  2. requires_grad=True
  3. 依赖该tensor的所有tensor的requires_grad=True。所有满足条件的变量梯度会自动保存到对应的grad属性里

4.2 autograd.grad()求解

x = torch.tensor(2., requires_grad=True)a = torch.add(x, 1)
b = torch.add(x, 2)
y = torch.mul(a, b)grad = torch.autograd.grad(outputs=y, inputs=x)
print(grad[0])
>>>tensor(7.)

        因为指定了输出y,输入x,所以返回值就是 ∂y/∂x 这一梯度,完整的返回值其实是一个元组,保留第一个元素就行,后面元素是?

5 二阶求导

5.1 计算图、中间变量梯度和高阶求导

5.1.1 计算图、中间变量梯度

retain_graph=True:

  • 作用:在执行反向传播时,PyTorch 默认会释放计算图以节省内存。如果你需要对同一计算图进行多次反向传播(例如,计算多个梯度或进行一些需要重复反向传播的操作),你需要设置 retain_graph=True 来保留计算图。
  • 限制:即使你设置了 retain_graph=True,它只保留了原始计算图及其计算结果,但不包括梯度的进一步计算(即梯度本身的计算图)。

上面仅仅需要y关于z的梯度,因此用retain_graph=True保留计算图和中间变量梯度即可

5.1.2 高阶求导

create_graph=True:

  • 作用create_graph=True 使得在反向传播时创建一个新的计算图。这个新的计算图会包含原始计算图中的梯度的计算过程。这是为了支持更高阶的梯度计算,例如二阶导数。
  • 使用场景:当你需要对梯度进行进一步的反向传播时,比如计算梯度的梯度(即二阶导数)时,就需要使用 create_graph=True

5.2 二阶求导

再举一个复杂一点且 高阶求导的例子: z=x2y ,计算 ∂z/∂x,∂z/∂y,∂2z/∂2x ,假设给定 x=2,y=3

一阶导可以用backward(),代码如下:

x = torch.tensor(2., requires_grad=True)
y = torch.tensor(3., requires_grad=True)z = x * x * yz.backward()
print(x.grad, y.grad)
>>>tensor(12.) tensor(4.)

也可以用autograd.grad()实现:

x = torch.tensor(2.).requires_grad_()
y = torch.tensor(3.).requires_grad_()z = x * x * ygrad_x = torch.autograd.grad(outputs=z, inputs=x)
print(grad_x[0])
>>>tensor(12.)

autograd.grad():

  1. 在这里只会保留inputs里面的值的梯度,其他都会在计算结束后释放
  2. 整个计算图会被释放,同时不允许再次被生成(如果后面还用到计算图中的量则需要retain-graph去手动保存)

如下面代码:

x = torch.tensor(2.).requires_grad_()
y = torch.tensor(3.).requires_grad_()z = x * x * ygrad_x = torch.autograd.grad(outputs=z, inputs=x, retain_graph=True)
grad_y = torch.autograd.grad(outputs=z, inputs=y)print(grad_x[0], grad_y[0])
>>>tensor(12.) tensor(4.) 

        再来看如何求高阶导,理论上其实是上面的grad_x再对x求梯度,试一下看

x = torch.tensor(2.).requires_grad_()
y = torch.tensor(3.).requires_grad_()z = x * x * ygrad_x = torch.autograd.grad(outputs=z, inputs=x, retain_graph=True)
grad_xx = torch.autograd.grad(outputs=grad_x, inputs=x)print(grad_xx[0])
>>>RuntimeError: element 0 of tensors does not require grad and does not have a grad_fn

        报错了,虽然retain_graph=True保留了计算图和中间变量梯度, 但没有保存grad_x的运算方式,不能进一步高阶求导

# autograd.grad() + autograd.grad()
x = torch.tensor(2.).requires_grad_()
y = torch.tensor(3.).requires_grad_()z = x * x * ygrad_x = torch.autograd.grad(outputs=z, inputs=x, create_graph=True)
grad_xx = torch.autograd.grad(outputs=grad_x, inputs=x)print(grad_xx[0])
>>>tensor(6.)

        grad_xx这里也可以直接用backward(),相当于直接从 ∂z/∂x=2xy 开始回传

# autograd.grad() + backward()
x = torch.tensor(2.).requires_grad_()
y = torch.tensor(3.).requires_grad_()z = x * x * ygrad = torch.autograd.grad(outputs=z, inputs=x, create_graph=True)
grad[0].backward()print(x.grad)
>>>tensor(6.)

        也可以先用backward()然后对x.grad这个一阶导继续求导

# backward() + autograd.grad()
x = torch.tensor(2.).requires_grad_()
y = torch.tensor(3.).requires_grad_()z = x * x * yz.backward(create_graph=True)
grad_xx = torch.autograd.grad(outputs=x.grad, inputs=x)print(grad_xx[0])
>>>tensor(6.)

总结:

  1. 两种求导方法在二次求导中可以组合使用,但是注意backward的特点
  2. pyTorch使用backward()时默认会累加梯度

错误实例:

# backward() + backward()
x = torch.tensor(2.).requires_grad_()
y = torch.tensor(3.).requires_grad_()z = x * x * yz.backward(create_graph=True) # x.grad = 12
x.grad.backward()print(x.grad)
>>>tensor(18., grad_fn=<CopyBackwards>)

        发现了问题,结果不是6,而是18,发现第一次回传时输出x梯度是12。这是因为PyTorch使用backward()时默认会累加梯度,需要手动把前一次的梯度清零。修改后代码如下:

x = torch.tensor(2.).requires_grad_()
y = torch.tensor(3.).requires_grad_()z = x * x * yz.backward(create_graph=True)
x.grad.data.zero_()
x.grad.backward()print(x.grad)
>>>tensor(6., grad_fn=<CopyBackwards>)

6. 向量求导

x = torch.tensor([1., 2.]).requires_grad_()
y = x + 1y.backward()
print(x.grad)
>>>RuntimeError: grad can be implicitly created only for scalar outputs

        报错了,因为只能标量对标量,标量对向量求梯度, x 可以是标量或者向量,但 y 只能是标量;所以只需要先将 y 转变为标量,对分别求导没影响的就是求和。

x = torch.tensor([1., 2.]).requires_grad_()
y = x * xy.sum().backward()
print(x.grad)
>>>tensor([2., 4.])

核心思想:将向量转变为标量

另一种方式:

x = torch.tensor([1., 2.]).requires_grad_()
y = x * xy.backward(torch.ones_like(y))
print(x.grad)
>>>tensor([2., 4.])

        也可以使用autograd。上面和这里的torch.ones_like(y) 位置指的就是雅可比矩阵左乘的那个向量。

x = torch.tensor([1., 2.]).requires_grad_()
y = x * xgrad_x = torch.autograd.grad(outputs=y, inputs=x, grad_outputs=torch.ones_like(y))
print(grad_x[0])
>>>tensor([2., 4.])

或者

x = torch.tensor([1., 2.]).requires_grad_()
y = x * xgrad_x = torch.autograd.grad(outputs=y.sum(), inputs=x)
print(grad_x[0])
>>>tensor([2., 4.])

7. 梯度清零

Pytorch 的自动求导梯度不会自动清零,会累积,所以一次反向传播后需要手动清零。

x.grad.zero_()

而在神经网络中,我们只需要执行

optimizer.zero_grad()

8. 总结 

  【如果想学习更多深度学习文章,可以订阅一下热门专栏:】

  • 《小白读透AI原理》_十二月的猫的博客-CSDN博客
  • 《深度学习理论直觉三十讲》_十二月的猫的博客-CSDN博客
  • 《Python/PyTorch极简课》_十二月的猫的博客-CSDN博客

如果想要学习更多pyTorch/python编程的知识,大家可以点个关注并订阅,持续学习、天天进步你的点赞就是我更新的动力,如果觉得对你有帮助,辛苦友友点个赞,收个藏呀~~~

关键字:邢台seo外包_马鞍山网站建设哪里有_百度seo白皮书_百度网络推广怎么做

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

责任编辑: