YOLO V2 代碼分析


先介紹YOLO[轉]:

第一個顛覆ross的RCNN系列,提出region-free,把檢測任務直接轉換為回歸來做,第一次做到精度可以,且實時性很好。 
1. 直接將原圖划分為SxS個grid cell,如果有物體的中心落到這個格子里那么這個格子的gt就是這個物體。 
2. 每個格子被指定的gt需要對應B個bounding box(下面簡稱為bbox)去回歸,也就是說每個格子對應的B個bbox的gt是一樣的。 
3. 每個bbox預測5個值: x, y, w, h, 置信度。(x, y)是bbox的中心在對應格子里的相對位置,范圍[0,1]。(w, h)是bbox相對於全圖的的長寬,范圍[0,1]。x, y, w, h的4個gt值可以算出來。confidence = P(object)* iou, 它的gt值是這樣指定的: 若bbox對應格子包含物體,則P(object) = 1,否則P(object) = 0。它和ssd及rcnn系列在這里有個很不同的地方,它是直接回歸bbox的位置,而ssd及rcnn系列是回歸的是default box/anchor的偏移量,它沒有default box/anchor這個東西。 
4. 每個格子也會預測屬於各個類別的置信度,也就是每個格子對應的B個box是共享這個值的,這B個box只能屬於一類的,所以和第一步呼應它們的gt都是一樣的。 
5. inference階段這里寫圖片描述,class-specific confidence score既包含了bounding box最終屬於哪個類別的概率,又包含了bounding box位置的准確度。最后設置一個閾值與class-specific confidence score對比,過濾掉score低於閾值的boxes,然后對score高於閾值的boxes進行非極大值抑制(NMS, non-maximum suppression)后得到最終的檢測框體。

YOLO使用均方和誤差作為loss函數來優化模型參數,即網絡輸出的S*S*(B*5 + C)維向量與真實圖像的對應S*S*(B*5 + C)維向量的均方和誤差。如下式所示。其中,coordError、iouError和classError分別代表預測數據與標定數據之間的坐標誤差、IOU誤差和分類誤差。 
每個格子的 loss=coordError + iouError + classError 
YOLO對上式loss的計算進行了如下修正。

    1. 位置相關誤差(坐標、IOU)與分類誤差對網絡loss的貢獻值是不同的,因此YOLO在計算loss時,使用λcoord =5修正coordError。
    2. 在計算IOU誤差時,包含物體的格子與不包含物體的格子,二者的IOU誤差對網絡loss的貢獻值是不同的。若采用相同的權值,那么不包含物體的格子的confidence值近似為0,變相放大了包含物體的格子的confidence誤差在計算網絡參數梯度時的影響。為解決這個問題,YOLO 使用λnoobj =0.5修正iouError。(注此處的‘包含’是指存在一個物體,它的中心坐標落入到格子內)。
    3. 對於相等的誤差值,大物體誤差對檢測的影響應小於小物體誤差對檢測的影響。這是因為,相同的位置偏差占大物體的比例遠小於同等偏差占小物體的比例。YOLO將物體大小的信息項(w和h)進行求平方根來改進這個問題。(注:這個方法並不能完全解決這個問題)。

YOLO的loss

進一步理解YOLO

  1. 在YOLO網絡中,首先通過一組CNN提取feature maps
  2. 然后通過最后一個全連接FC層生成SxSx(5*B+C)=7x7x(5*2+20)=1470長的向量
  3. 再把1470向量reshape成SxSx(5*B+C)=7x7x30形狀的多維矩陣
  4. 通過解析多維矩陣獲得Detection bounding box + Confidence
  5. 最后對Detection bounding box + Confidence進行Non maximum suppression獲得輸出

在設置好網絡,並進行初始化后,通過forward就可以獲得我們需要的SxSx(5*B+C)矩陣,只不過其中數值並不是我們想要的。當經過上述YOLO Loss下的負反饋訓練后,顯然就可以獲得我們SxSx(5*B+C)矩陣,再經過解析+NMS就可以獲得輸出框了。

從本質上說,Faster RCNN通過對Anchors的判別和修正獲得檢測框;而YOLO通過強行回歸獲得檢測框。

 

 

3.3 passthrough操作

修改后的網絡最終在13 * 13的特征圖上進行預測,雖然這足以勝任大尺度物體的檢測,如果用上細粒度特征可能對小尺度的物體檢測有幫助。Faser R-CNN和SSD都在不同層次的特征圖上產生proposal以獲得多尺度的適應性。

YOLOv2使用了一種不同的方法,簡單添加一個 passthrough layer,把淺層特征圖(分辨率為26 * 26)連接到深層特征圖。passthrough layer把高低分辨率的特征圖concat,疊加相鄰特征到不同通道

這個方法把26 * 26 * 512的特征圖疊加成13 * 13 * 2048的特征圖,與原生的深層特征圖相連接(即:加深channel的conv1與conv3 concat后作為conv4的輸入)。

YOLOv2的檢測器使用的就是經過擴展后的的特征圖,它可以使用細粒度(淺層)特征,使得模型的性能獲得了1%的提升。

regorg layer分析:這里ReorgLayer層就是將2626512的張量中2626切割成4個1313,然后連接起來,使得原來的512通道變成了2048。

1 #darknet.py
2         self.reorg = ReorgLayer(stride=2)   # stride*stride times the channels of conv1s
 1 #reorg_layer.py
 2     def forward(self, x): 
 3         stride = self.stride
 4 
 5         bsize, c, h, w = x.size()
 6         out_w, out_h, out_c = int(w / stride), int(h / stride), c * (stride * stride)
 7         out = torch.FloatTensor(bsize, out_c, out_h, out_w)
 8 
 9         if x.is_cuda:
10             out = out.cuda()
11             reorg_layer.reorg_cuda(x, out_w, out_h, out_c, bsize, stride, 0, out)
12         else:
13             reorg_layer.reorg_cpu(x, out_w, out_h, out_c, bsize, stride, 0, out)
14 
15         return out 
 1 //reorg_cpu.c
 2 int reorg_cpu(THFloatTensor *x_tensor, int w, int h, int c, int batch, int stride, int forward, THFloatTensor *out_tensor)
 3 {
 4     // Grab the tensor
 5     float * x = THFloatTensor_data(x_tensor);
 6     float * out = THFloatTensor_data(out_tensor);
 7 
 8     // https://github.com/pjreddie/darknet/blob/master/src/blas.c
 9     int b,i,j,k;
10     int out_c = c/(stride*stride);
11 
12     for(b = 0; b < batch; ++b){
13         //batch_size
14         for(k = 0; k < c; ++k){
15            //channel
16             for(j = 0; j < h; ++j){
17                 //height
18                 for(i = 0; i < w; ++i){
19                     //width
20                     int in_index  = i + w*(j + h*(k + c*b));
21                     int c2 = k % out_c;
22                     int offset = k / out_c;
23                     int w2 = i*stride + offset % stride;
24                     int h2 = j*stride + offset / stride;
25                     int out_index = w2 + w*stride*(h2 + h*stride*(c2 + out_c*b));
26                     if(forward) out[out_index] = x[in_index]; // 壓縮channel
27                     else out[in_index] = x[out_index];        // 擴展channel
28                 }
29             }
30         }
31     }
32 
33     return 1;
34 }

圖片有錯誤,待改,輸入的1,3點分布在輸出的第1個feature map上,輸入的2,4點分布在輸出的第2個feature map上,idx2后面+w2

下圖從右到左為forward計算方向,從左到右為backward求導方向

3.4 目標函數計算

 1 #darknet.py
 2     def loss(self):
 3         #可以看出,損失值也是基於預測框bbox,預測的iou,分類三個不同的誤差和
 4         return self.bbox_loss + self.iou_loss + self.cls_loss
 5 
 6     def forward(self, im_data, gt_boxes=None, gt_classes=None, dontcare=None):
 7         conv1s = self.conv1s(im_data)
 8         conv2 = self.conv2(conv1s)
 9         conv3 = self.conv3(conv2)
10         conv1s_reorg = self.reorg(conv1s)
11         cat_1_3 = torch.cat([conv1s_reorg, conv3], 1)
12         conv4 = self.conv4(cat_1_3)
13         conv5 = self.conv5(conv4)   # batch_size, out_channels, h, w
14         ……
15         ……
16         # tx, ty, tw, th, to -> sig(tx), sig(ty), exp(tw), exp(th), sig(to)
17         '''預測tx ty'''
18         xy_pred = F.sigmoid(conv5_reshaped[:, :, :, 0:2])
19         '''預測tw th '''
20         wh_pred = torch.exp(conv5_reshaped[:, :, :, 2:4])
21         bbox_pred = torch.cat([xy_pred, wh_pred], 3)
22         '''預測置信度to '''
23         iou_pred = F.sigmoid(conv5_reshaped[:, :, :, 4:5])
24         '''預測分類class  '''
25         score_pred = conv5_reshaped[:, :, :, 5:].contiguous()
26         prob_pred = F.softmax(score_pred.view(-1, score_pred.size()[-1])).view_as(score_pred)
27 
28         # for training
29         if self.training:
30             bbox_pred_np = bbox_pred.data.cpu().numpy()
31             iou_pred_np = iou_pred.data.cpu().numpy()
32             _boxes, _ious, _classes, _box_mask, _iou_mask, _class_mask = self._build_target(
33                                          bbox_pred_np, gt_boxes, gt_classes, dontcare, iou_pred_np)
34             _boxes = net_utils.np_to_variable(_boxes)
35             _ious = net_utils.np_to_variable(_ious)
36             _classes = net_utils.np_to_variable(_classes)
37             box_mask = net_utils.np_to_variable(_box_mask, dtype=torch.FloatTensor)
38             iou_mask = net_utils.np_to_variable(_iou_mask, dtype=torch.FloatTensor)
39             class_mask = net_utils.np_to_variable(_class_mask, dtype=torch.FloatTensor)
40 
41             num_boxes = sum((len(boxes) for boxes in gt_boxes))
42 
43             # _boxes[:, :, :, 2:4] = torch.log(_boxes[:, :, :, 2:4])
44             box_mask = box_mask.expand_as(_boxes)
45             #計算預測的平均bbox損失值
46             self.bbox_loss = nn.MSELoss(size_average=False)(bbox_pred * box_mask, _boxes * box_mask) / num_boxes
47            #計算預測的平均iou損失值
48             self.iou_loss = nn.MSELoss(size_average=False)(iou_pred * iou_mask, _ious * iou_mask) / num_boxes
49            #計算預測的平均分類損失值
50             class_mask = class_mask.expand_as(prob_pred)
51             self.cls_loss = nn.MSELoss(size_average=False)(prob_pred * class_mask, _classes * class_mask) / num_boxes
52 
53         return bbox_pred, iou_pred, prob_pred

參考自:仙守

YOLO部分:

白裳

爆米花好美啊


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM