clip 操作可能会导致无法优化 (clip之后相当于可学习参数被一个常数替代,故而无法求导)
迪丽瓦拉
2025-05-28 13:22:04
0次

在重看GIoU的motivation时,我有这样一句话:

当GT框与预测框不重叠时,框无论向哪个方向移动IoU始终是0,无法优化

也就是如下的这个操作,左图有重叠,IoU大于0,右图无重叠,IoU始终为0

请添加图片描述

无法优化,意思是梯度为0吧,所以梯度based优化方法都无法使用,我在计算IoU的过程中哪个部分会导致loss为0呢?
仅仅是因为IoU始终为0而导致IoU的梯度就为0了吗?
(这是显然的,无论Box向哪个方向进行微小移动,IoU都是定值0,也就是在该邻域中始终为定值,显然梯度为0)

但是在代码层面,这个无法优化的部分是由哪个组件产生的呢?

先说结论,是clip 操作导致的,clip之后相当于可学习参数被一个常数替代,故而无法求导,下边儿做个实验看看IoU为0时,其梯度为0的对比实验

1. 代码实验一

先放一个计算IoU的函数,如果不是做检测的,无需关注具体实现,只要知道中间有一步clip操作即可

import torchdef box_area(boxes: torch.Tensor) -> torch.Tensor:"""Computes the area of a set of bounding boxes, which are specified by their(x1, y1, x2, y2) coordinates.Args:boxes (Tensor[N, 4]): boxes for which the area will be computed. Theyare expected to be in (x1, y1, x2, y2) format with``0 <= x1 < x2`` and ``0 <= y1 < y2``.Returns:Tensor[N]: the area for each box"""# boxes = _upcast(boxes)return (boxes[:, 2] - boxes[:, 0]) * (boxes[:, 3] - boxes[:, 1])def box_iou(boxes1, boxes2):area1 = box_area(boxes1)area2 = box_area(boxes2)lt = torch.maximum(boxes1[:, None, :2], boxes2[:, :2])  # [N,M,2]rb = torch.minimum(boxes1[:, None, 2:], boxes2[:, 2:])  # [N,M,2]wh = (rb - lt).clip(min=0)  # [N,M,2]inter = wh[:, :, 0] * wh[:, :, 1]  # [N,M]union = area1[:, None] + area2 - interiou = inter / unionreturn iou, union
# 此处的预测框是可学习参数
pred_box = torch.tensor([0.3, 0.3, 0.6, 0.6])[None]
pred_box.requires_grad = True# 这是GT值
GT_box   = torch.tensor([0.0, 0.0, 1.0, 1.0])[None]# 学习率,每次朝着负梯度方向移动 0.001 的比例
lr = 0.001
for _ in range(10000):iou, _ = box_iou(pred_box, GT_box)loss = 1 - iou.sum()print(loss)loss.backward()with torch.no_grad():pred_box -= pred_box.grad * lrprint(pred_box.grad)pred_box.grad = None # 手动清除该 Tensor 梯度

pred_box 与 GT_box 都是 x1y1x2y2 的坐标,看代码坐标二者显然有重叠,所以loss可以下降,最后 pred_box 移动至 GT_box

而如果我将 pred_box 改为与 GT_box 无重叠的

# pred_box = torch.tensor([0.3, 0.3, 0.6, 0.6])[None]
pred_box = torch.tensor([10.3, 10.3, 10.6, 10.6])[None]         # 就是这一步改成没有重叠的
pred_box.requires_grad = TrueGT_box   = torch.tensor([0.0, 0.0, 1.0, 1.0])[None]
lr = 0.001
for _ in range(10000):iou, _ = box_iou(pred_box, GT_box)loss = 1 - iou.sum()print(loss)loss.backward()with torch.no_grad():pred_box -= pred_box.grad * lrprint(pred_box.grad)pred_box.grad = None # 手动清除该 Tensor 梯度

打印梯度,可以看到梯度始终为0

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

本实验只是验证了两box不重叠时存在梯度为0,无法优化的现象,接下来通过一个小实验,来验证clip会导致梯度为0的现象

2. 代码实验二

pred_box_wo_clip = torch.tensor([0.3, 0.3, -3, -3])
pred_box_wo_clip.requires_grad = TrueGT_box   = torch.tensor([1.0, 1.0, 1.0, 1.0])
lr = 0.0001
for _ in range(35000):pred_box = torch.clip(pred_box_wo_clip, min=-1)loss = ( (GT_box - pred_box)**2 ).sum()print(loss)loss.backward()with torch.no_grad():pred_box_wo_clip -= pred_box_wo_clip.grad * lrprint(pred_box_wo_clip.grad)pred_box_wo_clip.grad = None # 手动清除该 Tensor 梯度

运行代码,可以看到由于后两维梯度为0,而前两维可以正常求导

tensor([-1.4000, -1.4000,  0.0000,  0.0000])

数学中这样的clip操作被称作 hard 硬截断

3. 回忆

这让我想到一年前和锐哥做比赛,有个求反余弦的操作,然后反向求角度的操作,大概是酱紫的:

cos = xxxx   # 这里拿到角度的cos值
theta = arccos(cos)

由于那个求反余弦角度函数显然只会接受(-1, 1)的输入,若不在这个范围内,直接返回NaN。由于cos的数值精度误差问题,有时会返回1.000001,然后我就直接clip为1了,之后得到角度。

当时用的是DDPG,不论我们怎么调,他就是啥都学不会,始终朝着一个方向转动。一个可能的原因是因为这里用了clip导致梯度传回去始终是0,所以无法优化?

但是 不是这里… 真破大防了… 翻了一下去年的博客,那个操作是numpy做的,所以也就和梯度没什么事儿了,不知道我当时程序里还有没有clip操作了[捂脸笑哭]

相关内容

热门资讯

【甘快看•文化中国行】文博“站... 国庆假期,嘉峪关方特游人如织。文博场馆内,非遗技艺可看可感;山野村落间,田园风光令人心旷神怡;城市街...
江畔开演,嗦粉迎客!四川南充国... 封面新闻记者 刘彦君10 月 7 日,四川南充国庆假期文旅成绩单正式出炉:全市A级景区接待游客322...
国庆外骨骼助力器销售额涨12.... 【文/王力 编辑/吕栋】国庆假期,爬山这件事开始有了点“开外挂”的味道。过去登山装备是登山杖、护膝和...
这个假期,天衢新区活力拉满! 德百奥莱广场上,大型实景演出《德运星河》再度精彩上演;董子文化街里,祭董大典庄严肃穆,文化市集青春洋...
走进印尼帕达尔岛 俯瞰蓝色海洋   印尼科莫多国家公园的核心岛屿——帕达尔岛由古老火山喷发形成,登顶观景台,四片海湾同时铺展在眼前,...
美丽中国行·大河长歌|一河穿沙...   黄河自黑山峡奔涌而入,从甘肃进入宁夏首站中卫,在沙坡头,与腾格里沙漠迎面相拥,形成沙水交融、河漠...
来上海别只吃小笼包了!新场古镇... 在上海市浦东新区,有一座被称为“小小新场赛苏州”的古镇,你知道是哪一座古镇吗?它北靠上海迪士尼度假区...
双节文旅收官盘点:古韵山西破壁... 本报(chinatimes.net.cn)记者赵文娟 平遥、忻州、太原报道2026年中秋国庆双节叠加...
“双节”期间 河南全省博物馆接... 来源:中国新闻网中新网郑州10月8日电(记者 韩章云)10月8日,记者从河南省文物局获悉,中秋国 庆...
外媒关注国庆假期:消费市场持续... 来源:中国新闻网中新网10月7日电10月7日是2026年国庆假期的最后一天。假期旅游热潮吸引国际媒体...