Yolov5_Seg输出解析
- 蒙面西红柿
- 174
通过矩阵乘法(在代码中称为“matmul
”)来计算分割掩码的原因,主要与实例分割网络(例如 YOLOv5 Segmentation)的实现方式有关。这种方法实际上是一种高效的特征图与目标分割系数组合的方式,可以将目标的分割信息从特征空间映射到图像空间中。
在这里,矩阵乘法用于将分割特征图(通常称为 proto
)和目标的分割系数进行组合,以生成每个目标的分割掩码。以下是更详细的解释:
实例分割的输出
在实例分割任务中,通常模型会输出以下两类信息:
- 边界框和类别预测:每个目标的边界框、类别以及置信度。这个部分与传统目标检测类似。
- 分割特征图(Proto):这是一个多通道特征图,通常被称为原型特征图(Proto Map),它捕捉了图像中所有像素的丰富特征信息,但并不直接给出每个实例的分割结果。
此外,模型还会为每个目标预测出一组系数(Mask Coefficients),这些系数是与分割特征图相结合,用于提取特定目标的分割掩码。
通过矩阵乘法计算分割掩码的原理
- 分割特征图(Proto Map):
- 在推理阶段,模型会输出一个包含多通道特征的二维分割特征图。这些特征图可以看作是一种全局的表示,包含了图像中所有物体的信息。
- 特征图的尺寸通常为
[C, H, W]
,其中C
是通道数,H
和W
是特征图的高度和宽度。
- 分割系数(Mask Coefficients):
- 模型还为每个检测到的目标输出一组分割系数,这些系数的数量等于分割特征图的通道数
C
。 - 每个目标的分割系数可以看作是如何从全局分割特征图中“提取”出该目标的独特掩码的权重。
- 模型还为每个检测到的目标输出一组分割系数,这些系数的数量等于分割特征图的通道数
- 矩阵乘法(Matmul):
- 将每个目标的分割系数与分割特征图进行矩阵乘法,计算出该目标的分割掩码。
- 通常,矩阵乘法的形式是:分割系数向量(大小为
[1, C]
)与分割特征图(大小为[C, H, W]
)相乘,得到一个二维掩码(大小为[H, W]
)。 - 这个乘法的结果是,将全局特征图中的信息根据每个目标的特征权重提取出来,从而生成该目标的掩码。
为什么使用矩阵乘法?
- 高效计算:
- 通过矩阵乘法的方式,可以利用分割特征图与每个目标的特征系数生成多个目标的分割掩码。这样做相比对每个目标分别生成掩码更加高效。
- 特别是在硬件(如 GPU、NPU)上执行时,矩阵乘法可以得到充分的加速,极大提高计算效率。
- 共享全局特征图:
- 分割特征图是整个图像的全局特征图,它们包含了所有可能的目标的语义信息。每个目标仅通过一组系数来选择和组合这些特征,从而得到特定的目标掩码。这种共享全局特征图的方法节省了内存,并且提升了推理速度,因为不用为每个目标单独计算一张分割图。
- 特征的线性组合:
- 分割特征图的通道实际上是特定空间的“基”,而分割系数就是对这些基的加权组合系数。通过矩阵乘法,我们可以用分割系数对这些基进行线性组合,从而得到每个目标的分割掩码。
- 这样做的好处是,特征图能够捕捉到图像整体的语义信息,分割系数则能够有效地从中提取出特定目标的信息。
代码中的矩阵乘法
在代码中,矩阵乘法部分的逻辑如下:
int ROWS_A = boxes_num; // 每个目标一个系数向量
int COLS_A = PROTO_CHANNEL; // 分割特征图的通道数
int COLS_B = PROTO_HEIGHT * PROTO_WEIGHT; // 分割特征图的空间维度
float *matmul_out = (float *)malloc(boxes_num * PROTO_HEIGHT * PROTO_WEIGHT * sizeof(float));
matmul_by_cpu_fp(filterSegments_by_nms, proto, matmul_out, ROWS_A, COLS_A, COLS_B);
filterSegments_by_nms
:这是每个目标的分割系数向量。proto
:分割特征图。matmul_out
:矩阵乘法的结果,即每个目标在特征图上的分割掩码,维度为[boxes_num, H, W]
。
通过矩阵乘法,matmul_out
存储了每个目标的初步分割结果,然后会经过尺寸调整和裁剪等操作,得到最终用于后续分析的分割掩码。
相同大小的图像,检测到的物体越多,所需的时间也会增加
1. 矩阵乘法的规模增大
在实例分割中,通过矩阵乘法将检测到的目标系数向量与分割特征图(proto
)相结合来生成分割掩码。当检测到的物体越多时,执行矩阵乘法的规模会增加,因为你需要为每一个目标执行一次矩阵乘法。
假设:
- 检测到的物体数量为
N
。 - 分割特征图的通道数为
C
。 - 分割特征图的高度和宽度分别为
H
和W
。
矩阵乘法的形状可以看作是:
- 系数矩阵(
N × C
):每个目标有一个C
维的系数向量。 - 特征矩阵(
C × (H * W)
):分割特征图展开后的形状。
在执行矩阵乘法时,每个目标都需要将 C
维的系数与 C × (H * W)
的特征图相乘。因此,如果检测到的物体数量增加到 N
,矩阵乘法的总复杂度为 O(N * C * H * W)
,这意味着检测到的物体越多,需要执行的计算量也越多。
2. 计算复杂度增加
每个新增的物体都会增加一次矩阵乘法,因此计算时间也会相应增加。如果检测到的物体数量翻倍,那么执行矩阵乘法所需的计算量也会近乎翻倍。这就导致了整个推理后处理的时间增加。
3. 矩阵乘法与硬件加速
尽管现代硬件(例如 GPU 或 NPU)可以加速矩阵乘法,但每次新增一个物体都意味着更多的数据需要计算和传输,因此计算时间仍然会有所增加。硬件加速器的能力通常可以减少这种增长的影响,但在物体数量非常多时,计算资源的消耗仍然会显著增加。
4. 内存需求增加
除了计算时间,更多的物体还意味着需要分配更多的内存来存储每个物体的分割系数、计算出的分割掩码以及临时的中间结果。这也可能增加内存带宽的压力,进一步增加处理时间。
代码中的表现
在代码中,有以下逻辑:
int ROWS_A = boxes_num; // 每个目标一个系数向量,表示物体数量
int COLS_A = PROTO_CHANNEL; // 分割特征图的通道数
int COLS_B = PROTO_HEIGHT * PROTO_WEIGHT; // 分割特征图的空间维度
float *matmul_out = (float *)malloc(boxes_num * PROTO_HEIGHT * PROTO_WEIGHT * sizeof(float));
matmul_by_cpu_fp(filterSegments_by_nms, proto, matmul_out, ROWS_A, COLS_A, COLS_B);
ROWS_A
等于boxes_num
,即检测到的物体数量。- 随着
boxes_num
增加,矩阵乘法的规模和计算量成比例增加,这意味着时间开销也会增加。
文章评论