PointRend 在 Mask R-CNN、Panoptic FPN 等分割框架中常被用来提升目标边界的精细度,但一旦输入分辨率提高或实例数量增加,朴素 PointRend 实现很容易成为推理瓶颈。它的核心思想是对粗糙分割结果进行点级细化:在高不确定区域选取若干点,用小型多层感知机预测这些点的类别,再插值回高分辨率掩码。这个流程本身具有自适应特性,问题在于候选点规模失控、点级网络调用次数过多以及细化范围缺少约束。要降低计算量,需要从点采样策略和自适应渲染流程同时优化,而不是简单去掉点渲染模块。

以实际分割任务为背景,PointRend 的计算量可以被拆成候选点产生、点特征提取和点级分类三个阶段。每个阶段都有对应的压缩空间,尤其是候选点数量和细化迭代方式,往往决定了整体推理速度的上限。
一、PointRend 计算量大的根源:候选点规模与点级网络开销
PointRend 的推理过程可以概括为三步:首先通过分割骨干网络得到粗糙预测,然后在粗糙预测中选取不确定性最高的点,最后对每个选中点提取特征并送入一个轻量多层感知机进行分类。单看一个点,多层感知机的成本很低,但点数量级会极大改变整体耗时。
假设输入图像经过 FPN 后的特征图步长为 4,粗略上采样 4 倍到原始尺寸时,1024×1024 的图像会产生约 104 万个格点。即使只选择其中 10% 作为不确定点,也有十万次点级前向。若每个点需要拼接多个尺度的特征向量,比如 256 维,那么仅中间张量就会占用大量显存。更常见的问题是在训练阶段使用了均匀随机采样来覆盖不同区域,这些采样点并不全是边界点;推理时则按照置信度排序取 topK 个点,排序操作本身也会随特征图增大而增长。
另一个容易被忽略的来源是不同实例之间的重复计算。Mask R-CNN 中每个候选框都会独立执行 PointRend 细化,如果 RoI 数量达到数百个,点级多层感知机就会被调用数百次。即使每个 RoI 只取 784 个点,总点数依然可观。因此不能只关注单点复杂度,还必须控制采样范围和候选实例数量。理解了这些来源后,优化方向就很明确:减少无效候选点,降低点级网络调用频率,并用更粗糙但足够的信息提前过滤不必要区域。
二、点采样策略:用置信度、边界掩码与分层采样压缩候选点
降低计算量最直接的方法是在细化之前大幅减少候选点数量,同时保留真正影响边界的点。粗糙预测经过 softmax 后可以得到每个位置属于各类别的概率,用 1 减去最大概率就可以形成一个不确定性图。物体边缘的概率通常接近 0.5,最大概率不高,因此不确定性最强;而背景或物体内部的最大概率接近 1,不确定性较弱。设置一个阈值,例如只保留不确定性高于 0.2 的点,能够过滤掉大量内部和背景区域。
仅用阈值还不够稳定,因为一张图中边界点可能仍然很多,且容易集中在实例重叠区域。此时可以结合实例掩码或边缘检测结果进行限制:只在实例的前景边界邻域内采样,避免将背景远处同样具有不确定性的噪声点纳入点级网络。对于语义分割任务,可以使用预测掩码的二值化结果做距离变换,把采样点限制在边界两侧的窄带内。这样点采样从全局散点变为局部带状采样,候选规模通常能下降一个数量级。
此外,PointRend 原论文在训练时采用均匀采样和不确定性采样相结合的策略,目的不是单纯加速,而是保证训练稳定性。工程部署时可以进一步调整采样配比:推理阶段只保留不确定点,训练阶段保持混合采样。下述代码展示了基于不确定性分数选取候选点的简化实现。
import torch
import torch.nn.functional as F
def select_uncertain_points(coarse_logits, num_points=512, threshold=0.2):
"""
coarse_logits: shape = (N, C, H, W)
返回:points 下标与对应不确定性分数
"""
prob = F.softmax(coarse_logits, dim=1)
top_prob, _ = prob.max(dim=1)
uncertain = 1.0 - top_prob # 边界处较大,内部和背景较小
# 只保留高于阈值的点,避免对大图执行全量 topk
mask = uncertain > threshold
valid_scores = uncertain[mask]
if valid_scores.numel() == 0:
# 退化情况:直接返回全图最高不确定性的若干点
flat_scores = uncertain.flatten()
topk_scores, topk_idx = torch.topk(flat_scores, num_points)
return topk_idx, topk_scores
# 在有效点中按不确定性排序
sorted_scores, sorted_idx = torch.sort(valid_scores, descending=True)
keep = min(num_points, sorted_idx.numel())
keep_idx = sorted_idx[:keep]
return keep_idx, sorted_scores[:keep]
上面的实现首先用 softmax 得到概率,再用 1 - top_prob 构造不确定性图。通过阈值 threshold 过滤掉大量低不确定点后,只需在少量有效点上执行排序。对于分辨率很高的特征图,这样做可以显著降低排序和后续特征索引的时间。实际使用时,threshold 应根据验证集调整:过低则过滤效果有限,过高则可能丢掉真实边界。一般从 0.1 到 0.3 开始搜索比较稳妥。
分层采样是另一类有效手段。先将特征图划分为多个小块,在每块内按不确定性选点,而不是全局统一排序。这样能避免点集中在少数高不确定区域,保证边界覆盖度,同时便于在张量上并行实现。结合阈值和每块点数上限,候选点数量可以被严格控制在预算以内,后续点级网络的处理时间也会稳定可预测。
三、自适应渲染:从粗到细迭代优化,把算力集中到边界
PointRend 的名称来自渲染中的自适应采样思想,它并不需要对所有位置做同样精度的计算。粗预测已经给出了大致区域,细化阶段可以根据当前预测的动态不确定区域来安排计算。自适应渲染的关键是迭代上采样:不要一次性把低分辨率粗糙图放大 4 倍或 8 倍,而是每次只放大 2 倍,在每一级重新评估不确定点并刷新边界。
这样做有两个明显好处。第一,每级只需要处理当前放大后的新边界点,避免在最终高分辨率上一次性处理全部点;第二,中间级产生的细化结果可以作为下一级更准确的不确定图,使后续采样沿着物体边缘逐步收敛。对于 1024 尺寸的掩码,通常 2 到 3 次 2 倍上采样即可获得平滑边界,总计算量相比一步到位的全图细化要低得多。