YOLO V1,V2,V3

YOLO V1
yolo将输入图像划分为 S×S 的网格,物体的中心落在哪一个网格内,这个网格就负责预测该物体的置信度,类别以及位置。yolov1全连接层之后通过reshape得到S×S×30 的数据块。每张图像的目标label,编码出一个 S×S×30的数据块,然后让卷积网络去拟合这个targe。
confidence损失:负责检测的box的label是在线计算的IOU,不负责和无目标的都为0,为了平衡,加入λnoobj
位置损失:容易理解,负责检测的才有位置损失,小目标对于预测wh的误差更敏感,用开根的方法缓解。
类别损失:容易理解,含有目标的网格才有类别损失。默认网格只出现一种类别,这当然是有缺陷的。
预测
网络的输出是 S×S×30的数据块,首先用一个阈值选取含有目标的box,再用一个阈值筛选,最后由NMS去除一些重叠的框。
NMS是大部分深度学习目标检测网络所需要的,大致算法流程为:
1.对所有预测框的置信度降序排序
2.选出置信度最高的预测框,确认其为正确预测(下次就没有他了,已经被确认了),并计算他与其他预测框的IOU.
3.根据2中计算的IOU去除重叠度高的,IOU>threshold就删除。
4.剩下的预测框返回第1步,直到没有剩下的为止
具体的:
假设有6个框,根据confidence排序,从大到小为A.B.C.D.E.F
1.先从最大的F,分别判断A~E和F的重叠程度IOU是否大于阈值
2.假设B、D与F的重叠度超过阈值,就扔掉B、D,标记F
3.从生下的A、C、E中选择confidence最大的。假设最大的为E,就分别判断A与E 的IOU和C与E的IOU值。若大于阈值就扔掉,并标记E。一直重复,留下被标记的框框。
在这里插入图片描述
红色部分是坐标误差,1obj是判断第i个网格的第j个box是否负责这个目标,是就为1,不是为0。负责检测的才有位置损失,只有有目标的计算坐标误差,没有的不算。
绿色部分是confidence误差,1obj是包含目标的confidence预测,1noobj是不包含目标的conference的预测,这部分要乘一个系数。就是说多预测了惩罚小一点,少预测了惩罚多。
紫色部分是类别误差,用二次函数误差。
缺点:
YOLO 对相互靠的很近的物体,还有很小的群体检测效果不好,这是因为一个网格中只预测了两个框,并且只属于一类。
同一类物体出现的新的不常见的长宽比和其他情况时,泛化能力偏弱。
由于损失函数的问题,定位误差是影响检测效果的主要原因。尤其是大小物体的处理上,还有待加强。
部分程序(以VOC2007数据集为例):

def build_networks(self):
##网络的输入到全连接层,输出是(?,1470)
	if self.disp_console : print("Building YOLO_small graph...")
	self.x = tf.placeholder('float32',[None,448,448,3])
	self.conv_1 = self.conv_layer(1,self.x,64,7,2)
	self.pool_2 = self.pooling_layer(2,self.conv_1,2,2)
	self.conv_3 = self.conv_layer(3,self.pool_2,192,3,1)
	self.pool_4 = self.pooling_layer(4,self.conv_3,2,2)
	self.conv_5 = self.conv_layer(5,self.pool_4,128,1,1)
	...
	self.conv_26 = self.conv_layer(26,self.conv_25,1024,3,2)
	self.conv_27 = self.conv_layer(27,self.conv_26,1024,3,1)
	self.conv_28 = self.conv_layer(28,self.conv_27,1024,3,1)
	self.fc_29 = self.fc_layer(29,self.conv_28,512,flat=True,linear=False)
	self.fc_30 = self.fc_layer(30,self.fc_29,4096,flat=False,linear=False)
	#skip dropout_31
	self.fc_32 = self.fc_layer(32,self.fc_30,1470,flat=False,linear=True)
	self.sess = tf.Session()
	self.sess.run(tf.initialize_all_variables())
	self.saver = tf.train.Saver()
	self.saver.restore(self.sess,self.weights_file)
#训练
        if is_training:#开始训练
        self.labels = tf.placeholder(
            tf.float32,
            [None, self.cell_size, self.cell_size, 5 + self.num_class])#(?,7,7,25)
        self.loss_layer(self.logits, self.labels)#相当于第37层-loss层,这个里边得到box的标签输出
        self.total_loss = tf.losses.get_total_loss()
        tf.summary.scalar('total_loss', self.total_loss)
 #最重要的loss部分:
def loss_layer(self, predicts, labels, scope='loss_layer'):
    with tf.variable_scope(scope):
        predict_classes = tf.reshape(predicts[:, :self.boundary1],[self.batch_size, self.cell_size, self.cell_size, self.num_class])#(45,7,7,20)
        predict_scales = tf.reshape(predicts[:, self.boundary1:self.boundary2],[self.batch_size, self.cell_size, self.cell_size, self.boxes_per_cell])#(45,7,7,2)
        predict_boxes = tf.reshape(predicts[:, self.boundary2:],[self.batch_size, self.cell_size, self.cell_size, self.boxes_per_cell, 4])#(45,7,7,2,4)
        response = tf.reshape(labels[..., 0],[self.batch_size, self.cell_size, self.cell_size, 1])#(45,7,7,1)
        boxes = tf.reshape(labels[..., 1:5],[self.batch_size, self.cell_size, self.cell_size, 1, 4])#(45,7,7,1,4)
        boxes = tf.tile(boxes, [1, 1, 1, self.boxes_per_cell, 1]) / self.image_size#(45,7,7,2,4)
        classes = labels[..., 5:]#(?,7,7,20)
        offset = tf.reshape(tf.constant(self.offset, dtype=tf.float32),[1, self.cell_size, self.cell_size, self.boxes_per_cell])#(1,7,7,2)
        offset = tf.tile(offset, [self.batch_size, 1, 1, 1])#(45,7,7,2)
        offset_tran = tf.transpose(offset, (0, 2, 1, 3))#(45,7,7,2)
        predict_boxes_tran = tf.stack([(predict_boxes[..., 0] + offset) / self.cell_size,(predict_boxes[..., 1] + offset_tran) / self.cell_size,tf.square(predict_boxes[..., 2]),tf.square(predict_boxes[..., 3])], axis=-1)#(45,7,7,2,4)
        iou_predict_truth = self.calc_iou(predict_boxes_tran, boxes)#计算IOU值
        # calculate I tensor [BATCH_SIZE, CELL_SIZE, CELL_SIZE, BOXES_PER_CELL]
        object_mask = tf.reduce_max(iou_predict_truth, 3, keep_dims=True)  ##计算最大值
        object_mask = tf.cast((iou_predict_truth >= object_mask), tf.float32) * response  ##张量数据类型转换
        # calculate no_I tensor [CELL_SIZE, CELL_SIZE, BOXES_PER_CELL]
        noobject_mask = tf.ones_like(object_mask, dtype=tf.float32) - object_mask   ##创建一个将所有元素都为1的张量再减去object_mask
        boxes_tran = tf.stack([boxes[..., 0] * self.cell_size - offset,boxes[..., 1] * self.cell_size - offset_tran,tf.sqrt(boxes[..., 2]),tf.sqrt(boxes[..., 3])], axis=-1)
        # class_loss
        class_delta = response * (predict_classes - classes)#(45,7,7,20)
        class_loss = tf.reduce_mean(tf.reduce_sum(tf.square(class_delta), axis=[1, 2, 3]),name='class_loss') * self.class_scale
        # object_loss
        object_delta = object_mask * (predict_scales - iou_predict_truth)
        object_loss = tf.reduce_mean(tf.reduce_sum(tf.square(object_delta), axis=[1, 2, 3]),name='object_loss') * self.object_scale
        # noobject_loss
        noobject_delta = noobject_mask * predict_scales
        noobject_loss = tf.reduce_mean(tf.reduce_sum(tf.square(noobject_delta), axis=[1, 2, 3]),name='noobject_loss') * self.noobject_scale
        # coord_loss
        coord_mask = tf.expand_dims(object_mask, 4)
        boxes_delta = coord_mask * (predict_boxes - boxes_tran)
        coord_loss = tf.reduce_mean(tf.reduce_sum(tf.square(boxes_delta), axis=[1, 2, 3, 4]),name='coord_loss') * self.coord_scale
        tf.losses.add_loss(class_loss)
        tf.losses.add_loss(object_loss)
        tf.losses.add_loss(noobject_loss)
        tf.losses.add_loss(coord_loss)
        tf.summary.scalar('class_loss', class_loss)
        tf.summary.scalar('object_loss', object_loss)
        tf.summary.scalar('noobject_loss', noobject_loss)
        tf.summary.scalar('coord_loss', coord_loss)
        tf.summary.histogram('boxes_delta_x', boxes_delta[..., 0])
        tf.summary.histogram('boxes_delta_y', boxes_delta[..., 1])
        tf.summary.histogram('boxes_delta_w', boxes_delta[..., 2])
        tf.summary.histogram('boxes_delta_h', boxes_delta[..., 3])
        tf.summary.histogram('iou', iou_predict_truth)
#测试:把网络的全连接层的输出中的每一个(1470,)进行reshape等操作,最后得到测试出的类别和bbox信息。
def interpret_output(self,output):
	probs = np.zeros((7,7,2,20))
	class_probs = np.reshape(output[0:980],(7,7,20))
	scales = np.reshape(output[980:1078],(7,7,2))
	boxes = np.reshape(output[1078:],(7,7,2,4))
	offset = np.transpose(np.reshape(np.array([np.arange(7)]*14),(2,7,7)),(1,2,0))
	boxes[:,:,:,0] += offset
	boxes[:,:,:,1] += np.transpose(offset,(1,0,2))
	boxes[:,:,:,0:2] = boxes[:,:,:,0:2] / 7.0
	boxes[:,:,:,2] = np.multiply(boxes[:,:,:,2],boxes[:,:,:,2])
	boxes[:,:,:,3] = np.multiply(boxes[:,:,:,3],boxes[:,:,:,3])
	boxes[:,:,:,0] *= self.w_img
	boxes[:,:,:,1] *= self.h_img
	boxes[:,:,:,2] *= self.w_img
	boxes[:,:,:,3] *= self.h_img
	for i in range(2):
		for j in range(20):
			probs[:,:,i,j] = np.multiply(class_probs[:,:,j],scales[:,:,i])
	filter_mat_probs = np.array(probs>=self.threshold,dtype='bool')
	filter_mat_boxes = np.nonzero(filter_mat_probs)  ##得到数组非零元素的位置(数组索引)的函数。
	boxes_filtered = boxes[filter_mat_boxes[0],filter_mat_boxes[1],filter_mat_boxes[2]]
	probs_filtered = probs[filter_mat_probs]
	classes_num_filtered = np.argmax(filter_mat_probs,axis=3)[filter_mat_boxes[0],filter_mat_boxes[1],filter_mat_boxes[2]] 
	argsort = np.array(np.argsort(probs_filtered))[::-1]
	boxes_filtered = boxes_filtered[argsort]
	probs_filtered = probs_filtered[argsort]
	classes_num_filtered = classes_num_filtered[argsort]
	for i in range(len(boxes_filtered)):
		if probs_filtered[i] == 0 : continue
		for j in range(i+1,len(boxes_filtered)):
			if self.iou(boxes_filtered[i],boxes_filtered[j]) > self.iou_threshold :   ##如果大于阈值
				probs_filtered[j] = 0.0
	filter_iou = np.array(probs_filtered>0.0,dtype='bool')
	boxes_filtered = boxes_filtered[filter_iou]
	probs_filtered = probs_filtered[filter_iou]
	classes_num_filtered = classes_num_filtered[filter_iou]
	result = []
	for i in range(len(boxes_filtered)):
		result.append([self.classes[classes_num_filtered[i]],boxes_filtered[i][0],boxes_filtered[i][1],boxes_filtered[i][2],boxes_filtered[i][3],probs_filtered[i]])
	return result
##数据集(VOC2007)
def get(self):
    images = np.zeros((self.batch_size, self.image_size, self.image_size, 3))
    labels = np.zeros((self.batch_size, self.cell_size, self.cell_size, 25))
    count = 0
    while count < self.batch_size:
        imname = self.gt_labels[self.cursor]['imname']  ##gt_label是从数据集\pascal_voc\cache\pascal_train_gt_labels.pkl文件得到的n个(7,7,25)的标签字典
        flipped = self.gt_labels[self.cursor]['flipped']
        images[count, :, :, :] = self.image_read(imname, flipped)
        labels[count, :, :, :] = self.gt_labels[self.cursor]['label']
        count += 1
        self.cursor += 1
        if self.cursor >= len(self.gt_labels):
            np.random.shuffle(self.gt_labels)
            self.cursor = 0
            self.epoch += 1
    return images, labels

结果展示
在这里插入图片描述
voc2007数据集标签
例如(\pascal_voc\VOCdevkit\VOC2007\JPEGImages\003300.jpg),程序中resize成(1,448,448,3)的
在这里插入图片描述

对应的标签为:(7,7,25)
在这里插入图片描述
1.[ 1. 219.864 200.704 43.904 50.176 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. ]
2.[ 1. 332.312 244.906667 78.848 66.90133333 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.0. 0. 1. 0. 0. ]
3.[ 1. 98.456 283.7333 107.52 125.44 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. ]
4…
5…
6…
7…
8…
YOLO V2
用偏移量代替直接预测坐标值,特征图大小为 1313
在这里插入图片描述
去掉了全连接层,使用 Anchor Boxes 来预测 Bounding Boxes。作者去掉了网络中一个池化层,这让卷积层的输出能有更高的分辨率。收缩网络让其运行在 416
416 而不是 448448。使用 Anchor Box 会让精确度稍微下降,但用了它能让 YOLO 能预测出大于一千个框,同时 recall 达到88%,mAP 达到 69.2%。
用 K-means 选择 Anchor Boxes 时,在训练集的 Bounding Boxes 上跑一下 k-means聚类,来找到一个比较好的值。
用 Anchor Box 的方法,会让 model 变得不稳定,尤其是在最开始的几次迭代的时候。大多数不稳定因素产生自预测 Box 的(x,y)位置的时候。按照之前 YOLO的方法,网络不会预测偏移量,而是根据 YOLO 中的网格单元的位置来预测坐标,这就让 Ground Truth 的值介于 0 到 1 之间。而为了让网络的结果能落在这一范围内,网络使用一个 Logistic Activation 来对于网络预测结果进行限制,让结果介于 0 到 1 之间。 网络在每一个网格单元中预测出 5 个 Bounding Boxes,每个 Bounding Boxes 有五个坐标值 tx,ty,tw,th,t0,他们的关系见下图(Figure3)。
作者希望 YOLOv2 能健壮地运行于不同尺寸的图片之上,所以YOLOv2 每迭代几次都会改变网络参数。每 10 个 Batch,网络会随机地选择一个新的图片尺寸,由于使用了下采样参数是 32,所以不同的尺寸大小也选择为 32 的倍数 {320,352……608},最小 320
320,最大 608*608,网络会自动改变尺寸,并继续训练的过程。这一政策让网络在不同的输入尺寸上都能达到一个很好的预测效果,同一网络能在不同分辨率上进行检测。当输入图片尺寸比较小的时候跑的比较快,输入图片尺寸比较大的时候精度高,
YOLO V3
改进:
之前一个点只能预测一个物体的中心,改进后可以预测3个。
多尺度预测 (类FPN):每种尺度预测 3 个 box, anchor 的设计方式仍然使用聚类,得到9个聚类中心,将其按照大小均分给 3 个尺度。
更好的基础分类网络(类ResNet)和分类器 darknet-53;把Softmax 改成多个 logistic ,分类损失采用 binary cross-entropy loss.。
在这里插入图片描述
部分程序:

def create_model(input_shape, anchors, num_classes, load_pretrained=True, freeze_body=2,
            weights_path='model_data/yolo_weights.h5'):###freeze_body这个是2代表冻结除3个输出层以外的所有层
    K.clear_session() # get a new session
    image_input = Input(shape=(None, None, 3))
    h, w = input_shape
    num_anchors = len(anchors)     ##一共9个anchors
    y_true = [Input(shape=(h//{0:32, 1:16, 2:8}[l], w//{0:32, 1:16, 2:8}[l], num_anchors//3, num_classes+5)) for l in range(3)]###//是浮点数除法。416除以32,16,8=13;26;52.输入的三种形状。
    model_body = yolo_body(image_input, num_anchors//3, num_classes)#####创建的模型,anchors决定最后输出y的形状,这里anchors=9,y的最后一个维度是(9/3)*(class+5)=75,也就是说一个点可以同时预测三个物体
    print('Create YOLOv3 model with {} anchors and {} classes.'.format(num_anchors, num_classes))
    def yolo_body(inputs, num_anchors, num_classes):
        darknet = Model(inputs, darknet_body(inputs))######创建网络模型,前23层,得到(?,?,?,1024)的数据
        x, y1 = make_last_layers(darknet.output, 512, num_anchors*(num_classes+5))#####把网络最后的输出怎么变换得到x=(?,?,?,512),y1=(?,?,?,75).在最后一层变一个步长
        x = compose(DarknetConv2D_BN_Leaky(256, (1,1)),UpSampling2D(2))(x)###把x再变换成(?,?,?,256)
        x = Concatenate()([x,darknet.layers[152].output])#######把第152层的输出怎么了(?,?,?,768)连接起来
        x, y2 = make_last_layers(x, 256, num_anchors*(num_classes+5))#####把网络第152层的输出怎么变换得到x=(?,?,?,256),y1=(?,?,?,75)
        x = compose(DarknetConv2D_BN_Leaky(128, (1,1)),UpSampling2D(2))(x)#####把之前256的变换成128的
        x = Concatenate()([x,darknet.layers[92].output])#######把第92层的输出怎么了(?,?,?,384)
        x, y3 = make_last_layers(x, 128, num_anchors*(num_classes+5))#####再怎么变换成x=(?,?,?,128)的,y3=(?,?,?,75)的
        return Model(inputs, [y1,y2,y3])   ##输出由三部分组成
def darknet_body(x):
    '''Darknent body having 52 Convolution2D layers'''
    x = DarknetConv2D_BN_Leaky(32, (3,3))(x)
    x = resblock_body(x, 64, 1)#####x是上一层的输入,64是该层输出大小,1是block的数目
    x = resblock_body(x, 128, 2)#####x是上一层的输入,128是该层输出大小,2是block的数目
    x = resblock_body(x, 256, 8)
    x = resblock_body(x, 512, 8)
    x = resblock_body(x, 1024, 4)######1+2+8+8+4共23层
    return x
def make_last_layers(x, num_filters, out_filters):
    x = compose(DarknetConv2D_BN_Leaky(num_filters, (1,1)),####(512,(1,1))
            DarknetConv2D_BN_Leaky(num_filters*2, (3,3)),####(1024,(3,3))
            DarknetConv2D_BN_Leaky(num_filters, (1,1)),####(512,(1,1))
            DarknetConv2D_BN_Leaky(num_filters*2, (3,3)),####(1024,(3,3))
            DarknetConv2D_BN_Leaky(num_filters, (1,1)))(x)###函数组合,使得每个函数的结果作为下一个函数的参数传递.例两个函数f和g的组成表示为f(g(x)).就是让x经过这一系列函数,得到(?,?,?,512)
    y = compose(DarknetConv2D_BN_Leaky(num_filters*2, (3,3)),
            DarknetConv2D(out_filters, (1,1)))(x)###把x经过这两个函数变换得到(?,?,?,75)
    return x, y
def yolo_loss(args, anchors, num_classes, ignore_thresh=.5, print_loss=False):
    num_layers = len(anchors)//3 # default setting
    yolo_outputs = args[:num_layers]####yolo的三个输出
    y_true = args[num_layers:]
    anchor_mask = [[6,7,8], [3,4,5], [0,1,2]] if num_layers==3 else [[3,4,5], [1,2,3]]
    input_shape = K.cast(K.shape(yolo_outputs[0])[1:3] * 32, K.dtype(y_true[0]))
    grid_shapes = [K.cast(K.shape(yolo_outputs[l])[1:3], K.dtype(y_true[0])) for l in range(num_layers)]
    loss = 0
    m = K.shape(yolo_outputs[0])[0] # batch size, tensor
    mf = K.cast(m, K.dtype(yolo_outputs[0]))
    for l in range(num_layers):###l=0,1,2
        object_mask = y_true[l][..., 4:5]##y_true是(?,13,13,3,13),(?,26,26,3,13),(?,52,52,3,13)取最后一个维度的第五个吧
        true_class_probs = y_true[l][..., 5:]
        grid, raw_pred, pred_xy, pred_wh = yolo_head(yolo_outputs[l],
             anchors[anchor_mask[l]], num_classes, input_shape, calc_loss=True)
        pred_box = K.concatenate([pred_xy, pred_wh])###3个候选框对应的box(?,?,3,4)
        # Darknet raw box to calculate loss.
        raw_true_xy = y_true[l][..., :2]*grid_shapes[l][::-1] - grid
        raw_true_wh = K.log(y_true[l][..., 2:4] / anchors[anchor_mask[l]] * input_shape[::-1])
        raw_true_wh = K.switch(object_mask, raw_true_wh, K.zeros_like(raw_true_wh)) # avoid log(0)=-inf
        box_loss_scale = 2 - y_true[l][...,2:3]*y_true[l][...,3:4]
        # Find ignore mask, iterate over each of batch.
        ignore_mask = tf.TensorArray(K.dtype(y_true[0]), size=1, dynamic_size=True)
        object_mask_bool = K.cast(object_mask, 'bool')
        def loop_body(b, ignore_mask):
            true_box = tf.boolean_mask(y_true[l][b,...,0:4], object_mask_bool[b,...,0])
            iou = box_iou(pred_box[b], true_box)
            best_iou = K.max(iou, axis=-1)
            ignore_mask = ignore_mask.write(b, K.cast(best_iou<ignore_thresh, K.dtype(true_box)))
            return b+1, ignore_mask
        _, ignore_mask = K.control_flow_ops.while_loop(lambda b,*args: b<m, loop_body, [0, ignore_mask])
        ignore_mask = ignore_mask.stack()
        ignore_mask = K.expand_dims(ignore_mask, -1)
        # K.binary_crossentropy is helpful to avoid exp overflow.
        xy_loss = object_mask * box_loss_scale * K.binary_crossentropy(raw_true_xy, raw_pred[...,0:2], from_logits=True)######这个计算loss的公式
        wh_loss = object_mask * box_loss_scale * 0.5 * K.square(raw_true_wh-raw_pred[...,2:4])
        confidence_loss = object_mask * K.binary_crossentropy(object_mask, raw_pred[...,4:5], from_logits=True)+ \
            (1-object_mask) * K.binary_crossentropy(object_mask, raw_pred[...,4:5], from_logits=True) * ignore_mask
        class_loss = object_mask * K.binary_crossentropy(true_class_probs, raw_pred[...,5:], from_logits=True)
        xy_loss = K.sum(xy_loss) / mf
        wh_loss = K.sum(wh_loss) / mf
        confidence_loss = K.sum(confidence_loss) / mf
        class_loss = K.sum(class_loss) / mf
        loss += xy_loss + wh_loss + confidence_loss + class_loss      ##为什么loss等于三次加起来??
        if print_loss:
            loss = tf.Print(loss, [loss, xy_loss, wh_loss, confidence_loss, class_loss, K.sum(ignore_mask)], message='loss: ')
    return loss
def yolo_head(feats, anchors, num_classes, input_shape, calc_loss=False):
    num_anchors = len(anchors)
    # Reshape to batch, height, width, num_anchors, box_params.
    anchors_tensor = K.reshape(K.constant(anchors), [1, 1, 1, num_anchors, 2])
    grid_shape = K.shape(feats)[1:3] # height, width
    grid_y = K.tile(K.reshape(K.arange(0, stop=grid_shape[0]), [-1, 1, 1, 1]),
        [1, grid_shape[1], 1, 1])
    grid_x = K.tile(K.reshape(K.arange(0, stop=grid_shape[1]), [1, -1, 1, 1]),
        [grid_shape[0], 1, 1, 1])
    grid = K.concatenate([grid_x, grid_y])
    grid = K.cast(grid, K.dtype(feats))
    feats = K.reshape(
        feats, [-1, grid_shape[0], grid_shape[1], num_anchors, num_classes + 5])####把网络输出的(?,?,?,39)reshape成(?,?,?,3,13)
    # Adjust preditions to each spatial grid point and anchor size.
    box_xy = (K.sigmoid(feats[..., :2]) + grid) / K.cast(grid_shape[::-1], K.dtype(feats))
    box_wh = K.exp(feats[..., 2:4]) * anchors_tensor / K.cast(input_shape[::-1], K.dtype(feats))
    box_confidence = K.sigmoid(feats[..., 4:5])
    box_class_probs = K.sigmoid(feats[..., 5:])
    if calc_loss == True:
        return grid, feats, box_xy, box_wh
    return box_xy, box_wh, box_confidence, box_class_probs

把[y1,y2,y3]中的每个y,(?,?,?,75)先reshape成(?,?,?,3,25)的
再按照最后一个维度拆开成4部分:0:2;2:4;4:5;5:
前两个经过sigmoid等操作作为box_xy;第2:4经过exp再乘以anchor等操作作为box_wh;第五个作为confidence;剩下的经过sigmoid作为class
YOLO V3 的数据集:先要用k-means等聚类方法找到9个anchors,这个anchors在loss函数里用到了。
box_wh=输出的[…, 2:4] * anchors_tensor ##输出的wh
pred_box = K.concatenate([pred_xy, pred_wh]) ##输出的box
iou = box_iou(pred_box[b], true_box)
即输出的2:4部分*anchor作为输出的wh,再与标签的wh计算loss
y_true是3个不同的尺度,每个标签在某一尺度下生成y_true.选择9个簇,3个尺度,将9个簇均匀分布在这几个尺度上。
在这里插入图片描述
参考程序:
https://github.com/gliese581gg/YOLO_tensorflow
https://github.com/hizhangp/yolo_tensorflow
https://github.com/qqwweee/keras-yolo3

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值