查看模型的哪些梯度/参数被更新了

在进行反向传播的时候,需要查看哪些参数被更新了,也就是说看看参数是否正确的被更新了,所以需要查看模型的哪些梯度被更新了

注意:param.grad和param.requires_grad:“grad”才是看是否会被更新,"requires_grad"只是看是否应该被更新,没有什么用,

简单的方法:

1、对于一般的模型来说,可以使用下面的方法:

直接在 backward 后面加上下面这段代码就可以了,就可以知道哪个参数没被更新

            for name, param in self.model.named_parameters():
                if param.grad is None:
                    print("The None grad model is:")
                    print(name)

2、对于分布式DDP模型,可以更简单:

 在你的运行命令之前加上“TORCH_DISTRIBUTED_DEBUG=DETAIL”,然后就会出现哪里未更新:

例如:

TORCH_DISTRIBUTED_DEBUG=DETAIL python config/xxx.py

https://blog.51cto.com/u_15906550/5921634


方法一:通过写log日志的的方式查看

 可以通过日志进行记录哪些参数更新了,也可以直接print。

这些操作需要在反向传播以后才可以使用(放在loss.backward()之后就可以)【因为在使用PyTorch进行反向传播时,为了避免梯度累积的影响,通常会在每次反向传播之前将梯度清零。看下面的“小例子””有详细的放在什么位置

# 自定义log的名字
log_name = "which_parameter_update"
import logging

# 查看哪些梯度被更新了 + 保存这些信息到log
logger_name = "logger_" + log_name
formatter_name = "formatter_" + log_name
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
globals()[logger_name] = logging.getLogger(logger_name)
file_handler = logging.FileHandler(log_name + '.log')
file_handler.setLevel(logging.INFO)
globals()[formatter_name] = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
file_handler.setFormatter(globals()[formatter_name])
globals()[logger_name].addHandler(file_handler)

requires_grad_list = []
no_requires_grad_list = []
grad_list = []
no_grad_list = []
for name, param in model.named_parameters():
    if param.requires_grad:
        requires_grad_list.append(name)
    else:
        no_requires_grad_list.append(name)

    if param.grad is not None:
        grad_list.append(name)
    else:
        no_grad_list.append(name)

# 要写入的信息
globals()[logger_name].info('1、requires_grad_list是:%s', requires_grad_list)
globals()[logger_name].info('2、no_requires_grad_list是:%s', no_requires_grad_list)
globals()[logger_name].info('3、grad_list是:%s', grad_list)
globals()[logger_name].info('4、no_grad_list是:%s', no_grad_list)

小例子:

import torch
import torch.nn as nn
import torch.optim as optim


# 定义模型
class MyModel(nn.Module):
    def __init__(self):
        super(MyModel, self).__init__()
        self.linear_new = nn.Linear(1, 1)  # 假设输入维度为1,输出维度为1
        self.activation = nn.ReLU()  # 额外的函数,比如ReLU激活函数

    def forward(self, x):
        x = self.linear_new(x)
        x = self.activation(x)
        return x


# 创建模型实例
model = MyModel()

# 创建输入和目标张量
input_tensor = torch.tensor([1.0])
target = torch.tensor([0.0])

# 定义损失函数和优化器
criterion = nn.MSELoss()
optimizer = optim.SGD(model.parameters(), lr=0.01)

# 迭代训练过程
num_epochs = 10  # 设定迭代次数
for epoch in range(num_epochs):
    # 执行前向传播
    output = model(input_tensor)
    # 计算损失
    loss = criterion(output, target)
    # 将梯度清零
    optimizer.zero_grad()
    # 执行反向传播
    loss.backward()

    # 自定义log的名字
    log_name = "which_parameter_update"
    import logging

    # 查看哪些梯度被更新了 + 保存这些信息到log
    logger_name = "logger_" + log_name
    formatter_name = "formatter_" + log_name
    logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
    globals()[logger_name] = logging.getLogger(logger_name)
    file_handler = logging.FileHandler(log_name + '.log')
    file_handler.setLevel(logging.INFO)
    globals()[formatter_name] = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
    file_handler.setFormatter(globals()[formatter_name])
    globals()[logger_name].addHandler(file_handler)

    requires_grad_list = []
    no_requires_grad_list = []
    grad_list = []
    no_grad_list = []
    for name, param in model.named_parameters():
        if param.requires_grad:
            requires_grad_list.append(name)
        else:
            no_requires_grad_list.append(name)

        if param.grad is not None:
            grad_list.append(name)
        else:
            no_grad_list.append(name)

    # 要写入的信息
    globals()[logger_name].info('1、requires_grad_list是:%s', requires_grad_list)
    globals()[logger_name].info('2、no_requires_grad_list是:%s', no_requires_grad_list)
    globals()[logger_name].info('3、grad_list是:%s', grad_list)
    globals()[logger_name].info('4、no_grad_list是:%s', no_grad_list)

    # 更新参数
    optimizer.step()
    # 打印损失
    print(f"Epoch [{epoch + 1}/{num_epochs}], Loss: {loss.item():.4f}")




输出:

2023-07-29 20:23:29,258 - INFO - 1、requires_grad_list是:['linear_new.weight', 'linear_new.bias']
2023-07-29 20:23:29,259 - INFO - 2、no_requires_grad_list是:[]
2023-07-29 20:23:29,259 - INFO - 3、grad_list是:['linear_new.weight', 'linear_new.bias']
2023-07-29 20:23:29,259 - INFO - 4、no_grad_list是:[]

其中“linear_new”是有梯度的层,假如此时你想debug找出哪些层的gred没有进行更新,直接全局搜索list张工的weight前的字符串即可,例如此时搜索“linear_new


方法二:通过debug的方式查看

来源于此:pytorch学习(2):通过检查梯度参数,判断是否正常反向传播_pycharm查看梯度_香菜冰激凌的博客-CSDN博客

1)、相关代码

我们创建了一个CFAR10的神经网络,输入测试集,计算交叉熵和下降梯度,并将梯度进行反向传播(优化器部分没有写,这里只演示如何寻找存储梯度的变量文件)

#loss函数的作用:
#计算实际输出和标签之间的误差
#为反向传播提供依据(即梯度)gradient
import torch
import torchvision
#from tensorboardX import SummaryWriter
from torch import nn
from torch.nn import Conv2d, Flatten, Linear, Sequential, MaxPool2d
from torch.utils.data import DataLoader

dataset = torchvision.datasets.CIFAR10("./dataset", train=False,
                                       transform=torchvision.transforms.ToTensor(),download=True)
test_data = DataLoader(dataset, batch_size=64, drop_last=True)
#Sequential 是一个更为简洁的书写方式
class CFAR10_Modle(nn.Module):
    def __init__(self):
        super().__init__()
        self.model1 = Sequential(
            Conv2d(3, 32, kernel_size=5,stride=1, padding=2),
            MaxPool2d(kernel_size=2),
            Conv2d(32, 32, kernel_size=5, stride=1, padding=2),
            MaxPool2d(kernel_size=2),
            Conv2d(32, 64, kernel_size=5, stride=1, padding=2),
            MaxPool2d(kernel_size=2),
            Flatten(),
        #self.linear = Linear(64, 10)  应该是64*4*4
            Linear(1024, 64),
            Linear(64, 10)
        )


    def forward(self, x):
        x = self.model1(x)
        return(x)

Ni_CFAR10_Modle = CFAR10_Modle()
#loss = torch.nn.MSELoss()  #选用误差函数,要看神经网络的输出和数据集的标签是什么形式,这里就是错误的
loss = torch.nn.CrossEntropyLoss()

print(Ni_CFAR10_Modle)
#将数据集传入网络中,计算输出结果并与targets核对,计算误差
for data in test_data:
    images, targets = data
    output = Ni_CFAR10_Modle(images)
    print(output)
    loss_result = loss(output, targets)
    print(loss_result)
    loss_result.backward()  #计算梯度,并反向传播,如果没有这一行是不会计算梯度的, 计算梯度以后,就可以传给优化器 ,来调整参数了
    #查看梯度时,将断点设在“上一行“,然后查看Variables,点击自己”实例化“好的模型名称,
    # 在这里是Ni_CIFAR10_Modle,点击里面的modle1-->protected attitude -->modules -->'0',最下面有个weights文件夹,
    # 在里面找不到grad文件夹,只能看到“grad = {NoneType}None” 那说明没有生成梯度参数
    #注意:如果点击的是‘1’ ‘3’等非卷积层文件夹,,是没有weights的
    #然后,你运行上一行,就可以看到 weight文件夹下出现了grad文件夹,
    # 虽然文件夹下依然可以看到“grad = {NoneType}None”,但这已经说明有了梯度参数。

二、寻找含有梯度参数的变量文件

在最后一行(loss_result.backward())处设置断点,运行pycharm的Debug工具,此时查看Variable一栏,可以看到生成了一堆变量文件

在这里插入图片描述

 由代码可知,我们实例化了一个叫Ni_CFAR10_Modle(你的模型名称)的网络模型,所以存储网络模型参数的变量文件也叫Ni_CFAR10_Modle,我们点开此文件,可以看到里面有一个叫modle1的文件夹,我们依次点击modle1–>protected attitude -->modules -->‘0’,'0’文件夹中,最下面有个weight文件夹。

注意:****‘0’ ‘2’ ‘4’ 层是Conv2d,卷积层,有weights参数;像’1’ '3’层这种池化层是没有weights参数的,也就没有weight变量文件。
此时,我们weights文件夹下是没有黄色的名为grad的变量文件夹的,只有一个蓝色图标的:“grad = {NoneType}None” ,那说明没有生成梯度参数。
在这里插入图片描述

然后我们运行断点(点击debug的下一步F8,让他进入下一行),就可以看到 weights文件夹下出现了grad文件夹,虽然文件夹下依然可以看到“grad = {NoneType}None”,但这已经说明有了梯度参数,并进行了反向传播,只不过由于没写优化器,所以优化器还没接受梯度。 

在这里插入图片描述

 黄色文件夹grad后的参数就是梯度参数(grad = {Tensor:(32,3,5,5)}……)
黄色文件夹weight后的参数是该卷积层的权重参数(weight ={Parameter:[32,3,5,5]……})。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
PyTorch是一个非常流行的深度学习框架,可以用来训练各种类型的神经网络。在训练神经网络时,了解模型参数梯度值对于优化模型非常重要。在PyTorch中,可以通过调用模型对象的"backward()"方法来计算模型参数梯度。具体来说,可以按照以下步骤查看模型参数梯度: 1. 创建一个PyTorch模型,例如一个多层感知器。 2. 运行一些输入数据,并将输出和真实标签传递给损失函数。 3. 在完成一次前向传递和反向传递后,可以通过调用"backward()"方法计算所有参数梯度值。 4. 最后,可以通过访问参数对象的"grad"属性来获取每个参数梯度值。 例如,如果想要查看模型的第二个全连接层的权重参数梯度值,可以使用以下代码: ```python import torch import torch.nn as nn # 创建模型 class MLP(nn.Module): def __init__(self): super(MLP, self).__init__() self.fc1 = nn.Linear(784, 256) self.fc2 = nn.Linear(256, 128) self.fc3 = nn.Linear(128, 10) def forward(self, x): x = x.view(-1, 784) x = F.relu(self.fc1(x)) x = F.relu(self.fc2(x)) x = self.fc3(x) return x model = MLP() # 前向传播和反向传播 input = torch.randn((64, 1, 28, 28)) output = model(input) target = torch.randn((64, 10)) criterion = nn.MSELoss() loss = criterion(output, target) loss.backward() # 查看参数梯度 print(model.fc2.weight.grad) ``` 这将输出第二个全连接层的权重参数梯度,该层包括128个输入和256个输出: ```python tensor([[-0.0005, -0.0005, -0.0005, ..., -0.0005, -0.0005, -0.0005], [-0.0008, -0.0008, -0.0008, ..., -0.0008, -0.0008, -0.0008], [-0.0011, -0.0011, -0.0011, ..., -0.0011, -0.0011, -0.0011], ..., [-0.0009, -0.0009, -0.0009, ..., -0.0009, -0.0009, -0.0009], [-0.0009, -0.0009, -0.0009, ..., -0.0009, -0.0009, -0.0009], [-0.0003, -0.0003, -0.0003, ..., -0.0003, -0.0003, -0.0003]]) ``` 总之,通过使用PyTorch中的"backward()"方法和参数对象的"grad"属性,可以轻松地查看模型参数梯度值。这对于分析和优化深度学习模型非常有用。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

马鹏森

太谢谢了

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值