当前位置:   article > 正文

ncnn源码阅读(四)----模型推理过程_ex.extract("prob", @out);

ex.extract("prob", @out);

模型推理过程

推理过程主要涉及两个类:Net、Extractor,在示例中的使用如下:

 	ncnn::Net squeezenet;
    squeezenet.load_param("squeezenet_v1.1.param");
    squeezenet.load_model("squeezenet_v1.1.bin");

    ncnn::Mat in = ncnn::Mat::from_pixels_resize(bgr.data, ncnn::Mat::PIXEL_BGR, bgr.cols, bgr.rows, 227, 227);

    const float mean_vals[3] = {104.f, 117.f, 123.f};
    in.substract_mean_normalize(mean_vals, 0);
	
	//利用Net得到Extractor类的实例
    ncnn::Extractor ex = squeezenet.create_extractor();
    //为Extractor设置模式和输入数据
    ex.set_light_mode(true);
    ex.input("data", in);
	
	//通过extract得到推理结果
    ncnn::Mat out;
    ex.extract("prob", out);

    cls_scores.resize(out.c);
    for (int j=0; j<out.c; j++)
    {
        const float* prob = out.data + out.cstep * j;
        cls_scores[j] = prob[0];
    }

    return 0;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

ncnn中的调用逻辑

  • 利用net的实例和blob的数量创建Extractor实例
Extractor Net::create_extractor() const
{
    return Extractor(this, blobs.size());
}
  • 1
  • 2
  • 3
  • 4

Extractor的构造函数

Extractor::Extractor(const Net* _net, int blob_count) : net(_net)
{
    blob_mats.resize(blob_count);
    lightmode = false;
    num_threads = 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

blob_mats存储所有blob的数据内容,它的定义为std::vector<Mat> blob_mats;
Extractor类中的set_light_mode方法主要用来修改类中的lightmode成员变量,input方法则是对对应的blob进行赋值:

int Extractor::input(const char* blob_name, const Mat& in)
{
	//根据blob的名字得到在blob集合中的索引
    int blob_index = net->find_blob_index_by_name(blob_name);
    if (blob_index == -1)
        return -1;
	//将对应的输入数据赋值给对应索引的Mat
    blob_mats[blob_index] = in;
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

在Extractor类中的blob_mats是这个类共享的,而Net类中的blob是数据节点。blob_mats中存储的是每个数据节点的具体数值,与Net类中的blob依次对应。
使用Extractor类中的extract方法,可以根据blob名字得到对应blob的数据,具体逻辑就是根据blob名字找到对应的index,然后根据index将blob_mats中Mat进行返回。

int Extractor::extract(const char* blob_name, Mat& feat)
{
	//根据名字得到对应的索引
    int blob_index = net->find_blob_index_by_name(blob_name);
    if (blob_index == -1)
        return -1;

    int ret = 0;
	
	//根据索引判断blob_mats中对应的数据是否有效,如果没有效,就执行推理,得到填充后的blob_mats
    if (blob_mats[blob_index].dims == 0)
    {
        int layer_index = net->blobs[blob_index].producer;
        ret = net->forward_layer(layer_index, blob_mats, lightmode);
    }
	//根据索引返回对应位置的Mat
    feat = blob_mats[blob_index];

    return ret;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

递归调用实现网络逐层的执行

根据上面的extract方法,可以看到,对blob_mats填充的过程由Net类中的forward_layer来完成,所以需要重点看一下这个推理的过程,也就是下面这段代码的具体实现:

 if (blob_mats[blob_index].dims == 0)
 {
     int layer_index = net->blobs[blob_index].producer;
     ret = net->forward_layer(layer_index, blob_mats, lightmode);
 }
  • 1
  • 2
  • 3
  • 4
  • 5

上述分支中表达的意思是:根据blob名字得到索引,然后根据索引在blob_mats获取的数据是无效数据,所以需要进行网络的前向计算得到当前blob数据节点的具体数据。
首先,根据blob的produce可以知道前blob的数据应该由网络的哪一层产生;为了减少计算量,并不需要推理完成所有的网络,只要计算到当前需要的blob的那一层即可,所以在前向计算的时候,需要知道网络中目标层的索引,以及运行的模式。
基于上述的过程,int layer_index = net->blobs[blob_index].producer;就是获得目标层的索引,然后将目标层的索引,和存储数据节点数据的blob_mats以及运行模式lightmode,告诉前向推理。也就是这行代码:ret = net->forward_layer(layer_index, blob_mats, lightmode);下面将详细分析一下Net类中的forward_layer方法:

  • 根据layer_index也就是层的索引,获取层的实例;
const Layer* layer = layers[layer_index];
  • 1
  • 根据层是不是单输入,单输出,进行分支处理;
    • 是单输入、单输出的层
      1、首先获取输入和输出blob的index;
      2、根据索引从blob_mats中取得输入的blob;
      3、根据输入blob的dims判断输入数据是否有效;
      4、如果无效,则利用当前blob的producer,递归调用当前forward_layer函数;
      5、如果有效,则取出当前层的输入blob;
      lightmode表示轻量级模式在网络推理中会不断地进行垃圾回收;
      根据lightmode为false,则执行逻辑相对简单:
      6、调用层的forward函数,将输入的blob输入,得到输出的blob,然后再将输出的blob对blob_mats相应的位置赋值;
      如果lightmode为true:
      将blob_mats中对应输入blob位置的mat进行release();后面的操作或者inplace计算中,也要保证使用Mat的独立性,方便资源的释放。
if (layer->one_blob_only)
{
    // load bottom blob
    int bottom_blob_index = layer->bottoms[0];
    int top_blob_index = layer->tops[0];

    if (blob_mats[bottom_blob_index].dims == 0)
    {
        int ret = forward_layer(blobs[bottom_blob_index].producer, blob_mats, lightmode);
        if (ret != 0)
            return ret;
    }

    Mat bottom_blob = blob_mats[bottom_blob_index];

    if (lightmode)
    {
        // delete after taken in light mode
        blob_mats[bottom_blob_index].release();
        // deep copy for inplace forward if data is shared
        if (layer->support_inplace && *bottom_blob.refcount != 1)
        {
            bottom_blob = bottom_blob.clone();
        }
    }

    // forward
    if (lightmode && layer->support_inplace)
    {
        Mat& bottom_top_blob = bottom_blob;
        int ret = layer->forward_inplace(bottom_top_blob);
        if (ret != 0)
            return ret;

        // store top blob
        blob_mats[top_blob_index] = bottom_top_blob;
    }
    else
    {
        Mat top_blob;
        int ret = layer->forward(bottom_blob, top_blob);
        if (ret != 0)
            return ret;

        // store top blob
        blob_mats[top_blob_index] = top_blob;
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
- 不是单输入、单输出的层
  • 1

1、根据当前layer的输入数量多少,定义一个存储输入blob的Mat的vector;
2、根据输入数量的多少进行遍历,每次遍历进行的操作与单输入单输出的流程相同;

 std::vector<Mat> bottom_blobs;
 bottom_blobs.resize(layer->bottoms.size());
 for (size_t i=0; i<layer->bottoms.size(); i++)
 {
     int bottom_blob_index = layer->bottoms[i];

     if (blob_mats[bottom_blob_index].dims == 0)
     {
         int ret = forward_layer(blobs[bottom_blob_index].producer, blob_mats, lightmode);
         if (ret != 0)
             return ret;
     }

     bottom_blobs[i] = blob_mats[bottom_blob_index];

     if (lightmode)
     {
         // delete after taken in light mode
         blob_mats[bottom_blob_index].release();
         // deep copy for inplace forward if data is shared
         if (layer->support_inplace && *bottom_blobs[i].refcount != 1)
         {
             bottom_blobs[i] = bottom_blobs[i].clone();
         }
     }
 }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

3、forward的过程与单输入单输出的流程相似,在输出的时候需要通过遍历赋值

 // forward
 if (lightmode && layer->support_inplace)
  {
      std::vector<Mat>& bottom_top_blobs = bottom_blobs;
      int ret = layer->forward_inplace(bottom_top_blobs);
      if (ret != 0)
          return ret;

      // store top blobs
      for (size_t i=0; i<layer->tops.size(); i++)
      {
          int top_blob_index = layer->tops[i];

          blob_mats[top_blob_index] = bottom_top_blobs[i];
      }
  }
  else
  {
      std::vector<Mat> top_blobs;
      top_blobs.resize(layer->tops.size());
      int ret = layer->forward(bottom_blobs, top_blobs);
      if (ret != 0)
          return ret;

      // store top blobs
      for (size_t i=0; i<layer->tops.size(); i++)
      {
          int top_blob_index = layer->tops[i];

          blob_mats[top_blob_index] = top_blobs[i];
      }
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

完整的多输入多输出的代码如下:

 // load bottom blobs
 std::vector<Mat> bottom_blobs;
 bottom_blobs.resize(layer->bottoms.size());
 for (size_t i=0; i<layer->bottoms.size(); i++)
 {
     int bottom_blob_index = layer->bottoms[i];

     if (blob_mats[bottom_blob_index].dims == 0)
     {
         int ret = forward_layer(blobs[bottom_blob_index].producer, blob_mats, lightmode);
         if (ret != 0)
             return ret;
     }

     bottom_blobs[i] = blob_mats[bottom_blob_index];

     if (lightmode)
     {
         // delete after taken in light mode
         blob_mats[bottom_blob_index].release();
         // deep copy for inplace forward if data is shared
         if (layer->support_inplace && *bottom_blobs[i].refcount != 1)
         {
             bottom_blobs[i] = bottom_blobs[i].clone();
         }
     }
 }

 // forward
 if (lightmode && layer->support_inplace)
 {
     std::vector<Mat>& bottom_top_blobs = bottom_blobs;
     int ret = layer->forward_inplace(bottom_top_blobs);
     if (ret != 0)
         return ret;

     // store top blobs
     for (size_t i=0; i<layer->tops.size(); i++)
     {
         int top_blob_index = layer->tops[i];

         blob_mats[top_blob_index] = bottom_top_blobs[i];
     }
 }
 else
 {
     std::vector<Mat> top_blobs;
     top_blobs.resize(layer->tops.size());
     int ret = layer->forward(bottom_blobs, top_blobs);
     if (ret != 0)
         return ret;

     // store top blobs
     for (size_t i=0; i<layer->tops.size(); i++)
     {
         int top_blob_index = layer->tops[i];

         blob_mats[top_blob_index] = top_blobs[i];
     }
 }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/703303
推荐阅读
相关标签
  

闽ICP备14008679号