你或许也想拥有专属于自己的AI模型文件格式(推理部署篇)-(8)

AI模型各种存储格式文件介绍(pb, onnx, ckpt, tflite, h5) .ckpt模型文件 tensorflow框架下保存的模型,包含以下几个子文件: model.ckpt.meta :保存Tensorflow计算图结构,可以理解为神经网络的网络结构 model.ckpt :保存Tensorflow程序中每一个变量的取值,变量是模型中可训练的部分 checkpoint :保存一个目录下所有模型文件列表 .pb 模型文件 pb是protocolbuffer的缩写。TensorFlow训练模型后存成的pb文件,是一种表示模型(神经网络)结构的二进制文件,不带有源代码,也. 阅读详情

《你或许也想拥有专属于自己的AI模型文件格式》这个系列,上一次的文章更新是2022-03-05,距今已经4个月之久了。

主要是这段时间有其他的事情在忙,期间也在断断续续地在完善该推理框架的。而让我思绪卡死的地方就是整网的推理时事件依赖问题,这个问题主要是因为OpenCL的clEnqueueNDRangeKernel这个核函数推理函数的局限性问题(正文仔细说明)。

1、前言

本次的进展内容:

  • 完成了整网的推理时框架构建
  • 根据有向图重排了网络的网络层推理顺序
  • 根据重排后的网络层的有向图形成了事件依赖
  • 适配了卷积(Conv2d)算子和池化(Pool2d)算子(编写.cl核函数以及相关适配代码)

本次文章的主要内容:

  • 有向图重排网络层的核心算法
  • 算子适配的过程以及核函数的具体实现(以Conv2d为例)
  • 初次测试推理框架的推理速度性能

目前的工程整体结构:

.
|-- 3rdparty
|   `-- flatbuffers
|       |-- bin
|       |   `-- flatc.exe
|       |-- include
|       |   `-- flatbuffers
|       `-- lib
|           |-- debug
|           `-- release
|-- CMakeLists.txt
|-- README.md
|-- build
|-- clkernel
|   |-- conv2d.cl
|   |-- img2col.cl
|   `-- pool2d.cl
|-- example
|   |-- create_model_sample.cpp
|   `-- main.cpp
|-- include
|   |-- CL
|   |-- model
|   |   |-- json11.hpp
|   |   |-- pzk-schema_generated.h
|   |   `-- pzk.hpp
|   `-- runtime
|       |-- builder.hpp
|       |-- engine.hpp
|       |-- img2col.hpp
|       `-- op
|           |-- allops.hpp
|           |-- conv2d.hpp
|           |-- img2col.hpp
|           `-- pool2d.hpp
|-- model-flatbuffer
|   |-- pzk-metadata.json
|   `-- pzk-schema.fbs
|-- run.sh
|-- src
|   |-- model
|   |   `-- json11.cpp
|   `-- runtime
`-- test-model
    `-- first.pzkm

2、有向图重排网络层

2.1、重排的原因和目的
  • 模型的网络层顺序是乱序的:我们自定义的模型中,对应网络层的顺序没有任何要求。因此根据原始的网络层顺序去构建整网的推理时,那么必然会导致推理顺序出现问题,这很可能会导致卡死、结果出错的问题。
  • 重排网络层有利于形成核函数执行时的事件依赖:也就是如果不进行网络层重排,对于clEnqueueNDRangeKernel的事件依赖参数就无法正确给出,这样我们就无法对推理框架形成异步推理接口。注重事件依赖的原因是:事件依赖保证了网络层的执行顺序,而保证了最终结果的正确性。
2.2、重排的核心思想
如标题所示,重排网络层用到的主要思想就是有向图。而具体的方法是:
  • 1、用有向图抽象出了网络层之间的连接关系,尤其是数据流的流向关系,也就是剥离了网络层的输入输出依赖关系;
  • 2、找出现在有向图中的所有的根节点RootNodeSet(也就是只有输出没有输入的节点)
  • 3、这些RootNodeSet作为一个整体部分,作为事件依赖的一个整体节点
  • 4、去掉现有有向图中的这部分RootNodeSet
  • 5、更新有向图
  • 6、如果有向图还有节点,则重复2-5步骤;否则,结束。

对应的流程图如下所示:

模型文件
有向图
存在
不存在
RootNodeSet
开始
获取有向图
存在节点
结束
找出根节点
保存根节点
移除根节点
更新有向图
2.3、具体的代码如下所示
    /* 返回重排结果的标号信息 */
    std::vector<size_t> ReSortByDirectedGraph(std::vector<std::vector<bool>> DirectedGraph){
        /* 运用的主要原理是根节点只有输出没有输入的特性;
            通过不断去除掉根节点,更新有向图,然后进行操作的时候
        */
        std::vector<size_t> ReSortIndex;
        std::vector<size_t> RemainIndex;
        std::vector<size_t> RegIndex;
        std::vector<size_t> Reg2Index;
        std::vector<std::vector<bool>> BakDirectedGraph = DirectedGraph;
        for(size_t i = 0; i < DirectedGraph.size(); i++){
            RemainIndex.push_back(i);
        }
        /* 1.开始进行根节点获取操作 */
        ReSortIndex = JudgeRootNode(DirectedGraph);
        /* 2. 移除RemainIndex中的重复点 */
        RemainIndex = MinusSet(RemainIndex, ReSortIndex);
        BakDirectedGraph = RemoveDirectedGraph(BakDirectedGraph, ReSortIndex);
        /* 3. 重复上述两个步骤,直到BakDirectedGraph中不存在节点或者是RemainIndex中没有值 */
        while(RemainIndex.size() > 0 && BakDirectedGraph.size() > 0 && ReSortIndex.size() < DirectedGraph.size()){
            Reg2Index.clear();
            RegIndex = JudgeRootNode(BakDirectedGraph);
            /* 加入到ReSortIndex中 */
            for(auto i:RegIndex){
                ReSortIndex.push_back(RemainIndex[i]);
                Reg2Index.push_back(RemainIndex[i]);
            }
            RemainIndex = MinusSet(RemainIndex, Reg2Index);
            BakDirectedGraph = RemoveDirectedGraph(BakDirectedGraph, RegIndex);
        }
        return ReSortIndex;
    }

3、算子适配过程(以Conv2d算子作为具体说明)

3.1、跟整体推理框架的接入
  • 1、cl算子编写在clkernel文件夹内(比如clkernel/conv2d.cl)

  • 2、在include/runtime/op中增加算子头文件(比如conv2d.hpp)

#include "runtime/engine.hpp"

namespace OCLEngine{
    struct Conv2dCfg{
        cl_mem* input = NULL;
        cl_mem* weights = NULL;
        cl_mem* biases = NULL;
        cl_mem* output = NULL;
        NodeEvent event;
        uint batchSize = 1;
        uint inputChannels;
        uint inputWidth;
        uint inputHeight;
        uint kernelWidth;
        uint kernelHeight;
        uint padTop = 0;
        uint padRight = 0;
        uint padBottom = 0;
        uint padLeft = 0;
        uint strideX;
        uint strideY;
        size_t outputChannels;
        size_t outputHeight;
        size_t outputWeight;
    };

    class Conv2dLayer : public CLFunction{
    private:
        Conv2dCfg cfg;
        cl_kernel kernel = NULL;
        size_t* globalWorkSize = NULL;
        size_t* localWorkSize = NULL;
        cl_uint work_dim = 0;
        bool useful = false;
        cl_int Conv2derrNum = CL_SUCCESS;
    public:
        Conv2dLayer() = default;
        ~Conv2dLayer(){
            if (this->globalWorkSize != NULL){
                free(this->globalWorkSize);
            }
            if (this->localWorkSize != NULL){
                free(this->localWorkSize);
            }
            if (this->kernel != NULL){
                clReleaseKernel(kernel);
            }
        };
        // 配置函数
        bool configure(Conv2dCfg conf){
            this->cfg  = conf;
            std::vector<std::string> buildOptions;
            if (this->cfg.biases != NULL){
                /* 如果有bias,则进行如下所示的编译命令 */
                buildOptions.push_back(std::string("-D HASBIAS"));
            }
            /* 1、获取对应的核心 */
            this->kernel = ProgramManager.GetKernel(std::string("conv2d.cl"), buildOptions, std::string("convolutionNaive"));
            if (this->kernel == NULL){
                printf("Get convolutionNaive kernel of conv2d.cl Failed\n");
                return false;
            }
            /* 2、对核心进行相应的参数设置 */
            cl_uint arg_idx = 0;
            Conv2derrNum = clSetKernelArg(kernel,arg_idx,sizeof (cl_mem),
                                    this->cfg.input);
            Conv2derrNum |= clSetKernelArg(kernel,++arg_idx,sizeof (uint), this->cfg.weights);
            if (this->cfg.biases != NULL)
                Conv2derrNum |= clSetKernelArg(kernel,++arg_idx,sizeof (cl_mem),this->cfg.biases);
            Conv2derrNum |= clSetKernelArg(kernel,++arg_idx,sizeof (uint),&(this->cfg.batchSize));
            Conv2derrNum |= clSetKernelArg(kernel,++arg_idx,sizeof (uint),&(this->cfg.inputChannels));
            Conv2derrNum |= clSetKernelArg(kernel,++arg_idx,sizeof (uint),&(this->cfg.inputWidth));
            Conv2derrNum |= clSetKernelArg(kernel,++arg_idx,sizeof (uint),&(this->cfg.inputHeight));
            Conv2derrNum |= clSetKernelArg(kernel,++arg_idx,sizeof (uint),&(this->cfg.kernelWidth));
            Conv2derrNum |= clSetKernelArg(kernel,++arg_idx,sizeof (uint),&(this->cfg.kernelHeight));
            Conv2derrNum |= clSetKernelArg(kernel,++arg_idx,sizeof (uint),&(this->cfg.padTop));
            Conv2derrNum |= clSetKernelArg(kernel,++arg_idx,sizeof (uint),&(this->cfg.padRight));
            Conv2derrNum |= clSetKernelArg(kernel,++arg_idx,sizeof (uint),&(this->cfg.padBottom));
            Conv2derrNum |= clSetKernelArg(kernel,++arg_idx,sizeof (uint),&(this->cfg.padLeft));
            Conv2derrNum |= clSetKernelArg(kernel,++arg_idx,sizeof (uint),&(this->cfg.strideX));
            Conv2derrNum |= clSetKernelArg(kernel,++arg_idx,sizeof (uint),&(this->cfg.strideY));
            Conv2derrNum |= clSetKernelArg(kernel,++arg_idx,sizeof (cl_mem),this->cfg.output);
            if (Conv2derrNum != CL_SUCCESS){
                printf("Set kernel Arguments Failed On Conv2d Layers\n");
                return false;
            }
            /* 3、 设置全局尺寸和局部尺寸大小,以便于后续的加入命令队列操作 */
            this->work_dim = 3;
            this->globalWorkSize = (size_t*)malloc(sizeof(size_t) * this->work_dim);
            this->globalWorkSize[0] = this->cfg.batchSize * this->cfg.outputChannels;
            this->globalWorkSize[1] = this->cfg.outputHeight;
            this->globalWorkSize[2] = this->cfg.outputWeight;
            this->localWorkSize = (size_t*)malloc(sizeof(size_t) * this->work_dim);
            this->localWorkSize[0] = 1;
            this->localWorkSize[1] = 1;
            this->localWorkSize[2] = 1;
            useful = true;
            return true;
        };
        // 重载函数,主要的run函数
        void run() override{
            if (this->useful){
                this->Conv2derrNum = clEnqueueNDRangeKernel(commandQueue,
                                                            this->kernel,
                                                            this->work_dim,
                                                            NULL,
                                                            this->globalWorkSize,
                                                            this->localWorkSize,
                                                            this->cfg.event.wait_event.num,
                                                            this->cfg.event.wait_event.event,
                                                            this->cfg.event.this_event.event);
                if (this->Conv2derrNum != CL_SUCCESS){
                    printf("Inference Conv2d Layers Failed\n");
                    return;
                }
            }else{
                printf("This Conv2d Layers is useless\n");
                return;
            }
        };
        /* cpu推理函数,主要用于测试 
            此时,因为其父类拥有这个
        */
        void cpu_run() override{

        };
    };
}
  • 3、在include/runtime/op/allops.hpp中新增对接代码
/* 2.1、增加对应算子头 */
#include "runtime/op/conv2d.hpp"
#include <memory>
namespace OCLEngine{
    /* ... */
    /* 2.2、conv2d网络层的构建 */
    bool add_conv2d_layer(layer_maker l, NodeEvent node_event, std::vector<TensorsS> input, std::vector<TensorsS> output){
        Conv2dCfg cfg;
        cfg.input = l.get_input_id("input") != -1 ? &(clmem[l.get_input_id("input")]):NULL;
        cfg.weights = l.get_input_id("weights") != -1 ? &(clmem[l.get_input_id("weights")]):NULL;
        cfg.biases = l.get_input_id("biases") != -1 ? &(clmem[l.get_input_id("biases")]):NULL;
        cfg.output = l.get_output_id("conv2d-output") != -1 ? &(clmem[l.get_output_id("conv2d-output")]):NULL;
        if (cfg.input == NULL || cfg.weights == NULL || cfg.output == NULL){
            return false;
        }
        cfg.event = node_event;
        TensorsS input_tensor = input[find_tensor_by_id(input, l.get_input_id("input"))];
        cfg.batchSize = input_tensor.shape.dims[0];
        cfg.inputChannels = input_tensor.shape.dims[1];
        cfg.inputHeight = input_tensor.shape.dims[2];
        cfg.inputWidth = input_tensor.shape.dims[3];
        TensorsS weight_tensor = input[find_tensor_by_id(input, l.get_input_id("weights"))];
        cfg.kernelHeight = weight_tensor.shape.dims[2];
        cfg.kernelWidth = weight_tensor.shape.dims[3];
        cfg.padTop = l.get_attr<uint>(std::string("padTop")).size() == 0 ? 0:l.get_attr<uint>(std::string("padTop"))[0];
        cfg.padRight = l.get_attr<uint>(std::string("padRight")).size() == 0 ? 0:l.get_attr<uint>(std::string("padRight"))[0];
        cfg.padBottom = l.get_attr<uint>(std::string("padBottom")).size() == 0 ? 0:l.get_attr<uint>(std::string("padBottom"))[0];
        cfg.padLeft = l.get_attr<uint>(std::string("padLeft")).size() == 0 ? 0:l.get_attr<uint>(std::string("padLeft"))[0];
        cfg.strideX = l.get_attr<uint>(std::string("strideX")).size() == 0 ? cfg.kernelWidth:l.get_attr<uint>(std::string("strideX"))[0];
        cfg.strideY = l.get_attr<uint>(std::string("strideY")).size() == 0 ? cfg.kernelHeight:l.get_attr<uint>(std::string("strideY"))[0];
        TensorsS output_tensor = output[find_tensor_by_id(output, l.get_output_id("conv2d-output"))];
        cfg.outputChannels = output_tensor.shape.dims[1];
        cfg.outputHeight = output_tensor.shape.dims[2];
        cfg.outputWeight = output_tensor.shape.dims[3];
        /* 正式构建卷积层 */
        std::shared_ptr<Conv2dLayer> conv2d = std::make_shared<Conv2dLayer>();
        if (conv2d->configure(cfg) == false){
            printf("conv2d make failed\n");
            return false;
        }else{
            AllLayers.push_back(conv2d);
            return true;
        }
    }

    /* 构建运行时的网络层 */
    bool BuildLayers(PzkM model){
        bool ret = true;
        for (size_t i = 0; i < model.rLayers.size(); i++){
            /* 进行各种不同类型的选择 */
            if (onelayer.type == "img2col"){
            }else if (onelayer.type == "Convolution2dLayer"){
                /* 2.2、增加上述函数的调用 */
                ret = add_conv2d_layer(onelayer, node_event, input_tensor, output_tensor);
            }else if (onelayer.type == "Pooling2dLayer"){
            }
            else{
                printf("unknown type = %s layer, cant't finish it\n", onelayer.type.c_str());
                return false;
            }
            /* 查看是否正确与否 */
            if (!ret){
                printf("failed to build type=%s, name=%s Layers\n", onelayer.type.c_str(), onelayer.name.c_str());
                return false;
            }
        }
        return true;
    }
}
3.2、CL算子介绍

实际cl核函数如下所示:

__kernel
void convolutionNaive(__global const float* input,
            __global const float* weights,
#ifdef HASBIAS
            __global const float* biases,
#endif
            const uint batchSize,
            const uint inputChannels,
            const uint inputWidth,
            const uint inputHeight,
            const uint kernelWidth,
            const uint kernelHeight,
            const uint padTop,
            const uint padRight,
            const uint padBottom,
            const uint padLeft,
            const uint strideX,
            const uint strideY,
            __global float* output
            ){
  int outputChannels = get_global_size(0) / batchSize;
  int outputHeight = get_global_size(1);
  int outputWeight = get_global_size(2);
  /* NC融合进行之后,如何拆分出相应维度
    错误示例如下:
    解释:会导致实际分配不正确,当batchSize=1,OutputChannels=10的时候,发现oc===0,明显出错。
    int b = get_global_id(0) / batchSize;
    int oc = get_global_id(0) % batchSize;
   */
  int b = get_global_id(0) / outputChannels;/* batchSize被融入到第一个并行度中,N*C */
  int oc = get_global_id(0) % outputChannels;
  int ohx = get_global_id(1); // [0, col_chw)
  int owy = get_global_id(2);
  uint output_offset = b * outputChannels * outputHeight * outputWeight + oc * outputHeight * outputWeight + ohx * outputWeight + owy;
  uint input_feature_map_size = inputHeight * inputWidth;
  uint input_one_size = inputChannels * input_feature_map_size;
  uint weight_feature_map_size = kernelWidth * kernelHeight;
  uint weight_one_size = inputChannels * weight_feature_map_size;
// /* 定义一次卷积的长度=kernelWidth乘kernelHeight */
// #define CalSize 10
//   local float input_reg[CalSize];
//   local float weights_reg[CalSize];
  /*
  [ohx, owy]表示输出特征图的x,y点坐标
  我们需要从输出映射到输入的坐标值,需要考虑到Pad的偏移等因素。
  */
  float result = 0.0;
  int padinputWidthMax = padLeft + inputWidth;
  int padinputHeightMax = padBottom + inputHeight;
  int ihx = ohx * strideX;
  int iwy = owy * strideY;
  /* 首先只进行卷积的weight乘加 */
  for (uint i = 0; i < kernelHeight; i++){
    if (ihx + i < padTop || ihx + i >= padinputHeightMax){
        continue;
    }else{
        for (uint j = 0; j < kernelWidth; j++){
            if (iwy + j < padRight || iwy + j >= padinputWidthMax){
                continue;
            }else{
                /* 此时表示没有超出卷积的尺寸范围之外,所以需要进行卷积操作 */
                uint one_featuremap_offset = (ihx + i - padTop) * inputWidth + (iwy + j - padRight);
                uint one_weight_offset = i * kernelWidth + j;
                for (uint ic = 0; ic < inputChannels; ic++){
                    uint input_ptr = b * input_one_size + ic * input_feature_map_size + one_featuremap_offset;
                    uint weight_ptr = oc * weight_one_size + ic * weight_one_size + one_weight_offset;
                    result += (input[input_ptr] * weights[weight_ptr]);
                }
            }
        }
    }
  }
  /* 然后进行bias的相加 */
#ifdef HASBIAS
  result += biases[oc];
#endif
  output[output_offset] = result;
}

解释说明:

  • 假设卷积层的输出尺寸为[N,C,H,W],并行工作维度设置为3,其工作项数目分别是[N*C,H,W]
  • 使用opencl的预编译指令优化和函数:当存在bias权重时,设置HASBIAS开始bias计算
  • 一个工作项只处理一个输出数据,实现最大并行度。
3.3、推理性能测试

测试条件如下所示:

  • 硬件环境:处理器Intel® Xeon® Silver 4110 CPU @ 2.10GH,32核心
  • 软件环境:Intel-CPU-OpenCL-SDK-64bit,GCC编译,Linux-Ubuntu18.64
  • 测试环境:10000次,异步推理,float推理
  • 测试模型:如下所示
[1,3,416,416]
[1,10,104,104]
[1,10,52,52]
输入
卷积 核[10,3,4,4]
池化 核[2,2]
输出

测试结果如下所示:

There is no GPU,trying CPU……
Result: open ../model-flatbuffer/pzk-metadata.json success
<-------------------------------------->
DirectedGraph Mat:
False, True , False, False, 
False, False, True , False, 
False, False, False, True , 
False, False, False, False, 
<-------------------------------------->
<-------------------------------------->
DirectedGraph Mat:
False, True , 
False, False, 
<-------------------------------------->
depend of event is 
node=0--->[dpnum=0,dphead=-1,thisid=0]
node=1--->[dpnum=1,dphead=0,thisid=1]
node=2--->[dpnum=1,dphead=1,thisid=2]
node=3--->[dpnum=1,dphead=2,thisid=-1]
inference time is 480.458 fps

结果显示:

  • 该模型在该cpu上能够实现480fps的帧率,成绩还算不错。
  • 使用htop工具可观察出在推理的时候CPU占用率高,所有核心都被利用起来了,适合多核CPU推理。

4、后续计划与安排

其实到这一部分,该推理框架已经成形,可以应对众多的算子适配和相应模型推理。但是还有待相应的完善,如下所示:

  • 适配更多常用算子:常见激活算子relu、sigmoid等
  • 验证算子计算正确性:验证cl核函数是否编写正确
  • 模型转换工具:需要编写一个模型转换工具去转换比如pytorch或者是caffe,甚至是darknet的训练前端模型到自定义的模型上。
  • 其他:异构推理(调度多计算设备去同时进行一次推理)、量化支持等
《AI大模型技术应知应会100篇》No1. 大模型文件格式一篇读懂读全 大模型作为人工智能领域的核心技术,其文件格式和存储方式直接影响模型的训练、推理、部署和跨平台适配能力。根据现有研究和实践,大模型文件主要分为两类:**参数文件**和**代码文件**,前者存储神经网络的权重和偏差,后者包含运行模型的操作逻辑 。本文将详细介绍AI大模型的文件格式、对应的深度学习AI框架、主流应用场景、国内外主要厂商采用的文件格式以及技术趋势等,并提供了一个具体的大模型文件格式及参数说明,并提供了加载每一种大模型的代码示例,是比较全面的一篇介绍,建议收藏! 阅读详情

相关推荐

YOLOv8n 模型部署 RK3588 全流程

本文介绍了在Windows系统上训练YOLOv8目标检测模型并将其部署到RK3588芯片的全流程。主要内容包括:1) Windows环境下通过Miniconda搭建PyTorch训练环境;2) 使用Ultralytics库训练得到.pt模型;3) 在Ubuntu系统下将PyTorch模型转换为ONNX格式;4) 通过RKNN-Toolkit2工具链将ONNX模型转换为RK3588专用的.rknn格式;5) 在RK3588开发板上部署RKNN模型,支持图片、视频和RTSP实时流推理。

weixin_52938172的博客 2305

AI 模型格式大揭秘

和 HashTable 的区别,假如发⽣了 hash 碰撞,如何设计能让遍历效率⾼?8.volatile的作⽤,为什么会出现变量读取不⼀致的情况,与 synchronized 的区别?13.讲讲 Java 的泛型擦除,泛型主要是为了解决什么问题?如何⽤泛型做 Json 的解析的?14.谈谈 Java 的 Error 和 Exception 的区别联系。重写的规则是怎样的?20.十亿条淘宝购买记录,怎么获取出现最多的前十个?3.讲一下JVM虚拟机内存结构,以及它们的作⽤。15.说一下软引⽤和弱引⽤的区别?

2401_89221867的博客 1531

AI全景之第十章第二节:模型转换与推理引擎

模型转换与推理引擎技术概览 AI模型产品化过程中面临训练框架与生产环境的鸿沟,ONNX、TensorRT和OpenVINO构成了关键的技术解决方案。ONNX作为通用中间格式,解决了框架异构性问题,通过标准化计算图表示和算子库实现模型互通。TensorRT专为NVIDIA GPU优化,采用算子融合、混合精度等核心技术提升推理性能。OpenVINO则针对Intel平台提供跨硬件统一API和深度优化。三者协同工作,形成从训练框架转换到高性能推理部署的完整技术链,有效应对生产环境中的性能、硬件适配等核心挑战。

技术引领业务创新 1147

智能计算系统实验(1) BANGC算子实现与TensorFlow的集成

这是2020年6月份的实验。现在决定发出来。 1.1 算子实现和测试 1.1.1 算子实验 (1) 初始化环境:cd /opt/AICSE-demo-student/env; source env.sh (2) 进入plugin_power_difference_kernel.h,补全头文件。补全头文件需要确定参数列表。PowerDifference的含义是(X-Y)Z(X-Y)^Z(X-Y)Z,其中ZZZ是标量,因此至少需要3个参数。后来从PPT中发现了PowerDifference的声明,确定了是有5个

一个幽灵 5046

《DeepSeek R1:大模型的安装与部署全攻略》:此文为AI自动生成

在正式开始安装部署之前,我们先来简单了解一下 DeepSeek R1。DeepSeek R1 是由 [研发机构名称] 推出的一款具有先进技术的人工智能模型,它在自然语言处理、文本生成、智能问答等多个领域都展现出了出色的能力。该模型经过大量数据的训练,具备了强大的语义理解和逻辑推理能力,能够处理各种复杂的任务,为用户提供高质量的服务。通过以上步骤,我们详细介绍了 DeepSeek R1 的安装与部署流程,以及在安装部署过程中可能遇到的问题及解决方法。

zheng_ruiguo的专栏 2万+

深入探讨基于PyTorch框架的算子开发与适配:如何高效适配至昇腾AI处理器

算子适配是将PyTorch模型在昇腾AI处理器上高效运行的关键步骤。通过遵循统一的适配原则,合理配置yaml文件,编写合适的算子适配代码,我们可以充分发挥硬件的计算能力,提升模型的推理效率。随着深度学习模型和硬件加速技术的不断发展,算子适配将变得越来越重要。未来,我们可以预见更多高级的适配策略和优化技术,将帮助开发者更好地使用PyTorch与昇腾AI处理器。

qq_38334677的博客 1342

AI中大模型的文件格式有哪些?

大模型。

本博客,博文仅代表个人操作经验,不能完全解决你的问题,仅供参考,佛系回复。 5357

IoTDB时序数据库在Windows下的保姆级安装教程(含环境变量配置避坑指南)

本文提供Apache IoTDB时序数据库在Windows系统下的详细安装与配置指南。教程涵盖从Java环境检查、软件下载解压,到环境变量配置的深度避坑,并详解了服务的启动、停止、连接测试及基础数据操作,帮助物联网开发者在Windows平台快速搭建稳定的时序数据库环境。

weixin_29275257的博客 261

你或许也想拥有专属于自己的AI模型文件格式(推理部署篇)-(6)

如果有新的观众,如有疑惑,请从《专ai模》这个专栏从头开始阅读。特别是想要彻底搞清楚深度学习中模型的起源,以及如何一步一步构建自定义模型格式的观众来说,除了可视化篇,前面的三章必须要仔细阅读。 而为什么我又有雅致来开启《推理部署篇》呢?主要原因是,目前个人野心有点大,想要从零构建一整套关于推理部署的工具链吧。也就是向着推理引擎的方向努力吧。特别是自从上次结束了《可视化篇》,总感觉意犹未尽。 因此,全新的篇章开启了,该篇章的文章数可能会控制在10篇以下吧。不...

Pengcode的博客 1054

STM32实战篇:使用USART发送/接收数据

在发送字符时也可以直接使用其对应的ASCLL码,例如: USART_SendData(USART1, 0x61);

qq_74114817的博客 596

Fedora 41上安装钉钉7.6.25,我踩过的那些依赖坑和最终解决方案

本文详细记录了在Fedora 41上安装钉钉7.6.25版本的全过程,包括解决依赖问题、转换deb包为rpm格式、处理libm.so.6错误等关键步骤。通过分享这些实用技巧和最终解决方案,帮助Linux用户顺利在Fedora系统上运行钉钉客户端,提升工作效率。

weixin_30485379的博客 205

还在为模型加速推理发愁吗?不如看看这篇吧。手把手教你把pytorch模型转化为TensorRT,加速推理

文章目录摘要环境配置ONNX模型转化第一种推理写法第二种推理写法TensorRT模型转化动态推理静态推理 摘要 最近,学习了一些模型转化和加速推理的知识,本文是对学习成果的总结。 对模型的转化,本文实现了pytorch模型转onnx模型和onnx转TensorRT,在转为TensorRT模型的过程中,实现了模型单精度的压缩。 对于加速推理,本文实现GPU环境下的onnxruntime推理、TensorRT动态推理和TensorRT静态推理。 希望本文能帮助大家。 环境配置 CUDA版本:11.3.1 cuD

AI浩 5528

【收藏向】模拟电子技术超强知识点总结 20小时不挂科

模电真的有难度的一门课,一定要好好学… 用的教材是华科康华光版,其他版本教材也可参考,内容是差不多的。 话不多说直接上思维导图干货(后有思维导图高清原图链接) 1 绪论 2 运算放大器 3 二极管及其基本电路 4 场效应管及其放大电路 5 双极结型三极管及其放大电路 三种组态总结看不清,给个特写 发现虽然csdn上传的是原图,但是显示的图片不是特别清晰,在此贴出B站知乎地址,第五章之后的内容都在上面,还可以查看原图 ———————————————————— B站专栏地址(可以点击查看原图) htt

工科狗Niko的博客 1万+

YOLOv8的5种不同部署方式推理速度对比:Pytorch、ONNX、OpenVINO-FP32、OpenVINO-int8、TensorRT

YOLOv8的5种不同部署方式推理速度对比:Pytorch、ONNX、OpenVINO-FP32、OpenVINO-int8、TensorRT

阿旭的博客 1万+

madgwick算法基于MPU9250姿态融合漂移问题的原因

基于MPU9250的madgwick算法姿态漂移问题原因 0、声明 因为这个是现象是不是共性目前本人还没研究过,只是在个体问题研究的基础上得出的结论,或许我买到的MPU9250是一个盗版:),所以只具备参考性。 1、现象 使用MPU9250的加速度计、角速度计、磁力计(AK8963)原始数据输入到madgwick算法中,得到的姿态最终会在停止姿态变化后仍然漂移一小段时间。其中加速度计和角速度计读取速率为400hz,磁力计为100hz,加速度计采样范围±2g,角速度计采样范...

GreedyZombie的博客 3872

CANoe/CAPL ,远程邮件通知

CANoe/CAPL ,远程邮件通知

蚂蚁小兵 3036

5个维度彻底理解AI模型文件格式

在当今机器学习模型部署的实践中,模型文件格式扮演着连接研发与生产的关键角色。一个高效的AI模型文件格式不仅能够显著提升机器学习推理优化效率,还能简化跨平台部署流程。本文将从概念本质、技术架构、行业对比、应用实践和未来演进五个维度,全面解析AI模型文件格式的核心原理与实践价值,为开发者提供从理论到落地的完整视角。 ## 一、概念本质:AI模型的"数字容器" ### 1.1 模型文件的核心功能定位

gitblog_00687的博客 486

《QT+PCL》点云的点选与框选

【代码】《QT+PCL》点云的点选与框选。

阁楼 3602

Android高通平台上大模型部署和推理

高通NPU(HTP)平台上对PyTorch/SAFETensor模型进行优化和部署的过程,具体包括格式转换、量化、编译等步骤。这个流程是为了确保模型能够在终端设备上高效地推理运行。以下是对每个步骤的详细解释和涉及的底层原理。

酌沧 5180

淘宝滑块验证码破解:Python模拟鼠标轨迹过关详解

本文全面解析了淘宝滑块验证码的破解流程,从机制分析、图像处理、轨迹模拟到自动化实现,提供了完整的Python解决方案。随着2026年风控技术的升级,单纯依靠轨迹模拟已经难以达到高通过率,未来趋势是结合强化学习生成更自然的轨迹,并利用深度神经网络进行端到端的验证码绕过。此外,各大平台正逐步推广"无感验证"和"行为式验证码",将验证融入到用户正常操作中,这对爬虫技术提出了更高的挑战。技术本身是中立的,关键在于使用者的目的。希望本文能帮助开发者更好地理解验证码对抗技术,从而设计出更安全的防御系统。

2201_76125261的博客 96

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

YOLOv10模型部署推理速度对比:Pytorch、ONNX、OpenVINO-FP32、OpenVINO-int8、TensorRT

YOLOv10模型部署推理速度对比:Pytorch、ONNX、OpenVINO-FP32、OpenVINO-int8、TensorRT

阿旭的博客 3301

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

AI 大模型实战工具详解,涵盖从开发、训练、推理到部署的全流程工具链,帮助开发者高效构建和落地大模型应用

AI 大模型实战工具详解,涵盖从开发、训练、推理到部署的全流程工具链,帮助开发者高效构建和落地大模型应用

爱的叹息的专栏 1467

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

【AI】推理引擎的模型转换模块

在推理引擎架构中,API 接口下来就是模型转换,狭义的模型转换是指将各种框架的模型转换为统一的格式,即;广义的模型转换则还包括部分,因为不同的框架提供的算子不同,且类型太多,存在优化空间。

weixin_45651194的博客 938

AI嵌入式RK3588人工智能开发 --- 【2】RKNPU软件推理框架

RKNN Toolkit2 仓库链接为 https://github.com/rockchip-linux/rknn-toolkit2。文件夹内容如下所示:RKNN-Toolkit2 资料包中有四个目录,分别为 docs、examples、packages 和 rknn_toolkit_lite2。docs 目录包含了 RKNN-Toolkit2 的使用文档,包括更新记录、两个 Python 环境所需的依赖 项、RKNN 算子支持列表、快速入门指南以及使用指导手册。

xinxin的博客 3380

收藏这篇就够了!C++版Llama.cpp入门,从环境搭建到模型推理,一篇搞定!

llama.cpp是由Georgi Gerganov创建的轻量级推理引擎,它是基于C/C++语言编码实现的LLM框架,支持大模型的训练和推理,专注于在本地硬件环境(比如个人电脑、树莓派等)上高效运行LLM模型。

m0_56255097的博客 5719

AI模型文件格式技术指南:从概念到实践的GGUF深度解析

### 1.1 什么是GGUF格式 GGUF(GGML Universal Format)是一种专为机器学习推理设计的二进制文件格式,作为GGML生态系统的核心组成部分,它整合了GGML、GGMF和GGJT格式的技术优势,实现了单一文件部署和完全向后兼容的设计目标。与传统模型格式不同,GGUF将模型权重、架构信息和元数据封装在单个文件中,无需额外依赖文件,如同为AI模型打造的"集装箱",既保证了

gitblog_00625的博客 456
上一篇: Intel-Cpu-OpenCL-Runtime-SDK的使用和示例源码编译
下一篇: 你或许也想拥有专属于自己的AI模型文件格式(推理部署篇)-(9)
慷仔
博客等级 码龄9年 88粉丝 46原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值