图解 YoloV3

整个训练分两部分

  1. 对 DarkNet53 的预训练
  2. 基于 DarkNet53 进行物体检测训练

原图尺寸:input_shape

标签:原始标签 box 为 (class, xmin, ymin, xmax, ymax),xmin, ymin, xmax, ymax 是左上角为坐标轴相对于原图的偏移。

anchor 总共 9 个,三个为一组分给三组输出,其中 anchor 的 0,1,2 分给 32x32 的输出,3,4,5 分给 16x16 的输出,6,7,8分给 8x8 的输出。anchor 的生产通过 k-mean 算法生成(参见附录)。

Darknet53 训练

预处理

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

检测物体训练

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

预处理

图片预处理

resize 为 256x256 并除以 255 进行归一化

标签预处理

boxes 转换为 (x,y, w,h, class) 格式得到 true_box,x, y, w, h 除以图片 input_shape 进行归一化

标签计算

  1. 将 boxes resize 到 13x13 得到 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 (13x13) 有

# 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)

验证

输入

图片尺寸 :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 为 256x256 并除以 255 进行归一化

编码

对于 pred_1 (13x13) 有

# 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 直到连续两次聚类中心都重合,表明已经找到合适的聚类中心

你可能感兴趣的:(物体检测,物体检测)