refinedet tensorRT实现


本文github链接,欢迎star

1.准备工作

首先,要弄清楚自己在做什么,然后一步步的去实现它,在此过程中,要不断验证自己的每个步骤是否正确。验证正确了再往下继续走,不正确就要去排查哪里有问题。
现在是需要把pytorch refinedet转tensorrt。而且是一步步的搭建网络实现。
pytorch refinedet https://github.com/luuuyi/RefineDet.PyTorch
tensorrt https://github.com/wang-xinyu/tensorrtx
我也是刚接触tensorrt,做一件事情之前找个现成例子,跑一跑,再学习实现细节。
我跑通了tensorrtx里面yolov3的demo,这期间花费了很长时间。因为tensorrt实现的是U版的yolov3,https://github.com/ultralytics/yolov3。
这个仓库代码是商用的,实现的质量很高,并且代码里面用了各种奇淫技巧。我首先是把这个纯pytorch的yoloV3代码跑通,并结合论文弄懂了原理。然后再去看tensorrt的复现。
tensorrtx这个仓库里面包含了很多流行网络的实现,都是用tensorrt api一步步搭的。
感谢作者开源了这么优秀的代码,并且一群优秀的人不断贡献着开源。
总结一个大体流程:
(1)要了解网络的每层的实现,数据流流向,nchw大小,卷积核大小。
(2)在pytorch工程里面把权重导出,用16进制导致wts文件,字典格式,层名对应权重。后续tensorRT里面根据对应层名字来索引对应权重。
(3)搭一层网络加载对应权重
(4)先生成engine,再推理。
具体细节后面讲解
给出一些链接:
安装tensorrt教程:
https://github.com/wang-xinyu/tensorrtx/blob/master/tutorials/install.md
一步步搭建tensorrt详细的教程:
https://github.com/wang-xinyu/tensorrtx/blob/master/tutorials/from_pytorch_to_trt_stepbystep_hrnet.md

2.wts权重文件生成

import torch
import torch.nn as nn
import struct
from models.refinedet import build_refinedet

num_classes = 25
path_model = "/data_2/pytorch_refinedet/2021/20210308.pth"
path_save_wts = "./refinedet0312.wts"
input_size = 320

net = build_refinedet('test', input_size, num_classes)  # initialize net
net.load_state_dict(torch.load(path_model))
net.eval()

f = open(path_save_wts, 'w')
f.write('{}
'.format(len(net.state_dict().keys())))
for k, v in net.state_dict().items():
    vr = v.reshape(-1).cpu().numpy()
    f.write('{} {} '.format(k, len(vr)))
    for vv in vr:
        f.write(' ')
        f.write(struct.pack('>f',float(vv)).hex())
    f.write('
')
print("success generate wts!")

这个基本是用的仓库的代码。加载pth文件,然后保存字典权重wts到本地。
在tensorrt里面解析,解析成类似与字典格式,层名对应权重:

std::map<std::string, Weights> loadWeights(const std::string file) {
    std::cout << "Loading weights: " << file << std::endl;
    std::map<std::string, Weights> weightMap;

    // Open weights file
    std::ifstream input(file);
    assert(input.is_open() && "Unable to load weight file.");

    // Read number of weight blobs
    int32_t count;
    input >> count;
    assert(count > 0 && "Invalid weight map file.");

    while (count--)
    {
        Weights wt{DataType::kFLOAT, nullptr, 0};
        uint32_t size;

        // Read name and type of blob
        std::string name;
        input >> name >> std::dec >> size;
        wt.type = DataType::kFLOAT;

        // Load blob
        uint32_t* val = reinterpret_cast<uint32_t*>(malloc(sizeof(val) * size));
        for (uint32_t x = 0, y = size; x < y; ++x)
        {
            input >> std::hex >> val[x];
        }
        wt.values = val;
        
        wt.count = size;
        weightMap[name] = wt;
    }

    return weightMap;
}

3.tensorrt 网络搭建

由于原生的pytorch工程网络实现复杂。如下网络的构建:

def build_refinedet(phase, size=320, num_classes=21):
    if phase != "test" and phase != "train":
        print("ERROR: Phase: " + phase + " not recognized")
        return
    if size != 320 and size != 512:
        print("ERROR: You specified size " + repr(size) + ". However, " +
              "currently only RefineDet320 and RefineDet512 is supported!")
        return
    base_ = vgg(base[str(size)], 3)
    extras_ = add_extras(extras[str(size)], size, 1024)
    ARM_ = arm_multibox(base_, extras_, mbox[str(size)])
    ODM_ = odm_multibox(base_, extras_, mbox[str(size)], num_classes)
    TCB_ = add_tcb(tcb[str(size)])
    return RefineDet(phase, size, base_, extras_, ARM_, ODM_, TCB_, num_classes)

网络由各个组件来完成,每个组件里面各种循环添加卷积、池化层等。这种方式不利于我们一层层的搭建和调试代码。所以,我就用最笨的方法,就是一层层的写卷积、池化同样实现作者的这个网络。
当然,也是需要把refinedet这个工程看的熟透。所以,我自己按照官方实现的搭的网络就是像下面这样的:

class refinedet_my(nn.Module):  # SfSNet = PS-Net in SfSNet_deploy.prototxt
    def __init__(self):
        # C64
        super(refinedet_my, self).__init__()
        self.num_classes = 25
        self.conv0 = nn.Conv2d(3, 64, kernel_size=3, padding=1)
        self.relu1 = nn.ReLU(inplace=True)
        self.conv2 = nn.Conv2d(64, 64, kernel_size=3, padding=1)
        self.relu3 = nn.ReLU(inplace=True)
        self.maxpool4 = nn.MaxPool2d(kernel_size=2, stride=2)

        self.conv5 = nn.Conv2d(64, 128, kernel_size=3, padding=1)
        self.relu6 = nn.ReLU(inplace=True)
        self.conv7 = nn.Conv2d(128, 128, kernel_size=3, padding=1)
        self.relu8 = nn.ReLU(inplace=True)
        self.maxpool9 = nn.MaxPool2d(kernel_size=2, stride=2)

        self.conv10 = nn.Conv2d(128, 256, kernel_size=3, padding=1)
        self.relu11 = nn.ReLU(inplace=True)
        self.conv12 = nn.Conv2d(256, 256, kernel_size=3, padding=1)
        self.relu13 = nn.ReLU(inplace=True)
        self.conv14 = nn.Conv2d(256, 256, kernel_size=3, padding=1)
        self.relu15 = nn.ReLU(inplace=True)
        self.maxpool16 = nn.MaxPool2d(kernel_size=2, stride=2, ceil_mode=True)

        self.conv17 = nn.Conv2d(256, 512, kernel_size=3, padding=1)
        self.relu18 = nn.ReLU(inplace=True)
        self.conv19 = nn.Conv2d(512, 512, kernel_size=3, padding=1)
        self.relu20 = nn.ReLU(inplace=True)
        self.conv21 = nn.Conv2d(512, 512, kernel_size=3, padding=1)
        self.relu22 = nn.ReLU(inplace=True)
        self.maxpool23 = nn.MaxPool2d(kernel_size=2, stride=2)

        self.conv24 = nn.Conv2d(512, 512, kernel_size=3, padding=1)
        self.relu25 = nn.ReLU(inplace=True)
        self.conv26 = nn.Conv2d(512, 512, kernel_size=3, padding=1)
        self.relu27 = nn.ReLU(inplace=True)
        self.conv28 = nn.Conv2d(512, 512, kernel_size=3, padding=1)
        self.relu29 = nn.ReLU(inplace=True)
        self.maxpool30 = nn.MaxPool2d(kernel_size=2, stride=2, padding=0)

        self.conv31 = nn.Conv2d(512, 1024, kernel_size=3, padding=3, dilation=3)
        self.relu32 = nn.ReLU(inplace=True)

        self.conv33 = nn.Conv2d(1024, 1024, kernel_size=1)
        self.relu34 = nn.ReLU(inplace=True)

        self.extras0 = nn.Conv2d(1024, 256, kernel_size=1)
        self.relu_e0 = nn.ReLU(inplace=True)
        self.extras1 = nn.Conv2d(256, 512, kernel_size=3, stride=2, padding=1)
        self.relu_e1 = nn.ReLU(inplace=True)

        self.conv4_3_L2Norm = L2Norm(512, 10)
        self.conv5_3_L2Norm = L2Norm(512, 8)

        self.arm_loc_0 = nn.Conv2d(512, 12, kernel_size=3, padding=1)
        self.arm_loc_1 = nn.Conv2d(512, 12, kernel_size=3, padding=1)
        self.arm_loc_2 = nn.Conv2d(1024, 12, kernel_size=3, padding=1)
        self.arm_loc_3 = nn.Conv2d(512, 12, kernel_size=3, padding=1)

        self.arm_conf_0 = nn.Conv2d(512, 6, kernel_size=3, padding=1)
        self.arm_conf_1 = nn.Conv2d(512, 6, kernel_size=3, padding=1)
        self.arm_conf_2 = nn.Conv2d(1024, 6, kernel_size=3, padding=1)
        self.arm_conf_3 = nn.Conv2d(512, 6, kernel_size=3, padding=1)

        self.tcb0_9 = nn.Conv2d(512, 256, 3, padding=1)
        self.tcb0_10 = nn.ReLU(inplace=True)
        self.tcb0_11 = nn.Conv2d(256, 256, 3, padding=1)

        self.tcb0_6 = nn.Conv2d(1024, 256, 3, padding=1)
        self.tcb0_7 = nn.ReLU(inplace=True)
        self.tcb0_8 = nn.Conv2d(256, 256, 3, padding=1)

        self.tcb0_3 = nn.Conv2d(512, 256, 3, padding=1)
        self.tcb0_4 = nn.ReLU(inplace=True)
        self.tcb0_5 = nn.Conv2d(256, 256, 3, padding=1)

        self.tcb0_0 = nn.Conv2d(512, 256, 3, padding=1)
        self.tcb0_1 = nn.ReLU(inplace=True)
        self.tcb0_2 = nn.Conv2d(256, 256, 3, padding=1)

        self.tcb2_0 = nn.ReLU(inplace=True)
        self.tcb2_1 = nn.Conv2d(256, 256, 3, padding=1)
        self.tcb2_2 = nn.ReLU(inplace=True)

        self.tcb2_3 = nn.ReLU(inplace=True)
        self.tcb2_4 = nn.Conv2d(256, 256, 3, padding=1)
        self.tcb2_5 = nn.ReLU(inplace=True)

        self.tcb2_6 = nn.ReLU(inplace=True)
        self.tcb2_7 = nn.Conv2d(256, 256, 3, padding=1)
        self.tcb2_8 = nn.ReLU(inplace=True)

        self.tcb2_9 = nn.ReLU(inplace=True)
        self.tcb2_10 = nn.Conv2d(256, 256, 3, padding=1)
        self.tcb2_11 = nn.ReLU(inplace=True)

        self.tcb1_2 = nn.ConvTranspose2d(256, 256, 2, 2)
        self.tcb1_1 = nn.ConvTranspose2d(256, 256, 2, 2)
        self.tcb1_0 = nn.ConvTranspose2d(256, 256, 2, 2)

        self.odm_loc_0 = nn.Conv2d(256, 12, kernel_size=3, padding=1)
        self.odm_loc_1 = nn.Conv2d(256, 12, kernel_size=3, padding=1)
        self.odm_loc_2 = nn.Conv2d(256, 12, kernel_size=3, padding=1)
        self.odm_loc_3 = nn.Conv2d(256, 12, kernel_size=3, padding=1)

        self.odm_conf_0 = nn.Conv2d(256, 3 * self.num_classes, kernel_size=3, padding=1)
        self.odm_conf_1 = nn.Conv2d(256, 3 * self.num_classes, kernel_size=3, padding=1)
        self.odm_conf_2 = nn.Conv2d(256, 3 * self.num_classes, kernel_size=3, padding=1)
        self.odm_conf_3 = nn.Conv2d(256, 3 * self.num_classes, kernel_size=3, padding=1)

        self.softmax = nn.Softmax(dim=-1)

    def forward(self, inputs):
        inputs = inputs.cuda()
        sources = list()
        tcb_source = list()

        x = self.relu1(self.conv0(inputs))
        x = self.relu3(self.conv2(x))
        x = self.maxpool4(x)

        x = self.relu6(self.conv5(x))
        x = self.relu8(self.conv7(x))
        x = self.maxpool9(x)

        x = self.relu11(self.conv10(x))
        x = self.relu13(self.conv12(x))
        x = self.relu15(self.conv14(x))
        x = self.maxpool16(x)

        x = self.relu18(self.conv17(x))
        x = self.relu20(self.conv19(x))
        x = self.relu22(self.conv21(x))
        out_conv4_3_L2Norm = x.clone()
        out_conv4_3_L2Norm = self.conv4_3_L2Norm(out_conv4_3_L2Norm)  ####s_0
        sources.append(out_conv4_3_L2Norm)
        x = self.maxpool23(x)

        x = self.relu25(self.conv24(x))
        x = self.relu27(self.conv26(x))
        x = self.relu29(self.conv28(x))
        out_conv5_3_L2Norm = x.clone()
        out_conv5_3_L2Norm = self.conv5_3_L2Norm(out_conv5_3_L2Norm) ####s_1
        sources.append(out_conv5_3_L2Norm)
        x = self.maxpool30(x)

        x = self.relu32(self.conv31(x))
        x = self.relu34(self.conv33(x))
        sources.append(x)
       return x

有一层搭一层,这样还可以随时调试,在forward函数里面想看哪一层输出就可以直接打断点看tensor值。

当然一开始不会一下子写这么多的,一开始都是pytorch里面写一层,然后tensorrt里面搭一层,然后对比两边结果一不一样,一样再继续往下走。
这里有几个问题:
1.需要保证两边送到网络的input一致才能验证网络输出是否一致
2.需要两边加载的权重是一致的才能才能验证网络输出是否一致
对于第一个问题,保证两边送到网络的input一致
pytorch读一张图代码,这张图我本地resize到320大小

    img = cv2.imread(path_img).astype(np.float32)
    img = img[:, :, (2, 1, 0)] ## bgr --> rgb
    img = img / 255.0
    img_2 = torch.from_numpy(img).permute(2, 0, 1) ## hwc --> chw
    bb0 = img_2.unsqueeze(0)
    out = net(img_2.unsqueeze(0))

然后对应的tensorrt里面读图的代码

        float data[3 * INPUT_H * INPUT_W];
        pr_img = cv::imread(path_img);
        for (int i = 0; i < INPUT_H * INPUT_W; i++) {
            data[i] = (float)(pr_img.at<cv::Vec3b>(i)[2]) * 1.0 / 255.0;
            data[i + INPUT_H * INPUT_W] = (float)(pr_img.at<cv::Vec3b>(i)[1]) * 1.0 / 255.0;
            data[i + 2 * INPUT_H * INPUT_W] = (float)(pr_img.at<cv::Vec3b>(i)[0]) * 1.0  / 255.0;
        }

这两者是等价的。(i)[2]代表的R通道,opencv读取的图片是BGR的格式。
对于第二个问题,保证两边加载的权重是一致
pytorch加载模型权重接口:
checkpoint = torch.load(path_model, map_location=torch.device('cpu'))
net.load_state_dict(checkpoint, strict=False)
这里面的参数strict,设置为true的时候就是需要加载的pth和网络每一层一致,不一致就会报错退出
设置为false的时候就是需要加载的pth和网络层有一致的就加载,不一致的就不加载。
checkpoint = torch.load(path_model, map_location=torch.device('cpu'))
这个pth通过torch.load之后就是相当于一个字典的格式,名字对应权重。
通过下面的代码,可以打印出网络需要的层名和权重大小。同时也打印出load的模型的层名和shape大小。

    net = refinedet_my()
    # net.eval()
    index = 0
    print("=" * 50)
    for name, param in list(net.named_parameters()):
        print(str(index) + ':', name, param.size())
        index += 1
    print("=" * 50)

    for k, v in net.state_dict().items():
        print(k,"  shape::",v.shape)

    print("@" * 50)

    checkpoint = torch.load(path_model, map_location=torch.device('cpu'))

    import collections
    new_state_dict = collections.OrderedDict()

    print("--------load pth name----------------------")
    for k, v in checkpoint.items():
        print(k,"  shape==",v.shape)

打印如下:

==================================================
0: conv0.weight torch.Size([64, 3, 3, 3])
1: conv0.bias torch.Size([64])
2: conv2.weight torch.Size([64, 64, 3, 3])
3: conv2.bias torch.Size([64])
4: conv5.weight torch.Size([128, 64, 3, 3])
5: conv5.bias torch.Size([128])
6: conv7.weight torch.Size([128, 128, 3, 3])
7: conv7.bias torch.Size([128])
8: conv10.weight torch.Size([256, 128, 3, 3])
9: conv10.bias torch.Size([256])
10: conv12.weight torch.Size([256, 256, 3, 3])
11: conv12.bias torch.Size([256])
12: conv14.weight torch.Size([256, 256, 3, 3])
13: conv14.bias torch.Size([256])
14: conv17.weight torch.Size([512, 256, 3, 3])
15: conv17.bias torch.Size([512])
16: conv19.weight torch.Size([512, 512, 3, 3])
17: conv19.bias torch.Size([512])
18: conv21.weight torch.Size([512, 512, 3, 3])
19: conv21.bias torch.Size([512])
20: conv24.weight torch.Size([512, 512, 3, 3])
21: conv24.bias torch.Size([512])
22: conv26.weight torch.Size([512, 512, 3, 3])
23: conv26.bias torch.Size([512])
24: conv28.weight torch.Size([512, 512, 3, 3])
25: conv28.bias torch.Size([512])
26: conv31.weight torch.Size([1024, 512, 3, 3])
27: conv31.bias torch.Size([1024])
28: conv33.weight torch.Size([1024, 1024, 1, 1])
29: conv33.bias torch.Size([1024])
30: extras0.weight torch.Size([256, 1024, 1, 1])
31: extras0.bias torch.Size([256])
32: extras1.weight torch.Size([512, 256, 3, 3])
33: extras1.bias torch.Size([512])
34: conv4_3_L2Norm.weight torch.Size([512])
35: conv5_3_L2Norm.weight torch.Size([512])
36: arm_loc_0.weight torch.Size([12, 512, 3, 3])
37: arm_loc_0.bias torch.Size([12])
38: arm_loc_1.weight torch.Size([12, 512, 3, 3])
39: arm_loc_1.bias torch.Size([12])
40: arm_loc_2.weight torch.Size([12, 1024, 3, 3])
41: arm_loc_2.bias torch.Size([12])
42: arm_loc_3.weight torch.Size([12, 512, 3, 3])
43: arm_loc_3.bias torch.Size([12])
44: arm_conf_0.weight torch.Size([6, 512, 3, 3])
45: arm_conf_0.bias torch.Size([6])
46: arm_conf_1.weight torch.Size([6, 512, 3, 3])
47: arm_conf_1.bias torch.Size([6])
48: arm_conf_2.weight torch.Size([6, 1024, 3, 3])
49: arm_conf_2.bias torch.Size([6])
50: arm_conf_3.weight torch.Size([6, 512, 3, 3])
51: arm_conf_3.bias torch.Size([6])
52: tcb0_9.weight torch.Size([256, 512, 3, 3])
53: tcb0_9.bias torch.Size([256])
54: tcb0_11.weight torch.Size([256, 256, 3, 3])
55: tcb0_11.bias torch.Size([256])
56: tcb0_6.weight torch.Size([256, 1024, 3, 3])
57: tcb0_6.bias torch.Size([256])
58: tcb0_8.weight torch.Size([256, 256, 3, 3])
59: tcb0_8.bias torch.Size([256])
60: tcb0_3.weight torch.Size([256, 512, 3, 3])
61: tcb0_3.bias torch.Size([256])
62: tcb0_5.weight torch.Size([256, 256, 3, 3])
63: tcb0_5.bias torch.Size([256])
64: tcb0_0.weight torch.Size([256, 512, 3, 3])
65: tcb0_0.bias torch.Size([256])
66: tcb0_2.weight torch.Size([256, 256, 3, 3])
67: tcb0_2.bias torch.Size([256])
68: tcb2_1.weight torch.Size([256, 256, 3, 3])
69: tcb2_1.bias torch.Size([256])
70: tcb2_4.weight torch.Size([256, 256, 3, 3])
71: tcb2_4.bias torch.Size([256])
72: tcb2_7.weight torch.Size([256, 256, 3, 3])
73: tcb2_7.bias torch.Size([256])
74: tcb2_10.weight torch.Size([256, 256, 3, 3])
75: tcb2_10.bias torch.Size([256])
76: tcb1_2.weight torch.Size([256, 256, 2, 2])
77: tcb1_2.bias torch.Size([256])
78: tcb1_1.weight torch.Size([256, 256, 2, 2])
79: tcb1_1.bias torch.Size([256])
80: tcb1_0.weight torch.Size([256, 256, 2, 2])
81: tcb1_0.bias torch.Size([256])
82: odm_loc_0.weight torch.Size([12, 256, 3, 3])
83: odm_loc_0.bias torch.Size([12])
84: odm_loc_1.weight torch.Size([12, 256, 3, 3])
85: odm_loc_1.bias torch.Size([12])
86: odm_loc_2.weight torch.Size([12, 256, 3, 3])
87: odm_loc_2.bias torch.Size([12])
88: odm_loc_3.weight torch.Size([12, 256, 3, 3])
89: odm_loc_3.bias torch.Size([12])
90: odm_conf_0.weight torch.Size([75, 256, 3, 3])
91: odm_conf_0.bias torch.Size([75])
92: odm_conf_1.weight torch.Size([75, 256, 3, 3])
93: odm_conf_1.bias torch.Size([75])
94: odm_conf_2.weight torch.Size([75, 256, 3, 3])
95: odm_conf_2.bias torch.Size([75])
96: odm_conf_3.weight torch.Size([75, 256, 3, 3])
97: odm_conf_3.bias torch.Size([75])
==================================================
conv0.weight   shape:: torch.Size([64, 3, 3, 3])
conv0.bias   shape:: torch.Size([64])
conv2.weight   shape:: torch.Size([64, 64, 3, 3])
conv2.bias   shape:: torch.Size([64])
conv5.weight   shape:: torch.Size([128, 64, 3, 3])
conv5.bias   shape:: torch.Size([128])
conv7.weight   shape:: torch.Size([128, 128, 3, 3])
conv7.bias   shape:: torch.Size([128])
conv10.weight   shape:: torch.Size([256, 128, 3, 3])
conv10.bias   shape:: torch.Size([256])
conv12.weight   shape:: torch.Size([256, 256, 3, 3])
conv12.bias   shape:: torch.Size([256])
conv14.weight   shape:: torch.Size([256, 256, 3, 3])
conv14.bias   shape:: torch.Size([256])
conv17.weight   shape:: torch.Size([512, 256, 3, 3])
conv17.bias   shape:: torch.Size([512])
conv19.weight   shape:: torch.Size([512, 512, 3, 3])
conv19.bias   shape:: torch.Size([512])
conv21.weight   shape:: torch.Size([512, 512, 3, 3])
conv21.bias   shape:: torch.Size([512])
conv24.weight   shape:: torch.Size([512, 512, 3, 3])
conv24.bias   shape:: torch.Size([512])
conv26.weight   shape:: torch.Size([512, 512, 3, 3])
conv26.bias   shape:: torch.Size([512])
conv28.weight   shape:: torch.Size([512, 512, 3, 3])
conv28.bias   shape:: torch.Size([512])
conv31.weight   shape:: torch.Size([1024, 512, 3, 3])
conv31.bias   shape:: torch.Size([1024])
conv33.weight   shape:: torch.Size([1024, 1024, 1, 1])
conv33.bias   shape:: torch.Size([1024])
extras0.weight   shape:: torch.Size([256, 1024, 1, 1])
extras0.bias   shape:: torch.Size([256])
extras1.weight   shape:: torch.Size([512, 256, 3, 3])
extras1.bias   shape:: torch.Size([512])
conv4_3_L2Norm.weight   shape:: torch.Size([512])
conv5_3_L2Norm.weight   shape:: torch.Size([512])
arm_loc_0.weight   shape:: torch.Size([12, 512, 3, 3])
arm_loc_0.bias   shape:: torch.Size([12])
arm_loc_1.weight   shape:: torch.Size([12, 512, 3, 3])
arm_loc_1.bias   shape:: torch.Size([12])
arm_loc_2.weight   shape:: torch.Size([12, 1024, 3, 3])
arm_loc_2.bias   shape:: torch.Size([12])
arm_loc_3.weight   shape:: torch.Size([12, 512, 3, 3])
arm_loc_3.bias   shape:: torch.Size([12])
arm_conf_0.weight   shape:: torch.Size([6, 512, 3, 3])
arm_conf_0.bias   shape:: torch.Size([6])
arm_conf_1.weight   shape:: torch.Size([6, 512, 3, 3])
arm_conf_1.bias   shape:: torch.Size([6])
arm_conf_2.weight   shape:: torch.Size([6, 1024, 3, 3])
arm_conf_2.bias   shape:: torch.Size([6])
arm_conf_3.weight   shape:: torch.Size([6, 512, 3, 3])
arm_conf_3.bias   shape:: torch.Size([6])
tcb0_9.weight   shape:: torch.Size([256, 512, 3, 3])
tcb0_9.bias   shape:: torch.Size([256])
tcb0_11.weight   shape:: torch.Size([256, 256, 3, 3])
tcb0_11.bias   shape:: torch.Size([256])
tcb0_6.weight   shape:: torch.Size([256, 1024, 3, 3])
tcb0_6.bias   shape:: torch.Size([256])
tcb0_8.weight   shape:: torch.Size([256, 256, 3, 3])
tcb0_8.bias   shape:: torch.Size([256])
tcb0_3.weight   shape:: torch.Size([256, 512, 3, 3])
tcb0_3.bias   shape:: torch.Size([256])
tcb0_5.weight   shape:: torch.Size([256, 256, 3, 3])
tcb0_5.bias   shape:: torch.Size([256])
tcb0_0.weight   shape:: torch.Size([256, 512, 3, 3])
tcb0_0.bias   shape:: torch.Size([256])
tcb0_2.weight   shape:: torch.Size([256, 256, 3, 3])
tcb0_2.bias   shape:: torch.Size([256])
tcb2_1.weight   shape:: torch.Size([256, 256, 3, 3])
tcb2_1.bias   shape:: torch.Size([256])
tcb2_4.weight   shape:: torch.Size([256, 256, 3, 3])
tcb2_4.bias   shape:: torch.Size([256])
tcb2_7.weight   shape:: torch.Size([256, 256, 3, 3])
tcb2_7.bias   shape:: torch.Size([256])
tcb2_10.weight   shape:: torch.Size([256, 256, 3, 3])
tcb2_10.bias   shape:: torch.Size([256])
tcb1_2.weight   shape:: torch.Size([256, 256, 2, 2])
tcb1_2.bias   shape:: torch.Size([256])
tcb1_1.weight   shape:: torch.Size([256, 256, 2, 2])
tcb1_1.bias   shape:: torch.Size([256])
tcb1_0.weight   shape:: torch.Size([256, 256, 2, 2])
tcb1_0.bias   shape:: torch.Size([256])
odm_loc_0.weight   shape:: torch.Size([12, 256, 3, 3])
odm_loc_0.bias   shape:: torch.Size([12])
odm_loc_1.weight   shape:: torch.Size([12, 256, 3, 3])
odm_loc_1.bias   shape:: torch.Size([12])
odm_loc_2.weight   shape:: torch.Size([12, 256, 3, 3])
odm_loc_2.bias   shape:: torch.Size([12])
odm_loc_3.weight   shape:: torch.Size([12, 256, 3, 3])
odm_loc_3.bias   shape:: torch.Size([12])
odm_conf_0.weight   shape:: torch.Size([75, 256, 3, 3])
odm_conf_0.bias   shape:: torch.Size([75])
odm_conf_1.weight   shape:: torch.Size([75, 256, 3, 3])
odm_conf_1.bias   shape:: torch.Size([75])
odm_conf_2.weight   shape:: torch.Size([75, 256, 3, 3])
odm_conf_2.bias   shape:: torch.Size([75])
odm_conf_3.weight   shape:: torch.Size([75, 256, 3, 3])
odm_conf_3.bias   shape:: torch.Size([75])
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
--------load pth name----------------------
vgg.0.weight   shape== torch.Size([64, 3, 3, 3])
vgg.0.bias   shape== torch.Size([64])
vgg.2.weight   shape== torch.Size([64, 64, 3, 3])
vgg.2.bias   shape== torch.Size([64])
vgg.5.weight   shape== torch.Size([128, 64, 3, 3])
vgg.5.bias   shape== torch.Size([128])
vgg.7.weight   shape== torch.Size([128, 128, 3, 3])
vgg.7.bias   shape== torch.Size([128])
vgg.10.weight   shape== torch.Size([256, 128, 3, 3])
vgg.10.bias   shape== torch.Size([256])
vgg.12.weight   shape== torch.Size([256, 256, 3, 3])
vgg.12.bias   shape== torch.Size([256])
vgg.14.weight   shape== torch.Size([256, 256, 3, 3])
vgg.14.bias   shape== torch.Size([256])
vgg.17.weight   shape== torch.Size([512, 256, 3, 3])
vgg.17.bias   shape== torch.Size([512])
vgg.19.weight   shape== torch.Size([512, 512, 3, 3])
vgg.19.bias   shape== torch.Size([512])
vgg.21.weight   shape== torch.Size([512, 512, 3, 3])
vgg.21.bias   shape== torch.Size([512])
vgg.24.weight   shape== torch.Size([512, 512, 3, 3])
vgg.24.bias   shape== torch.Size([512])
vgg.26.weight   shape== torch.Size([512, 512, 3, 3])
vgg.26.bias   shape== torch.Size([512])
vgg.28.weight   shape== torch.Size([512, 512, 3, 3])
vgg.28.bias   shape== torch.Size([512])
vgg.31.weight   shape== torch.Size([1024, 512, 3, 3])
vgg.31.bias   shape== torch.Size([1024])
vgg.33.weight   shape== torch.Size([1024, 1024, 1, 1])
vgg.33.bias   shape== torch.Size([1024])
conv4_3_L2Norm.weight   shape== torch.Size([512])
conv5_3_L2Norm.weight   shape== torch.Size([512])
extras.0.weight   shape== torch.Size([256, 1024, 1, 1])
extras.0.bias   shape== torch.Size([256])
extras.1.weight   shape== torch.Size([512, 256, 3, 3])
extras.1.bias   shape== torch.Size([512])
arm_loc.0.weight   shape== torch.Size([12, 512, 3, 3])
arm_loc.0.bias   shape== torch.Size([12])
arm_loc.1.weight   shape== torch.Size([12, 512, 3, 3])
arm_loc.1.bias   shape== torch.Size([12])
arm_loc.2.weight   shape== torch.Size([12, 1024, 3, 3])
arm_loc.2.bias   shape== torch.Size([12])
arm_loc.3.weight   shape== torch.Size([12, 512, 3, 3])
arm_loc.3.bias   shape== torch.Size([12])
arm_conf.0.weight   shape== torch.Size([6, 512, 3, 3])
arm_conf.0.bias   shape== torch.Size([6])
arm_conf.1.weight   shape== torch.Size([6, 512, 3, 3])
arm_conf.1.bias   shape== torch.Size([6])
arm_conf.2.weight   shape== torch.Size([6, 1024, 3, 3])
arm_conf.2.bias   shape== torch.Size([6])
arm_conf.3.weight   shape== torch.Size([6, 512, 3, 3])
arm_conf.3.bias   shape== torch.Size([6])
odm_loc.0.weight   shape== torch.Size([12, 256, 3, 3])
odm_loc.0.bias   shape== torch.Size([12])
odm_loc.1.weight   shape== torch.Size([12, 256, 3, 3])
odm_loc.1.bias   shape== torch.Size([12])
odm_loc.2.weight   shape== torch.Size([12, 256, 3, 3])
odm_loc.2.bias   shape== torch.Size([12])
odm_loc.3.weight   shape== torch.Size([12, 256, 3, 3])
odm_loc.3.bias   shape== torch.Size([12])
odm_conf.0.weight   shape== torch.Size([75, 256, 3, 3])
odm_conf.0.bias   shape== torch.Size([75])
odm_conf.1.weight   shape== torch.Size([75, 256, 3, 3])
odm_conf.1.bias   shape== torch.Size([75])
odm_conf.2.weight   shape== torch.Size([75, 256, 3, 3])
odm_conf.2.bias   shape== torch.Size([75])
odm_conf.3.weight   shape== torch.Size([75, 256, 3, 3])
odm_conf.3.bias   shape== torch.Size([75])
tcb0.0.weight   shape== torch.Size([256, 512, 3, 3])
tcb0.0.bias   shape== torch.Size([256])
tcb0.2.weight   shape== torch.Size([256, 256, 3, 3])
tcb0.2.bias   shape== torch.Size([256])
tcb0.3.weight   shape== torch.Size([256, 512, 3, 3])
tcb0.3.bias   shape== torch.Size([256])
tcb0.5.weight   shape== torch.Size([256, 256, 3, 3])
tcb0.5.bias   shape== torch.Size([256])
tcb0.6.weight   shape== torch.Size([256, 1024, 3, 3])
tcb0.6.bias   shape== torch.Size([256])
tcb0.8.weight   shape== torch.Size([256, 256, 3, 3])
tcb0.8.bias   shape== torch.Size([256])
tcb0.9.weight   shape== torch.Size([256, 512, 3, 3])
tcb0.9.bias   shape== torch.Size([256])
tcb0.11.weight   shape== torch.Size([256, 256, 3, 3])
tcb0.11.bias   shape== torch.Size([256])
tcb1.0.weight   shape== torch.Size([256, 256, 2, 2])
tcb1.0.bias   shape== torch.Size([256])
tcb1.1.weight   shape== torch.Size([256, 256, 2, 2])
tcb1.1.bias   shape== torch.Size([256])
tcb1.2.weight   shape== torch.Size([256, 256, 2, 2])
tcb1.2.bias   shape== torch.Size([256])
tcb2.1.weight   shape== torch.Size([256, 256, 3, 3])
tcb2.1.bias   shape== torch.Size([256])
tcb2.4.weight   shape== torch.Size([256, 256, 3, 3])
tcb2.4.bias   shape== torch.Size([256])
tcb2.7.weight   shape== torch.Size([256, 256, 3, 3])
tcb2.7.bias   shape== torch.Size([256])
tcb2.10.weight   shape== torch.Size([256, 256, 3, 3])
tcb2.10.bias   shape== torch.Size([256])

可以看到,我们自定义的网络和加载的pth层名有些许不一样,比如net里面的层名

tcb0_2.weight   shape:: torch.Size([256, 256, 3, 3])
tcb0_2.bias   shape:: torch.Size([256])
tcb2_1.weight   shape:: torch.Size([256, 256, 3, 3])
tcb2_1.bias   shape:: torch.Size([256])

对应pth层名

tcb0.2.weight   shape== torch.Size([256, 256, 3, 3])
tcb0.2.bias   shape== torch.Size([256])
tcb2.1.weight   shape== torch.Size([256, 256, 3, 3])
tcb2.1.bias   shape== torch.Size([256])

还有其他的,我现在要做的就是把pth的层名改成和net所需要的层名一致,才能加载到net中。对应代码如下:

    checkpoint = torch.load(path_model, map_location=torch.device('cpu'))
    import collections
    new_state_dict = collections.OrderedDict()

    for k, v in checkpoint.items():
        name = k.replace('vgg.', 'conv')
        name = name.replace('extras.', 'extras')
        if "arm" in name or "loc" in name or "tcb" in name or "odm" in name:
            name = name.replace(".","_",1)
        new_state_dict[name] = v

    net.load_state_dict(new_state_dict, strict=False)

这样的话就保证了两边加载了一样的权重了。

然后在tensorrt里面搭建对应的操作,类似代码如下

// Creat the engine using only the API and not any parser.
ICudaEngine* createEngine(unsigned int maxBatchSize, IBuilder* builder, IBuilderConfig* config, DataType dt) {
    INetworkDefinition* network = builder->createNetworkV2(0U);

    ITensor* data = network->addInput(INPUT_BLOB_NAME, dt, Dims3{3, INPUT_H, INPUT_W});
    assert(data);

    std::map<std::string, Weights> weightMap = loadWeights(path_wts);
    Weights emptywts{DataType::kFLOAT, nullptr, 0};
    DimsHW maxpool_hw = DimsHW(2,2);

    auto lr0 = convRelu(network, weightMap, *data, 64, 3, 1, 1, 0);
    auto lr1 = convRelu(network, weightMap, *lr0->getOutput(0), 64, 3, 1, 1, 2);
    IPoolingLayer* pool1 = network->addPoolingNd(*lr1->getOutput(0), PoolingType::kMAX, DimsHW{2, 2});
    assert(pool1);
    pool1->setStrideNd(DimsHW{2, 2});

    auto lr2 = convRelu(network, weightMap, *pool1->getOutput(0), 128, 3, 1, 1, 5);
    auto lr3 = convRelu(network, weightMap, *lr2->getOutput(0), 128, 3, 1, 1, 7);
    IPoolingLayer* pool2 = network->addPoolingNd(*lr3->getOutput(0), PoolingType::kMAX, DimsHW{2, 2});
    assert(pool2);
    pool2->setStrideNd(DimsHW{2, 2});

    auto lr4 = convRelu(network, weightMap, *pool2->getOutput(0), 256, 3, 1, 1, 10);
    auto lr5 = convRelu(network, weightMap, *lr4->getOutput(0), 256, 3, 1, 1, 12);
    auto lr6 = convRelu(network, weightMap, *lr5->getOutput(0), 256, 3, 1, 1, 14);
    IPoolingLayer* pool3 = network->addPoolingNd(*lr6->getOutput(0), PoolingType::kMAX, DimsHW{2, 2});
    assert(pool3);
    pool3->setStrideNd(DimsHW{2, 2});

    auto lr7 = convRelu(network, weightMap, *pool3->getOutput(0), 512, 3, 1, 1, 17);
    auto lr8 = convRelu(network, weightMap, *lr7->getOutput(0), 512, 3, 1, 1, 19);
    auto lr9 = convRelu(network, weightMap, *lr8->getOutput(0), 512, 3, 1, 1, 21);
    IPoolingLayer* pool4 = network->addPoolingNd(*lr9->getOutput(0), PoolingType::kMAX, DimsHW{2, 2});
    assert(pool4);
    pool4->setStrideNd(DimsHW{2, 2});

    auto lr24 = convRelu(network, weightMap, *pool4->getOutput(0), 512, 3, 1, 1, 24);
    auto lr26 = convRelu(network, weightMap, *lr24->getOutput(0), 512, 3, 1, 1, 26);
    auto lr28 = convRelu(network, weightMap, *lr26->getOutput(0), 512, 3, 1, 1, 28);

4.验证精度

tensorrt比较麻烦,需要先生成engine,然后本地再加载engine推理。
在搭建模型的时候,想验证哪一层就让这一层输出。

    auto lr0 = convRelu(network, weightMap, *data, 64, 3, 1, 1, 0);

    lr0->getOutput(0)->setName("out");
    network->markOutput(*lr0->getOutput(0));


    builder->setMaxBatchSize(maxBatchSize);
    config->setMaxWorkspaceSize(16 * (1 << 20));  // 16MB
    ICudaEngine* engine = builder->buildEngineWithConfig(*network, *config);
    std::cout << "Build engine successfully!" << std::endl;
    // Don't need the network any more
    network->destroy();
    // Release host memory
    for (auto& mem : weightMap)
    {
        free((void*) (mem.second.values));
    }

    return engine;

后续代码会本地生成engine,然后加载跑推理,但是需要改,推理是需要指定输出的大小的。具体见下面代码

void doInference(IExecutionContext& context, float* input, float* output, int batchSize) {
    const ICudaEngine& engine = context.getEngine();

    // Pointers to input and output device buffers to pass to engine.
    // Engine requires exactly IEngine::getNbBindings() number of buffers.
    std::cout<<"engine.getNbBindings()==="<<engine.getNbBindings()<<std::endl;
    assert(engine.getNbBindings() == 2);//getNbBindings()输入和输出的个数
    void* buffers[2];

    // In order to bind the buffers, we need to know the names of the input and output tensors.
    // Note that indices are guaranteed to be less than IEngine::getNbBindings()
    const int inputIndex = engine.getBindingIndex(INPUT_BLOB_NAME);
    const int outputIndex = engine.getBindingIndex(OUTPUT_BLOB_NAME);
    printf("inputIndex=%d
",inputIndex);
    printf("outputIndex=%d
",outputIndex);

    // Create GPU buffers on device
    CUDA_CHECK(cudaMalloc(&buffers[inputIndex], batchSize * 3 * INPUT_H_refinedet * INPUT_W_refinedet * sizeof(float)));

    const int OUTPUT_SIZE_2 = 1 * 64 * 160 * 160; //大小需要自己知道写这里  分配显存
    CUDA_CHECK(cudaMalloc(&buffers[outputIndex], batchSize * OUTPUT_SIZE_2 * sizeof(float)));

    // Create stream
    cudaStream_t stream;
    CUDA_CHECK(cudaStreamCreate(&stream));

    // DMA input batch data to device, infer on the batch asynchronously, and DMA output back to host
    CUDA_CHECK(cudaMemcpyAsync(buffers[inputIndex], input, batchSize * 3 * INPUT_H_refinedet * INPUT_W_refinedet * sizeof(float), cudaMemcpyHostToDevice, stream));
    context.enqueue(batchSize, buffers, stream, nullptr); //推理!!!
    CUDA_CHECK(cudaMemcpyAsync(output, buffers[outputIndex], batchSize * OUTPUT_SIZE_2 * sizeof(float), cudaMemcpyDeviceToHost, stream)); //输出从cuda给到cpu
    cudaStreamSynchronize(stream);

    // Release stream and buffers
    cudaStreamDestroy(stream);
    CUDA_CHECK(cudaFree(buffers[inputIndex]));
    CUDA_CHECK(cudaFree(buffers[outputIndex]));
}

然后函数外面就可以output看输出的值和pytorch对比。看看是不是一致的。应该是一致的。

5.不支持的层实现

对于不支持的层需要自己实现,写plugin或者用既有的api实现。
这里我耗费了几天。见下面博客。
【L2norm 层tensorrt api实现】(https://www.cnblogs.com/yanghailin/p/14448829.html)

还有一个softmax也耗费了我大半天时间:
【指定维度softmax 层tensorRT api实现】(https://www.cnblogs.com/yanghailin/p/14486077.html)

6.后处理

后处理是个大坑。
也不叫大坑吧,是有难度。因为需要用到cuda编程。需要把任务分解成可并行的格式来做。
我看到的yolov3里面就是自己写了个plugin来做后处理。
我一开始也准备这么搞的,之前也看了centernet的tensorrt实现。
https://github.com/CaoWGG/TensorRT-CenterNet
https://github.com/CaoWGG/TensorRT-CenterNet/blob/f949252e37b51e60f873808f46d3683f15735e79/src/ctdetNet.cpp#L146
这份代码也比较优秀,基于tensorrt5.0实现的。这份代码是通过tensorrt推理出来cuda的数组,然后自己写的cuda函数完成后处理.
然后我就想,我也可以这么做,写plugin要按照好多格式太麻烦了。
这里还有个问题是refinedet网络出来的是4个tensor,如果要4个输出的话没有看到有人这么做过,但是后面我自己摸索,是可以输出多个tensor的。

    arm_loc->getOutput(0)->setName(OUTPUT_BLOB_NAME_arm_loc);
    network->markOutput(*arm_loc->getOutput(0));

  arm_conf_111->getOutput(0)->setName(OUTPUT_BLOB_NAME_arm_conf);
    network->markOutput(*arm_conf_111->getOutput(0));

 odm_loc->getOutput(0)->setName(OUTPUT_BLOB_NAME_odm_loc);
    network->markOutput(*odm_loc->getOutput(0));

 odm_conf->getOutput(0)->setName(OUTPUT_BLOB_NAME_odm_conf);
    network->markOutput(*odm_conf->getOutput(0));

定义网络的时候直接多个输出。然后推理的时候,engine.getNbBindings()就是5,context.enqueue(batchSize, buffers, stream, nullptr);这里的buffer就是5维的数据

void doInference(IExecutionContext& context, void* buffers[], cudaStream_t &stream, float* input, std::vector<std::vector<float>> &detections) {
    auto start_infer = std::chrono::system_clock::now();
    detections.clear();
    int batchSize = 1;
    const ICudaEngine& engine = context.getEngine();

    // Pointers to input and output device buffers to pass to engine.
    // Engine requires exactly IEngine::getNbBindings() number of buffers.
//    std::cout<<"engine.getNbBindings()==="<<engine.getNbBindings()<<std::endl;
    assert(engine.getNbBindings() == 5);

    // In order to bind the buffers, we need to know the names of the input and output tensors.
    // Note that indices are guaranteed to be less than IEngine::getNbBindings()
    const int inputIndex = engine.getBindingIndex(INPUT_BLOB_NAME);
    const int outputIndex_arm_loc = engine.getBindingIndex(OUTPUT_BLOB_NAME_arm_loc);
    const int outputIndex_arm_conf = engine.getBindingIndex(OUTPUT_BLOB_NAME_arm_conf);
    const int outputIndex_odm_loc = engine.getBindingIndex(OUTPUT_BLOB_NAME_odm_loc);
    const int outputIndex_odm_conf = engine.getBindingIndex(OUTPUT_BLOB_NAME_odm_conf);
//    const int outputIndex2 = engine.getBindingIndex("prob2");
//    printf("inputIndex=%d
",inputIndex);
//    printf("outputIndex_arm_loc=%d
",outputIndex_arm_loc);
//    printf("outputIndex_arm_conf=%d
",outputIndex_arm_conf);
//    printf("outputIndex_odm_loc=%d
",outputIndex_odm_loc);
//    printf("outputIndex_odm_conf=%d
",outputIndex_odm_conf);

    // DMA input batch data to device, infer on the batch asynchronously, and DMA output back to host
    CUDA_CHECK(cudaMemcpyAsync(buffers[inputIndex], input, batchSize * 3 * INPUT_H * INPUT_W * sizeof(float), cudaMemcpyHostToDevice, stream));
    context.enqueue(batchSize, buffers, stream, nullptr);

context.enqueue(batchSize, buffers, stream, nullptr);
所以其实到这里为止,我就得到了4个在cuda上面的tensor,并且数值和pytorch里面是一致的。
看cuda编程看了几天,对应我们这个任务还是无从下手。请教了大神,说尽快和我讨论。然后就没有下文了。
没有办法,我仔细盯着pytorch的后处理实现

 def forward(self, num_classes, size, bkg_label, top_k, conf_thresh, nms_thresh,
                objectness_thre, keep_top_k,arm_loc_data, arm_conf_data, odm_loc_data, odm_conf_data, prior_data):
        """
        Args:
            loc_data: (tensor) Loc preds from loc layers
                Shape: [batch,num_priors*4]
            conf_data: (tensor) Shape: Conf preds from conf layers
                Shape: [batch*num_priors,num_classes]
            prior_data: (tensor) Prior boxes and variances from priorbox layers
                Shape: [1,num_priors,4]

                arm_loc_data: torch.Size([1, 6375, 4])
                arm_conf_data: torch.Size([1, 6375, 2])
                odm_loc_data: torch.Size([1, 6375, 4])
                odm_conf_data: torch.Size([1, 6375, 25])
                prior_data: torch.Size([6375, 4])
        """

        self.num_classes = num_classes
        self.background_label = bkg_label
        self.top_k = top_k
        self.keep_top_k = keep_top_k
        # Parameters used in nms.
        self.nms_thresh = nms_thresh
        if nms_thresh <= 0:
            raise ValueError('nms_threshold must be non negative.')
        self.conf_thresh = conf_thresh  ## 0.01
        self.objectness_thre = objectness_thre  ## 0.01
        self.variance = cfg[str(size)]['variance']


        loc_data = odm_loc_data  #[1, 6375, 4]
        conf_data = odm_conf_data #[1, 6375, 25]
        # [1,6375,1]             #[1,6375,2]    --->>> [1,6375,1]
        arm_object_conf = arm_conf_data.data[:, :, 1:]
        # [1,6375,1]
        no_object_index = arm_object_conf <= self.objectness_thre
        conf_data[no_object_index.expand_as(conf_data)] = 0  ##[1, 6375, 25]

        num = loc_data.size(0)  # 1 batch size: 1
        num_priors = prior_data.size(0) #6375
        output = torch.zeros(num, self.num_classes, self.top_k, 5)##[1,25,1000,5]
        conf_preds = conf_data.view(num, num_priors,
                                    self.num_classes).transpose(2, 1)
        ####[1,25,6375]

        # Decode predictions into bboxes.
        for i in range(num):
            #[6375,4]          [6375,4]        [6375,4]
            default = decode(arm_loc_data[i], prior_data, self.variance)

            default = center_size(default)
            #[6375,4]                       #[6375,4]    [6375,4]
            decoded_boxes = decode(loc_data[i], default, self.variance)
            #[25,6375]   For each class, perform nms
            conf_scores = conf_preds[i].clone()
            #print(decoded_boxes, conf_scores)
            for cl in range(1, self.num_classes):
                c_mask = conf_scores[cl].gt(self.conf_thresh)
                scores = conf_scores[cl][c_mask]
                #print(scores.dim())
                if scores.size(0) == 0:
                    continue
                l_mask = c_mask.unsqueeze(1).expand_as(decoded_boxes)
                boxes = decoded_boxes[l_mask].view(-1, 4)
                #或者boxes = decoded_boxes[c_mask].view(-1, 4)

                # idx of highest scoring and non-overlapping boxes per class
                #print(boxes, scores)
                ids, count = nms(boxes, scores, self.nms_thresh, self.top_k)
                output[i, cl, :count] = 
                    torch.cat((scores[ids[:count]].unsqueeze(1),
                               boxes[ids[:count]]), 1)
        flt = output.contiguous().view(num, -1, 5)
        _, idx = flt[:, :, 0].sort(1, descending=True)
        _, rank = idx.sort(1)
        flt[(rank < self.keep_top_k).unsqueeze(-1).expand_as(flt)].fill_(0)
        return output

挺复杂的,都是坐标索引,维度变换,想用cuda实现,想想就头疼。cuda上面各个tensor就是一维数组。这咋搞。
然后有一天盯着这段代码,抱着试试的心态,把cuda数据看看能不能转libtorch,因为我libtorch的实现是已经写好的。
然后在cmakelist里面添加libtorch库。

 assert(engine.getNbBindings() == 5);

    // In order to bind the buffers, we need to know the names of the input and output tensors.
    // Note that indices are guaranteed to be less than IEngine::getNbBindings()
    const int inputIndex = engine.getBindingIndex(INPUT_BLOB_NAME);
    const int outputIndex_arm_loc = engine.getBindingIndex(OUTPUT_BLOB_NAME_arm_loc);
    const int outputIndex_arm_conf = engine.getBindingIndex(OUTPUT_BLOB_NAME_arm_conf);
    const int outputIndex_odm_loc = engine.getBindingIndex(OUTPUT_BLOB_NAME_odm_loc);
    const int outputIndex_odm_conf = engine.getBindingIndex(OUTPUT_BLOB_NAME_odm_conf);
//    const int outputIndex2 = engine.getBindingIndex("prob2");
//    printf("inputIndex=%d
",inputIndex);
//    printf("outputIndex_arm_loc=%d
",outputIndex_arm_loc);
//    printf("outputIndex_arm_conf=%d
",outputIndex_arm_conf);
//    printf("outputIndex_odm_loc=%d
",outputIndex_odm_loc);
//    printf("outputIndex_odm_conf=%d
",outputIndex_odm_conf);

    // DMA input batch data to device, infer on the batch asynchronously, and DMA output back to host
    CUDA_CHECK(cudaMemcpyAsync(buffers[inputIndex], input, batchSize * 3 * INPUT_H * INPUT_W * sizeof(float), cudaMemcpyHostToDevice, stream));
    context.enqueue(batchSize, buffers, stream, nullptr);
   
    int m_prior_size = 6375;
    torch::Tensor arm_loc = torch::from_blob(buffers[outputIndex_arm_loc],{m_prior_size,4}).cuda().toType(torch::kFloat64).unsqueeze(0);
    torch::Tensor arm_conf = torch::from_blob(buffers[outputIndex_arm_conf],{m_prior_size,2}).cuda().toType(torch::kFloat64).unsqueeze(0);
    torch::Tensor odm_loc = torch::from_blob(buffers[outputIndex_odm_loc],{m_prior_size,4}).cuda().toType(torch::kFloat64).unsqueeze(0);
    torch::Tensor odm_conf = torch::from_blob(buffers[outputIndex_odm_conf],{m_prior_size,25}).cuda().toType(torch::kFloat64).unsqueeze(0);

居然可以啊!!!
厉害了,试了这里可以访问torch::Tensor arm_loc里面数据,并且是对的!
然后我把后处理代码搬过来,是可以跑通出效果图的!
至此,后处理就可以用libtorch,摆脱cuda编程了!

7.性能比较


int8会多检测出很多框,但是评价脚本对多出框这没有惩罚,所以导致精度差不多。
int8多出框导致后处理时间变长。

小弟不才,同时谢谢友情赞助!

好记性不如烂键盘---点滴、积累、进步!
原文地址:https://www.cnblogs.com/yanghailin/p/14525128.html