圖解 YoloV3

整個訓練分兩部分

  1. 對 DarkNet53 的預訓練
  2. 基於 DarkNet53 進行物體檢測訓練

原圖尺寸:input_shape

標籤:原始標籤 box 為 (class, xmin, ymin, xmax, ymax),xmin, ymin, xmax, ymax 是左上角為座標軸相對於原圖的偏移。

anchor 總共 9 個,三個為一組分給三組輸出,其中 anchor 的 0,1,2 分給 32×32 的輸出,3,4,5 分給 16×16 的輸出,6,7,8分給 8×8 的輸出。anchor 的生產通過 k-mean 演算法生成(參見附錄)。

yolov3 base

Darknet53 訓練

yolov3 classification

預處理

random crops, rotation, hue, saturation, exposure shift 等。最常用的是 Alex 的預處理方式,四個角剪下 中心,翻轉,再四個角 中心

訓練

用 ImageNet 資料集經過 DarkNet53 avgpool softmax 之後,得到 1000 分類。

訓練過程:

  1. 在 224x224x3 以 0.1 的學習速率,weight decay 0.0005 momentum 0.9 訓練 160 epoch;
  2. 448x448x3 上以 learning rate 10-3 次方繼續訓練 10 epoch

檢測物體訓練

yolov3 detect

圖片尺寸 :input_shape = [height, width]

預處理

圖片預處理

resize 為 256×256 併除以 255 進行歸一化

標籤預處理

boxes 轉換為 (x,y, w,h, class) 格式得到 true_box,x, y, w, h 除以圖片 input_shape 進行歸一化

標籤計算

  1. 將 boxes resize 到 13×13 得到 box_output,此時, box_output 的 x, y 中心正好落在 output 的某一個 grid cell
  2. box_output 中的每個元素 box 與所有 anchors 計算 IOU,找到 IoU 最大值對應的 anchor。
  3. 對每張圖片的每個 box
    # yolo_1 初始化為 [batch_size, grid_1_h, grid_1_w, num_anchors, 4   1   num_class] 的全零矩陣
# yolo_2 初始化為 [batch_size, grid_2_h, grid_2_w, num_anchors, 4   1   num_class] 的全零矩陣
# yolo_3 初始化為 [batch_size, grid_3_h, grid_3_w, num_anchors, 4   1   num_class] 的全零矩陣
# grid_1 為 [13, 13] grid_2 為 [26x26] grid_3 為 [52, 52]
grids = [grid3, grid2, grid1]
yolos = [yolo_3, yolo_2, yolo_1]
i = floor(box_output[1]) # box_ouput 所屬 grid_cell 的 height
j = floor(box_output[0]) # box_ouput 所屬 grid_cell 的 width
anchor_index = 最大 IOU 對應的 anchor 索引
yolo = yolos[anchor_index // 3]  # 通過 anchor 定位到所屬 yolo
grid = yolos[anchor_index // 3]
yolo[image_index, i, j, anchor_index%3, 4] = 1 
yolo[image_index, i, j, anchor_index%3, 0:4]=[x/grid[1],y/grid[0],log(w)/anchor.w, log(h)/anchor.h]
yolo[image_index, i, j, anchor_index%3, 5 c] = 1  # c 為標籤中分類索引
#由上可以非常容易知道, yolos 就是生成的標籤
# true_box 初始化為 [batch_size, 1, 1, 1, num_box_per_image, 4] 
true_box[image_index, 0,0,0, box_index] = [x/input_shape[0], y/input_shape[1], w/input_shape[0], h/input_shape[1]] #box_index 為當前 box 在圖片中索引
object_mask = box_output[..., 4] # 記錄某個  grid_cell 的某個 anchor 是否包含物體

編碼

對於 pred_1 (13×13) 有

# pred_1 為 [batch_size, grid_1_h, grid_1_w, num_anchors, 4   1   num_classes]
# index 為  grid_1 的元素索引,(0,0),(0,1)...(w-1, h-1)
pred_box_xy = index   sigmoid(pred_1[..., 0:2]) / grid_1
pred_box_wh = exp(pred_1[..., 2:4]) * anchor / input_shape
pred_box_confidence = pred_1[..., 4]
pred_box_class = pred_1[..., 5:]

對 pred_2, pred_3 處理同上。

loss 計算

對於 pred_1

#計算 pred_box 與  true_box 的 IoU1,並且 IoU 小於 < 0.5 為 1,大於 0.5 為 0,這裡有疑問?
pred_box_confidence = pred_box_confidence * IoU1
obj_scale = 5
noobj_scale = 1
wh_scale = exp(true_box_wh) * anchors / input_shape
wh_scale = 2 - wh_scale[0] * wh_scale[1]
# 標籤
true_box_xy = yolo_1[image_index, i, j, anchor_index%3, 0:2]
true_box_wh = yolo_1[image_index, i, j, anchor_index%3, 2:4]
true_box_confidence = yolo_1[image_index, i, j, anchor_index%3, 4]
true_box_class = yolo_1[image_index, i, j, anchor_index%3, 5:]
# object_mask 使得只有對應 grid_cell 對應的 anchor 有對應標籤物體存在是才計算 loss
xy_loss = object_mask * square(pred_box_xy - true_box_xy) * wh_scale
wh_loss = object_mask * square(pred_box_wh - true_box_wh) * wh_scale
confidence_loss = object_mask * square(pred_box_confidence - true_box_confidence) * obj_scale   (1-object_mask) * square(conf_delta) * noobj_scale
#logits = tf.nn.sparse_softmax_cross_entropy_with_logits
class_loss = object_mask * logits(true_box_class, pred_box_class)
loss = sum(xy_loss)   sum(wh_loss)   sum(confidence_loss)   sum(class_loss)

驗證

yolov3 inference

輸入

圖片尺寸 :input_shape = [height, width]

經過 DarkNet 之後,得到 yolo_output,維度為 [batch_size, height, width, num_anchors, 5 num_classes]

其中最後一維依次為 [x,y,w,h, confidence, class_one_hot]

預處理

圖片預處理

resize 為 256×256 併除以 255 進行歸一化

編碼

對於 pred_1 (13×13) 有

# pred_1 為 [batch_size, grid_1_h, grid_1_w, num_anchors, 4   1   num_classes]
# index 為  grid_1 的元素索引,(0,0),(0,1)...(w-1, h-1)
pred_box_xy = index   sigmoid(pred_1[..., 0:2])
pred_box_wh = pred_1[..., 2:4]
pred_box_confidence = sigmoid(pred_1[..., 4])
pred_box_class = pred_box_confidence * softmax(pred_1[..., 5:]) > 0.5
對 pred_box,保留 pred_box_confidence > 0.5 的元素。

對 pred_2, pred_3 處理同上。

修正

對於任一 pred_box

  1. new_shape = round(image_shape * arg_min(input_shape/image_shape))
  2. offset = (input_shape-new_shape)/2./input_shape
  3. scale = input_shape/new_shape
  4. box_yx = (box_yx – offset) * scale; box_hw = box_hw * scales
  5. pred_box * [image_shape, image_shape]
  6. pred_box 轉為 [ymin, xmin, ymax, xmax] 格式

比如 image_shape 為 [600,800],input_shape 為 [300, 500],那麼 new_shape 為 [300, 400]

offset 為 [0, 0.125] scales 為 [0.5, 0.625]

過濾

對於任意 pred_box 進行以 0.45 為 IoU 閾值進行 NMS

以上就是 YoloV3 實現。

附錄

k-mean 的 anchor 生成演算法

  1. 隨機初始化 一個聚類中心
  2. 算出每個元素與聚類中心的距離,在這裡是 1-IOU
  3. 將每個元素劃入對應的聚類,依據就是該元素與聚類中心距離最小,此時,已經有新的聚類中心
  4. 求各個聚類的均值作為新的中心
  5. 迭代 2-4 直到連續兩次聚類中心都重合,表明已經找到合適的聚類中心