【模型加速】PointPillars模型加速实验(1)
2021/7/25 23:46:13
本文主要是介绍【模型加速】PointPillars模型加速实验(1),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
在阅读这个系列文章之前假定你已经对PointPillars算法本身有一定的了解,并且有一个可用的Pytorch模型。这里的加速实验主要还是遵循"Pytorch-->ONNX-->TensorRT引擎-->推理"这一思路,暂不涉及使用TensorRT API手动搭建网络。
实验环境
- 系统,Ubuntu16.04
- 内核,Linux version 4.15.0-112-generic
- CUDA,10.2
- Python, 3.6
- Pytorch,1.4.0
- TensorRT, 7.1.3.4
- cmake, 3.13.2
- GPU,4卡Titan RTX
算法流程
整个算法逻辑包含3个部分:数据预处理,神经网络,后处理。其中神经网络部分,原论文中将其结构描叙为3个部分:PFN(PillarFeature Net),Backbone(2D CNN)和Detection Head(SSD)。在实际部署的时候,结构拆分和论文中稍微有些出入。主要是分成PFN,MFN和RPN。其中MFN是用来将PFN提取的Pillar级的点云深度特征进一步转化成伪点云图像。RPN就是Backbone,而检测头的部分功能被包含在了后处理的逻辑里面。
输入数据准备
PFN的输入包括8组数据:
- pillar_x:包含Pillar化后的点云x坐标,shape为(1,1,P,100)
- pillar_y:包含Pillar化后的点云y坐标,shape为(1,1,P,100)
- pillar_z:包含Pillar化后的点云z坐标, shape为(1,1,P,100)
- pillar_i:包含Pillar化后的点云强度值,shape为(1,1,P,100)
- num_points: 保存每个Pillar包含的实际点云数量,shape为(1,P)
- x_sub_shaped:保存Pillar的中心x坐标,shape为(1,1,P,100)
- y_sub_shaped:保存Pillar的中心y坐标,shape为(1,1,P,100)
- mask:pillar点云掩码,shape为(1,1,P,100)
MFN的输入包括2组数据:
- voxel_features:就是是就是PFN的输出,shape为(1,64,P,1)
- coords:pillar在x-y网格中的坐标,shape为(P,4)
这两部分的输入我统一在make_input函数中实现。
def make_input(self, points): pillars,coors,num_points_per_pillar = self.points_to_pillar(points, self.pillar_size, self.point_cloud_range, self.max_num_points_per_pillar, self.reverse_index, self.max_num_pillars) pillar_x = pillars[0][np.newaxis, np.newaxis, :] #(1,1,N,100) pillar_y = pillars[1][np.newaxis, np.newaxis, :] pillar_z = pillars[2][np.newaxis, np.newaxis, :] pillar_i = pillars[3][np.newaxis, np.newaxis, :] x_sub = coors[:, 2][:, np.newaxis].astype(np.float32) * 0.16 + 0.1 y_sub = coors[:, 1][:, np.newaxis].astype(np.float32) * 0.16 + -39.9 ones_array = np.ones(shape=[1, self.max_num_points_per_pillar], dtype=np.float32) x_sub_shaped = np.dot(x_sub, ones_array) #(N,1)x(1,100)=> (N,100) y_sub_shaped = np.dot(y_sub, ones_array) x_sub_shaped = x_sub_shaped.reshape(1, 1, *x_sub_shaped.shape) y_sub_shaped = y_sub_shaped.reshape(1, 1, *y_sub_shaped.shape) #num_points_a_pillar = pillar_x_d.shape[3] num_points_per_pillar = num_points_per_pillar[np.newaxis, :].astype(np.float32) mask = self.get_paddings_indicator(num_points_per_pillar, self.max_num_points_per_pillar) mask = mask.astype(pillar_x.dtype) #bool->float32,i have tested,not necessary anchors = self.anchors_cache['anchors'] anchors = anchors[np.newaxis,:] anchor_area_threshold = self.anchors_cache['anchor_area_threshold'] if anchor_area_threshold >= 0: dense_pillar_map = box_np_ops.sparse_sum_for_anchors_mask(coors, tuple(self.grid_size[::-1][1:])) dense_pillar_map = dense_pillar_map.cumsum(0) dense_pillar_map = dense_pillar_map.cumsum(1) anchors_area = box_np_ops.fused_get_anchors_area( dense_map = dense_pillar_map, anchors_bv = self.anchors_cache['anchors_bv'], stride = self.pillar_size, offset = self.point_cloud_range, grid_size = self.grid_size) #Use torch.bool/torch.uint8 as mask type anchors_mask = anchors_area > anchor_area_threshold #anchors_mask = anchors_mask.astype(np.uint8) #deprecated,use torch.bool instead anchors_mask = anchors_mask[np.newaxis,:] return (pillar_x,pillar_y,pillar_z,pillar_i, num_points_per_pillar,x_sub_shaped,y_sub_shaped,mask,anchors,anchors_mask,coors)
其中points_to_pillar函数用来将原始点云points(?,4)转化为pillars(D,P,N),同时得到coords和num_points_per_pillar。其中最重要的是_points_to_voxel_reverse_kernel这个转换函数。
@numba.jit(nopython=True) def g_points_to_pillar_reverse_kernel(points, pillar_size, point_cloud_range, num_points_per_pillar, coor_to_pillar_idx, pillars, coors, max_num_points_per_pillar=100, max_num_pillars=12000): ndim = 3 N = points.shape[0] ndim_minus_1 = ndim - 1 grid_size = (point_cloud_range[3:] - point_cloud_range[:3]) / pillar_size grid_size = np.round(grid_size, 0, grid_size).astype(np.int32) coor = np.zeros(shape=(3, ), dtype=np.int32) pillar_num = 0 failed = False for i in range(N): failed = False for j in range(ndim): c = np.floor((points[i, j] - point_cloud_range[j]) / pillar_size[j]) if c < 0 or c >= grid_size[j]: failed = True break coor[ndim_minus_1 - j] = c if failed: continue pillar_idx = coor_to_pillar_idx[coor[0], coor[1], coor[2]] if pillar_idx == -1: pillar_idx = pillar_num if pillar_num >= max_num_pillars: # pillar_num >= max_num_pillars 立刻结束 break pillar_num += 1 coor_to_pillar_idx[coor[0], coor[1], coor[2]] = pillar_idx coors[pillar_idx] = coor num = num_points_per_pillar[pillar_idx] if num < max_num_points_per_pillar: pillars[:, pillar_idx, num] = points[i] num_points_per_pillar[pillar_idx] += 1 return pillar_num
它被numba的jit装饰器装饰,括号里的(nopython=True)表示关闭掉python编译模式,用llvm代替默认的python编译器。numba是一款可以将python函数编译为机器代码的JIT编译器,经过numba编译的python代码(仅限数组运算),其速度可以接近C或者FORTRAN语言。在使用numpy数组做大量科学计算或者使用for循环时推荐使用numba来加速计算,效果显著。
汇编码(Assembly Code)是用 人类可读的 汇编语言助记符 书写的代码。 机器码(Machine Code)是用 硬件可执行的 二进制 表示的代码。 十六进制码(Hexadecimal Code) 是用 人类可读的 十六进制 表示的代码。
我这里在我服务器上简单对比了一下numba加速的效果,就这么一行代码简直是化腐朽为神奇,你都不需要改动代码本身,速度提升了322倍。
numba加速 | g_points_to_pillar_reverse_kernel |
是 | 2.45ms |
否 | 790ms |
【参考文献】
https://blog.csdn.net/Small_Munich/article/details/101559424
https://github.com/nutonomy/second.pytorch
https://forums.developer.nvidia.com/t/6-assertion-failed-convertdtype-onnxtype-dtype-unsupported-cast/179605/2
https://zhuanlan.zhihu.com/p/78882641
这篇关于【模型加速】PointPillars模型加速实验(1)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-12-27数据结构与算法面试题详解及练习
- 2024-12-27网络请求面试题详解与实战
- 2024-12-27数据结构和算法面试真题详解与实战教程
- 2024-12-27网络请求面试真题解析与实战教程
- 2024-12-27数据结构和算法大厂面试真题详解与实战指南
- 2024-12-27TS大厂面试真题解析与应对策略
- 2024-12-27TS大厂面试真题详解与解析
- 2024-12-27网站安全入门:如何识别和修复漏洞
- 2024-12-27SQL注入基础教程
- 2024-12-27初学者指南:理解和修复跨域漏洞