model_260.py 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656
  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, Input
  6. # keras2.6.0 and keras2.1.5
  7. # from keras.engine import Layer
  8. from keras.layers import Conv2D, Add, ZeroPadding2D, UpSampling2D, Concatenate, MaxPooling2D, Dense, \
  9. GlobalAveragePooling2D, Multiply, Lambda, Layer
  10. from keras.layers.advanced_activations import LeakyReLU
  11. # keras2.6.0 and keras2.1.5
  12. # from keras.layers.normalization import BatchNormalization
  13. from tensorflow.keras.layers import BatchNormalization
  14. from keras.models import Model
  15. from keras.regularizers import l2
  16. from utils import compose
  17. def yolo_net(input_shape, anchors, num_classes, load_pretrained=True,
  18. weights_path='models/tiny_yolo_weights.h5'):
  19. """create the training model, for Tiny YOLOv3"""
  20. # get a new session
  21. # ops.reset_default_graph()
  22. K.clear_session()
  23. image_input = Input(shape=(None, None, 1))
  24. h, w = input_shape
  25. num_anchors = len(anchors)
  26. y_true = [Input(shape=(h//{0: 32, 1: 16}[l], w//{0: 32, 1: 16}[l],
  27. num_anchors//2, num_classes+5)) for l in range(2)]
  28. model_body = tiny_yolo_body(image_input, num_anchors//2, num_classes)
  29. print('Create Tiny YOLOv3 model with {} anchors and {} classes.'.format(num_anchors, num_classes))
  30. if load_pretrained:
  31. model_body.load_weights(weights_path)
  32. print('Load weights {}.'.format(weights_path))
  33. model_loss = Lambda(yolo_loss, output_shape=(1,), name='yolo_loss',
  34. arguments={'anchors': anchors, 'num_classes': num_classes, 'ignore_thresh': .5})(
  35. [*model_body.output, *y_true])
  36. model = Model([model_body.input, *y_true], model_loss)
  37. # model.summary(120)
  38. return model
  39. @wraps(Conv2D)
  40. def DarknetConv2D(*args, **kwargs):
  41. """Wrapper to set Darknet parameters for Convolution2D."""
  42. darknet_conv_kwargs = {'kernel_regularizer': l2(5e-4)}
  43. darknet_conv_kwargs['padding'] = 'valid' if kwargs.get('strides')==(2,2) else 'same'
  44. darknet_conv_kwargs.update(kwargs)
  45. return Conv2D(*args, **darknet_conv_kwargs)
  46. def DarknetConv2D_BN_Leaky(*args, **kwargs):
  47. """Darknet Convolution2D followed by BatchNormalization and LeakyReLU."""
  48. no_bias_kwargs = {'use_bias': False}
  49. no_bias_kwargs.update(kwargs)
  50. return compose(
  51. DarknetConv2D(*args, **no_bias_kwargs),
  52. BatchNormalization(),
  53. LeakyReLU(alpha=0.1))
  54. def resblock_body(x, num_filters, num_blocks):
  55. '''A series of resblocks starting with a downsampling Convolution2D'''
  56. # Darknet uses left and top padding instead of 'same' mode
  57. x = ZeroPadding2D(((1,0),(1,0)))(x)
  58. x = DarknetConv2D_BN_Leaky(num_filters, (3,3), strides=(2,2))(x)
  59. for i in range(num_blocks):
  60. y = compose(
  61. DarknetConv2D_BN_Leaky(num_filters//2, (1,1)),
  62. DarknetConv2D_BN_Leaky(num_filters, (3,3)))(x)
  63. x = Add()([x,y])
  64. return x
  65. class SeBlock(Layer):
  66. def __init__(self, reduction=4, **kwargs):
  67. super(SeBlock, self).__init__(**kwargs)
  68. self.reduction = reduction
  69. def build(self, input_shape):
  70. # 构建layer时需要实现
  71. # 手动将该自定义层参数加入,否则参数为0
  72. self.pool = GlobalAveragePooling2D(name="my_pool")
  73. self.expand_1 = Lambda(lambda x: K.expand_dims(x, axis=1))
  74. self.expand_2 = Lambda(lambda x: K.expand_dims(x, axis=1))
  75. self.dense_1 = Dense(int(input_shape[-1]) // self.reduction, use_bias=False, activation="relu", name='my_dense_1')
  76. self.dense_2 = Dense(int(input_shape[-1]), use_bias=False, activation="hard_sigmoid", name='my_dense_2')
  77. # keras2.2.0以下需要单独加,keras2.6.0不用加
  78. # self.dense_1.build((input_shape[0], 1, 1, input_shape[-1]))
  79. # self.dense_2.build((input_shape[0], 1, 1, input_shape[-1] // self.reduction))
  80. self._trainable_weights += self.dense_1._trainable_weights
  81. self._trainable_weights += self.dense_2._trainable_weights
  82. super(SeBlock, self).build(input_shape)
  83. def call(self, inputs):
  84. x = self.pool(inputs)
  85. x = self.expand_1(x)
  86. x = self.expand_2(x)
  87. x = self.dense_1(x)
  88. x = self.dense_2(x)
  89. # 给通道加权重
  90. return Multiply()([inputs, x])
  91. def darknet_body(x):
  92. '''Darknent body having 52 Convolution2D layers'''
  93. x = DarknetConv2D_BN_Leaky(32, (3,3))(x)
  94. x = resblock_body(x, 64, 1)
  95. x = resblock_body(x, 128, 2)
  96. x = resblock_body(x, 256, 8)
  97. x = resblock_body(x, 512, 8)
  98. x = resblock_body(x, 1024, 4)
  99. return x
  100. def make_last_layers(x, num_filters, out_filters):
  101. '''6 Conv2D_BN_Leaky layers followed by a Conv2D_linear layer'''
  102. x = compose(
  103. DarknetConv2D_BN_Leaky(num_filters, (1,1)),
  104. DarknetConv2D_BN_Leaky(num_filters*2, (3,3)),
  105. DarknetConv2D_BN_Leaky(num_filters, (1,1)),
  106. DarknetConv2D_BN_Leaky(num_filters*2, (3,3)),
  107. DarknetConv2D_BN_Leaky(num_filters, (1,1)))(x)
  108. y = compose(
  109. DarknetConv2D_BN_Leaky(num_filters*2, (3,3)),
  110. DarknetConv2D(out_filters, (1,1)))(x)
  111. return x, y
  112. def yolo_body(inputs, num_anchors, num_classes):
  113. """Create YOLO_V3 model CNN body in Keras."""
  114. darknet = Model(inputs, darknet_body(inputs))
  115. x, y1 = make_last_layers(darknet.output, 512, num_anchors*(num_classes+5))
  116. x = compose(
  117. DarknetConv2D_BN_Leaky(256, (1,1)),
  118. UpSampling2D(2))(x)
  119. x = Concatenate()([x,darknet.layers[152].output])
  120. x, y2 = make_last_layers(x, 256, num_anchors*(num_classes+5))
  121. x = compose(
  122. DarknetConv2D_BN_Leaky(128, (1,1)),
  123. UpSampling2D(2))(x)
  124. x = Concatenate()([x,darknet.layers[92].output])
  125. x, y3 = make_last_layers(x, 128, num_anchors*(num_classes+5))
  126. return Model(inputs, [y1,y2,y3])
  127. def tiny_yolo_body(inputs, num_anchors, num_classes):
  128. """Create Tiny YOLO_v3 model CNN body in keras."""
  129. x1 = compose(
  130. DarknetConv2D_BN_Leaky(16, (3,3)),
  131. MaxPooling2D(pool_size=(2,2), strides=(2,2), padding='same'),
  132. DarknetConv2D_BN_Leaky(32, (3,3)),
  133. MaxPooling2D(pool_size=(2,2), strides=(2,2), padding='same'),
  134. DarknetConv2D_BN_Leaky(64, (3,3)),
  135. MaxPooling2D(pool_size=(2,2), strides=(2,2), padding='same'),
  136. DarknetConv2D_BN_Leaky(128, (3,3)),
  137. MaxPooling2D(pool_size=(2,2), strides=(2,2), padding='same'),
  138. DarknetConv2D_BN_Leaky(256, (3,3)))(inputs)
  139. x2 = compose(
  140. MaxPooling2D(pool_size=(2,2), strides=(2,2), padding='same'),
  141. DarknetConv2D_BN_Leaky(512, (3,3)),
  142. MaxPooling2D(pool_size=(2,2), strides=(1,1), padding='same'),
  143. DarknetConv2D_BN_Leaky(1024, (3,3)),
  144. DarknetConv2D_BN_Leaky(256, (1,1)))(x1)
  145. y1 = compose(
  146. DarknetConv2D_BN_Leaky(512, (3,3)),
  147. DarknetConv2D(num_anchors*(num_classes+5), (1,1)))(x2)
  148. x2 = compose(
  149. DarknetConv2D_BN_Leaky(128, (1,1)),
  150. UpSampling2D(2))(x2)
  151. y2 = compose(
  152. Concatenate(),
  153. DarknetConv2D_BN_Leaky(256, (3,3)),
  154. DarknetConv2D(num_anchors*(num_classes+5), (1,1)))([x2,x1])
  155. model = Model(inputs, [y1,y2])
  156. # model.summary(120)
  157. return model
  158. def tiny_yolo_se_body(inputs, num_anchors, num_classes):
  159. """Create Tiny YOLO_v3 model CNN body in keras."""
  160. x1 = compose(
  161. DarknetConv2D_BN_Leaky(16, (3, 3)),
  162. MaxPooling2D(pool_size=(2, 2), strides=(2, 2), padding='same'),
  163. DarknetConv2D_BN_Leaky(32, (3, 3)),
  164. MaxPooling2D(pool_size=(2, 2), strides=(2, 2), padding='same'),
  165. DarknetConv2D_BN_Leaky(64, (3, 3)),
  166. MaxPooling2D(pool_size=(2, 2), strides=(2, 2), padding='same'),
  167. DarknetConv2D_BN_Leaky(128, (3, 3)),
  168. MaxPooling2D(pool_size=(2, 2), strides=(2, 2), padding='same'),
  169. DarknetConv2D_BN_Leaky(256, (3, 3)),
  170. )(inputs)
  171. x1 = SeBlock()(x1)
  172. x2 = compose(
  173. MaxPooling2D(pool_size=(2, 2), strides=(2, 2), padding='same'),
  174. DarknetConv2D_BN_Leaky(512, (3, 3)),
  175. MaxPooling2D(pool_size=(2, 2), strides=(1, 1), padding='same'),
  176. DarknetConv2D_BN_Leaky(1024, (3, 3)),
  177. DarknetConv2D_BN_Leaky(256, (1, 1)),
  178. )(x1)
  179. x2 = SeBlock()(x2)
  180. y1 = compose(
  181. DarknetConv2D_BN_Leaky(512, (3, 3)),
  182. DarknetConv2D(num_anchors*(num_classes+5), (1, 1))
  183. )(x2)
  184. y1 = SeBlock()(y1)
  185. x2 = compose(
  186. DarknetConv2D_BN_Leaky(128, (1, 1)),
  187. UpSampling2D(2)
  188. )(x2)
  189. x2 = SeBlock()(x2)
  190. y2 = compose(
  191. Concatenate(),
  192. DarknetConv2D_BN_Leaky(256, (3, 3)),
  193. DarknetConv2D(num_anchors*(num_classes+5), (1, 1))
  194. )([x2, x1])
  195. y2 = SeBlock()(y2)
  196. model = Model(inputs, [y1, y2])
  197. model.summary(120)
  198. return model
  199. def tinier_yolo_se_body(inputs, num_anchors, num_classes):
  200. """Create Tiny YOLO_v3 model CNN body in keras."""
  201. x1 = compose(
  202. DarknetConv2D_BN_Leaky(8, (3, 3)),
  203. MaxPooling2D(pool_size=(2, 2), strides=(2, 2), padding='same'),
  204. DarknetConv2D_BN_Leaky(16, (3, 3)),
  205. MaxPooling2D(pool_size=(2, 2), strides=(2, 2), padding='same'),
  206. DarknetConv2D_BN_Leaky(32, (3, 3)),
  207. MaxPooling2D(pool_size=(2, 2), strides=(2, 2), padding='same'),
  208. DarknetConv2D_BN_Leaky(64, (3, 3)),
  209. MaxPooling2D(pool_size=(2, 2), strides=(2, 2), padding='same'),
  210. DarknetConv2D_BN_Leaky(128, (3, 3)),
  211. )(inputs)
  212. x1 = SeBlock()(x1)
  213. x2 = compose(
  214. MaxPooling2D(pool_size=(2, 2), strides=(2, 2), padding='same'),
  215. DarknetConv2D_BN_Leaky(256, (3, 3)),
  216. MaxPooling2D(pool_size=(2, 2), strides=(1, 1), padding='same'),
  217. DarknetConv2D_BN_Leaky(512, (3, 3)),
  218. DarknetConv2D_BN_Leaky(128, (1, 1)),
  219. )(x1)
  220. x2 = SeBlock()(x2)
  221. y1 = compose(
  222. DarknetConv2D_BN_Leaky(256, (3, 3)),
  223. DarknetConv2D(num_anchors*(num_classes+5), (1, 1))
  224. )(x2)
  225. y1 = SeBlock()(y1)
  226. x2 = compose(
  227. DarknetConv2D_BN_Leaky(64, (1, 1)),
  228. UpSampling2D(2)
  229. )(x2)
  230. x2 = SeBlock()(x2)
  231. y2 = compose(
  232. Concatenate(),
  233. DarknetConv2D_BN_Leaky(128, (3, 3)),
  234. DarknetConv2D(num_anchors*(num_classes+5), (1, 1))
  235. )([x2, x1])
  236. y2 = SeBlock()(y2)
  237. model = Model(inputs, [y1, y2])
  238. model.summary(120)
  239. return model
  240. def yolo_head(feats, anchors, num_classes, input_shape, calc_loss=False):
  241. """Convert final layer features to bounding box parameters."""
  242. num_anchors = len(anchors)
  243. # Reshape to batch, height, width, num_anchors, box_params.
  244. anchors_tensor = K.reshape(K.constant(anchors), [1, 1, 1, num_anchors, 2])
  245. grid_shape = K.shape(feats)[1:3] # height, width
  246. grid_y = K.tile(K.reshape(K.arange(0, stop=grid_shape[0]), [-1, 1, 1, 1]),
  247. [1, grid_shape[1], 1, 1])
  248. grid_x = K.tile(K.reshape(K.arange(0, stop=grid_shape[1]), [1, -1, 1, 1]),
  249. [grid_shape[0], 1, 1, 1])
  250. grid = K.concatenate([grid_x, grid_y])
  251. grid = K.cast(grid, K.dtype(feats))
  252. feats = K.reshape(
  253. feats, [-1, grid_shape[0], grid_shape[1], num_anchors, num_classes + 5])
  254. # Adjust preditions to each spatial grid point and anchor size.
  255. box_xy = (K.sigmoid(feats[..., :2]) + grid) / K.cast(grid_shape[::-1], K.dtype(feats))
  256. box_wh = K.exp(feats[..., 2:4]) * anchors_tensor / K.cast(input_shape[::-1], K.dtype(feats))
  257. box_confidence = K.sigmoid(feats[..., 4:5])
  258. box_class_probs = K.sigmoid(feats[..., 5:])
  259. if calc_loss == True:
  260. return grid, feats, box_xy, box_wh
  261. return box_xy, box_wh, box_confidence, box_class_probs
  262. def yolo_correct_boxes(box_xy, box_wh, input_shape, image_shape):
  263. '''Get corrected boxes'''
  264. box_yx = box_xy[..., ::-1]
  265. box_hw = box_wh[..., ::-1]
  266. input_shape = K.cast(input_shape, K.dtype(box_yx))
  267. image_shape = K.cast(image_shape, K.dtype(box_yx))
  268. new_shape = K.round(image_shape * K.min(input_shape/image_shape))
  269. offset = (input_shape-new_shape)/2./input_shape
  270. scale = input_shape/new_shape
  271. box_yx = (box_yx - offset) * scale
  272. box_hw *= scale
  273. box_mins = box_yx - (box_hw / 2.)
  274. box_maxes = box_yx + (box_hw / 2.)
  275. boxes = K.concatenate([
  276. box_mins[..., 0:1], # y_min
  277. box_mins[..., 1:2], # x_min
  278. box_maxes[..., 0:1], # y_max
  279. box_maxes[..., 1:2] # x_max
  280. ])
  281. # Scale boxes back to original image shape.
  282. boxes *= K.concatenate([image_shape, image_shape])
  283. return boxes
  284. def yolo_boxes_and_scores(feats, anchors, num_classes, input_shape, image_shape):
  285. '''Process Conv layer output'''
  286. box_xy, box_wh, box_confidence, box_class_probs = yolo_head(feats,
  287. anchors, num_classes, input_shape)
  288. boxes = yolo_correct_boxes(box_xy, box_wh, input_shape, image_shape)
  289. boxes = K.reshape(boxes, [-1, 4])
  290. box_scores = box_confidence * box_class_probs
  291. box_scores = K.reshape(box_scores, [-1, num_classes])
  292. return boxes, box_scores
  293. def yolo_eval(yolo_outputs,
  294. anchors,
  295. num_classes,
  296. image_shape,
  297. max_boxes=20,
  298. score_threshold=.6,
  299. iou_threshold=.5):
  300. """Evaluate YOLO model on given input and return filtered boxes."""
  301. num_layers = len(yolo_outputs)
  302. print("yolo_outputs", yolo_outputs[0])
  303. print("num_layers", num_layers)
  304. print("image_shape", image_shape)
  305. anchor_mask = [[6,7,8], [3,4,5], [0,1,2]] if num_layers==3 else [[3,4,5], [1,2,3]] # default setting
  306. input_shape = K.shape(yolo_outputs[0])[1:3] * 32
  307. boxes = []
  308. box_scores = []
  309. for l in range(num_layers):
  310. _boxes, _box_scores = yolo_boxes_and_scores(yolo_outputs[l],
  311. anchors[anchor_mask[l]], num_classes, input_shape, image_shape)
  312. boxes.append(_boxes)
  313. box_scores.append(_box_scores)
  314. boxes = K.concatenate(boxes, axis=0)
  315. box_scores = K.concatenate(box_scores, axis=0)
  316. mask = box_scores >= score_threshold
  317. max_boxes_tensor = K.constant(max_boxes, dtype='int32')
  318. boxes_ = []
  319. scores_ = []
  320. classes_ = []
  321. for c in range(num_classes):
  322. # TODO: use keras backend instead of tf.
  323. class_boxes = tf.boolean_mask(boxes, mask[:, c])
  324. class_box_scores = tf.boolean_mask(box_scores[:, c], mask[:, c])
  325. nms_index = tf.image.non_max_suppression(
  326. class_boxes, class_box_scores, max_boxes_tensor, iou_threshold=iou_threshold)
  327. class_boxes = K.gather(class_boxes, nms_index)
  328. class_box_scores = K.gather(class_box_scores, nms_index)
  329. classes = K.ones_like(class_box_scores, 'int32') * c
  330. boxes_.append(class_boxes)
  331. scores_.append(class_box_scores)
  332. classes_.append(classes)
  333. boxes_ = K.concatenate(boxes_, axis=0)
  334. scores_ = K.concatenate(scores_, axis=0)
  335. classes_ = K.concatenate(classes_, axis=0)
  336. return boxes_, scores_, classes_
  337. def preprocess_true_boxes(true_boxes, input_shape, anchors, num_classes):
  338. '''Preprocess true boxes to training input format
  339. Parameters
  340. ----------
  341. true_boxes: array, shape=(m, T, 5)
  342. Absolute x_min, y_min, x_max, y_max, class_id relative to input_shape.
  343. input_shape: array-like, hw, multiples of 32
  344. anchors: array, shape=(N, 2), wh
  345. num_classes: integer
  346. Returns
  347. -------
  348. y_true: list of array, shape like yolo_outputs, xywh are reletive value
  349. '''
  350. assert (true_boxes[..., 4]<num_classes).all(), 'class id must be less than num_classes'
  351. num_layers = len(anchors)//3 # default setting
  352. anchor_mask = [[6,7,8], [3,4,5], [0,1,2]] if num_layers==3 else [[3,4,5], [1,2,3]]
  353. true_boxes = np.array(true_boxes, dtype='float32')
  354. input_shape = np.array(input_shape, dtype='int32')
  355. boxes_xy = (true_boxes[..., 0:2] + true_boxes[..., 2:4]) // 2
  356. boxes_wh = true_boxes[..., 2:4] - true_boxes[..., 0:2]
  357. true_boxes[..., 0:2] = boxes_xy/input_shape[::-1]
  358. true_boxes[..., 2:4] = boxes_wh/input_shape[::-1]
  359. m = true_boxes.shape[0]
  360. grid_shapes = [input_shape//{0:32, 1:16, 2:8}[l] for l in range(num_layers)]
  361. y_true = [np.zeros((m,grid_shapes[l][0],grid_shapes[l][1],len(anchor_mask[l]),5+num_classes),
  362. dtype='float32') for l in range(num_layers)]
  363. # Expand dim to apply broadcasting.
  364. anchors = np.expand_dims(anchors, 0)
  365. anchor_maxes = anchors / 2.
  366. anchor_mins = -anchor_maxes
  367. valid_mask = boxes_wh[..., 0]>0
  368. for b in range(m):
  369. # Discard zero rows.
  370. wh = boxes_wh[b, valid_mask[b]]
  371. if len(wh)==0: continue
  372. # Expand dim to apply broadcasting.
  373. wh = np.expand_dims(wh, -2)
  374. box_maxes = wh / 2.
  375. box_mins = -box_maxes
  376. intersect_mins = np.maximum(box_mins, anchor_mins)
  377. intersect_maxes = np.minimum(box_maxes, anchor_maxes)
  378. intersect_wh = np.maximum(intersect_maxes - intersect_mins, 0.)
  379. intersect_area = intersect_wh[..., 0] * intersect_wh[..., 1]
  380. box_area = wh[..., 0] * wh[..., 1]
  381. anchor_area = anchors[..., 0] * anchors[..., 1]
  382. iou = intersect_area / (box_area + anchor_area - intersect_area)
  383. # Find best anchor for each true box
  384. best_anchor = np.argmax(iou, axis=-1)
  385. for t, n in enumerate(best_anchor):
  386. for l in range(num_layers):
  387. if n in anchor_mask[l]:
  388. i = np.floor(true_boxes[b,t,0]*grid_shapes[l][1]).astype('int32')
  389. j = np.floor(true_boxes[b,t,1]*grid_shapes[l][0]).astype('int32')
  390. k = anchor_mask[l].index(n)
  391. c = true_boxes[b,t, 4].astype('int32')
  392. y_true[l][b, j, i, k, 0:4] = true_boxes[b,t, 0:4]
  393. y_true[l][b, j, i, k, 4] = 1
  394. y_true[l][b, j, i, k, 5+c] = 1
  395. return y_true
  396. def box_iou(b1, b2):
  397. '''Return iou tensor
  398. Parameters
  399. ----------
  400. b1: tensor, shape=(i1,...,iN, 4), xywh
  401. b2: tensor, shape=(j, 4), xywh
  402. Returns
  403. -------
  404. iou: tensor, shape=(i1,...,iN, j)
  405. '''
  406. # Expand dim to apply broadcasting.
  407. b1 = K.expand_dims(b1, -2)
  408. b1_xy = b1[..., :2]
  409. b1_wh = b1[..., 2:4]
  410. b1_wh_half = b1_wh/2.
  411. b1_mins = b1_xy - b1_wh_half
  412. b1_maxes = b1_xy + b1_wh_half
  413. # Expand dim to apply broadcasting.
  414. b2 = K.expand_dims(b2, 0)
  415. b2_xy = b2[..., :2]
  416. b2_wh = b2[..., 2:4]
  417. b2_wh_half = b2_wh/2.
  418. b2_mins = b2_xy - b2_wh_half
  419. b2_maxes = b2_xy + b2_wh_half
  420. intersect_mins = K.maximum(b1_mins, b2_mins)
  421. intersect_maxes = K.minimum(b1_maxes, b2_maxes)
  422. intersect_wh = K.maximum(intersect_maxes - intersect_mins, 0.)
  423. intersect_area = intersect_wh[..., 0] * intersect_wh[..., 1]
  424. b1_area = b1_wh[..., 0] * b1_wh[..., 1]
  425. b2_area = b2_wh[..., 0] * b2_wh[..., 1]
  426. iou = intersect_area / (b1_area + b2_area - intersect_area)
  427. return iou
  428. def yolo_loss(args, anchors, num_classes, ignore_thresh=.5, print_loss=False):
  429. """Return yolo_loss tensor
  430. Parameters
  431. ----------
  432. yolo_outputs: list of tensor, the output of yolo_body or tiny_yolo_body
  433. y_true: list of array, the output of preprocess_true_boxes
  434. anchors: array, shape=(N, 2), wh
  435. num_classes: integer
  436. ignore_thresh: float, the iou threshold whether to ignore object confidence loss
  437. Returns
  438. -------
  439. loss: tensor, shape=(1,)
  440. """
  441. num_layers = len(anchors)//3 # default setting
  442. yolo_outputs = args[:num_layers]
  443. y_true = args[num_layers:]
  444. anchor_mask = [[6,7,8], [3,4,5], [0,1,2]] if num_layers==3 else [[3,4,5], [1,2,3]]
  445. input_shape = K.cast(K.shape(yolo_outputs[0])[1:3] * 32, K.dtype(y_true[0]))
  446. grid_shapes = [K.cast(K.shape(yolo_outputs[l])[1:3], K.dtype(y_true[0])) for l in range(num_layers)]
  447. loss = 0
  448. m = K.shape(yolo_outputs[0])[0] # batch size, tensor
  449. mf = K.cast(m, K.dtype(yolo_outputs[0]))
  450. for l in range(num_layers):
  451. object_mask = y_true[l][..., 4:5]
  452. true_class_probs = y_true[l][..., 5:]
  453. grid, raw_pred, pred_xy, pred_wh = yolo_head(yolo_outputs[l],
  454. anchors[anchor_mask[l]], num_classes, input_shape, calc_loss=True)
  455. pred_box = K.concatenate([pred_xy, pred_wh])
  456. # Darknet raw box to calculate loss.
  457. raw_true_xy = y_true[l][..., :2]*grid_shapes[l][::-1] - grid
  458. raw_true_wh = K.log(y_true[l][..., 2:4] / anchors[anchor_mask[l]] * input_shape[::-1])
  459. raw_true_wh = K.switch(object_mask, raw_true_wh, K.zeros_like(raw_true_wh)) # avoid log(0)=-inf
  460. box_loss_scale = 2 - y_true[l][...,2:3]*y_true[l][...,3:4]
  461. # Find ignore mask, iterate over each of batch.
  462. ignore_mask = tf.TensorArray(K.dtype(y_true[0]), size=1, dynamic_size=True)
  463. object_mask_bool = K.cast(object_mask, 'bool')
  464. def loop_body(b, ignore_mask):
  465. true_box = tf.boolean_mask(y_true[l][b,...,0:4], object_mask_bool[b,...,0])
  466. iou = box_iou(pred_box[b], true_box)
  467. best_iou = K.max(iou, axis=-1)
  468. ignore_mask = ignore_mask.write(b, K.cast(best_iou<ignore_thresh, K.dtype(true_box)))
  469. return b+1, ignore_mask
  470. # keras2.6.0 and keras2.1.5
  471. # _, ignore_mask = K.control_flow_ops.while_loop(lambda b,*args: b<m, loop_body, [0, ignore_mask])
  472. _, ignore_mask = tf.while_loop(lambda b,*args: b<m, loop_body, [0, ignore_mask])
  473. ignore_mask = ignore_mask.stack()
  474. ignore_mask = K.expand_dims(ignore_mask, -1)
  475. # K.binary_crossentropy is helpful to avoid exp overflow.
  476. xy_loss = object_mask * box_loss_scale * K.binary_crossentropy(raw_true_xy, raw_pred[...,0:2], from_logits=True)
  477. wh_loss = object_mask * box_loss_scale * 0.5 * K.square(raw_true_wh-raw_pred[...,2:4])
  478. confidence_loss = object_mask * K.binary_crossentropy(object_mask, raw_pred[...,4:5], from_logits=True)+ \
  479. (1-object_mask) * K.binary_crossentropy(object_mask, raw_pred[...,4:5], from_logits=True) * ignore_mask
  480. class_loss = object_mask * K.binary_crossentropy(true_class_probs, raw_pred[...,5:], from_logits=True)
  481. xy_loss = K.sum(xy_loss) / mf
  482. wh_loss = K.sum(wh_loss) / mf
  483. confidence_loss = K.sum(confidence_loss) / mf
  484. class_loss = K.sum(class_loss) / mf
  485. loss += xy_loss + wh_loss + confidence_loss + class_loss
  486. # loss += (xy_loss + wh_loss + confidence_loss) * 2
  487. # loss += xy_loss + confidence_loss + 2*wh_loss
  488. # loss += xy_loss * 10 + wh_loss * 10 + confidence_loss
  489. if print_loss:
  490. loss = tf.Print(loss, [loss, xy_loss, wh_loss, confidence_loss, class_loss, K.sum(ignore_mask)], message='loss: ')
  491. return loss
  492. def new_yolo_loss(input_shape, anchors, num_classes, ignore_thresh=.8, print_loss=False):
  493. """Return yolo_loss tensor
  494. """
  495. def yolo_loss_fixed(y_true, y_pred):
  496. num_layers = len(anchors)//3 # default setting
  497. yolo_outputs = y_pred
  498. print("y_true.shape", y_true.shape)
  499. print("y_pred.shape", y_pred.shape)
  500. anchor_mask = [[6, 7, 8], [3, 4, 5], [0, 1, 2]] if num_layers == 3 else [[3, 4, 5], [1, 2, 3]]
  501. input_shape = K.cast(K.shape(yolo_outputs[0])[1:3] * 32, K.dtype(y_true[0]))
  502. grid_shapes = [K.cast(K.shape(yolo_outputs[l])[1:3], K.dtype(y_true[0])) for l in range(num_layers)]
  503. loss = 0
  504. m = K.shape(yolo_outputs[0])[0] # batch size, tensor
  505. mf = K.cast(m, K.dtype(yolo_outputs[0]))
  506. for l in range(num_layers):
  507. object_mask = y_true[l][..., 4:5]
  508. true_class_probs = y_true[l][..., 5:]
  509. grid, raw_pred, pred_xy, pred_wh = yolo_head(yolo_outputs[l],
  510. anchors[anchor_mask[l]], num_classes, input_shape, calc_loss=True)
  511. pred_box = K.concatenate([pred_xy, pred_wh])
  512. # Darknet raw box to calculate loss.
  513. raw_true_xy = y_true[l][..., :2]*grid_shapes[l][::-1] - grid
  514. raw_true_wh = K.log(y_true[l][..., 2:4] / anchors[anchor_mask[l]] * input_shape[::-1])
  515. raw_true_wh = K.switch(object_mask, raw_true_wh, K.zeros_like(raw_true_wh)) # avoid log(0)=-inf
  516. box_loss_scale = 2 - y_true[l][...,2:3]*y_true[l][...,3:4]
  517. # Find ignore mask, iterate over each of batch.
  518. ignore_mask = tf.TensorArray(K.dtype(y_true[0]), size=1, dynamic_size=True)
  519. object_mask_bool = K.cast(object_mask, 'bool')
  520. def loop_body(b, ignore_mask):
  521. true_box = tf.boolean_mask(y_true[l][b,...,0:4], object_mask_bool[b,...,0])
  522. iou = box_iou(pred_box[b], true_box)
  523. best_iou = K.max(iou, axis=-1)
  524. ignore_mask = ignore_mask.write(b, K.cast(best_iou<ignore_thresh, K.dtype(true_box)))
  525. return b+1, ignore_mask
  526. _, ignore_mask = K.control_flow_ops.while_loop(lambda b,*args: b<m, loop_body, [0, ignore_mask])
  527. ignore_mask = ignore_mask.stack()
  528. ignore_mask = K.expand_dims(ignore_mask, -1)
  529. # K.binary_crossentropy is helpful to avoid exp overflow.
  530. xy_loss = object_mask * box_loss_scale * K.binary_crossentropy(raw_true_xy, raw_pred[...,0:2], from_logits=True)
  531. wh_loss = object_mask * box_loss_scale * 0.5 * K.square(raw_true_wh-raw_pred[...,2:4])
  532. confidence_loss = object_mask * K.binary_crossentropy(object_mask, raw_pred[...,4:5], from_logits=True)+ \
  533. (1-object_mask) * K.binary_crossentropy(object_mask, raw_pred[...,4:5], from_logits=True) * ignore_mask
  534. class_loss = object_mask * K.binary_crossentropy(true_class_probs, raw_pred[...,5:], from_logits=True)
  535. xy_loss = K.sum(xy_loss) / mf
  536. wh_loss = K.sum(wh_loss) / mf
  537. confidence_loss = K.sum(confidence_loss) / mf
  538. class_loss = K.sum(class_loss) / mf
  539. # loss += xy_loss + wh_loss + confidence_loss + class_loss
  540. loss += (xy_loss + wh_loss + confidence_loss) * 2
  541. if print_loss:
  542. loss = tf.Print(loss, [loss, xy_loss, wh_loss, confidence_loss, class_loss, K.sum(ignore_mask)], message='loss: ')
  543. return loss
  544. # h, w = input_shape
  545. # y_true = [Input(shape=(h//{0: 32, 1: 16}[l], w//{0: 32, 1: 16}[l],
  546. # len(anchors)//2, num_classes+5)) for l in range(2)]
  547. return yolo_loss_fixed