精华内容
下载资源
问答
  • YOLOv3 算法的一点理解

    千次阅读 2020-03-20 16:21:06
    今天讲一讲 YOLOv3, 目标检测网络的巅峰之作, 疾如风,快如闪电。</p> 算法背景假设我们想对下面这张 416 X 416 大小的图片进行预测,把图中 dog、bicycle 和 car 三种物体给框出来,这涉及到以下三个...
        <p>今天讲一讲 YOLOv3, 目标检测网络的巅峰之作, 疾如风,快如闪电。</p>
    

    算法背景

    假设我们想对下面这张 416 X 416 大小的图片进行预测,把图中 dog、bicycle 和 car 三种物体给框出来,这涉及到以下三个过程:

    • 怎么在图片上找出很多有价值的候选框?
    • 接着判断候选框里有没有物体?
    • 如果有物体的话,那么它属于哪个类别?

    听起来就像把大象装进冰箱,分三步走。事实上,目前的 anchor-based 机制算法例如 RCNN、Faster rcnn 以及 YOLO 算法都是这个思想。最早的时候,RCNN 是这么干的,它首先利用 Selective Search 的方法通过图片上像素之间的相似度和纹理特征进行区域合并,然后提出很多候选框并喂给 CNN 网络提取出特征向量 (embeddings),最后利用特征向量训练 SVM 来对目标和背景进行分类。

    image

    这是最早利用神经网络进行目标检测的开山之作,虽然现在看来有不少瑕疵,例如:

    • Selective Search 会在图片上提取2000个候选区域,每个候选区域都会喂给 CNN 进行特征提取,这个过程太冗余啦,其实这些候选区域之间很多特征其实是可以共享的;
    • 由于 CNN 最后一层是全连接层,因此输入图片的尺寸大小也有限制,只能进行 Crop 或者 Warp,这样一来图片就会扭曲、变形和失真;
    • 在利用 SVM 分类器对候选框进行分类的时候,每个候选框的特征向量都要保留在磁盘上,很浪费空间!

    尽管如此,但仍不可否认它具有划时代的意义,至少告诉后人我们是可以利用神经网络进行目标检测的。后面,一些大神们在此基础上提出了很多改进,从 Fast RCNN 到 Faster RCNN 再到 Mask RCNN, 目标检测的 region proposal 过程变得越来越有针对性,并提出了著名的 RPN 网络去学习如何给出高质量的候选框,然后再去判断所属物体的类别。简单说来就是: 提出候选框,然后分类,这就是我们常说的 two-stage 算法。two-stage 算法的好处就是精度较高,但是检测速度满足不了实时性的要求。

    在这样的背景下,YOLO 算法横空出世,江湖震惊!

    YOLO 算法简介

    发展历程

    2015 年 Redmon J 等提出 YOLO 网络, 其特点是将生成候选框与分类回归合并成一个步骤, 预测时特征图被分成 7x7 个 cell, 对每个 cell 进行预测, 这就大大降低了计算复杂度, 加快了目标检测的速度, 帧率最高可达 45 fps!

    时隔一年,Redmon J 再次提出了YOLOv2, 与前代相比: YOLOv1是利用全连接层直接预测bounding box的坐标,而YOLOv2借鉴了Faster R-CNN的思想,引入anchor。它在VOC2007 测试集上的 mAP 由 67.4% 提高到 78.6%, 然而由于一个 cell 只负责预测一个物体, 面对重叠性的目标的识别得并不够好。

    最终在 2018 年 4 月, 作者又发布了第三个版本 YOLOv3,它延续了 YOLOv2 的 anchor 策略,没有太大变化,主要的改变在于融合了多尺度特征。结果在 COCO 数据集上的 mAP-50 由 YOLOv2 的 44.0% 提高到 57.9%, 与 mAP 61.1% 的 RetinaNet 相比, RetinaNet 在输入尺寸 500×500 的情况下检测速度约 98 ms/帧, 而 YOLOv3 在输入尺寸 416×416 时检测速 度可达 29 ms/帧。

    上面这张图足以秒杀一切, 说明 YOLOv3 在保证速度的前提下, 也达到了很高的准确率。

    基本思想

    作者在YOLO算法中把物体检测(object detection)问题处理成回归问题,并将图像分为S×S的网格。如果一个目标的中心落入格子,该格子就负责检测该目标。

    If the center of an object falls into a grid cell, that grid cell is responsible for detecting that object.

    每个网格都会输出 bounding box,confidence 和 class probability map。其中:

    • bounding box 包含4个值:x,y,w,h,(x,y)代表 box 的中心。(w,h)代表 box 的宽和高;
    • confidence 表示这个预测框中包含物体的概率,其实也是预测框与真实框之间的 iou 值;
    • class probability 表示的是该物体的类别概率,在 YOLOv3 中采用的是二分类的方法。

    网络结构

    下面这幅图就是 YOLOv3 网络的整体结构,在图中我们可以看到:尺寸为 416X416 的输入图片进入 Darknet-53 网络后得到了 3 个分支,这些分支在经过一系列的卷积、上采样以及合并等操作后最终得到了三个尺寸不一的 feature map,形状分别为 [13, 13, 255]、[26, 26, 255] 和 [52, 52, 255]。

    image

    讲了这么多,还是不如看代码来得亲切。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    def YOLOv3(input_layer):
    # 输入层进入 Darknet-53 网络后,得到了三个分支
    route_1, route_2, conv = backbone.darknet53(input_layer)
    # 见上图中的橘黄色模块(DBL),一共需要进行5次卷积操作
    conv = common.convolutional(conv, (1, 1, 1024, 512))
    conv = common.convolutional(conv, (3, 3, 512, 1024))
    conv = common.convolutional(conv, (1, 1, 1024, 512))
    conv = common.convolutional(conv, (3, 3, 512, 1024))
    conv = common.convolutional(conv, (1, 1, 1024, 512))
    conv_lobj_branch = common.convolutional(conv, (3, 3, 512, 1024))
    # conv_lbbox 用于预测大尺寸物体,shape = [None, 13, 13, 255]
    conv_lbbox = common.convolutional(conv_lobj_branch, (1, 1, 1024, 3*(NUM_CLASS + 5)),
    activate=False, bn=False)
    conv = common.convolutional(conv, (1, 1, 512, 256))
    # 这里的 upsample 使用的是最近邻插值方法,这样的好处在于上采样过程不需要学习,从而减少了网络参数
    conv = common.upsample(conv)
    conv = tf.concat([conv, route_2], axis=-1)
    conv = common.convolutional(conv, (1, 1, 768, 256))
    conv = common.convolutional(conv, (3, 3, 256, 512))
    conv = common.convolutional(conv, (1, 1, 512, 256))
    conv = common.convolutional(conv, (3, 3, 256, 512))
    conv = common.convolutional(conv, (1, 1, 512, 256))
    conv_mobj_branch = common.convolutional(conv, (3, 3, 256, 512))
    # conv_mbbox 用于预测中等尺寸物体,shape = [None, 26, 26, 255]
    conv_mbbox = common.convolutional(conv_mobj_branch, (1, 1, 512, 3*(NUM_CLASS + 5)),
    activate=False, bn=False)
    conv = common.convolutional(conv, (1, 1, 256, 128))
    conv = common.upsample(conv)
    conv = tf.concat([conv, route_1], axis=-1)
    conv = common.convolutional(conv, (1, 1, 384, 128))
    conv = common.convolutional(conv, (3, 3, 128, 256))
    conv = common.convolutional(conv, (1, 1, 256, 128))
    conv = common.convolutional(conv, (3, 3, 128, 256))
    conv = common.convolutional(conv, (1, 1, 256, 128))

    conv_sobj_branch = common.convolutional(conv, (3, 3, 128, 256))
    # conv_sbbox 用于预测小尺寸物体,shape = [None, 52, 52, 255]
    conv_sbbox = common.convolutional(conv_sobj_branch, (1, 1, 256, 3*(NUM_CLASS +5)),
    activate=False, bn=False)
    return [conv_sbbox, conv_mbbox, conv_lbbox]

    Darknet53 结构

    Darknet-53 的主体框架如下图所示,它主要由 Convolutional 和 Residual 结构所组成。需要特别注意的是,最后三层 Avgpool、Connected 和 softmax layer 是用于在 Imagenet 数据集上作分类训练用的。当我们用 Darknet-53 层对图片提取特征时,是不会用到这三层的。

    Darknet-53 有多牛逼?看看下面这张图,作者进行了比较,得出的结论是 Darknet-53 在精度上可以与最先进的分类器进行媲美,同时它的浮点运算更少,计算速度也最快。和 ReseNet-101 相比,Darknet-53 网络的速度是前者的1.5倍;虽然 ReseNet-152 和它性能相似,但是用时却是它的2倍以上。

    此外,Darknet-53 还可以实现每秒最高的测量浮点运算,这就意味着网络结构可以更好地利用 GPU,从而使其测量效率更高,速度也更快。

    Convolutional 结构

    Convolutional 结构其实很简单,就是普通的卷积层,其实没啥讲的。但是对于 if downsample 的情况,初学者可能觉得有点陌生, ZeroPadding2D 是什么层?

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    def convolutional(input_layer, filters_shape, downsample=False, activate=True, bn=True):
    if downsample:
    input_layer = tf.keras.layers.ZeroPadding2D(((1, 0), (1, 0)))(input_layer)
    padding = 'valid'
    strides = 2
    else:
    strides = 1
    padding = 'same'
    conv = tf.keras.layers.Conv2D(filters=filters_shape[-1],
    kernel_size = filters_shape[0],
    strides=strides, padding=padding, use_bias=not bn,
    kernel_regularizer=tf.keras.regularizers.l2(0.0005),
    kernel_initializer=tf.random_normal_initializer(stddev=0.01),
    bias_initializer=tf.constant_initializer(0.))(input_layer)
    if bn: conv = BatchNormalization()(conv)
    if activate == True: conv = tf.nn.leaky_relu(conv, alpha=0.1)
    return conv

    讲到 ZeroPadding2D层,我们得先了解它是什么,为什么有这个层。对于它的定义,Keras 官方给了很好的解释:

    keras.layers.convolutional.ZeroPadding2D(padding=(1, 1), data_format=None) 说明: 对2D输入(如图片)的边界填充0,以控制卷积以后特征图的大小

    其实就是对图片的上下左右四个边界填充0而已,padding=((top_pad, bottom_pad), (left_pad, right_pad))。 很简单吧,快打开你的 ipython 试试吧!

    1
    2
    3
    4
    5
    6
    7
    In [2]: x=tf.keras.layers.Input([416,416,3])

    In [3]: tf.keras.layers.ZeroPadding2D(padding=((1,0),(1,0)))(x)
    Out[3]: <tf.Tensor 'zero_padding2d/Identity:0' shape=(None, 417, 417, 3) dtype=float32>

    In [4]: tf.keras.layers.ZeroPadding2D(padding=((1,1),(1,1)))(x)
    Out[4]: <tf.Tensor 'zero_padding2d_1/Identity:0' shape=(None, 418, 418, 3) dtype=float32>

    Residual 残差模块

    残差模块最显著的特点是使用了 short cut 机制(有点类似于电路中的短路机制)来缓解在神经网络中增加深度带来的梯度消失问题,从而使得神经网络变得更容易优化。它通过恒等映射(identity mapping)的方法使得输入和输出之间建立了一条直接的关联通道,从而使得网络集中学习输入和输出之间的残差。

    1
    2
    3
    4
    5
    6
    def residual_block(input_layer, input_channel, filter_num1, filter_num2):
    short_cut = input_layer
    conv = convolutional(input_layer, filters_shape=(1, 1, input_channel, filter_num1))
    conv = convolutional(conv , filters_shape=(3, 3, filter_num1, filter_num2))
    residual_output = short_cut + conv
    return residual_output

    提取特征

    要想详细地知道 YOLO 的预测过程,就非常有必要先来了解一下什么是特征映射 (feature map) 和特征向量 (embeddings)。

    特征映射

    当我们谈及 CNN 网络,总能听到 feature map 这个词。它也叫特征映射,简单说来就是输入图像在与卷积核进行卷积操作后得到图像特征

    一般而言,CNN 网络在对图像自底向上提取特征时,feature map 的数量(其实也对应的就是卷积核的数目) 会越来越多,而空间信息会越来越少,其特征也会变得越来越抽象。比如著名的 VGG16 网络,它的 feature map 变化就是这个样子。

    feature map 在空间尺寸上越来越小,但在通道尺寸上变得越来越深,这就是 VGG16 的特点。

    特征向量

    讲到 feature map 哦,就不得不提一下人脸识别领域里经常提到的 embedding. 一般来说,它其实就是 feature map 被最后一层全连接层所提取到特征向量。早在2006年,深度学习鼻祖 hinton 就在《SCIENCE》上发表了一篇论文,首次利用自编码网络对 mnist 手写数字提取出了特征向量(一个2维或3维的向量)。值得一提的是,也是这篇论文揭开了深度学习兴起的序幕。

    下面就是上面这张图片里的数字在 CNN 空间里映射后得到的特征向量在2维和3维空间里的样子:

    前面我们提到:CNN 网络在对图像自底向上提取特征时,得到的 feature map 一般都是在空间尺寸上越来越小,而在通道尺寸上变得越来越深。 那么,为什么要这么做?

    其实,这就与 ROI (感兴趣区域)映射到 Feature Map 有关。在上面这幅图里:原图里的一块 ROI 在 CNN 网络空间里映射后,在 feature map 上空间尺寸会变得更小,甚至是一个点, 但是这个点的通道信息会很丰富,这些通道信息是 ROI 区域里的图片信息在 CNN 网络里映射得到的特征表示。由于图像中各个相邻像素在空间上的联系很紧密,这在空间上造成具有很大的冗余性。因此,我们往往会通过在空间上降维,而在通道上升维的方式来消除这种冗余性,尽量以最小的维度来获得它最本质的特征。

    原图左上角红色 ROI 经 CNN 映射后在 feature map 空间上只得到了一个点,但是这个点有85个通道。那么,ROI的维度由原来的 [32, 32, 3] 变成了现在的 85 维,这难道又不是降维打击么?👊

    按照我的理解,这其实就是 CNN 网络对 ROI 进行特征提取后得到的一个 85 维的特征向量。这个特征向量前4个维度代表的是候选框信息,中间这个维度代表是判断有无物体的概率,后面80个维度代表的是对 80 个类别的分类概率信息。

    如何检测

    多尺度检测

    YOLOv3 对输入图片进行了粗、中和细网格划分,以便分别实现对大、中和小物体的预测。假如输入图片的尺寸为 416X416, 那么得到粗、中和细网格尺寸分别为 13X13、26X26 和 52X52。这样一算,那就是在长宽尺寸上分别缩放了 32、16 和 8 倍。

    image

    decode 处理

    YOLOv3 网络的三个分支输出会被送入 decode 函数中对 Feature Map 的通道信息进行解码。 在下面这幅图里:黑色虚线框代表先验框(anchor),蓝色框表示的是预测框.

    • bhbh 则代表网格左上角的坐标。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    def decode(conv_output, i=0):
    # 这里的 i=0、1 或者 2, 以分别对应三种网格尺度
    conv_shape = tf.shape(conv_output)
    batch_size = conv_shape[0]
    output_size = conv_shape[1]
    conv_output = tf.reshape(conv_output, (batch_size, output_size,
    output_size, 3, 5 + NUM_CLASS))
    conv_raw_dxdy = conv_output[:, :, :, :, 0:2] # 中心位置的偏移量
    conv_raw_dwdh = conv_output[:, :, :, :, 2:4] # 预测框长宽的偏移量
    conv_raw_conf = conv_output[:, :, :, :, 4:5] # 预测框的置信度
    conv_raw_prob = conv_output[:, :, :, :, 5: ] # 预测框的类别概率
    # 好了,接下来需要画网格了。其中,output_size 等于 13、26 或者 52
    y = tf.tile(tf.range(output_size, dtype=tf.int32)[:, tf.newaxis], [1, output_size])
    x = tf.tile(tf.range(output_size, dtype=tf.int32)[tf.newaxis, :], [output_size, 1])
    xy_grid = tf.concat([x[:, :, tf.newaxis], y[:, :, tf.newaxis]], axis=-1)
    xy_grid = tf.tile(xy_grid[tf.newaxis, :, :, tf.newaxis, :], [batch_size, 1, 1, 3, 1])
    xy_grid = tf.cast(xy_grid, tf.float32) # 计算网格左上角的位置
    # 根据上图公式计算预测框的中心位置
    pred_xy = (tf.sigmoid(conv_raw_dxdy) + xy_grid) * STRIDES[i]
    # 根据上图公式计算预测框的长和宽大小
    pred_wh = (tf.exp(conv_raw_dwdh) * ANCHORS[i]) * STRIDES[i]
    pred_xywh = tf.concat([pred_xy, pred_wh], axis=-1)
    pred_conf = tf.sigmoid(conv_raw_conf) # 计算预测框里object的置信度
    pred_prob = tf.sigmoid(conv_raw_prob) # 计算预测框里object的类别概率
    return tf.concat([pred_xywh, pred_conf, pred_prob], axis=-1)

    NMS 处理

    非极大值抑制(Non-Maximum Suppression,NMS),顾名思义就是抑制不是极大值的元素,说白了就是去除掉那些重叠率较高并且 score 评分较低的边界框。 NMS 的算法非常简单,迭代流程如下:

    • 流程1: 判断边界框的数目是否大于0,如果不是则结束迭代;
    • 流程2: 按照 socre 排序选出评分最大的边界框 A 并取出;
    • 流程3: 计算这个边界框 A 与剩下所有边界框的 iou 并剔除那些 iou 值高于阈值的边界框,重复上述步骤;
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    # 流程1: 判断边界框的数目是否大于0
    while len(cls_bboxes) > 0:
    # 流程2: 按照 socre 排序选出评分最大的边界框 A
    max_ind = np.argmax(cls_bboxes[:, 4])
    # 将边界框 A 取出并剔除
    best_bbox = cls_bboxes[max_ind]
    best_bboxes.append(best_bbox)
    cls_bboxes = np.concatenate([cls_bboxes[: max_ind], cls_bboxes[max_ind + 1:]])
    # 流程3: 计算这个边界框 A 与剩下所有边界框的 iou 并剔除那些 iou 值高于阈值的边界框
    iou = bboxes_iou(best_bbox[np.newaxis, :4], cls_bboxes[:, :4])
    weight = np.ones((len(iou),), dtype=np.float32)
    iou_mask = iou > iou_threshold
    weight[iou_mask] = 0.0
    cls_bboxes[:, 4] = cls_bboxes[:, 4] * weight
    score_mask = cls_bboxes[:, 4] > 0.
    cls_bboxes = cls_bboxes[score_mask]

    最后所有取出来的边界框 A 就是我们想要的。不妨举个简单的例子:假如5个边界框及评分为: A: 0.9,B: 0.08,C: 0.8, D: 0.6,E: 0.5,设定的评分阈值为 0.3,计算步骤如下。

    • 步骤1: 边界框的个数为5,满足迭代条件;
    • 步骤2: 按照 socre 排序选出评分最大的边界框 A 并取出;
    • 步骤3: 计算边界框 A 与其他 4 个边界框的 iou,假设得到的 iou 值为:B: 0.1,C: 0.7, D: 0.02, E: 0.09, 剔除边界框 C;
    • 步骤4: 现在只剩下边界框 B、D、E,满足迭代条件;
    • 步骤5: 按照 socre 排序选出评分最大的边界框 D 并取出;
    • 步骤6: 计算边界框 D 与其他 2 个边界框的 iou,假设得到的 iou 值为:B: 0.06,E: 0.8,剔除边界框 E;
    • 步骤7: 现在只剩下边界框 B,满足迭代条件;
    • 步骤8: 按照 socre 排序选出评分最大的边界框 B 并取出;
    • 步骤9: 此时边界框的个数为零,结束迭代。

    最后我们得到了边界框 A、B、D,但其中边界框 B 的评分非常低,这表明该边界框是没有物体的,因此应当抛弃掉。在代码中:

    1
    2
    3
    4
    # # (5) discard some boxes with low scores
    classes = np.argmax(pred_prob, axis=-1)
    scores = pred_conf * pred_prob[np.arange(len(pred_coor)), classes]
    score_mask = scores > score_threshold

    在 YOLO 算法中,NMS 的处理有两种情况:一种是所有的预测框一起做 NMS 处理,另一种情况是分别对每个类别的预测框做 NMS 处理。后者会出现一个预测框既属于类别 A 又属于类别 B 的现象,这比较适合于一个小单元格中同时存在多个物体的情况。

    anchor 响应机制

    K-means 聚类

    首先需要抛出一个问题:先验框 anchor 是怎么来的?对于这点,作者在 YOLOv2 论文里给出了很好的解释:

    we run k-means clustering on the training set bounding boxes to automatically find good priors.

    其实就是使用 k-means 算法对训练集上的 boudnding box 尺度做聚类。此外,考虑到训练集上的图片尺寸不一,因此对此过程进行归一化处理。

    k-means 聚类算法有个坑爹的地方在于,类别的个数需要人为事先指定。这就带来一个问题,先验框 anchor 的数目等于多少最合适?一般来说,anchor 的类别越多,那么 YOLO 算法就越能在不同尺度下与真实框进行回归,但是这样就会导致模型的复杂度更高,网络的参数量更庞大。

    We choose k = 5 as a good tradeoff between model complexity and high recall. If we use 9 centroids we see a much higher average IOU. This indicates that using k-means to generate our bounding box starts the model off with a better representation and makes the task easier to learn.

    在上面这幅图里,作者发现 k = 5 时就能较好地实现高召回率与模型复杂度之间的平衡。由于在 YOLOv3 算法里一共有3种尺度预测,因此只能是3的倍数,所以最终选择了 9 个先验框。这里还有个问题需要解决,k-means 度量距离的选取很关键。距离度量如果使用标准的欧氏距离,大框框就会比小框产生更多的错误。在目标检测领域,我们度量两个边界框之间的相似度往往以 IOU 大小作为标准。因此,这里的度量距离也和 IOU 有关。需要特别注意的是,这里的IOU计算只用到了 boudnding box 的长和宽。在我的代码里,是认为两个先验框的左上角位置是相重合的。(其实在这里偏移至哪都无所谓,因为聚类的时候是不考虑 anchor 框的位置信息的。)

    d( box, centroid )=1IOU(box, centroid )d( box, centroid )=1−IOU(box, centroid )
    ,即欧几里德范数,常用于计算向量的长度;

    当 L1 或 L2 范数都相同的时候,发现 IoU 和 GIoU 的值差别都很大,这表明使用 L 范数来度量边界框的距离是不合适的。在这种情况下,学术界普遍使用 IoU 来衡量两个边界框之间的相似性。作者发现使用 IoU 会有两个缺点,导致其不太适合做损失函数:

    • 预测框和真实框之间没有重合时,IoU 值为 0, 导致优化损失函数时梯度也为 0,意味着无法优化。例如,场景 A 和场景 B 的 IoU 值都为 0,但是显然场景 B 的预测效果较 A 更佳,因为两个边界框的距离更近( L 范数更小)。

    尽管场景 A 和场景 B 的 IoU 值都为 0,但是场景 B 的预测效果较 A 更佳,这是因为两个边界框的距离更近。

    • 即使预测框和真实框之间相重合且具有相同的 IoU 值时,检测的效果也具有较大差异,如下图所示。

    上面三幅图的 IoU = 0.33, 但是 GIoU 值分别是 0.33, 0.24 和 -0.1, 这表明如果两个边界框重叠和对齐得越好,那么得到的 GIoU 值就会越高。

    GIoU 的计算公式

    the smallest enclosing convex object C 指的是最小闭合凸面 C,例如在上述场景 A 和 B 中,C 的形状分别为:

    图中绿色包含的区域就是最小闭合凸面 C,the smallest enclosing convex object。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    def bbox_giou(boxes1, boxes2):
    ......
    # 计算两个边界框之间的 iou 值
    iou = inter_area / union_area
    # 计算最小闭合凸面 C 左上角和右下角的坐标
    enclose_left_up = tf.minimum(boxes1[..., :2], boxes2[..., :2])
    enclose_right_down = tf.maximum(boxes1[..., 2:], boxes2[..., 2:])
    enclose = tf.maximum(enclose_right_down - enclose_left_up, 0.0)
    # 计算最小闭合凸面 C 的面积
    enclose_area = enclose[..., 0] * enclose[..., 1]
    # 根据 GIoU 公式计算 GIoU 值
    giou = iou - 1.0 * (enclose_area - union_area) / enclose_area
    return giou

    模型训练

    权重初始化

    训练神经网络尤其是深度神经网络所面临的一个问题是,梯度消失或梯度爆炸,也就是说 当你训练深度网络时,导数或坡度有时会变得非常大,或非常小甚至以指数方式变小,这个时候我们看到的损失就会变成了 NaN。假设你正在训练下面这样一个极深的神经网络,为了简单起见,这里激活函数 g(z) = z 并且忽略偏置参数。

    这里我们首先假定 g(z)=z,b[l]=0g(z)=z,b[l]=0,激活函数的值将以指数形式递减;

    其实这里直观的理解是:如果权重 W 只比 1 略大一点,或者说只比单位矩阵大一点,深度神经网络的输出将会以爆炸式增长,而如果 W 比 1 略小一点,可能是 0.9, 0.9,每层网络的输出值将会以指数级递减。因此合适的初始化权重值就显得尤为重要! 下面就写个简单的代码给大家演示一下。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    import numpy as np
    import tensorflow as tf
    import matplotlib.pyplot as plt

    x = np.random.randn(2000, 800) * 0.01 # 制作输入数据
    stds = [0.1, 0.05, 0.01, 0.005, 0.001] # 尝试使用不同标准差,这样初始权重大小也不一样

    for i, std in enumerate(stds):
    # 第一层全连接层
    dense_1 = tf.keras.layers.Dense(750, kernel_initializer=tf.random_normal_initializer(std), activation='tanh')
    output_1 = dense_1(x)
    # 第二层全连接层
    dense_2 = tf.keras.layers.Dense(700, kernel_initializer=tf.random_normal_initializer(std), activation='tanh')
    output_2 = dense_2(output_1)
    # 第三层全连接层
    dense_3 = tf.keras.layers.Dense(650, kernel_initializer=tf.random_normal_initializer(std), activation='tanh')
    output_3 = dense_3(output_2).numpy().flatten()

    plt.subplot(1, len(stds), i+1)
    plt.hist(output_3, bins=60, range=[-1, 1])
    plt.xlabel('std = %.3f' %std)
    plt.yticks([])
    plt.show()

    我们可以看到当标准差较大( std = 0.1 和 0.05 )时,几乎所有的输出值集中在 -1 或1 附近,这表明此时的神经网络发生了梯度爆炸;当标准差较小( std = 0.005 和 0.001)时,我们看到输出值迅速向 0 靠拢,这表明此时的神经网络发生了梯度消失。其实笔者也曾在 YOLOv3 网络里做过实验,初始化权重的标准差如果太大或太小,都容易出现 NaN 。

    学习率的设置

    学习率是最影响性能的超参数之一,如果我们只能调整一个超参数,那么最好的选择就是它。 其实在我们的大多数的炼丹过程中,遇到 loss 变成 NaN 的情况大多数是由于学习率选择不当引起的。

    有句话讲得好啊,步子大了容易扯到蛋。由于神经网络在刚开始训练的时候是非常不稳定的,因此刚开始的学习率应当设置得很低很低,这样可以保证网络能够具有良好的收敛性。但是较低的学习率会使得训练过程变得非常缓慢,因此这里会采用以较低学习率逐渐增大至较高学习率的方式实现网络训练的“热身”阶段,称为 warmup stage。但是如果我们使得网络训练的 loss 最小,那么一直使用较高学习率是不合适的,因为它会使得权重的梯度一直来回震荡,很难使训练的损失值达到全局最低谷。因此最后采用了这篇论文里[8]的 cosine 的衰减方式,这个阶段可以称为 consine decay stage。

    1
    2
    3
    4
    5
    6
    if global_steps < warmup_steps:
    lr = global_steps / warmup_steps *cfg.TRAIN.LR_INIT
    else:
    lr = cfg.TRAIN.LR_END + 0.5 * (cfg.TRAIN.LR_INIT - cfg.TRAIN.LR_END) * (
    (1 + tf.cos((global_steps - warmup_steps) / (total_steps - warmup_steps) * np.pi))
    )

    加载预训练模型

    目前针对目标检测的主流做法是基于 Imagenet 数据集预训练的模型来提取特征,然后在 COCO 数据集进行目标检测fine-tunning训练(比如 yolo 算法),也就是大家常说的迁移学习。其实迁移学习是建立在数据集分布相似的基础上的,像 yymnist 这种与 COCO 数据集分布完全不同的情况,就没有必要加载 COCO 预训练模型的必要了吧。

    在 tensorflow-yolov3 版本里,由于 README 里训练的是 VOC 数据集,因此推荐加载预训练模型。由于在 YOLOv3 网络的三个分支里的最后卷积层与训练的类别数目有关,因此除掉这三层的网络权重以外,其余所有的网络权重都加载进来了。

    下面是 tensorflow-yolov3 在 PASCAL VOC 2012 上比赛刷的成绩,最后进了榜单的前十名。

    参考文献

    </div>
    
    展开全文
  • YOLOv3网络结构和解析

    万次阅读 多人点赞 2019-05-09 23:03:07
    学了这么久的YOLOv3,把自己的学习心得记录下。欢迎指正! 参考: YOLOv3网络结构细致解析 基于keras-yolov3,原理及代码细节的理解 论文地址:https://pjreddie.com/media/files/papers/YOLOv3.pdf yolov3官网:...

    学了这么久的YOLOv3,把自己的学习心得记录下。欢迎指正!

    参考:
    YOLOv3网络结构细致解析
    基于keras-yolov3,原理及代码细节的理解
    论文地址:https://pjreddie.com/media/files/papers/YOLOv3.pdf
    yolov3官网:https://pjreddie.com/darknet/yolo/
    Keras版本推荐:https://github.com/qqwweee/keras-yolo3
    以及keras版本的解读:https://danielack.github.io/2018/08/25/yolov3Keras实现解读/

    本文仅仅对于YOLO的网络结构进行说明。

    1

    YOLOv3本身使用的是全卷积层,连图或者说特征图的尺寸的修改都是通过卷积层来实现。来张YOLO论文的结构图:
    在这里插入图片描述
    再来一个YOLO输出时的显示:

    layer     filters    size              input                output
       0 conv     32  3 x 3 / 1   416 x 416 x   3   ->   416 x 416 x  32 0.299 BF
       1 conv     64  3 x 3 / 2   416 x 416 x  32   ->   208 x 208 x  64 1.595 BF
       2 conv     32  1 x 1 / 1   208 x 208 x  64   ->   208 x 208 x  32 0.177 BF
       3 conv     64  3 x 3 / 1   208 x 208 x  32   ->   208 x 208 x  64 1.595 BF
       4 Shortcut Layer: 1
       5 conv    128  3 x 3 / 2   208 x 208 x  64   ->   104 x 104 x 128 1.595 BF
       6 conv     64  1 x 1 / 1   104 x 104 x 128   ->   104 x 104 x  64 0.177 BF
       7 conv    128  3 x 3 / 1   104 x 104 x  64   ->   104 x 104 x 128 1.595 BF
       8 Shortcut Layer: 5
       9 conv     64  1 x 1 / 1   104 x 104 x 128   ->   104 x 104 x  64 0.177 BF
      10 conv    128  3 x 3 / 1   104 x 104 x  64   ->   104 x 104 x 128 1.595 BF
      11 Shortcut Layer: 8
      12 conv    256  3 x 3 / 2   104 x 104 x 128   ->    52 x  52 x 256 1.595 BF
      13 conv    128  1 x 1 / 1    52 x  52 x 256   ->    52 x  52 x 128 0.177 BF
      14 conv    256  3 x 3 / 1    52 x  52 x 128   ->    52 x  52 x 256 1.595 BF
      15 Shortcut Layer: 12
      16 conv    128  1 x 1 / 1    52 x  52 x 256   ->    52 x  52 x 128 0.177 BF
      17 conv    256  3 x 3 / 1    52 x  52 x 128   ->    52 x  52 x 256 1.595 BF
      18 Shortcut Layer: 15
      19 conv    128  1 x 1 / 1    52 x  52 x 256   ->    52 x  52 x 128 0.177 BF
      20 conv    256  3 x 3 / 1    52 x  52 x 128   ->    52 x  52 x 256 1.595 BF
      21 Shortcut Layer: 18
      22 conv    128  1 x 1 / 1    52 x  52 x 256   ->    52 x  52 x 128 0.177 BF
      23 conv    256  3 x 3 / 1    52 x  52 x 128   ->    52 x  52 x 256 1.595 BF
      24 Shortcut Layer: 21
      25 conv    128  1 x 1 / 1    52 x  52 x 256   ->    52 x  52 x 128 0.177 BF
      26 conv    256  3 x 3 / 1    52 x  52 x 128   ->    52 x  52 x 256 1.595 BF
      27 Shortcut Layer: 24
      28 conv    128  1 x 1 / 1    52 x  52 x 256   ->    52 x  52 x 128 0.177 BF
      29 conv    256  3 x 3 / 1    52 x  52 x 128   ->    52 x  52 x 256 1.595 BF
      30 Shortcut Layer: 27
      31 conv    128  1 x 1 / 1    52 x  52 x 256   ->    52 x  52 x 128 0.177 BF
      32 conv    256  3 x 3 / 1    52 x  52 x 128   ->    52 x  52 x 256 1.595 BF
      33 Shortcut Layer: 30
      34 conv    128  1 x 1 / 1    52 x  52 x 256   ->    52 x  52 x 128 0.177 BF
      35 conv    256  3 x 3 / 1    52 x  52 x 128   ->    52 x  52 x 256 1.595 BF
      36 Shortcut Layer: 33
      37 conv    512  3 x 3 / 2    52 x  52 x 256   ->    26 x  26 x 512 1.595 BF
      38 conv    256  1 x 1 / 1    26 x  26 x 512   ->    26 x  26 x 256 0.177 BF
      39 conv    512  3 x 3 / 1    26 x  26 x 256   ->    26 x  26 x 512 1.595 BF
      40 Shortcut Layer: 37
      41 conv    256  1 x 1 / 1    26 x  26 x 512   ->    26 x  26 x 256 0.177 BF
      42 conv    512  3 x 3 / 1    26 x  26 x 256   ->    26 x  26 x 512 1.595 BF
      43 Shortcut Layer: 40
      44 conv    256  1 x 1 / 1    26 x  26 x 512   ->    26 x  26 x 256 0.177 BF
      45 conv    512  3 x 3 / 1    26 x  26 x 256   ->    26 x  26 x 512 1.595 BF
      46 Shortcut Layer: 43
      47 conv    256  1 x 1 / 1    26 x  26 x 512   ->    26 x  26 x 256 0.177 BF
      48 conv    512  3 x 3 / 1    26 x  26 x 256   ->    26 x  26 x 512 1.595 BF
      49 Shortcut Layer: 46
      50 conv    256  1 x 1 / 1    26 x  26 x 512   ->    26 x  26 x 256 0.177 BF
      51 conv    512  3 x 3 / 1    26 x  26 x 256   ->    26 x  26 x 512 1.595 BF
      52 Shortcut Layer: 49
      53 conv    256  1 x 1 / 1    26 x  26 x 512   ->    26 x  26 x 256 0.177 BF
      54 conv    512  3 x 3 / 1    26 x  26 x 256   ->    26 x  26 x 512 1.595 BF
      55 Shortcut Layer: 52
      56 conv    256  1 x 1 / 1    26 x  26 x 512   ->    26 x  26 x 256 0.177 BF
      57 conv    512  3 x 3 / 1    26 x  26 x 256   ->    26 x  26 x 512 1.595 BF
      58 Shortcut Layer: 55
      59 conv    256  1 x 1 / 1    26 x  26 x 512   ->    26 x  26 x 256 0.177 BF
      60 conv    512  3 x 3 / 1    26 x  26 x 256   ->    26 x  26 x 512 1.595 BF
      61 Shortcut Layer: 58
      62 conv   1024  3 x 3 / 2    26 x  26 x 512   ->    13 x  13 x1024 1.595 BF
      63 conv    512  1 x 1 / 1    13 x  13 x1024   ->    13 x  13 x 512 0.177 BF
      64 conv   1024  3 x 3 / 1    13 x  13 x 512   ->    13 x  13 x1024 1.595 BF
      65 Shortcut Layer: 62
      66 conv    512  1 x 1 / 1    13 x  13 x1024   ->    13 x  13 x 512 0.177 BF
      67 conv   1024  3 x 3 / 1    13 x  13 x 512   ->    13 x  13 x1024 1.595 BF
      68 Shortcut Layer: 65
      69 conv    512  1 x 1 / 1    13 x  13 x1024   ->    13 x  13 x 512 0.177 BF
      70 conv   1024  3 x 3 / 1    13 x  13 x 512   ->    13 x  13 x1024 1.595 BF
      71 Shortcut Layer: 68
      72 conv    512  1 x 1 / 1    13 x  13 x1024   ->    13 x  13 x 512 0.177 BF
      73 conv   1024  3 x 3 / 1    13 x  13 x 512   ->    13 x  13 x1024 1.595 BF
      74 Shortcut Layer: 71
      75 conv    512  1 x 1 / 1    13 x  13 x1024   ->    13 x  13 x 512 0.177 BF
      76 conv   1024  3 x 3 / 1    13 x  13 x 512   ->    13 x  13 x1024 1.595 BF
      77 conv    512  1 x 1 / 1    13 x  13 x1024   ->    13 x  13 x 512 0.177 BF
      78 conv   1024  3 x 3 / 1    13 x  13 x 512   ->    13 x  13 x1024 1.595 BF
      79 conv    512  1 x 1 / 1    13 x  13 x1024   ->    13 x  13 x 512 0.177 BF
      80 conv   1024  3 x 3 / 1    13 x  13 x 512   ->    13 x  13 x1024 1.595 BF
      81 conv     18  1 x 1 / 1    13 x  13 x1024   ->    13 x  13 x  18 0.006 BF
      82 yolo
      83 route  79
      84 conv    256  1 x 1 / 1    13 x  13 x 512   ->    13 x  13 x 256 0.044 BF
      85 upsample            2x    13 x  13 x 256   ->    26 x  26 x 256
      86 route  85 61
      87 conv    256  1 x 1 / 1    26 x  26 x 768   ->    26 x  26 x 256 0.266 BF
      88 conv    512  3 x 3 / 1    26 x  26 x 256   ->    26 x  26 x 512 1.595 BF
      89 conv    256  1 x 1 / 1    26 x  26 x 512   ->    26 x  26 x 256 0.177 BF
      90 conv    512  3 x 3 / 1    26 x  26 x 256   ->    26 x  26 x 512 1.595 BF
      91 conv    256  1 x 1 / 1    26 x  26 x 512   ->    26 x  26 x 256 0.177 BF
      92 conv    512  3 x 3 / 1    26 x  26 x 256   ->    26 x  26 x 512 1.595 BF
      93 conv     18  1 x 1 / 1    26 x  26 x 512   ->    26 x  26 x  18 0.012 BF
      94 yolo
      95 route  91
      96 conv    128  1 x 1 / 1    26 x  26 x 256   ->    26 x  26 x 128 0.044 BF
      97 upsample            2x    26 x  26 x 128   ->    52 x  52 x 128
      98 route  97 36
      99 conv    128  1 x 1 / 1    52 x  52 x 384   ->    52 x  52 x 128 0.266 BF
     100 conv    256  3 x 3 / 1    52 x  52 x 128   ->    52 x  52 x 256 1.595 BF
     101 conv    128  1 x 1 / 1    52 x  52 x 256   ->    52 x  52 x 128 0.177 BF
     102 conv    256  3 x 3 / 1    52 x  52 x 128   ->    52 x  52 x 256 1.595 BF
     103 conv    128  1 x 1 / 1    52 x  52 x 256   ->    52 x  52 x 128 0.177 BF
     104 conv    256  3 x 3 / 1    52 x  52 x 128   ->    52 x  52 x 256 1.595 BF
     105 conv     18  1 x 1 / 1    52 x  52 x 256   ->    52 x  52 x  18 0.025 BF
     106 yolo
    

    实际,这个已经告诉了我们每层的输出情况。每层特征图的大小情况:
    在这里插入图片描述
    在前文网络的基础上,用红色做了注释。residual使用残差结构。什么是残差结构?举个例子在第一层残差结构(其输出为208208128),其输入为20820864,经过3211和6433的卷积后,其生成的特征图与输入叠加起来。其结构如下:
    在这里插入图片描述
    其叠加后的特征图作为新的输入输入下一层。YOLO主体是由许多这种残差模块组成,减小了梯度爆炸的风险,加强了网络的学习能力。

    可以看到YOLO有3个尺度的输出,分别在52×52,26×26,13×13。嗯,都是奇数,使得网格会有个中心位置。同时YOLO输出为3个尺度,每个尺度之间还有联系。比如说,13×13这个尺度输出用于检测大型目标,对应的26×26为中型的,52×52用于检测小型目标。上一张图,我觉得很详细看得懂。
    在这里插入图片描述
    这个检测COCO(80个类的),所以其输出需要构造为:S×S×3×(5+class_number)。解释下为什么是这样。
    YOLO将图像划分为S×S的网格,当目标中心落在某个网格中,就用这个网格去检测它,这是S×S的由来。为什么是3,是因为每个网格需要检测3个anchorbox(注意有3个尺度),所以对于每个尺度,其输出为S×S×3×???
    对于一个anchorbox,它包含坐标信息(x , y , w , h )以及置信度,而这有5个信息;同时还会包含是否所有类别的信息,使用one-hot编码。比如说有3个类:person、car、dog。检测的结果是人,那么就编码为[1,0,0]。可见所有类别信息都会被编码,COCO有80个类别的话,便是5+80。所以,对于每个维度的输出,其结果为: S × S × 3 × ( 5 + 80 ) = S × S × 255 S×S×3×(5+80) = S×S×255 S×S×3×5+80=S×S×255.
    同时从上图可以看到,其结果便是通过一些卷积操作,将输出构造成这样。并且将不同尺度的特征图叠加到一起,增加输出的信息。这个图可以好好看看。

    展开全文
  • yolov3转caffe模型

    千次阅读 热门讨论 2019-10-12 09:32:25
    最近的项目,需要将训练的yolov3模型部署到hisi3516CV500上去,中间经过yolov3训练出来的weights转caffemodel,这里将自己 走过的步骤和error记录下来,一是自己做个总结记录;二是可以让遇到同样错误的朋友快速解决...

            最近的项目,需要将训练的yolov3模型部署到hisi3516CV500上去,中间经过yolov3训练出来的weights转caffemodel,这里将自己 走过的步骤和error记录下来,一是自己做个总结记录;二是可以让遇到同样错误的朋友快速解决问题;

            一,yolov3 训练出来的模型转为caffemodel需要(***.cfg和***.weights)两个文件,用github上的转化项目caffe-yolov3工程这里给出地址:https://github.com/ChenYingpeng/caffe-yolov3.git

             第一次调用这个工程,自己的linux上已经安装了caffe环境,进入python,import caffe 没有报错

             调用caffe-yolov3步骤:

             1、git clone https://github.com/ChenYingpeng/caffe-yolov3,cd  model_convert ,

                  使用python yolov3_darknet2caffe.py  yolov3.cfg yolov3.weights  yolov3.prototxt  yolov3.caffemodel,不出错就会生成**.prototxt和**.caffemodel两个你需要的文件,但事实“不会那么一帆风顺”

                   error1:[libprotobuf ERROR google/protobuf/text_format.cc:274] Error parsing text-format caffe.NetParameter: 2622:20: Message type "caffe.LayerParameter" has no field named "upsample_param".
    WARNING: Logging before InitGoogleLogging() is written to STDERR
    F0626 10:08:24.390600 9704 upgrade_proto.cpp:90] Check failed: ReadProtoFromTextFile(param_file, param) Failed to parse NetParameter file: yolov3.prototxt

                    解决办法:1、原生安装的caffe1.0,没有upsample layer,需要按照这篇博客的第二点添加caffe中缺少的upsample layer层https://blog.csdn.net/Chen_yingpeng/article/details/80692018,我也总结到这里:

                    首先caffe环境搭建自行百度解决,其次需要了解Yolov3里面有shortcut、route、upsample、yolo等这些层是caffe不支持的,但是shortcut可以用eltwise替换,route可以用concat替换,yolo只能自己写,upsample可以添加。这里添加upsample这一层的代码我的百度云盘分享:https://pan.baidu.com/s/1xJ3xo4aeCj2ndIqwtaU7Hg 无提取密码哦。

                    添加过程:把upsample_layer.hpp 放在include/caffe/layers下面;

                                      把upsample_layer.cpp与upsample_layer.cu放在src/caffe/layers下面;

                    往自己的caffe.proto里面添加UpsampleParameter相关参数; 然后重新编译;编译成功之后就可以加载yolov3.prototxt与yolov3.caffemodel运行;

                     这里可能会报错:“fatal error: caffe/proto/caffe.pb.h: No such file or directory(使用caffe时编译出错)”

                     解决办法:1、打开终端,进入caffe所在路径;

                                        2、执行以下指令

                                              mkdir include/caffe/proto

                                              protoc ./src/caffe/proto/caffe.proto --cpp_out=.

                                               mv ./src/caffe/proto/caffe.pb.h ./include/caffe/proto/

                                         3、重新编译即可

     

    展开全文
  • 实际工作中,目标检测 yolov3 或者 yolov4 模型移植到 AI 芯片中,经常需要将其先转换为 caffe1.x 模型,大家可能或多或少也有这方面的需求。例如华为海思 NNIE 只...

    实际工作中,目标检测 yolov3 或者 yolov4 模型移植到 AI 芯片中,经常需要将其先转换为 caffe1.x 模型,大家可能或多或少也有这方面的需求。例如华为海思 NNIE 只支持caffe1.x 模型,所以 yolov3/yolov4 模型要想在海思芯片上部署,转换为 caffe1.x 模型是必须的。

    今天,专门给大家写一下将  yolov3/yolov4 模型转为 caffe 模型的详细步骤。

    0. 系统环境

    • Ubuntu 16.04

    • cuda 9.x

    1. 部署 caffe 环境

    常规的方法是下载 caffe 的源码,进行编译安装。但是,这种方法比较复杂,不太容易成功。更简单的方法是直接下载 caffe1.x 的docker 镜像。这种方法需要你提前在 Ubuntu 里安装了 nvidia-docker。

    打开 docker hub 网站:

    https://registry.hub.docker.com/

    搜索 caffe,看到这个镜像:

    点击进去,看到该镜像的下拉命令:

    sudo docker pull bvlc/caffe
    

    打开 Ubuntu 终端,输入上面的下拉命令,将 caffe 的 docker 镜像下载下来。

    下载完成之后,终端输入命令:

    sudo docker images
    

    就可以看到已下载的 caffe 镜像了。

    为镜像创建容器:

    docker run --runtime=nvidia --name caffe -i -t bvlc/caffe /bin/bash
    

    这里我们给该容器起的名字是 caffe,大家可以自由设置,根据 `sudo docker ps -a` 这条命令来查看各个容器。

    这样,我们就进入了 caffe1.x 的 docker 容器之内了。

    该容器已部署好了 caffe1.x,caffe1.x 路径为:

    2. caffe 源码修改

    因为官方 caffe1.x 框架不支持 yolo3/yolov4 的 upsample 层,所以需要手动增加 upsample 层,对 caffe 源码进行修改。

    克隆 GitHub 上的转换工具项目:

    git clone https://github.com/ChenYingpeng/darknet2caffe.git
    

    将 darknet2caffe/caffe_layers/mish_layer 下的 mish_layer.hpp 文件和 darknet2caffe/tree/master/caffe_layers/upsample_layer 下的 upsample_layer.hpp  拷贝到容器的路径:/opt/caffe/include/caffe/layers  下。

    将 darknet2caffe/caffe_layers/mish_layer 下的 mish_layer.cpp、mish_layer.cu 文件和 darknet2caffe/tree/master/caffe_layers/upsample_layer 下的 upsample_layer.cpp、upsample_layer.cu  拷贝到容器的路径:/opt/caffe/src/caffe/layers/  下。

    将 darknet2caffe/caffe_layers/pooling_layer 下的 pooling_layer.cpp 拷贝到容器的路径:/opt/caffe/src/caffe/layers/  下。

    然后,打开容器内的 caffe 文件:/opt/caffe/src/caffe/proto/caffe.proto。按照如下说明修改相应字段的程序。

    // LayerParameter next available layer-specific ID: 147 (last added: recurrent_param)
    message LayerParameter {
      optional TileParameter tile_param = 138;
      optional VideoDataParameter video_data_param = 207;
      optional WindowDataParameter window_data_param = 129;
    ++optional UpsampleParameter upsample_param = 149; //added by chen for Yolov3, make sure this id 149 not the same as before.
    ++optional MishParameter mish_param = 150; //added by chen for yolov4,make sure this id 150 not the same as before.
    }
    
    
    // added by chen for YoloV3
    ++message UpsampleParameter{
    ++  optional int32 scale = 1 [default = 1];
    ++}
    
    
    // Message that stores parameters used by MishLayer
    ++message MishParameter {
    ++  enum Engine {
    ++    DEFAULT = 0;
    ++    CAFFE = 1;
    ++    CUDNN = 2;
    ++  }
    ++  optional Engine engine = 2 [default = DEFAULT];
    ++}
    

    其中,++ 表示该行是增加的内容。

    3. caffe 重新编译

    修改完 caffe 的一些源码之后,需要对 caffe 重新编译。

    进入 /opt/caffe/build 目录,输入以下命令:

    make clean
    make all -j8
    make pycaffe -j8
    

    caffe 重新编译之后,就可以对 yolov3/yolov4 模型进行 caffe 转换了。

    4. 模型转换

    准备好我们已有的 yolov3 模型的配置文件和权重文件,例如:yolov3.cfg 和 yolov3.weights。在 darknet2caffe 目录下,输入以下命令:

    python darknet2caffe.py ./yolov3.cfg ./yolov3.weights ./yolov3.prototxt ./yolov3.caffemodel
    

    如果输出类似下面的语句,则证明转换成功!

    I0522 10:19:19.015708 25251 net.cpp:228] layer1-act does not need backward computation.
    I0522 10:19:19.015712 25251 net.cpp:228] layer1-scale does not need backward computation.
    I0522 10:19:19.015714 25251 net.cpp:228] layer1-bn does not need backward computation.
    I0522 10:19:19.015718 25251 net.cpp:228] layer1-conv does not need backward computation.
    I0522 10:19:19.015722 25251 net.cpp:228] input does not need backward computation.
    I0522 10:19:19.015725 25251 net.cpp:270] This network produces output layer139-conv
    I0522 10:19:19.015731 25251 net.cpp:270] This network produces output layer150-conv
    I0522 10:19:19.015736 25251 net.cpp:270] This network produces output layer161-conv
    I0522 10:19:19.015911 25251 net.cpp:283] Network initialization done.
    unknow layer type yolo 
    unknow layer type yolo 
    save prototxt to ./yolov3.prototxt
    save caffemodel to ./yolov3.caffemodel
    

    其中,yolov3.prototxt 和 yolov3.caffemodel 为转换后的 caffe 模型。

    至此,yolov3/yolov4 转换为 caffe 模型完成!


    AI 角:机器人遛弯

    推荐阅读

    (点击标题可跳转阅读)

    干货 | 公众号历史文章精选

    我的深度学习入门路线

    我的机器学习入门路线图

    感谢你的分享,点赞,在看三  

    展开全文
  • yolov3模型部署实战weights转onnx并推理

    千次阅读 热门讨论 2019-12-26 16:44:09
    前言: 最近比较忙(懒),本学渣在写毕业论文(好难受),所以博客的更新不是那么有效率,哈哈;...实现yolov3/yolov3-tiny模型从.weights模型转换到.onnx模型然后再转换到.trt模型; 当然,本文也是本人自己对自己学...
  • YOLOv3中darknet的各种层的说明

    千次阅读 2019-03-22 10:07:30
    很多层里面有 activation 这一项,这是激活函数,我看到的配置文件里面最常用的就3个: LINEAR:啥都不干 RELU :值 > 0时保持不变,小于0时置0 LEAKY :值 > 0时保持不变,小于0时值 * 0.1 (类似于caffe的...
  • Yolov3转化Caffe框架详解

    万次阅读 多人点赞 2018-08-26 21:32:17
    前些日子因工程需求,需要将yolov3从基于darknet转化为基于Caffe框架,过程中踩了一些坑,特在此记录一下。 1.Yolov3的网络结构 想要转化为Caffe框架,就要先了解yolov3的网络结构,如下图。  如果有运行过...
  • 基于caffe框架复现yolov3目标检测

    千次阅读 2018-07-16 15:55:05
     2)、yolov3上有upsample层在之前的版本上没有; 因此针对这些不同点我重新写了一个darknet2caffe.py的文件 [html]   view plain   copy < code   class = "language-html" > import sys  sys.path.append('/...
  • ROS Kinetic + Ubuntu 16.04 yolov3配置

    千次阅读 2018-10-16 10:35:14
    ROS Kinetic + Ubuntu 16.04 yolov3配置 1.安装 项目Github地址:https://github.com/leggedrobotics/darknet_ros,安装到本地时选择ssh方式才能完整下载,需要先配置自己电脑上的ssh key并添加到Github账户(参考...
  • darknet yolo v3 route layer

    千次阅读 2018-07-19 13:38:31
    route layer层主要是把输入层连接在一起,在darknet 网络结构中,要求输入层对应的width、height必须相等,如果不相等,则把route layer层的输出w,h,c都设置为0。 例如输入层1:26*26*256 输入层2:26*26*128 则...
  • 基于darknet-53框架的Yolov3实验

    万次阅读 2019-04-26 19:18:50
    基于darknet-53框架的Yolov3实验 配置环境虽然有点麻烦,但是跑出结果的时候还是感觉到了yolov3的强大,下面是跑出来的结果 首先是yolov3的输出结果: 茯苓@FL /cygdrive/d/软件/cygwin2/home/茯苓/darknet $ ./...
  • PyTorch实现yolov3代码详细解密(一)

    万次阅读 多人点赞 2019-04-17 19:33:51
    下面我们来讲解pytorch实现的yolov3源码。在讲解之前,大家应该具备相应的原理知识yolov1,yolov2,yolov3。 创建YOLO网络 首先我们知道yolov3将resnet改造变成了具有更好性能的Darknet作为它的backbone,称为...
  • Yolov3(Mxnet)修改检测层

    千次阅读 2019-10-30 10:58:53
    我们已经介绍了如何使用Mxnet中Gluoncv里model_zoo中的各种分类模型作为特征提取网络,快速修改Yolov3的基础网络,现在我们继续介绍如何修改Yolov3的检测层、特征变换层和输出层。Yolov3实际上已经具有一定的历史,...
  • YOLOv3论文理解

    千次阅读 2018-11-19 11:52:47
    论文 - YOLO v3 引自:https://xmfbit.github.io/2018/04/01/paper-yolov3/ 发表于 2018-04-01 ...这篇博客介绍这篇论文:YOLOv3: An Incremental Improvement。下面这张图是YOLO V3与RetinaNet的比较。 可...
  • 1,1,0) self.upsample = nn.Upsample(scale_factor=2) #融合之后 256 + 128 = 384 self.conB2 = convBatchReluBlock(384,256,3,1,1) self.yolo2 = convBatchReluBlock(256,255,1,1,0) def forward(self, x): x = ...
  • TenSorRT部署运行yolov3

    千次阅读 2020-03-19 11:22:26
    本文是基于TensorRT 5.0.2基础上,关于其内部的yolov3_onnx例子的分析和介绍。 本例子展示一个完整的ONNX的pipline,在tensorrt 5.0的ONNX-TensorRT基础上,基于Yolov3-608网络进行inference,包含预处理和后处理。 ...
  • Ubuntu18.04 + Yolov3 + OpenCV

    千次阅读 2020-03-03 00:26:39
    一、YoloV3安装 1.进入官网 根据说明:下载Darknet: 进入Darknet目录并编译: cd darknet make 等待完成即可: 下载权重文件:wget https://pjreddie.com/media/files/yolov3.weights特别慢 ...
  • darknet源码学习 (1) :yolov3 推理过程

    千次阅读 2018-12-02 16:11:46
    最近项目中会频繁用到yolov3这个目标检测算法框,由于其在速度和精度尤其是小物体检测的能力上都比较突出所以目前应用面很广泛,在应用yolov3的过程中经常会遇到一些算法上的疑点,由于之前没有好好学习过darknet这...
  •   文件目录:YOUR_PATH\YOLOv3_TensorFlow-master\utils\layer_utils.py   这里函数的主要作用是对卷积等操作做出一定的个性化封装,方便代码的编写。主要包括: 卷积的封装 darknet网络结构的定义 ...
  • YOLOv3 Darknet安装编译与训练自己的数据集文章目录:1安装编译darknet与测试darknet1.1 安装编译darknet1.1.1 下载darknet安装包1.1.2 编译darknet1.2 测试darknet2 darknet训练自己的数据集2.1 数据集的准备2.1.1 ...
  • 1.yolov3的darknet使用 2. yolov3训练自己的数据集 1)普通的训练 2)将anchor换成自己数据集匹配的值: 3. yolov3的caffe使用 1)转成caffe模型 2)使用caffe模型: Gaussian yolov3使用 1).训练自己数据...
  • TensorRT加速yoloV3(caffe版本)

    千次阅读 热门讨论 2018-11-21 14:38:49
    加速yolov3需要处理三件事情: 1)upsample层; 2)图像的预处理; 3)加速结果的后处理; 贴出upsample层的代码:(参考:) /******************************/ // Upsample Plugin /******************************/ //...
  • pytorch实现YoloV3模型

    2021-03-08 16:59:38
    yoloV3的学习笔记 以下内容是学习Bubbliiiing大佬,自己整理的笔记 大佬的CSDN链接 点击这里 YOLOv3 Yolov3的模型的实现过程 简单介绍: ​ yolov3主要主要就是通过Darknet-53的主干网络进行特征的提取。首先是...
  • 就是需要上采样啊,卷积层的数量自然就多了,另外作者还是用了一连串的3*3、1*1卷积,3*3的卷积增加channel,而1*1的卷积在于压缩3*3卷积后的特征表示,这波操作很具有实用性,一增一减,效果棒棒。 为什么有...
  • 1. 下载YOLOv3 git clone https://github.com/pjreddie/darknet cd darknet 2. 修改Makefile配置 GPU=1 #如果使用GPU设置为1,CPU设置为0 CUDNN=1 #如果使用CUDNN设置为1,否则为0 OPENCV=0 #如果调用摄像头,还...
  • yolov3原理+训练损失

    万次阅读 热门讨论 2018-04-15 22:35:50
    对象分数: YOLOv3使用逻辑回归预测每个边界框(bounding box)的对象分数。 如果先前的边界框比之前的任何其他边界框重叠ground truth对象,则该值应该为1。如果以前的边界框不是最好的,但是确实将ground truth...
  • YOLOv3使用笔记——yolov3 weights转caffemodel

    万次阅读 热门讨论 2019-01-17 17:43:46
    原工程作者在TX2上实现将yolov3模型转换成caffemodel并调用。 1.修改为在Ubuntu16.04环境编译 CMakeLists.txt 主要修改交叉编译器为x86_64编译,修改自己的caffe路径(其中caffe.proto中增加upsample层),修改...
  • YOLOv1、YOLOv2和YOLOv3对比

    千次阅读 2020-06-06 10:04:22
    YOLOv1、YOLOv2和YOLOv3对比R-CNN系列YOLOv1结构目标输出网络训练YOLOv1的局限性和R-CNN系列的对比YOLOv2结构目标输出网络训练关于YOLO9000YOLOv3结构目标输出网络训练YOLOv3系统做过的不成功的尝试未来 YOLO深度...
  • yolov3的网络结构如下图 这张图片转自此篇博客. 观察我们可以发现Darknet53本身由大量的残差连接构成,所以首先我们实现基础卷积模块 创建基础卷积模块DBL ######################## # 创建一个基础的卷积模块 DBL ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 889
精华内容 355
关键字:

layerupsampleyolov3