pytorch基本运算-梯度运算:requires_grad_(True)和backward()

引言

前序学习进程中,已经对pytorch基本运算中的求导进行了基础讨论,相关文章链接为:

导数运算pytorch基本运算-导数和f-string-CSDN博客

实际上,求导是微分的进一步计算,要想求导的前一步其实是计算微分:

导数表达式:
f ′ ( x ) 或 d y d x f^{'}(x) 或 \frac{dy}{dx} f(x)dxdy
​微分表达式:
f ′ ( x ) d x 或 d y = f ′ ( x ) d x f^{'}(x) dx 或 {dy=f^{'}(x) dx} f(x)dxdy=f(x)dx
导数是某一点处的变化率,微分是某一点附近的变化量。
如果一个函数在多个点进行导数求解,或者说子安多维度上进行导数计算,实际上就是在求梯度。

pytorch自动微分获取梯度

为完整展示pytorch的梯度计算功能,将测试分为以下部分。

初始定义

首先是引入模块,完成变量定义:

# 导入模块
import torch
# 定义变量
x=torch.arange(3.0)
print('x=',x)

这里的输出结果是:

x= tensor([0., 1., 2.])

需要说明的是,因为pytorch默认对浮点数进行求导,所以定义变量的时候,pyorch.arange()使用了3.0而不是整数3。
紧接着,需要对变量执行梯度运算。

梯度运算标定

梯度运算标定的目的是,声明要对x进行梯度运算。任何没有经过提前标定的量,都不能正常执行梯度运算。

# 标记需要对x进行梯度计算
z=x.requires_grad_(True)
print('z=',z)

梯度标定使用requires_grad_(True),就像对话一样,需要求梯度_(需要)。
代码运行的效果为:

z= tensor([0., 1., 2.], requires_grad=True)

下一步是定义一个函数。

函数定义

这里定义一个简单函数:
f ( x ) = 2 x 2 f(x)=2x^{2} f(x)=2x2
具体定义代码为:

# 点乘定义
m=2*torch.dot(x,x)
print('m=',m)

计算微分对函数开展才有意义,所以必须定义函数,这里只是一个示例,也可以是其他函数。
torch.dot()函数的计算规则为:对位相乘然后求和。
代码运行效果为:

m= tensor(10., grad_fn=)

这里输出了两个部分:
第一部分是10,就是元素对位相乘后求和的效果(2X0X0+2X1X1+2X2X2=10)。
第二部分是grad_fn=,grad_fn的意思是grad_function,就是求导函数的意思,后面的MulBackward0是对求导函数的具体定义。
MulBackward0 表示这是一个乘法操作的梯度函数,具体拆开来:multiplication-backward,字面意思解释:乘法-反向传播。
这就是pytorch自动微分的核心机制:它可以自动测算求导函数的类型,比如这是一个自变量相乘的函数,并且指出要用哪种方法,比如这里要用反向传播法。
到这一步还无法计算微分,只是通过输出效果知道用反向传播方法计算微分,然后就是正式使用反向传播方法计算微分。

梯度计算

微分计算使用的代码为:

# 执行梯度运算
n=m.backward()
k=x.grad
print('n=',n)
print('k=',k)

这里用了两步,第一步是定义对函数m调用backward方法求倒数,然后具体是对x求导数,所获得计算结果为:

n= None
k= tensor([0., 4., 8.])

n对应的其实是方法定义,k才是具体的对x的求导效果。

实际上到这一步,如何用pytorch直接计算导数已经非常清晰:先要标定梯度计算的变量,然后要对函数声明梯度计算的方法,最后直接计算梯度。完整代码为:

# 导入模块
import torch
# 定义变量
x=torch.arange(3.0)
print('x=',x)
# 标记需要对x进行梯度计算
z=x.requires_grad_(True)
print('z=',z)
# 点乘定义
m=2*torch.dot(x,x)
print('m=',m)
# 执行梯度运算
n=m.backward()
k=x.grad
print('n=',n)
print('k=',k)

新的函数

未计算对新函数进行求导运算,需要提前将梯度清零,避免梯度计算效果彼此叠加,出现预料之外的效果。

梯度清零

代码为:

# 梯度清零
kk=x.grad.zero_()
print('kk=',kk)

代码运行效果为:

kk= tensor([0., 0., 0.])

定义新函数

代码为:

# 定义新函数
hh=x.sum()
print('hh=',hh)

这里使用了求和函数sim(),代码运行效果为:

hh= tensor(3., grad_fn=)

这里也输出了两个部分:
第一部分是3,就是元素求和的效果(0+1+2=3)。
第二部分是grad_fn=,grad_fn的意思是grad_function,就是求导函数的意思,后面的SumBackward0是对求导函数的具体定义。
SumBackward0 表示这是一个加法操作的梯度函数,具体拆开来:Sum-backward,字面意思解释:加法-反向传播。

导数计算

此时可以直接计算导数,代码为:

# 定义用backward方法计算导数
nn=hh.backward()
print('nn=',nn)
# 导数计算
tt=x.grad
print('tt=',tt)

代码运行效果为:

nn= None
tt= tensor([1., 1., 1.])

因为是各个变量直接叠加,所以每个变量前的系数都是1,所以导数运算的结果是[1.0,1.0,1.0].
此时的完整代码为:

# 导入模块
import torch
# 定义变量
x=torch.arange(3.0)
print('x=',x)
# 标记需要对x进行梯度计算
z=x.requires_grad_(True)
print('z=',z)
# 点乘定义
m=2*torch.dot(x,x)
print('m=',m)
# 执行梯度运算
n=m.backward()
k=x.grad
print('n=',n)
print('k=',k)
# 梯度清零
kk=x.grad.zero_()
print('kk=',kk)
# 定义新函数
hh=x.sum()
print('hh=',hh)
# 定义用backward方法计算导数
nn=hh.backward()
print('nn=',nn)
# 导数计算
tt=x.grad
print('tt=',tt)

完整的输出效果为:

x= tensor([0., 1., 2.])
z= tensor([0., 1., 2.], requires_grad=True)
m= tensor(10., grad_fn=)
n= None
k= tensor([0., 4., 8.])
kk= tensor([0., 0., 0.])
hh= tensor(3., grad_fn=)
nn= None
tt= tensor([1., 1., 1.])

梯度清零操作的讨论

前述有一个梯队清零的操作,如果没有这步操作,输出效果会如何变化,这里直接给出完整代码来测试。给出完整代码为:

# 导入模块
import torch
# 定义变量
x=torch.arange(3.0)
print('x=',x)
# 标记需要对x进行梯度计算
z=x.requires_grad_(True)
print('z=',z)
# 点乘定义
m=2*torch.dot(x,x)
print('m=',m)
# 执行梯度运算
n=m.backward()
k=x.grad
print('n=',n)
print('k=',k)
# 梯度清零
#kk=x.grad.zero_()
#print('kk=',kk)
# 定义新函数
hh=x.sum()
print('hh=',hh)
# 定义用backward方法计算导数
nn=hh.backward()
print('nn=',nn)
# 导数计算
tt=x.grad
print('tt=',tt)

此时的输出效果为:

x= tensor([0., 1., 2.])
z= tensor([0., 1., 2.], requires_grad=True)
m= tensor(10., grad_fn=)
n= None
k= tensor([0., 4., 8.])
hh= tensor(3., grad_fn=)
nn= None
tt= tensor([1., 5., 9.])

这里可以看到sum()函数的梯度输出为:[1.,5.,9.],这个结果的来源其实是:[0., 4., 8.]+[1., 1., 1.]=[1., 5., 9.]。
此处可见,及时将梯度清零很有必要。

总结

掌握了通过python+pytorch执行梯度运算的基本技巧。

<think>我们讨论的是如何设置PyTorch模型的所有参数为`requires_grad=True`,以便进行梯度更新。根据引用[1][2],`requires_grad=True`表示PyTorch会跟踪该张量的操作并计梯度,这是进行梯度更新的前提。引用[3]展示了如何冻结参数(即设置`requires_grad=False`),而我们的需求恰好相反:解冻所有参数,即全部设置为`requires_grad=True`。在PyTorch中,模型参数通常通过`model.parameters()`访问,每个参数都是一个`torch.nn.Parameter`对象,它继承自`torch.Tensor`,默认情况下`requires_grad=True`(见引用[4])。但是,有时我们可能加载了一个预训练模型,其中某些参数被冻结了(即`requires_grad=False`),我们需要将它们重新设置为可训练状态。方法如下:1.遍历模型的所有参数(通过`model.parameters()`)。2.将每个参数的`requires_grad`属性设置为`True`。示例代码: ```python#假设model是你定义的模型实例model= MyModel()#设置所有参数为可训练(即需要梯度)for paramin model.parameters():param.requires_grad =True```注意:-默认情况下,通过`nn.Module`定义的模型参数在创建时`requires_grad=True`,所以如果你从头开始训练,通常不需要显式设置。但如果你加载了一个预训练模型并且之前有冻结操作,那么需要这样设置。-引用[4]指出,`nn.Parameter`普通张量设置`requires_grad=True`的区别在于,`nn.Parameter`会自动被添加到模型的参数列表中(即可以通过`model.parameters()`访问),而普通张量则不会,除非你将其注册为参数。所以,我们这里操作的是模型参数,都是`nn.Parameter`类型。另外,如果你只想训练模型的一部分,可以针对特定层设置。例如,只训练模型的最后一层:```python#首先冻结所有参数for paramin model.parameters():param.requires_grad =False#然后解冻最后一层(假设最后一层是一个线性层,命名为'fc') forparam inmodel.fc.parameters():param.requires_grad= True```但是,根据问题要求,我们需要所有参数都进行梯度更新,因此使用第一种方法。验证方法:我们可以打印每个参数的`requires_grad`属性来确认:```pythonforname,param inmodel.named_parameters():print(f"{name}:requires_grad={param.requires_grad}") ```这样,在后续训练中,优化器(如`torch.optim.SGD`)会更新所有参数,因为优化器默认会收集所有`requires_grad=True`的参数。引用说明: [^1]:设置`requires_grad=True`是为了让PyTorch跟踪操作并计梯度。[^2]:`requires_grad`属性默认为False,但模型参数(`nn.Parameter`)默认是True。[^3]:冻结参数的方法是设置`requires_grad=False`,我们这里做相反操作。[^4]:模型参数都是`nn.Parameter`类型,会自动被优化器管理。相关问题:1.如何冻结PyTorch模型的部分层?2.在PyTorch中,如何只对特定层应用不同的学习率?3.为什么在微调预训练模型时,通常先冻结底层参数而只训练顶层?4.如何检查PyTorch模型中哪些参数需要梯度更新?</think>### PyTorch 设置模型所有参数为可训练状态 (`requires_grad=True`)PyTorch 中,设置模型参数为可训练状态(即需要计梯度)是训练神经网络的关键步骤。以下是详细的操作方法: #### 方法 1:遍历模型所有参数 ```python # 遍历模型的所有参数,设置 requires_grad=True for param in model.parameters(): param.requires_grad = True ``` #### 方法 2:使用 `nn.Module` 的 `apply` 方法 ```python # 定义设置 requires_grad 的函数 def set_requires_grad(module): if hasattr(module, 'weight'): module.weight.requires_grad_(True) if hasattr(module, 'bias'): module.bias.requires_grad_(True) # 应用到整个模型 model.apply(set_requires_grad) ``` #### 方法 3:直接操作参数(推荐) ```python # 最简洁有效的方法 model.requires_grad_(True) # PyTorch 1.0+ ``` #### 验证设置是否成功 ```python # 检查所有参数的 requires_grad 状态 all_trainable = all(param.requires_grad for param in model.parameters()) print(f"所有参数可训练: {all_trainable}") # 输出不可训练的参数 for name, param in model.named_parameters(): if not param.requires_grad: print(f"警告: 参数 {name} 未设置为可训练") ``` #### 数学原理 参数更新遵循梯度下降规则: $$ \theta_{t+1} = \theta_t - \eta \cdot \nabla_\theta J(\theta) $$ 其中: - $\theta$ 是模型参数 - $\eta$ 是学习率 - $\nabla_\theta J(\theta)$ 是损失函数 $J$ 关于 $\theta$ 的梯度 当 `requires_grad=True` 时,PyTorch 会自动计 $\nabla_\theta J(\theta)$[^1]。 #### 注意事项 1. **优化器关联**:设置参数为可训练后,需将其添加到优化器中: ```python optimizer = torch.optim.SGD(model.parameters(), lr=0.01) ``` 2. **冻结参数例外**:若需要冻结部分参数(如迁移学习),可单独设置: ```python for param in model.backbone.parameters(): param.requires_grad = False # 冻结骨干网络[^3] ``` 3. **与 `nn.Parameter` 区别**:直接设置 `requires_grad=True` 不会自动将张量注册为模型参数,需使用 `nn.Parameter` 包装[^4]: ```python # 正确做法 self.weight = nn.Parameter(torch.randn(10, 5), requires_grad=True) ``` #### 典型应用场景 ```python # 加载预训练模型 model = torchvision.models.resnet18(pretrained=True) # 解冻所有参数(默认部分层可能被冻结) model.requires_grad_(True) # 所有参数可训练 # 配置优化器 optimizer = torch.optim.Adam(model.parameters(), lr=1e-3) # 训练循环 for epoch in range(epochs): for data, target in dataloader: optimizer.zero_grad() output = model(data) loss = criterion(output, target) loss.backward() # 计梯度requires_grad=True时生效) optimizer.step() # 更新参数 ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值