【YOLO系列】YOLO v3(网络结构图+代码)

news2024/11/23 12:31:17

文章目录

    • 网络结构
      • YOLO v3
      • YOLOv3-SPP
    • 多尺度预测
    • 损失函数
    • 参考

最近在研究YOLO系列,打算写一系列的YOLO博文。在YOLO的发展史中,v1到v3算法思想逐渐完备,后续的系列也都以v3为基石,在v3的基础上进行改进,所以很有必要单独出一篇详细讲解v3的博文。

网络结构

从 Ali Farhadi的YOLO网站主页下载YOLOv3-320和YOLOv3-spp的权重。加了SPP结构的YOLO v3,mAP会更高,但是速度会变慢。
在这里插入图片描述
在这里插入图片描述

然后基于qqwweee/keras-yolo3项目中的convert.py文件将.weight转换成.h5格式。权重格式转换需要keras和tensorflow的环境。转换命令如下所示。

python convert.py yolov3.cfg yolov3.weights model_data/yolo.h5
python convert.py yolov3-spp.cfg yolov3-spp.weights model_data/yolov3-spp.h5

YOLO v3

权重格式转换后会打印出yolov3的各层级关联信息,基于此信息,得到下述的YOLOv3网络结构简图,从此结构简图中,可以看出,YOLO v3整体网络架构是一个全卷积神经网络,无池化层,它通过卷积的步长实现张量尺寸的变化。每个Resn起始都有一个PCBL,将卷积的步长设置为2使得输出张量尺寸是输入张量尺寸的1/2,五个Resn的总步长为32。

YOLO v3的基础组件简写为CBL,即卷积+BN+leaky relu激活函数的组合;v3中大量应用了Res_block,Resn中的n表示有几个res_unit,v3中负责提取图像特征的backbone网络主要由五个Resn组成,v3论文中称主干网络中总有53个卷积,所以backbone网络被称为Darknet-53。但是打印出来的网络结构,却有1+3(1+21)+5(1+22)+17(1+28)+17+9(1+24)=52个卷积,又下载YOLOv3-416的权重进行转换验证,主干网络仍有52个卷积。

在这里插入图片描述
backbone的keras代码表示如下所示:

def resblock_body(x, num_filters, num_blocks):
    '''A series of resblocks starting with a downsampling Convolution2D'''
    # Darknet uses left and top padding instead of 'same' mode
    x = ZeroPadding2D(((1,0),(1,0)))(x) # padding
    x = DarknetConv2D_BN_Leaky(num_filters, (3,3), strides=(2,2))(x) # 步长为2,输出尺寸为1/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])
    return x

def darknet_body(x):
    '''Darknent body having 52 Convolution2D layers'''
    x = DarknetConv2D_BN_Leaky(32, (3,3))(x)
    x = resblock_body(x, 64, 1) #Res1
    x = resblock_body(x, 128, 2) #Res2
    x = resblock_body(x, 256, 8) #Res8
    x = resblock_body(x, 512, 8) #Res8
    x = resblock_body(x, 1024, 4) #Res4
    return x

YOLOv3-SPP

如下图所示,YOLOv3-SPP相比于YOLO v3多了一个空间金字塔池化SPP(Space Pyramid Pooling)结构。SPP的提出本来是为了解决剪裁、缩放等操作导致的图像失真问题,不用限制输入图像的尺寸大小,输出尺寸都是固定的。但是YOLO v3是全卷积神经网络,无全连接层,那么不需要固定尺寸的输入,为什么要加入SPP呢?而且加入SPP后能大幅提升精度?

在这里插入图片描述

YOLOv3-SPP中是由四个并行的分支构成的,三个池化层和一个跳跃连接,然后将此四个分支Concatenate,比原SPP增加了一个跳跃连接。YOLO v3通过SPP实现了局部特征和全局特征的融合,丰富了特征图的表达能力。如上图所示,SPP只被添加在第一个输出分支上,第二第三输出分支和YOLO v3保持一致。第二第三分支将两个相邻Res_block块的特征进行融合,某种程度上已经实现了不同程度的局部特征的融合,丰富了特征图的表达能力。所以,增加SPP能够提升检测的精度。

多尺度预测

从上述简图中,可以看出YOLO v3是在3个不同尺寸的特征图上进行预测的,这是YOLO v3的一个基本特征,多尺度预测,有助于获得更精细的检测框,可以大大改善对小物体的检测。如果输入的图像大小为 416 × 416 416\times 416 416×416,那么最后三个输出特征图的大小从上往下分别是 13 × 13 13 \times 13 13×13, 26 × 26 26 \times 26 26×26 52 × 52 52 \times 52 52×52。每种尺度的特征图上每个网格预测3个bbox,对于COCO数据集,每个特征图将的预测张量大小 N × N × [ 3 ∗ ( 4 + 1 + 80 ) ] N \times N \times [ 3 * (4 + 1 + 80)] N×N×[3(4+1+80)]。其中N的取值是[12,26,52],4是bbox的4个偏移量,1是物体预测置信度,80是COCO物体类别数量。
在这里插入图片描述

v3是通过上采样的方式实现多尺度的特征图。backbone中的每一个Res_block中第一个卷积会将步长设为2,使得特征图的尺寸缩小1倍。从上述的YOLO v3特征图中,我们可以看到有两次上采样,上采样操作将特征图的尺寸扩大一倍,这样得到的特征图尺寸就可以和上一个Res_block的特征图尺寸相同,将两个特征图进行Concatenate操作后,再经过5个CBL和CBL->C操作后就得到最后的输出特征图。

下述代码描述了yolov3的模型结构,make_last_layers为每个尺度分支最后的卷积层,x和y分别对应上述结构图中的CBL*5和CBL->C。yolo_body函数中,第一次上采样得到的特征图和darknet.layers[152].output(backbone中的第二个Res8输出)进行合并操作;第二次上采样的特征图和darknet.layers[92].output(backbone中的第一个Res8输出)进行合并操作。最后的输出有三个分别是y1,y2和y3。

def make_last_layers(x, num_filters, out_filters):
    '''6 Conv2D_BN_Leaky layers followed by a Conv2D_linear layer'''
    # x为上述结构图中的CBL*5
    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为上述结构图中的CBL->C
    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."""
    darknet = Model(inputs, darknet_body(inputs)) # backbone
    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,y2,y3])

YOLO v3中仍然沿用了v2中用k-means聚类确定bbox的尺寸大小,v3中选了如下9种anchors。

10,13, 16,30, 33,23, 30,61, 62,45, 59,119, 116,90, 156,198, 373,326

九种anchor,三种不同尺度的输出特征图,每个尺度特征图中的每个grid cell预测3个bbox,那也就是说每种尺度的输出特征图上分配到3个anchors。下采样的倍数越大,其对应的感受野(特征图上的某个点能看到的输入图像的区域)也就越大,那么其对应的特征图更适合检测大物体;相反,下采样的倍数越小,其对应的感受野越小,那么相对应的特征图包含的特征趋向于局部和细节,更适合小物体的检测。
下述代码将真实框转换成训练的输入格式,从下述代码中,我们也可以看出anchor的大小与不同尺度输出特征图的对应关系。

def preprocess_true_boxes(true_boxes, input_shape, anchors, num_classes):
    '''Preprocess true boxes to training input format

    Parameters
    ----------
    true_boxes: array, shape=(m, T, 5)
        Absolute x_min, y_min, x_max, y_max, class_id relative to input_shape.
    input_shape: array-like, hw, multiples of 32
    anchors: array, shape=(N, 2), wh
    num_classes: integer

    Returns
    -------
    y_true: list of array, shape like yolo_outputs, xywh are reletive value

    '''
    assert (true_boxes[..., 4]<num_classes).all(), 'class id must be less than num_classes'
    num_layers = len(anchors)//3 # default setting
    anchor_mask = [[6,7,8], [3,4,5], [0,1,2]] if num_layers==3 else [[3,4,5], [1,2,3]] # anchor

    true_boxes = np.array(true_boxes, dtype='float32')
    input_shape = np.array(input_shape, dtype='int32')
    # x,y,w,h处理
    boxes_xy = (true_boxes[..., 0:2] + true_boxes[..., 2:4]) // 2
    boxes_wh = true_boxes[..., 2:4] - true_boxes[..., 0:2]
    true_boxes[..., 0:2] = boxes_xy/input_shape[::-1]
    true_boxes[..., 2:4] = boxes_wh/input_shape[::-1]

    m = true_boxes.shape[0]
    # 每种尺度的特征图的网格大小,输入图像大小为416时为(13,26,52)
    grid_shapes = [input_shape//{0:32, 1:16, 2:8}[l] for l in range(num_layers)]
    # 对于COCO数据集,每个输出层的shape为(m,N,N,3,85)
    y_true = [np.zeros((m,grid_shapes[l][0],grid_shapes[l][1],len(anchor_mask[l]),5+num_classes), dtype='float32') for l in range(num_layers)]

    # Expand dim to apply broadcasting.
    anchors = np.expand_dims(anchors, 0)
    anchor_maxes = anchors / 2.
    anchor_mins = -anchor_maxes
    valid_mask = boxes_wh[..., 0]>0

    for b in range(m):
        # Discard zero rows.
        wh = boxes_wh[b, valid_mask[b]]
        if len(wh)==0: continue
        # Expand dim to apply broadcasting.
        wh = np.expand_dims(wh, -2)
        box_maxes = wh / 2.
        box_mins = -box_maxes
        # IoU计算
        intersect_mins = np.maximum(box_mins, anchor_mins)
        intersect_maxes = np.minimum(box_maxes, anchor_maxes)
        intersect_wh = np.maximum(intersect_maxes - intersect_mins, 0.)
        intersect_area = intersect_wh[..., 0] * intersect_wh[..., 1]
        box_area = wh[..., 0] * wh[..., 1]
        anchor_area = anchors[..., 0] * anchors[..., 1]
        iou = intersect_area / (box_area + anchor_area - intersect_area)

        # Find best anchor for each true box
        best_anchor = np.argmax(iou, axis=-1)

        for t, n in enumerate(best_anchor):
            for l in range(num_layers):
                if n in anchor_mask[l]:
                    i = np.floor(true_boxes[b,t,0]*grid_shapes[l][1]).astype('int32') # x * w
                    j = np.floor(true_boxes[b,t,1]*grid_shapes[l][0]).astype('int32') # y * h
                    k = anchor_mask[l].index(n) #0,1,2
                    c = true_boxes[b,t, 4].astype('int32') # class_id
                    y_true[l][b, j, i, k, 0:4] = true_boxes[b,t, 0:4]
                    y_true[l][b, j, i, k, 4] = 1
                    y_true[l][b, j, i, k, 5+c] = 1

    return y_true

损失函数

YOLO v3的每个bbox类别类别采用了多标签分类方式。使用独立的逻辑分类器替换softmax分类器。那么,训练时类别损失函数则采用BCE。

下述是YOLO v3的损失函数实现代码,回归损失会乘以一个box_loss_scale系数,它等于2-相对面积,取值范围为(1,2),查了很多解读,box_loss_scale系数是针对小物体的一个小技巧,用来加大对小框的损失。而且x,y的loss用的是交叉熵损失而不是和wh一样用平方差损失。

最终的Loss采用和的形式而未取平均。v3有三种尺度的输出,总共会产生 13 ∗ 13 ∗ 3 + 26 ∗ 26 ∗ 3 + 52 ∗ 52 ∗ 3 = 10647 13*13*3+26*26*3+52*52*3=10647 13133+26263+52523=10647个预测框,正负样本比巨大。如果图像中,只包含一个物体,正负样本比会高达1:10464。如果采用平均损失,会使得损失趋近于0,使得网络失去预测能力。

在这里插入图片描述

loss = 0
# batch size
m = K.shape(yolo_outputs[0])[0] # batch size, tensor
mf = K.cast(m, K.dtype(yolo_outputs[0]))
# object_mask
object_mask = y_true[l][..., 4:5] # bbox置信度
box_loss_scale = 2 - y_true[l][...,2:3]*y_true[l][...,3:4] # 权重=2-相对面积,取值范围为(1,2)
# K.binary_crossentropy is helpful to avoid exp overflow.
xy_loss = object_mask * box_loss_scale * K.binary_crossentropy(raw_true_xy, raw_pred[...,0:2], from_logits=True)
wh_loss = object_mask * box_loss_scale * 0.5 * K.square(raw_true_wh-raw_pred[...,2:4])
confidence_loss = object_mask * K.binary_crossentropy(object_mask, raw_pred[...,4:5], from_logits=True)+ (1-object_mask) * K.binary_crossentropy(object_mask, raw_pred[...,4:5], from_logits=True) * ignore_mask  # bbox置信度交叉熵损失
class_loss = object_mask * K.binary_crossentropy(true_class_probs, raw_pred[...,5:], from_logits=True) # bbox类别预测交叉熵损失

# 求和
xy_loss = K.sum(xy_loss) / mf
wh_loss = K.sum(wh_loss) / mf
confidence_loss = K.sum(confidence_loss) / mf
class_loss = K.sum(class_loss) / mf
loss += xy_loss + wh_loss + confidence_loss + class_loss

参考

  1. keras-yolo3
  2. darknet/yolo/
  3. YOLOv3: An Incremental Improvement

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/531533.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

KD600A变频抗干扰精密介质损耗测量仪

一、产品概述 KD600A变压器介质损耗测试仪是发电厂、变电站等现场自动测量各种高压电力设备介损正切值及电容量的高精度仪器。由于采用了变频技术能保证在强电场干扰下准确测量。仪器采用中文菜单操作&#xff0c;微机自动完成测量。 该仪器同样适用于车间、试验室、科研单位测…

映射及有关概念

映射的概念:有两个集合A,B&#xff0c;若A的任何元素都有唯一的B中元素与之对应&#xff0c;B中元素与之对应的称为像&#xff0c;A中对应的元素称为原像 一个集合也有像&#xff0c;定义为各自像的集合 B中集合也有原像&#xff0c;定义为各自原像的集合 虽然采用了f-1的符号&…

端口隔离、MAC地址表项、MAC地址漂移防止与检测

目录 前言 端口隔离 MAC地址表项 端口安全 MAC地址漂移检测 前言 目前网络中以太网技术的应用非常广泛。然而&#xff0c;各种网络攻击的存在&#xff08;例如针对ARP、DHCP等协议的攻击&#xff09;&#xff0c;不仅造成了网络合法用户无法正常访问网络资源&#xff0c;…

【案例教程】山洪径流过程模拟及洪水危险性评价技术

GIS水文分析&#xff08;ArcHydro、Spatial Anlysist等模块&#xff09;是流域水文模拟建模的重要工具&#xff0c;能够自动提取及计算流域边界、河网水系、流向、汇流时间和其它流域特征参数。美国陆军工程兵团开发的开源、免费Hec-RAS软件具有强大的空间数据分析与整合功能、…

每日学术速递5.13

CV - 计算机视觉 | ML - 机器学习 | RL - 强化学习 | NLP 自然语言处理 Subjects: cs.CV 1.VideoChat: Chat-Centric Video Understanding 标题&#xff1a;VideoChat&#xff1a;以聊天为中心的视频理解 作者&#xff1a;KunChang Li, Yinan He, Yi Wang, Yizhuo Li, Wen…

计算机网络基础知识(五)——什么是TCPUDP协议?图文并茂的方式对两大传输层协议进行从头到尾的讲解

文章目录 01 | &#x1f4d5; 什么是 T C P &#xff1f; \color{red}{什么是TCP&#xff1f;} 什么是TCP&#xff1f;&#x1f4d5;特点三次握手 && 四次挥手超时重传滑动窗口 02 | &#x1f4d9; 什么是 U D P &#xff1f; \color{orange}{什么是UDP&#xff1f;} 什…

1. 链表

b站懒猫数据结构课程笔记&#xff1a;https://www.bilibili.com/read/cv8013121?spm_id_from333.999.0.0 一、链表的概念 单链表&#xff1a;线性表的链接存储结构 单链表存储特点&#xff1a; 逻辑次序和物理次序不一定相同 元素之间的逻辑关系用指针表示 举例&#xff1a…

PASCAL VOC数据集

一、前言 之前寒假好像就学了&#xff0c;但是没有记笔记&#xff0c;现在看来还是得记笔记&#xff0c;都忘得差不多了啊。 二、数据集的介绍 2.1数据集背景 分类类别 2.2数据集文件结构&#xff1a; 2.3文件夹 2.3.1Annotations文件夹 对于标注文件Annotations&#xff1a;里…

基于SpringBoot框架的程序开发步骤

SpringBoot简介 1. 入门案例问题导入1.1 入门案例开发步骤1.2 基于SpringBoot官网创建项目1.3 SpringBoot项目快速启动 2. SpringBoot概述问题导入2.1 起步依赖2.2 辅助功能 1. 入门案例 问题导入 SpringMVC的HelloWord程序怎么写&#xff1f; SpringBoot是由Pivotal团队提供…

死锁、生产者和消费者问题

目录 生产者和消费者问题 死锁的概念 内存的基础知识 内存管理的概念 覆盖与交换 介绍一下PCB 连续分配管理方式​编辑 生产者和消费者问题 死锁的概念 什么是死锁 进程死锁、饥饿、死循环的区别 死锁产生的必要条件 什么时候会发生死锁 死锁的处理策略 内存的基础知识 内存…

微三云润秋带你解析商城分销系统

管理大师德鲁克曾说过&#xff1a;当今企业间的竞争&#xff0c;不是产品之间的竞争&#xff0c;而是商业模式之间的竞争。创业不只是项目选择重要&#xff0c;好的商业模式同样重要&#xff0c;如果没有好的商业模式&#xff0c;企业将会被淘汰。 今天我们要聊的这个商城就有点…

怎样设置CRM目标?有什么作用?

实施CRM系统可以帮助企业提高客户保留率&#xff0c;增加收入&#xff0c;并推动业绩增长。然而&#xff0c;在实施CRM系统之前&#xff0c;必须设定明确的目标&#xff0c;与企业的整体战略保持一致。在这篇文章中&#xff0c;我们来讨论实施CRM目标是什么&#xff0c;如何设定…

基于AD9172/AD9176的4 通道12.6GSPS 采样率16 位DA 播放FMC JESD204B 接口子卡模块

板卡概述 FMC_XM131 是一款4 通道12.6GSPS 采样率16 位DA 播放FMC子卡模块&#xff0c;该板卡为FMC标准&#xff0c;符合VITA57.4 规范&#xff0c;可以作为一个理想的IO 模块耦合至FPGA 前端&#xff0c;16 通道的JESD204B 接口通过FMC连接器连接至FPGA 的高速串行端…

【Python TurboGears】零基础也能轻松掌握的学习路线与参考资料

Python TurboGears是一款开源的web框架&#xff0c;它篮了多种Python库和工具&#xff0c;可以更容易地开发和维护web应用程序。TurboGears具有优秀的文档和活跃的社区支持&#xff0c;是学习web开发的理想选择之一。以下是Python TurboGears学习路线&#xff0c;参考资料和优秀…

一句话简短解析 jsjiami.v6

jsjiami.v6 是一种广泛使用的 JavaScript 代码混淆工具&#xff0c;它提供了多种代码混淆技术&#xff0c;包括变量名重命名、函数名重构、字符串替换、代码结构混淆等&#xff0c;可以将代码转换为难以理解和阅读的形式。在本文中&#xff0c;我们将对 jsjiami.v6 进行分析&am…

【FMC137】基于 VITA57.4 标准的4 路2GSPS/2.6GSPS/3GSPS 14 位AD 采集子卡模块--AD9208得多通道中文资料

板卡概述 FMC137 是一款基于VITA57.4 标准规范的JESD204B 接口FMC 子卡模块&#xff0c; 该模块可以实现4 路14-bit 、2GSPS/2.6GSPS/3GSPSADC 采集功能。该板卡ADC 器件采用ADI公司的AD9208 芯片&#xff0c;&#xff0c;与ADI 公司的AD9689 可以实现PIN 脚兼容。该ADC 与FPGA…

Agisoft Metashape 基于影像的外部点云着色

Agisoft Metashape 基于影像的外部点云着色 提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加 例如:第一章 Python 机器学习入门之pandas的使用 提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 Agisoft Metashape 基于影像的外…

JavaScript全解析——this指向

本系列内容为JS全解析&#xff0c;为千锋教育资深前端老师独家创作 致力于为大家讲解清晰JavaScript相关知识点&#xff0c;含有丰富的代码案例及讲解。如果感觉对大家有帮助的话&#xff0c;可以【点个关注】持续追更~ this指向&#xff08;掌握&#xff09; this 是一个关…

Python系列之判断和循环

感谢点赞和关注 &#xff0c;每天进步一点点&#xff01;加油&#xff01; 目录 一、判断语句 1.1 Shell里的判断语句格式 1.2 Python里的判断语句格式 二、循环语句 2.1 Python while循环 2.1.1 while 循环的基本格式 2.1.2 while 循环使用else语句 2.2 Python for 循…

每日学术速递5.16

CV - 计算机视觉 | ML - 机器学习 | RL - 强化学习 | NLP 自然语言处理 Subjects: cs.CV 1.Locally Attentional SDF Diffusion for Controllable 3D Shape Generation 标题&#xff1a;用于可控 3D 形状生成的局部注意 SDF 扩散 作者&#xff1a;Xin-Yang Zheng, Hao Pa…