C++ YOLOv3推理 第二讲: 权重及参数加载

简介

本文主要想要讨论的是,是yolov3模型加载过程中,所涉及的数据结构。

流程

在main函数中,可以看到,首先调用的是;ggml_time_init(), 在ggml.c中可以找到它的定义,主要用于计时。

然后,初始化对象yolo_model,观察其数据结构发现,有一个ggml_context的数据结构,以及一个conv2d组成的vector。

然后, 创建对象yolo_params,并赋值。这里提供了模型的名称等信息。然后,我们可以通过输入的参数修改这些信息。

struct yolo_params {
    float thresh          = 0.5;
    std::string model     = "yolov3-tiny.gguf";
    std::string fname_inp = "input.jpg";
    std::string fname_out = "predictions.jpg";
};

在load_model函数中,我们可以通过模型的路径,将模型参数加载到yolo_model中,

这里对应的关键函数是gguf_init_from_model()。 这个函数的长度为300多行,实现了gguf模型的加载。这个过程详细说起来又是另一个i故事了。

static bool load_model(const std::string & fname, yolo_model & model) {
    struct gguf_init_params params = {
        /*.no_alloc   =*/ false,
        /*.ctx        =*/ &model.ctx,
    };
    gguf_context * ctx = gguf_init_from_file(fname.c_str(), params);
    if (!ctx) {
        fprintf(stderr, "%s: gguf_init_from_file() failed\n", __func__);
        return false;
    }
    model.width  = 416;
    model.height = 416;
    model.conv2d_layers.resize(13);
    model.conv2d_layers[7].padding = 0;
    model.conv2d_layers[9].padding = 0;
    model.conv2d_layers[9].batch_normalize = false;
    model.conv2d_layers[9].activate = false;
    model.conv2d_layers[10].padding = 0;
    model.conv2d_layers[12].padding = 0;
    model.conv2d_layers[12].batch_normalize = false;
    model.conv2d_layers[12].activate = false;
    for (int i = 0; i < (int)model.conv2d_layers.size(); i++) {
        char name[256];
        snprintf(name, sizeof(name), "l%d_weights", i);
        model.conv2d_layers[i].weights = ggml_get_tensor(model.ctx, name);
        snprintf(name, sizeof(name), "l%d_biases", i);
        model.conv2d_layers[i].biases = ggml_get_tensor(model.ctx, name);
        if (model.conv2d_layers[i].batch_normalize) {
            snprintf(name, sizeof(name), "l%d_scales", i);
            model.conv2d_layers[i].scales = ggml_get_tensor(model.ctx, name);
            snprintf(name, sizeof(name), "l%d_rolling_mean", i);
            model.conv2d_layers[i].rolling_mean = ggml_get_tensor(model.ctx, name);
            snprintf(name, sizeof(name), "l%d_rolling_variance", i);
            model.conv2d_layers[i].rolling_variance = ggml_get_tensor(model.ctx, name);
        }
    }
    return true;
}

在完成了gguf数据的读取后,模型的每一个卷积层的参数被读取,值得注意的是,这里卷积层的层数为13,是被提前定义,而不是读取到的,同样,输入的大小也是提前定义好的。读取的参数类型包括:weights, bias, scales,rolling_mean, rolling variance。

我们再来看看函数ggml_get_tensor的实现。

其实现可以在src/ggml.c中找到。通过对比tensor的名称,我们加载模型参数中对应的tensor。

struct ggml_tensor * ggml_get_tensor(struct ggml_context * ctx, const char * name) {
    struct ggml_object * obj = ctx->objects_begin;

    char * const mem_buffer = ctx->mem_buffer;

    while (obj != NULL) {
        if (obj->type == GGML_OBJECT_TENSOR) {
            struct ggml_tensor * cur = (struct ggml_tensor *)(mem_buffer + obj->offs);
            if (strcmp(cur->name, name) == 0) {
                return cur;
            }
        }
        obj = obj->next;
    }

    return NULL;
}

我们再来看一下ggml_tensor的数据结构。

ggml_backend_type定义了tensor是通过gpu还是cpu计算。此外,还定义参数的数量等·参数。

    // n-dimensional tensor
    struct ggml_tensor {
        enum ggml_type         type;
        enum ggml_backend_type backend;

        struct ggml_backend_buffer * buffer;

        int64_t ne[GGML_MAX_DIMS]; // number of elements
        size_t  nb[GGML_MAX_DIMS]; // stride in bytes:
                                   // nb[0] = ggml_type_size(type)
                                   // nb[1] = nb[0]   * (ne[0] / ggml_blck_size(type)) + padding
                                   // nb[i] = nb[i-1] * ne[i-1]

        // compute data
        enum ggml_op op;

        // op params - allocated as int32_t for alignment
        int32_t op_params[GGML_MAX_OP_PARAMS / sizeof(int32_t)];

        bool is_param;

        struct ggml_tensor * grad;
        struct ggml_tensor * src[GGML_MAX_SRC];

        // performance
        int     perf_runs;
        int64_t perf_cycles;
        int64_t perf_time_us;

        struct ggml_tensor * view_src;
        size_t               view_offs;

        void * data;

        char name[GGML_MAX_NAME];

        void * extra; // extra things e.g. for ggml-cuda.cu

        char padding[8];
    };

总结

模型参数的记载到此基本讲完,读取的这些参数的作用,只有结合具体的推理,才能更好的理解,这也是下一章的目标。

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要在C++中进行Yolov5推理,您需要下载Yolov5的预训练模型和相应的C++推理库。以下是一个简单的步骤: 1. 下载Yolov5预训练模型 您可以从Yolov5官方GitHub存储库(https://github.com/ultralytics/yolov5)下载预训练模型。选择您需要的模型并将其下载到本地计算机。 2. 安装C++推理库 有许多C++推理库可供选择,例如OpenCV、TensorRT、ONNX等。在这里,我们将使用OpenCV深度学习模块,因为它易于安装和使用。 要使用OpenCV中的深度学习模块,您需要安装OpenCV并启用DNN模块。可以在以下链接中找到有关如何安装OpenCV的信息:https://opencv.org/releases/。 3. 加载模型 使用OpenCV的dnn模块,您可以轻松加载Yolov5模型。以下是示例代码: ```cpp cv::dnn::Net net = cv::dnn::readNet("path/to/your/yolov5/model", "path/to/your/yolov5/config"); ``` 4. 进行推理 一旦模型加载,您可以使用它来进行推理。以下是一个示例代码,演示如何使用OpenCV进行Yolov5推理: ```cpp cv::Mat image = cv::imread("path/to/your/image"); cv::Mat blob = cv::dnn::blobFromImage(image, 1/255.0, cv::Size(640, 640), cv::Scalar(0, 0, 0), true, false); net.setInput(blob); std::vector<cv::Mat> outs; net.forward(outs, net.getUnconnectedOutLayersNames()); for (cv::Mat& out : outs) { // 处理输出结果 } ``` 在上面的代码中,我们首先读取要进行推理的图像,然后使用OpenCV的blobFromImage函数将其转换为网络输入。接下来,我们将输入设置为网络的输入,然后调用前向方法来获得输出。最后,我们可以通过处理输出来获得检测结果。 请注意,在处理输出时,您需要将输出转换为可读的格式。具体来说,您需要将它们转换为边界框,并且通过应用非最大抑制算法来过滤掉重叠的检测结果。 这是一个简单的步骤来使用C++进行Yolov5推理,但要进行更深入的推理,您需要深入了解底层库和算法。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值