深度学习-ResNet-50实现目标检测(基于Pascal VOC数据集)2
  WWIUEgpSPVHw 2023年11月02日 73 0

摘要:关于上一篇文章《深度学习-ResNet-50实现目标检测(基于Pascal VOC数据集)》很多朋友提到说,作者你实现的属于分类任务,不属于目标检测。如果按照课本和其他教程上来说确实如此,但是呢,我还是理解为目标检测,从网络现实结合到现实问题,我个人保留自己的主观意见。这里建议大家按照课本的定义进行归纳。

    大家好,上一期我介绍了使用ResNet实现了目标检测,检测图片中是否有指定的对象。但是一直缺少了标注的位置信息,所以看起来总是缺少了一部分内容。那么,今天我来完善一下,把边框信息也给识别出来吧。

    在不使用第三方目标检测框架的情况下,如何才能实现这个功能呢?使用如yolo这样的目标检测框架来实现功能,确实非常的简单,但是它是一个黑箱,我们没法知道它背后的原理,没有原理的代码是没有灵魂的。那我们来思考下,应该怎么来实现这个功能?

    简单的说,目标检测任务旨在从图像中标记出感兴趣的对象的位置和形状。为了完成这个任务,我们需要将每个对象的位置和形状用一组数值来描述。这组数值通常称为边界框 (bounding box) 或 bounding box 的位置坐标和大小。在目标检测中,我们可以使用回归模型来学习将图像特征映射到每个对象的边界框位置和大小的映射关系。换句话说,回归模型可以学习如何从图像特征预测边界框位置和大小。使用回归可以识别出来主要是因为回归模型可以有效地学习目标物体的位置和大小以及与之相关的特征,这样就可以更加准确地预测边界框坐标和大小。此外,回归模型还可以使用深度神经网络等强大的算法来提取图像特征,从而从复杂的图像中检测出不同的目标物体。综合来看,使用回归模型可以使目标检测具有更高的准确性和鲁棒性。

    思路这不就来了吗?对了,我们使用回归的方式来预测边界框,本着不浪费的精神,作者就把上一篇的模型拿过来修改修改吧,毕竟这个并不困难。

    在这一类的目标检测中,我们需要检测出目标是什么名称以及他的位置和边框,使用分类的方式可以将目标检测出来,使用回归的方式,我们把位置和边框检测出来这个网络就完整了,但是,分类和位置以及边框他们同时存在的(这里我们约定,他们两个同样重要,也就是说,检测出目标:检测出目标位置和边框=1:1),可以在实际的应用场景中设置不同的比例。

    ok,把这块模型代码放出来吧,做个接水管的事情而已


def o_check_model(num_classes=1):
    num_anchors=1
    # 定义输入的tensor类型,shape于输入图像相同
    X_input = Input(shape=(input_size, input_size, 3), name='input_1')
    X = ZeroPadding2D((3, 3))(X_input)

    # stage 1
    ...

    # stage 2
    ...

    # stage 3
    ...

    # stage 4
    ...

    # stage 5
    ...

    resNet = X
    X=AveragePooling2D((2,2))(X)
    X=Flatten()(X)
    class_head=Dense(num_classes,activation='softmax',name='class_head')(X)

    # 检测头部
    x = Conv2D(64, (3, 3), activation='relu', padding='same',name='f_f1')(resNet)
    x = BatchNormalization(axis=3)(x)
    x = Conv2D(64, (3, 3), activation='relu', padding='same',name='f_f2')(x)
    x = BatchNormalization(axis=3)(x)
    x = Conv2D(64, (3, 3), activation='relu', padding='same',name='f_f3')(x)
    x = BatchNormalization(axis=3)(x)
    x = Flatten()(x)
    # 回归头部
    reg_head = Dense(num_anchors*4,activation='linear',name='reg_head')(x)
    # 构建完整模型
    model = Model(inputs=X_input, outputs={'class_head':class_head, 'reg_head':reg_head})

    # 编译模型
    model.compile(optimizer='adam',
                  loss={
                        'class_head':'categorical_crossentropy',
                        'reg_head':'mean_squared_error'
                    },
                  loss_weights={
                      'class_head':1.0,
                      'reg_head':1.0
                  },
                  metrics={
                      'class_head':'accuracy',
                      'reg_head':'mae'
                  })
    model.summary()
    return model

把后面回归的水管接上去了,在ResNet50后面添加了对位置和边框信息处理,同时使用


loss={
                        'class_head':'categorical_crossentropy',
                        'reg_head':'mean_squared_error'
                    }

同时使用分类和回归的loss,他们对应的权重占比为1:1,当然这里也可以自己去定义个loss_function本质上是一样的,例如


# 总体损失函数
def total_loss(y_true, y_pred):
    print('y_true',y_true[0].shape,'y_pred',y_pred.shape)
    # 分类损失
    class_true = y_true[0]
    class_pred = y_pred[0]
    class_loss = base_loss(class_true, class_pred)
    # 回归损失
    reg_true = y_true[1]
    reg_pred = y_pred[1]
    reg_loss =mean_squared_error(reg_true,reg_pred) #iou_loss(reg_true,reg_pred) #

    # 组合损失
    total_loss = class_loss + reg_loss

    # 设置类型为浮点数类型 这个地方后期可能要调整
    # total_loss = tf.cast(total_loss,dtype=tf.int32)
    return total_loss

    非常的方便,这样我们的网络就创建好了,接下来就是准备数据了。本着不浪费的前提,把上一篇的代码拿过来用一下吧,不同的是,这里的标签是需要我们处理一下的。

    标签位置样本是(x1,y1,x2,y2)这样的格式,(x1,y2)表示左上角坐标位置,(x2,y2)表示右下角坐标位置。这里我们处理下这个坐标信息转换成(x,y,w,h)这样的格式,其中(x,y)表示边框的中心点,(w,h)表示边框的宽度和高度。这样做的目的是便于mean_squared_error loss处理回归问题,当然,你也可以自己定义。

    对了,做上一步位置转换之前,还有一个注意点,由于网络输入的size是224,而且我们用的是batch的方式训练的,所以图片会resize到224,那么问题来了,label位置就会发生改变,所以需要处理下label的位置,这里是一个注意点。
我们整理下代码:

先把样本的类别整理一下,这里一共有20个类别

catalogs=['aeroplane','bicycle','bird','boat','bottle','bus','car','cat','chair','cow','diningtable','dog','horse','motorbike','person','pottedplant','sheep','sofa','train','tvmonitor']

接下来就是部分图片预处理的代码

image = cv2.imread(img_path)


# 设置image 的resize 为input_size
image = cv2.resize(image,(input_size,input_size))
label = fix_label_scale(label,[height,width])
label = convert_to_mse(label)
obj_catalog=np.zeros(dtype=np.float,shape=len(catalogs))
obj_catalog_idx=catalogs.index(obj_name)
obj_catalog[obj_catalog_idx]=1


classes.append(obj_catalog)
images.append(image)
labels.append(label)




if(len(images)>=batch_size):
    yield (np.array(images),{'class_head':np.array(classes), 'reg_head':np.array(labels)})

上面的代码就是处理的整个流程,只需要保持图片和标签保持一致就可以了。

我们来看下训练的过程

深度学习-ResNet-50实现目标检测(基于Pascal VOC数据集)2_目标检测

我们对比训练集和验证集的结果,发现有收敛的过程,并且在持续。

深度学习-ResNet-50实现目标检测(基于Pascal VOC数据集)2_数据_02

深度学习-ResNet-50实现目标检测(基于Pascal VOC数据集)2_数据_03

深度学习-ResNet-50实现目标检测(基于Pascal VOC数据集)2_图像特征_04

深度学习-ResNet-50实现目标检测(基于Pascal VOC数据集)2_数据_05

   分类识别率在86%、回归在5.1的时候结束训练,我们用这个小绵羊来进行训练,可以看到,能够大体上识别出范围,但是这个框偏移的有点厉害。

那怎么解决呢?

在刚刚的代码中我们没有添加数据增加的功能,模型的泛化能力也不是特别强,我们把数据增加添加一下:缩放、旋转、翻转,再次训练,评测。

深度学习-ResNet-50实现目标检测(基于Pascal VOC数据集)2_目标检测_06

深度学习-ResNet-50实现目标检测(基于Pascal VOC数据集)2_图像特征_07

深度学习-ResNet-50实现目标检测(基于Pascal VOC数据集)2_数据_08

深度学习-ResNet-50实现目标检测(基于Pascal VOC数据集)2_目标检测_09

咋一看,貌似还行,那就差不多了吧,那今天就到这里吧。

本质上来说,跟上一篇是一样的,只是添加了一个检测头部和数据扩充,接下来就交给网络,网络会找出最适合的权重信息的。

篇幅原因在此仅对课程做简要概述,若您想更详细地了解,可关注微信公众号 星玉阁网络

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论

推荐阅读
WWIUEgpSPVHw