Yolo3特征提取+预测网络构建(基于keras)

yolo系列目标检测算法的核心思想就是:把一张图片分为n*n网格,每个网格负责以该网格中心的区域的检测。

Yolo3使用的特征提取网络使用的是darknet53,它相较于其他yolo系列的检测算法主要改进有:
1、主干网络修改为darknet53,其重要特点是使用了残差网络Residual,darknet53中的残差卷积就是进行一次3X3、步长为2的卷积,然后保存该卷积layer,再进行一次1X1的卷积和一次3X3的卷积,并把这个结果加上layer作为最后的结果。
2、darknet53的每一个卷积部分使用了特有的DarknetConv2D结构,每一次卷积的时候进行L2正则化,完成卷积后进行BatchNormalization标准化与LeakyReLU。

这里以输入图片尺寸416x416x3、coco数据集分类数80为例来详细看一看Yolo3的整体网络结构图:

Yolo3特征提取+预测网络构建(基于keras)_第1张图片

1.一个图片经过darknet53特征提取网络会依次得到6个特征层,
其大小分别是:
(416,416,32) (208,208,64) (104,104,128)
(52,52,256)(26,26,512)(13,13,1024)
2.后三个特征层再进行5次卷积处理,处理完后一部分用于输出该特征层对应的预测结果,一部分用于进行反卷积UmSampling2d后与其它特征层进行结合。(至于为什么要5次卷积我也不清楚)
3.经过一系列的卷积得到三个预测结果,其大小分别为:(52,52,255),(26,26,255),(13,13,255)

为什么是255呢? 这里是个需要好好理解的地方。
255 = 3*( 80 + 1 + 4 )
3表示的是:每一个网格所拥有的先验框的个数为3个(yolo3中默认设置的)
80代表的是:cooc数据集里面一共有80给类
1代表的是:每一个先验框所包含某个物体的置信度
4代表的是:预测框相对于每个网格中心点x轴偏移情况x_offset,预测框相对于每个网格中心点y轴偏移情况 y_offset, 先验框的height, 先验框的width。(每个网格点的中心加上x_offset和y_offset就可以得到预测框的中心,通过调整先验框的w和h就可以得到预测框的w和h,这样就可以确定预测框的位置啦)
PS:如果是voc数据集的话,最后的预测结果应该就是:(52,52,25)
(26,26,26)(13,13,25)。因为是20+1+4

以上就是yolo3特征提取+处理网络的输入和输出全部内容。

代码实现:

from functools import wraps
from keras.regularizers import l2
from keras.layers import Conv2D, BatchNormalization, Activation, ZeroPadding2D, Add, Input, UpSampling2D, Concatenate
from keras.layers.advanced_activations import LeakyReLU
from keras.models import Model
from functools import reduce

#重新定义darknet中的Conv2D卷积操作
@wraps(Conv2D)
def DarknetConv2D(*args, **kwargs):
    darknet_conv_kwargs = {'kernel_regularizer':l2(5e-4)}
    darknet_conv_kwargs['padding'] = 'valid' if kwargs.get('strides')==(2, 2) else 'same'
    darknet_conv_kwargs.update(kwargs)
    return Conv2D(*args, **darknet_conv_kwargs)

#darknet卷积块: DarknetConv2D + BatchNormalization + Activation (三步)
def DarknetConv2D_BN_Leaky(*args, **kwargs):
    no_bias_kwargs = {'use_bias': False}
    no_bias_kwargs.update(kwargs)
    return compose(DarknetConv2D(*args, **no_bias_kwargs),
                   BatchNormalization(),
                   LeakyReLU(alpha=0.1))
#残差块  zeropadding + darknet卷积块 + shoutcut
def Resblock_body(x, num_filters, num_blocks):
    x = ZeroPadding2D(((1,0), (1,0)))(x)
    x = DarknetConv2D_BN_Leaky(num_filters, (3, 3), strides=(2, 2))(x)
    for i in range(num_blocks):
        y = DarknetConv2D_BN_Leaky(num_filters//2, (1,1))(x)
        y = DarknetConv2D_BN_Leaky(num_filters, (3,3))(y)
        x = Add()([x, y])
    return x
    
#DarkNet53特征提取网络
def darknet53_body(input_tensor):
    #shape 416, 416, 3  --> 416, 416, 32
    x = DarknetConv2D_BN_Leaky(32, (3, 3))(input_tensor)
    #shape 416, 416, 32  --> 208, 208, 64
    x = Resblock_body(x, 64, 1)
    #shape 208, 208, 64  --> 104, 104, 128
    x = Resblock_body(x, 128, 2)
    #shape 104, 104, 128  --> 52, 52, 256
    x = Resblock_body(x, 256, 8)
    feature_map1 = x
    #shape 52, 52, 256  --> 26, 26, 512
    x = Resblock_body(x, 512, 8)
    feature_map2 = x
    #shape 26, 26, 512  --> 13, 13, 1024
    x = Resblock_body(x, 1024, 4)
    feature_map3 = x
    return feature_map1, feature_map2, feature_map3
    
#特征处理网络
def feature_to_output(x, num_filters, out_filters):
    #五次卷积
    x = DarknetConv2D_BN_Leaky(num_filters, (1, 1))(x)
    x = DarknetConv2D_BN_Leaky(num_filters*2, (3, 3))(x)
    x = DarknetConv2D_BN_Leaky(num_filters, (1, 1))(x)
    x = DarknetConv2D_BN_Leaky(num_filters*2, (3, 3))(x)
    x = DarknetConv2D_BN_Leaky(num_filters, (1, 1))(x)
    #两个去处:1个进行3*3核1*1卷积直接输出   2.上采样跟上一级的特征层进行堆叠
    y = DarknetConv2D_BN_Leaky(num_filters*2, (3, 3))(x)
    y = DarknetConv2D(out_filters, (1, 1))(y)
    return x, y

#yolo3的整体网络结构
def yolo3_body(inputs, num_anchors, num_classes):
    #这里主要是对特征提取网路得到的三个特征层 进行处理 
    #52 52 256   26 26 512  13 13 1024
    feature1, feature2, feature3 = darknet53_body(inputs)
    darknet = Model(inputs, feature3)
    
    #预测结果1 13 13 255 这里的255上文有解释
    x, y1 = feature_to_output(darknet.output, 512, num_anchors*(num_classes+1+4))  
    x = DarknetConv2D_BN_Leaky(256, (1,1))(x) #卷积+上采样+feature2堆叠
    x = UpSampling2D(2)(x)
    x = Concatenate()([x, feature2])
    
    #预测结果2 26 26 255
    x, y2 = feature_to_output(x, 256, num_anchors * (num_classes + 1 + 4))
    x = DarknetConv2D_BN_Leaky(128, (1, 1))(x)# 卷积+上采样+feature1堆叠
    x = UpSampling2D(2)(x)
    x = Concatenate()([x, feature1])
    
    #预测结果3 52 52 255
    x, y3 = feature_to_output(x, 128, num_anchors * (num_classes + 1 + 4))
    return Model(inputs, [y1, y2, y3])

def compose(*funcs):
    if funcs:
        return reduce(lambda f, g: lambda *a, **kw: g(f(*a, **kw)), funcs)
    else:
        raise ValueError('Composition of empty sequence not supported.')

if __name__=="__main__":
    inputs = Input(shape=(416, 416, 3))
    yolo_model = yolo3_body(inputs, 3, 80)
    yolo_model.summary()#打印模型

想更深入地了解之后的编码解码过程可以去看这个博客,那里讲的非常详细https://blog.csdn.net/weixin_44791964/article/details/103276106

你可能感兴趣的:(卷积,算法,深度学习,卷积神经网络)