YOLO-v1-目标检测

本文详细介绍了YOLO-v1目标检测模型的原理和实现,包括数据集编码、损失函数、模型结构及实验结果。作者探讨了YOLO模型的设计思路,分析了训练过程中的问题及解决方案,并提供了源码。实验显示,虽然模型在训练集上表现良好,但在测试集上存在过拟合问题,可能由于数据集复杂、模型规模过大以及训练数据不足等原因。
摘要由CSDN通过智能技术生成

YOLO-v1-目标检测

目标检测是一件比较实际的且具有挑战性的计算机视觉任务,可以看成图像分类与定位的结合。给定一张图片,要求能够识别出图片中的目标并给出其具体位置。相比于图片分类问题,目标检测问题更加复杂。

在这里插入图片描述
针对目标检测,一个很自然的想法就是:将图片输入到深度网络,让网络吐出目标物体的中心坐标比例和长宽比例(x,y,w,h)。这种做法原理上是可行的,因为前期可以通过卷积、池化不断提取图片的深度特征,后期再利用全连接对提取到特征进行编码,就可以让它映射成我们想要的位置坐标。

但如果要对多目标进行检测,上面的做法就失效了。因为图片中目标物体的数量、类别是不定的,我们无法固定网络模型的输出维度,自然也无法进行后续的参数优化。

1、数据集简介

本次实验我采用的是VOC2007数据集。

我只选用了其中的JEPGImages、Annotations两个文件夹。JEPGImages文件夹含有9963张RGB图片,总共20个类别,Annotations文件夹含有9963个xml文件,分别记录了每张图片中目标物体的类别与信息。

在这里插入图片描述
在这里插入图片描述

2、数据结构编码

原始数据集中的数据,我们是不可能直接放入网络中去训练的,网络无法拟合如此复杂的映射。比如说,我总不能把一张RGB图片输入到网络,期待网络输出一个xml文件,让其记录着我们想要的目标物体的类别与位置,这不现实。我们必须对原始数据集中的数据结构进行转换,让它变成网络更容易学习的形式。

数据结构编码是整个模型算法的灵魂。我们必须借助自身对问题的理解,设计合适的数据结构,让整个映射过程更具备可解释性,从而能够更加轻易的学到数据背后的映射关系。好的数据结构对模型的最终效果至关重要。

YOLO算法训练集x:(448,448,3)大小的RGB图片。针对JEPGImages文件夹中的图片,resize到(448,448,3)大小,再除以255像素归一化。

YOLO算法训练集y:(7,7,25)大小的矩阵。

采用grid网格划分的思路,将每张图片划分成(7,7)总共49个区域,每个区域对应一个25维的向量。第0维-第19维记录落在该网格的物体中心的类别,第20维记录该网格是否含有物体,第21维-第22维记录“bounding box中心坐标相对于grid左上角坐标的差值,再除以gird长宽的比例”,第23维-第24维度记录“bounding box的长宽,再除以整张图片长宽的比例”。

在这里插入图片描述
此时,向量的前20维就是一个单位向量,第21维就是0或者1,第22维-第25维都是0-1之间的小数,便于网络学习。

train_x : (8000, 448, 448, 3)
train_y : (8000, 7, 7, 30)
val_x : (1000, 448, 448, 3)
val_y : (1000, 7, 7, 30)
test_x : (963, 448, 448, 3)
test_y : (963, 7, 7, 30)

3、YOLO-v1损失函数

YOLO-v1损失函数分为4个部分:

第1部分是true-y中含目标物体的grid的坐标与pre-y对应的grid的坐标的均方误差,权重取5。第2部分是true-y中含目标物体的grid的置信度与pre-y对应的grid的置信度的均方误差,权重取1。第3部分是true-y中不含目标物体的grid的置信度与pre-y对应的grid的置信度的均方误差,权重取0.5。第4部分是true-y中含目标物体的grid的类别向量与pre-y对应的grid的类别向量的均方误差,权重取1。

在这里插入图片描述
这里1ij表示:当真实样本的grid含有目标物体,且网络输出的grid中的bounding box是效果最好的那个时,取值为1,其它情况都取值为0。我训练时真实样本是(7,7,25)的true-y,网络输出是(7,7,30)的pre-y,含有两个bounding box和两个置信度。

在计算loss值时,并不是对两个bounding box同时累加误差,而是从两个bounding box中选取效果最好的一个计算误差,通过计算与真实bounding box的IOU值进行判断。对于pre-y预测的两个bounding box,我分别计算它们与true-y中bounding box的IOU,保留下IOU较大的那个bounding box和置信度,再利用上面计算公式,计算两个(7,7,30)矩阵的loss值。

4、YOLO-v1模型结构

YOLO-v1模型结构如下:
在这里插入图片描述网络输入:(448,448,3)

第1轮卷积池化:
(1)64个(7,7)卷积核,stride = 2。
(2)(2,2)最大池化,stride = 2。

第2轮卷积池化:
(1)192个(3,3)卷积核,stride = 1。
(2)(2,2)最大池化,stride = 2。

第3轮卷积池化:
(1)128个(1,1)卷积核,stride = 1。
(2)256个(3,3)卷积核,stride = 1。
(3)256个(1,1)卷积核,stride = 1。
(4)512个(3,3)卷积核,stride = 1。
(5)(2,2)最大池化,stride = 2。

第4轮卷积池化:
(1)256个(1,1)卷积核,stride = 1。
(2)512个(3,3)卷积核,stride = 1。
(3)256个(1,1)卷积核,stride = 1。
(4)512个(3,3)卷积核,stride = 1。
(5)256个(1,1)卷积核,stride = 1。
(6)512个(3,3)卷积核,stride = 1。
(7)256个(1,1)卷积核,stride = 1。
(8)512个(3,3)卷积核,stride = 1。
(9)512个(1,1)卷积核,stride = 1。
(10)1024个(3,3)卷积核,stride = 1。
(11)(2,2)最大池化,stride = 2。

第5轮卷积池化:
(1)512个(1,1)卷积核,stride = 1。
(2)1024个(3,3)卷积核,stride = 1。
(3)512个(1,1)卷积核,stride = 1。
(4)1024个(3,3)卷积核,stride = 1。
(5)1024个(3,3)卷积核,stride = 1。
(6)1024个(3,3)卷积核,stride = 2。

第6轮卷积池化:
(1)1024个(3,3)卷积核,stride = 1。
(2)1024个(3,3)卷积核,stride = 1。

第7轮全连接:
(1)Flatten()层。
(2)Dense(4096)。

第8层全连接:
(1)Dense(1470)。

第9层自定义Reshape层:
(1)my_reshape((7, 7, 30))。

将dense1470个神经元进行重排列,前980个神经元转化为(7,7,20),添加softmax激活函数,后96个神经元转化为(7,7,2),添加sigmoid激活函数,最后392个神经元转化为(7,7,8),添加sigmoid激活函数。

5、实验结果

自己训练了一个多礼拜,最终效果却并不理想。虽然能在训练集上达到非常高的检测精度,但测试集的检测精度却并不好。

在这里插入图片描述
训练集上的YOLO-v1检测结果,看上去有模有样:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

测试集上的YOLO-v1检测结果,一团稀烂:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
感觉主要是出现了过拟合问题。训练集loss虽然能不断降低,但验证集loss始终无变化。自己首先尝试加入正则项,kernel_regularizer=l2(1e-3),效果反而更差,连训练集loss都无法降低。后来又尝试简化模型结构,将YOLO-v1的卷积层数量减少,依然出现上述问题,验证集loss始终无法降低。

自己网络训练的经验不多,暂时也没能解决这个问题。我认为自己YOLO-v1模型训练出问题的原因主要有这些:

(1)数据集复杂。VOC2007数据集需要处理20个类别的目标检测,非常复杂。
(2)YOLO-v1自身的损失函数非常复杂,难以对它进行优化。
(3)训练数据集不够,20个类别总共才8000张训练图片,而网络层数又很深。

6、深入思考

Ques1:之前都是类似RCNN模型的检测思路,YOLO模型是如何灵光一现想出来的?

之前RCNN的处理思路都是:先找到一些非常精确的候选区域,然后对这些候选区域进行分类。此时容易出现的问题就是:Selective Search算法是个无监督的聚类过程,我很难保证它最终聚类出来的效果就正好是我想要的目标检测区域。

YOLO干脆退一步处理,我不再追求得到非常精确的候选区域,只要能得到一个比较粗糙的候选区域就好,后面我再利用有监督方法对这个区域位置进行调整。这时处理起来就非常方便了,图片总共就这么大,干脆把它划分为(7,7)的grid,目标物体总会落在某个gird之中。这样我只需要检测49个候选区域即可,后续再对检测到目标的区域进行位置调整。

所以YOLO的检测速度非常快。RCNN、fast RCNN都要处理2000+个selective search候选区域,而YOLO只需处理49个grid即可。

Ques2:YOLO模型为什么要把train_y编码成(7,7,25)的数据结构?

YOLO算法一个关键之处就是train_y结构的设定,它不是简单的输出四个坐标位置,而是借用了空间位置上的对应关系,让feature map提取出的特征能够更加轻易的映射成类别、置信度、坐标位置。

就像U-net网络一样,它的train_y结构的设定是很有技巧的,不是傻瓜式的直接让网络映射输出一个1-p标号的分类矩阵,这个映射太抽象了,网络学习不到,而是将它拉成一个长方体数据结构,每个类别对应长方体的一层。

Ques3:为什么YOLO-v1模型最后要加上一个全连接层,全连接层不是会破坏原卷积层里特征的位置信息吗?

尽管全卷积得到的特征图被拉成一条向量,但我拉成的这条向量中的数据编码是有特殊含义的,也就是说“我必须要求第几位表示什么”。我训练的时候用对应的信息监督,我推理的时候自然认为输出就是我编码的这个含义。YOLO-v1最后的全连接层输出是面向目标检测任务的,也就是说其编码方式体现了目标预测结果。

其实本质说来,全连接层再截断回去转化成(7,7,30)的形式已经无太多必要。我在全连接层已经要求了某些位置对应上某个含义,至于非要再转化成(7,7,30),只是为了方便我们的理解。

Ques4:图片训练集train_x占用内存过大的解决技巧。

CV领域,往往出现加载图片数量过多导致内存占用过大的问题。对于(448,448,3)的图片,一般存储8000张图片进一个数组后,内存就会爆掉。

这时有个非常好的解决方法:

在这里插入图片描述

train_x不再存储图片数据,而是存储图片的路径。当我要对一个batch的图片进行处理时,再从硬盘里读入图片数据。这样每次内存中就只加载了32张(448,448,3)的图片,无论训练图片总数量有多大,都不会出现内存不够的问题。

Ques5:在YOLO-v1中,如果训练集某个grid中有两个目标物体,该如何编码?

YOLO-v1的一个缺陷就是:每个grid只能预测一个目标物体,无法处理多个目标物体中心重叠的情况。当编码时遇到多个目标物体中心重叠的情况,我只对第一个目标物体的类别、位置进行编码,而将后面的目标物体忽略不记。

Ques6:训练过程中训练集loss不下降,有哪些原因?

训练集loss在训练过程中迟迟不下降,一般是由这几个方面导致的:

(1)模型结构和特征工程存在问题。

如果一个模型的结构有问题,那么它就很难训练,通常自己“自主研发”设计的网络结构可能很难适应实际问题。当模型结构不好、特征工程存在问题时,其对于数据的拟合能力会不足。这是很多人在进行一个新的研究或者工程应用时,遇到的第一个大问题。

(2)权重初始化方案有问题。

合适的初始化方案很重要,用对了事半功倍,用不对模型训练状况不忍直视。有时训练一个模型,初始化方案不对,训练半天都训练不动,loss值居高不下,最后改了初始化方案,loss值就如断崖式下降。

(3)选择合适的优化器和学习速率。

神经网络的优化器一般选取Adam,但是在有些情况下Adam难以训练,这时候需要使用如SGD之类的其他优化器。

(4)正则化过度。

L1、L2、Dropout是防止过拟合用的,当训练集loss下不来时,就要考虑一下是不是正则化过度,导致模型欠拟合了。

(5)训练时间不足。

有时会遇到这样的问题,为什么训练了好几个小时了,怎么loss没降多少。心急吃不了热豆腐,各种深度学习的训练都有不同的计算量,当需要的计算量很大时,怎么可能几个小时就训练完,尤其是还在使用自己的个人电脑CPU来训练模型的情况下。

Ques7:训练过程中验证集loss不下降,有哪些原因?

训练集loss不断降低,而验证集loss迟迟不变,一般是出现了过拟合问题。

(1)适当的正则化和降维。

正则化是用来解决模型过拟合问题的一个很重要的手段,比如通过增加一个正则项,可以将一些相关性不大的特征项的参数衰减到几乎为0,相当于去掉了这一特征,这跟降维类似,相当于减少了特征维度。去掉基本无关的维度,那么就避免了模型对于这一维度特征的过分拟合。

(2)适当降低模型的规模。

过拟合很重要的一个原因也是模型的复杂度太高。适当减小模型的规模,尽量让神经网络结构的假设空间与预期目标模型需要存储的信息量相匹配。

7、源码

main函数:

import read_data_path as rp
import numpy as np
import cv2
import train as tr
from train import SequenceData
import yolo_loss
import yolo_model

class_dictionary = {'aeroplane': 0, 'bicycle': 1, 'bird': 2, 'boat': 3, 'bottle': 4, 'bus': 5,
                    'car': 6, 'cat': 7, 'chair': 8, 'cow': 9, 'diningtable': 10, 'dog': 11,
                    'horse': 12, 'motorbike': 13, 'person': 14, 'pottedplant': 15, 'sheep': 16,
                    'sofa': 17, 'train': 18, 'tvmonitor': 19}
class_list = list(class_dictionary.keys())

if __name__ == "__main__":

    train_x, train_y, val_x, val_y, test_x, test_y = rp.make_data()
    train_generator = SequenceData(train_x, train_y, 32)
    validation_generator = SequenceData(val_x, val_y, 32)

    # tr.train_network(train_generator, validation_generator, epoch=50)
    # tr.load_network_then_train(train_generator, validation_generator, epoch=400,
    #                            input_name='weights.hdf5', output_name='second_weights.hdf5')
    # tr.load_network_then_train(train_generator, validation_generator, epoch=400,
    #                            input_name='second_weights.hdf5', output_name='third_weights.hdf5')

    for i in range(len(test_x)-20, len(test_x)):

        img1 = cv2.imread(test_x[i])
        size = img1.shape

        img2 = img1 / 255
        img3 = cv2.resize(img2, (448, 448), interpolation=cv2.INTER_AREA)
        img4 = img3[np.newaxis, :, :, :]

        model = yolo_model.create_network()
        model.compile(loss=yolo_loss, optimizer='adam')

        model.load_weights('third_weights.hdf5')
        pre = model.predict(img4)
        pre1 = pre[0]

        candidate = []
        for j in range(7):
            for k in range(7):
                pre1[j, k, 22] = pre1[j, k, 22] * 448 / 7 + k * 448 / 7
                pre1[j, k, 23] = pre1[j, k, 23] * 448 / 7 + j * 448 / 7
                pre1[j, k, 24] = pre1[j, k, 24] * 448
                pre1[j, k, 25] = pre1[j, k, 25] * 448
                pre1[j, k, 26] = pre1[j, k, 26] * 448 / 7 + k * 448 / 7
                pre1[j, k, 27] = pre1[j, k, 27] * 448 / 7 + j * 448 / 7
                pre1[j, k, 28] = pre1[j, k, 28] * 448
                pre1[j, k, 29] = pre1[j, k, 29] * 448

                if pre1[j, k, 20] > 0.2 or pre1[j, k, 21] > 0.2:

                    if pre1[j, k, 20] > pre1[j, k, 21]:
                        x1 = pre1[j, k, 22] - pre1[j, k, 24] / 2
                        y1 = pre1[j, k, 23] - pre1[j, k, 25] / 2
                        x2 = pre1[j, k, 22] + pre1[j, k, 24] / 2
                        y2 = pre1[j, k, 23] + pre1[j, k, 25] / 2
                        category = np.argmax(pre1[j, k, 0:20])
                        confidence = pre1[j, k, 20] * np.max(pre1[j, k, 0:20])

                        x1 = x1 / 448 * size[1]
                        y1 = y1 / 448 * size[0]
                        x2 = x2 / 448 * size[1]
                        y2 = y2 / 448 * size[0]

                        candidate.append([x1, y1, x2, y2, category, confidence])

                    else:
                        x1 = pre1[j, k, 26] - pre1[j, k, 28] / 2
                        y1 = pre1[j, k, 27] - pre1[j, k, 29] / 2
                        x2 = pre1[j, k, 26] + pre1[j, k, 28] / 2
                        y2 = pre1[j, k, 27] + pre1[j, k, 29] / 2
                        category = np.argmax(pre1[j, k, 0:20])
                        confidence = pre1[j, k, 21] * np.max(pre1[j, k, 0:20])

                        x1 = x1 / 448 * size[1]
                        y1 = y1 / 448 * size[0]
                        x2 = x2 / 448 * size[1]
                        y2 = y2 / 448 * size[0]

                        candidate.append([x1, y1, x2, y2, category, confidence])

        candidate = np.array(candidate)

        for num in range(len(candidate)):
            a1 = int(candidate[num, 0])
            b1 = int(candidate[num, 1])
            a2 = int(candidate[num, 2])
            b2 = int(candidate[num, 3])
            index = int(candidate[num, 4])
            pre_class = class_list[index]
            confidence = str(candidate[num, 5])

            cv2.rectangle(img1, (a1, b1), (a2, b2), (0, 0, 255), 2)
            cv2.putText(img1, pre_class, (a1, b1), 1, 1, (0, 0, 255))
            cv2.putText(img1, confidence, (a1, b2), 1, 1, (0, 0, 255))

        # cv2.namedWindow("Image")
        # cv2.imshow("Image", img1)
        # cv2.waitKey(0)

        cv2.imwrite('/home/archer/CODE/PF/demo_result/' + str(i) + '.jpg', img1)


制作训练数据集:

import os
import xml.etree.ElementTree as ET


class_dictionary = {'aeroplane': 0, 'bicycle': 1, 'bird': 2, 'boat': 3, 'bottle': 4, 'bus': 5,
                    'car': 6, 'cat': 7, 'chair': 8, 'cow': 9, 'diningtable': 10, 'dog': 11,
                    'horse': 12, 'motorbike': 13, 'person': 14, 'pottedplant': 15, 'sheep': 16,
                    'sofa': 17, 'train': 18, 'tvmonitor': 19}
class_list = list(class_dictionary.keys())


def read_image_path():
    data_x = []
    filename = os.listdir('/home/archer/CODE/PF/JPEGImages')
    filename.sort()
    for name in filename:
        path = '/home/archer/CODE/PF/JPEGImages/' + name
        data_x.append(path)

    print('JPEGImages has been download ! ')
    return data_x


def read_coordinate_txt():
    data_y = []
    filename = os.listdir('/home/archer/CODE/PF/Annotations')
    filename.sort()
    for name in filename:
        tree = ET.parse('/home/archer/CODE/PF/Annotations/' + name)
        root = tree.getroot()

        coordinate = ''
        for obj in root.iter('object'):

            difficult = obj.find('difficult').text
            cls = obj.find('name').text
            if cls not in class_list or int(difficult) == 1:
                continue

            cls_id = class_list.index(cls)
            xml_box = obj.find('bndbox')
            x_min = int(xml_box.find('xmin').text)
            y_min = int(xml_box.find('ymin').text)
            x_max = int(xml_box.find('xmax').text)
            y_max = int(xml_box.find('ymax').text)

            loc = (str(x_min) + ',' + str(y_min) + ',' + str(x_max) + ',' + str(y_max) + ',' + str(cls_id) + '  ')
            coordinate = coordinate + loc

        data_y.append(coordinate)

    print('Object Coordinate has been download ! ')
    return data_y


def make_data():
    data_x = read_image_path()
    data_y = read_coordinate_txt()

    n = len(data_x)
    train_x = data_x[0:8000]
    train_y = data_y[0:8000]
    val_x = data_x[8000:9000]
    val_y = data_y[8000:9000]
    test_x = data_x[9000:n]
    test_y = data_y[9000:n]

    return train_x, train_y, val_x, val_y, test_x, test_y


YOLO-v1网络结构:

import numpy as np
from keras.models import Sequential, Model
from keras.layers import Dense, LeakyReLU, Flatten, Dropout
from keras.layers import Conv2D, MaxPooling2D
from keras.layers import BatchNormalization
from keras import layers
from keras.engine.topology import Layer
import keras.backend as k
from keras.regularizers import l2


class my_reshape(Layer):
    def __init__(self, target_shape, **kwargs):
        super(my_reshape, self).__init__(**kwargs)
        self.target_shape = target_shape    # (7, 7, 30)

    def compute_output_shape(self, input_shape):
        return (input_shape[0], ) + self.target_shape    # (batch, 7, 7, 30)

    def call(self, inputs, **kwargs):
        s = [self.target_shape[0], self.target_shape[1]]    # [7, 7]
        c = 20
        b = 2

        idx1 = s[0] * s[1] * c    # 7 * 7 * 20 = 980
        idx2 = idx1 + s[0] * s[1] * b    # 980 + 7 * 7 * 2 = 1078

        class_tensor = k.reshape(inputs[:, :idx1], (k.shape(inputs)[0],) + tuple([s[0], s[1], c]))
        class_tensor = k.softmax(class_tensor)    # shape = (batch, 7, 7, 20)

        confidence_tensor = k.reshape(inputs[:, idx1:idx2], (k.shape(inputs)[0],) + tuple([s[0], s[1], b]))
        confidence_tensor = k.sigmoid(confidence_tensor)    # shape = (batch, 7, 7, 2)

        location_tensor = k.reshape(inputs[:, idx2:], (k.shape(inputs)[0],) + tuple([s[0], s[1], b * 4]))
        location_tensor = k.sigmoid(location_tensor)    # shape = (batch, 7, 7, 8)

        outputs = k.concatenate([class_tensor, confidence_tensor, location_tensor])    # shape = (batch, 7, 7, 30)

        return outputs


def create_network():

    model = Sequential()
    model.add(Conv2D(64, (7, 7), input_shape=(448, 448, 3), strides=2, padding='same', trainable=False))
    model.add(MaxPooling2D(pool_size=(2, 2), strides=2, padding='same'))
    model.add(LeakyReLU(alpha=0.1))
    model.add(BatchNormalization(trainable=False))

    model.add(Conv2D(192, (3, 3), strides=1, padding='same', trainable=False))
    model.add(MaxPooling2D(pool_size=(2, 2), strides=2, padding='same'))
    model.add(LeakyReLU(alpha=0.1))
    model.add(BatchNormalization(trainable=False))

    model.add(Conv2D(128, (1, 1), strides=1, padding='same', trainable=False))
    model.add(LeakyReLU(alpha=0.1))
    model.add(Conv2D(256, (3, 3), strides=1, padding='same', trainable=False))
    model.add(LeakyReLU(alpha=0.1))
    model.add(Conv2D(256, (1, 1), strides=1, padding='same', trainable=False))
    model.add(LeakyReLU(alpha=0.1))
    model.add(Conv2D(512, (3, 3), strides=1, padding='same', trainable=False))
    model.add(MaxPooling2D(pool_size=(2, 2), strides=2, padding='same'))
    model.add(LeakyReLU(alpha=0.1))
    model.add(BatchNormalization(trainable=False))

    model.add(Conv2D(256, (1, 1), strides=1, padding='same', trainable=False))
    model.add(LeakyReLU(alpha=0.1))
    model.add(Conv2D(512, (3, 3), strides=1, padding='same', trainable=False))
    model.add(LeakyReLU(alpha=0.1))
    model.add(Conv2D(256, (1, 1), strides=1, padding='same', trainable=False))
    model.add(LeakyReLU(alpha=0.1))
    model.add(Conv2D(512, (3, 3), strides=1, padding='same', trainable=False))
    model.add(LeakyReLU(alpha=0.1))
    model.add(Conv2D(256, (1, 1), strides=1, padding='same', trainable=False))
    model.add(LeakyReLU(alpha=0.1))
    model.add(Conv2D(512, (3, 3), strides=1, padding='same', trainable=False))
    model.add(LeakyReLU(alpha=0.1))
    model.add(Conv2D(256, (1, 1), strides=1, padding='same', trainable=False))
    model.add(LeakyReLU(alpha=0.1))
    model.add(Conv2D(512, (3, 3), strides=1, padding='same', trainable=False))
    model.add(LeakyReLU(alpha=0.1))
    model.add(Conv2D(512, (1, 1), strides=1, padding='same', trainable=False))
    model.add(LeakyReLU(alpha=0.1))
    model.add(Conv2D(1024, (3, 3), strides=1, padding='same', trainable=False))
    model.add(MaxPooling2D(pool_size=(2, 2), strides=2, padding='same'))
    model.add(LeakyReLU(alpha=0.1))
    model.add(BatchNormalization(trainable=False))

    model.add(Conv2D(512, (1, 1), strides=1, padding='same', trainable=False))
    model.add(LeakyReLU(alpha=0.1))
    model.add(Conv2D(1024, (3, 3), strides=1, padding='same', trainable=False))
    model.add(LeakyReLU(alpha=0.1))
    model.add(Conv2D(512, (1, 1), strides=1, padding='same', trainable=False))
    model.add(LeakyReLU(alpha=0.1))
    model.add(Conv2D(1024, (3, 3), strides=1, padding='same', trainable=False))
    model.add(LeakyReLU(alpha=0.1))
    model.add(Conv2D(1024, (3, 3), strides=1, padding='same', trainable=False))
    model.add(LeakyReLU(alpha=0.1))
    model.add(Conv2D(1024, (3, 3), strides=2, padding='same', trainable=False))
    model.add(LeakyReLU(alpha=0.1))
    model.add(BatchNormalization(trainable=False))

    model.add(Conv2D(1024, (3, 3), strides=1, padding='same', trainable=False))
    model.add(LeakyReLU(alpha=0.1))
    model.add(BatchNormalization(trainable=False))
    model.add(Conv2D(1024, (3, 3), strides=1, padding='same', trainable=False))
    model.add(LeakyReLU(alpha=0.1))
    model.add(BatchNormalization(trainable=False))

    model.add(Flatten())
    model.add(Dense(4096))
    model.add(Dropout(0.5))

    model.add(Dense(1470))
    model.add(my_reshape((7, 7, 30)))

    model.summary()

    return model

YOLO-v1损失函数:

import keras.backend as k


def iou(pre_min, pre_max, true_min, true_max):

    intersect_min = k.maximum(pre_min, true_min)    # batch * 7 * 7 * 2 * 2
    intersect_max = k.minimum(pre_max, true_max)    # batch * 7 * 7 * 2 * 2

    intersect_wh = k.maximum(intersect_max - intersect_min, 0.)    # batch * 7 * 7 * 2 * 2
    intersect_area = intersect_wh[..., 0] * intersect_wh[..., 1]    # batch * 7 * 7 * 2

    pre_wh = pre_max - pre_min    # batch * 7 * 7 * 2 * 2
    true_wh = true_max - true_min    # batch * 7 * 7 * 1 * 2
    pre_area = pre_wh[..., 0] * pre_wh[..., 1]    # batch * 7 * 7 * 2
    true_area = true_wh[..., 0] * true_wh[..., 1]   # batch * 7 * 7 * 1

    union_area = pre_area + true_area - intersect_area    # batch * 7 * 7 * 2
    iou_score = intersect_area / union_area    # batch * 7 * 7 * 2

    return iou_score


def yolo_loss(y_true, y_pre):
    true_class = y_true[..., :20]    # batch * 7 * 7 * 20
    true_confidence = y_true[..., 20]    # batch * 7 * 7
    true_confidence1 = k.expand_dims(true_confidence)    # batch * 7 * 7 * 1
    true_location = y_true[..., 21:25]    # batch * 7 * 7 * 4

    pre_class = y_pre[..., :20]    # batch * 7 * 7 * 20
    pre_confidence = y_pre[..., 20:22]    # batch * 7 * 7 * 2
    pre_location = y_pre[..., 22:30]    # batch * 7 * 7 * 8

    true_location1 = k.reshape(true_location, [-1, 7, 7, 1, 4])    # batch * 7 * 7 * 1 * 4
    pre_location1 = k.reshape(pre_location, [-1, 7, 7, 2, 4])    # batch * 7 * 7 * 2 * 4

    true_xy = true_location1[..., :2] * 448 / 7    # batch * 7 * 7 * 1 * 2
    true_wh = true_location1[..., 2:4] * 448     # batch * 7 * 7 * 1 * 2
    true_xy_min = true_xy - true_wh / 2    # batch * 7 * 7 * 1 * 2
    true_xy_max = true_xy + true_wh / 2    # batch * 7 * 7 * 1 * 2

    pre_xy = pre_location1[..., :2] * 448 / 7    # batch * 7 * 7 * 2 * 2
    pre_wh = pre_location1[..., 2:4] * 448    # batch * 7 * 7 * 2 * 2
    pre_xy_min = pre_xy - pre_wh / 2    # batch * 7 * 7 * 2 * 2
    pre_xy_max = pre_xy + pre_wh / 2  # batch * 7 * 7 * 2 * 2

    iou_score = iou(pre_xy_min, pre_xy_max, true_xy_min, true_xy_max)  # batch * 7 * 7 * 2
    best_score = k.max(iou_score, axis=3, keepdims=True)   # batch * 7 * 7 * 1
    box_mask = k.cast(iou_score >= best_score, k.dtype(iou_score))  # batch * 7 * 7 * 2

    no_object_loss = 0.5 * (1 - box_mask * true_confidence1) * k.square(0 - pre_confidence)
    object_loss = box_mask * true_confidence1 * k.square(1 - pre_confidence)
    confidence_loss = no_object_loss + object_loss
    confidence_loss = k.sum(confidence_loss)

    class_loss = true_confidence1 * k.square(true_class - pre_class)
    class_loss = k.sum(class_loss)

    box_mask1 = k.expand_dims(box_mask)    # batch * 7 * 7 * 2 * 1
    true_confidence2 = k.expand_dims(true_confidence1)    # batch * 7 * 7 * 1 * 1

    location_loss_xy = 5 * box_mask1 * true_confidence2 * k.square((true_xy - pre_xy) / 448)
    location_loss_wh = 5 * box_mask1 * true_confidence2 * k.square((k.sqrt(true_wh) - k.sqrt(pre_wh)) / 448)
    location_loss = k.sum(location_loss_xy) + k.sum(location_loss_wh)

    total_loss = confidence_loss + class_loss + location_loss

    return total_loss

训练过程:

import numpy as np
from keras.models import load_model
import cv2
import yolo_model
from yolo_loss import yolo_loss
from keras.utils import Sequence
import math
from keras.callbacks import ModelCheckpoint
from keras import optimizers


class SequenceData(Sequence):

    def __init__(self, data_x, data_y, batch_size):
        self.batch_size = batch_size
        self.data_x = data_x
        self.data_y = data_y
        self.indexes = np.arange(len(self.data_x))

    def __len__(self):
        return math.floor(len(self.data_x) / float(self.batch_size))

    def on_epoch_end(self):
        np.random.shuffle(self.indexes)

    def __getitem__(self, idx):

        batch_index = self.indexes[idx * self.batch_size:(idx + 1) * self.batch_size]
        batch_x = [self.data_x[k] for k in batch_index]
        batch_y = [self.data_y[k] for k in batch_index]

        x = np.zeros((len(batch_x), 448, 448, 3))
        y = np.zeros((len(batch_y), 7, 7, 25))

        for i in range(self.batch_size):

            img = cv2.imread(batch_x[i])
            obj_all = batch_y[i].strip().split()
            size = img.shape

            img1 = img / 255
            resize_img = cv2.resize(img1, (448, 448), interpolation=cv2.INTER_AREA)
            x[i, :, :, :] = resize_img

            for j in range(len(obj_all)):
                obj = obj_all[j].split(',')
                x1, y1, x2, y2 = [int(obj[0]), int(obj[1]), int(obj[2]), int(obj[3])]
                category = int(obj[4])

                center_x = (x1 + x2) / 2
                center_y = (y1 + y2) / 2
                w = x2 - x1
                h = y2 - y1

                grid_x = int(7 * center_x / size[1])
                grid_y = int(7 * center_y / size[0])

                center_x_ratio = center_x * 7 / size[1] - grid_x
                center_y_ratio = center_y * 7 / size[0] - grid_y
                w_ratio = w / size[1]
                h_ratio = h / size[0]

                y[i, grid_y, grid_x, category] = 1
                y[i, grid_y, grid_x, 20] = 1
                y[i, grid_y, grid_x, 21:25] = np.array([center_x_ratio, center_y_ratio, w_ratio, h_ratio])

        return x, y


# create model and train and save
def train_network(train_generator, validation_generator, epoch):

    model = yolo_model.create_network()

    model.compile(loss=yolo_loss, optimizer='adam')

    checkpoint = ModelCheckpoint('/home/archer/CODE/best_weights.hdf5', monitor='val_loss',
                                 save_weights_only=True, save_best_only=True)

    model.fit_generator(
        train_generator,
        steps_per_epoch=len(train_generator),
        epochs=epoch,
        validation_data=validation_generator,
        validation_steps=len(validation_generator),
        callbacks=[checkpoint]
    )

    model.save_weights('first_weights.hdf5')


# Load the partially trained model and continue training and save
def load_network_then_train(train_generator, validation_generator, epoch, input_name, output_name):

    model = yolo_model.create_network()

    model.compile(loss=yolo_loss, optimizer='adam')

    model.load_weights(input_name)
    checkpoint = ModelCheckpoint('/home/archer/CODE/PF/best_weights.hdf5', monitor='val_loss',
                                 save_weights_only=True, save_best_only=True)

    model.fit_generator(
        train_generator,
        steps_per_epoch=len(train_generator),
        epochs=epoch,
        validation_data=validation_generator,
        validation_steps=len(validation_generator),
        callbacks=[checkpoint]
    )

    model.save_weights(output_name)

8、项目链接

如果代码跑不通,或者想直接使用训练好的模型,可以去下载项目链接:
https://blog.csdn.net/Twilight737

YOLO-WASTE是一个与YOLO(You Only Look Once)目标检测算法相关的术语。它是YOLO的一个改进版本,旨在提高YOLO在垃圾分类领域的应用效果。YOLO-WASTE通过训练模型来实现对垃圾的自动分类和识别。该算法使用YOLO的核心思想,即将整张图作为网络的输入,并直接在输出层回归bounding box的位置和bounding box所属的类别。通过对标注数据进行训练,YOLO-WASTE可以准确地检测和分类不同类型的垃圾。同时,YOLO-WASTE还可以实现实时的目标检测,具有较高的速度和泛化能力。然而,与YOLO相同,YOLO-WASTE也存在一些缺点,如对相互靠近的物体和小群体的检测效果不佳,以及在处理大小物体时定位误差较大。因此,YOLO-WASTE仍然需要进一步改进和优化以提高其性能和准确性。\[1\]\[3\] #### 引用[.reference_title] - *1* *3* [【论文笔记】:YOLO v1](https://blog.csdn.net/qq_41375609/article/details/100898123)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [YOLO V5 实操](https://blog.csdn.net/weixin_41929524/article/details/118915489)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值