6. RT-DETR(目标检测)

RT-DETR是百度团队提出基于Transformer的实时的端到端检测器。

RT-DETR设计了一个高效的混合编码器,通过解耦尺度内交互和跨尺度融合来高效处理多尺度特征, 并提出了IoU感知的查询选择机制,以优化解码器查询的初始化。

此外,RT-DETR支持通过使用不同的解码器层来灵活调整推理速度,而不需要重新训练,有助于实时目标检测器的实际应用。

broken

RT-DETRv2在SOTA的RT-DETR的基础上,引入了灵活的解码器,并运用了一系列有效的训练策略, 为适应各种部署方案,解码器现在提供了一个利用离散采样而非网格采样的选项。

RT-DETR更多详情请查看论文:

DETRs Beat YOLOs on Real-time Object Detection

RT-DETRv2: Improved Baseline with Bag-of-Freebies for Real-Time Detection Transformer

RT-DETR/RT-DETRv2源码地址:https://github.com/lyuwenyu/RT-DETR

本章将简单测试RT-DETRv2模型,并在鲁班猫板卡上部署测试。

6.1. RT-DETRv2推理测试

RT-DETRv2源码在 RT-DETR 工程文件中,教程测试使用Pytorch,就是对应工程rtdetrv2_pytorch目录下的源码。 使用paddlepaddle的参考下 这里

使用Anaconda进行环境管理,创建一个环境:

conda create -n rtdetr python=3.10
conda activate rtdetr

# 配置pip源
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple/

# 拉取
git clone https://github.com/lyuwenyu/RT-DETR

# 切换到rtdetrv2_pytorch目录下
cd RT-DETR/rtdetrv2_pytorch

pip install -r  requirements.txt

测试rtdetrv2_pytorch源码中提供的推理例程,查看rtdetrv2_pytorch目录下README.md文件,获取配置和权重文件。 为了与后面板卡上部署,教程测试的是离散采样调优模型RT-DETRv2-S_dsp,基础模型采样方法是grid_sampling。

broken
cd RT-DETR/rtdetrv2_pytorch

# 获取RT-DETRv2-S_dsp模型的checkpoint,使用的配置文件configs/rtdetrv2/rtdetrv2_r18vd_dsp_3x_coco.yml
wget https://github.com/lyuwenyu/storage/releases/download/v0.1/rtdetrv2_r18vd_dsp_3x_coco.pth

cp references/deploy/rtdetrv2_torch.py  ./

# -c指定配置文件,-r指定checkpoint路径,--im-file指定测试文件路径
python rtdetrv2_torch.py -c configs/rtdetrv2/rtdetrv2_r18vd_dsp_3x_coco.yml \
                -r ./rtdetrv2_r18vd_dsp_3x_coco.pth --im-file=./test.jpg

结果保存在但其目录下results_0.jpg(这里指定了一个测试文件),教程测试结果如下:

broken

6.2. 模型转换

将RT-DETRv2-S_dsp模型导出成onnx模型,简单修改源码rtdetrv2_pytorch/tools/export_onnx.py, 使导出的onnx模型不包括源码提供的后处理,输入固定为(1, 3, 640, 640),模型输出pred_logits添加sigmoid。具体修改如下:

6.2.1. 导出onnx模型

export_onnx_dsp.py(参考配套例程)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 省略............
class Model(nn.Module):
    def __init__(self, ) -> None:
        super().__init__()
        self.model = cfg.model.deploy()
        self.postprocessor = cfg.postprocessor.deploy()

    def forward(self, images):
        outputs = self.model(images)
        # outputs = self.postprocessor(outputs, orig_target_sizes) orig_target_sizes
        outputs = torch.sigmoid(outputs['pred_logits']), outputs['pred_boxes']
        return outputs

model = Model()

data = torch.rand(1, 3, 640, 640)
model.eval()

torch.onnx.export(
    model,
    data,
    args.output_file,
    input_names=['images'],
    output_names=['output1', 'output2'],
    opset_version=16,
    verbose=False,
    do_constant_folding=True,
)

导出onnx模型:

# 获取rtdetrv2_r18vd_dsp_3x_coco.pth 权重文件,如果前面获取了就不用
wget https://github.com/lyuwenyu/storage/releases/download/v0.1/rtdetrv2_r18vd_dsp_3x_coco.pth

# 使用下面命令导出rtdetrv2_r18vd_dsp.onnx模型
# -c指定配置文件,-r指定checkpoint路径,--im-file指定测试文件路径,-o指定输出模型
(rtdetr) llh@llh:/xxx$ python tools/export_onnx_dsp.py -c configs/rtdetrv2/rtdetrv2_r18vd_dsp_3x_coco.yml -r \
./rtdetrv2_r18vd_dsp_3x_coco.pth  --check -o rtdetrv2_r18vd_dsp.onnx
# 省略................
Check export onnx model done...

6.2.2. 转换成rknn模型

使用toolkit2工具将onnx模型转换成rknn模型,注意这里使用fp16,不经过INT8量化或者混合量化。 实际测试INT8量化模型部署时存在infinity等原因,不能在板卡上部署。

# 教程测试lubancat4 设置rk3588
# Usage: python3 onnx2rknn.py onnx_model_path [platform] [dtype(optional)] [output_rknn_path(optional)]
(toolkit2.2) llh@llh:/xxx$ python onnx2rknn.py rtdetrv2_r18vd_dsp.onnx rk3588 fp
I rknn-toolkit2 version: 2.2.0
--> Config model
done
--> Loading model
I Loading : 100%|██████████████████████████████████████████████| 247/247 [00:00<00:00, 24820.15it/s]
done
--> Building model
I OpFusing 0: 100%|███████████████████████████████████████████████| 100/100 [00:02<00:00, 48.84it/s]
I OpFusing 1 : 100%|██████████████████████████████████████████████| 100/100 [00:04<00:00, 20.49it/s]
I OpFusing 0 : 100%|██████████████████████████████████████████████| 100/100 [00:08<00:00, 11.22it/s]
I OpFusing 1 : 100%|██████████████████████████████████████████████| 100/100 [00:09<00:00, 10.94it/s]
I OpFusing 0 : 100%|██████████████████████████████████████████████| 100/100 [00:09<00:00, 10.22it/s]
I OpFusing 1 : 100%|██████████████████████████████████████████████| 100/100 [00:09<00:00, 10.12it/s]
I OpFusing 2 : 100%|██████████████████████████████████████████████| 100/100 [00:10<00:00,  9.94it/s]
I OpFusing 0 : 100%|██████████████████████████████████████████████| 100/100 [00:10<00:00,  9.54it/s]
I OpFusing 1 : 100%|██████████████████████████████████████████████| 100/100 [00:10<00:00,  9.44it/s]
I OpFusing 2 : 100%|██████████████████████████████████████████████| 100/100 [00:11<00:00,  8.40it/s]
I rknn building ...
I rknn buiding done.
done
--> Export rknn model
done

6.3. 板卡上部署测试

部署测试使用rknpu2 runtime提供的c/c++接口,接口使用教程请参考下前面章节。

直接部署前面转换出的rtdetrv2_r18vd_dsp.rknn模型,会出现段错误 ,可能是模型中的Topk导致,经过测试自定义Topk算子可以避免这个问题 (未来版本的toolkit2可能不会出现这个问题)。

Topk是onnx标准算子,只需要在部署程序中自定义Topk算子,然后注册这个cpu算子:

rtdetr.cc(参考配套例程)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 省略................
/**
* compute_custom_topk_fp
* */
int compute_custom_topk_fp(rknn_custom_op_context* op_ctx, rknn_custom_op_tensor* inputs, uint32_t n_inputs,
                                    rknn_custom_op_tensor* outputs, uint32_t n_outputs)
{
    unsigned char*      in_ptr   = (unsigned char*)inputs[0].mem.virt_addr + inputs[0].mem.offset;
    unsigned char*      out_ptr1  = (unsigned char*)outputs[1].mem.virt_addr + outputs[1].mem.offset;

    const float*        in_data  = (const float*)in_ptr;
    float *           out_data1 = (float*)out_ptr1;

    int N = inputs[0].attr.dims[1];
    int K = outputs[1].attr.dims[1];
    topk(in_data, out_data1, N, K);
    return 0;
}
// 省略................

// register a custom op
rknn_custom_op user_op[1];
memset(user_op, 0, sizeof(rknn_custom_op));
strncpy(user_op[0].op_type, "TopK", RKNN_MAX_NAME_LEN - 1);
user_op[0].version = 1;
user_op[0].target  = RKNN_TARGET_TYPE_CPU;
user_op[0].compute = compute_custom_topk_fp;
ret = rknn_register_custom_ops(ctx, user_op, 1);
if (ret < 0) {
    printf("rknn_register_custom_op fail! ret = %d\n", ret);
    return -1;
}
// 省略................

编译测试例程:

# 板卡上获取配套例程(例程可能没有及时更新)
git clone https://gitee.com/LubanCat/lubancat_ai_manual_code.git

# 切换到rt-detr
cd lubancat_ai_manual_code/examples/rt-detr/cpp

# 编译例程
cat@lubancat:~/xxx/examples/rt-detr/cpp$  ./build-linux.sh -t rk3588
./build-linux.sh -t rk3588
===================================
TARGET_SOC=rk3588
INSTALL_DIR=/xxx/examples/rt-detr/cpp/install/rk3588_linux
BUILD_DIR=/xxx/examples/rt-detr/cpp/build/build_rk3588_linux
ENABLE_DMA32=TRUE
DISABLE_RGA=OFF
BUILD_TYPE=Release
ENABLE_ASAN=OFF
CC=aarch64-linux-gnu-gcc
CXX=aarch64-linux-gnu-g++
===================================
# 省略................
Scanning dependencies of target rtdetr_image_demo
[ 75%] Building CXX object CMakeFiles/rtdetr_image_demo.dir/postprocess.cc.o
[ 83%] Building CXX object CMakeFiles/rtdetr_image_demo.dir/rknpu2/rtdetr.cc.o
[ 91%] Building CXX object CMakeFiles/rtdetr_image_demo.dir/rtdetr_image_demo.cc.o
[100%] Linking CXX executable rtdetr_image_demo
[100%] Built target rtdetr_image_demo
[ 16%] Built target fileutils
[ 33%] Built target imageutils
[ 50%] Built target imagedrawing
[ 83%] Built target rtdetr_image_demo
[100%] Built target audioutils
Install the project...
# 省略................

运行例程:

# 设置cpu、npu频率最高,然后运行例程
cat@lubancat:~/xxx/examples/rt-detr/cpp$
load lable ./model/coco_80_labels_list.txt
model input num: 1, output num: 2
input tensors:
index=0, name=images, n_dims=4, dims=[1, 640, 640, 3], n_elems=1228800, size=2457600,
w_stride = 640, size_with_stride = 2457600, fmt=NHWC, type=FP16, qnt_type=AFFINE, zp=0, scale=1.000000
output tensors:
index=0, name=output1, n_dims=3, dims=[1, 300, 80], n_elems=24000, size=48000,
 w_stride = 0, size_with_stride = 48000, fmt=UNDEFINED, type=FP16, qnt_type=AFFINE, zp=0, scale=1.000000
index=1, name=output2, n_dims=3, dims=[1, 300, 4], n_elems=1200, size=2400,
w_stride = 0, size_with_stride = 2400, fmt=UNDEFINED, type=FP16, qnt_type=AFFINE, zp=0, scale=1.000000
model is NHWC input fmt
model input height=640, width=640, channel=3
origin size=640x640 crop size=640x640
input image: 640 x 640, subsampling: 4:2:0, colorspace: YCbCr, orientation: 1
-- read_image use: 4.527000 ms
scale=1.000000 dst_box=(0 0 639 639) allow_slight_change=1 _left_offset=0 _top_offset=0 padding_w=0 padding_h=0
rga_api version 1.10.1_[0]
rknn_run
-- inference_rtdetr_model use: 331.776001 ms
bus @ (85 133 557 437) 0.949
person @ (108 239 224 535) 0.946
person @ (212 240 284 510) 0.929
person @ (476 235 560 524) 0.954
person @ (80 327 124 517) 0.883
write_image path: out.png width=640 height=640 channel=3 data=0x557dcedee0

测试结果保存当前目录下out.png,教程测试结果截图如下:

broken

以上就是在lubancat上简单部署RT-DETRv2,详情请查看配套例程,可以自行去优化。