YOLACT分割:32个原型如何生成百万Mask 第一步为什么不能直接预测 Mask假设输入图片640 × 640检测出来猫1 猫2 狗1如果每一个目标直接预测一个 Mask例如640 × 640就是409600个像素。假设100 个目标。网络需要输出100 × 640 × 640 40960000四千多万个数。这是非常大的。所以大家开始想有没有办法不要每一个目标都预测一张 Mask于是YOLACT 提出了Prototype。第二步什么叫 Prototype先不要想 Mask。想画画。假设我给你32 种颜色。例如红 蓝 绿 黄 黑 白 ……以后任何一幅画。是不是都可以用不同颜色 不同配比画出来例如天空蓝色 80% 白色 20%树绿色 90% 黄色 10%其实颜色没有无限多。只需要几十种基础颜色。然后调配。Prototype就是基础颜色。第三步Prototype 在 Mask 里面是什么意思例如网络先预测32 张基础Mask例如这里只是示意Prototype1████ ████ .... ....Prototype2.... ████ ████ ....Prototype3██.. ██.. ██..……一直到Prototype32注意这些 Prototype 并不是猫、狗、人。而是网络自己学习出来的一组基础模板basis。你可以把它理解成32 张可以重复利用的“Mask 积木”。第四步那 Coefficient 是什么现在猫来了。网络说这只猫不要重新画。只需要Prototype1 × 0.8Prototype7 × 0.2Prototype18 × 0.5……最后组合。就得到猫Mask所以Coefficient其实就是32 个权重例如[0.8,0.1,0.0,...0.4]长度就是32。第五步为什么一定是32这里就是你的问题。答案其实是没有任何理论规定必须是32。完全是超参数Hyperparameter。论文作者实验发现例如Prototype数量效果8太少16一般32很好64提升很小128几乎没提升所以选择32这是经验值。不是数学公式。实际上如果你打开 YOLACT 源码。你会看到mask_dim32完全可以改mask_dim64网络一样可以训练。YOLO Seg也是一样。第六步为什么32就够了这是很多人第二个疑问。例如猫有胖猫 瘦猫 黑猫 白猫 ……为什么32 张 Prototype能够表示所有 Mask答案因为Prototype不是猫模板而是边缘 圆形 竖条 横条 局部纹理 ……这些基础形状。类似数学里面向量空间。例如二维空间任何向量(x,y)其实都可以表示成(1,0) (0,1)两个基底。Prototype其实就是Mask Space里面的Basis基。所以论文里面很多地方直接叫Prototype Basis Dictionary意思一样。第七步真正的公式是什么假设PrototypeProto.shape(32,160,160)表示32 张160×160Mask。例如第一张160×160第二张160×160……第32张160×160现在第一只猫预测Coeff(32,)例如[0.2,0.7,...0.5]真正计算就是MaskΣ Coeff[i]× Proto[i]也就是Mask0.2×Proto10.7×Proto2……0.5×Proto32最后得到160×160Mask。然后Upsample640×640结束。第八步为什么这种方法这么快假设有100 个目标。如果每一个预测640×640Mask。需要100 × 640 × 640如果用 Prototype。整个图片只预测32 × 160 × 160一次。然后每个目标只预测32个数。例如100 个目标只需要100 × 32这点计算量几乎可以忽略。所以速度非常快。这也是YOLACT 和 YOLO11-Seg 能做到实时实例分割的关键原因。最后我想纠正一个很多人都会产生的误解Prototype 不是 32 个类别。也不是第一张 Proto 猫第二张 Proto 狗第三张 Proto 人完全不是。它更像是神经网络自己学习出来的32 个“基础形状基底basis masks”。和线性代数非常像二维空间 e1 e2 ↓ 任何向量YOLO 分割里面32 个 Prototype ↓ 任何实例 Mask所以32 只是“基底数量”而不是类别数、目标数或者 Mask 数。我们就拿一个真实例子来算你立刻就能理解为什么要用Prototype Coefficient。假设有这样一张图片输入640 × 640里面有猫 × 50 狗 × 50 共100个目标我们来比较两种方案。第一种每个目标直接预测一张 Mask最笨的方法假设每个目标输出640 × 640Mask。那么一个目标需要预测640 × 640 409600个数。100个目标需要409600 × 100 40960000也就是4096 万个输出值。注意这里只是输出。还没开始SigmoidNMSCropLoss所以输出层已经爆炸了。再算一下内存假设float32一个数4 Byte那么40960000需要40960000 × 4 163840000 Byte约156 MB仅仅Mask。还没算Feature。还没算梯度。所以基本不可接受。第二种YOLO SegPrototypeYOLO先预测Proto(32,160,160)总共有32 × 160 × 160 819200个数。然后100个目标。每一个预测32Coefficient。所以100个目标100 × 32 3200最后总输出819200 3200 822400约82 万个数。对比一下方法输出数量每目标预测640×640 Mask40,960,000YOLO Prototype822,400比值40960000 / 822400 ≈ 49.8也就是说减少约 50 倍。注意这里只是假设100个目标。目标越多优势越明显。为什么还要用160×160继续算。如果Prototype也是640×640那么Proto32 × 640 × 640 131072001310万个数。反而又变大了。所以作者继续降低分辨率。变成160×160于是只有819200个数。这就是为什么Prototype不是640。而是160。那会不会精度下降这是大家都会想到的问题。答案会。所以YOLO最后还要Upsample恢复640。同时利用Box进行Crop。例如猫实际上Box100×120那么Mask最终只保留这个区域。所以即使Prototype比较粗。最后Mask还是比较准确。那 Prototype × Coefficient 的计算量大吗再算。Proto32 × 160 × 160每一个Pixel都有32个值。例如某一个Pixel[ 0.1, 0.3, 0.8, ... ]第一只猫Coeff[ 0.2, 0.4, 0.1, ... ]计算其实就是一个长度为32的点积Dot Product32 次乘法 31 次加法得到Mask(100,200)整个160×160共有160 × 160 25600Pixel。所以总计算25600 × 32 819200次乘法。对于GPU来说这几乎可以忽略。如果不用 Prototype 呢假设直接预测100个640×640Mask。实际上最后一层卷积需要100 × 640 × 640输出。GPU压力巨大。所以Prototype真正解决的是“每个目标都画一张完整 Mask”的问题。它改成了“整张图只画一次基础模板Prototype每个目标只告诉我怎么组合Coefficient”。既然只有 32 张 Prototype100 只猫是不是都在共用同样的 32 张它们怎么可能长得不一样这个问题实际上是Prototype 最核心、也是最容易误解的地方。答案是是的它们共用同一组 Prototype但每个目标使用的是不同的 Coefficient而且后面还会根据自己的检测框进行裁剪Crop。