点击下方
卡片
,关注“
自动驾驶之心
”公众号
ADAS巨卷干货,即可获取
>>
点击进入→
自动驾驶之心【模型部署】技术交流群
论文作者 | 汽车人
编辑 | 自动驾驶之心
0. 把检测器加进来
本文是我在学习韩博《CUDA与TensorRT部署实战课程》第六章的课程部分输出的个人学习笔记,欢迎大家一起讨论学习!
1. 导出onnx需要注意的地方
不要pip install ultralytics 而是选择git clone 的方式安装yolov8
# Clone the ultralytics repository
git clone https://github.com/ultralytics/ultralytics
# Navigate to the cloned directory
cd ultralytics
# Install the package in editable mode for development
pip install -e .
然后导出onnx看一下
model = YOLO('yolov8n.pt') # load an official model
model = YOLO('path/to/best.pt') # load a custom trained model
# Export the model
model.export(format='onnx')
这个onnx长这样,但是为了速度能够更好一些,因为内存是连续的,为了提升速度我们需要把这个输出变成1x8400x84, 因为这样我们的数据才是8400 个bbox信息(cx, cy, w, h)连在一起, 不然根据官方的信息这里是每8400个cx, 然后是每8400个cy这样的,所以这里需要对他进行一个转置,简单的代码如下:
# ultralytics/ultralytics/nn/modules/head.py
class Detect(nn.Module):
# ...
def forward(self, x):
# ...
y = torch.cat((dbox, cls.sigmoid()), 1)
y = y.transpose(1, 2)
return y if self.export else (y, x)
这里加了一个转置之后,他就会变成
这样我们的yolov8的onnx就成功的导出了
扫码领取八折优惠券!
2. 看一下在分类器的框架上这里修改了哪些地方
这里再复习一下这个推理框架的操作
首先, Worker 是一个类,它负责管理模型的生命周期,包括模型的初始化、加载图像、进行推理等。它根据任务类型(分类或检测)来创建相应的模型实例。
然后,我们有两种类型的模型:Classifier 和 Detector 。这两种模型都继承自 Model 基类。
Classifier 是用于图像分类任务的模型。它包含了一些特定于分类任务的方法,如预处理和后处理。
Detector 是用于目标检测任务的模型。它也包含了一些特定于检测任务的方法,如预处理和后处理。
这两种模型都有自己的 setup 方法来初始化模型,包括创建推理引擎、设置输入/输出绑定、分配内存等。此外,它们还有自己的 preprocess 和 postprocess 方法来处理输入图像和输出结果。
主要更改下面几个文件
-
src/cpp/trt_detector.cpp
-
src/cpp/trt_worker.cpp
-
src/cpp/trt_model.cpp
-
include/trt_detector.hpp
-
inlcude/trt_worker.hpp
下面是整个框架的一个流程图
其实通过上面的流程图可以简单的看出来除了前后处理其他的都是差不多的, 所以也就是前面worker加了一个m_detector, 然后加了一些bbox的数据结构,
所以这里重点看这个前后处理
3. 前处理
读取图片, 然后调用一下前处理函数, 这里的以GPU版本的前处理为例子, 下面是在Detector类实现的, 先读取图片, 然后从param读取信息, 然后这里调用
bool Detector::preprocess_gpu() {
/*Preprocess -- yolo的预处理并没有mean和std,所以可以直接skip掉mean和std的计算 */
/*Preprocess -- 读取数据*/
m_inputImage = cv::imread(m_imagePath);
if (m_inputImage.data == nullptr) {
LOGE("ERROR: file not founded! Program terminated"); return false;
}
/*Preprocess -- 测速*/
m_timer->start_gpu();
/*Preprocess -- 使用GPU进行warpAffine, 并将结果返回到m_inputMemory中*/
preprocess::preprocess_resize_gpu(m_inputImage, m_inputMemory[1],
m_params->img.h, m_params->img.w,
preprocess::tactics::GPU_WARP_AFFINE);
m_timer->stop_gpu();
m_timer->duration_gpu("preprocess(GPU)");
return true;
}
这里就是正常的核函数的一个步骤, 通过分配内存然后把数据从Host搬到Device然后开始执行cu里面的函数, 再借由执行cu文件里面的函数调用kernel函数
void preprocess_resize_gpu(
cv::Mat &h_src, float* d_tar,
const int& tar_h, const int& tar_w,
tactics tac)
{
uint8_t* d_src = nullptr;
int height = h_src.rows;
int width = h_src.cols;
int chan = 3;
int src_size = height * width * chan * sizeof(uint8_t);
int norm_size = 3 * sizeof(float);
// 分配device上的src的内存
CUDA_CHECK(cudaMalloc(&d_src, src_size));
// 将数据拷贝到device上
CUDA_CHECK(cudaMemcpy(d_src, h_src.data, src_size, cudaMemcpyHostToDevice));
// device上处理resize, BGR2RGB的核函数
resize_bilinear_gpu(d_tar, d_src, tar_w, tar_h, width, height, tac);
// host和device进行同步处理
CUDA_CHECK(cudaDeviceSynchronize());
CUDA_CHECK(cudaFree(d_src));
// 因为接下来会继续在gpu上进行处理,所以这里不用把结果返回到host
}
这个是cu里面的核函数, 这里主要是用来调用核函数,这里有很多种, 通过选择的方法来进行不同的核函数:
warpaffine_init(srcH, srcW, tarH, tarW);
warpaffine_BGR2RGB_kernel <<<dimGrid, dimBlock>>>
(d_tar, d_src, trans, affine_matrix);
void resize_bilinear_gpu(
float* d_tar, uint8_t* d_src,
int tarW, int tarH,
int srcW, int srcH,
tactics tac)
{
dim3 dimBlock(32, 32, 1);
dim3 dimGrid(tarW / 32 + 1, tarH / 32 + 1, 1);
//scaled resize
float scaled_h = (float)srcH / tarH;
float scaled_w = (float)srcW / tarW;
float scale = (scaled_h > scaled_w ? scaled_h : scaled_w);
switch (tac) {
case tactics::GPU_NEAREST:
nearest_BGR2RGB_nhwc2nchw_kernel <<<dimGrid, dimBlock>>>
(d_tar, d_src, tarW, tarH, srcW, srcH, scaled_w, scaled_h);
break;
case tactics::GPU_NEAREST_CENTER:
nearest_BGR2RGB_nhwc2nchw_kernel <<<dimGrid, dimBlock>>>
(d_tar, d_src, tarW, tarH, srcW, srcH, scale, scale);
break;
case tactics::GPU_BILINEAR:
bilinear_BGR2RGB_nhwc2nchw_kernel <<<dimGrid, dimBlock>>>
(d_tar, d_src, tarW, tarH, srcW, srcH, scaled_w, scaled_h);
break;
case tactics::GPU_BILINEAR_CENTER:
bilinear_BGR2RGB_nhwc2nchw_shift_kernel <<<dimGrid, dimBlock>>>
(d_tar, d_src, tarW, tarH, srcW, srcH, scale, scale);
break;
case tactics::GPU_WARP_AFFINE:
warpaffine_init(srcH, srcW, tarH, tarW);
warpaffine_BGR2RGB_kernel <<<dimGrid, dimBlock>>>
(d_tar, d_src, trans, affine_matrix);
break;
default:
LOGE("ERROR: Wrong GPU resize tactics selected. Program terminated");
exit(1);
}
}
这里是对这些函数的解释:
-
warpaffine_init
:
void warpaffine_init(int srcH, int srcW, int tarH, int tarW){
trans.src_h = srcH;
trans.src_w = srcW;
trans.tar_h = tarH;
trans.tar_w = tarW;
affine_matrix.init(trans);
}
affine_transformation
:
__host__ __device__ void affine_transformation(
float trans_matrix[6],
int src_x, int src_y,
float* tar_x, float* tar_y)
{
*tar_x = trans_matrix[0] * src_x + trans_matrix[1] * src_y + trans_matrix[2];
*tar_y = trans_matrix[3] * src_x + trans_matrix[4] * src_y + trans_matrix[5];
}
-
nearest_BGR2RGB_nhwc2nchw_norm_kernel
:
__global__ void nearest_BGR2RGB_nhwc2nchw_norm_kernel(
float* tar, uint8_t* src,
int tarW, int tarH,
int srcW, int srcH,
float scaled_w, float scaled_h,
float* d_mean, float* d_std)
{
// nearest neighbour -- resized之后的图tar上的坐标
int x = blockIdx.x * blockDim.x + threadIdx.x;
int y = blockIdx.y * blockDim.y + threadIdx.y;
// nearest neighbour -- 计算最近坐标
int src_y = floor((float)y * scaled_h);
int src_x = floor((float)x * scaled_w);
if (src_x < 0 || src_y < 0 || src_x > srcW || src_y > srcH) {
// nearest neighbour -- 对于越界的部分,不进行计算
} else {
// nearest neighbour -- 计算tar中对应坐标的索引
int tarIdx = y * tarW + x;
int tarArea = tarW * tarH;
// nearest neighbour -- 计算src中最近邻坐标的索引
int srcIdx = (src_y * srcW + src_x) * 3;
// nearest neighbour -- 实现nearest beighbour的resize + BGR2RGB + nhwc2nchw + norm
tar[tarIdx + tarArea * 0] = (src[srcIdx + 2] / 255.0f - d_mean[2]) / d_std[2];
tar[tarIdx + tarArea * 1] = (src[srcIdx + 1] / 255.0f - d_mean[1]) / d_std[1];
tar[tarIdx + tarArea * 2] = (src[srcIdx + 0] / 255.0f - d_mean[0]) / d_std[0];
}
}
-
bilinear_BGR2RGB_nhwc2nchw_norm_kernel
:
__global__ void bilinear_BGR2RGB_nhwc2nchw_norm_kernel(
float* tar, uint8_t* src,
int tarW, int tarH,
int srcW, int srcH,
float scaled_w, float scaled_h,
float* d_mean, float* d_std)
{
// bilinear interpolation -- resized之后的图tar上的坐标
int x = blockIdx.x * blockDim.x + threadIdx.x;
int y = blockIdx.y * blockDim.y + threadIdx.y;
// // bilinear interpolation -- 计算x,y映射到原图时最近的4个坐标
int src_y1 = floor((y + 0.5) * scaled_h - 0.5);
int src_x1 = floor((x + 0.5) * scaled_w - 0.5);
int src_y2 = src_y1 + 1;
int src_x2 = src_x1 + 1;
if (src_y1 < 0 || src_x1 < 0 || src_y2 > srcH || src_x2 > srcW) {
// bilinear interpolation -- 对于越界的坐标不进行计算
} else {
// bilinear interpolation -- 计算原图上的坐标(浮点类型)在0~1之间的值
float th = ((y + 0.5) * scaled_h - 0.5) - src_y1;
float tw = ((x + 0.5) * scaled_w - 0.5) - src_x1;
// bilinear interpolation -- 计算面积(这里建议自己手画一张图来理解一下)
float a1_1 = (1.0 - tw) * (1.0 - th); //右下
float a1_2 = tw * (1.0 - th); //左下
float a2_1 = (1.0 - tw) * th; //右上
float a2_2 = tw * th; //左上
// bilinear interpolation -- 计算4个坐标所对应的索引
int srcIdx1_1 = (src_y1 * srcW + src_x1) * 3; //左上
int srcIdx1_2 = (src_y1 * srcW + src_x2) * 3; //右上
int srcIdx2_1 = (src_y2 * srcW + src_x1) * 3; //左下
int srcIdx2_2 = (src_y2 * srcW + src_x2) * 3; //右下
// bilinear interpolation -- 计算resized之后的图的索引
int tarIdx = y * tarW + x;
int tarArea = tarW * tarH;
// bilinear interpolation -- 实现bilinear interpolation的resize + BGR2RGB + NHWC2NCHW normalization
// 注意,这里tar和src进行遍历的方式是不一样的
tar[tarIdx + tarArea * 0] =
(round((a1_1 * src[srcIdx1_1 + 2] +
a1_2 * src[srcIdx1_2 + 2] +
a2_1 * src[srcIdx2_1 + 2] +
a2_2 * src[srcIdx2_2 + 2])) / 255.0f - d_mean[2]) / d_std[2];
tar[tarIdx + tarArea * 1] =
(round((a1_1 * src[srcIdx1_1 + 1] +
a1_2 * src[srcIdx1_2 + 1] +
a2_1 * src[srcIdx2_1 + 1] +
a2_2 * src[srcIdx2_2 + 1])) / 255.0f - d_mean[1]) / d_std[1];
tar[tarIdx + tarArea * 2] =
(round((a1_1 * src[srcIdx1_1 + 0] +
a1_2 * src[srcIdx1_2 + 0] +
a2_1 * src[srcIdx2_1 + 0] +
a2_2 * src[srcIdx2_2 + 0])) / 255.0f - d_mean[0]) / d_std[0];
}
}
-
bilinear_BGR2RGB_nhwc2nchw_shift_norm_kernel
:
__global__ void bilinear_BGR2RGB_nhwc2nchw_shift_norm_kernel(
float* tar, uint8_t* src,
int tarW, int tarH,
int srcW, int srcH,
float scaled_w, float scaled_h,
float* d_mean, float* d_std)
{
// resized之后的图tar上的坐标
int x = blockIdx.x * blockDim.x + threadIdx.x;
int y = blockIdx.y * blockDim.y + threadIdx.y;
// bilinear interpolation -- 计算x,y映射到原图时最近的4个坐标
int src_y1 = floor((y + 0.5) * scaled_h - 0.5);
int src_x1 = floor((x + 0.5) * scaled_w - 0.5);
int src_y2 = src_y1 + 1;
int src_x2 = src_x1 + 1;
if (src_y1 < 0 || src_x1 < 0 || src_y2 > srcH || src_x2 > srcW) {
// bilinear interpolation -- 对于越界的坐标不进行计算
} else {
// bilinear interpolation -- 计算原图上的坐标(浮点类型)在0~1之间的值
float th = (float)y * scaled_h - src_y1;
float tw = (float)x * scaled_w - src_x1;
// bilinear interpolation -- 计算面积(这里建议自己手画一张图来理解一下)
float a1_1 = (1.0 - tw) * (1.0 - th); // 右下
float a1_2 = tw * (1.0 - th); // 左下
float a2_1 = (1.0 - tw) * th; // 右上
float a2_2 = tw * th; // 左上
// bilinear interpolation -- 计算4个坐标所对应的索引
int srcIdx1_1 = (src_y1 * srcW + src_x1) * 3; // 左上
int srcIdx1_2 = (src_y1 * srcW + src_x2) * 3; // 右上
int srcIdx2_1 = (src_y2 * srcW + src_x1) * 3; // 左下
int srcIdx2_2 = (src_y2 * srcW + src_x2) * 3; // 右下
// bilinear interpolation -- 计算原图在目标图中的x, y方向上的偏移量
y = y - int(srcH / (scaled_h * 2)) + int(tarH / 2);
x = x - int(srcW / (scaled_w * 2)) + int(tarW / 2);
// bilinear interpolation -- 计算resized之后的图的索引
int tarIdx = (y * tarW + x) * 3;
int tarArea = tarW * tarH;
// bilinear interpolation -- 实现bilinear interpolation + BGR2RGB + shift + nhwc2nchw
tar[tarIdx + tarArea * 0] =
(round((a1_1 * src[srcIdx1_1 + 2] +
a1_2 * src[srcIdx1_2 + 2] +
a2_1 * src[srcIdx2_1 + 2] +
a2_2 * src[srcIdx2_2 + 2])) / 255.0f - d_mean[2]) / d_std[2];
tar[tarIdx + tarArea * 1] =
(round((a1_1 * src[srcIdx1_1 + 1] +
a1_2 * src[srcIdx1_2 + 1] +
a2_1 * src[srcIdx2_1 + 1] +
a2_2 * src[srcIdx2_2 + 1])) / 255.0f - d_mean[1]) / d_std[1];
tar[tarIdx + tarArea * 2] =
(round((a1_1 * src[srcIdx1_1 + 0] +
a1_2 * src[srcIdx1_2 + 0] +
a2_1 * src[srcIdx2_1 + 0] +
a2_2 * src[srcIdx2_2 + 0])) / 255.0f - d_mean[0]) / d_std[0];
}
}
-
nearest_BGR2RGB_nhwc2nchw_kernel
和
bilinear_BGR2RGB_nhwc2nchw_kernel
:
__global__ void nearest_BGR2RGB_nhwc2nchw_kernel(
float* tar, uint8_t* src,
int tarW, int tarH,
int srcW, int srcH,
float scaled_w, float scaled_h)
{
// nearest neighbour -- resized之后的图tar上的坐标
int x = blockIdx.x * blockDim.x + threadIdx.x;
int y = blockIdx.y * blockDim.y + threadIdx.y;
// nearest neighbour -- 计算最近坐标
int src_y = floor((float)y * scaled_h);
int src_x = floor((float)x * scaled_w);
if (src_x < 0 || src_y < 0 || src_x > srcW || src_y > srcH) {
// nearest neighbour -- 对于越界的部分,不进行计算
} else {
// nearest neighbour -- 计算tar中对应坐标的索引
int tarIdx = y * tarW + x;
int tarArea = tarW * tarH;
// nearest neighbour -- 计算src中最近邻坐标的索引
int srcIdx = (src_y * srcW + src_x) * 3;
// nearest neighbour -- 实现nearest beighbour的resize + BGR2RGB + nhwc2nchw + norm
tar[tarIdx + tarArea * 0] = src[srcIdx + 2] / 255.0f;
tar[tarIdx + tarArea * 1] = src[srcIdx + 1] / 255.0f;
tar[tarIdx + tarArea * 2] = src[srcIdx + 0] / 255.0f;
}
}
-
bilinear_BGR2RGB_nhwc2nchw_shift_kernel
:
__global__ void bilinear_BGR2RGB_nhwc2nchw_shift_kernel(
float* tar, uint8_t* src,
int tarW, int tarH,
int srcW, int srcH,
float scaled_w, float scaled_h)
{
// resized之后的图tar上的坐标
int x = blockIdx.x * blockDim.x + threadIdx.x;
int y = blockIdx.y * blockDim.y + threadIdx.y;
// bilinear interpolation -- 计算x,y映射到原图时最近的4个坐标
int src_y1 = floor((y + 0.5) * scaled_h - 0.5);
int src_x1 = floor((x + 0.5) * scaled_w - 0.5);
int src_y2 = src_y1 + 1;
int src_x2 = src_x1 + 1;
if (src_y1 < 0 || src_x1 < 0 || src_y2 > srcH || src_x2 > srcW) {
// bilinear interpolation -- 对于越界的坐标不进行计算
} else {
// bilinear interpolation -- 计算原图上的坐标(浮点类型)在0~1之间的值
float th = (float)y * scaled_h - src_y1;
float tw = (float)x * scaled_w - src_x1;
// bilinear interpolation -- 计算面积(这里建议自己手画一张图来理解一下)
float a1_1 = (1.0 - tw) * (1.0 - th); // 右下
float a1_2 = tw * (1.0 - th); // 左下
float a2_1 = (1.0 - tw) * th; // 右上
float a2_2 = tw * th; // 左上
// bilinear interpolation -- 计算4个坐标所对应的索引
int srcIdx1_1 = (src_y1 * srcW + src_x1) * 3; // 左上
int srcIdx1_2 = (src_y1 * srcW + src_x2) * 3; // 右上
int srcIdx2_1 = (src_y2 * srcW + src_x1) * 3; // 左下
int srcIdx2_2 = (src_y2 * srcW + src_x2) * 3; // 右下
// bilinear interpolation -- 计算原图在目标图中的x, y方向上的偏移量
y = y - int(srcH / (scaled_h * 2)) + int(tarH / 2);
x = x - int(srcW / (scaled_w * 2)) + int(tarW / 2);
// bilinear interpolation -- 计算resized之后的图的索引
int tarIdx = y * tarW + x;
int tarArea = tarW * tarH;
// bilinear interpolation -- 实现bilinear interpolation + BGR2RGB + shift + nhwc2nchw
tar[tarIdx + tarArea * 0] =
round((a1_1 * src[srcIdx1_1 + 2] +
a1_2 * src[srcIdx1_2 + 2] +
a2_1 * src[srcIdx2_1 + 2] +
a2_2 * src[srcIdx2_2 + 2])) / 255.0f;
tar[tarIdx + tarArea * 1] =
round((a1_1 * src[srcIdx1_1 + 1] +
a1_2 * src[srcIdx1_2 + 1] +
a2_1 * src[srcIdx2_1 + 1] +
a2_2 * src[srcIdx2_2 + 1])) / 255.0f;
tar[tarIdx + tarArea * 2] =
round((a1_1 * src[srcIdx1_1 + 0] +
a1_2 * src[srcIdx1_2 + 0] +
a2_1 * src[srcIdx2_1 + 0] +
a2_2 * src[srcIdx2_2 + 0])) / 255.0f;
}
}
-
warpaffine_BGR2RGB_kernel
:
__global__ void warpaffine_BGR2RGB_kernel(
float* tar, uint8_t* src,
TransInfo trans,
AffineMatrix affine_matrix)
{
float src_x, src_y;
int x = blockIdx.x * blockDim.x + threadIdx.x;
int y = blockIdx.y * blockDim.y + threadIdx.y;
affine_transformation(affine_matrix.reverse, x + 0.5, y + 0.5, &src_x, &src_y);
int src_x1 = floor(src_x - 0.5);
int src_y1 = floor(src_y - 0.5);
int src_x2 = src_x1 + 1;
int src_y2 = src_y1 + 1;
if (src_y1 < 0 || src_x1 < 0 || src_y1 > trans.src_h || src_x1 > trans.src_w) {
} else {
float tw = src_x - src_x1;
float th = src_y - src_y1;
float a1_1 = (1.0 - tw) * (1.0 - th);
float a1_2 = tw * (1.0 - th);
float a2_1 = (1.0 - tw) * th;
float a2_2 = tw * th;
int srcIdx1_1 = (src_y1 * trans.src_w + src_x1) * 3;
int srcIdx1_2 = (src_y1 * trans.src_w + src_x2) * 3;
int srcIdx2_1 = (src_y2 * trans.src_w + src_x1) * 3;
int srcIdx2_2 = (src_y2 * trans.src_w + src_x2) * 3;
int tarIdx = y * trans.tar_w + x;
int tarArea = trans.tar_w * trans.tar_h;
tar[tarIdx + tarArea * 0] =
round((a1_1 * src[srcIdx1_1 + 2] +
a1_2 * src[srcIdx1_2 + 2] +
a2_1 * src[srcIdx2_1 + 2] +
a2_2 * src[srcIdx2_2 + 2])) / 255.0f;
tar[tarIdx + tarArea * 1] =
round((a1_1 * src[srcIdx1_1 + 1] +
a1_2 * src[srcIdx1_2 + 1] +
a2_1 * src[srcIdx2_1 + 1] +
a2_2 * src[srcIdx2_2 + 1])) / 255.0f;
tar[tarIdx + tarArea * 2] =
round((a1_1 * src[srcIdx1_1 + 0] +
a1_2 * src[srcIdx1_2 + 0] +
a2_1 * src[srcIdx2_1 + 0] +
a2_2 * src[srcIdx2_2 + 0])) / 255.0f;
}
}
-
resize_bilinear_gpu
(两个重载版本)
:
这里基本上除了warpffine都是分类网络的前处理, 各种不同的可以拿来做实验
4. 后处理
重点是下面这种写法, 因为vector这种数据结构的类型你在中间删掉了一个,他会动到整个vector, 所以这里bbox会赋予一个flag模式,这样最后是给通过这个查看是否添加进最后的final_bboxes里面, 这样就很高效
vector<bbox> final_bboxes;
final_bboxes.reserve(m_bboxes.size());
std::sort(m_bboxes.begin(), m_bboxes.end(),
[](bbox& box1, bbox& box2){return box1.confidence > box2.confidence;});
/*
* nms在网上有很多实现方法,其中有一些是根据nms的值来动态改变final_bboex的大小(resize, erease)
* 这里需要注意的是,频繁的对vector的大小的更改的空间复杂度会比较大,所以尽量不要这么做
* 可以通过给bbox设置skip计算的flg来调整。
*/
for(int i = 0; i < m_bboxes.size(); i ++){
if (m_bboxes[i].flg_remove)
continue;
final_bboxes.emplace_back(m_bboxes[i]);
for (int j = i + 1; j < m_bboxes.size(); j ++) {
if (m_bboxes[j].flg_remove)
continue;
if (m_bboxes[i].label == m_bboxes[j].label){
if (iou_calc(m_bboxes[i], m_bboxes[j]) > nms_threshold)
m_bboxes[j].flg_remove = true;
}
}
}
LOGD("the count of bbox after NMS is %d", final_bboxes.size());
5. 后期的提升
-
这边的提升思想主要是量化后掉精度的问题, 在这里面韩君给出了一些检查的方案, 因为我们这里是一个Mutil-Task的任务
-
是否在input/output附近做了int8量化
-
如果是multi-task的话,是否所有的task都掉点严重
-
calibration的数据集是不是选的不是很好
-
calibration batch size是不是选择的不是很好
-
calibrator是不是没有选择好
-
某些计算是否不应该做量化
-
使用polygraphy分析
① 全网独家视频课程
BEV感知
、毫米波雷达视觉融合
、
多传感器标定
、
多传感器融合
、
多模态3D目标检测
、
点云3D目标检测
、
目标跟踪
、
Occupancy、
cuda与TensorRT模型部署
、
协同感知
、
语义分割、
自动驾驶仿真、
传感器部署、
决策规划、轨迹预测
等多个方向学习视频(
扫码即可学习
)
视频官网:www.zdjszx.com
② 国内首个自动驾驶学习社区
近2000人的交流社区,涉及30+自动驾驶技术栈学习路线,想要了解更多自动驾驶感知(2D检测、分割、2D/3D车道线、BEV感知、3D目标检测、Occupancy、多传感器融合、多传感器标定、目标跟踪、光流估计)、自动驾驶定位建图(SLAM、高精地图、局部在线地图)、自动驾驶规划控制/轨迹预测等领域技术方案、AI模型部署落地实战、行业动态、岗位发布,欢迎扫描下方二维码,加入自动驾驶之心知识星球,
这是一个真正有干货的地方,与领域大佬交流入门、学习、工作、跳槽上的各类难题,日常分享论文+代码+视频
,期待交流!
③【自动驾驶之心】技术交流群
自动驾驶之心是首个自动驾驶开发者社区,聚焦
目标检测、语义分割、全景分割、实例分割、关键点检测、车道线、目标跟踪、3D目标检测、BEV感知、多模态感知、Occupancy、多传感器融合、transformer、大模型、点云处理、端到端自动驾驶、SLAM、光流估计、深度估计、轨迹预测、高精地图、NeRF、规划控制、模型部署落地、自动驾驶仿真测试、产品经理、硬件配置、AI求职交流
等方向。扫码添加汽车人助理微信邀请入群,备注:学校/公司+方向+昵称(快速入群方式)
④【自动驾驶之心】平台矩阵,
欢迎联系我们!