人工智能学习74-Yolo主干网络

发布于:2025-07-10 ⋅ 阅读:(20) ⋅ 点赞:(0)

人工智能学习74-Yolo主干网络—快手视频
人工智能学习75-Yolo主干网络—快手视频
YoloV3是主干网络DarkNet53,主要是使用了残差块Residual block,残差块中包含3个卷积层,YoloV3主干网络使用5个残差块,故此称为DarkNet53网络。
通过不同多尺寸特征预测,YoloV3提取3个特征图,如果分类物体为20类,三个特征图分别为(13,13,75),(26,26,75),(52,52,75)。如果分类物体为80类,三个特征图分别为(13,13,255),(26,26,255),(52,52,255)。如下图:
在这里插入图片描述

Darknet类

from functools import wraps

#存在语法错误,使用下面一行代替  by chenqx at 2023/9/19
from keras.initializers import random_normal
#from keras.initializers.initializers_v2 import RandomNormal as random_normal

from keras.layers import (Add, BatchNormalization, Conv2D, LeakyReLU,ZeroPadding2D)
from keras.regularizers import l2
from utils import compose


#---------------------------------------------------#
#   单次卷积
#   DarknetConv2D
#   random_normal生成随机数,stddev=0.02方差为0.02
#---------------------------------------------------#
@wraps(Conv2D)
def DarknetConv2D(*args, **kwargs):
    #构造参数darknet_conv_kwargs
    darknet_conv_kwargs = {'kernel_initializer' : random_normal(stddev=0.02)}
    #含义:如果kwargs.get('strides')==(2,2)时padding=valid,否则padding=same
    #padding=valid p=0
    #padding=same strides=1,p=0;strides=3,p=1;strides=5,p=2;依次类推
    darknet_conv_kwargs['padding'] = 'valid' if kwargs.get('strides')==(2,2) else 'same'
    #更新参数darknet_conv_kwargs
    darknet_conv_kwargs.update(kwargs)
    #构造二维卷积网络层
    return Conv2D(*args, **darknet_conv_kwargs)

#---------------------------------------------------#
#   卷积块 -> 卷积 + 标准化 + 激活函数
#   DarknetConv2D + BatchNormalization + LeakyReLU
#---------------------------------------------------#
def DarknetConv2D_BN_Leaky(*args, **kwargs):
    #是否使用偏置项
    no_bias_kwargs = {'use_bias': False}
    no_bias_kwargs.update(kwargs)
    #依次调用DarknetConv2D(*args, **no_bias_kwargs)->BatchNormalization()->LeakyReLU(alpha=0.1)
    return compose( 
        DarknetConv2D(*args, **no_bias_kwargs), #进行二维卷积Conv2D
        BatchNormalization(), #批量标准化
        LeakyReLU(alpha=0.1)) #执行LeakyReLU操作

#---------------------------------------------------------------------#
#   残差结构
#   首先利用ZeroPadding2D和一个步长为2x2的卷积块进行高和宽的压缩
#   num_filters输出张量通道数量
#   num_blocks残差块数量,对num_blocks进行循环,循环内部是残差结构。
#---------------------------------------------------------------------#
def resblock_body(x, num_filters, num_blocks):
    #代表高与宽使用不同填报项  `((top_pad, bottom_pad), (left_pad, right_pad))`
    #top_pad=1,bottom_pad=0,left_pad=1,right_pad=0
    x = ZeroPadding2D(((1,0),(1,0)))(x)
    #(3,3)是卷积核大小,strides=(2,2)是高与宽的步长
    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) #卷积核大小(1,1)
        y = DarknetConv2D_BN_Leaky(num_filters, (3,3))(y) #卷积核大小(3,3)
        x = Add()([x,y]) #残差网络,输出与输入合并
    return x

#---------------------------------------------------#
#   darknet53 的主体部分
#   输入为一张416x416x3的图片
#   输出为三个有效特征层
#---------------------------------------------------#
def darknet_body(x):
    # 计算输出数据大小  shape = (输入大小 - 卷积核大小 + padding)/步长 + 1
    # 416,416,3 -> 416,416,32  计算输出大小 (416-3 + 2)/1 + 1 = 416  核大小为(3,3),步长为strides=(1,1)
    x = DarknetConv2D_BN_Leaky(32, (3,3))(x) #输出(416,416,32)
    # 416,416,32 -> 208,208,64 计算输出大小 (416-3 + 2)/2 + 1 = 208  核大小为(3,3),步长为strides=(2,2)
    x = resblock_body(x, 64, 1) #1个残差结构,输出(208,208,64)
    # 208,208,64 -> 104,104,128 计算输出大小 (208-3 + 2)/2 + 1 = 104  核大小为(3,3),步长为strides=(2,2)
    x = resblock_body(x, 128, 2) #2个残差结构,输出通道数(104,104,128)
    # 104,104,128 -> 52,52,256  计算输出大小 (104-3 + 2)/2 + 1 = 52  核大小为(3,3),步长为strides=(2,2)
    x = resblock_body(x, 256, 8) #8个残差结构,输出通道数(52,52,256)
    feat1 = x #feat1将图像划分为(52,52)网格大小
    # 52,52,256 -> 26,26,512    计算输出大小 (52-3 + 2)/2 + 1 = 26  核大小为(3,3),步长为strides=(2,2)
    x = resblock_body(x, 512, 8) #8个残差结构,输出通道数(26,26,512)
    feat2 = x #feat2将图像划分为(26,26)网格大小
    # 26,26,512 -> 13,13,1024   计算输出大小 (26-3 + 2)/2 + 1 = 13  核大小为(3,3),步长为strides=(2,2)
    x = resblock_body(x, 1024, 4) #4个残差结构,输出通道数(13,13,1024)
    feat3 = x #feat3将图像划分为(13,13)网格大小
    return feat1, feat2, feat3  #输出尺寸分别是(52,52,256) (26,26,512) (13,13,1024)

代码解释部分

方法DarknetConv2D_BN_Leaky第24行
在这里插入图片描述
padding='valid’是卷积神经网络中不进行边缘填充的操作模式,其特点是允许输出特征图的尺寸自然缩小,适用于无需保持输入输出尺寸一致的场景。
当使用padding='same’时,卷积操作的输出尺寸会通过计算来确保与输入尺寸相同。具体来说,系统会自动计算需要在输入特征图的边缘添加多少零填充,使得卷积操作后的输出尺寸与输入尺寸一致。
当调用DarknetConv2D时,根据卷积核尺寸自行决定padding的大小,
#padding=valid p=0
#padding=same strides=1,p=0;strides=3,p=1;strides=5,p=2;依次类推

方法DarknetConv2D_BN_Leaky第39行
在这里插入图片描述
方法compose是utils.py中定义的,是将各层依次连接在一起对数据进行处理。
在这里插入图片描述
例程:

def func1(x):
    return x+1
def func2(x):
    return x*2
from utils import compose

func3 = compose(func1, func2)
print(func3(2))

输出6

方法resblock_body第59行
在这里插入图片描述
体现了残差结构,x是为参与卷积之前的数据,y是参与卷积之后的数据,x=Add([x,y])是将卷积前后数据融合,避免特征数据损失。

方法darknet_body第85行
在这里插入图片描述
体现Yolo主干网络输出三个不同尺寸特征图。


网站公告

今日签到

点亮在社区的每一天
去签到