End-to-End Instance Segmentation with Recurrent Attention 讀書筆記

第一版

這是對End-to-End Instance Segmentation with Recurrent Attention這篇paper的初步理解,由于實習跟課程設計的關系,時間有點趕,可能有些理解不到位的地方,以后有時間會持續(xù)更新。
引用部分是筆者對論文重要內容的英文摘錄和翻譯(意譯),正文部分是筆者的理解及相關知識的補充說明。


這篇paper中作者提出了一個端對端的遞歸注意力網絡(Recurrent Attention)架構,類似于人腦計數機制的原理,用于解決目標分割的問題。這個模型能檢測出每個區(qū)域中最顯著的目標并實現分割
這里的翻譯可能不太妥當,“human-like counting process”(直譯類人腦計數機制),但遞歸注意力網絡在很大程度上可以說比其他網絡架構更符合人腦工作原理。

  1. CNN的機制在于逐行逐像素掃描,符合我們的觀察習慣;
  2. 而RNN則認為人腦解決問題是有時間序列的,前面的記憶對后面的判斷有重要作用,更貼近人腦思維過程
  3. 筆者認為遞歸注意力網絡可以說是RNN的加強版(內部主要組成也是RNN),提到了一個重要的概念:注意力轉移,跟LSTM有同樣邏輯,保留有用的信息,去除冗余的垃圾,但還有一個優(yōu)勢在于可以通過轉移注意力使得特征對當前某一區(qū)域更為敏感,抽取更魯棒的特征,更符合人腦認知習慣

文章中還提到解決圖片分割最基本的問題就是要先計算有多少類目標(classes),更進一步需要計算每類目標的數量(numbers)。這其實就是語義分割(semantic segmentation)跟實例分割(instance segmentation)的區(qū)別:

Instance VS Semantic

(c)圖描述的是“語義分割”,下面5只綿羊被歸為一類“綿羊”
(d)圖描述的是“實例分割”,5只綿羊分別用5種顏色加以標記
標準的基于像素級別的圖像語義分割回答了“每個像素屬于什么類別”的問題,但無法回答“照片中每一類目標的數量是多少”。顯然,實例級別的圖像分割要比語義級別的分割更難,前者需要區(qū)分鄰近(nearby)和被遮擋(occluded)的實例(instances)*
對于實例分割的作用有個很直觀的理解,在自動駕駛這一領域,系統(tǒng)需要解決的是當前駕駛方向都有些什么物體(人、車、交通標志……),每一類都有多少數量,都處于什么位置,這樣子才能做出正確的駕駛判斷,而不是單純知道前方有“人”就可以了。

解決“實例分割”問題,一個看起來可行的方法是看作是一個結構化輸出問題。
既然在“語義分割”中已經可以解決“每個像素點屬于什么類別”這一問題,按照同樣的處理方式,可以建立關于每個像素點的模型,輸出其跟周邊所有像素點的關系(刻畫當前像素與周邊像素是否屬于同一個物體),但這樣的處理方式最大的問題在于這個結構化輸出的維度,達到了“全部像素點數X全部目標的全部個數”。

這里需要再解釋一下Fully Convolutional Networks (全卷積神經網絡,FCN),其是實現“語義分割”的經典模型。


Fully Convolutional Networks.png

與經典的CNN在卷積層之后使用全連接層得到固定長度的特征向量進行分類不同,FCN可以接受任意尺寸的輸入圖像,采用反卷積層對最后一個卷積層的feature map進行上采樣, 使它恢復到輸入圖像相同的尺寸,從而可以對每個像素都產生了一個預測, 同時也保留了原始輸入圖像中的空間信息, 最后在上采樣的特征圖上進行逐像素分類。逐像素計算softmax分類的損失, 相當于每一個像素對應一個訓練樣本。

想要實現“實例分割”,最大的兩個問題是:

  1. One of the main challenges in instance segmentation, as in many other computer vision tasks such as object detection,is occlusion. Many approaches to handle occlusion utilize a form of non-maximal suppression (NMS), which is typically difficult to tune. In cluttered scenes, NMS may suppress the detection results for a heavily occluded object because it has too much overlap with foreground objects.One motivation of this work is to introduce an iterative procedure to perform dynamic NMS, reasoning about occlusion in a top-down manner.
    在一般的機器視覺任務中,采用“非極大抑制算法”能取得一個不錯的結果,但其非常難調整。如果采用動態(tài)非極大抑制(dynamic NMS),可以自上而下地解決遮擋問題。
  1. A related problem of interest entails counting the instances of an object class in an image.
    圖像計數問題。

*Our system addresses the dimensionality issue by using a temporal chain that outputs a single instance at a time. It also performs dynamic NMS, using an object that is already segmented to aid in the discovery of an occluded object later in the sequence.
使用時間序列解決維度問題,每次輸出一個實例。它還執(zhí)行動態(tài)的非極大抑制算法,使用一個已經被分割的對象來幫助在序列中發(fā)現一個可能被遮擋的對象。
*Using an RNN to segment one instance at a time is also inspired by human-like iterative and attentive counting processes. For real-world cluttered scenes, iterative counting with attention will likely perform better than a regression model that operates on the global image level. *
同時使用RNN對一個實例進行分段,也受到類似于人類的迭代和計數過程的啟發(fā)。對于真實世界的混亂場景,與注意力的迭代計數可能比在全局圖像級別上運行的回歸模型更好。


Recurrent attention model

原始圖片X0(h,w,3)經過預處理(Input pre-processing)成為X(h,w,9),示意圖如下:

數據預處理

然后將X輸入到模型中,訓練模型分為四大部分:

Our proposed model has four major components:
A) an external memory that tracks the state of the segmented objects;
B) a box proposal network responsible for localizing objects of interest;
C) a segmentation network for segmenting image pixels within the box;
D) a scoring network that determines if an object instance has been found, and also decides when to stop.

整體模型結構圖
Part A: External memory

外部存儲器,記錄了物體的邊界信息以及根據已經分割出來的目標決定下一目標的位置,從上方的模型結構圖中也能看出C)Seg. net部分跟A) Ext. Mem部分是有回路

A) 計算公式

  1. 下標(t)表示當前的timespan,對于CVPPP數據集,timespan固定值15,表示一張圖片中最多不超過15片葉子,所以整個模型最多循環(huán)15次。當然,模型在Part D) Score net部分還設置了“終止”機制(Termination condition),當得分低于閾值0.5時也會被強制終止,這部分的詳細內容在稍后解釋。
  2. max函數
    采用tf.maximum函數,將當前的ct(canvas)跟Part C) Seg. net部分的預測值yt取較大值
    這樣的處理主要是基于這樣的假設,“提供已分割的目標的信息給模型有助于推理出遮擋的目標以及下一個感興趣的區(qū)域”
    從直觀上也很容易理解,如果ct的初始值為0,如果每次迭代都從0開始,經過同樣的層、同樣的操作,但進行Part C) 部分的預測時卻告訴模型不一樣的GT(真實標簽,用來判斷是否分割正確),這會使得模型長期處于“不學習狀態(tài)”。
Paste_Image.png
Part B: Box network
Box network

這一部分主要包括ConvLSTM操作以及patch的提取,Conv作為特征提取器,將提取的特征輸入到LSTM中進行邊框(坐標)預測,選取感興趣區(qū)域。但作者認為直接對整張圖片進行多層卷積激活太復雜且低效,而且單純的pooling是無法保存空間結構信息的。
為了解決這個問題,作者引入了一個概念,“soft-atttention(dynamic pooling)”,柔性注意力(動態(tài)池化),通過權重att(可以理解為注意力轉移)來提取有用的空間結構信息。
Conv的輸出按L(feature dimension)的維度方向乘以對應的權重a輸入LSTM中,具體計算公式如下:

Paste_Image.png

  1. 其中第一個下標(t)是當前迭代的timespan(默認最大值15),第二個下標t表示的當前LSTM的第t次glimpse(默認最大值5),這兩個都應該是屬于超參數,是人為設定的
  2. a在各個位置上的初始權值都是一樣的,隨著迭代循環(huán)次數(glimpses)的增加,會有某一些位置的權值趨近于1,一些趨近于0,從而完成“位置相對重要程度”的劃分,再通過這樣的a與CNN的輸出值進行點乘操作,使得位置重要的響應值得以保留,噪聲得以消除
  3. 取LSTM最終輸出的隱含層Zt,end,通過一個線性層處理得到預測的邊框的中心坐標和尺寸,具體計算公式如下:
    坐標計算公式

    Zt,end是LSTM隱含層狀態(tài),shape為(1,49),進行矩陣運算過后,得到的是一個shape為(1,9)的輸出,輸出的每一維都有特地含義,例如前兩維分別儲存中心點(x,y)的坐標,第7維存放換算系數(scaling factor)……
    Q:這些特地維度存放什么特征是怎么決定的?有什么高深的數學原理?
    A沒有,亂來的
    當然,這只是筆者的理解,如果有不對的地方希望能被指出。
    筆者數學基礎不算好,但接觸到的機器學習模型貌似都有一個共同的特點,不管中間層是什么,提取了怎樣的特征,只需要精心設計一個權重矩陣,通過矩陣運算就能改變輸出的shape,然后強行回歸學習
    而一個好的模型意味著將這樣的權重矩陣訓練好,用于feature identify
  1. Extracting a sub-region 這一部分的工作主要根據DRAW: A Recurrent Neural Network For Image Generation
    來實現,下面是原理示意圖及計算公式:
    原理示意圖

利用高斯插值內核將原圖X0與Part A) 部分的輸出值dt串聯起來組成Xt
如上圖所示,(gx,gy)表示patch的中心點坐標;stride表示步長,也可以理解為“視野”,控制了這個patch的空間大小(zoom),當步長越大,這個attention patch所覆蓋的原圖區(qū)域就會更多,但分辨率也會更低。
而中心點和步長采用下面的公式也就決定了這個patch在(i,j)位置的位置平均值:

計算公式

其中Fx和Fy函數采用的是高斯函數,描述的是對應點在原圖像位置(a,b)對這個點在patch中(i,j)位置的貢獻值,即通過給定位置(a,b),能夠計算出這個點在patch中的位置(i,j),從而畫出邊框。
等等,這樣看起來貌似有作弊的嫌疑,一開始給定正確邊框某一點(a,b),拿來生成預測邊框中對應點(i,j),這跟我明明知道“1+1=2”,偏偏還要跟別人說“1+1+1-1=2”有什么區(qū)別?
所以真正的模型是有技巧的,需要單獨先預訓練Part B) box network,作者在代碼中有很好的說明,此時的(a,b)是真實邊框的某一點,計算loss用于回歸學習。
然后利用預訓練好的box net訓練整個模型時,這時的(a,b)其實不過是Part B) 部分前饋計算的值(并非真實值),進行聯合訓練

高斯函數
Part C: Segmentation network
Segmentation network

這一部分主要是利用Part B)中輸出的Pt作為輸入,進行卷積操作提取特征,再進行反卷積(D-CNN)進行原尺寸圖像恢復,從而完成在原圖像上的物體分割。
反卷積(deconvolution)其實是一個糟糕的名字,正確的叫法應該是“轉置卷積(transposed convolution)
反卷積的數學含義(可逆運算),通過反卷積可以將通過卷積的輸出信號,完全還原輸入信號。
但在一般的深度卷積神經網絡模型中,轉置卷積只能還原shape大小(轉置運算),目的并不是完全還原value,可以理解為一種溫和的upsampling。下面是conv與d-conv的示意圖:

卷積

反卷積

在此我們簡單對比一下FCN圖像分割與GAN圖像生成中deconvolution的不同

  1. FCN中需要deconvolution恢復原圖像尺寸,從而完成在原圖像上的語義分割/實例分割
  2. GAN圖像生成中,需要利用deconvolution近似恢復原來的value,減低loss,這里面有deconvolution network的learning,inference,optimize等過程(注:deconvolution network 跟 deconvolution 是完全不同的兩個東西)

Q:為什么FCN不關心deconvolution 生成的values?這樣的values直接輸出肯定無法成為一個有正常顏色的物體。
A:FCN用于物體分割,只需要給出分割的邊界,并不需要考慮重構圖片這個問題。響應值(value)的大小并不決定這個像素點的顏色值,而是決定了其是否屬于目標的一部分(利用激活函數很容易就能實現這一點),描述的是位置分布

Part D: Scoring network

這一部分主要是一個sigmod函數的線性激活層,利用Part B) 和 Part C) 部分的輸出值,計算圖片中有多少個目標,并且給出模型的終止條件(Termination condition):一旦輸出分數<0.5,則終止

Scoring network

閾值0.5這個指標怎么確定的?跟loss function有關?
曾一度認為Part D)這一模塊的設計是多余的。
后面loss function部分會對Part D)做進一步的描述


Loss functions

loss function

總的loss函數包括三部分:

  1. 匹配損失Ly(matching IoU loss)
  2. 邊框損失Lb(box IoU loss)
  3. 得分損失Ls<(score cross-entropy loss)

前面兩部分其實都是IoU指標值的計算,為方便理解下面的計算公式,這里先補充一下IoU的相關知識:
在目標檢測的評價體系中,有一個參數叫做 IoU ,簡單來講就是模型產生的目標窗口和原來標記窗口的交疊率。具體我們可以簡單的理解為: 即檢測結果(DetectionResult)與 Ground Truth 的交集比上它們的并集,即為檢測的準確率 IoU

IoU
Matching IoU loss (mIOU)
Paste_Image.png
  ############################
  # Box loss
  ############################
  if fixed_order:
    # [B, T] for fixed order.
    iou_soft_box = modellib.f_iou(attn_box, attn_box_gt, pairwise=False)
  else:
    if use_knob:
      # [B, T, T] for matching.
      iou_soft_box = tf.concat(
          1, [tf.expand_dims(iou_soft_box[tt], 1) for tt in range(timespan)])
    else:
      iou_soft_box = modellib.f_iou(
          attn_box, attn_box_gt, timespan, pairwise=True)
    # iou_soft_box = modellib.f_iou_pair_new(attn_box, attn_box_gt)

  identity_match = modellib.get_identity_match(num_ex, timespan, s_gt)
  if fixed_order:
    match_box = identity_match
  else:
    match_box = modellib.f_segm_match(iou_soft_box, s_gt)

  model['match_box'] = match_box
  match_sum_box = tf.reduce_sum(match_box, reduction_indices=[2])
  match_count_box = tf.reduce_sum(match_sum_box, reduction_indices=[1])
  match_count_box = tf.maximum(1.0, match_count_box)

  # [B] if fixed order, [B, T] if matching.
  if fixed_order:
    iou_soft_box_mask = iou_soft_box
  else:
    iou_soft_box_mask = tf.reduce_sum(iou_soft_box * match_box, [1])
  iou_soft_box = tf.reduce_sum(iou_soft_box_mask, [1])
  iou_soft_box = tf.reduce_sum(iou_soft_box / match_count_box) / num_ex_f

  if box_loss_fn == 'mse':
    box_loss = modellib.f_match_loss(
        attn_params,
        attn_params_gt,
        match_box,
        timespan,
        modellib.f_squared_err,
        model=model)
  elif box_loss_fn == 'huber':
    box_loss = modellib.f_match_loss(attn_params, attn_params_gt, match_box,
                                     timespan, modellib.f_huber)
  elif box_loss_fn == 'iou':
    box_loss = -iou_soft_box
  elif box_loss_fn == 'wt_cov':
    box_loss = -modellib.f_weighted_coverage(iou_soft_box, attn_box_gt)
  elif box_loss_fn == 'bce':
    box_loss_fn = modellib.f_match_loss(y_out, y_gt, match_box, timespan, f_bce)
  else:
    raise Exception('Unknown box_loss_fn: {}'.format(box_loss_fn))
  model['box_loss'] = box_loss

  box_loss_coeff = tf.constant(1.0)
  model['box_loss_coeff'] = box_loss_coeff
  tf.add_to_collection('losses', box_loss_coeff * box_loss)
Soft box IoU loss
Paste_Image.png
  ##############################
  # Segmentation loss
  ##############################
  # IoU (soft)
  iou_soft_pairwise = modellib.f_iou(y_out, y_gt, timespan, pairwise=True)
  real_match = modellib.f_segm_match(iou_soft_pairwise, s_gt)
  if fixed_order:
    iou_soft = modellib.f_iou(y_out, y_gt, pairwise=False)
    match = identity_match
  else:
    iou_soft = iou_soft_pairwise
    match = real_match
  model['match'] = match
  match_sum = tf.reduce_sum(match, reduction_indices=[2])
  match_count = tf.reduce_sum(match_sum, reduction_indices=[1])
  match_count = tf.maximum(1.0, match_count)

  # Weighted coverage (soft)
  wt_cov_soft = modellib.f_weighted_coverage(iou_soft_pairwise, y_gt)
  model['wt_cov_soft'] = wt_cov_soft
  unwt_cov_soft = modellib.f_unweighted_coverage(iou_soft_pairwise, match_count)
  model['unwt_cov_soft'] = unwt_cov_soft

  # [B] if fixed order, [B, T] if matching.
  if fixed_order:
    iou_soft_mask = iou_soft
  else:
    iou_soft_mask = tf.reduce_sum(iou_soft * match, [1])
  iou_soft = tf.reduce_sum(iou_soft_mask, [1])
  iou_soft = tf.reduce_sum(iou_soft / match_count) / num_ex_f
  model['iou_soft'] = iou_soft

  if segm_loss_fn == 'iou':
    segm_loss = -iou_soft
  elif segm_loss_fn == 'wt_cov':
    segm_loss = -wt_cov_soft
  elif segm_loss_fn == 'bce':
    segm_loss = f_match_bce(y_out, y_gt, match, timespan)
  else:
    raise Exception('Unknown segm_loss_fn: {}'.format(segm_loss_fn))
  model['segm_loss'] = segm_loss
  segm_loss_coeff = tf.constant(1.0)
  tf.add_to_collection('losses', segm_loss_coeff * segm_loss)
Monotonic score loss
Paste_Image.png
  ####################
  # Score loss
  ####################
  conf_loss = modellib.f_conf_loss(s_out, match, timespan, use_cum_min=True)
  model['conf_loss'] = conf_loss
  tf.add_to_collection('losses', loss_mix_ratio * conf_loss)

以上是筆者對這篇paper的一些粗略理解,有些地方也還不是很理解,后續(xù)會繼續(xù)補充。有任何不對的地方,歡迎在文章下方留言指出,也可直接電郵1643206826@qq.com,歡迎討論改正,謝謝。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容