当前位置:   article > 正文

【23年4月强悍更新】Yolov8详解与实战,独家首发最新改进 | 轻松涨点_yolov8什么时候出来的

yolov8什么时候出来的

Yolov8 摘要

YOLOv8 是 ultralytics 公司在 2023 年 1 月 10 号开源的 YOLOv5 的下一个重大更新版本,目前支持图像分类、物体检测和实例分割任务,鉴于 Yolov5 的良好表现,Yolov8 在还没有开源时就收到了用户的广泛关注。

yolov8 的整体架构如下:

【23年4月强悍更新】Yolov8详解与实战,独家首发最新改进 | 轻松涨点

Yolov8 的改进之处有以下几个地方:

  • Backbone:使用的依旧是 CSP 的思想,将 YOLOv5 中的 C3 模块被替换成了 C2f 模块,实现了进一步的轻量化,同时 YOLOv8 依旧使用了 YOLOv5 等架构中使用的 SPPF 模块;
  • PAN-FPN:YOLOv8 依旧使用了 PAN 的思想,不同的是 YOLOv8 将 YOLOv5 中 PAN-FPN 上采样阶段中的卷积结构删除了,同时也将 C3 模块替换为了 C2f 模块;
  • Decoupled-Head:这一点源自 YOLOX;分类和回归两个任务的 head 不再共享参数,YoloV8 也借鉴了这样的 head 设计。
  • Anchor-Free:YOLOv8 抛弃了以往的 Anchor-Base,使用了 Anchor-Free 的思想;
  • 损失函数:YOLOv8 使用 VFL Loss 作为分类损失,使用 DFL Loss+CIOU Loss 作为分类损失;
  • 样本匹配:YOLOv8 抛弃了以往的 IOU 匹配或者单边比例的分配方式,而是使用了 Task-Aligned Assigner 匹配方式。

yolov8 是个模型簇,从小到大包括:yolov8n、yolov8s、yolov8m、yolov8l、yolov8x 等。模型参数、运行速度、参数量等详见下表:
【23年4月强悍更新】Yolov8详解与实战,独家首发最新改进 | 轻松涨点
对比 yolov5,如下表:
【23年4月强悍更新】Yolov8详解与实战,独家首发最新改进 | 轻松涨点

mAP 和参数量都上升了不少,具体的感受还是要亲自实践一番。

这篇文章首先对 YoloV8 做详细的讲解,然后实现对 COCO 数据集的训练和测试,最后,实现自定义数据集的训练和测试。

希望能帮助到朋友们!

分割的结果
【23年4月强悍更新】Yolov8详解与实战,独家首发最新改进 | 轻松涨点
分类的结果

【23年4月强悍更新】Yolov8详解与实战,独家首发最新改进 | 轻松涨点

模型详解

C2F 模块

yolov8 将 yolov5 中的 C3 模块换成了 C2F 模型,我们先了解一下 C3 模块,如图:
【23年4月强悍更新】Yolov8详解与实战,独家首发最新改进 | 轻松涨点
C3 模块,其主要是借助 CSPNet 提取分流的思想,同时结合残差结构的思想,设计了所谓的 C3 Block,这里的 CSP 主分支梯度模块为 BottleNeck 模块,堆叠的个数由参数 n 来进行控制,不同的模型,n 的个数也不相同。C3 的 pytorch 代码如下:

class C3(nn.Module):
    # CSP Bottleneck with 3 convolutions
    def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5):  # ch_in, ch_out, number, shortcut, groups, expansion
        super().__init__()
        c_ = int(c2 * e)  # hidden channels
        self.cv1 = Conv(c1, c_, 1, 1)
        self.cv2 = Conv(c1, c_, 1, 1)
        self.cv3 = Conv(2 * c_, c2, 1)  # optional act=FReLU(c2)
        self.m = nn.Sequential(*(Bottleneck(c_, c_, shortcut, g, k=((1, 1), (3, 3)), e=1.0) for _ in range(n)))

    def forward(self, x):
        return self.cv3(torch.cat((self.m(self.cv1(x)), self.cv2(x)), 1))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

接下来,我们一起学习 C2F 模块,先经过一个 Conv,然后使用 chunk 函数将 out 平均拆分成两个向量,然后保存到 list 中,将后半部分输入到 Bottleneck Block 里面,Bottleneck Block 里面有 n 个 Bottleneck,将每个 Bottleneck 的输出都追加 list 中,有 n 个,所以拼接之后的 out 等于 0.5✖(n+2)。然后经过一个 Conv 输出,所以输出为 h×w×c_out。如下图:

【23年4月强悍更新】Yolov8详解与实战,独家首发最新改进 | 轻松涨点

如果还是比较难懂,我将具体的数据代入图中,得出下图:
【23年4月强悍更新】Yolov8详解与实战,独家首发最新改进 | 轻松涨点

Loss

对于 YOLOv8,其分类损失为 VFL Loss,其回归损失为 CIOU Loss+DFL 的形式,这里 Reg_max 默认为 16。

VFL 主要改进是提出了非对称的加权操作,FL 和 QFL 都是对称的。而非对称加权的思想来源于论文 PISA,该论文指出首先正负样本有不平衡问题,即使在正样本中也存在不等权问题,因为 mAP 的计算是主正样本。

【23年4月强悍更新】Yolov8详解与实战,独家首发最新改进 | 轻松涨点

q 是 label,正样本时候 q 为 bbox 和 gt 的 IoU,负样本时候 q=0,当为正样本时候其实没有采用 FL,而是普通的 BCE,只不过多了一个自适应 IoU 加权,用于突出主样本。而为负样本时候就是标准的 FL 了。可以明显发现 VFL 比 QFL 更加简单,主要特点是正负样本非对称加权、突出正样本为主样本。

针对这里的 DFL(Distribution Focal Loss),其主要是将框的位置建模成一个 general distribution,让网络快速的聚焦于和目标位置距离近的位置的分布。
【23年4月强悍更新】Yolov8详解与实战,独家首发最新改进 | 轻松涨点

DFL 能够让网络更快地聚焦于目标 y 附近的值,增大它们的概率;

DFL 的含义是以交叉熵的形式去优化与标签 y 最接近的一左一右 2 个位置的概率,从而让网络更快的聚焦到目标位置的邻近区域的分布;也就是说学出来的分布理论上是在真实浮点坐标的附近,并且以线性插值的模式得到距离左右整数坐标的权重。

head 部分

相对于 YOLOv5,YOLOv8 将 Head 里面 C3 模块替换为了 C2f,将上采样之前的 1×1 卷积去除了,将 Backbone 不同阶段输出的特征直接送入了上采样操作。通过下图对比可以看出差别:
【23年4月强悍更新】Yolov8详解与实战,独家首发最新改进 | 轻松涨点

【23年4月强悍更新】Yolov8详解与实战,独家首发最新改进 | 轻松涨点

YOLOv8 则是使用了 Decoupled-Head,同时由于使用了 DFL 的思想,因此回归头的通道数也变成了 4*reg_max 的形式:
【23年4月强悍更新】Yolov8详解与实战,独家首发最新改进 | 轻松涨点

模型实战

训练 COCO 数据集

本次使用 2017 版本的 COCO 数据集作为例子,演示如何使用 YoloV8 训练和预测。

下载数据集

Images:

  • 2017 Train images [118K/18GB] :http://images.cocodataset.org/zips/train2017.zip
  • 2017 Val images [5K/1GB]:http://images.cocodataset.org/zips/val2017.zip
  • 2017 Test images [41K/6GB]:http://images.cocodataset.org/zips/unlabeled2017.zip

Annotations:

  • 2017 annotations_trainval2017 [241MB]:http://images.cocodataset.org/annotations/annotations_trainval2017.zip

COCO 转 yolo 格式数据集(适用 V4,V5,V6,V7,V8)

最初的研究论文中,COCO 中有 91 个对象类别。然而,在 2014 年的第一次发布中,仅发布了 80 个标记和分割图像的对象类别。2014 年发布之后,2017 年发布了后续版本。详细的类别如下:

IDOBJECT (PAPER)OBJECT (2014 REL.)OBJECT (2017 REL.)SUPER CATEGORY
1personpersonpersonperson
2bicyclebicyclebicyclevehicle
3carcarcarvehicle
4motorcyclemotorcyclemotorcyclevehicle
5airplaneairplaneairplanevehicle
6busbusbusvehicle
7traintraintrainvehicle
8trucktrucktruckvehicle
9boatboatboatvehicle
10trafficlighttraffic lighttraffic lightoutdoor
11fire hydrantfire hydrantfire hydrantoutdoor
12streetsign--
13stop signstop signstop signoutdoor
14parking meterparking meterparking meteroutdoor
15benchbenchbenchoutdoor
16birdbirdbirdanimal
17catcatcatanimal
18dogdogdoganimal
19horsehorsehorseanimal
20sheepsheepsheepanimal
21cowcowcowanimal
22elephantelephantelephantanimal
23bearbearbearanimal
24zebrazebrazebraanimal
25giraffegiraffegiraffeanimal
26hat--accessory
27backpackbackpackbackpackaccessory
28umbrellaumbrellaumbrellaaccessory
29shoe--accessory
30eye glasses--accessory
31handbaghandbaghandbagaccessory
32tietietieaccessory
33suitcasesuitcasesuitcaseaccessory
34frisbeefrisbeefrisbeesports
35skisskisskissports
36snowboardsnowboardsnowboardsports
37sports ballsports ballsports ballsports
38kitekitekitesports
39baseball batbaseball batbaseball batsports
40baseball glovebaseball glovebaseball glovesports
41skateboardskateboardskateboardsports
42surfboardsurfboardsurfboardsports
43tennis rackettennis rackettennis racketsports
44bottlebottlebottlekitchen
45plate--kitchen
46wine glasswine glasswine glasskitchen
47cupcupcupkitchen
48forkforkforkkitchen
49knifeknifeknifekitchen
50spoonspoonspoonkitchen
51bowlbowlbowlkitchen
52bananabananabananafood
53appleappleapplefood
54sandwichsandwichsandwichfood
55orangeorangeorangefood
56broccolibroccolibroccolifood
57carrotcarrotcarrotfood
58hot doghot doghot dogfood
59pizzapizzapizzafood
60donutdonutdonutfood
61cakecakecakefood
62chairchairchairfurniture
63couchcouchcouchfurniture
64potted plantpotted plantpotted plantfurniture
65bedbedbedfurniture
66mirror--furniture
67dining tabledining tabledining tablefurniture
68window--furniture
69desk--furniture
70toilettoilettoiletfurniture
71door--furniture
72tvtvtvelectronic
73laptoplaptoplaptopelectronic
74mousemousemouseelectronic
75remoteremoteremoteelectronic
76keyboardkeyboardkeyboardelectronic
77cell phonecell phonecell phoneelectronic
78microwavemicrowavemicrowaveappliance
79ovenovenovenappliance
80toastertoastertoasterappliance
81sinksinksinkappliance
82refrigeratorrefrigeratorrefrigeratorappliance
83blender--appliance
84bookbookbookindoor
85clockclockclockindoor
86vasevasevaseindoor
87scissorsscissorsscissorsindoor
88teddy bearteddy bearteddy bearindoor
89hair drierhair drierhair drierindoor
90toothbrushtoothbrushtoothbrushindoor
91hair brush--indoor

可以看到,2014 年和 2017 年发布的对象列表是相同的,它们是论文中最初 91 个对象类别中的 80 个对象。所以在转换的时候,要重新对类别做映射,映射函数如下:

def coco91_to_coco80_class():  # converts 80-index (val2014) to 91-index (paper)
    # https://tech.amikelive.com/node-718/what-object-categories-labels-are-in-coco-dataset/
    # a = np.loadtxt('data/coco.names', dtype='str', delimiter='\n')
    # b = np.loadtxt('data/coco_paper.names', dtype='str', delimiter='\n')
    # x1 = [list(a[i] == b).index(True) + 1 for i in range(80)]  # darknet to coco
    # x2 = [list(b[i] == a).index(True) if any(b[i] == a) else None for i in range(91)]  # coco to darknet
    x = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, None, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, None, 24, 25, None,
         None, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, None, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
         51, 52, 53, 54, 55, 56, 57, 58, 59, None, 60, None, None, 61, None, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72,
         None, 73, 74, 75, 76, 77, 78, 79, None]
    return x

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

接下来,开始格式转换,工程的目录如下:
【23年4月强悍更新】Yolov8详解与实战,独家首发最新改进 | 轻松涨点

  • coco:存放解压后的数据集。
  • out:保存输出结果。
  • coco2yolo.py:转换脚本。

转换代码如下:

import json
import glob
import os
import shutil
from pathlib import Path
import numpy as np
from tqdm import tqdm


def make_folders(path='../out/'):
    # Create folders

    if os.path.exists(path):
        shutil.rmtree(path)  # delete output folder
    os.makedirs(path)  # make new output folder
    os.makedirs(path + os.sep + 'labels')  # make new labels folder
    os.makedirs(path + os.sep + 'images')  # make new labels folder
    return path


def convert_coco_json(json_dir='./coco/annotations_trainval2017/annotations/'):
    jsons = glob.glob(json_dir + '*.json')
    coco80 = coco91_to_coco80_class()

    # Import json
    for json_file in sorted(jsons):
        fn = 'out/labels/%s/' % Path(json_file).stem.replace('instances_', '')  # folder name
        fn_images = 'out/images/%s/' % Path(json_file).stem.replace('instances_', '')  # folder name
        os.makedirs(fn,exist_ok=True)
        os.makedirs(fn_images,exist_ok=True)
        with open(json_file) as f:
            data = json.load(f)
        print(fn)
        # Create image dict
        images = {'%g' % x['id']: x for x in data['images']}

        # Write labels file
        for x in tqdm(data['annotations'], desc='Annotations %s' % json_file):
            if x['iscrowd']:
                continue

            img = images['%g' % x['image_id']]
            h, w, f = img['height'], img['width'], img['file_name']
            file_path='coco/'+fn.split('/')[-2]+"/"+f
            # The Labelbox bounding box format is [top left x, top left y, width, height]
            box = np.array(x['bbox'], dtype=np.float64)
            box[:2] += box[2:] / 2  # xy top-left corner to center
            box[[0, 2]] /= w  # normalize x
            box[[1, 3]] /= h  # normalize y

            if (box[2] > 0.) and (box[3] > 0.):  # if w > 0 and h > 0
                with open(fn + Path(f).stem + '.txt', 'a') as file:
                    file.write('%g %.6f %.6f %.6f %.6f\n' % (coco80[x['category_id'] - 1], *box))
            file_path_t=fn_images+f
            print(file_path,file_path_t)
            shutil.copy(file_path,file_path_t)


def coco91_to_coco80_class():  # converts 80-index (val2014) to 91-index (paper)
    # https://tech.amikelive.com/node-718/what-object-categories-labels-are-in-coco-dataset/
    # a = np.loadtxt('data/coco.names', dtype='str', delimiter='\n')
    # b = np.loadtxt('data/coco_paper.names', dtype='str', delimiter='\n')
    # x1 = [list(a[i] == b).index(True) + 1 for i in range(80)]  # darknet to coco
    # x2 = [list(b[i] == a).index(True) if any(b[i] == a) else None for i in range(91)]  # coco to darknet
    x = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, None, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, None, 24, 25, None,
         None, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, None, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
         51, 52, 53, 54, 55, 56, 57, 58, 59, None, 60, None, None, 61, None, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72,
         None, 73, 74, 75, 76, 77, 78, 79, None]
    return x

convert_coco_json()
  • 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
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71

开始运行:
【23年4月强悍更新】Yolov8详解与实战,独家首发最新改进 | 轻松涨点

转换完成后,验证转换的结果:

import cv2
import os

def draw_box_in_single_image(image_path, txt_path):
    # 读取图像
    image = cv2.imread(image_path)

    # 读取txt文件信息
    def read_list(txt_path):
        pos = []
        with open(txt_path, 'r') as file_to_read:
            while True:
                lines = file_to_read.readline()  # 整行读取数据
                if not lines:
                    break
                # 将整行数据分割处理,如果分割符是空格,括号里就不用传入参数,如果是逗号, 则传入‘,'字符。
                p_tmp = [float(i) for i in lines.split(' ')]
                pos.append(p_tmp)  # 添加新读取的数据
                # Efield.append(E_tmp)
                pass
        return pos


    # txt转换为box
    def convert(size, box):
        xmin = (box[1]-box[3]/2.)*size[1]
        xmax = (box[1]+box[3]/2.)*size[1]
        ymin = (box[2]-box[4]/2.)*size[0]
        ymax = (box[2]+box[4]/2.)*size[0]
        box = (int(xmin), int(ymin), int(xmax), int(ymax))
        return box

    pos = read_list(txt_path)
    print(pos)
    tl = int((image.shape[0]+image.shape[1])/2)
    lf = max(tl-1,1)
    for i in range(len(pos)):
        label = str(int(pos[i][0]))
        print('label is '+label)
        box = convert(image.shape, pos[i])
        image = cv2.rectangle(image,(box[0], box[1]),(box[2],box[3]),(0,0,255),2)
        cv2.putText(image,label,(box[0],box[1]-2), 0, 1, [0,0,255], thickness=2, lineType=cv2.LINE_AA)
        pass

    if pos:
        cv2.imwrite('./Data/see_images/{}.png'.format(image_path.split('\\')[-1][:-4]), image)
    else:
        print('None')



img_folder = "./out/images/val2017"
img_list = os.listdir(img_folder)
img_list.sort()

label_folder = "./out/labels/val2017"
label_list = os.listdir(label_folder)
label_list.sort()
if not os.path.exists('./Data/see_images'):
    os.makedirs('./Data/see_images')
for i in range(len(img_list)):
    image_path = img_folder + "\\" + img_list[i]
    txt_path = label_folder + "\\" + label_list[i]
    draw_box_in_single_image(image_path, txt_path)
  • 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
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64

结果展示:
【23年4月强悍更新】Yolov8详解与实战,独家首发最新改进 | 轻松涨点

配置 yolov8 环境

可以直接安装 requirements.txt 里面所有的库文件,执行安装命令:

pip install -r requirements.txt
  • 1

如果不想安装这么多库文件,在运行的时候,查看缺少哪个库,就安装哪个库,比如我的环境:

pip install thop
  • 1

我的本地只缺少了这个库文件。

训练

下载代码:https://github.com/ultralytics/ultralytics,通过下载的方式可以下载到源码,这样方便修改。
也可以使用命令:

pip install ultralytics
  • 1

如果仅仅是为了使用 yolov8,可以使用这种方式安装。

yolov8 还支持使用命令的方式,例如:

yolo predict model=yolov8n.pt source="https://ultralytics.com/images/bus.jpg"
  • 1

接下来,创建训练脚本,可以使用 yaml 文件创建,例如:

from ultralytics import YOLO

# Load a model
model = YOLO("yolov8n.yaml")  # build a new model from scratch
  • 1
  • 2
  • 3
  • 4

模型文件在ultralytics/models/v8下面,如图:

【23年4月强悍更新】Yolov8详解与实战,独家首发最新改进 | 轻松涨点

也可以使用预训练模型创建。例如:

model = YOLO("yolov8n.pt")  # load a pretrained model (recommended for training)
  • 1

然后开启训练。

# Use the model
model.train(data="coco128.yaml", epochs=3)  # train the model
  • 1
  • 2

数据集的配置文件在:ultralytics/datasets/ 下面,如图:
【23年4月强悍更新】Yolov8详解与实战,独家首发最新改进 | 轻松涨点
是不是很简单!!!!

接下来,我们配置自己的环境。
第一步 找到ultralytics/datasets/coco.yaml文件。
【23年4月强悍更新】Yolov8详解与实战,独家首发最新改进 | 轻松涨点
然后将其复制到根目录
【23年4月强悍更新】Yolov8详解与实战,独家首发最新改进 | 轻松涨点
将里面的路径修改为:

# Ultralytics YOLO 
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/352042
推荐阅读
相关标签