Paddle 【论文复现】自定义loss函数中使用numpy实现bool索引的方式会不会影响反向传播

u59ebvdq  于 2021-11-30  发布在  Java
关注(0)|答案(6)|浏览(247)

目前paddle版本是2.1的;
论文复现过程中,模型主体部分全部使用paddle实现,但是loss部分,因为涉及三种loss函数的使用L1Loss、BCEWithLogitsLoss和IOUloss。
而loss部分,具体的一些操作需要使用bool索引。paddle似乎不支持bool索引,因此使用间接的方式:numpy转换后再paddle的方式。如下例子所示:
paddle.to_tensor(bbox_preds.reshape((-1, 4)).numpy()[fg_masks.numpy()], dtype=bbox_preds.dtype)。

反向传播方面的代码是:
yolo_outputs, loss_stats = model(inps, targets=targets)
#yolo_outputs 是模型主体部分的输出,要传入loss部分和targets进行比对运算的。
loss = loss_stats['loss']
loss.backward()
#位置1
optimizer.step()
optimizer.clear_grad()

然后在 #位置1 处,通过输出各层的梯度发现,各元素的加和均为0,代码示例如下:
elif np.sum(value.grad.numpy()) == 0:
print(colored("====> 0: Problem key:{},grad.shape:{}".format(key, value.grad.shape)))

因此我的问题是

1 为什么模型在反向传播时各层的bias和weights的梯度均为0?
2 是否numpy间接实现的方式会对反向传播产生消极影响?不考虑效率的情况下。

l5tcr1uw

l5tcr1uw1#

您好,我们已经收到了您的问题,会安排技术人员尽快解答您的问题,请耐心等待。请您再次检查是否提供了清晰的问题描述、复现代码、环境&版本、报错信息等。同时,您也可以通过查看官网API文档常见问题历史IssueAI社区来寻求解答。祝您生活愉快~

Hi! We've received your issue and please be patient to get responded. We will arrange technicians to answer your questions as soon as possible. Please make sure that you have posted enough message to demo your request. You may also check out the APIFAQGithub Issue and AI community to get the answer.Have a nice day!

9w11ddsr

9w11ddsr2#

你好,目前develop的paddle已经支持了bool类型的索引,可以试一下,另外,如果需要bbox_preds进行反向传播的话,那么.numpy方法之后,是无法梯度回传的,这块需要注意一下

iyfamqjs

iyfamqjs3#

好的 ,我尝试一下,谢谢!

oiopk7p5

oiopk7p54#

我想再请教一下,为什么输出所有模型参数的梯度,结果都是0呢?能不能提供一下可能的解决方法?

loss.grad:Tensor(shape=[1], dtype=float32, place=CUDAPlace(0), stop_gradient=False,
       [1.])
......
head.obj_preds.1.weight, gradient: Tensor(shape=[1], dtype=float32, place=CUDAPlace(0), stop_gradient=False,
       [0.])
head.obj_preds.1.bias, gradient: Tensor(shape=[1], dtype=float32, place=CUDAPlace(0), stop_gradient=False,
       [0.])
head.obj_preds.2.weight, gradient: Tensor(shape=[1], dtype=float32, place=CUDAPlace(0), stop_gradient=False,
       [0.])
head.obj_preds.2.bias, gradient: Tensor(shape=[1], dtype=float32, place=CUDAPlace(0), stop_gradient=False,
       [0.])
head.stems.0.conv.weight, gradient: Tensor(shape=[1], dtype=float32, place=CUDAPlace(0), stop_gradient=False,
       [0.])
head.stems.0.bn.weight, gradient: Tensor(shape=[1], dtype=float32, place=CUDAPlace(0), stop_gradient=False,
       [0.])
head.stems.0.bn.bias, gradient: Tensor(shape=[1], dtype=float32, place=CUDAPlace(0), stop_gradient=False,
       [0.])
head.stems.1.conv.weight, gradient: Tensor(shape=[1], dtype=float32, place=CUDAPlace(0), stop_gradient=False,
       [0.])
head.stems.1.bn.weight, gradient: Tensor(shape=[1], dtype=float32, place=CUDAPlace(0), stop_gradient=False,
       [0.])
head.stems.1.bn.bias, gradient: Tensor(shape=[1], dtype=float32, place=CUDAPlace(0), stop_gradient=False,
       [0.])
head.stems.2.conv.weight, gradient: Tensor(shape=[1], dtype=float32, place=CUDAPlace(0), stop_gradient=False,
       [0.])
head.stems.2.bn.weight, gradient: Tensor(shape=[1], dtype=float32, place=CUDAPlace(0), stop_gradient=False,
       [0.])
head.stems.2.bn.bias, gradient: Tensor(shape=[1], dtype=float32, place=CUDAPlace(0), stop_gradient=False,
       [0.])
......

以上输出的是各参数的均值。如果铺开的话,都是0.0;
代码如下所示:

loss_stats = model(inps, targets=targets, logger=logger)
                loss = loss_stats['loss']

                # model backward
                loss.backward()
                print("loss.grad:{}".format(loss.grad))
                for name, param in model.named_parameters():
                    if not param.stop_gradient:
                        if param.grad is not None:
                            print("{}, gradient: {}".format(name, param.grad.mean()))
                        else:
                            print("{} has not gradient".format(name))
                optimizer.step()
                optimizer.clear_grad()
t30tvxxf

t30tvxxf5#

看下是否包含relu激活函数以及relu之前的tensor已经是小于0的值了

ncecgwcz

ncecgwcz6#

首先,YOLOX模型使用的是silu激活函数;

其次,参考链接:#35241 (comment) ;

也提到了相同的问题,解决途径是:

比如yolox_head中的以下两个操作:
output[:, :, :2] = (output[:, :, :2] + grid) * stride
output[:, :, 2:4] = paddle.exp(output[:, :, 2:4]) * stride
单独执行任一个都不会有问题,但是两个一起操作就会导致反向传播梯度为0
改成以下方式,反向传播梯度正常:
xy = (output[:, :, :2] + grid) * stride
wh = paddle.exp(output[:, :, 2:4]) * stride
obj_cls = output[:, :, 4:]
output=paddle.concat([xy, wh, obj_cls], 2)

我经过修改确实也避免了梯度为0的情况,但是运行几个step之后,又出现了以下719 cuda error,而这一情况,目前我也是没有思路了。

具体的过程是:

而在使用silu函数的情况下,首先运行到一定step之后,网络模型的前几层,假设是第4层开始,第四层的weigh的梯度先出现一个nan值,之后网络的第一层、第二层和第三层的梯度也都全变成了nan。
而进行了参数更新之后,网络模型的全部参数将会全部变成nan。
随后,各层输出自然而然也会变成nan,有时会变成inf;
这就是我打印的模型参数的参数值、梯度值和输出值的情况;

相关问题