yolov5内存分布分析

发布时间 2023-05-30 13:51:35作者: Truman001

yolov5内存分布分析

Transpose输出分析

假设batch_size为1,yolov5有三个输出,shape分别是:

  • (1,3,80,80,85)
  • (1,3,40,40,85)
  • (1,3,20,20,85)

其中3代表anchor数量,20*20代表feature_map大小,85代表boundbox的(x,y,w,h,c+80个类别的概率)

其中(x,y,w,h,c+80个类别的概率)在内存中是连续分布的,即:

(1,3,20,20,85)整个数组在内存分布中也是连续分布的,

  • (0,0,0,0,0)->x->第一个anchor在第一个cell对应的boundingbox的x
  • (0,0,0,0,1)->y->第一个anchor在第一个cell对应的boundingbox的y
  • (0,0,0,0,2)->w->第一个anchor在第一个cell对应的boundingbox的w
  • (0,0,0,0,3)->h->第一个anchor在第一个cell对应的boundingbox的h
  • ​ ......
  • (0,0,0,1,0)->x->第一个anchor在第二个cell对应的boundingbox的x
  • (0,0,0,1,1)->y->第一个anchor在第二个cell对应的boundingbox的y
  • (0,0,0,1,2)->w->第一个anchor在第二个cell对应的boundingbox的w
  • (0,0,0,1,3)->h->第一个anchor在第二个cell对应的boundingbox的h
  • ......
  • (0,1,0,0,0)->x->第二个anchor在第一个cell对应的boundingbox的x
  • (0,1,0,0,1)->y->第二个anchor在第一个cell对应的boundingbox的y
  • (0,1,0,0,2)->w->第二个anchor在第一个cell对应的boundingbox的w
  • (0,1,0,0,3)->h->第二个anchor在第一个cell对应的boundingbox的h

即:

后处理代码分析

# 从第一个anchor开始获取
for (int q = 0; q < num_anchors; q++)
    {
        const float anchor_w = anchors[q * 2];
        const float anchor_h = anchors[q * 2 + 1];

        const ncnn::Mat feat = feat_blob.channel(q);
    
        #从第一个cell开始获取
        for (int i = 0; i < num_grid_y; i++)
        {
            for (int j = 0; j < num_grid_x; j++)
            {
                const float* featptr = feat.row(i * num_grid_x + j);
                #第5个是box_confidence值,需要使用sigmoid函数求值
                float box_confidence = sigmoid(featptr[4]);
                if (box_confidence >= prob_threshold)
                {
                    # 之所以这么写是因为可以减少sigmoid(class_score)的次数,sigmoid较为耗时
                    #find class index with max class score
                    int class_index = 0;
                    float class_score = -FLT_MAX;
                    for (int k = 0; k < num_class; k++)
                    {
                        # box_confidence之后是每个类别的概率
                        float score = featptr[5 + k];
                        if (score > class_score)
                        {
                            class_index = k;
                            class_score = score;
                        }
                    }
                    #论文规定
                    float confidence = box_confidence * sigmoid(class_score);
                    if (confidence >= prob_threshold)
                    {
                        # 依次获取x,y,w,h
                        float dx = sigmoid(featptr[0]);
                        float dy = sigmoid(featptr[1]);
                        float dw = sigmoid(featptr[2]);
                        float dh = sigmoid(featptr[3]);
    					
                        # 其余部分省略,可以参考ncnn代码
                        .......
   
                    }
                }
            }
        }
    }

====================================================================================

Conv输出分析

NPU对算法进行加速处理时,shape算子,如reshape、transpose通常不支持加速,有两种解决方法,

  • 使用C/C++语言重新实现reshape、transpose算子功能,使用CPU进行处理(待完善)
  • 直接按照conv层的输出内存分布获取数据进行处理

假设batch_size为1,卷积层的输出shape为:

  • (1,255,80,80)
  • (1,255,40,40)
  • (1,255,20,20)

其中255表示3*85,3代表anchor数量,,85代表boundbox的(x,y,w,h,c+80个类别的概率),20x20代表feature_map大小。

其中(x,y,w,h,c+80个类别的概率)在内存中是连续分布的,即:

(1,255,20,20)整个数组在内存分布中也是连续分布的,

  • (0,0,0,0)->x->第一个anchor在第一个cell对应的boundingbox的x

  • (0,0,0,1)->x->第一个anchor在第二个cell对应的boundingbox的x

  • (0,0,0,2)->x->第一个anchor在第三个cell对应的boundingbox的x

  • ......

  • (0,1,0,0)->x->第一个anchor在第一个cell对应的boundingbox的y

  • (0,1,0,1)->x->第一个anchor在第二个cell对应的boundingbox的y

  • (0,1,0,2)->x->第一个anchor在第三个cell对应的boundingbox的y

  • ......

  • (0,85,0,0)->x->第一个anchor在第一个cell对应的boundingbox的y

  • (0,85,0,1)->x->第二个anchor在第二个cell对应的boundingbox的y

  • (0,85,0,2)->x->第二个anchor在第三个cell对应的boundingbox的y

  • ....

    即:

后处理代码分析

# 从第一个cell开始
for(int shiftY = 0; shiftY < gridY; shiftY++)
	{
		for(int shiftX = 0; shiftX < gridX; shiftX++)
		{
                        # 从第一个anchor开始
			for(int i = 0; i < 3; i++)
			{
				pRecord = pMatData[i];
				# 获取当前cell
				int pindex = shiftY* gridX + shiftX;
				# coordindex的坐标对应x
                                int coordindex = pindex;
                                # 指针移动到y
				pindex = pindex + gridX * gridY;
				# 指针移动到w
                                pindex = pindex + gridX * gridY;
				# 指针移动到h
                                pindex = pindex + gridX * gridY;
				# 指针移动到C
                                pindex = pindex + gridX * gridY;
				# 获取C的值
				float  precord4 = sigmoid(pRecord[pindex]);
				# 指针移动到P
                                pindex = pindex + gridX * gridY ;
	
				for (cls = 0; cls < classNum; cls++)
				{
					#获取P的值
					float  precord5 = sigmoid(pRecord[pindex]);
					#指针移动到P1
                                         pindex = pindex + gridX * gridY;
	
					score = precord5 * precord4;

	
					if (score > gYolov7Para.confidenceThreshold)
					{//大于设置的阈值
						# 获取x
						float  precord0 = sigmoid(pRecord[coordindex]);
						coordindex = coordindex + gridX * gridY;
						# 获取y	
                     	                        float  precord1 = sigmoid(pRecord[coordindex]);
						coordindex = coordindex + gridX * gridY;
						# 获取w	
                     	                        float  precord2 = sigmoid(pRecord[coordindex]);
						coordindex = coordindex + gridX * gridY;
						# 获取h	
                     	                        float  precord3 = sigmoid(pRecord[coordindex]);
						coordindex = coordindex + gridX * gridY;
						# 其余部分省略
                        .......
					}
	
				}
			}
		}
	}