FCOS:簡潔的anchor-free目標(biāo)檢測器

論文題目:FCOS: Fully Convolutional One-Stage Object Detection

其亮點(diǎn):

  • 基于FCN構(gòu)建全卷積檢測器,使得視覺任務(wù)(如語義分割)可以統(tǒng)一在FCN框架
  • anchor-free,proposal free,避免了訓(xùn)練階段關(guān)于anchor或者proposal的iou計(jì)算.更重要的是,避免了一切與anchor有關(guān)的超參數(shù)
  • 簡單的Backbone;neck;head檢測算法框架

原始anchor-base的缺點(diǎn):

  • 檢測器對anchor的大小工三、縱橫比和二、數(shù)量比較敏感;在RetinaNet,更改這些超參數(shù)會影響性能高達(dá)4% ap(coco基準(zhǔn)).因此在使用基于anchor的檢測器時(shí)要仔細(xì)調(diào)關(guān)于anchor的超參數(shù)
  • 由于anchor的比例和縱橫比在初始時(shí)保持固定,檢測器在處理形狀變化較大的候選對象時(shí)能力不夠
  • 為了實(shí)現(xiàn)較高的召回率,anchor-base的檢測器將anchor密集地放置在圖像特征中,導(dǎo)致訓(xùn)練過程加大了正負(fù)樣本的不平衡,當(dāng)然也顯著增加訓(xùn)練過程的計(jì)算量(一般通過計(jì)算與GT之間的IOU來計(jì)算loss,anchor生成的proposal越多,計(jì)算量就越大)

本文方法:

逐像素回歸預(yù)測

信息表示:

對于訓(xùn)練目標(biāo)ground-truth bounding boxes,我們用其四元向量表示:B_{i}=\left(x_{0}^{(i)}, y_{0}^{(i)}, x_{1}^{(i)} y_{1}^{(i)}, c^{(i)}\right)

其中(x_{0}^{i},y_{0}^{i})代表了邊框的左上角點(diǎn)坐標(biāo),(x_{1}^{i},y_{1}^{i})代表了邊框的右上角坐標(biāo),c_{i}代表了其目標(biāo)框的類別.

backbone CNN網(wǎng)絡(luò)提取的特征圖F_{i} \in \mathbb{R}^{H \times W \times C}屬于第i層,其中縮放的步幅(stride)為s.
對于特征圖中的每一對坐標(biāo)點(diǎn)(x,y),我們可以與原始圖像建立一一對應(yīng)關(guān)系\left(\left\lfloor\frac{s}{2}\right\rfloor+ x s,\left\lfloor\frac{s}{2}\right\rfloor+ y s\right);

不同于anchor-base的檢測器,fcos對每一個(gè)特征圖上的坐標(biāo)(x,y)都作為訓(xùn)練樣本進(jìn)行回歸(也就是像素級別回歸).

如同上面的對應(yīng)關(guān)系,如果(x,y)落在任何一個(gè)ground-truth bounding box中,那么它是一個(gè)正訓(xùn)練樣本,其標(biāo)簽是ground-truth的標(biāo)簽c^{*},如果不落在box中,則該樣本則為負(fù)樣本,c=0.

除此之外,fcos還對每一個(gè)像素進(jìn)行回歸預(yù)測一個(gè)四元組向量t^{*}=(l^{*}, t^{*}, r^{*}, b^{*}),分別代表了其四個(gè)邊框到中心點(diǎn)的距離;

某個(gè)點(diǎn)落入邊框B_{i}內(nèi),則回歸的目標(biāo)為:
\begin{array}{l} l^{*}=x-x_{0}^{(i)}, \quad t^{*}=y-y_{0}^{(i)} \\ r^{*}=x_{1}^{(i)}-x, \quad b^{*}=y_{1}^{(i)}-y \end{array}

image

作者認(rèn)為fcos優(yōu)于其他訓(xùn)練器的方式在于利用了盡可能多的前景樣本來訓(xùn)練回歸器,而不是通過計(jì)算iou來獲取正樣本.

網(wǎng)絡(luò)輸出

image

根據(jù)逐像素級別的回歸目標(biāo),我們得到其訓(xùn)練目標(biāo)為C-d向量代表了其分類結(jié)果,4-d向量t=(l,t,r,b)代表了其邊框坐標(biāo)回歸信息.

fcos采取訓(xùn)練C個(gè)二分類器的方式進(jìn)行類別判斷,而不是直接訓(xùn)練一個(gè)多分類判別器.

Moreover, since the regression targets are always positive, we employ exp(x) to map any real number to (0,∞) on the top of the regression branch

在回歸分支中像yolo一樣采取exp函數(shù)對坐標(biāo)進(jìn)行比例map.

loss 函數(shù)

整體loss分為分類loss與邊框信息loss:

\begin{aligned} L\left(\left\{\boldsymbol{p}_{x, y}\right\},\left\{\boldsymbol{t}_{x, y}\right\}\right) &=\frac{1}{N_{\text {pos }}} \sum_{x, y} L_{\text {cls }}\left(\boldsymbol{p}_{x, y}, c_{x, y}^{*}\right) \\ &+\frac{\lambda}{N} \sum \mathbb{1}_{\left\{c_{x, y}^{*}>0\right\}} L_{\text {reg }}\left(\boldsymbol{t}_{x, y}, \boldsymbol{t}_{x, y}^{*}\right) \end{aligned}

其中L_{cls}采取focal loss;

L_{reg}則是計(jì)算預(yù)測框與真實(shí)框的 iou loss.

N_{pos}為正樣本的數(shù)量.

\lambda為平衡系數(shù),用來平衡分類損失與坐標(biāo)損失.

多級預(yù)測提高召回率

利用FPN多級特征圖尺寸來提高召回率;

image

如結(jié)構(gòu)圖所示,作者使用了FPN的5個(gè)層次的特征圖為別為{P_{3},P_{4},P_{5},P_{6},P_{7}},其stride分別為是:8,16,32,64和128.

那么對于回歸目標(biāo)(l_{*},t^{*},r^{*},b^{*}),其處于不同的特征圖,最大最小值也有了一定的限制;

如果\max \left(l^{*}, t^{*}, r^{*}, b^{*}\right)>m_{i},或者\max \left(l^{*}, t^{*}, r^{*}, b^{*}\right)<m_{i-1},則其是一個(gè)負(fù)樣本,不參與訓(xùn)練計(jì)算;

m_{2},m_{3},m_{4},m_{5},m_{6},m_{7}的取值分別為0,64,128,256,512;

center-ness 分支

作者發(fā)現(xiàn),通過原始的辦法,由于在遠(yuǎn)離中心點(diǎn)的位置產(chǎn)生了許多低質(zhì)量的預(yù)測邊框,導(dǎo)致檢測器性能不高,所以作者提出了一個(gè)新的分支,center-ness,用來抑制這些低質(zhì)量邊框;

對于同一個(gè)坐標(biāo)點(diǎn)的回歸坐標(biāo)信息(l^{*},t^{*},b^{*},r^{*}),通過以下公式計(jì)算其中心度:
\text { centerness }^{*}=\sqrt{\frac{\min \left(l^{*}, r^{*}\right)}{\max \left(l^{*}, r^{*}\right)} \times \frac{\min \left(t^{*}, b^{*}\right)}{\max \left(t^{*}, b^{*}\right)}}

遠(yuǎn)離中心點(diǎn)的邊框,其max(l^{*},r^{* })會很大,導(dǎo)致center-ness分?jǐn)?shù)很低.這樣就可以有效地抑制這些低質(zhì)量邊框.

center-ness 乘以分類分?jǐn)?shù)來得到最終score(用來計(jì)算預(yù)測框的排名).最后,這些低質(zhì)量的邊框可能通過最終的NMS過程來濾除掉.

代碼閱讀

本代碼采取mmdetion 框架中的fcos代碼部分;

其backbone跟fpn均采取普通設(shè)置(resnet+fpn多尺度預(yù)測),來窺探下fcos_heads.py

#/mmdet/models/anchor_heads/fcos_heads.py
@HEADS.register_module
class FCOSHead(nn.Module):
    """
    Fully Convolutional One-Stage Object Detection head from [1]_.

    The FCOS head does not use anchor boxes. Instead bounding boxes are
    predicted at each pixel and a centerness measure is used to supress
    low-quality predictions.

    References:
        .. [1] https://arxiv.org/abs/1904.01355

    Example:
        >>> self = FCOSHead(11, 7)
        >>> feats = [torch.rand(1, 7, s, s) for s in [4, 8, 16, 32, 64]]
        >>> cls_score, bbox_pred, centerness = self.forward(feats)
        >>> assert len(cls_score) == len(self.scales)
    """

    def __init__(self,
                 num_classes,
                 in_channels,
                 feat_channels=256,
                 stacked_convs=4,
                 #定義了其采樣步長,回歸邊框
                 strides=(4, 8, 16, 32, 64),
                 regress_ranges=((-1, 64), (64, 128), (128, 256), (256, 512),
                                 (512, INF)),
                #三種loss分別為focalloss,iouloss 跟CrossEntropyLoss計(jì)算
                 loss_cls=dict(
                     type='FocalLoss',
                     use_sigmoid=True,
                     gamma=2.0,
                     alpha=0.25,
                     loss_weight=1.0),
                 loss_bbox=dict(type='IoULoss', loss_weight=1.0),
                 loss_centerness=dict(
                     type='CrossEntropyLoss',
                     use_sigmoid=True,
                     loss_weight=1.0),
                 conv_cfg=None,
                 norm_cfg=dict(type='GN', num_groups=32, requires_grad=True)):
        super(FCOSHead, self).__init__()

        self.num_classes = num_classes
        self.cls_out_channels = num_classes - 1
        self.in_channels = in_channels
        self.feat_channels = feat_channels
        self.stacked_convs = stacked_convs
        self.strides = strides
        self.regress_ranges = regress_ranges
        self.loss_cls = build_loss(loss_cls)
        self.loss_bbox = build_loss(loss_bbox)
        self.loss_centerness = build_loss(loss_centerness)
        self.conv_cfg = conv_cfg
        self.norm_cfg = norm_cfg
        self.fp16_enabled = False

        self._init_layers()
    def _init_layers(self):
        #每一個(gè)head都有兩個(gè)分支:分類分支和回歸坐標(biāo)分支,經(jīng)過四次采樣.
        self.cls_convs = nn.ModuleList()
        self.reg_convs = nn.ModuleList()
        for i in range(self.stacked_convs):
            chn = self.in_channels if i == 0 else self.feat_channels
            self.cls_convs.append(
                ConvModule(
                    chn,
                    self.feat_channels,
                    3,
                    stride=1,
                    padding=1,
                    conv_cfg=self.conv_cfg,
                    norm_cfg=self.norm_cfg,
                    bias=self.norm_cfg is None))
            self.reg_convs.append(
                ConvModule(
                    chn,
                    self.feat_channels,
                    3,
                    stride=1,
                    padding=1,
                    conv_cfg=self.conv_cfg,
                    norm_cfg=self.norm_cfg,
                    bias=self.norm_cfg is None))
        self.fcos_cls = nn.Conv2d(
            self.feat_channels, self.cls_out_channels, 3, padding=1)
        self.fcos_reg = nn.Conv2d(self.feat_channels, 4, 3, padding=1)
        self.fcos_centerness = nn.Conv2d(self.feat_channels, 1, 3, padding=1)

        self.scales = nn.ModuleList([Scale(1.0) for _ in self.strides])

        #前饋計(jì)算方式,分類分支,回歸分支,center-ness 分支分別計(jì)算結(jié)果
    def forward_single(self, x, scale):
        cls_feat = x
        reg_feat = x

        for cls_layer in self.cls_convs:
            cls_feat = cls_layer(cls_feat)
        cls_score = self.fcos_cls(cls_feat)
        centerness = self.fcos_centerness(cls_feat)

        for reg_layer in self.reg_convs:
            reg_feat = reg_layer(reg_feat)
        # scale the bbox_pred of different level
        # float to avoid overflow when enabling FP16
        bbox_pred = scale(self.fcos_reg(reg_feat)).float().exp()
        return cls_score, bbox_pred, centerness
        #center-ness 的定義
    def centerness_target(self, pos_bbox_targets):
        # only calculate pos centerness targets, otherwise there may be nan
        left_right = pos_bbox_targets[:, [0, 2]]
        top_bottom = pos_bbox_targets[:, [1, 3]]
        centerness_targets = (
            left_right.min(dim=-1)[0] / left_right.max(dim=-1)[0]) * (
                top_bottom.min(dim=-1)[0] / top_bottom.max(dim=-1)[0])
        return torch.sqrt(centerness_targets)

總結(jié)

  • FCN的方式進(jìn)行像素級別中心點(diǎn)回歸預(yù)測邊框方式
  • 提出center-ness分支,有效解決了離中心點(diǎn)較遠(yuǎn)的低質(zhì)量預(yù)測框問題
  • 建立統(tǒng)一的FCN視覺任務(wù)框架

reference

[1] Tian Z, Shen C, Chen H, et al. Fcos: Fully convolutional one-stage object detection[C]//Proceedings of the IEEE International Conference on Computer Vision. 2019: 9627-9636.

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末遇八,一起剝皮案震驚了整個(gè)濱河市碧绞,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,734評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異漓踢,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)漏隐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評論 3 394
  • 文/潘曉璐 我一進(jìn)店門喧半,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人青责,你說我怎么就攤上這事挺据。” “怎么了脖隶?”我有些...
    開封第一講書人閱讀 164,133評論 0 354
  • 文/不壞的土叔 我叫張陵扁耐,是天一觀的道長。 經(jīng)常有香客問我产阱,道長婉称,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,532評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮王暗,結(jié)果婚禮上悔据,老公的妹妹穿的比我還像新娘。我一直安慰自己俗壹,他們只是感情好科汗,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,585評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著绷雏,像睡著了一般头滔。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上涎显,一...
    開封第一講書人閱讀 51,462評論 1 302
  • 那天坤检,我揣著相機(jī)與錄音,去河邊找鬼棺禾。 笑死缀蹄,一個(gè)胖子當(dāng)著我的面吹牛峭跳,可吹牛的內(nèi)容都是我干的膘婶。 我是一名探鬼主播,決...
    沈念sama閱讀 40,262評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼蛀醉,長吁一口氣:“原來是場噩夢啊……” “哼悬襟!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起拯刁,我...
    開封第一講書人閱讀 39,153評論 0 276
  • 序言:老撾萬榮一對情侶失蹤脊岳,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后垛玻,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體割捅,經(jīng)...
    沈念sama閱讀 45,587評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,792評論 3 336
  • 正文 我和宋清朗相戀三年帚桩,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了亿驾。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,919評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡账嚎,死狀恐怖莫瞬,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情郭蕉,我是刑警寧澤疼邀,帶...
    沈念sama閱讀 35,635評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站召锈,受9級特大地震影響旁振,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,237評論 3 329
  • 文/蒙蒙 一规求、第九天 我趴在偏房一處隱蔽的房頂上張望筐付。 院中可真熱鬧,春花似錦阻肿、人聲如沸瓦戚。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽较解。三九已至,卻和暖如春赴邻,著一層夾襖步出監(jiān)牢的瞬間印衔,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評論 1 269
  • 我被黑心中介騙來泰國打工姥敛, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留奸焙,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,048評論 3 370
  • 正文 我出身青樓彤敛,卻偏偏與公主長得像与帆,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子墨榄,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,864評論 2 354

推薦閱讀更多精彩內(nèi)容