SHU研究方法与前沿

YOLO报告

image-20220221235938515

实验环境

OS:Unbuntu

Docker:Nvidia-Torch

IDE:Pycharm远程连接Docker Container。

GPU:Quadro P6000*1,24G。

Driver Version: 465.19.01,CUDA Version: 11.3。

root@a34bb36ed5ba:/tmp/pycharm_project_125/PyTorch_YOLOv1-main# nvidia-smi
Mon Feb 21 23:20:36 2022       
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 465.19.01    Driver Version: 465.19.01    CUDA Version: 11.3     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|                               |                      |               MIG M. |
|===============================+======================+======================|
|   0  NVIDIA Quadro P...  Off  | 00000000:AF:00.0 Off |                  Off |
| 26%   19C    P8     8W / 250W |      0MiB / 24449MiB |      0%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+

+-----------------------------------------------------------------------------+
| Processes:                                                                  |
|  GPU   GI   CI        PID   Type   Process name                  GPU Memory |
|        ID   ID                                                   Usage      |
|=============================================================================|
|  No running processes found                                                 |
+-----------------------------------------------------------------------------+

实验记录

image-20220219110200799

打开项目的data/voc0712.py文件来查看读取VOC数据的代码。首先,我们会看到两个单独的变量VOC_CLASSESVOC_ROOT,分别是VOC数据集的所有类别名称和数据集的路径。请读者自行更改适用于自己电脑或服务器的数据集路径。

我的数据集当前路径是/tmp/pycharm_project_125/PyTorch_YOLOv1-main/data

接着,我们会看到一个名为VOCAnnotationTransform的类,这个类的主要作用就是将读取进来的数据类别(如“person”和“bird”)依据变量VOC_CLASSES中所存放的所有类别名称来转换成相应的类别序号,如读进来是一个“bird”的数据,那么它的类别序号就是2,注意,这里是从0开始计数的。

然后,就是这个代码文件最主要的部分了:VOCDetection类。该类的作用就是从VOC数据集中读取数据,我们主要看这个类中的pull_item函数。

nohup python train.py --cuda -d voc -ms 1>1.txt 2>2.txt &

前言

从深度学习时代开始,目标检测的框架也基本明确了下来:一个常见的目标检测网络,其本身往往可以分为一下三大块:

img

Backbone network,即主干网络,是目标检测网络最为核心的部分,大多数时候,backbone选择的好坏,对检测性能影响是十分巨大的。

Neck network,即颈部网络(脖子网络?说实话,这个不翻译过来比较好……),Neck部分的主要作用就是将由backbone输出的特征进行整合。其整合方式有很多,最为常见的就是FPN(Feature Pyramid Network),有关FPN的内容,我们会在展开介绍Neck的时候再次提到的。

Detection head,即检测头,这一部分的作用就没什么特殊的含义了,就是若干卷积层进行预测,也有些工作里把head部分称为decoder(解码器)的,这种称呼不无道理,head部分就是在由前面网络输出的特征上去进行预测,约等于是从这些信息里解耦出来图像中物体的类别和位置信息。

我们可以为以上任意部分单独去设计一个模块,然后“插进去”即可。很多目标检测的优化工作就是这么来的,比如2018年的ECCV上的RFBNet,就是在SSD基础上,设计了RFB模块插进Neck部分,从而显著提升了模型性能。

通常,为了实现从图像中检测目标的位置和类别,我们会先从图像中提取出些必要的特征信息,比如HOG特征,然后利用这些特征去实现定位和分类。而在深度学习这一块,这一任务就交由backbone网络来完成。深度学习的强大之处就在于其特征提取的能力,在很多任务上都超越了人工特征。

网络结构改进

img

改进Backbone

官方的YOLOv1的主干网络是参考了GoogLeNet设计的(没有inception结构),这里我们直接替换成ResNet18。关于ResNet18的网络结构,如下图所示:

img

esNet18网络更轻,使用了诸如residual connection、batch normalization等结构,性能上要更强于原先的backbone网络。这里,我们没必要换更大的ResNet网络,如ResNet50、101等,18即可满足要求。

添加一个Neck

对于给定输入的416x416x3的图像,经过ResNet18网络处理后,最后会输出一张13x13x512的特征图。这里,我们添加一个Neck结构,对特征图中的信息进行更好地处理,这里,我们选择性价比极高的SPP:

img

注意,SPP接受的输入特征图大小是13x13x512,经过四个maxpooling分支处理后,再汇合到一起,那么得到的是一个13x13x2048的特征图,这里,我们会再接一个1x1的卷积层(conv1x1+BN+LeakyReLU)将通道压缩一下,这个1x1的卷积层没有在图中体现出来。

最终Neck部分的输入同样是13x13x512的特征图。

Detection head

官方的YOLOv1中这一部分使用了全连接层,也就是先将特征图flatten成一维向量,然后接全连接层得到4096维的一维向量。这里,我们抛掉flatten操作,而是在SPP输出的13x13x512的特征图上使用若干层卷积来处理,类似于RetinaNet那样。这里,我们使用非常简单的1x1卷积和3x3卷积重复堆叠的方式,如下图所示:

img

改进prediction层

官方的YOLOv1最后使用全连接层来做预测,我们直接替换成当下主流的做法:用1x1卷积在特征图上去做预测,具体来说,head输出一个13x13x512大小的特征图,然后用1x1卷积(不接BN层,不接激活函数)去得到一个13x13x(1+C+4)的特征图,其中1对应YOLO中的objectness预测,C对应类别预测(PASCAL VOC上,C=20;COCO上,C=80),4则是bbox预测,这里,每个grid处之预测一个bbox,而不是B个。

objectness预测

不同于官方YOLOv1中的使用预测框和真实框之间的IoU作为优化目标,我们直接采用最简单的01标签即可。无需在训练过程中计算IoU。

4.2 class预测

不同于官方YOLOv1的线性输出,我们使用更为合理的softmax来输出。

4.3 bbox预测

在YOLOv1中,bbox分支就是学习中心点的偏移量$c_x,c_y$和归一化的边界框的宽高 $ w,h$,但是不论是哪个量,YOLOv1均使用线性函数来输出,未加任何约束限制,很明显会有以下两点问题。

损失函数Loss

对于objectness $\hat{C}_{i j}$ ,我们仍使用L2 loss:
$$
L_{o b j}=5 \sum_{0}^{s^{2}} 1_{i j}^{o b j}\left(\hat{C}_{i j}-1\right)^{2}+1 \sum_{0}^{s^{2}} 1_{i j}^{n o o b j}\left(\hat{C}_{i j}-0\right)^{2}
$$
对于class $\hat{p}_{i j}$ ,我们使用Cross-entropy loss:
$$
L_{c l s}=\sum_{0}^{s^{2}} 1_{i j}^{o b j} C E\left(\hat{p}_{i j}, p_{i j}\right)
$$
对于txty $\hat{t}_{x}, \hat{t}_{y}$ ,我们使用Binary cross-entropy:
$$
L_{t_{x}, t_{y}}=\sum_{0}^{s^{2}} 1_{i j}^{o b j} B C E\left(\hat{t}_{x_{i j}}, t_{x_{i j}}\right)
$$
对于twth,我们使用L2 loss:
$$
L_{t_{w}, t_{h}}=\sum_{0}^{s^{2}} 1_{i j}^{o b j}\left[\left(\hat{t}_{w}-t_{w}\right)^{2}+\left(\hat{t}_{h}-t_{h}\right)^{2}\right]
$$
最后总的loss为:
$$
L=L_{o b j}+L_{c l s}+\lambda_{b b o x} \left(L_{t_{x}, t_{y}}+L_{t_{w}, t_{h}}\right)
$$
其中 $\lambda_{b b o x}=2-\left(w / w_{i}\right)
\left(h / h_{i}\right)$ ,是bbox loss的权重,用于平衡不同大小的边界框之间的loss, $w_{i}, h_{i}$ 是输入图像的尺寸,如 416 。

数据预处理

数据预处理在训练模型时是非常重要的,一个好的预处理手段可以大幅提升模型的性能以及泛化性,反之则会严重损害模型的性能。那么在这本节,我们将针对这一概念来展开介绍一下。

预处理一共包括两种方式:数据增强(data augmentation)和基础变换(base transform)。

数据增强

数据增强是使用一系列的诸如随机裁剪、随机翻转、随机图像色彩变换等手段对图像进行处理;

基础变换

基础变换则是普通的图像归一化操作,即将所有像素除以255,再使用ImageNet上的均值和方差做归一化。

多尺度训练技巧

代码中的“多尺度训练”技巧,代码图下所示,代码实现参考了YOLOv5:

#多尺度训练
if iter_i % 10 == 0 and iter_i > 0 and args.multi_scale:
    # 随机选择一个新的训练尺寸
    train_size = random.randint(10, 19) * 32
    model.set_grid(train_size)
    if args.multi_scale:
        # 插值
        images = torch.nn.functional.interpolate(images, 
                                                 size=train_size, 
                                                 mode='bilinear', 
                                                 align_corners=False)

所谓的多尺度训练技巧,即在训练过程中,不断随机改变输入图像的大小,图像大小改变了,那图像中的物体大小也会跟着发生变化,其目的就是让模型能够见到更多尺度的物体,缓解模型对尺度变化不敏感的问题。其实,这一点和常用的尺度缩放的数据增强是一个性质。

这里,我们借鉴了YOLOv2工作给出的多尺度配置,每训练10次,就随机从{320,352,384,416,448,480,512,544,576,608}中抽取一个新的尺寸,用做接下来训练中的图像尺寸。我们只需要在命令行中传入“-ms”参数即可调用多尺度训练技巧。具体来说,训练时开启了多尺度训练技巧,那么dataloader给出的图像尺寸都会设置为640x640,然后使用双线性插值的方法去resize图像的尺寸,得到不同尺度的图像。

开始训练时,请读者先打开电脑的终端(建议使用ubuntu系统,当然,win10也是支持的),进入项目文件夹下,输入下面一行命令即可运行训练文件:

python train.py --cuda -d voc

切记,一定要输入“–cuda”,否则程序只调用CPU来训练,那将是个极其漫长的训练过程。“-d voc”是指定程序使用VOC数据集进行训练,也可以换成”-d coco”来切换至COCO数据集进行训练。

如果使用多尺度训练技巧的话,只需增加“-ms”参数即可:

python train.py --cuda -ms

更多的命令行参数,还请读者打开train.py代码文件详细阅读。

训练过程中,读者将会在终端下看到如图2的输出,在训练的初期,由于默认使用warmup策略,因此损失下降的会较慢。

训练结果

大概2h训练完成了。

[Epoch 150/150][Iter 480/517][lr 0.000010][Loss: obj 2.10 || cls 0.70 || bbox 4.38 || total 7.18 || size 416 || time: 2.02]
[Epoch 150/150][Iter 490/517][lr 0.000010][Loss: obj 2.19 || cls 0.32 || bbox 3.50 || total 6.00 || size 512 || time: 1.87]
[Epoch 150/150][Iter 500/517][lr 0.000010][Loss: obj 2.71 || cls 0.33 || bbox 3.76 || total 6.79 || size 480 || time: 2.66]
[Epoch 150/150][Iter 510/517][lr 0.000010][Loss: obj 1.89 || cls 0.13 || bbox 3.38 || total 5.41 || size 512 || time: 2.36]
im_detect: 1/4952 0.007s
im_detect: 501/4952 0.006s
im_detect: 1001/4952 0.006s
im_detect: 1501/4952 0.006s
im_detect: 2001/4952 0.006s
im_detect: 2501/4952 0.006s
im_detect: 3001/4952 0.006s
im_detect: 3501/4952 0.006s
im_detect: 4001/4952 0.006s
im_detect: 4501/4952 0.006s
Evaluating detections
VOC07 metric? Yes
AP for aeroplane = 0.6739
AP for bicycle = 0.7894
AP for bird = 0.6578
AP for boat = 0.5891
AP for bottle = 0.3757
AP for bus = 0.7786
AP for car = 0.7695
AP for cat = 0.8805
AP for chair = 0.5301
AP for cow = 0.7549
AP for diningtable = 0.6860
AP for dog = 0.7815
AP for horse = 0.8007
AP for motorbike = 0.7708
AP for person = 0.6734
AP for pottedplant = 0.4112
AP for sheep = 0.7191
AP for sofa = 0.7073
AP for train = 0.8071
AP for tvmonitor = 0.6772
Mean AP = 0.6917
Mean AP:  0.6917037706264867
Saving state, epoch: 150

实验结果

代码输出结果

训练log输出在1.txt

train.py
def train():
        # 创建命令行参数
        args = parse_args()
        print("Setting Arguments.. : ", args)
        print("----------------------------------------------------------")

        # 保存模型的路径
        path_to_save = os.path.join(args.save_folder, args.dataset, args.version)
        os.makedirs(path_to_save, exist_ok=True)

        # 是否使用cuda来训练
        if args.cuda:
            print('use cuda')
            cudnn.benchmark = True
            device = torch.device("cuda")
        else:
            device = torch.device("cpu")

        # 是否使用多尺度训练技巧
        if args.multi_scale:
            print('use the multi-scale trick ...')
            train_size = 640
            val_size = 416
        else:
            train_size = 416
            val_size = 416

        # 训练所使用到的配置参数
        cfg = train_cfg

        # 创建dataset类和dataloader类
        if args.dataset == 'voc':
            # 加载VOC数据集
            data_dir = VOC_ROOT
            num_classes = 20
            dataset = VOCDetection(root=data_dir, 
                                    transform=SSDAugmentation(train_size)
                                    )

            evaluator = VOCAPIEvaluator(data_root=data_dir,
                                        img_size=val_size,
                                        device=device,
                                        transform=BaseTransform(val_size),
                                        labelmap=VOC_CLASSES
                                        )

        elif args.dataset == 'coco':
            # 加载COCO数据集
            data_dir = coco_root
            num_classes = 80
            dataset = COCODataset(
                        data_dir=data_dir,
                        img_size=train_size,
                        transform=SSDAugmentation(train_size),
                        debug=args.debug
                        )

            evaluator = COCOAPIEvaluator(
                            data_dir=data_dir,
                            img_size=val_size,
                            device=device,
                            transform=BaseTransform(val_size)
                            )

        else:
            print('unknow dataset !! Only support voc and coco !!')
            exit(0)

        print('Training model on:', dataset.name)
        print('The dataset size:', len(dataset))
        print("----------------------------------------------------------")

        # dataloader类
        dataloader = torch.utils.data.DataLoader(
                        dataset, 
                        batch_size=args.batch_size, 
                        shuffle=True, 
                        collate_fn=detection_collate,
                        num_workers=args.num_workers,
                        pin_memory=True
                        )

        # 创建我们的模型
        if args.version == 'yolo':
            from models.yolo import myYOLO
            yolo_net = myYOLO(device=device, 
                            input_size=train_size, 
                            num_classes=num_classes, 
                            trainable=True)
            print('Let us train yolo on the %s dataset ......' % (args.dataset))

        else:
            print('We only support YOLO !!!')
            exit()

        model = yolo_net
        model.to(device).train()

        # 是否使用tensorboard来保存训练过程中的各类数据
        if args.tfboard:
            print('use tensorboard')
            from torch.utils.tensorboard import SummaryWriter
            c_time = time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time()))
            log_path = os.path.join('log/coco/', args.version, c_time)
            os.makedirs(log_path, exist_ok=True)

            writer = SummaryWriter(log_path)

        if args.resume is not None:
            print('keep training model: %s' % (args.resume))
            model.load_state_dict(torch.load(args.resume, map_location=device))

        # 构建训练所使用的优化器
        base_lr = args.lr
        tmp_lr = base_lr
        optimizer = optim.SGD(model.parameters(), 
                                lr=args.lr, 
                                momentum=args.momentum,
                                weight_decay=args.weight_decay
                                )

        max_epoch = cfg['max_epoch']                  # 最大训练轮次
        epoch_size = len(dataset) // args.batch_size  # 每一训练轮次的迭代次数

        # 开始训练
        for epoch in range(args.start_epoch, max_epoch):

            # 使用阶梯式学习衰减策略
            if epoch in cfg['lr_epoch']:
                tmp_lr = tmp_lr * 0.1
                set_lr(optimizer, tmp_lr)

            # 获取一批数据
            for iter_i, (images, targets) in enumerate(dataloader):
                # 使用warm-up学习率策略
                if not args.no_warm_up:
                    if epoch < args.wp_epoch:
                        ni = iter_i+epoch*epoch_size
                        nw = args.wp_epoch*epoch_size
                        tmp_lr = base_lr * pow(ni / nw, 4)
                        set_lr(optimizer, tmp_lr)

                    elif epoch == args.wp_epoch and iter_i == 0:
                        tmp_lr = base_lr
                        set_lr(optimizer, tmp_lr) 
                #多尺度训练
                if iter_i % 10 == 0 and iter_i > 0 and args.multi_scale:
                    # 随机选择一个新的训练尺寸
                    train_size = random.randint(10, 19) * 32
                    model.set_grid(train_size)
                if args.multi_scale:
                    # 插值
                    images = torch.nn.functional.interpolate(images, 
                                                            size=train_size, 
                                                            mode='bilinear', 
                                                            align_corners=False
                                                            )

                # 制作训练标签
                targets = [label.tolist() for label in targets]
                targets = tools.gt_creator(input_size=train_size, 
                                        stride=yolo_net.stride, 
                                        label_lists=targets
                                        )

                # to device
                images = images.to(device)          
                targets = targets.to(device)

                # 前向推理,计算损失
                conf_loss, cls_loss, bbox_loss, total_loss = model(images, target=targets)

                # 反向传播和模型更新
                total_loss.backward()        
                optimizer.step()
                optimizer.zero_grad()

            # 验证模型的性能
            if (epoch + 1) % args.eval_epoch == 0:
                model.trainable = False
                model.set_grid(val_size)
                model.eval()

                # evaluate
                evaluator.evaluate(model)

                # convert to training mode.
                model.trainable = True
                model.set_grid(train_size)
                model.train()

            # 保存模型
            if (epoch + 1) % 10 == 0:
                print('Saving state, epoch:', epoch + 1)
                torch.save(model.state_dict(), os.path.join(path_to_save, 
                            args.version + '_' + repr(epoch + 1) + '.pth')
                            )
test.py

测试时,默认使用输入尺寸是416,由于我们的模型在训练时使用了多尺度训练技巧,因此读者不妨多加一个“-size”命令来调整输入的尺寸,比如608。

python test.py --cuda -d voc --trained_model 权重文件路径 -size 608
python test.py --cuda -d voc --trained_model /tmp/pycharm_project_125/PyTorch_YOLOv1-main/weights/voc/yolo/yolo_140.pth -size 608

image-20220222000332939

000001

eval.py输出

单独在VOC2007测试集上测试我们模型的mAP,那么运行项目中所提供的eval.py文件即可,具体命令如下行所示。

python eval.py --cuda -d voc --trained_model weights/voc/yolo/yolo_140.pth -size 608
608
root@a34bb36ed5ba:/tmp/pycharm_project_125/PyTorch_YOLOv1-main# python eval.py --cuda -d voc --trained_model weights/voc/yolo/yolo_140.pth -size 608
NOTE! Installing ujson may make loading annotations faster.
eval on voc ...
use cuda
Finished loading model!
/opt/conda/lib/python3.8/site-packages/torch/nn/functional.py:718: UserWarning: Named tensors and all their associated APIs are an experimental feature and subject to change. Please do not use them for anything important until they are released as stable. (Triggered internally at  ../c10/core/TensorImpl.h:1153.)
  return torch.max_pool2d(input, kernel_size, stride, padding, dilation, ceil_mode)
im_detect: 1/4952 0.877s

im_detect: 501/4952 0.006s
im_detect: 1001/4952 0.006s
im_detect: 1501/4952 0.006s
im_detect: 2001/4952 0.006s
im_detect: 2501/4952 0.007s
im_detect: 3001/4952 0.006s
im_detect: 3501/4952 0.006s
im_detect: 4001/4952 0.006s
im_detect: 4501/4952 0.006s
Evaluating detections
Writing aeroplane VOC results file
Writing bicycle VOC results file
Writing bird VOC results file
Writing boat VOC results file
Writing bottle VOC results file
Writing bus VOC results file
Writing car VOC results file
Writing cat VOC results file
Writing chair VOC results file
Writing cow VOC results file
Writing diningtable VOC results file
Writing dog VOC results file
Writing horse VOC results file
Writing motorbike VOC results file
Writing person VOC results file
Writing pottedplant VOC results file
Writing sheep VOC results file
Writing sofa VOC results file
Writing train VOC results file
Writing tvmonitor VOC results file
VOC07 metric? Yes
AP for aeroplane = 0.7415
AP for bicycle = 0.8155
AP for bird = 0.7189
AP for boat = 0.6250
AP for bottle = 0.4761
AP for bus = 0.7799
AP for car = 0.8255
AP for cat = 0.8512
AP for chair = 0.5768
AP for cow = 0.7789
AP for diningtable = 0.6656
AP for dog = 0.8054
AP for horse = 0.8525
AP for motorbike = 0.8012
AP for person = 0.7142
AP for pottedplant = 0.4637
AP for sheep = 0.8098
AP for sofa = 0.6780
AP for train = 0.8048
AP for tvmonitor = 0.7454
Mean AP = 0.7265
~~~~~~~~
Results:
0.741
0.815
0.719
0.625
0.476
0.780
0.826
0.851
0.577
0.779
0.666
0.805
0.853
0.801
0.714
0.464
0.810
0.678
0.805
0.745
0.726
~~~~~~~~

--------------------------------------------------------------
Results computed with the **unofficial** Python eval code.
Results should be very close to the official MATLAB eval code.
--------------------------------------------------------------
Mean AP:  0.7264871672869923
512
AP for aeroplane = 0.7361
AP for bicycle = 0.8080
AP for bird = 0.6943
AP for boat = 0.6410
AP for bottle = 0.4455
AP for bus = 0.7837
AP for car = 0.8005
AP for cat = 0.8649
AP for chair = 0.5755
AP for cow = 0.7824
AP for diningtable = 0.6887
AP for dog = 0.8197
AP for horse = 0.8046
AP for motorbike = 0.7706
AP for person = 0.7077
AP for pottedplant = 0.4647
AP for sheep = 0.7414
AP for sofa = 0.7030
AP for train = 0.8233
AP for tvmonitor = 0.7006
Mean AP = 0.7178

模型结果分析

我们的YOLOv1+在VOC2007测试集上的mAP如下所示,最后一行所展示的YOLOv1的mAP结果摘自YOLOv1论文。

模型 输入图像尺寸 数据集 mAP
YOLOv1+ 320 VOC2007 test 64.8
YOLOv1+ 416 VOC2007 test 69.2
YOLOv1+ 512 VOC2007 test 71.8
YOLOv1+ 608 VOC2007 test 73.3
YOLOv1 448 VOC2007 test 63.4

可见,我们的YOLOv1+在416的图像输入尺寸下所获得的69.2% mAP指标要高于YOLOv1在448输入下的63.4% mAP指标。这得益于我们更好的结构设计和更好的训练方法。

验证数据增强的必要性

在训练代码train.py文件中,先找到VOCDetection类,其中,我们是将数据增强的类SSDAugmentation传给了参数transform,这里,我们改为将基础变换的类BaseTransform(在文件data/__init__.py中已定义)传给参数transform,如此改动后,在训练过程中,我们的代码就不会使用数据增强来对数据进行预处理了。

模型 输入图像尺寸 是否数据增强 mAP
YOLOv1+ 416 69.2
YOLOv1+ 416 60.4

训练完毕后,在VOC2007测试集上进行测试,结果如下表所示。可见,使用数据增强确实可以显著提升模型的性能。

image-20220222000520745

资料

关于YOLO入门

https://www.zhihu.com/column/c_1364967262269693952

https://github.com/yjh0410/PyTorch_YOLOv1

yolov3——mmdetection

YOLOv1原理讲解->YOLOv1模型搭建->VOC数据集准备->数据预处理->数据增强->训练模型->制作训练正样本->损失函数->测试模型与检测结果的可视化->计算mAP->COCO数据集准备->COCO-val验证集和AP计算方法。

YOLO官方都是使用自制的darknet模型作为backbone网络,如darknet19和darknet53。在使用pytorch框架实现YOLO时,一种常见的做法是将YOLO官方的权重通过一些不太复杂的操作加载进来。出于方便考虑,YOLOv1和YOLOv2backbone就都换成resnet系列,具体来说,YOLOv1将使用resnet18,YOLOv2使用resnet50。

这一部分可以让我们能够对目标检测有个基本而初步的认识,对于YOLO系列的思想有了基本了解和认识,并且能够在当下的主流数据集COCO上完成测试,最重要的,是我们能够掌握数据预处理的基本方法,这对于开展今后的学术工作是很重要的。

很多时候,每当我们进入一个新的领域,首先最需要解决的问题包括三点:

1.确定常用的数据预处理方法;

2.确定该领域的benchmark;

3.确定评价指标及其计算方法。

本部分的核心项目代码:

https://github.com/yjh0410/PyTorch_YOLOv1github.com/yjh0410/PyTorch_YOLOv1

这一部分的最后,我们会介绍一个增强版的YOLOv1检测器,性能很强大。

https://github.com/yjh0410/PyTorch_YOLO-Familygithub.com/yjh0410/PyTorch_YOLO-Family

作者:Kissrabbit
链接:https://zhuanlan.zhihu.com/p/364216183
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

第十周上课提交报告。

补充材料

从深度学习时代开始,目标检测的框架也基本明确了下来:一个常见的目标检测网络,其本身往往可以分为一下三大块:

img

Backbone network,即主干网络,是目标检测网络最为核心的部分,大多数时候,backbone选择的好坏,对检测性能影响是十分巨大的。

Neck network,即颈部网络(脖子网络?说实话,这个不翻译过来比较好……),Neck部分的主要作用就是将由backbone输出的特征进行整合。其整合方式有很多,最为常见的就是FPN(Feature Pyramid Network),有关FPN的内容,我们会在展开介绍Neck的时候再次提到的。

Detection head,即检测头,这一部分的作用就没什么特殊的含义了,就是若干卷积层进行预测,也有些工作里把head部分称为decoder(解码器)的,这种称呼不无道理,head部分就是在由前面网络输出的特征上去进行预测,约等于是从这些信息里解耦出来图像中物体的类别和位置信息。

我们可以为以上任意部分单独去设计一个模块,然后“插进去”即可。很多目标检测的优化工作就是这么来的,比如2018年的ECCV上的RFBNet,就是在SSD基础上,设计了RFB模块插进Neck部分,从而显著提升了模型性能。

通常,为了实现从图像中检测目标的位置和类别,我们会先从图像中提取出些必要的特征信息,比如HOG特征,然后利用这些特征去实现定位和分类。而在深度学习这一块,这一任务就交由backbone网络来完成。深度学习的强大之处就在于其特征提取的能力,在很多任务上都超越了人工特征。

最后,简单介绍几个常用的backbone模型:

首先是大型网络:

1.VGG网络:《Very Deep Convolutional Networks for Large-Scale Image Recognition》。其中最常用的就是VGG-16.

2.ResNet网络:《Deep Residual Learning for Image Recognition》。其中最常用的就是ResNet50ResNet101。当任务需求很小的时候,也可以用ResNet18.

3.ResNeXT网络:《Aggregated residual transformations for deep neural networks》,这个我没有用过,但很多sota工作中都会使用,刷榜的小伙伴不妨考虑一下。

4.ResNet+DCN网络:这一网络主要是将DCN工作应用在ResNet网络上,DCN来源于这篇文章:《Deformable Convolutional Networks》。DCN是常用的涨点神器,不过似乎在实际部署的时候要复杂一些,刷榜的时候还是很值得一用。

5.DarkNet网络:常用的包括darknet19darknet53,这两个网络分别来源于YOLOv2和YOLOv3两个工作中。其中darknet19对标的是vgg19,darknet53对标的是resnet101,但由于darknet本身是个很小众的深度学习框架,不受学术界关注,且这两个网络均是由darknet框架实现的,因此也就很少会在其他工作中看到这两个backbone。不过,笔者更偏爱darknet,也对其进行了复现,因为结构简洁,便于理解。

6.CSPResNet网络:出自于《CSPNet: A New Backbone that can Enhance Learning Capability of CNN》。CSP是一种很好用的结构,在减少参数量的同时,还能够提升模型性能,是不可多得的性价比极高的模块之一。像前一段时间的Scaled-YOLOv4就借鉴了这一工作的思想大幅度提升了YOLOv4的性能。不过,目前似乎也不是主流,仍旧无法撼动ResNet101和ResNet+DCN的刷榜地位。

然后是轻量型网络:

1.MobileNet:谷歌的工作,一共出了v1,v2,v3三个版本了,相较于上面那些以GPU为主要应用平台的大型网络,MobileNet则着眼于低性能的移动端平台,如手机、嵌入式设备等。

2.ShuffleNet:旷视的工作,一共出了v1和v2两个版本,同样是针对于低性能的移动端平台。

还有很多出色的backbone网络这里就不一一列举了,本文就只列出几个常用的,感兴趣的小伙伴可以自行查找更多的backbone网络。


YOLO-v1最大的特点就在于:仅使用一个卷积神经网络端到端地实现检测物体的目的。其网络整体的结构如下图所示:

3yolo

image-20220222102753192

事实上,YOLOv1本身最大的弊端就在于“flatten”这种方式本身。

基本上,对于“flatten方式会破坏特征的空间结构信息”的这一观点已经成为业界共识。现在几乎看不到还有哪个one-stage检测模型还会用全连接层来做检测了。

4yolo4

另外,我们注意到,YOLOv1中并没有使用BatchNormalization(BN),这是因为在那个时候,BN还没有兴起。

参考资料

1.2 YOLO入门教程:YOLOv1(2)-浅析YOLOv1 - Kissrabbit的文章 - 知乎 https://zhuanlan.zhihu.com/p/364367221

环境配置

#删除原来指向python2的软链接
sudo mv /usr/bin/python /usr/bin/python.bak 
#/usr/local/python3.6/bin/python3.6 这个路径为实际的python3.6的bin文件夹下的Python3.6的路径,这个按照自己的实际情况进行更改 
sudo ln -s /usr/bin/python3.6 /usr/bin/python  

然后再窗口直接输入python就会调用python3.6了

补充 查看看python版本和路径的命名方法:

python --version

source python
ln -s /opt/conda/bin/python3.8 /usr/bin/python
ln -sv /opt/conda/lib/python3.8/site-packages/pip /usr/bin/pip

报错资料

报错1

Traceback (most recent call last):
  File "/tmp/pycharm_project_125/PyTorch_YOLOv1-main/data/voc0712.py", line 207, in <module>
    dataset = VOCDetection(VOC_ROOT, img_size, [('2007', 'trainval')],
TypeError: __init__() got an unexpected keyword argument 'mosaic'

报错2

nohup python train.py --cuda -d voc -ms 1>1.txt 2>2.txt &
nohup /opt/conda/bin/python3.8 train.py --cuda -d voc -ms 1>1.txt 2>2.txt &

才反应过来好像是用的P6000在跑。

ImportError: libGL.so.1: cannot open shared object file: No such file or directory——docker容器内问题报错

容器内libGL.so.1问题
在实验室的服务器下跑yolov3的pytorch版本时,原本前几天跑的好好的,后来突然不知道怎么回事,就开始报错,一脸懵逼。
后来百度了好多。。。emm 挺不顺利的,所以mark一下。
ImportError: libGL.so.1: cannot open shared object file: No such file or directory。

环境是ubuntu16.04。
指令:
sudo apt update
sudo apt install libgl1-mesa-glx
就ok了

可能容器内没有sudo指令
可以
apt-get update
apt-get install sudo
————————————————
版权声明:本文为CSDN博主「Atsea0107」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_35516745/article/details/103822597


The function is not implemented. Rebuild the library with Windows, GTK+ 2.x or Carbon support. If you are on Ubuntu or Debian, install libgtk2.0-dev and pkg-config, then re-run cmake or configure script in function 'cvShowImage'

qt.qpa.plugin: Could not find the Qt platform plugin "xcb" in ""
This application failed to start because no Qt platform plugin could be initialized. Reinstalling the application may fix this problem.

image-20220221163430118

装Qt


   转载规则


《SHU研究方法与前沿》 Henry-Avery 采用 知识共享署名 4.0 国际许可协议 进行许可。
  目录