当前位置:   article > 正文

广西民族大学高级人工智能课程—头歌实践教学实践平台-目标检测

广西民族大学高级人工智能课程—头歌实践教学实践平台-目标检测

代码文件

  1. """YOLO_v3 Model Defined in Keras."""
  2. from functools import wraps
  3. import numpy as np
  4. import tensorflow as tf
  5. from keras import backend as K
  6. from keras.layers import Conv2D, Add, ZeroPadding2D, UpSampling2D, Concatenate, MaxPooling2D
  7. from keras.layers.advanced_activations import LeakyReLU
  8. from keras.layers.normalization import BatchNormalization
  9. from keras.models import Model
  10. from keras.regularizers import l2
  11. from yolov3.utils import compose
  12. # your code
  13. K.clear_session()
  14. @wraps(Conv2D)
  15. def DarknetConv2D(*args, **kwargs):
  16. """Wrapper to set Darknet parameters for Convolution2D."""
  17. darknet_conv_kwargs = {'kernel_regularizer': l2(5e-4)}
  18. darknet_conv_kwargs['padding'] = 'valid' if kwargs.get('strides') == (2, 2) else 'same'
  19. darknet_conv_kwargs.update(kwargs)
  20. return Conv2D(*args, **darknet_conv_kwargs)
  21. def DarknetConv2D_BN_Leaky(*args, **kwargs):
  22. """Darknet Convolution2D followed by BatchNormalization and LeakyReLU."""
  23. no_bias_kwargs = {'use_bias': False}
  24. no_bias_kwargs.update(kwargs)
  25. return compose(
  26. DarknetConv2D(*args, **no_bias_kwargs),
  27. BatchNormalization(),
  28. LeakyReLU(alpha=0.1))
  29. def resblock_body(x, num_filters, num_blocks):
  30. '''A series of resblocks starting with a downsampling Convolution2D'''
  31. # Darknet uses left and top padding instead of 'same' mode
  32. x = ZeroPadding2D(((1, 0), (1, 0)))(x)
  33. x = DarknetConv2D_BN_Leaky(num_filters, (3, 3), strides=(2, 2))(x)
  34. for i in range(num_blocks):
  35. y = compose(
  36. DarknetConv2D_BN_Leaky(num_filters // 2, (1, 1)),
  37. DarknetConv2D_BN_Leaky(num_filters, (3, 3)))(x)
  38. x = Add()([x, y])
  39. return x
  40. def darknet_body(x):
  41. '''Darknet body having 52 Convolution2D layers'''
  42. x = DarknetConv2D_BN_Leaky(32, (3, 3))(x)
  43. x = resblock_body(x, 64, 1)
  44. # 添加缺失的两层 resn 层
  45. x = resblock_body(x, 128, 2) # 第一层 resn 层,num_filters 为 128
  46. x = resblock_body(x, 256, 8) # 第二层 resn 层,num_filters 为 256
  47. x = resblock_body(x, 512, 8)
  48. x = resblock_body(x, 1024, 4)
  49. return x
  50. def make_last_layers(x, num_filters, out_filters):
  51. '''6 Conv2D_BN_Leaky layers followed by a Conv2D_linear layer'''
  52. x = compose(
  53. DarknetConv2D_BN_Leaky(num_filters, (1, 1)),
  54. DarknetConv2D_BN_Leaky(num_filters * 2, (3, 3)),
  55. DarknetConv2D_BN_Leaky(num_filters, (1, 1)),
  56. DarknetConv2D_BN_Leaky(num_filters * 2, (3, 3)),
  57. DarknetConv2D_BN_Leaky(num_filters, (1, 1)))(x)
  58. # 添加最后的两层
  59. y = compose(
  60. DarknetConv2D_BN_Leaky(num_filters * 2, (3, 3)),
  61. DarknetConv2D(out_filters, (1, 1)))(x)
  62. return x, y
  63. def yolo_body(inputs, num_anchors, num_classes):
  64. """Create YOLO_V3 model CNN body in Keras."""
  65. darknet = Model(inputs, darknet_body(inputs))
  66. x, y1 = make_last_layers(darknet.output, 512, num_anchors * (num_classes + 5))
  67. # 添加中间层
  68. x = compose(
  69. DarknetConv2D_BN_Leaky(256, (1, 1)),
  70. UpSampling2D(2))(x)
  71. x = Concatenate()([x, darknet.layers[152].output])
  72. x, y2 = make_last_layers(x, 256, num_anchors * (num_classes + 5))
  73. # 添加最后的层
  74. x = compose(
  75. DarknetConv2D_BN_Leaky(128, (1, 1)),
  76. UpSampling2D(2))(x)
  77. x = Concatenate()([x, darknet.layers[92].output])
  78. x, y3 = make_last_layers(x, 128, num_anchors * (num_classes + 5))
  79. return Model(inputs, [y1, y2, y3])
  80. # 其余代码保持不变
  81. def tiny_yolo_body(inputs, num_anchors, num_classes):
  82. '''Create Tiny YOLO_v3 model CNN body in keras.'''
  83. x1 = compose(
  84. DarknetConv2D_BN_Leaky(16, (3, 3)),
  85. MaxPooling2D(pool_size=(2, 2), strides=(2, 2), padding='same'),
  86. DarknetConv2D_BN_Leaky(32, (3, 3)),
  87. MaxPooling2D(pool_size=(2, 2), strides=(2, 2), padding='same'),
  88. DarknetConv2D_BN_Leaky(64, (3, 3)),
  89. MaxPooling2D(pool_size=(2, 2), strides=(2, 2), padding='same'),
  90. DarknetConv2D_BN_Leaky(128, (3, 3)),
  91. MaxPooling2D(pool_size=(2, 2), strides=(2, 2), padding='same'),
  92. DarknetConv2D_BN_Leaky(256, (3, 3)))(inputs)
  93. x2 = compose(
  94. MaxPooling2D(pool_size=(2, 2), strides=(2, 2), padding='same'),
  95. DarknetConv2D_BN_Leaky(512, (3, 3)),
  96. MaxPooling2D(pool_size=(2, 2), strides=(1, 1), padding='same'),
  97. DarknetConv2D_BN_Leaky(1024, (3, 3)),
  98. DarknetConv2D_BN_Leaky(256, (1, 1)))(x1)
  99. y1 = compose(
  100. DarknetConv2D_BN_Leaky(512, (3, 3)),
  101. DarknetConv2D(num_anchors * (num_classes + 5), (1, 1)))(x2)
  102. x2 = compose(
  103. DarknetConv2D_BN_Leaky(128, (1, 1)),
  104. UpSampling2D(2))(x2)
  105. y2 = compose(
  106. Concatenate(),
  107. DarknetConv2D_BN_Leaky(256, (3, 3)),
  108. DarknetConv2D(num_anchors * (num_classes + 5), (1, 1)))([x2, x1])
  109. return Model(inputs, [y1, y2])
  110. def yolo_head(feats, anchors, num_classes, input_shape, calc_loss=False):
  111. """Convert final layer features to bounding box parameters."""
  112. num_anchors = len(anchors)
  113. # Reshape to batch, height, width, num_anchors, box_params.
  114. anchors_tensor = K.reshape(K.constant(anchors), [1, 1, 1, num_anchors, 2])
  115. grid_shape = K.shape(feats)[1:3] # height, width
  116. grid_y = K.tile(K.reshape(K.arange(0, stop=grid_shape[0]), [-1, 1, 1, 1]),
  117. [1, grid_shape[1], 1, 1])
  118. grid_x = K.tile(K.reshape(K.arange(0, stop=grid_shape[1]), [1, -1, 1, 1]),
  119. [grid_shape[0], 1, 1, 1])
  120. grid = K.concatenate([grid_x, grid_y])
  121. grid = K.cast(grid, K.dtype(feats))
  122. feats = K.reshape(
  123. feats, [-1, grid_shape[0], grid_shape[1], num_anchors, num_classes + 5])
  124. # Adjust preditions to each spatial grid point and anchor size.
  125. box_xy = (K.sigmoid(feats[..., :2]) + grid) / K.cast(grid_shape[::-1], K.dtype(feats))
  126. box_wh = K.exp(feats[..., 2:4]) * anchors_tensor / K.cast(input_shape[::-1], K.dtype(feats))
  127. box_confidence = K.sigmoid(feats[..., 4:5])
  128. box_class_probs = K.sigmoid(feats[..., 5:])
  129. if calc_loss == True:
  130. return grid, feats, box_xy, box_wh
  131. return box_xy, box_wh, box_confidence, box_class_probs
  132. def yolo_correct_boxes(box_xy, box_wh, input_shape, image_shape):
  133. '''Get corrected boxes'''
  134. box_yx = box_xy[..., ::-1]
  135. box_hw = box_wh[..., ::-1]
  136. input_shape = K.cast(input_shape, K.dtype(box_yx))
  137. image_shape = K.cast(image_shape, K.dtype(box_yx))
  138. new_shape = K.round(image_shape * K.min(input_shape / image_shape))
  139. offset = (input_shape - new_shape) / 2. / input_shape
  140. scale = input_shape / new_shape
  141. box_yx = (box_yx - offset) * scale
  142. box_hw *= scale
  143. box_mins = box_yx - (box_hw / 2.)
  144. box_maxes = box_yx + (box_hw / 2.)
  145. boxes = K.concatenate([
  146. box_mins[..., 0:1], # y_min
  147. box_mins[..., 1:2], # x_min
  148. box_maxes[..., 0:1], # y_max
  149. box_maxes[..., 1:2] # x_max
  150. ])
  151. # Scale boxes back to original image shape.
  152. boxes *= K.concatenate([image_shape, image_shape])
  153. return boxes
  154. def yolo_boxes_and_scores(feats, anchors, num_classes, input_shape, image_shape):
  155. '''Process Conv layer output'''
  156. box_xy, box_wh, box_confidence, box_class_probs = yolo_head(feats,
  157. anchors, num_classes, input_shape)
  158. boxes = yolo_correct_boxes(box_xy, box_wh, input_shape, image_shape)
  159. boxes = K.reshape(boxes, [-1, 4])
  160. box_scores = box_confidence * box_class_probs
  161. box_scores = K.reshape(box_scores, [-1, num_classes])
  162. return boxes, box_scores
  163. def yolo_eval(yolo_outputs,
  164. anchors,
  165. num_classes,
  166. image_shape,
  167. max_boxes=20,
  168. score_threshold=.6,
  169. iou_threshold=.5):
  170. """Evaluate YOLO model on given input and return filtered boxes."""
  171. num_layers = len(yolo_outputs)
  172. anchor_mask = [[6, 7, 8], [3, 4, 5], [0, 1, 2]] if num_layers == 3 else [[3, 4, 5], [1, 2, 3]] # default setting
  173. input_shape = K.shape(yolo_outputs[0])[1:3] * 32
  174. boxes = []
  175. box_scores = []
  176. for l in range(num_layers):
  177. _boxes, _box_scores = yolo_boxes_and_scores(yolo_outputs[l],
  178. anchors[anchor_mask[l]], num_classes, input_shape, image_shape)
  179. boxes.append(_boxes)
  180. box_scores.append(_box_scores)
  181. boxes = K.concatenate(boxes, axis=0)
  182. box_scores = K.concatenate(box_scores, axis=0)
  183. mask = box_scores >= score_threshold
  184. max_boxes_tensor = K.constant(max_boxes, dtype='int32')
  185. boxes_ = []
  186. scores_ = []
  187. classes_ = []
  188. for c in range(num_classes):
  189. # TODO: use keras backend instead of tf.
  190. class_boxes = tf.boolean_mask(boxes, mask[:, c])
  191. class_box_scores = tf.boolean_mask(box_scores[:, c], mask[:, c])
  192. nms_index = tf.image.non_max_suppression(
  193. class_boxes, class_box_scores, max_boxes_tensor, iou_threshold=iou_threshold)
  194. class_boxes = K.gather(class_boxes, nms_index)
  195. class_box_scores = K.gather(class_box_scores, nms_index)
  196. classes = K.ones_like(class_box_scores, 'int32') * c
  197. boxes_.append(class_boxes)
  198. scores_.append(class_box_scores)
  199. classes_.append(classes)
  200. boxes_ = K.concatenate(boxes_, axis=0)
  201. scores_ = K.concatenate(scores_, axis=0)
  202. classes_ = K.concatenate(classes_, axis=0)
  203. return boxes_, scores_, classes_
  204. def preprocess_true_boxes(true_boxes, input_shape, anchors, num_classes):
  205. '''Preprocess true boxes to training input format
  206. Parameters
  207. ----------
  208. true_boxes: array, shape=(m, T, 5)
  209. Absolute x_min, y_min, x_max, y_max, class_id relative to input_shape.
  210. input_shape: array-like, hw, multiples of 32
  211. anchors: array, shape=(N, 2), wh
  212. num_classes: integer
  213. Returns
  214. -------
  215. y_true: list of array, shape like yolo_outputs, xywh are reletive value
  216. '''
  217. assert (true_boxes[..., 4] < num_classes).all(), 'class id must be less than num_classes'
  218. num_layers = len(anchors) // 3 # default setting
  219. anchor_mask = [[6, 7, 8], [3, 4, 5], [0, 1, 2]] if num_layers == 3 else [[3, 4, 5], [1, 2, 3]]
  220. true_boxes = np.array(true_boxes, dtype='float32')
  221. input_shape = np.array(input_shape, dtype='int32')
  222. boxes_xy = (true_boxes[..., 0:2] + true_boxes[..., 2:4]) // 2
  223. boxes_wh = true_boxes[..., 2:4] - true_boxes[..., 0:2]
  224. true_boxes[..., 0:2] = boxes_xy / input_shape[::-1]
  225. true_boxes[..., 2:4] = boxes_wh / input_shape[::-1]
  226. m = true_boxes.shape[0]
  227. grid_shapes = [input_shape // {0: 32, 1: 16, 2: 8}[l] for l in range(num_layers)]
  228. y_true = [np.zeros((m, grid_shapes[l][0], grid_shapes[l][1], len(anchor_mask[l]), 5 + num_classes),
  229. dtype='float32') for l in range(num_layers)]
  230. # Expand dim to apply broadcasting.
  231. anchors = np.expand_dims(anchors, 0)
  232. anchor_maxes = anchors / 2.
  233. anchor_mins = -anchor_maxes
  234. valid_mask = boxes_wh[..., 0] > 0
  235. for b in range(m):
  236. # Discard zero rows.
  237. wh = boxes_wh[b, valid_mask[b]]
  238. if len(wh) == 0: continue
  239. # Expand dim to apply broadcasting.
  240. wh = np.expand_dims(wh, -2)
  241. box_maxes = wh / 2.
  242. box_mins = -box_maxes
  243. intersect_mins = np.maximum(box_mins, anchor_mins)
  244. intersect_maxes = np.minimum(box_maxes, anchor_maxes)
  245. intersect_wh = np.maximum(intersect_maxes - intersect_mins, 0.)
  246. intersect_area = intersect_wh[..., 0] * intersect_wh[..., 1]
  247. box_area = wh[..., 0] * wh[..., 1]
  248. anchor_area = anchors[..., 0] * anchors[..., 1]
  249. iou = intersect_area / (box_area + anchor_area - intersect_area)
  250. # Find best anchor for each true box
  251. best_anchor = np.argmax(iou, axis=-1)
  252. for t, n in enumerate(best_anchor):
  253. for l in range(num_layers):
  254. if n in anchor_mask[l]:
  255. i = np.floor(true_boxes[b, t, 0] * grid_shapes[l][1]).astype('int32')
  256. j = np.floor(true_boxes[b, t, 1] * grid_shapes[l][0]).astype('int32')
  257. k = anchor_mask[l].index(n)
  258. c = true_boxes[b, t, 4].astype('int32')
  259. y_true[l][b, j, i, k, 0:4] = true_boxes[b, t, 0:4]
  260. y_true[l][b, j, i, k, 4] = 1
  261. y_true[l][b, j, i, k, 5 + c] = 1
  262. return y_true
  263. def box_iou(b1, b2):
  264. '''Return iou tensor
  265. Parameters
  266. ----------
  267. b1: tensor, shape=(i1,...,iN, 4), xywh
  268. b2: tensor, shape=(j, 4), xywh
  269. Returns
  270. -------
  271. iou: tensor, shape=(i1,...,iN, j)
  272. '''
  273. # Expand dim to apply broadcasting.
  274. b1 = K.expand_dims(b1, -2)
  275. b1_xy = b1[..., :2]
  276. b1_wh = b1[..., 2:4]
  277. b1_wh_half = b1_wh / 2.
  278. b1_mins = b1_xy - b1_wh_half
  279. b1_maxes = b1_xy + b1_wh_half
  280. # Expand dim to apply broadcasting.
  281. b2 = K.expand_dims(b2, 0)
  282. b2_xy = b2[..., :2]
  283. b2_wh = b2[..., 2:4]
  284. b2_wh_half = b2_wh / 2.
  285. b2_mins = b2_xy - b2_wh_half
  286. b2_maxes = b2_xy + b2_wh_half
  287. intersect_mins = K.maximum(b1_mins, b2_mins)
  288. intersect_maxes = K.minimum(b1_maxes, b2_maxes)
  289. intersect_wh = K.maximum(intersect_maxes - intersect_mins, 0.)
  290. intersect_area = intersect_wh[..., 0] * intersect_wh[..., 1]
  291. b1_area = b1_wh[..., 0] * b1_wh[..., 1]
  292. b2_area = b2_wh[..., 0] * b2_wh[..., 1]
  293. iou = intersect_area / (b1_area + b2_area - intersect_area)
  294. return iou
  295. def yolo_loss(args, anchors, num_classes, ignore_thresh=.5, print_loss=False):
  296. '''Return yolo_loss tensor
  297. Parameters
  298. ----------
  299. yolo_outputs: list of tensor, the output of yolo_body or tiny_yolo_body
  300. y_true: list of array, the output of preprocess_true_boxes
  301. anchors: array, shape=(N, 2), wh
  302. num_classes: integer
  303. ignore_thresh: float, the iou threshold whether to ignore object confidence loss
  304. Returns
  305. -------
  306. loss: tensor, shape=(1,)
  307. '''
  308. num_layers = len(anchors) // 3 # default setting
  309. yolo_outputs = args[:num_layers]
  310. y_true = args[num_layers:]
  311. anchor_mask = [[6, 7, 8], [3, 4, 5], [0, 1, 2]] if num_layers == 3 else [[3, 4, 5], [1, 2, 3]]
  312. input_shape = K.cast(K.shape(yolo_outputs[0])[1:3] * 32, K.dtype(y_true[0]))
  313. grid_shapes = [K.cast(K.shape(yolo_outputs[l])[1:3], K.dtype(y_true[0])) for l in range(num_layers)]
  314. loss = 0
  315. m = K.shape(yolo_outputs[0])[0] # batch size, tensor
  316. mf = K.cast(m, K.dtype(yolo_outputs[0]))
  317. for l in range(num_layers):
  318. object_mask = y_true[l][..., 4:5]
  319. true_class_probs = y_true[l][..., 5:]
  320. grid, raw_pred, pred_xy, pred_wh = yolo_head(yolo_outputs[l],
  321. anchors[anchor_mask[l]], num_classes, input_shape, calc_loss=True)
  322. pred_box = K.concatenate([pred_xy, pred_wh])
  323. # Darknet raw box to calculate loss.
  324. raw_true_xy = y_true[l][..., :2] * grid_shapes[l][::-1] - grid
  325. raw_true_wh = K.log(y_true[l][..., 2:4] / anchors[anchor_mask[l]] * input_shape[::-1])
  326. raw_true_wh = K.switch(object_mask, raw_true_wh, K.zeros_like(raw_true_wh)) # avoid log(0)=-inf
  327. box_loss_scale = 2 - y_true[l][..., 2:3] * y_true[l][..., 3:4]
  328. # Find ignore mask, iterate over each of batch.
  329. ignore_mask = tf.TensorArray(K.dtype(y_true[0]), size=1, dynamic_size=True)
  330. object_mask_bool = K.cast(object_mask, 'bool')
  331. def loop_body(b, ignore_mask):
  332. true_box = tf.boolean_mask(y_true[l][b, ..., 0:4], object_mask_bool[b, ..., 0])
  333. iou = box_iou(pred_box[b], true_box)
  334. best_iou = K.max(iou, axis=-1)
  335. ignore_mask = ignore_mask.write(b, K.cast(best_iou < ignore_thresh, K.dtype(true_box)))
  336. return b + 1, ignore_mask
  337. _, ignore_mask = K.control_flow_ops.while_loop(lambda b, *args: b < m, loop_body, [0, ignore_mask])
  338. ignore_mask = ignore_mask.stack()
  339. ignore_mask = K.expand_dims(ignore_mask, -1)
  340. # K.binary_crossentropy is helpful to avoid exp overflow.
  341. xy_loss = object_mask * box_loss_scale * K.binary_crossentropy(raw_true_xy, raw_pred[..., 0:2],
  342. from_logits=True)
  343. wh_loss = object_mask * box_loss_scale * 0.5 * K.square(raw_true_wh - raw_pred[..., 2:4])
  344. confidence_loss = object_mask * K.binary_crossentropy(object_mask, raw_pred[..., 4:5], from_logits=True) + \
  345. (1 - object_mask) * K.binary_crossentropy(object_mask, raw_pred[..., 4:5],
  346. from_logits=True) * ignore_mask
  347. class_loss = object_mask * K.binary_crossentropy(true_class_probs, raw_pred[..., 5:], from_logits=True)
  348. xy_loss = K.sum(xy_loss) / mf
  349. wh_loss = K.sum(wh_loss) / mf
  350. confidence_loss = K.sum(confidence_loss) / mf
  351. class_loss = K.sum(class_loss) / mf
  352. loss += xy_loss + wh_loss + confidence_loss + class_loss
  353. if print_loss:
  354. loss = tf.Print(loss, [loss, xy_loss, wh_loss, confidence_loss, class_loss, K.sum(ignore_mask)],
  355. message='loss: ')
  356. return loss

题目描述

任务描述

本关任务:了解并掌握目标检测的原理及实现。

相关知识

一、基本概念

1. 什么是目标检测

目标检测(Object Detection)的任务是找出图像中所有感兴趣的目标(物体),确定它们的类别和位置,是计算机视觉领域的核心问题之一。由于各类物体有不同的外观、形状和姿态,加上成像时光照、遮挡等因素的干扰,目标检测一直是计算机视觉领域最具有挑战性的问题。

计算机视觉中关于图像识别有四大类任务:

(1)分类-Classification:解决“是什么?”的问题,即给定一张图片或一段视频判断里面包含什么类别的目标。

(2)定位-Location:解决“在哪里?”的问题,即定位出这个目标的的位置。

(3)检测-Detection:解决“在哪里?是什么?”的问题,即定位出这个目标的位置并且知道目标物是什么。

(4)分割-Segmentation:分为实例的分割(Instance-level)和场景分割(Scene-level),解决“每一个像素属于哪个目标物或场景”的问题。

,

所以,目标检测是一个分类、回归问题的叠加。

2. 目标检测的核心问题

(1)分类问题:即图片(或某个区域)中的图像属于哪个类别。

(2)定位问题:目标可能出现在图像的任何位置。

(3)大小问题:目标有各种不同的大小。

(4)形状问题:目标可能有各种不同的形状。

3. 目标检测算法分类

基于深度学习的目标检测算法主要分为两类:Two stage和One stage。

1)Tow Stage

先进行区域生成,该区域称之为region proposal(简称RP,一个有可能包含待检物体的预选框),再通过卷积神经网络进行样本分类。

任务流程:特征提取 --> 生成RP --> 分类/定位回归。

常见tow stage目标检测算法有:R-CNN、SPP-Net、Fast R-CNN、Faster R-CNN和R-FCN等。

2)One Stage

不用RP,直接在网络中提取特征来预测物体分类和位置。

任务流程:特征提取–> 分类/定位回归。

常见的one stage目标检测算法有:OverFeat、YOLOv1、YOLOv2、YOLOv3、SSD和RetinaNet等。

4. 目标检测应用
1)人脸检测
  • 智能门控
  • 员工考勤签到
  • 智慧超市
  • 人脸支付
  • 车站、机场实名认证
  • 公共安全:逃犯抓捕、走失人员检测
2)行人检测
  • 智能辅助驾驶
  • 智能监控
  • 暴恐检测(根据面相识别暴恐倾向)
  • 移动侦测、区域入侵检测、安全帽/安全带检测
3)车辆检测
  • 自动驾驶
  • 违章查询、关键通道检测
  • 广告检测(检测广告中的车辆类型,弹出链接)
4)遥感检测
  • 大地遥感,如土地使用、公路、水渠、河流监控

  • 农作物监控

  • 军事检测

二、目标检测原理

目标检测分为两大系列——RCNN系列和YOLO系列,RCNN系列是基于区域检测的代表性算法,YOLO是基于区域提取的代表性算法,另外还有著名的SSD是基于前两个系列的改进。

1. 候选区域产生

很多目标检测技术都会涉及候选框(bounding boxes)的生成,物体候选框获取当前主要使用图像分割与区域生长技术。区域生长(合并)主要由于检测图像中存在的物体具有局部区域相似性(颜色、纹理等)。目标识别与图像分割技术的发展进一步推动有效提取图像中信息。

1)滑动窗口

通过滑窗法流程图可以很清晰理解其主要思路:首先对输入图像进行不同窗口大小的滑窗进行从左往右、从上到下的滑动。每次滑动时候对当前窗口执行分类器(分类器是事先训练好的)。如果当前窗口得到较高的分类概率,则认为检测到了物体。对每个不同窗口大小的滑窗都进行检测后,会得到不同窗口检测到的物体标记,这些窗口大小会存在重复较高的部分,最后采用非极大值抑制(Non-Maximum Suppression, NMS)的方法进行筛选。最终,经过NMS筛选后获得检测到的物体。
滑窗法简单易于理解,但是不同窗口大小进行图像全局搜索导致效率低下,而且设计窗口大小时候还需要考虑物体的长宽比。所以,对于实时性要求较高的分类器,不推荐使用滑窗法。

2)选择性搜索
① 什么是选择性搜索

滑窗法类似穷举进行图像子区域搜索,但是一般情况下图像中大部分子区域是没有物体的。学者们自然而然想到只对图像中最有可能包含物体的区域进行搜索以此来提高计算效率。选择搜索(selective search,简称SS)方法是当下最为熟知的图像bounding boxes提取算法,由Koen E.A于2011年提出。
选择搜索算法的主要思想:图像中物体可能存在的区域应该是有某些相似性或者连续性区域的。因此,选择搜索基于上面这一想法采用子区域合并的方法进行提取bounding boxes。首先,对输入图像进行分割算法产生许多小的子区域。其次,根据这些子区域之间相似性(相似性标准主要有颜色、纹理、大小等等)进行区域合并,不断的进行区域迭代合并。每次迭代过程中对这些合并的子区域做bounding boxes(外切矩形),这些子区域外切矩形就是通常所说的候选框。

② 选择搜索流程
  • step0:生成区域集R
  • step1:计算区域集R里每个相邻区域的相似度S=s1,s2,…
  • step2:找出相似度最高的两个区域,将其合并为新集,添加进R
  • step3:从S中移除所有与step2中有关的子集
  • step4:计算新集与所有子集的相似度
  • step5:跳至step2,直至 S 为空
③ 选择搜索优点
  • 计算效率优于滑窗法
  • 由于采用子区域合并策略,所以可以包含各种大小的疑似物体框
  • 合并区域相似的指标多样性,提高了检测物体的概率
2. 数据表示

经过标记后的样本数据如下所示:

预测输出可以表示为:

,

其中, pc​ 为预测结果的置信概率,bx​, by​, bw​, bh​ 为边框坐标,C1​, C2​, C3​ 为属于某个类别的概率。通过预测结果、实际结果,构建损失函数。损失函数包含了分类、回归两部分组成。

3. 效果评估

使用IoU(Intersection over Union,交并比)来判断模型的好坏。所谓交并比,是指预测边框、实际边框交集和并集的比率,一般约定 0.5 为一个可以接收的值。

4. 非极大值抑制

预测结果中,可能多个预测结果间存在重叠部分,需要保留交并比最大的、去掉非最大的预测结果,这就是非极大值抑制(Non-Maximum Suppression,简写作NMS)。如下图所示,对同一个物体预测结果包含三个概率0.8/0.9/0.95,经过非极大值抑制后,仅保留概率最大的预测结果。

三、目标检测模型

1. R-CNN系列
1)R-CNN
① 定义

R-CNN(全称Regions with CNN features) ,是R-CNN系列的第一代算法,其实没有过多的使用“深度学习”思想,而是将“深度学习”和传统的“计算机视觉”的知识相结合。比如 R-CNN pipeline 中的第二步和第四步其实就属于传统的“计算机视觉”技术。使用 selective search 提取 region proposals,使用 SVM 实现分类。

② 流程
  • 预训练模型。选择一个预训练 (pre-trained)神经网络(如AlexNet、VGG)。
  • 重新训练全连接层。使用需要检测的目标重新训练(re-train)最后全连接层(connected layer)。
  • 提取 proposals并计算CNN 特征。利用选择性搜索(Selective Search)算法提取所有proposals(大约2000幅images),调整(resize/warp)它们成固定大小,以满足 CNN输入要求(因为全连接层的限制),然后将feature map 保存到本地磁盘。
  • 训练SVM。利用feature map 训练SVM来对目标和背景进行分类(每个类一个二进制SVM)
  • 边界框回归(Bounding boxes Regression)。训练将输出一些校正因子的线性回归分类器
③ 效果
  • R-CNN在VOC 2007测试集上mAP达到58.5%,打败当时所有的目标检测算法
④ 缺点
  • 重复计算,每个region proposal,都需要经过一个AlexNet特征提取,为所有的RoI(region of interest)提取特征大约花费47秒,占用空间
  • selective search方法生成region proposal,对一帧图像,需要花费2秒
  • 三个模块(提取、分类、回归)是分别训练的,并且在训练时候,对于存储空间消耗较大
2)Fast R-CNN
① 定义

Fast R-CNN是基于R-CNN和SPPnets进行的改进。SPPnets,其创新点在于只进行一次图像特征提取(而不是每个候选区域计算一次),然后根据算法,将候选区域特征图映射到整张图片特征图中。

② 流程
  • 使用selective search生成region proposal,大约2000个左右区域候选框
  • (joint training)缩放图片的scale得到图片金字塔,FP得到conv5的特征金字塔
  • (joint training)对于每个scale的每个ROI,求取映射关系,在conv5中剪裁出对应的patch。并用一个单层的SSP layer来统一到一样的尺度(对于AlexNet是6*6)
  • (joint training) 继续经过两个全连接得到特征,这特征又分别共享到两个新的全连接,连接上两个优化目标。第一个优化目标是分类,使用softmax,第二个优化目标是bbox regression,使用了一个平滑的L1-loss
  • 测试时需要加上NMS处理:利用窗口得分分别对每一类物体进行非极大值抑制提出重叠建议框,最终得到每个类别中回归修正后的得分最高的窗口
③ 改进
  • 和RCNN相比,训练时间从84小时减少为9.5小时,测试时间从47秒减少为0.32秒。在VGG16上,Fast RCNN训练速度是RCNN的9倍,测试速度是RCNN的213倍;训练速度是SPP-net的3倍,测试速度是SPP-net的3倍
  • Fast RCNN在PASCAL VOC 2007上准确率相差无几,约在66~67%之间
  • 加入RoI Pooling,采用一个神经网络对全图提取特征
  • 在网络中加入了多任务函数边框回归,实现了端到端的训练
④ 缺点
  • 依旧采用selective search提取region proposal(耗时2~3秒,特征提取耗时0.32秒)
  • 无法满足实时应用,没有真正实现端到端训练测试
  • 利用了GPU,但是region proposal方法是在CPU上实现的
3)Faster RCNN

经过R-CNN和Fast-RCNN的积淀,Ross B.Girshick在2016年提出了新的Faster RCNN,在结构上将特征抽取、region proposal提取, bbox regression,分类都整合到了一个网络中,使得综合性能有较大提高,在检测速度方面尤为明显。

① 整体流程
  • Conv Layers。作为一种CNN网络目标检测方法,Faster RCNN首先使用一组基础的卷积/激活/池化层提取图像的特征,形成一个特征图,用于后续的RPN层和全连接层。
  • Region Proposal Networks(RPN)。RPN网络用于生成候选区域,该层通过softmax判断锚点(anchors)属于前景还是背景,在利用bounding box regression(包围边框回归)获得精确的候选区域。
  • RoI Pooling。该层收集输入的特征图和候选区域,综合这些信息提取候选区特征图(proposal feature maps),送入后续全连接层判定目标的类别。
  • Classification。利用取候选区特征图计算所属类别,并再次使用边框回归算法获得边框最终的精确位置。
② Anchors

Anchors(锚点)指由一组矩阵,每个矩阵对应不同的检测尺度大小。如下矩阵:

 
  1. [[ -84. -40. 99. 55.]
  2. [-176. -88. 191. 103.]
  3. [-360. -184. 375. 199.]
  4. [ -56. -56. 71. 71.]
  5. [-120. -120. 135. 135.]
  6. [-248. -248. 263. 263.]
  7. [ -36. -80. 51. 95.]
  8. [ -80. -168. 95. 183.]
  9. [-168. -344. 183. 359.]]

其中每行4个值(x1​, y1​, x2​, y2​),对应矩形框左上角、右下角相对于中心点的偏移量。9 个矩形共有三种形状,即1:1, 1:2, 2:1,即进行多尺度检测。

例如,一张800 * 600的原始图片,经过VGG下采样后(生成特征矩阵)16倍大小,大小变为50 * 38,每个点设置 9 个anchor,则总数为:

 
  1. ceil(800 / 16) * ceil(600 / 16) * 9 = 50 * 38 * 9 = 17100
③ Bounding box regression

物体识别完成后,通过一种方式对外围框进行调整,使得和目标物体更加接近。

④ 损失函数

对一个图像的损失函数,是一个分类损失函数与回归损失函数的叠加:

L({pi​},{ti​})=Ncls​1​∑Lcls​(pi​,pi∗​)+λNreg​1​∑pi∗​Lreg​(ti​,ti∗​)

  • i是一个mini-batch中anchor的索引

  • pi​是anchor i 为目标的预测概率

  • ground truth标签 pi∗​就是1,如果anchor为负, pi∗​就是0

  • ti​是一个向量,表示预测的包围盒的4个参数化坐标

  • Ncls​是与正anchor对应的ground truth的坐标向量

  • Nreg​为anchor位置的数量(大约2400), λ=10

分类损失函数:

Lcls​(pi​,pi∗​)=−log[pi∗​pi​+(1−pi∗​)(1−pi​)]

位置损失函数:

Lreg​(ti​,ti∗​)=R(ti​−ti∗​)

其中:

,

⑤ 改进
  • 在VOC2007测试集测试mAP达到73.2%,目标检测速度可达5帧/秒
  • 提出Region Proposal Network(RPN),取代selective search,生成待检测区域,时间从2秒缩减到了10毫秒
  • 真正实现了一个完全的End-To-End的CNN目标检测模型
  • 共享RPN与Fast RCNN的特征
⑥ 缺点
  • 还是无法达到实时检测目标
  • 获取region proposal, 再对每个proposal分类计算量还是较大
2. YOLO系列
1)YOLOv1(2016)
① 基本思想

YOLO(You Only Look Once )是继RCNN,fast-RCNN和faster-RCNN之后,Ross Girshick针对DL目标检测速度问题提出的另一种框架,其核心思想是生成RoI+目标检测两阶段(two-stage)算法用一套网络的一阶段(one- stage)算法替代,直接在输出层回归bounding box的位置和所属类别。

之前的物体检测方法首先需要产生大量可能包含待检测物体的先验框, 然后用分类器判断每个先验框对应的边界框里是否包含待检测物体,以及物体所属类别的概率或者置信度,同时需要后处理修正边界框,最后基于一些准则过滤掉置信度不高和重叠度较高的边界框,进而得到检测结果。这种基于先产生候选区再检测的方法虽然有相对较高的检测准确率,但运行速度较慢。

YOLO创造性的将物体检测任务直接当作回归问题(regression problem)来处理,将候选区和检测两个阶段合二为一。只需一眼就能知道每张图像中有哪些物体以及物体的位置。下图展示了各物体检测系统的流程图。

实际上,YOLO并没有真正去掉候选区,而是采用了预定义候选区的方法,也就是将图片划分为7 * 7个网格,每个网格允许预测出2个边框,总共49 * 2个bounding box,可以理解为98个候选区域,它们很粗略地覆盖了图片的整个区域。YOLO以降低mAP为代价,大幅提升了时间效率。

每个网格单元预测这些框的2个边界框和置信度分数。这些置信度分数反映了该模型对框是否包含目标的可靠程度,以及它预测框的准确程度。置信度定义为:

Pr(Object)∗IOUpredtruth​

如果该单元格中不存在目标,则置信度分数应为零。否则,我们希望置信度分数等于预测框与真实值之间联合部分的交集(IOU)。

每个边界框包含5个预测: x,y,w,h 和置信度。(x,y)坐标表示边界框相对于网格单元边界框的中心。宽度和高度是相对于整张图像预测的。最后,置信度预测表示预测框与实际边界框之间的IOU。

每个网格单元还预测 C 个条件类别概率 Pr(Classi​∣Object)。这些概率以包含目标的网格单元为条件。每个网格单元我们只预测的一组类别概率,而不管边界框的的数量 B是多少。

② 网络结构

YOLOv1网络有24个卷积层,后面是2个全连接层。我们只使用 1×1降维层,后面是 3×3卷积层。如下图所示:

为了快速实现快速目标检测,YOLOV1还训练了快速版本。快速YOLO使用具有较少卷积层(9层而不是24层)的神经网络,在这些层中使用较少的滤波器。除了网络规模之外,YOLO和快速YOLO的所有训练和测试参数都是相同的。网络的最终输出是7∗7∗30(1470)的预测张量。

③ 训练过程与细节

(1)预训练。采用前20个卷积层、平均池化层、全连接层进行了大约一周的预训练;

(2)输入。输入数据为224∗224和448∗448大小的图像;

(3)采用相对坐标。通过图像宽度和高度来规范边界框的宽度和高度,使它们落在0和1之间;边界框 x 和 y 坐标参数化为特定网格单元位置的偏移量,边界也在 0 和 1 之间;

(4)损失函数

  • 损失函数由坐标预测、是否包含目标物体置信度、类别预测构成;
  • 其中 1iobj​表示目标是否出现在网格单元 i中,表示 1ijobj​ 网格单元i中的第j个边界框预测器“负责”该预测;
  • 如果目标存在于该网格单元中(前面讨论的条件类别概率),则损失函数仅惩罚分类错误;
  • 如果预测器“负责”实际边界框(即该网格单元中具有最高IOU的预测器),则它也仅惩罚边界框坐标错误。

(5)学习率。第一个迭代周期,慢慢地将学习率从 10−3提高到 10−2;然后继续以 10−2 的学习率训练 75 个迭代周期,用 10−3 的学习率训练30个迭代周期,最后用 10−4 的学习率训练30个迭代周期。

(6)避免过拟合策略。使用dropout和数据增强来避免过拟合。

④ 优点与缺点

(1)优点

  • YOLO检测物体速度非常快,其增强版GPU中能跑45fps(frame per second),简化版155fps
  • YOLO在训练和测试时都能看到一整张图的信息(而不像其它算法看到局部图片信息),因此YOLO在检测物体是能很好利用上下文信息,从而不容易在背景上预测出错误的物体信息
  • YOLO可以学到物体泛化特征

(2)缺点

  • 精度低于其它state-of-the-art的物体检测系统
  • 容易产生定位错误
  • 对小物体检测效果不好,尤其是密集的小物体,因为一个栅格只能检测2个物体
  • 由于损失函数的问题,定位误差是影响检测效果的主要原因,尤其是大小物体处理上还有待加强
2)YOLOv2(2016)

Ross Girshick吸收fast-RCNN和SSD算法,设计了YOLOv2(论文原名《YOLO9000: Better, Faster,Stronger》),在精度上利用一些列训练技巧,在速度上应用了新的网络模型DarkNet19,在分类任务上采用联合训练方法,结合wordtree等方法,使YOLOv2的检测种类扩充到了上千种,作者在论文中称可以检测超过9000个目标类别,所以也称YOLO9000,YOLOv2模型可以以不同的尺寸运行,从而在速度和准确性之间提供了一个简单的折衷,在67FPS时,YOLOv2在VOC 2007上获得了76.8mAP。在40FPS时,YOLOv2获得了78.6 mAP,比使用ResNet的FasterR-CNN和SSD等先进方法表现更出色,同时仍然运行速度显著更快。

① 改进策略

YOLOv2对YOLOv1采取了很多改进措施,以提高模型mAP,如下图所示:

(1)Batch Normalization(批量正则化) 。YOLOv2中在每个卷积层后加Batch Normalization(BN)层,去掉dropout. BN层可以起到一定的正则化效果,能提升模型收敛速度,防止模型过拟合。YOLOv2通过使用BN层使得mAP提高了2%。

(2)High Resolution Classifier(高分辨率分类器) 。原来的YOLO网络在预训练的时候采用的是224 * 224的输入(这是因为一般预训练的分类模型都是在ImageNet数据集上进行的),然后在detection的时候采用448 * 448的输入,这会导致从分类模型切换到检测模型的时候,模型还要适应图像分辨率的改变。而YOLOv2则将预训练分成两步:先用224 * 224的输入从头开始训练网络,大概160个epoch(表示将所有训练数据循环跑160次),然后再将输入调整到448 * 448,再训练10个epoch。注意这两步都是在ImageNet数据集上操作。最后再在检测的数据集上fine-tuning,也就是detection的时候用448 * 448的图像作为输入就可以顺利过渡了。作者的实验表明这样可以提高几乎4%的mAP。

(3)Convolutional With Anchor Boxes(带Anchor Boxes的卷积) 。 YOLOv1利用全连接层直接对边界框进行预测,导致丢失较多空间信息,定位不准。YOLOv2去掉了YOLOv1中的全连接层,使用Anchor Boxes预测边界框,同时为了得到更高分辨率的特征图,YOLOv2还去掉了一个池化层。由于图片中的物体都倾向于出现在图片的中心位置,若特征图恰好有一个中心位置,利用这个中心位置预测中心点落入该位置的物体,对这些物体的检测会更容易。所以总希望得到的特征图的宽高都为奇数。YOLOv2通过缩减网络,使用416 * 416的输入,模型下采样的总步长为32,最后得到13 * 13的特征图,然后对13 * 13的特征图的每个cell预测 5 个anchor boxes,对每个anchor box预测边界框的位置信息、置信度和一套分类概率值。使用anchor boxes之后,YOLOv2可以预测13 * 13 * 5=845个边界框,模型的召回率由原来的81%提升到88%,mAP由原来的69.5%降低到69.2%.召回率提升了7%,准确率下降了0.3%。

(4)Dimension Clusters(维度聚类) 。在Faster R-CNN和SSD中,先验框都是手动设定的,带有一定的主观性。YOLOv2采用k-means聚类算法对训练集中的边界框做了聚类分析,选用boxes之间的IOU值作为聚类指标。综合考虑模型复杂度和召回率,最终选择5个聚类中心,得到5个先验框,发现其中中扁长的框较少,而瘦高的框更多,更符合行人特征。通过对比实验,发现用聚类分析得到的先验框比手动选择的先验框有更高的平均IOU值,这使得模型更容易训练学习。

VOC和COCO的聚类边界框尺寸。我们对边界框的维度进行k-means聚类,以获得我们模型的良好先验。左图显示了我们通过对k的各种选择得到的平均IOU。我们发现k=5给出了一个很好的召回率与模型复杂度的权衡。右图显示了VOC和COCO的相对中心。这两种先验都赞成更薄更高的边界框,而COCO比VOC在尺寸上有更大的变化。

(5)New Network(新的网络) 。 YOLOv2采用Darknet-19,其网络结构如下图所示,包括19个卷积层和5个maxpooling层,主要采用3 * 3卷积和1 * 1卷积,这里1 * 1卷积可以压缩特征图通道数以降低模型计算量和参数,每个卷积层后使用BN层以加快模型收敛同时防止过拟合。最终采用global avg pool 做预测。采用YOLOv2,模型的mAP值没有显著提升,但计算量减少了。

(6)直接定位预测(Direct location Prediction) 。 Faster R-CNN使用anchor boxes预测边界框相对先验框的偏移量,由于没有对偏移量进行约束,每个位置预测的边界框可以落在图片任何位置,会导致模型不稳定,加长训练时间。YOLOv2沿用YOLOv1的方法,根据所在网格单元的位置来预测坐标,则Ground Truth的值介于0到1之间。网络中将得到的网络预测结果再输入sigmoid函数中,让输出结果介于0到1之间。设一个网格相对于图片左上角的偏移量是 cx​,cy​。先验框的宽度和高度分别是 pw​和 ph​,则预测的边界框相对于特征图的中心坐标(bx​,by​)和宽高 bw​,bh​的计算公式如下图所示。

其中, σ 为 sigmoid 函数;tx​,ty​ 是 预测 的坐标偏移值(中心点坐标);tw​,th​ 是尺度缩放,分别经过 sigmoid ,输出0-1之间的偏移量,与 cx​,cy​ 相加后得到 boundingbox 中心点的位置。

(7)细粒度特征(Fine-Grained Features) 。 YOLOv2借鉴SSD使用多尺度的特征图做检测,提出pass through层将高分辨率的特征图与低分辨率的特征图联系在一起,从而实现多尺度检测。YOLOv2提取Darknet-19最后一个max pool层的输入,得到26 * 26 * 512的特征图。经过1 * 1 * 64的卷积以降低特征图的维度,得到26 * 26 * 64的特征图,然后经过pass through层的处理变成13 * 13 * 256的特征图(抽取原特征图每个2 * 2的局部区域组成新的channel,即原特征图大小降低4倍,channel增加4倍),再与13 * 13 * 1024大小的特征图连接,变成13 * 13 * 1280的特征图,最后在这些特征图上做预测。使用Fine-Grained Features,YOLOv2的性能提升了1%。

(8)多尺度训练(Multi-Scale Training) 。 YOLOv2中使用的Darknet-19网络结构中只有卷积层和池化层,所以其对输入图片的大小没有限制。YOLOv2采用多尺度输入的方式训练,在训练过程中每隔10个batches,重新随机选择输入图片的尺寸,由于Darknet-19下采样总步长为32,输入图片的尺寸一般选择32的倍数{320,352,…,608}(最小的选项是320×320,最大的是608×608。我们调整网络的尺寸并继续训练)。采用Multi-Scale Training,可以适应不同大小的图片输入,当采用低分辨率的图片输入时,mAP值略有下降,但速度更快,当采用高分辨率的图片输入时,能得到较高mAP值,但速度有所下降。

YOLOv2比先前的检测方法更快,更准确。它也可以以不同的分辨率运行,以便在速度和准确性之间进行简单折衷

② 训练过程
  • 第一阶段:现在ImageNet分类数据集上训练Darknet-19,此时模型输入为224 * 224,共训练160轮
  • 第二阶段:将网络输入调整为448 * 448,继续在ImageNet分类数据集上训练细调模型,共10轮,此时分类模型top-1准确率为76.5%,而top-5准确度为93.3%
  • 第三阶段:修改Darknet-19分类模型为检测模型,并在检测数据集上继续微调网络
③ 优点与缺点

(1)优点

  • YOLOv2使用了一个新的分类器作为特征提取部分,较多使用了3 * 3卷积核,在每次池化后操作后把通道数翻倍。网络使用了全局平均池化,把1 * 1卷积核置于3 * 3卷积核之间,用来压缩特征。也用了batch normalization稳定模型训练
  • 最终得出的基础模型就是Darknet-19,包含19个卷积层,5个最大池化层,运算次数55.8亿次,top-1图片分类准确率72.9%,top-5准确率91.2%
  • YOLOv2比VGG16更快,精度略低于VGG16

(2)缺点

  • YOLOv2检测准确率不够,比SSD稍差
  • 不擅长检测小物体
  • 对近距离物体准确率较低
3)YOLOv3(2018)

YOLOv3总结了自己在YOLOv2的基础上做的一些尝试性改进,有的尝试取得了成功,而有的尝试并没有提升模型性能。其中有两个值得一提的亮点,一个是使用残差模型,进一步加深了网络结构;另一个是使用FPN架构实现多尺度检测。

① 改进
  • 新网络结构:DarkNet-53;
  • 用逻辑回归替代softmax作为分类器;
  • 融合FPN(特征金字塔网络),实现多尺度检测。
② 多尺度预测

YOLOv3在基本特征提取器上添加几个卷积层,其中最后一个卷积层预测了一个三维张量——边界框,目标和类别预测。在COCO实验中,为每个尺度预测3个框,所以对于4个边界框偏移量,1个目标预测和80个类别预测,张量的大小为N×N×[3∗(4+1+80)]。接下来,从前面的2个层中取得特征图,并将其上采样2倍。

YOLOv3还从网络中的较前的层中获取特征图,并使用按元素相加的方式将其与上采样特征图进行合并。这种方法使得能够从上采样的特征图中获得更有意义的语义信息,同时可以从更前的层中获取更细粒度的信息。然后,再添加几个卷积层来处理这个组合的特征图,并最终预测出一个类似的张量,虽然其尺寸是之前的两倍。

最后,再次使用相同的设计来预测最终尺寸的边界框。因此,第三个尺寸的预测将既能从所有先前的计算,又能从网络前面的层中的细粒度的特征中获益。

③ 网络结构

YOLOv3在之前Darknet-19的基础上引入了残差块,并进一步加深了网络,改进后的网络有53个卷积层,取名为Darknet-53,网络结构如下图所示(以256 * 256的输入为例):

YOLOV3的模型结构图,让我们对YOLOV3可以更加直观的理解。

,

DBL:代码中的Darknetconv2d_BN_Leaky,是yolo_v3的基本组件。就是卷积+BN+Leaky relu。 resn:n代表数字,有res1,res2, … ,res8等等,表示这个res_block里含有多少个res_unit。不懂resnet请戳这儿 concat:张量拼接。将darknet中间层和后面的某一层的上采样进行拼接。拼接的操作和残差层add的操作是不一样的,拼接会扩充张量的维度,而add只是直接相加不会导致张量维度的改变。 从YOLOv1到YOLOv2再到YOLO9000、YOLOv3,YOLO经历三代变革,在保持速度优势的同时,不断改进网络结构,同时汲取其它优秀的目标检测算法的各种trick,先后引入anchor box机制、引入FPN实现多尺度检测等。

不同backbone的各种网络在准确度,billions of operations billion floating point operations per second和FPS上的比较

每个网络都使用相同的设置进行训练,并在256×256的图像上进行单精度测试。 运行时间是在Titan X上用256×256图像进行测量的。因此,Darknet-53可与最先进的分类器相媲美,但浮点运算更少,速度更快。 Darknet-53比ResNet-101更好,且速度快1.5倍。 Darknet-53与ResNet-152具有相似的性能,但速度快2倍。

Darknet-53也实现了最高的每秒浮点运算测量。 这意味着网络结构可以更好地利用GPU,使它的评测更加高效,更快。 这主要是因为ResNet的层数太多,效率不高。

④ 效果

(1)兼顾速度与准确率。在COCO数据机上,mAP指标与SSD模型相当,但速度提高了3倍;mAP指标比RetinaNet模型差些,但速度要高3.8倍。

(2)小目标检测有所提升,但中等和更大尺寸的物体上的表现相对较差。

当然,YOLOv3也有些失败的尝试,并未起到有效作用,请自行查阅原始论文。

附录:术语表

英文全写英文简写中文名称
one stage一阶段检测
two stage两阶段检测
region proposalRP候选区域(一个有可能包含待检物体的预选框)
bounding boxesbb候选框
Non-Maximum SuppressionNMS非极大值抑制
selective searchSS选择搜索
Regions with CNN featuresR-CNN
region of interestRoI感兴趣区域(候选区域)
You Only Look OnceYOLO
frame per secondfps帧每秒
High Resolution Classifier高分辨率分类器
Batch NormalizationBN批量正则化
Mean Average PrecisionmAP平均精度均值
Intersection over UnionIOU交并比(“预测的边框” 和 “真实的边框” 的交集和并集的比值)
Fine-Grained Features细粒度特征
Feature Pyramid NetworkFPN特征金字塔网络

编程要求

代码文件介绍:

  • mbjc
    • train.py 模型训练文件
    • yolo3_predict.py 模型预测文件
    • yolov3
      • model.py 模型结构文件
      • utils.py 图像处理文件

完成基于 YOLO V3 的目标检测的模型代码实现; 根据提示,在右侧编辑器 Begin-End 区间补充代码。 提示: 上面文章所提到的 YOLO V3 的模型结构图:

,

测试说明

平台会对调用你编写的模型判断正确性,然后再进行预测。


开始你的任务吧,祝你成功!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/凡人多烦事01/article/detail/490646
推荐阅读
相关标签
  

闽ICP备14008679号