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

摘要:传统的深度神经网络在网络层数较深时,会出现梯度消失或者爆炸的问题,导致难以有效训练。ResNet通过引入“残差块”(Residual Block)的方式来解决这一问题。残差块中,利用跳跃连接的方式将输入的恒等映射直接加到后续的输出中,从而使得网络直接可以学习到对输入的差异性特征,避免这些信息因为多网络层的堆叠而丢失。此外,在ResNet的设计中还采用了批规范化、池化等常规技术,进一步提高了模型的性能。

        大家好,上一期我介绍了VGG19这个深度神经网络,使用VGG19网络完成了一个多分类任务(手写汉字识别)。使用这样一个模型能够实现识别常用的3700多个汉字,当然,也对应着这3700个手写汉字。但是呢,VGG19也会有弊端,包括大量的参数、训练缓慢、容易过拟合、偏向于模式识别等等。

        今天,我给大家带来一个新的网络ResNet-50,首先我们来看下什么是ResNet。ResNet是一种深度卷积神经网络模型,在2015年由何凯明等人提出,主要用于解决深度神经网络训练时的梯度消失问题。划重点,他是解决梯度消失或者梯度爆炸的,那么他怎么解决这个问题的呢?ResNet通过引入“残差块”(Residual Block)的方式来解决这一问题。残差块中,利用跳跃连接的方式将输入的恒等映射直接加到后续的输出中,从而使得网络直接可以学习到对输入的差异性特征,避免这些信息因为多网络层的堆叠而丢失。所以,由于他是拥有多个“残差块”和多个网络堆叠,所以他的网络层次很深,比如ResNet50(注意:这里我有点歧义,官方或者教材上给出的是50层包括卷积+全连接,但是我觉得不对,因为缺少了跨维度的4个卷积层,我数下来是54层,如果有懂的朋友可以帮我解解惑)。这样的网络比VGG19深多了,VGG19总共有大约14,714,688个参数,而ResNet50大约有25,557,032个参数,这意味着ResNet50参数量是VGG19的大约1.78倍左右。需要注意的是,模型参数数量并不完全反映一个模型的复杂程度和性能。例如,ResNet50相对于VGG19来说,在ImageNet数据集上的分类准确率要更高。这是因为ResNet50的残差结构可以帮助提高梯度传播和捕获更深入的特征表示。此外,模型的训练方法、数据量等因素也会影响模型的性能,因此选用合适的模型需要考虑多方面因素,不能仅仅依据参数数量来判断。

        所以,ResNet50它是一种残差网络,想要学习推导和残差块的具体理论知识的,可以自行教程并不难,这里我们就不讲解。

        上面对ResNet介绍了很多,接下来我们看下利用ResNet50来处理一个目标检测问题,简单地说就是我们检测指定的图片中是否存在指定的目标。这明显是一个二分类问题,有或者没有就行。

那我们梳理下处理这个问题的过程:

        1.收集数据集===2.处理数据集===3.构建ResNet50网络模型===4.训练===5.测试===6.优化

1.收集数据集

我们使用Pascal VOC的数据集

地址:https://pjreddie.com/projects/pascal-voc-dataset-mirror/

        我这里使用了2007的训练集和测试集,别问为什么,问就是因为它小啊。

2.处理数据集

解压训练集和测试集之后,我们开始处理数据了,我看了下图片接近5000张,这个如果一次性加载的话,贫穷的我应该加载不了吧,OOM应该是肯定的。不过不用担心,数据集都帮我们整理好了,我们就以汽车检测为例吧,找到压缩文件夹下的

VOCtrainval_06-Nov-2007\VOCdevkit\VOC2007\ImageSets\Main

这个路径下存放着我们需要的目标索引

         我们看到内容分类还是比较丰富的,我们选择"car_train.txt",打开文件之后,我们先了解下它的结构

         它的结构比较简单,第一列是图片的文件名 第二列是标签1表示存在,-1表示不存在。图片的位置在

\VOCtrainval_06-Nov-2007\VOCdevkit\VOC2007\JPEGImages
def load_train(file_name):
    file_name=file_name+".txt"
    path=os.path.join(txt_root,file_name)
    images=[]
    labels=[]
    global batch_size
    while True:
        with open(path,'r',encoding='utf-8') as f:
            for line in f:
                name, label =line.split()
                label=int(label)
                if label<0:
                    label=0
                img_path = os.path.join(img_root,name+'.jpg')
                image = cv2.imread(img_path)
                image = image_plus(image)
                image = cv2.resize(image,(input_size,input_size))
                images.append(image)
                labels.append([label])

                if len(images)>=batch_size:
                    yield np.array(images),np.array(labels,dtype=np.uint8)
                    images=[]
                    labels=[]

以上代码是加载训练集的代码,测试集也是如此,基本上一致。

3.构建ResNet50网络模型

这里我没有采用预训练的ResNet模型和权重,自己构建了一个

def resnet50_model(nb_classes=1):
    global input_size
    # 定义输入的tensor类型,shape于输入图像相同
    X_input=Input(shape=(input_size,input_size,3))
    X=ZeroPadding2D((3,3))(X_input)

    #stage 1
    X=Convolution2D(filters=64,kernel_size=(7,7),strides=(2,2),kernel_initializer=glorot_uniform(seed=0))(X)
    X=BatchNormalization(axis=3)(X)
    X=Activation('relu')(X)
    X=MaxPooling2D((3,3),strides=(2,2))(X)

    #stage 2
    X=convolutional_block(X,f=3,filters=[64,64,256],s=1)
    X=identity_block(X,f=3,filters=[64,64,256])
    X = identity_block(X, f=3, filters=[64, 64, 256])

    # stage 3
    X=convolutional_block(X,f=3,filters=[128,128,512],s=2)
    X=identity_block(X,f=3,filters=[128,128,512])
    X = identity_block(X, f=3, filters=[128,128,512])
    X = identity_block(X, f=3, filters=[128,128,512])

    # stage 4
    X = convolutional_block(X, f=3, filters=[256, 256, 1024], s=2)
    X = identity_block(X, f=3, filters=[256, 256, 1024])
    X = identity_block(X, f=3, filters=[256, 256, 1024])
    X = identity_block(X, f=3, filters=[256, 256, 1024])
    X = identity_block(X, f=3, filters=[256, 256, 1024])
    X = identity_block(X, f=3, filters=[256, 256, 1024])

    # stage 5
    X = convolutional_block(X, f=4, filters=[512, 512, 2048], s=2)
    X = identity_block(X, f=3, filters=[512, 512, 2048])
    X = identity_block(X, f=3, filters=[512, 512, 2048])

    # 平均池化
    X =AveragePooling2D((2,2))(X)

    #输出层
    X=Flatten()(X)
    X=Dense(nb_classes,activation='sigmoid',kernel_initializer=glorot_uniform(seed=0))(X)
    model=Model(inputs=X_input,outputs=X)
    model.compile(loss='binary_crossentropy',
                  optimizer='adam',
                  metrics=['accuracy'],
                  )

    return model

当然,大家也可以采用keras自带的,这些都一样的没有什么。loss采用了binary_crossentropy 对应的激活函数 sigmoid。

4.训练


def train():
    model = resnet50_model(1)
    model.fit_generator(generator=load_train('car_train'),
                        steps_per_epoch= math.ceil(2501/batch_size)+1,
                        epochs=50,
                        validation_data=load_vaild('car_test')
                        )

这里采用小批量训练,减少内存的消耗。

5.测试

测试的效果咋一看还行,但是,我还是发现了问题。

模型能够很好地识别2号图片,但是并不能很好地识别1号图片,这是为什么呢?这两张图片都来自于网络,不是来自于样本里的任何数据,出现这样的结果,说明我的模型出现了泛化能力差鲁棒性差的现象。

6.优化

经过分析,我发现,导致这样的原因还是因为我的样本数据都比较规整,没有更多的干扰或者说训练图片特征没有学习到位。这个是时候我想到了一个词图像增强包括反转,旋转,缩放。


# 图片增强
def image_plus(img):
    idx = np.random.randint(low=1,high=4,dtype=np.uint8)
    if(idx==1): #随机缩放
        return random_scale(img,input_size+10)
    if(idx==2): #随机翻转
        return random_flip(img,0.5)
    if(idx==3):
        angle = np.random.randint(10,360)
        return random_rotate(img,angle)

        我的思路比较简单,在生成器生成图片的时候将图片随机进行处理,这样增加了图片的多样性。经过50轮的训练(为什么是50轮呢,因为我迫不及待地想看一下我的猜测)

         86%的正确率,这样的正确率还有上升的空间,不过这不重要。我们看下这两者图的预测情况吧。

        我们发现两者都是非常接近1了,尤其是1号图片,由检测错误,直接就转为成功了,两者之间的差异0.03的差异,是不是就代表着两者非常接近了呢?

好了,今天就给大家介绍完了,我对ResNet的目标检测还是非常满意的,拜拜下次见。

如有疑问请添加群:195889612

  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值