赞
踩
本文主要想要讨论的是,是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];
- };
模型参数的记载到此基本讲完,读取的这些参数的作用,只有结合具体的推理,才能更好的理解,这也是下一章的目标。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。