神经网络loss.backward()后网络参数梯度全部为None

起因:在对神经网络进行训练时,发现每个epoch的loss一模一样不发生变化,于是对loss.backward()后的网络参数的梯度进行了排查,代码如下:

for name, para in model.named_parameters():
    print(f'{name}的梯度:{para.grad}')

结果如下:

 

可以看到,网络所有的参数的梯度全部为None。于是我在csdn上查找相关问题的解决办法,首先是对网络以及变量有没有开启梯度计算进行检查,我对网络所有层数和变量用以下方法进行排查发现都开启了梯度计算,所以不是这个问题:

print(x.requires_grad)

于是继续在csdn上查找发现很多人都在谈论叶节点这个概念,具体可见下面这篇文章:

param.grad、requires_grad、grad_fn、grad/梯度为None?

文章中提到只有叶节点才能进行梯度计算,并且只要是用户创建的tensor均为叶节点,然而对叶节点进行.to(device)操作后就不再是叶节点了,因此建议直接在创建变量时直接将.to(device)的操作放在函数内或者重新创建一个变量,如下所示:

a = torch.tensor([1, 1], dtype=torch.float32, device=device)

或者 


a = torch.ones((2, 2), requires_grad=True)
c = a.to(device)
b = c.sum()
b.backward()

于是我将所有的.to(device)操作进行更改,然而依然不行。

就在准备放弃时突然发现,如果我将loss换成x时是有梯度的,然而我以前的loss是h(x),于是我觉着可能是h()这个函数的问题,最后发现确实是这个问题!h()这个函数如下所示:

def h(x):
    px, dpx, py, dpy, Delta = x[:, 0]
    y1 = torch.sqrt(px ** 2 + py ** 2 + height ** 2)
    y2 = torch.arctan2(py, px)
    y3 = torch.arctan(height / torch.sqrt(px ** 2 + py ** 2))
    y4 = (px * dpx + py * dpy) / y1
    zk = torch.tensor([[y1], [y2], [y3], [y4]], dtype=torch.float32, device=device)
    zk.requires_grad = True
    return zk

我一开始以为是y1, y2, y3, y4没开启梯度计算,于是对它们开启梯度计算,结果报错,说它们不是叶节点无法开启梯度计算,然后我检查发现x是叶节点并且开启了梯度计算,所以不是这个问题。

于是只有可能是zk的定义问题了,于是我在网上查找torch,tensor()这个函数后发现,当使用这个函数创建张量时,它会创建一个新的独立张量zk,也就是一个新的叶节点,然而,这个叶节点并没有与之前的计算过程建立联系,所以梯度反向传播时无法从zk传播到x,也就是说,这个新的张量zk与输入x之间的计算图被中断了,导致梯度无法进行计算,从而显示为None。

为了解决这一问题,查找资料后发现可以使用torch.stack()函数进行代替,该函数将多个张量沿某一维度进行堆叠,并且不会中断计算图使得梯度可以进行传递,修改后的h()函数如下:

def h(x):
    px, dpx, py, dpy, Delta = x[:, 0]
    y1 = torch.sqrt(px ** 2 + py ** 2 + height ** 2)
    y2 = torch.arctan2(py, px)
    y3 = torch.arctan(height / torch.sqrt(px ** 2 + py ** 2))
    y4 = (px * dpx + py * dpy) / y1
    # 使用 torch.stack 保持计算图的连续性
    zk = torch.stack([y1, y2, y3, y4]).unsqueeze(1).to(device)
    return zk

修改后,再次查看网络参数的梯度,发现是正常的值啦!

到此为止,问题得以解决,可以进行梯度反向传播和训练了!

感谢您的阅读!如果对您有帮助,烦请点赞收藏呀!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值