c++调用pytorch libtorch(YoloV3实战篇)

1.前言

此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,方便初学者快速入门。

2.直接使用仓库源码c++ libtorch输出结果与pytorch结果差别大

在custom的一个数据集上效果如下:
在这里插入图片描述
可见,直接使用源码通过torch.jit.trace生成的模型有点问题。可问题具体处在什么地方呢?

首先考虑了几个地方:

  • warning的地方
  • 图片读入及处理是不是都是通过opencv实现的
  • if分支结构
    也参考了https://zhuanlan.zhihu.com/p/246156517,将存在if语句的地方进行了修改,如:

这里对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

这样就完全一致了。
在这里插入图片描述
c++重新调用模型,结果也非常OK。

至于再进一步的原因,可以参考https://blog.csdn.net/u013066730/article/details/96484351,查看下tensor.data和tensor.detach()的差别。tensor.data尽管可以使用,但一般认为是不安全的。
在这里插入图片描述
可能也和pytorch版本有关系吧。

先分析到这,后续有心得再补。

3.关于nms

由于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

Guess you like

Origin blog.csdn.net/WANGWUSHAN/article/details/118968060