在进行反向传播的时候,需要查看哪些参数被更新了,也就是说看看参数是否正确的被更新了,所以需要查看模型的哪些梯度被更新了
注意: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]……})。