当前位置:   article > 正文

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

c++ yolov3 推理

简介

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

流程

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

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

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

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

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

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

  1. static bool load_model(const std::string & fname, yolo_model & model) {
  2. struct gguf_init_params params = {
  3. /*.no_alloc =*/ false,
  4. /*.ctx =*/ &model.ctx,
  5. };
  6. gguf_context * ctx = gguf_init_from_file(fname.c_str(), params);
  7. if (!ctx) {
  8. fprintf(stderr, "%s: gguf_init_from_file() failed\n", __func__);
  9. return false;
  10. }
  11. model.width = 416;
  12. model.height = 416;
  13. model.conv2d_layers.resize(13);
  14. model.conv2d_layers[7].padding = 0;
  15. model.conv2d_layers[9].padding = 0;
  16. model.conv2d_layers[9].batch_normalize = false;
  17. model.conv2d_layers[9].activate = false;
  18. model.conv2d_layers[10].padding = 0;
  19. model.conv2d_layers[12].padding = 0;
  20. model.conv2d_layers[12].batch_normalize = false;
  21. model.conv2d_layers[12].activate = false;
  22. for (int i = 0; i < (int)model.conv2d_layers.size(); i++) {
  23. char name[256];
  24. snprintf(name, sizeof(name), "l%d_weights", i);
  25. model.conv2d_layers[i].weights = ggml_get_tensor(model.ctx, name);
  26. snprintf(name, sizeof(name), "l%d_biases", i);
  27. model.conv2d_layers[i].biases = ggml_get_tensor(model.ctx, name);
  28. if (model.conv2d_layers[i].batch_normalize) {
  29. snprintf(name, sizeof(name), "l%d_scales", i);
  30. model.conv2d_layers[i].scales = ggml_get_tensor(model.ctx, name);
  31. snprintf(name, sizeof(name), "l%d_rolling_mean", i);
  32. model.conv2d_layers[i].rolling_mean = ggml_get_tensor(model.ctx, name);
  33. snprintf(name, sizeof(name), "l%d_rolling_variance", i);
  34. model.conv2d_layers[i].rolling_variance = ggml_get_tensor(model.ctx, name);
  35. }
  36. }
  37. return true;
  38. }

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

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

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

  1. struct ggml_tensor * ggml_get_tensor(struct ggml_context * ctx, const char * name) {
  2. struct ggml_object * obj = ctx->objects_begin;
  3. char * const mem_buffer = ctx->mem_buffer;
  4. while (obj != NULL) {
  5. if (obj->type == GGML_OBJECT_TENSOR) {
  6. struct ggml_tensor * cur = (struct ggml_tensor *)(mem_buffer + obj->offs);
  7. if (strcmp(cur->name, name) == 0) {
  8. return cur;
  9. }
  10. }
  11. obj = obj->next;
  12. }
  13. return NULL;
  14. }

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

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

  1. // n-dimensional tensor
  2. struct ggml_tensor {
  3. enum ggml_type type;
  4. enum ggml_backend_type backend;
  5. struct ggml_backend_buffer * buffer;
  6. int64_t ne[GGML_MAX_DIMS]; // number of elements
  7. size_t nb[GGML_MAX_DIMS]; // stride in bytes:
  8. // nb[0] = ggml_type_size(type)
  9. // nb[1] = nb[0] * (ne[0] / ggml_blck_size(type)) + padding
  10. // nb[i] = nb[i-1] * ne[i-1]
  11. // compute data
  12. enum ggml_op op;
  13. // op params - allocated as int32_t for alignment
  14. int32_t op_params[GGML_MAX_OP_PARAMS / sizeof(int32_t)];
  15. bool is_param;
  16. struct ggml_tensor * grad;
  17. struct ggml_tensor * src[GGML_MAX_SRC];
  18. // performance
  19. int perf_runs;
  20. int64_t perf_cycles;
  21. int64_t perf_time_us;
  22. struct ggml_tensor * view_src;
  23. size_t view_offs;
  24. void * data;
  25. char name[GGML_MAX_NAME];
  26. void * extra; // extra things e.g. for ggml-cuda.cu
  27. char padding[8];
  28. };

总结

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小丑西瓜9/article/detail/727950
推荐阅读
相关标签
  

闽ICP备14008679号