绿色系网站快速排名软件案例
- 作者: 五速梦信息网
- 时间: 2026年04月20日 10:28
当前位置: 首页 > news >正文
绿色系网站,快速排名软件案例,中国建设网站官方网站,新零售社交电商系统模型部署学习笔记——模型部署关键知识点总结 模型部署学习笔记——模型部署关键知识点总结1. CUDA中Grid和Block的定义是什么#xff1f;Shared Memory的定义#xff1f;Bank Conflict的定义#xff1f;Stream和Event的定义#xff1f;2. TensorRT的工作流程#xff1f;3… 模型部署学习笔记——模型部署关键知识点总结 模型部署学习笔记——模型部署关键知识点总结1. CUDA中Grid和Block的定义是什么Shared Memory的定义Bank Conflict的定义Stream和Event的定义2. TensorRT的工作流程3. ONNX是什么如何从Pytorch导出ONNX又如何将ONNX导入TensorRT可能遇到的问题是什么4. Layer Fusion的方法有哪些5. FLOPS、TOPS、FLOPs等的定义是什么Roofline Model是定义和用法6. 模型量化中PTQ和QAT的区别Calibration的种类常用的量化粒度有哪些如果出现精度下降怎么办7. 什么是模型剪枝模型剪枝的分类各类剪枝的利弊都有哪些8. 模型推理中的基本框架9. 模型部署中的一些注意点 模型部署学习笔记——模型部署关键知识点总结 最近在工作之余学习了一些和模型部署相关的课程本博客是对其中一些知识点的记录和总结主要是模型部署中的一些基本概念。
- CUDA中Grid和Block的定义是什么Shared Memory的定义Bank Conflict的定义Stream和Event的定义 Thread是执行计算任务的基本单位每个线程执行同一内核函数Kernel Function但每个线程可以根据其Thread ID来处理不同的数据或计算任务 线程是CUDA程序中并行计算的基本单位它们通过Grid和Block组织起来并执行内核函数。每个线程通过Thread ID标识其在线程块中的位置通过Block ID标识线程块在Grid中的位置。线程之间可以通过共享内存和同步机制进行协调但它们本身是独立执行的。 Block 是Grid的子集每个Block内包含多个Thread这些线程在同一个SMStreaming Multiprocessor上执行。Block是一个共享资源单元在同一Block内的线程能够共享内存和同步执行。 每个Block有一个维度通常为1D、2D或3D。Block中线程的数量是有限的通常由硬件决定每个Block最多1024个线程具体限制取决于GPU架构。在同一个Block内线程可以通过共享内存进行高效的通信。 Grid 是线程块Block的集合表示了CUDA程序中的一个大规模并行计算任务的整体。每个Grid包含多个Block这些Block可以在不同的SMStreaming Multiprocessor上并行执行。 一个Grid由多个线程块组成。每个Grid有一个维度通常为1D、2D或3D具体取决于任务的需求。每个Grid的大小通常由总的线程块数和每个Block中线程的数量决定。 Shared Memory是CUDA编程模型中的一种高效的内存类型专门用于Block内部的线程之间的高速数据共享和通信。Shared Memory位于每个线程块内部它比Global Memory要快得多但每个Block只能访问其自己Block内的共享内存不能访问其他Block的共享内存 不使用Shared Memory的代码如下 global void matrixMultiply(float *A, float *B, float *C, int N) {int row threadIdx.y blockIdx.y * blockDim.y;int col threadIdx.x blockIdx.x * blockDim.x;if (row N col N) {float value 0;for (int i 0; i N; i) {value A[row * N i] * B[i * N col];}C[row * N col] value;} }使用Shared Memory的代码如下 global void matmul_shared_memory(float *A, float *B, float *C, int N) {shared float sharedA[BLOCK_SIZE][BLOCK_SIZE];shared float sharedB[BLOCK_SIZE][BLOCK_SIZE];int row blockIdx.y * blockDim.y threadIdx.y;int col blockIdx.x * blockDim.x threadIdx.x;float value 0.0;for (int k 0; k (N / BLOCK_SIZE); k) {// Load data into shared memorysharedA[threadIdx.y][threadIdx.x] A[row * N k * BLOCK_SIZE threadIdx.x];sharedB[threadIdx.y][threadIdx.x] B[(k * BLOCK_SIZE threadIdx.y) * N col];// Synchronize threads to ensure shared memory is filledsyncthreads();// Perform multiplicationfor (int i 0; i BLOCK_SIZE; i) {value sharedA[threadIdx.y][i] * sharedB[i][threadIdx.x];}// Synchronize threads before the next iterationsyncthreads();}// Store the resultC[row * N col] value; }Bank Conflict 是指当多个线程在同一时刻访问共享内存的同一Bank时发生的冲突。共享内存的访问速度比全局内存快得多而通过将共享内存划分为多个Bank能够让多个线程同时访问共享内存而不会发生冲突从而提高性能。如果多个线程访问同一个Bank上的不同位置硬件必须序列化这些访问这样就会导致性能下降。一种比较简单地解决Bank Conflict问题的方法是在申请共享内存时进行Padding如下 #define N 32 // 假设线程块大小为32global void avoidBankConflict(float *data) {shared float shared[N 1]; // 添加了padding数组大小为N1int index threadIdx.x;// 原始数组数据存储shared[index] data[index];// 示例计算每个线程计算一个数据元素shared[index] shared[index] * 2.0f;// 将结果存回全局内存data[index] shared[index]; } 其中shared[N 1]中的N代表线程块中的线程数假设为32而我们给共享内存数组增加了1个额外元素即N1这就是Padding。这样线程0将访问shared[0]线程1访问shared[2]线程2访问shared[4]依此类推。通过间隔增加访问步长避免了线程间的冲突。 Stream 是CUDA中的一种机制用于管理异步执行的操作。同一个Stream的执行顺序和各个Kernel以及Memory Operation的启动顺序是一致的但是只要资源没有被占用不同流之间的执行时可以Overlap的如下图所示 如下是一个单流版本代码 #include cuda_runtime.h #include stdio.h#define N (1 20) // 数据大小 #define NUM_STREAMS 4 // 数据块数量global void kernel(float *data, int size) {int idx blockIdx.x * blockDim.x threadIdx.x;if (idx size) {data[idx] * 2.0f; // 简单的倍乘操作} }void singleStreamExample() {float *h_data, *d_data;size_t size N * sizeof(float);cudaMallocHost(h_data, size); // 主机内存cudaMalloc(d_data, size); // 设备内存// 初始化数据for (int i 0; i N; i) h_data[i] i;cudaMemcpy(d_data, h_data, size, cudaMemcpyHostToDevice); // 数据传输kernelN / 256, 256(d_data, N); // 内核执行cudaMemcpy(h_data, d_data, size, cudaMemcpyDeviceToHost); // 数据返回cudaFree(d_data);cudaFreeHost(h_data); } 如下是修改为多流版本的代码 void multiStreamExample() {float *h_data[NUM_STREAMS], *d_data[NUM_STREAMS];cudaStream_t streams[NUM_STREAMS];size_t size (N / NUM_STREAMS) * sizeof(float);// 为每个数据块分配内存和创建流for (int i 0; i NUM_STREAMS; i) {cudaMallocHost(h_data[i], size); // 主机内存cudaMalloc(d_data[i], size); // 设备内存cudaStreamCreate(streams[i]); // 创建流// 初始化数据for (int j 0; j N / NUM_STREAMS; j) {h_data[i][j] j i * (N / NUM_STREAMS);}}// 使用多个流异步执行for (int i 0; i NUM_STREAMS; i) {cudaMemcpyAsync(d_data[i], h_data[i], size, cudaMemcpyHostToDevice, streams[i]); // 异步数据传输kernel(N / NUM_STREAMS) / 256, 256, 0, streamsi; // 异步内核执行cudaMemcpyAsync(h_data[i], d_data[i], size, cudaMemcpyDeviceToHost, streams[i]); // 异步数据返回}// 等待所有流完成for (int i 0; i NUM_STREAMS; i) {cudaStreamSynchronize(streams[i]); // 等待每个流完成}// 释放资源for (int i 0; i NUM_STREAMS; i) {cudaFree(d_data[i]);cudaFreeHost(h_data[i]);cudaStreamDestroy(streams[i]);} } Event 是CUDA中的一种机制用于标记某些操作的完成并允许用户在不同的流或操作之间进行同步。事件通常用于监控计算或内存操作的进度并在不同流之间建立同步依赖关系。如下是使用Event对Stream进行时间测量的代码 cudaEvent_t start, stop; cudaEventCreate(start); cudaEventCreate(stop);cudaEventRecord(start, 0); // 调用 singleStreamExample 或 multiStreamExample cudaEventRecord(stop, 0);cudaEventSynchronize(stop); float milliseconds 0; cudaEventElapsedTime(milliseconds, start, stop); printf(Execution time: %f ms\n, milliseconds);cudaEventDestroy(start); cudaEventDestroy(stop);
- TensorRT的工作流程 TensorRT 的工作流程是 模型导入通过工具如Tensorflow中的tf2onnx或PyTorch中的torch.onnx.export将训练好的模型转换为ONNX格式再加载到TensorRT中或者通过Tensor API手动构建模型。模型优化TensorRT中模型优化策略包括Layer Tensor FusionKernel Auto-Tuning、Quantization等如下图所示 推理引擎构建即使用优化后的模型构建一个高效的推理引擎Engine。推理引擎是一个轻量化、高性能的运行时表示仅用于推理。模型推理将Engine部署到端侧输入实时数据进行推理。
- ONNX是什么如何从Pytorch导出ONNX又如何将ONNX导入TensorRT可能遇到的问题是什么 ONNXOpen Neural Network Exchange是一个开放的深度学习模型交换格式旨在实现不同深度学习框架之间的互操作性。 ONNX采用Protobuf二进制形式进行序列化模型。模型的组织结构由几个主要的组件构成如下如下是一个简化的 ONNX 模型的组织结构 ModelProto├── ir_version: 7├── producer_name: PyTorch├── producer_version: 1.10├── model_version: 1├── graph│ ├── name: example_graph│ ├── node│ │ ├── op_type: Conv│ │ ├── input: [input_tensor, weight]│ │ ├── output: [output_tensor]│ │ └── attribute: [{name: kernel_shape, value: [3,3]}]│ ├── node│ │ ├── op_type: Relu│ │ ├── input: [output_tensor]│ │ └── output: [relu_output]│ ├── input: [input_tensor]│ ├── output: [relu_output]│ ├── initializer: [weight_tensor]│ └── value_info: [intermediate_tensor]├── metadata_props: [] 其中ModelProto 包含了模型的基本信息GraphProto 包含了一个名为 example_graph 的计算图该图包含两个节点一个卷积层和一个 ReLU 激活层NodeProto 定义了每个操作节点例如卷积层Conv和 ReLU 层ReluTensorProto 用于表示图中的张量如 weight_tensor 和 input_tensor。 从Pytorch导出ONNX方法如下 import torch import torch.onnx# 假设你有一个训练好的 PyTorch 模型 model model … # 加载或定义你的模型 model.eval() # 设置为评估模式# 输入示例数据应该与模型的输入形状相同 dummy_input torch.randn(1, 3, 224, 224) # 示例输入假设是一个图片输入# 导出模型为 ONNX 文件 onnx_path model.onnx torch.onnx.export(model, dummy_input, onnx_path, input_names[input], output_names[output])将ONNX导入TensorRT方法如下 import tensorrt as trt import onnx# 1. 加载 ONNX 模型 onnx_model_path model.onnx onnx_model onnx.load(onnx_model_path)# 2. 创建 TensorRT 的 ONNX 解析器 TRT_LOGGER trt.Logger(trt.Logger.WARNING) builder trt.Builder(TRT_LOGGER) network builder.create_network(common.EXPLICIT_BATCH) parser trt.OnnxParser(network, TRT_LOGGER)# 3. 解析 ONNX 模型并构建 TensorRT 引擎 with open(onnx_model_path, rb) as f:parser.parse(f.read())# 4. 构建 TensorRT 引擎 engine builder.build_cuda_engine(network)# 5. 使用 TensorRT 引擎执行推理需要设置执行上下文等 从Pytorch到出ONNX的时候有时候会遇到导出失败的问题此时解决方案通常如下 1修改opset的版本查看不支持的算子在新的opset中是否被支持以及onnx-tensorrt中是否被支持。opset支持的算子通常在ONNX算子文档有列出onnx-tensorrt 支持的算子集通常会在其官方文档中列出。 2替换pytorch中的算子组合即将某些计算替换为onnx中可以识别的算子。 3在pytorch登记onnx中某些算子有可能onnx中支持但是pytorch中没有被登记登记方式可以使用 torch.onnx.export 的 custom_opsets 注册自定义算子或者通过 torch.onnx.symbolic 定义算子的符号 4直接修改onnx创建plugin这种方法需要使用onnx-surgeononnx-surgeon是一个Python 库用于对ONNX模型进行修改和修剪。它允许用户通过 API 对 ONNX 模型的结构进行编辑、调整和修剪支持添加、删除、修改节点等操作。 以上四种解决方案难度由易到难。
- Layer Fusion的方法有哪些 Layer Fusion是指将多个层可以通过融合来优化计算。Layer Fusion分为Horizontal Layer Fusion和Vertical Layer Fusion。Vertical Layer Fusion中典型的融合方式有ConvReLU、ConvBNReLUHorizontal Layer Fusion是将水平方向上同类Layer进行融合如下图所示是两类融合方式的示意图 Vertical Layer Fusion融合结果 Horizontal Layer Fusion融合结果 为什么融合可以减少计算量呢我们知道BN的公式如下 y i γ ∗ x i − μ B σ B 2 ϵ β y_i\gamma * \frac{x_i-\mu_B}{\sqrt{\sigma_B^2\epsilon}}\beta yiγ∗σB2ϵ xi−μBβ其中 γ \gamma γ和 β \beta β为可学习参数 μ B 1 B ∑ i 1 B x i \muB\frac{1}{B} \sum{i1}^B x_i μBB1∑i1Bxi σ B 2 1 B ∑ i 1 B ( x i − μ B ) 2 ϵ \sigmaB^2\frac{1}{B} \sum{i1}^B\left(x_i-\mu_B\right)^2\epsilon σB2B1∑i1B(xi−μB)2ϵ我们将卷积公式 x i w ∗ x i b x_iw * x_ib xiw∗xib代入得 y γ ∗ w ∗ x b − μ B σ B 2 ϵ β y\gamma * \frac{w * xb-\mu_B}{\sqrt{\sigma_B^2\epsilon}}\beta yγ∗σB2ϵ w∗xb−μBβ进一步展开和代换可得 y ( γ ∗ w σ B 2 ϵ ) ∗ x ( γ σ B 2 ϵ ( b − μ B ) β ) y\left(\frac{\gamma * w}{\sqrt{\sigma_B^2\epsilon}}\right) * x\left(\frac{\gamma}{\sqrt{\sigma_B^2\epsilon}}\left(b-\mu_B\right)\beta\right) y(σB2ϵ γ∗w)∗x(σB2ϵ γ(b−μB)β)我们定义 w ^ γ ∗ w σ B 2 ϵ \widehat{w}\frac{\gamma * w}{\sqrt{\sigma_B^2\epsilon}} w σB2ϵ γ∗w b ^ γ σ B 2 ϵ ( b − μ B ) β \hat{b}\frac{\gamma}{\sqrt{\sigma_B^2\epsilon}}\left(b-\mu_B\right)\beta b^σB2ϵ γ(b−μB)β由于 γ \gamma γ和 β \beta β在训练完成后都是已知的因此我们可以将上述公式简化为 y w ^ ∗ x b ^ y\widehat{w} * x\hat{b} yw ∗xb^这样ConvBN的计算量就和Conv本身基本接近。而ReLU只是做一个截取因此进行融合。
- FLOPS、TOPS、FLOPs等的定义是什么Roofline Model是定义和用法 名词缩写/单位定义和作用计算峰值FLOPS (Floating point number operations per second)表示每秒钟进行的浮点运算次数是衡量计算机性能的一个常见指标计算峰值TOPS (Tera operations per second)表示每秒钟处理的整型运算的次数是衡量计算机性能的另一个指标计算量FLOPs(Floating point number operations)表示模型中有多少个浮点运算是衡量模型大小的一个指标参数量Byte表示模型中所有的weights的量是另一个衡量模型大小的指标指的注意的是conv的参数量和input/output无关而transformer的参数会根据输入tensor的大小改变而改变访存量Byte表示模型中模型中某一个算子或者某一个layer进行计算时需要与memory产生读写的量是分析模型中某些计算的计算效率的标准之一带宽量Byte/s表示的是单位时间内可以传输的数据量的多少是衡量计算机硬件memory的性能的标准之一计算密度Operation IntensityFLOPs/Bytes表示的是单位数据可以进行的浮点运算数计算密度计算量/访存量 Roofline Model 是一种用于描述计算机系统性能的可视化模型特别用于展示计算密集型任务的性能瓶颈如下图所示 X轴表示计算密集度单位为 FLOP/byte浮点运算数与内存数据量的比率。它衡量了应用程序的计算与数据访问的关系通常越高表示计算密集度越大。Y轴表示计算性能通常单位是 FLOPS浮点运算每秒即系统能够执行的浮点运算数。 实际应用的表现会被标示在 Roofline 图中通常是在 Roofline 上下限之间。如果应用的性能接近 Roofline那么它就已经接近硬件的理论最大性能如果远低于 Roofline则意味着有潜在的性能瓶颈如上图中标识的Algorithm Fully-connected计算密度过低因此没法充分利用硬件性能而Algorithm 2 Convolution已经达到Roofline。 这里我们可以给出一些结论 1对于Convlution随着Kernel Size、Output Size、Channel Size的增大计算密度都是增大的但是计算密度增长率会逐渐下降 2 1 × 1 1\times 1 1×1 ConvDepth Wise Conv虽然计算量小但是其计算密度很低 3Fully Connected Layer计算密度非常小 4模型中Reshape操作多也会导致整个模型的计算密度下降。
- 模型量化中PTQ和QAT的区别Calibration的种类常用的量化粒度有哪些如果出现精度下降怎么办
PTQ和QAT的区别如下 PTQ (Post-Training Quantization) 是一种在模型训练完成后对模型进行量化的方法。它不需要重新训练模型而是通过在训练好的模型上直接应用量化算法来减少模型的存储需求和加速推理过程。其优点是方便缺点是会掉精度 1TensorR的Kernel Autotuning会选择核函数来做FP16/INT8的计算有可能会出现FP16在Tensor Core上计算而INT8则在Cuda Core上这样就有可能导致INT8量化后的速度反而比FP16/FP32要慢。 2量化后可能会导致层融合失效这样也可能会导致INT8量化速度反而变慢的问题。 3量化过程中需要注意Sensitive Analysis对于位于输入和输出的敏感层应该尽量使用FP16进行量化。 QAT(Quantization-Aware Training) 是一种在训练过程中就引入量化操作的技术通过在训练时模拟低精度的运算来帮助模型适应量化带来的误差。其优点是精度高缺点是实现复杂 1QAT通常通过Q/DQ Node实现Q/DQ Node也被称为Fake Quantization Node实现用来模拟FP32-INT8量化的Scale和Shift以及INT8-FP32的反量化的Scale和ShiftQAT通过Q和DQ Node里面存储的信息对FP32或者INT8进行线性变换。 2QAT是一种Fine-Tuning方式通过一个Pre-Trained Model进行添加Q/DQ节点模拟量化并通过训练来更新权重去吸收量化过程带来的误差。添加Q/DQ节点后算子会以INT8精度执行 Calibration的种类有 1Minmax Calibration、Entropy Calibration、Percentile Calibration 2如果Floating Point的分布比较分散各个区间下都比较均匀Minmax Calibration是一个不错的选择 3目前TensorRT使用默认是Entropy Calibration。一般来讲Entropy Calibration精度可以比较好 4Weight一般使用Minmax Calibration信息尽量不要有遗漏Activation Values一般使用Entropy或者Percentile Calibration不同Layer的Activation Values值分布会有较大不同 5Calibration时Batch Size会影响精度总的来讲Batch Size越大越好但不是绝对的。 常用的量化粒度 1Per-tensor 量化在 Per-tensor 量化中整个张量的所有元素使用相同的量化尺度和偏移量。也就是说对于一个特定的层或整个网络的某一张量所有的元素都共享一个全局的量化范围。其优点是延迟低缺点是错误率高因此一个Scale很难覆盖所有FP32的Dynamic Range。 2Per-channel 量化在 Per-channel 量化中每个通道例如卷积核或特征图的通道会使用独立的量化范围。这意味着每个通道的激活值或权重会有不同的量化尺度和偏移量。其优点是低错误率缺点是高延迟因为需要使用vector来存储一个Channel的Scale。 3Weight的量化一般会选取Per-Channel量化因为BN的参数会融合在线性计算中BN的参数是Per-Channle的此外还有Depthwise Convolution的存在Activation Values的量化一般会选取Per-Tensor量化。 如果出现精度下降怎么办 在我们进行模型量化时 1第一步应该先进行PTQ尝试多种Calibration算法如果精度不满足考虑进行第二步 2第二步应该是进行Partial-Quntization通过Layer-wise的Sensitive Analysis分析每一层精度损失尝试FP16INT8的组合将FP16用在敏感层INT8用在计算密集层如果精度还不满足再考虑第三步 3第三步才是QAT通过Fine-Tuning来训练权重原本训练的10%个Epoch。 量化后精度下降控制在相对精度损失小于2%是合理的
- 什么是模型剪枝模型剪枝的分类各类剪枝的利弊都有哪些 模型剪枝Model Pruning 是一种深度学习模型优化技术旨在减少模型的复杂度比如模型的参数数目或计算量从而提升推理速度、减少内存占用并提高模型的部署效率。它通过移除不重要的模型参数使得模型更加紧凑。步骤通常如下 1获取一个已经训练好的初始模型 2对这个模型进行剪枝 1. 可以通过训练的方式让模型去学习哪些权重可以归零例如使用L1 Regularization和BN的Scaling Factor让权重归零 2. 自定义一些规则手动地让某些权重归零例如对 1 × 4 1\times 4 1×4的Vector进行2:4的Weight Prunning 3对剪枝后的模型进行Fine-tuning剪枝后初期模型通常会掉点通过Fine-tuning来恢复精度 4获取到一个压缩的模型 模型分类通常分为结构化剪枝和非结构化剪枝、粗粒度剪枝和细粒度剪枝 1粗粒度剪枝 1. 比较常见的是Channel/Kernel Pruning通常是使用L1 Norm寻找权重中影响度比较低的卷积核 2. 优势是不依赖硬件劣势是更容易掉精度剪枝后可能反而不适合硬件加速Tensor Core的使用条件是Channel是8或者16的倍数 2细粒度剪枝 1. 细粒度剪枝可以进一步分为结构化剪枝Vector-wise和Block-wise和非结构化剪枝Element-wise 2. 优势是对精度影响较小劣势是需要特殊硬件支持Tensor Core可以支持Sparse以及需要额外的Memory来存储哪些Index是可以保留计算的 Channel-Level Pruning 参考文章是Learning Efficient Convolutional Networks through Network Slimming主要思路是通过使用BN中的Scaling Factor与L1-Regularization的训练可以让权重趋于0找到Conv中不是很重要的Channel实现Channel-Level的Pruning。 1L1 Regularization可以用来系数参数或者让参数趋向于零L2 Regularization可以用来减小参数值的大小这个通过L1 Regularization的公式就能理解 L min f ( x ) λ ∑ i 1 n ∣ w i ∣ L\min f(x)\lambda \sum_{i1}^n\left|wi\right| Lminf(x)λi1∑n∣wi∣2Batch Normalization通常放在Conv之后对Conv的输出进行Normalization。整个计算是Channel-wise的每个Channel都拥有自己的BN参数如果Batch Normalization之后发现某一个Channle的Scaling非常小或者为零那么就可以认为这个Channel参与的计算没有非常的大强度的改变或者特征提取可以忽略。 3使用Batch Normalization和L1 Regularization对模型的权重进行计算和重要度排序 L ∑ ( x , y ) l ( f ( x , W ) , y ) λ ∑ γ ∈ Γ g ( γ ) g ( s ) ∣ s ∣ L\sum{(x, y)} l(f(x, W), y)\lambda \sum{\gamma \in \Gamma} g(\gamma) \quad g(s)|s| L(x,y)∑l(f(x,W),y)λγ∈Γ∑g(γ)g(s)∣s∣这里和普通的L1 Regularization区别是惩罚的不再是每一个权重而是Batch Normalization对于Conv中每一个Channel的Scaling Factor。从而在学习过程中使得Scaling Factor趋近于零。如上图所示我们可以将 C i 2 C{i2} Ci2和 C i 4 C_{i4} Ci4去除掉。
- 模型推理中的基本框架 当我们对不同模型部署的时候可以发现 1不管是分类、分割还是检测模型推理的流程都是 前处理-模型推理-后处理 2在创建推理引擎时流程都是bulider-nertwork-config-serialize-save file 3在执行推理引擎时流程都是load file-deserialize-engine-context-enqueue 4在模型推理中用得比较多的设计模式是工厂模式和消费者-生产者模式 研究开源代码推荐Lidar_AI_Solution
- 模型部署中的一些注意点 FLOPs不能完全衡量模型性能 1因为FLOPs只是模型大小的单位 2模型性能还需要考虑访存量、和计算无关的DNN部分Reshape、Shortcut等、和DNN以外的部分前处理、后处理模型部署不能完全依靠TensorRT 1计算密度低的 1 × 1 1\times 1 1×1 ConvDepth Wise Conv不会重构 2GPU无法优化的地方会到CPU执行此时需要手动修改代码实现部分让部分CPU执行转到GPU 3部分冗长的计算TensorRT可能为了优化天机一些多余的操作比如reformatter这种 4存在TensorRT尚未支持的算子 5TensorRT不一定会分配Tensor Core因为TensorRT Kerner Auto Tunning会选择最合适的KernelCUDA Core和Tensor Core的使用 1因此Kernel Auto Tuning自动选择最优解有时候TensorRT并不会分配Tensor Core所以有时候会出现INT8速度比FP16慢的问题 2使用Tensor Core需要让Tensor Size为8或者16的倍数8的倍数为FP16的精度16的倍数为INT8的精度不能前处理和后处理的Overhead 1对于一些轻量模型前处理/后处理可能会更加耗时 2可以把有些前处理/后处理中可并行的地方用GPU处理例如RGP2BGR、Normalization、Resize、Crop、NCHW2NHWC 3可以在CPU上使用一些图像处理优化库例如HalideBlur、Resize、Crop、DBSCAN、Sobel这些操作会比Opencv要快 以上是对一些基本概念的总结实际模型部署的工作会比上述更加复杂有趣比如如果部署的算子导出失败如何通过ONNX的插件构建和修改等等还会有很多工具和API需要学习这些就在之后工作上具体遇到实际问题时再进一步了解吧
- 上一篇: 绿色系的网站公司装修设计案例
- 下一篇: 麻江网站建设推广小程序
