《CondInst:Conditional Convolutions for Instance Segmentation》论文笔记

《CondInst》论文介绍了单阶段实例分割算法,通过条件卷积为每个实例生成独立的预测参数,解决了传统方法如Mask RCNN的问题。方法基于FCOS,增加了动态卷积控制器和Mask分支,实验表明在保持性能的同时提高了效率。
部署运行你感兴趣的模型镜像

参考代码:

  1. 非官方: CondInst,这篇文章使用的代码来源于此
  2. 官方:AdelaiDet

1. 概述

介绍:这篇文章描述的是单阶段的实例分割算法,类似与SOLO模型将不同的实例编码到channel和动态卷积参数中一样,这篇文章借鉴的是动态卷积参数去独立表征每个实例mask。回顾之前的两阶段实例分割算法(以Mask RCNN)为代表其存在如下的一些问题:1) 该方法通常会根据RoI box使用RoI Align的策略将特征图实现对齐与抠取,但是在一些情况下会引入较多背景区域的干扰,特别是对于存在旋转的目标(现在也有针对旋转目标的检测算法,但是其更加复杂);2) 为了更好区分前景和背景往往分割网络需要较大的感受野,因而在Mask RCNN算法中会堆叠多个kernel为 3 ∗ 3 3*3 33的卷积,这样带来的问题就是耗时会增加;3) 实例分割对目标的边界是比较敏感的,在Mask RCNN中统一会将其分辨率压缩到 28 ∗ 28 28*28 2828,这显然不利于细节信息的表达;而直接使用分割算法却无法感知不同目标的外观信息与定位信息,导致实例分割的效果不佳。对此文行引入Conditional卷积,为每一个满足训练要求的实例生成对应的预测参数,从而实现在分割框架下完成实例分割。

这里将文章的方法与之前的一些实例分割方法进行对比,见下图:
在这里插入图片描述

2. 方法设计

2.1 整体pipeline

文章方法的流程图见下图所示:
在这里插入图片描述
通看文章的方法,其是建立在FCOS算法之上的,主要的区别有两点:

  • 1)在原有分类、box回归、center-ness分支上增加了一个预测动态卷积参数的controller模块,其生成169个channel的特征;
  • 2)在FPN网络输出P3部分增加Mask-branch,生成用于实例mask预测的 F m a s k F_{mask} Fmask特征,该特征之后会concat上相对位置结合动态卷积参数生成每个实例的预测mask;

上述内容中 F m a s k F_{mask} Fmask生成过程中包含的layer_nums和channels对整体分割性能和速度的影响见下表:
在这里插入图片描述
最后生成 F m a s k F_{mask} Fmask的时候其channel数量对性能的影响见下表(channel数为8也能达到不错的效果):
在这里插入图片描述

2.2 实例预测过程

在上面提到动态参数channel维度是169维度的,其实这169维度里面是包含了3个动态卷积的参数的,其decode过程(也是实例分割预测的过程)可以描述为下面的这一段代码:

# fcos/modeling/fcos/fcos_outputs.py#L366
def forward_for_mask(self, boxlists):
    N, dim, h, w = self.masks.shape
    # 生成位置坐标
    grid_x = torch.arange(w).view(1, -1).float().repeat(h, 1).cuda() / (w - 1) * 2 - 1
    grid_y = torch.arange(h).view(-1, 1).float().repeat(1, w).cuda() / (h - 1) * 2 - 1
    x_map = grid_x.view(1, 1, h, w).repeat(N, 1, 1, 1)
    y_map = grid_y.view(1, 1, h, w).repeat(N, 1, 1, 1)
    # 将相对坐标位置与mask_feat组合
    masks_feat = torch.cat((self.masks, x_map, y_map), dim=1)  # [N, 10, h, w]
    o_h = int(h * self.strides[0])
    o_w = int(w * self.strides[0])
    for im in range(N):  # 遍历每张图片
        boxlist = boxlists[im]
        input_h, input_w = boxlist.image_size
        mask = masks_feat[None, im]
        ins_num = boxlist.controllers.shape[0]
        # [inst_num*8, 10, 1, 1]
        weights1 = boxlist.controllers[:, :80].reshape(-1, 8, 10).reshape(-1, 10).unsqueeze(-1).unsqueeze(-1)
        # [inst_num*8]
        bias1 = boxlist.controllers[:, 80:88].flatten()
        # [inst_num*8, 8, 1, 1]
        weights2 = boxlist.controllers[:, 88:152].reshape(-1, 8, 8).reshape(-1, 8).unsqueeze(-1).unsqueeze(-1)
        # [inst_num*8]
        bias2 = boxlist.controllers[:, 152:160].flatten()
        # [inst_num, 8, 1, 1]
        weights3 = boxlist.controllers[:, 160:168].unsqueeze(-1).unsqueeze(-1)
        # [inst_num]
        bias3 = boxlist.controllers[:, 168:169].flatten()

        conv1 = F.conv2d(mask, weights1, bias1).relu()  # 普通卷积
        conv2 = F.conv2d(conv1, weights2, bias2, groups=ins_num).relu()  # 分组卷积
        masks_per_image = F.conv2d(conv2, weights3, bias3, groups=ins_num)  # 分组卷积

参与预测实例分割的特征图stride对于最后分割性能的影响见下表:
在这里插入图片描述

2.3 损失函数

对于mask部分的损失,只会将满足置信度和NMS要求的实例参与到最后mask求取过程,则实例分割部分的损失函数描述为:
L m a s k ( { θ x , y } ) = 1 N p o s ∑ x , y 1 { c x , y ∗ > 0 } L d i c e ( M a s k H e a d ( F ˉ x , y ; θ x , y ) , M x , y ∗ ) L_{mask}(\{\theta_{x,y}\})=\frac{1}{N_{pos}}\sum_{x,y}\mathcal{1}_{\{c_{x,y}^*\gt0\}}L_{dice}(MaskHead(\bar{F}_{x,y};\theta_{x,y}),M_{x,y}^*) Lmask({θx,y})=Npos1x,y1{cx,y>0}Ldice(MaskHead(Fˉx,y;θx,y),Mx,y)
总的损失函数为:
L o v e r a l l = L f c o s + λ L m a s k L_{overall}=L_{fcos}+\lambda L_{mask} Loverall=Lfcos+λLmask

3. 实验结果

MS-COCO test-dev上的性能比较:
在这里插入图片描述

您可能感兴趣的与本文相关的镜像

TensorFlow-v2.9

TensorFlow-v2.9

TensorFlow

TensorFlow 是由Google Brain 团队开发的开源机器学习框架,广泛应用于深度学习研究和生产环境。 它提供了一个灵活的平台,用于构建和训练各种机器学习模型

<think>我们正在讨论C++中的`std::conditional`,特别是嵌套使用的情况。`std::conditional`是C++标准库中`<type_traits>`头文件提供的一个模板,用于在编译时根据条件选择类型。其基本形式如下: ```cpp template <bool Cond, class T, class F> struct conditional; ``` 如果`Cond`为`true`,则`conditional::type`为`T`;否则为`F`。 嵌套使用`std::conditional`意味着在一个`std::conditional`的条件结果中再使用另一个`std::conditional`,这样可以实现多重条件判断。 下面是一个简单的嵌套使用示例。假设我们想根据一个整数的大小选择不同的类型: - 如果整数小于0,则选择`float`类型; - 如果整数在0到100之间(包含0和100),则选择`int`类型; - 如果整数大于100,则选择`double`类型。 我们可以使用两个嵌套的`std::conditional`来实现: ```cpp #include <type_traits> #include <iostream> template <int N> using SelectedType = typename std::conditional< (N < 0), float, typename std::conditional< (N <= 100), int, double >::type >::type; int main() { // 测试小于0的情况 SelectedType<-10> var1 = 3.14f; std::cout << "Type for -10: " << typeid(var1).name() << std::endl; // 应该输出float类型 // 测试0到100之间的情况 SelectedType<50> var2 = 42; std::cout << "Type for 50: " << typeid(var2).name() << std::endl; // 应该输出int类型 // 测试大于100的情况 SelectedType<200> var3 = 2.71828; std::cout << "Type for 200: " << typeid(var3).name() << std::endl; // 应该输出double类型 return 0; } ``` 在这个例子中,我们定义了一个模板别名`SelectedType`,它根据整数`N`的值选择不同的类型。首先,外层的`std::conditional`检查`N < 0`,如果为真则选择`float`;如果为假,则进入内层的`std::conditional`,检查`N <= 100`,如果为真则选择`int`,否则选择`double`。 注意:嵌套的`std::conditional`需要写成`typename std::conditional<...>::type`,因为`std::conditional`是一个类型萃取工具,其结果是嵌套在内部的`type`成员。 另外,我们使用了模板别名(`using`)来简化类型选择,这样在代码中可以直接使用`SelectedType<N>`来获取选择的类型。 在实际编译时,由于条件是在编译期确定的,所以不会产生运行时开销。但是,嵌套的条件可能会使代码可读性降低,因此对于复杂的条件判断,可以考虑使用`constexpr if`(C++17)或者模板特化等其他方法。 另外,需要注意的是,嵌套的`std::conditional`在条件较多时可能会变得复杂,因此需要谨慎设计。 下面再举一个例子,使用三个条件进行选择(例如:根据一个整数是正数、负数还是零来选择类型): - 如果整数小于0,选择`int`(表示负数处理); - 如果整数等于0,选择`void*`(特殊标记); - 如果整数大于0,选择`double`(正数处理)。 这里我们可以使用两层嵌套,但注意条件需要覆盖所有情况: ```cpp template <int N> using SelectedType2 = typename std::conditional< (N < 0), int, typename std::conditional< (N == 0), void*, double >::type >::type; ``` 这个例子中,首先判断是否小于0,如果是则选择`int`;否则进入内层判断是否等于0,如果是则选择`void*`,否则选择`double`。 使用示例: ```cpp SelectedType2<-5> v1 = -10; // int SelectedType2<0> v2 = nullptr; // void* SelectedType2<10> v3 = 3.14; // double ``` 需要注意的是,嵌套的`std::conditional`要求每个分支都是有效的类型,即使该分支不会被选择。例如,在上面的例子中,即使`N`小于0,内层的`std::conditional`也会被实例化(但它的分支不会被实际使用),所以内层条件中的类型(如`void*`和`double`)必须是有效的。 此外,嵌套的层数增加可能会导致编译时间变长,因此需要权衡。 最后,在C++17中,我们可以使用`std::conditional_t`来避免写`::type`,使代码更简洁: ```cpp template <int N> using SelectedType = std::conditional_t< (N < 0), float, std::conditional_t< (N <= 100), int, double > >; ``` `std::conditional_t`是C++14引入的模板别名,等同于`typename conditional<...>::type`。 希望这些例子能够帮助你理解`std::conditional`的嵌套使用。 §§相关问题§§ 1. `std::conditional`和`std::conditional_t`有什么区别? 2. 在C++17中,如何用`if constexpr`替代`std::conditional`的嵌套? 3. 使用嵌套的`std::conditional`时,如何避免编译错误(例如分支中的无效类型)? 4. `std::conditional`在模板元编程中还有哪些常见应用?</think>### C++ 中 `std::conditional` 的嵌套用法示例 `std::conditional` 是 C++ 标准库 `<type_traits>` 中的编译期条件判断工具,其基本语法为: ```cpp std::conditional<Condition, TypeIfTrue, TypeIfFalse>::type ``` 当需要处理多重条件时,可通过嵌套实现复杂类型选择逻辑。以下是具体示例: --- #### 示例 1:多重条件类型选择 ```cpp #include <type_traits> #include <iostream> // 根据整数范围选择类型: // N < 0 → float // 0≤N≤100 → int // N > 100 → double template <int N> using SelectedType = typename std::conditional< (N < 0), float, // 条件1为真时的类型 typename std::conditional< (N <= 100), int, // 条件2为真时的类型 double // 所有条件为假时的类型 >::type >::type; int main() { SelectedType<-5> v1 = 3.14f; // float SelectedType<42> v2 = 10; // int SelectedType<200> v3 = 2.718; // double std::cout << typeid(v1).name() << "\n"; // 输出 float std::cout << typeid(v2).name() << "\n"; // 输出 int std::cout << typeid(v3).name() << "\n"; // 输出 double } ``` --- #### 示例 2:结合类型特性嵌套 ```cpp #include <type_traits> template <typename T> struct Wrapper { // 根据 T 的特性选择存储类型: // 整型 → int // 浮点型 → double // 其他 → void* using StorageType = typename std::conditional< std::is_integral<T>::value, int, typename std::conditional< std::is_floating_point<T>::value, double, void* >::type >::type; StorageType data; }; // 测试用例 static_assert(std::is_same<Wrapper<int>::StorageType, int>::value, ""); static_assert(std::is_same<Wrapper<float>::StorageType, double>::value, ""); static_assert(std::is_same<Wrapper<std::string>::StorageType, void*>::value, ""); ``` --- #### 关键注意事项: 1. **嵌套语法** 内层 `std::conditional` 需通过 `::type` 暴露结果类型: ```cpp typename std::conditional<Cond1, T1, typename std::conditional<Cond2, T2, T3>::type >::type ``` 2. **C++14 简化写法** 使用 `std::conditional_t` 省去 `::type`: ```cpp template <int N> using SelectedType = std::conditional_t<(N<0), float, std::conditional_t<(N<=100), int, double>>; ``` 3. **编译期求值** 所有条件必须在编译期确定,否则会引发编译错误[^1]。 4. **替代方案** 复杂场景可用 `std::enable_if` 或 C++17 的 `if constexpr` 替代嵌套[^2]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值