此blog主要介绍本人在使用yolov3模型进行c++ libtorch 调试过程中遇到的一些问题。
Yolo-v3模型的介绍可参考深入拆解YOLO_V3,这里仍旧使用的是GitHub eriklindernoren/PyTorch-YOLOv3。
c++调用pytorch libtorch基础介绍可参考https://blog.csdn.net/WANGWUSHAN/article/details/118020188。该blog中包含了一个简单的demo,方便初学者快速入门。
在custom的一个数据集上效果如下:
可见,直接使用源码通过torch.jit.trace生成的模型有点问题。可问题具体处在什么地方呢?
首先考虑了几个地方:
这里对FloatTensor = torch.cuda.FloatTensor if cuda else torch.FloatTensor修改,使用gpu时将FloatTensor替换为torch.cuda.FloatTensor,使用cpu时替换为torch.FloatTensor
最后结果依然如此,不太对或者太不对。
百思不得其解之下,偶尔看到可以验证下,ts(input)与model(input)结果是否一致:
tem = torch.rand((1, 3, 416, 416))
ts = torch.jit.trace(model, img,check_trace=False)
with torch.no_grad():
output = model(tem)
print(output[0,0,:])
print(ts(tem)[0,0,:])
print(output.equal(ts(tem)))
只打出输出tensor的第一个box的各元素,可以看到,x,y,w,h数据是不一样的,而后面这些confidence信息是一致的。
奇怪的是,如果trace使用的input和ts()使用的input一致,output和ts(input)会相等。这点还是没搞清楚。
ts = torch.jit.trace(model, img)
with torch.no_grad():
output = model(img)
print(output)
print(ts(img))
print(output.equal(ts(img)))
至少发现了上述差异至少说明torch.jit.trace生成的模型和pytorch/python中的模型还是有差别的。
由于涉及到x,y,w,h的代码不多,都在class YOLOLayer(nn.Module)
类中,通过不断排查和尝试,终于定位到:
pred_boxes[..., 0] = x.data + self.grid_x # b_x=/simax(t_x)+c_x
pred_boxes[..., 1] = y.data + self.grid_y # b_y=/simax(t_y)+c_y
pred_boxes[..., 2] = torch.exp(w.data) * self.anchor_w
pred_boxes[..., 3] = torch.exp(h.data) * self.anchor_h
这几句应该修改为:
pred_boxes[..., 0] = x.detach() + self.grid_x # b_x=/simax(t_x)+c_x
pred_boxes[..., 1] = y.detach()+ self.grid_y # b_y=/simax(t_y)+c_y
pred_boxes[..., 2] = torch.exp(w.detach()) * self.anchor_w
pred_boxes[..., 3] = torch.exp(h.detach()) * self.anchor_h
至于再进一步的原因,可以参考https://blog.csdn.net/u013066730/article/details/96484351,查看下tensor.data和tensor.detach()的差别。tensor.data尽管可以使用,但一般认为是不安全的。
可能也和pytorch版本有关系吧。
先分析到这,后续有心得再补。
由于model与nms输出结果的数据类型/结构的差异,直接将nms部分打包到model中,会出现一些问题。另外,如果nms函数中使用了torchvision,则c++项目中可能就需要包含torchvision的库(未验证),目前这个操作也是非常繁琐的。
torchvision.ops.nms(boxes, scores, iou_thres) # NMS
因此,模型输出后,再通过c++实现的nms进行处理,生成最终结果,是一种可行的选择。
可参考知乎 Yolov3模型在pytorch上训练,在C++中利用Libtorch上进行模型的加载、推理。
这里,再介绍一种实现方式,那就是opencv实现nms,可直接在c++中进行调用,非常方便。
另外在python版本模型的训练及测试过程中也可以进行一键调用。
[1] 知乎 Yolov3模型在pytorch上训练,在C++中利用Libtorch上进行模型的加载、推理
[2] https://docs.opencv.org/4.5.3/d6/d0f/group__dnn.html#ga9d118d70a1659af729d01b10233213ee)
[3] Deep Learning based Object Detection using YOLOv3 with OpenCV ( Python / C++ )
[4] https://blog.csdn.net/u013066730/article/details/96484351