model.py
文件是用keras
模块编写的yolo
模型文件,主要包含14个函数:
DarknetConv2D
:为二维卷积层设置Darknet参数。DarknetConv2D_BN_Leaky
:由二维卷积层DarknetConv2D
与正则化层BatchNormalization
、激活函数LeakyReLU
组合而成。resblock_body
:封装了一个循环体:包含2个卷积核大小不同的DarknetConv2D_BN_Leaky
darknet_body
:Darknent
网络结构,含有52个二维卷积层。make_last_layers
:最后几个函数层,由6个DarknetConv2D_BN_Leaky
层和1个Conv2D_linear
层组成。yolo_body
:yolo
网络结构。tiny_yolo_body
:tiny_yolo
网络结构。yolo_head
:将最终网络层特征转换为边界框参数。yolo_correct_boxes
:获取正确的边界框。yolo_boxes_and_scores
:处理卷积层输出。yolo_eval
:在给定的输入和返回过滤框上评估YOLO模型。preprocess_true_boxes
:将true_boxes
预处理为训练输入格式box_iou
:检测框与原标注框的交并比。yolo_loss
:模型损失。
详细内容如下:
"""YOLO_v3 Model Defined in Keras."""
from functools import wraps
import numpy as np
import tensorflow as tf
from keras import backend as K
from keras.layers import Conv2D, Add, ZeroPadding2D, UpSampling2D, Concatenate, MaxPooling2D
from keras.layers.advanced_activations import LeakyReLU
from keras.layers.normalization import BatchNormalization
from keras.models import Model
from keras.regularizers import l2
from yolo3.utils import compose
@wraps(Conv2D)
def DarknetConv2D(*args, **kwargs):
"""
Wrapper to set Darknet parameters for Convolution2D.
为2维卷积层设置Darknet参数
返回带有Darknet参数的2维卷积层
"""
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参数的2维卷积层
def DarknetConv2D_BN_Leaky(*args, **kwargs):
"""
Darknet Convolution2D followed by BatchNormalization and LeakyReLU.
封装3个网络层:
1.带有Darknet参数的2维卷积层
2.批量正则化网络层:按批正则化前一个网络层的激活
3.激活函数层
"""
no_bias_kwargs = {
'use_bias': False}
no_bias_kwargs.update(kwargs)
return compose(
DarknetConv2D(*args, **no_bias_kwargs), #带有Darknet参数的2维卷积层
BatchNormalization(), #按批正则化前一个网络层的激活
LeakyReLU(alpha=0.1)) #激活函数
def resblock_body(x, num_filters, num_blocks):
'''
A series of resblocks starting with a downsampling Convolution2D
封装了一个循环体:包含2个卷积核大小不同的DarknetConv2D_BN_Leaky
x:输入的图像张量
num_filters:输出空间的维度
num_blocks:循环次数
本函数包含2个步骤:
1.填充,计算DarknetConv2D_BN_Leaky,输出维度为num_filters的x;
2.循环累加(循环体),将步骤1的输出作为该步骤的初始输入x:
首先计算1*1卷积核,然后将1*1卷积核的结果作为3*3卷积核的输入,计算出结果y
循环累加的初始输入x与得到的y进行相加,将相加结果赋值给x,进入下一次循环
'''
# Darknet uses left and top padding instead of 'same' mode
x = ZeroPadding2D(((1,0),(1,0)))(x) #对2维输入的的左侧和上侧进行0填充,((1,0),(1,0))表示((上,下),(左,右))
x = DarknetConv2D_BN_Leaky(num_filters, (3,3), strides=(2,2))(x) #3*3的卷积核,卷积步长(2,2)
for i in range(num_blocks):
y = compose(
DarknetConv2D_BN_Leaky(num_filters//2, (1,1)), #1*1的卷积核
DarknetConv2D_BN_Leaky(num_filters, (3,3)))(x) #3*3的卷积核
x = Add()([x,y]) #求和,输出维度与x,y一致
return x
def darknet_body(x):
'''
Darknent body having 52 Convolution2D layers
Darknent网络层主干,主要作用是对图像进行特征提取,包含52个2维卷积核层:52=1+3+5+17+17+9
1个DarknetConv2D_BN_Leaky里包含1个2维卷积层,1个批量正则化层,1个激活层
1个resblock_body包含1+2*num_blocks个DarknetConv2D_BN_Leaky
'''
x = DarknetConv2D_BN_Leaky(32, (3,3))(x) #1
x = resblock_body(x, 64, 1) #1+2*1=3
x = resblock_body(x, 128, 2) #1+2*2=5
x = resblock_body(x, 256, 8) #1+2*8=17
x = resblock_body(x, 512, 8) #1+2*8=17
x = resblock_body(x, 1024, 4) #1+2*4=9
return x
def make_last_layers(x, num_filters, out_filters):
'''
6 Conv2D_BN_Leaky layers followed by a Conv2D_linear layer
7个2维卷积层
5个DarknetConv2D_BN_Leaky计算得x
1个DarknetConv2D_BN_Leaky,1个DarknetConv2D计算得y
num_filters:中间卷积层x得输出通道数,也可以理解为特征数量
out_filters:最终层y的通道数
返回x,y
'''
x = compose(
DarknetConv2D_BN_Leaky(num_filters, (1,1)),
DarknetConv2D_BN_Leaky(num_filters*2, (3,3)),
DarknetConv2D_BN_Leaky(num_filters, (1,1)),
DarknetConv2D_BN_Leaky(num_filters*2, (3,3)),
DarknetConv2D_BN_Leaky(num_filters, (1,1)))(x)
y = compose(
DarknetConv2D_BN_Leaky(num_filters*2, (3,3)),
DarknetConv2D(out_filters, (1,1)))(x)
return x, y
def yolo_body(inputs, num_anchors, num_classes):
"""
Create YOLO_V3 model CNN body in Keras.
YOLO_V3卷积神经网络主干,共包含75个卷积层
1.Darknent模型,包含52个卷积层
2.Darknent模型的输出作为make_last_layers输入,输出x,y1,包含7个卷积层
3.将x进行DarknetConv2D_BN_Leaky,UpSampling2D计算,并将结果与Darknent模型的152层的输出进行拼接,得x,包含1个卷积层
4.将输出x作为make_last_layers输入,输出x,y2,包含7个卷积层
5.将x进行DarknetConv2D_BN_Leaky,UpSampling2D计算,并将结果与Darknent模型的92层的输出进行拼接,得x,包含1个卷积层
6.将输出x作为make_last_layers输入,输出x,y3,包含7个卷积层
"""
darknet = Model(inputs, darknet_body(inputs)) #Model(inputs, outputs, name=None)
x, y1 = make_last_layers(darknet.output, 512, num_anchors*(num_classes+5))
x = compose(
DarknetConv2D_BN_Leaky(256, (1,1)),
UpSampling2D(2))(x)
x = Concatenate()([x,darknet.layers[152].output]) #拼接
x, y2 = make_last_layers(x, 256, num_anchors*(num_classes+5))
x = compose(
DarknetConv2D_BN_Leaky(128, (1,1)),
UpSampling2D(2))(x)
x = Concatenate()([x,darknet.layers[92].output])
x, y3 = make_last_layers(x, 128, num_anchors*(num_classes+5))
return Model(inputs, [y1