25k字图文解读YOLOv8及实例分割(附python代码)

news2024/11/24 11:02:49

学习使用 未经详细专业审核

目录

  • 0.引言
  • 1.概述
    • 1.1 Backbone
    • 1.2 Head
    • 1.3 Loss
    • 1.4 Train
  • 2.模型结构
    • 2.1 Backbone和Neck的具体变化
    • 2.2 Head的具体变化
  • 3.Loss计算
    • 3.1 正负样本分配策略
    • 3.2 Loss计算
  • 4.训练数据增强
  • 5.训练策略
  • 6.模型推理过程
  • 7.网络模型解析
    • 7.1 卷积神经单元(model.py)
      • 7.1.1 autopad
      • 7.1.2 Conv
      • 7.1.3 DWConv
      • 7.1.4 DWConvTranspose2d
      • 7.1.5 ConvTranspose
      • 7.1.6 DFL(Distribution Focal Loss)
      • 7.1.7 TransformerLayer
      • 7.1.8 Transformer Block
      • 7.1.9 Bottleneck
      • 7.1.10 BottleneckCSP
      • 7.1.11 C3
      • 7.1.12 C2
      • 7.1.13 C2f
      • 7.1.14 ChannelAttention
      • 7.1.15 SpatialAttention
      • 7.1.16 CBAM
      • 7.1.17 C1
      • 7.1.18 C3x
      • 7.1.19 C3TR
      • 7.1.20 SPP
      • 7.1.21 SPPF
      • 7.1.22 Focus
      • 7.1.23 GhostConv
      • 7.1.24 GhostBottleneck
      • 7.1.25 C3Ghost
      • 7.1.26 Concat
  • 8.Yolov8实操
    • 8.1 下载工程并安装ultralytics
    • 8.2 数据集准备
    • 8.3 模型的训练/验证/预测/验证
      • 8.3.1 使用CLI
      • 8.3.2 使用python
    • 8.4 数据扩充
    • 8.5 日志、检查点、绘图与文件管理
  • 参考文献

本文讲解了YOLOv8算法的核心特性及改进点,详细介绍了网络结构、Loss计算、数据增强手段、训练策略、模型推理,最后给出实操步骤。

0.引言

Yolo系列对比:
在这里插入图片描述

1.概述

YOLOv8 算法的核心特性和改动可以归结为如下:

提供了一个全新的 SOTA 模型,包括 P5 640 和 P6 1280 分辨率的目标检测网络和基于 YOLACT 的实例分割模型。和 YOLOv5 一样,基于缩放系数也提供了 N/S/M/L/X 尺度的不同大小模型,用于满足不同场景需求

1.1 Backbone

骨干网络和 Neck 部分可能参考了 YOLOv7 ELAN 设计思想,将 YOLOv5 的 C3 结构换成了梯度流更丰富的 C2f 结构,并对不同尺度模型调整了不同的通道数。

  • 属于对模型结构精心微调,不再是无脑一套参数应用所有模型,大幅提升了模型性能。不过这个 C2f 模块中存在 Split 等操作对特定硬件部署没有之前那么友好了

1.2 Head

Head部分较yolov5而言有两大改进:

  1. 换成了目前主流的解耦头结构(Decoupled-Head),将分类和检测头分离
  2. 同时也从 Anchor-Based 换成了 Anchor-Free

1.3 Loss

  1. YOLOv8抛弃了以往的IOU匹配或者单边比例的分配方式,而是使用了Task-Aligned Assigner正负样本匹配方式。
  2. 并引入了 Distribution Focal Loss(DFL)

1.4 Train

训练的数据增强部分引入了 YOLOX 中的最后 10 epoch 关闭 Mosiac 增强的操作,可以有效地提升精度

  • 从上面可以看出,YOLOv8 主要参考了最近提出的诸如 YOLOX、YOLOv6、YOLOv7 和 PPYOLOE 等算法的相关设计,本身的创新点不多,偏向工程实践,主推的还是 ultralytics 这个框架本身。

  • 下面将按照模型结构设计、Loss 计算、训练数据增强、训练策略和模型推理过程共 5 个部分详细介绍 YOLOv8 目标检测的各种改进,实例分割部分暂时不进行描述。

2.模型结构

如下图, 左侧为 YOLOv5-s,右侧为 YOLOv8-s。

在暂时不考虑 Head 情况下,对比 YOLOv5 和 YOLOv8 的 yaml 配置文件可以发现改动较小。
在这里插入图片描述
在这里插入图片描述

2.1 Backbone和Neck的具体变化

a) 第一个卷积层的 kernel 从 6x6 变成了 3x3
b) 所有的 C3 模块换成 C2f,结构如下所示,可以发现多了更多的跳层连接和额外的 Split 操作
在这里插入图片描述
在这里插入图片描述
c)去掉了 Neck 模块中的 2 个卷积连接层
d) Backbone 中 C2f 的 block 数从 3-6-9-3 改成了 3-6-6-3
e) 查看 N/S/M/L/X 等不同大小模型,可以发现 N/S 和 L/X 两组模型只是改了缩放系数,但是 S/M/L 等骨干网络的通道数设置不一样,没有遵循同一套缩放系数。如此设计的原因应该是同一套缩放系数下的通道设置不是最优设计,YOLOv7 网络设计时也没有遵循一套缩放系数作用于所有模型

2.2 Head的具体变化

从原先的耦合头变成了解耦头,并且从 YOLOv5 的 Anchor-Based 变成了 Anchor-Free。
在这里插入图片描述
从上图可以看出,不再有之前的 objectness 分支,只有解耦的分类和回归分支,并且其回归分支使用了 Distribution Focal Loss 中提出的积分形式表示法。

3.Loss计算

Loss 计算过程包括 2 个部分: 正负样本分配策略和 Loss 计算。

3.1 正负样本分配策略

现代目标检测器大部分都会在正负样本分配策略上面做文章,典型的如 YOLOX 的 simOTA、TOOD 的 TaskAlignedAssigner 和 RTMDet 的 DynamicSoftLabelAssigner,这类 Assigner 大都是动态分配策略,而 YOLOv5 采用的依然是静态分配策略。考虑到动态分配策略的优异性,YOLOv8 算法中则直接引用了 TOOD 的 TaskAlignedAssigner。

TaskAlignedAssigner 的匹配策略简单总结为: 根据分类与回归的分数加权的分数选择正样本。

3.2 Loss计算

Loss 计算包括 2 个分支: 分类和回归分支,没有了之前的 objectness 分支。

分类分支依然采用 BCE Loss

回归分支需要和 Distribution Focal Loss 中提出的积分形式表示法绑定,因此使用了 Distribution Focal Loss, 同时还使用了 CIoU Loss。3 个 Loss 采用一定权重比例加权即可。

4.训练数据增强

在这里插入图片描述
考虑到不同模型应该采用的数据增强强度不一样,因此对于不同大小模型,有部分超参会进行修改,典型的如大模型会开启 MixUp 和 CopyPaste。数据增强后典型效果如下所示:
在这里插入图片描述

5.训练策略

YOLOv8 的训练策略和 YOLOv5 没有啥区别,最大区别就是模型的训练总 epoch 数从 300 提升到了 500,这也导致训练时间急剧增加。以 YOLOv8-S 为例,其训练策略汇总如下:
在这里插入图片描述

6.模型推理过程

YOLOv8 的推理过程和 YOLOv5 几乎一样,唯一差别在于前面需要对 Distribution Focal Loss 中的积分表示 bbox 形式进行解码,变成常规的 4 维度 bbox,后续计算过程就和 YOLOv5 一样了。
在这里插入图片描述

7.网络模型解析

7.1 卷积神经单元(model.py)

ultralytics/nn/modules.py文件中定义了yolov8网络中的卷积神经单元。

7.1.1 autopad

在这里插入图片描述

def autopad(k, p=None, d=1):  # kernel(卷积核), padding(填充), dilation(扩张)
    # 返回pad的大小,使得padding后输出张量的shape不变
    if d > 1: # 如果采用扩张卷积,则计算扩张后实际的kernel大小
        k = d * (k - 1) + 1 if isinstance(k, int) else [d * (x - 1) + 1 for x in k]  # 
    if p is None:
        p = k // 2 if isinstance(k, int) else [x // 2 for x in k]  # 自动pad
    return p

在这里插入图片描述

7.1.2 Conv

在这里插入图片描述

class Conv(nn.Module):
    # 标准的卷积 参数(输入通道数, 输出通道数, 卷积核大小, 步长, 填充, 组, 扩张, 激活函数)
    default_act = nn.SiLU()  # 默认的激活函数

    def __init__(self, c1, c2, k=1, s=1, p=None, g=1, d=1, act=True):
        super().__init__()
        self.conv = nn.Conv2d(c1, c2, k, s, autopad(k, p, d), groups=g, dilation=d, bias=False) # 2维卷积,其中采用了自动填充函数。
        self.bn = nn.BatchNorm2d(c2) # 使得每一个batch的特征图均满足均值为0,方差为1的分布规律
        # 如果act=True 则采用默认的激活函数SiLU;如果act的类型是nn.Module,则采用传入的act; 否则不采取任何动作 (nn.Identity函数相当于f(x)=x,只用做占位,返回原始的输入)。
        self.act = self.default_act if act is True else act if isinstance(act, nn.Module) else nn.Identity() 

    def forward(self, x):  # 前向传播
        return self.act(self.bn(self.conv(x))) # 采用BatchNorm
    def forward_fuse(self, x): #  用于Model类的fuse函数融合 Conv + BN 加速推理,一般用于测试/验证阶段
        return self.act(self.conv(x)) # 不采用BatchNorm

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

7.1.3 DWConv

在这里插入图片描述

class DWConv(Conv):
    # 深度可分离卷积
    def __init__(self, c1, c2, k=1, s=1, d=1, act=True):  # ch_in, ch_out, kernel, stride, dilation, activation
        super().__init__(c1, c2, k, s, g=math.gcd(c1, c2), d=d, act=act)

7.1.4 DWConvTranspose2d

在这里插入图片描述

class DWConvTranspose2d(nn.ConvTranspose2d):
    # Depth-wise transpose convolution
    def __init__(self, c1, c2, k=1, s=1, p1=0, p2=0):  # 输入通道, 输出通道, 卷积核大小, 步长, padding, padding_out
        super().__init__(c1, c2, k, s, p1, p2, groups=math.gcd(c1, c2))

7.1.5 ConvTranspose

和Conv类似,只是把Conv2d换成了ConvTranspose2d

class ConvTranspose(nn.Module):
    # Convolution transpose 2d layer
    default_act = nn.SiLU()  # default activation

    def __init__(self, c1, c2, k=2, s=2, p=0, bn=True, act=True):
        super().__init__()
        self.conv_transpose = nn.ConvTranspose2d(c1, c2, k, s, p, bias=not bn)
        self.bn = nn.BatchNorm2d(c2) if bn else nn.Identity()
        self.act = self.default_act if act is True else act if isinstance(act, nn.Module) else nn.Identity()

    def forward(self, x):
        return self.act(self.bn(self.conv_transpose(x)))

7.1.6 DFL(Distribution Focal Loss)

在这里插入图片描述

class DFL(nn.Module):
    # Integral module of Distribution Focal Loss (DFL) proposed in Generalized Focal Loss 
    def __init__(self, c1=16):
        super().__init__()
        self.conv = nn.Conv2d(c1, 1, 1, bias=False).requires_grad_(False)
        x = torch.arange(c1, dtype=torch.float)
        self.conv.weight.data[:] = nn.Parameter(x.view(1, c1, 1, 1))
        self.c1 = c1

    def forward(self, x):
        b, c, a = x.shape  # batch, channels, anchors
        return self.conv(x.view(b, 4, self.c1, a).transpose(2, 1).softmax(1)).view(b, 4, a)
        # return self.conv(x.view(b, self.c1, 4, a).softmax(1)).view(b, 4, a)

7.1.7 TransformerLayer

关于Transformer的理解和torch.nn.MultiheadAttention 的用法,请参考博客《详解注意力机制和Transformer》:https://blog.csdn.net/zyw2002/article/details/128788680
在这里插入图片描述
在这里插入图片描述
我们可以发现它和yolo中的TransformerLayer部分只是少了层规范化(LayerNorm),以及在Feed-Forward Networks 中只采用了两个不带偏置线性层,且没有采用激活函数。

class TransformerLayer(nn.Module):
    # Transformer layer  (LayerNorm layers removed for better performance)
    def __init__(self, c, num_heads): # c: 词特征向量的大小  num_heads 检测头的个数。
        super().__init__()
        self.q = nn.Linear(c, c, bias=False)# 计算query, in_features=out_features=c
        self.k = nn.Linear(c, c, bias=False)# 计算key
        self.v = nn.Linear(c, c, bias=False)# 计算value
        self.ma = nn.MultiheadAttention(embed_dim=c, num_heads=num_heads) # 多头注意力机制
        self.fc1 = nn.Linear(c, c, bias=False)
        self.fc2 = nn.Linear(c, c, bias=False)

    def forward(self, x):
        x = self.ma(self.q(x), self.k(x), self.v(x))[0] + x  # 多头注意力机制+残差连接
        x = self.fc2(self.fc1(x)) + x  # 两个全连接层+ 残差连接
        return x

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

7.1.8 Transformer Block

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

class TransformerBlock(nn.Module):
    def __init__(self, c1, c2, num_heads, num_layers):
        super().__init__()
        self.conv = None
        if c1 != c2:
            self.conv = Conv(c1, c2)
        self.linear = nn.Linear(c2, c2)  # learnable position embedding
        self.tr = nn.Sequential(*(TransformerLayer(c2, num_heads) for _ in range(num_layers)))
        self.c2 = c2

    def forward(self, x):  # x:(b,c1,w0,h0)
        if self.conv is not None:
            x = self.conv(x) # x:(b,c2,w,h)
        b, _, w, h = x.shape
        p = x.flatten(2).permute(2, 0, 1) # flatten后:(b,c2,w*h)  p: (w*h,b,c2)	
        # linear后: (w*h,b,c2)   tr后: (w*h,b,c2)   permute后: (b,c2,w*h)  reshape后:(b,c2,w,h)
        return self.tr(p + self.linear(p)).permute(1, 2, 0).reshape(b, self.c2, w, h)

在这里插入图片描述

7.1.9 Bottleneck

在这里插入图片描述

class Bottleneck(nn.Module):
    # Standard bottleneck
    def __init__(self, c1, c2, shortcut=True, g=1, k=(3, 3), e=0.5):  # ch_in, ch_out, shortcut, groups, kernels, expand
        super().__init__()
        c_ = int(c2 * e)  # hidden channels
        self.cv1 = Conv(c1, c_, k[0], 1)  # 输入通道: c1, 输出通道:c_ , 卷积核:3x3, 步长1
        self.cv2 = Conv(c_, c2, k[1], 1, g=g) # 输入通道:c_ , 输出通道c2, 卷积核:3x3, 步长1
        self.add = shortcut and c1 == c2  # 当传入的shortcut参数为true,且c1和c2相等时,则使用残差连接。

    def forward(self, x):
        return x + self.cv2(self.cv1(x)) if self.add else self.cv2(self.cv1(x))

7.1.10 BottleneckCSP

详细请参考CSPNet的论文和源码。
论文《CSPNet: A New Backbone that can Enhance Learning Capability of CNN》
源码https://github.com/WongKinYiu/CrossStagePartialNetworks
在这里插入图片描述

class BottleneckCSP(nn.Module):
    # CSP Bottleneck https://github.com/WongKinYiu/CrossStagePartialNetworks
    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
        # 输出x的大小是(b,c1,w,h)
        self.cv1 = Conv(c1, c_, 1, 1) # cv1的大小为(b,c_,w,h)
        self.cv2 = nn.Conv2d(c1, c_, 1, 1, bias=False) # cv2的大小为(b,c_,w,h)
        self.cv3 = nn.Conv2d(c_, c_, 1, 1, bias=False) # m通过Conv2d,变成cv3,大小是(b,c_,w,h)
        self.cv4 = Conv(2 * c_, c2, 1, 1)
        self.bn = nn.BatchNorm2d(2 * c_)  # applied to cat(cv2, cv3)
        self.act = nn.SiLU()
        self.m = nn.Sequential(*(Bottleneck(c_, c_, shortcut, g, e=1.0) for _ in range(n)))  
        # cv1通过n个串联的bottleneck,变成m,大小为(b,c_,w,h)

    def forward(self, x):
        y1 = self.cv3(self.m(self.cv1(x))) # (b,c_,w,h)
        y2 = self.cv2(x) # (b,c_,w,h)
        return self.cv4(self.act(self.bn(torch.cat((y1, y2), 1))))
        # cat后:(b,2*c_,w,h) 返回cv4: (b,c2,w,h)

在这里插入图片描述

7.1.11 C3

与 BottleneckCSP 类似,但少了 1 个 Conv、1 个 BN、1 个 Act,运算量更少。总共只有3次卷积(cv1,cv2,cv3)。
在这里插入图片描述

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, 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))

7.1.12 C2

C2只有两个卷积(cv1,cv2)的CSP Bottleneck。
在这里插入图片描述

class C2(nn.Module):
    # CSP Bottleneck with 2 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__()
 		# 假设输入的x大小是(b,c1,w,h)
        self.c = int(c2 * e)  # hidden channels e=0.5,对输出通道进行平分。
        self.cv1 = Conv(c1, 2 * self.c, 1, 1) # cv1的大小是(b,c2,w,h)
        self.cv2 = Conv(2 * self.c, c2, 1)  # optional act=FReLU(c2)
        # self.attention = ChannelAttention(2 * self.c)  # or SpatialAttention()   #此处可以使用空间注意力或者跨通道的注意力机制。
        self.m = nn.Sequential(*(Bottleneck(self.c, self.c, shortcut, g, k=((3, 3), (3, 3)), e=1.0) for _ in range(n)))  # a通过n个串联的Bottleneck后的到m,m的大小是(b,c,w,h)

    def forward(self, x):
        a, b = self.cv1(x).split((self.c, self.c), 1)# 对cv进行在维度1进行平分,a和b的大小都是(b,c,w,h)
        return self.cv2(torch.cat((self.m(a), b), 1)) # 把m和b在维度1进行cat后,大小是(b,c2,w,h)。最终通过cv2,大小是(b,c2,w,h)

在这里插入图片描述

7.1.13 C2f

C2f与C2相比,每个Bottleneck的输出都会被Concat到一起。
在这里插入图片描述

class C2f(nn.Module):
    # CSP Bottleneck with 2 convolutions
    def __init__(self, c1, c2, n=1, shortcut=False, g=1, e=0.5):  # ch_in, ch_out, number, shortcut, groups, expansion
        super().__init__()
        # 假设输入的x大小是(b,c1,w,h)
        self.c = int(c2 * e)  # hidden channels
        self.cv1 = Conv(c1, 2 * self.c, 1, 1) # cv1的大小是(b,c2,w,h)
        self.cv2 = Conv((2 + n) * self.c, c2, 1)  # optional act=FReLU(c2)
        self.m = nn.ModuleList(Bottleneck(self.c, self.c, shortcut, g, k=((3, 3), (3, 3)), e=1.0) for _ in range(n)) # n个Bottleneck组成的ModuleList,可以把m看做是一个可迭代对象

    def forward(self, x):
        y = list(self.cv1(x).split((self.c, self.c), 1))
        # cv1的大小是(b,c2,w,h),对cv1在维度1等分成两份(假设分别是a和b),a和b的大小均是(b,c2/2,w,h)。此时y=[a,b]。
        y.extend(m(y[-1]) for m in self.m)
        # 然后对列表y中的最后一个张量b输入到ModuleList中的第1个bottleneck里,得到c,c的大小是(b,c2/2,w,h)。然后把c也加入y中。此时y=[a,b,c]
        # 重复上述操作n次(因为是n个bottleneck),最终得到的y列表中一共有n+2个元素。
        return self.cv2(torch.cat(y, 1)) 
        # 对列表y中的张量在维度1进行连接,得到的张量大小是(b,(n+2)*c2/2,w,h)。
        # 最终通过cv2,输出张量的大小是(b,c2,w,h)

在这里插入图片描述

7.1.14 ChannelAttention

通道注意力模型: 通道维度不变,压缩空间维度。该模块关注输入图片中有意义的信息。
在这里插入图片描述

class ChannelAttention(nn.Module):
    # Channel-attention module https://github.com/open-mmlab/mmdetection/tree/v3.0.0rc1/configs/rtmdet
    def __init__(self, channels: int) -> None:
        super().__init__()
        self.pool = nn.AdaptiveAvgPool2d(1) # 自适应平均池化后,大小为(b,c,1,1)
        self.fc = nn.Conv2d(channels, channels, 1, 1, 0, bias=True)
        self.act = nn.Sigmoid()

    def forward(self, x: torch.Tensor) -> torch.Tensor:
        return x * self.act(self.fc(self.pool(x)))

在这里插入图片描述

7.1.15 SpatialAttention

空间注意力模块:空间维度不变,压缩通道维度。该模块关注的是目标的位置信息。
在这里插入图片描述

class SpatialAttention(nn.Module):
    # Spatial-attention module
    def __init__(self, kernel_size=7):
        super().__init__()
        assert kernel_size in (3, 7), 'kernel size must be 3 or 7'  # kernel size 的大小必须是3或者7
        padding = 3 if kernel_size == 7 else 1  # 当kernel_size是7时,padding=3; 当kernel_size是3时,padding=1
        self.cv1 = nn.Conv2d(2, 1, kernel_size, padding=padding, bias=False)
        self.act = nn.Sigmoid()

    def forward(self, x):
        return x * self.act(self.cv1(torch.cat([torch.mean(x, 1, keepdim=True), torch.max(x, 1, keepdim=True)[0]], 1)))

在这里插入图片描述

7.1.16 CBAM

CBAM就是把ChannelAttention和SpatialAttention串联在一起。
在这里插入图片描述

class CBAM(nn.Module):
    # Convolutional Block Attention Module
    def __init__(self, c1, kernel_size=7):  # ch_in, kernels
        super().__init__()
        self.channel_attention = ChannelAttention(c1)
        self.spatial_attention = SpatialAttention(kernel_size)

    def forward(self, x):
        return self.spatial_attention(self.channel_attention(x))

7.1.17 C1

总共只有3次卷积(cv1,cv2,cv3)的Bottleneck。
在这里插入图片描述

class C1(nn.Module):
    # CSP Bottleneck with 1 convolution
    def __init__(self, c1, c2, n=1):  # ch_in, ch_out, number
        super().__init__()
        self.cv1 = Conv(c1, c2, 1, 1)
        self.m = nn.Sequential(*(Conv(c2, c2, 3) for _ in range(n)))

    def forward(self, x):
        y = self.cv1(x)
        return self.m(y) + y

在这里插入图片描述

7.1.18 C3x

C3x 继承自C3, 变换是Bottleneck中的卷积核大小变为(1,3)和(3,3)

class C3x(C3):
    # C3 module with cross-convolutions
    def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5):
        super().__init__(c1, c2, n, shortcut, g, e)
        self.c_ = int(c2 * e)
        self.m = nn.Sequential(*(Bottleneck(self.c_, self.c_, shortcut, g, k=((1, 3), (3, 1)), e=1) for _ in range(n)))

7.1.19 C3TR

C3TR继承自C3, n 个 Bottleneck 更换为 1 个 TransformerBlock。
在这里插入图片描述

class C3TR(C3):
    # C3 module with TransformerBlock()
    def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5):
        super().__init__(c1, c2, n, shortcut, g, e)
        c_ = int(c2 * e)
        self.m = TransformerBlock(c_, c_, 4, n)# num_heads=4, num_layers=n

7.1.20 SPP

空间金字塔模型:三个MaxPool 并行连接,kernel size分别为5 * 59 * 913 * 13
《Spatial Pyramid Pooling in Deep Convolutional Networks for Visual Recognition》
在这里插入图片描述

class SPP(nn.Module):
    # Spatial Pyramid Pooling (SPP) layer https://arxiv.org/abs/1406.4729
    def __init__(self, c1, c2, k=(5, 9, 13)):
        super().__init__()
        c_ = c1 // 2  # hidden channels
        self.cv1 = Conv(c1, c_, 1, 1)
        self.cv2 = Conv(c_ * (len(k) + 1), c2, 1, 1)
        self.m = nn.ModuleList([nn.MaxPool2d(kernel_size=x, stride=1, padding=x // 2) for x in k])

    def forward(self, x):
        x = self.cv1(x)
        with warnings.catch_warnings():
            warnings.simplefilter('ignore')  # suppress torch 1.9.0 max_pool2d() warning
            return self.cv2(torch.cat([x] + [m(x) for m in self.m], 1))

7.1.21 SPPF

在这里插入图片描述
在这里插入图片描述
池化尺寸等价于SPP中kernel size分别为5 * 59 * 913 * 13的池化层并行连接。

class SPPF(nn.Module):
    # Spatial Pyramid Pooling - Fast (SPPF) layer for YOLOv5 by Glenn Jocher
    def __init__(self, c1, c2, k=5):  # equivalent to SPP(k=(5, 9, 13))
        super().__init__()
        c_ = c1 // 2  # hidden channels
        self.cv1 = Conv(c1, c_, 1, 1)
        self.cv2 = Conv(c_ * 4, c2, 1, 1)
        self.m = nn.MaxPool2d(kernel_size=k, stride=1, padding=k // 2)

    def forward(self, x):
        x = self.cv1(x)
        with warnings.catch_warnings():
            warnings.simplefilter('ignore')  # suppress torch 1.9.0 max_pool2d() warning
            y1 = self.m(x)
            y2 = self.m(y1)
            return self.cv2(torch.cat((x, y1, y2, self.m(y2)), 1))

7.1.22 Focus

Focus模块在v5中是图片进入backbone前,对图片进行切片操作,具体操作是在一张图片中每隔一个像素拿到一个值,类似于邻近下采样,这样就拿到了四张图片,四张图片互补,长的差不多,但是没有信息丢失,这样一来,将W、H信息就集中到了通道空间,输入通道扩充了4倍,即拼接起来的图片相对于原先的RGB三通道模式变成了12个通道,最后将得到的新图片再经过卷积操作,最终得到了没有信息丢失情况下的二倍下采样特征图。
例如: 原始的640 × 640 × 3的图像输入Focus结构,采用切片操作,先变成320 × 320 × 12的特征图,再经过一次卷积操作,最终变成320 × 320 × 32的特征图。切片操作如下:
在这里插入图片描述
在这里插入图片描述

class Focus(nn.Module):
    # Focus wh information into c-space
    def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True):  # ch_in, ch_out, kernel, stride, padding, groups
        super().__init__()
        self.conv = Conv(c1 * 4, c2, k, s, p, g, act=act)
        # self.contract = Contract(gain=2)

    def forward(self, x):  # x(b,c,w,h) -> y(b,4c,w/2,h/2)
        return self.conv(torch.cat((x[..., ::2, ::2], x[..., 1::2, ::2], x[..., ::2, 1::2], x[..., 1::2, 1::2]), 1))
        # return self.conv(self.contract(x))

7.1.23 GhostConv

Ghost卷积来自华为诺亚方舟实验室,《GhostNet: More Features from Cheap Operations》发表于2020年的CVPR上。提供了一个全新的Ghost模块,旨在通过廉价操作生成更多的特征图。
在这里插入图片描述
在这里插入图片描述

class GhostConv(nn.Module):
    # Ghost Convolution https://github.com/huawei-noah/ghostnet
    def __init__(self, c1, c2, k=1, s=1, g=1, act=True):  # ch_in, ch_out, kernel, stride, groups
        super().__init__()
        c_ = c2 // 2  # hidden channels
        self.cv1 = Conv(c1, c_, k, s, None, g, act=act)
        self.cv2 = Conv(c_, c_, 5, 1, None, c_, act=act)  # 分组数=c_=通道数,进行point-wise的深度分离卷积

    def forward(self, x):
        y = self.cv1(x)
        return torch.cat((y, self.cv2(y)), 1)

7.1.24 GhostBottleneck

在这里插入图片描述

class GhostBottleneck(nn.Module):
    # Ghost Bottleneck https://github.com/huawei-noah/ghostnet
    def __init__(self, c1, c2, k=3, s=1):  # ch_in, ch_out, kernel, stride
        super().__init__()
        c_ = c2 // 2
        self.conv = nn.Sequential(
            GhostConv(c1, c_, 1, 1),  # 卷积核的大小是1*1,属于point-wise的深度可分离卷积
            DWConv(c_, c_, k, s, act=False) if s == 2 else nn.Identity(),  # 输入通道数和输出通道数相等,属于depth-wise的深度可分离卷积
            GhostConv(c_, c2, 1, 1, act=False))  #point-wise的深度可分离卷积,且不采用偏置项。
        self.shortcut = nn.Sequential(DWConv(c1, c1, k, s, act=False), Conv(c1, c2, 1, 1,
                                                                            act=False)) if s == 2 else nn.Identity()

    def forward(self, x):
        return self.conv(x) + self.shortcut(x)

7.1.25 C3Ghost

C3Ghost继承自C3, Bottleneck更换为GhostBottleneck
在这里插入图片描述

class C3Ghost(C3):
    # C3 module with GhostBottleneck()
    def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5):
        super().__init__(c1, c2, n, shortcut, g, e)
        c_ = int(c2 * e)  # hidden channels
        self.m = nn.Sequential(*(GhostBottleneck(c_, c_) for _ in range(n)))

7.1.26 Concat

当dimension=1时,将多张相同尺寸的图像在通道维度维度上进行拼接。

class Concat(nn.Module):
    # Concatenate a list of tensors along dimension
    def __init__(self, dimension=1):
        super().__init__()
        self.d = dimension

    def forward(self, x):
        return torch.cat(x, self.d)

8.Yolov8实操

新建虚拟环境

# 新建名为pytorch的虚拟环境,python=3.8
conda create -n pytorch python=3.8 -y
# 查看当前存在的虚拟环境
conda env list
# 进入pytorch
conda activate pytorch

安装pytorch和torchvision

pip install torch-1.9.0+cu102-cp38-cp38-linux_x86_64.whl
pip install torchvision-0.10.0+cu102-cp38-cp38-linux_x86_64.whl

8.1 下载工程并安装ultralytics

git clone https://github.com/ultralytics/ultralytics
cd ultralytics
pip install -e .

8.2 数据集准备

数据集制作参考: YOLO格式数据集制作:https://blog.csdn.net/zyw2002/article/details/122995823

# 训练/验证/测试 数据
train: /data/zyw/project/dataset/finalTrafficLightDataset/train/images
val: /data/zyw/project/dataset/finalTrafficLightDataset/val/images
test: /data/zyw/project/dataset/finalTrafficLightDataset/WPIDataset/images
# 类别个数
nc: 12

# 类别名称
names: ["greenCircle", "yellowCircle", "redCircle", "greenLeft", "yellowLeft", "redLeft", "greenRight", "yellowRight", "redRight", "greenForward", "yellowForward", "redForward"]

8.3 模型的训练/验证/预测/验证

8.3.1 使用CLI

在这里插入图片描述

  • 语法
yolo task=detect    mode=train    model=yolov8n.yaml      args...
          classify       predict        yolov8n-cls.yaml  args...
          segment        val            yolov8n-seg.yaml  args...
                         export         yolov8n.pt        format=onnx  args...

注意: 参数不需要’- -'前缀。这些是为后面介绍的特殊命令保留的

  • 训练示例
yolo task=detect mode=train model=yolov8n.pt data=coco128.yaml device=0
  • 多GPU训练示例
yolo task=detect mode=train model=yolov8n.pt data=coco128.yaml device=\'0,1,2,3\'
  • 重写默认的配置参数
# 语法
yolo task= ... mode= ...  arg=val
# 例子: 进行10个epoch的检测训练,learning_rate为0.01
yolo task=detect mode=train  epochs=10 lr0=0.01
  • 重写默认配置文件
# 可以在当前工作目录下创建一个默认配置文件的副本
yolo task=init
# 然后可以使用cfg=name.yaml命令来传递新的配置文件
yolo cfg=default.yaml

8.3.2 使用python

允许用户在Python项目中轻松使用YOLOv8。它提供了加载和运行模型以及处理模型输出的函数。该界面设计易于使用,以便用户可以在他们的项目中快速实现目标检测。

训练

  • 从预训练模型开始训练
from ultralytics import YOLO

model = YOLO("yolov8n.pt") # pass any model type
model.train(epochs=5)
  • 从头开始训练
from ultralytics import YOLO

model = YOLO("yolov8n.yaml")
model.train(data="coco128.yaml", epochs=5)

验证

  • 训练后验证
from ultralytics import YOLO

  model = YOLO("yolov8n.yaml")
  model.train(data="coco128.yaml", epochs=5)
  model.val()  # It'll automatically evaluate the data you trained.
  • 单独验证
from ultralytics import YOLO

  model = YOLO("model.pt")
  # 如果不设置数据的话,就使用model.pt中的data yaml文件
  model.val()
  # 或者直接设置需要验证的数据。
  model.val(data="coco128.yaml")

预测

  • 从源文件中预测
from ultralytics import YOLO

model = YOLO("model.pt")
model.predict(source="0") # accepts all formats - img/folder/vid.*(mp4/format). 0 for webcam
model.predict(source="folder", show=True) # Display preds. Accepts all yolo predict arguments
  • 返回结果
from ultralytics import YOLO

model = YOLO("model.pt")
outputs = model.predict(source="0", return_outputs=True) # treat predict as a Python generator
for output in outputs:
  # each output here is a dict.
  # for detection
  print(output["det"])  # np.ndarray, (N, 6), xyxy, score, cls
  # for segmentation
  print(output["det"])  # np.ndarray, (N, 6), xyxy, score, cls
  print(output["segment"])  # List[np.ndarray] * N, bounding coordinates of masks
  # for classify
  print(output["prob"]) # np.ndarray, (num_class, ), cls prob

使用训练器

YOLO模型类是Trainer类的高级包装器。每个YOLO任务都有自己的从BaseTrainer继承来的训练器。

from ultralytics.yolo import v8 import DetectionTrainer, DetectionValidator, DetectionPredictor

# trainer
trainer = DetectionTrainer(overrides={})
trainer.train()
trained_model = trainer.best

# Validator
val = DetectionValidator(args=...)
val(model=trained_model)

# predictor
pred = DetectionPredictor(overrides={})
pred(source=SOURCE, model=trained_model)

# resume from last weight
overrides["resume"] = trainer.last
trainer = detect.DetectionTrainer(overrides=overrides)

多任务支持
yolov8支持检测、分割、分类 三种任务。
在这里插入图片描述
检测

物体检测是一项涉及识别图像或视频流中物体的位置和类别的任务。

对象检测器的输出是一组包围图像中的对象的包围框,以及每个框的类标签和置信度分数。当你需要识别场景中感兴趣的物体,但不需要知道物体的确切位置或它的确切形状时,物体检测是一个很好的选择。

YOLOv8检测模型没有后缀,是默认的YOLOv8模型,即yolov8n.pt,并在COCO上进行预训练。

训练

在COCO128数据集上训练YOLOv8n 100个epoch,图像大小为640。

  • python
from ultralytics import YOLO

# Load a model
model = YOLO("yolov8n.yaml")  # build a new model from scratch
model = YOLO("yolov8n.pt")  # load a pretrained model (recommended for training)

# Train the model
results = model.train(data="coco128.yaml", epochs=100, imgsz=640)
  • CLI
yolo task=detect mode=train data=coco128.yaml model=yolov8n.pt epochs=100 imgsz=640

验证

验证训练YOLOv8n模型在COCO128数据集上的准确性。不需要传递参数,因为模型保留了它的训练数据和参数作为模型属性。

  • python
from ultralytics import YOLO

# Load a model
model = YOLO("yolov8n.pt")  # load an official model
model = YOLO("path/to/best.pt")  # load a custom model

# Validate the model
results = model.val()  # no arguments needed, dataset and settings remembered
  • CLI
yolo task=detect mode=val model=yolov8n.pt  # val official model
yolo task=detect mode=val model=path/to/best.pt  # val custom model

预测

使用经过训练的YOLOv8n模型对图像进行预测。

  • python
from ultralytics import YOLO

# Load a model
model = YOLO("yolov8n.pt")  # load an official model
model = YOLO("path/to/best.pt")  # load a custom model

# Predict with the model
results = model("https://ultralytics.com/images/bus.jpg")  # predict on an image

  • CLI
yolo task=detect mode=predict model=yolov8n.pt source="https://ultralytics.com/images/bus.jpg"  # predict with official model
yolo task=detect mode=predict model=path/to/best.pt source="https://ultralytics.com/images/bus.jpg"  # predict with custom model

导出

导出YOLOv8n模型到不同的格式,如ONNX, CoreML等。

  • python
from ultralytics import YOLO

# Load a model
model = YOLO("yolov8n.pt")  # load an official model
model = YOLO("path/to/best.pt")  # load a custom trained

# Export the model
model.export(format="onnx")

  • CLI
yolo mode=export model=yolov8n.pt format=onnx  # export official model
yolo mode=export model=path/to/best.pt format=onnx  # export custom trained model

可用的YOLOv8导出格式包括:
在这里插入图片描述
实例分割

实例分割比对象检测更进一步,涉及识别图像中的单个对象,并将它们从图像的其余部分分割出来

实例分割模型的输出是一组掩码或轮廓,它们勾勒出图像中的每个对象,以及每个对象的类标签和置信度分数。当你不仅需要知道物体在图像中的位置,还需要知道它们的确切形状时,实例分割非常有用。

YOLOv8分割模型使用-seg后缀,即yolov8n- seg .pt,并在COCO上进行预训练。

训练

在COCO128-seg数据集上训练YOLOv8n-seg 100个epoch,图像大小为640。

  • python
from ultralytics import YOLO

# Load a model
model = YOLO("yolov8n-seg.yaml")  # build a new model from scratch
model = YOLO("yolov8n-seg.pt")  # load a pretrained model (recommended for training)

# Train the model
results = model.train(data="coco128-seg.yaml", epochs=100, imgsz=640)
  • CLI
yolo task=segment mode=train data=coco128-seg.yaml model=yolov8n-seg.pt epochs=100 imgsz=640

验证

在COCO128-seg数据集上验证训练过的YOLOv8n-seg模型的准确性。不需要传递参数,因为模型保留了它的训练数据和参数作为模型属性。

  • python
from ultralytics import YOLO

# Load a model
model = YOLO("yolov8n-seg.pt")  # load an official model
model = YOLO("path/to/best.pt")  # load a custom model

# Validate the model
results = model.val()  # no arguments needed, dataset and settings remembered

  • CLI
yolo task=segment mode=val model=yolov8n-seg.pt  # val official model
yolo task=segment mode=val model=path/to/best.pt  # val custom model

预测

使用训练过的YOLOv8n-seg模型对图像进行预测。

  • python
from ultralytics import YOLO

# Load a model
model = YOLO("yolov8n-seg.pt")  # load an official model
model = YOLO("path/to/best.pt")  # load a custom model

# Predict with the model
results = model("https://ultralytics.com/images/bus.jpg")  # predict on an image
  • CLI
yolo task=segment mode=predict model=yolov8n-seg.pt source="https://ultralytics.com/images/bus.jpg"  # predict with official model
yolo task=segment mode=predict model=path/to/best.pt source="https://ultralytics.com/images/bus.jpg"  # predict with custom model

导出

导出YOLOv8n-seg模型到不同的格式,如ONNX, CoreML等。

  • python
from ultralytics import YOLO

# Load a model
model = YOLO("yolov8n-seg.pt")  # load an official model
model = YOLO("path/to/best.pt")  # load a custom trained

# Export the model
model.export(format="onnx")
  • CLI
yolo mode=export model=yolov8n-seg.pt format=onnx  # export official model
yolo mode=export model=path/to/best.pt format=onnx  # export custom trained model

分类

图像分类是三个任务中最简单的,涉及到将整个图像分类到一组预定义的类中的一个。

图像分类器的输出是一个单一的类标签和一个置信度分数。当您只需要知道图像属于什么类,而不需要知道该类对象的位置或它们的确切形状时,图像分类是有用的。

YOLOv8分类模型使用-cls后缀,即yolov8n-cls.pt,并在ImageNet上进行预训练。其他的使用方法和检测与分割类似,不再赘述。

配置

YOLO设置和超参数在模型的性能、速度和准确性中起着至关重要的作用。这些设置和超参数可以在模型开发过程的各个阶段影响模型的行为,包括训练、验证和预测。

正确地设置和调优这些参数可以对模型有效地从训练数据中学习并推广到新数据的能力产生重大影响。例如,选择合适的学习率、批大小和优化算法会极大地影响模型的收敛速度和精度。同样,设置正确的置信度阈值和非最大抑制(NMS)阈值也会影响模型在检测任务上的性能。

设置操作类型
YOLO模型可用于各种任务,包括检测、分割和分类。这些任务的不同之处在于它们产生的输出类型和它们要解决的特定问题。
在这里插入图片描述
在这里插入图片描述
训练

YOLO模型的训练设置是指用于在数据集上训练模型的各种超参数和配置。这些设置会影响模型的性能、速度和精度。一些常见的YOLO训练设置包括批量大小、学习率、动量和权重衰减。其他可能影响训练过程的因素包括优化器的选择、损失函数的选择、训练数据集的大小和组成。重要的是要仔细调整和试验这些设置,以实现给定任务的最佳性能。
在这里插入图片描述
在这里插入图片描述
预测

YOLO模型的预测设置是指用于在新数据上使用模型进行预测的各种超参数和配置。这些设置会影响模型的性能、速度和精度。一些常见的YOLO预测设置包括置信度阈值、非最大抑制(NMS)阈值和要考虑的类别数量。其他可能影响预测过程的因素包括输入数据的大小和格式,是否存在额外的特征(如掩码或每个框的多个标签),以及模型正在用于的特定任务。重要的是要仔细调整和试验这些设置,以实现给定任务的最佳性能。
在这里插入图片描述
验证

YOLO模型的验证设置是指用于评估模型在验证数据集上性能的各种超参数和配置。这些设置会影响模型的性能、速度和精度。一些常见的YOLO验证设置包括批量大小、训练期间执行验证的频率以及用于评估模型性能的指标。其他可能影响验证过程的因素包括验证数据集的大小和组成,以及模型正在用于的特定任务。重要的是要仔细调整和实验这些设置,以确保模型在验证数据集上表现良好,并检测和防止过拟合。

在这里插入图片描述

8.4 数据扩充

YOLO模型的增强设置是指应用于训练数据的各种变换和修改,以增加数据集的多样性和大小。这些设置会影响模型的性能、速度和精度。一些常见的YOLO增强设置包括应用的转换类型和强度(例如随机翻转、旋转、裁剪、颜色变化),应用每个转换的概率,以及是否存在其他功能,如掩码或每个框多个标签。其他可能影响数据扩充过程的因素包括原始数据集的大小和组成,以及模型正在用于的特定任务。重要的是要仔细调整和实验这些设置,以确保增强后的数据集具有足够的多样性和代表性,以训练高性能的模型。
在这里插入图片描述

8.5 日志、检查点、绘图与文件管理

在训练YOLO模型时,日志记录、检查点、绘图和文件管理是重要的考虑因素。

  • 日志记录:在训练期间记录各种指标和统计数据通常有助于跟踪模型的进展和诊断任何可能出现的问题。这可以通过使用日志库(如TensorBoard)或将日志消息写入文件来实现。
  • 检查点:在训练期间,定期保存模型的检查点是一个很好的做法。如果训练过程被中断,或者你想尝试不同的训练配置,这允许你从之前的点恢复训练。绘图:可视化模型的性能和训练过程,有助于理解模型的行为方式和识别潜在问题。这可以使用matplotlib等绘图库完成,也可以使用TensorBoard等日志库来绘图。
  • 文件管理:管理训练过程中生成的各种文件,例如模型检查点、日志文件和绘图,可能具有挑战性。有一个清晰和有组织的文件结构是很重要的,以便跟踪这些文件,并使其易于根据需要访问和分析它们。

有效的日志记录、检查点、绘图和文件管理可以帮助您跟踪模型的进度,并使其更容易调试和优化训练过程。
在这里插入图片描述

自定义模型

Ultralytics YOLO命令行和python接口都只是基本引擎执行器的高级抽象。让我们来看看Trainer引擎。

BaseTrainer

BaseTrainer包含通用的样板训练例程。只要遵循正确的格式,它可以针对任何基于覆盖所需功能或操作的任务进行定制。例如,你可以通过覆盖这些函数来支持你自己的自定义模型和dataloder:

  • get_model(cfg, weights) - 用来创建模型的函数
  • get_dataloder() - 用来创建dataloader的函数

DetectionTrainer

以下是如何使用YOLOv8 DetectionTrainer并自定义它。

from ultralytics.yolo.v8.detect import DetectionTrainer

trainer = DetectionTrainer(overrides={...})
trainer.train()
trained_model = trainer.best  # get best model

自定义Detection Trainer

from ultralytics.yolo.v8.detect import DetectionTrainer

class CustomTrainer(DetectionTrainer):
    def get_model(self, cfg, weights):
        ...

    def criterion(self, preds, batch):
        # get ground truth
        imgs = batch["imgs"]
        bboxes = batch["bboxes"]
        ...
        return loss, loss_items  # see Reference-> Trainer for details on the expected format

# callback to upload model weights
def log_model(trainer):
    last_weight_path = trainer.last
    ...

trainer = CustomTrainer(overrides={...})
trainer.add_callback("on_train_epoch_end", log_model)  # Adds to existing callback
trainer.train()

参考文献

参考链接1:https://blog.csdn.net/zyw2002/article/details/128732494
参考链接2:https://mp.weixin.qq.com/s/vXIx7dBRxgxnvh5BoIRQZw
详细解读YOLOv8的改进模块: https://jishuin.proginn.com/p/763bfbd804d9
mmyolo: https://mmyolo.readthedocs.io/zh_CN/latest/algorithm_descriptions/yolov8_description.html

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

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

相关文章

软件设计模式之原型模式

一.定义 原型模式(Prototype Pattern)的简单程度仅次于单例模式和迭代器模式。正是由于简单,使用的场景才非常地多,其定义如下: Specify the kinds of objects to create using a prototypical instance, and create new objects by copyingthis protot…

基础设施SIG月度动态:ABS新增ISO、VHD镜像构建,自动热补丁制作流程正式上线

基础设施 SIG(OpenAnolis Infra SIG)目标:负责 OpenAnolis 社区基础设施工程平台的建设,包括官网、Bugzilla、Maillist、ABS、ANAS、CI 门禁以及社区 DevOps 相关的研发工程系统。 01 SIG 整体进展 1、龙蜥社区官网与 CSDN dev…

smigroup LAFERT 伺服电机 B7108P-03177

​ smigroup LAFERT 伺服电机 B7108P-03177 smigroup LAFERT 伺服电机 B7108P-03177 从系统的可扩展性和兼容性的方面来说: 市场上控制类产品繁多,无论DCS还是PLC,均有很多厂商在生产和销售。对于PLC系统来说,一般没有或很少有扩…

如何与领导团队沟通并赢得他们的支持以推动企业的敏捷转型进程?

在与领导团队沟通并赢得他们对敏捷转型的支持时,重要的是要从战略上有效地处理这种情况。建议采取的一些步骤: 了解他们的观点:在开始任何沟通之前,尝试了解领导团队对敏捷转型的担忧和保留意见。可以帮助调整方法并解决具体问题。…

2023年5月信息系统项目管理师试题及答案解析

请点击↑关注、收藏,本博客免费为你获取精彩知识分享!有惊喜哟!! 1.“新型基础设施”主要包括信息技术设施,融合基础设施和创新基础设施三个方面,其中信息基础设施包括___1___。 ①通信基础设施 ②智能交…

【Mysql 存储过程 Or 存储函数 傻傻分不清? 】

MySQL的存储函数(自定义函数)和存储过程都是用于存储SQL语句的。但是什么时候用什么呢?是不是总是傻傻的分不清? 本文来详细的讲一下存储函数 和存储过程 ,以后再也不会迷糊。 存储函数 | 存储过程 一、 异同点二、 存…

云原生之深入解析Kubernetes中Kubectl Top如何进行资源监控

一、Kubectl top 的使用 kubectl top 是基础命令,但是需要部署配套的组件才能获取到监控值: 1.8 以下:部署 heapter; 1.8 以上:部署 metric-server; kubectl top node:查看 node 的使用情况&a…

25.单元测试、反射

一.单元测试 1.1 什么是单元测试 单元测试就是针对最小的功能单元编写测试代码,Java程序最小的功能单元是方法。因此,单元测试就是针对Java方法进行的测试,进而检查 方法的正确性。 1.2 目前测试方法是怎么进行的 ①只有一个main方法&#x…

可观测性最佳实践 | 警惕!未知的风险正在摧毁你的系统

无声的刺客最为致命,往往表面看似云淡风轻,实际早已危机重重,血雨腥风一触即发。这样的场面看似离我们很遥远,但每个开发运维人员实际都遇到过。 在全球数字经济大潮下,现代企业纷纷投身于业务数字化转型的浪潮。越来越…

java8 (jdk 1.8) 新特性—— 方法引用+构造器引用

1. 方法引用 方法引用 其实 本质上 就是Lambda 表达式 ,之前已经知道 Lambda 是作为一个函数式接口的实例 ,因此,方法引用也就是一个函数式接口的实例 使用的情况: 当要将值传给Lambda 体,已经有实现的方法&#xff…

【Python 随练】判断101到200的素数

题目: 判断 101-200 之间有多少个素数,并输出所有素数。 简介: 在本篇博客中,我们将解决一个常见的问题:判断101到200之间的素数,并输出所有素数的列表。我们将给出问题的解析,并提供一个完整…

2023年了要怎么理解原型和原型链

1.prototype和__proto__有什么不可告人的关系? 说道原型对象,那一定绕不开几个概念: prototype,__proto__,constructor。 首先解释一下prototype是什么,原型对象。V8引擎给所有函数内置的一个对象。也就是只要创建的…

瑞丽-伯纳德对流的拉格朗日拟序结构(FTLE场结果对比)

FTLE场,即有限李雅普诺夫指数场是识别拉格朗日拟序结构的一种方法,其主要思路是如下: 1、t时刻在场内均匀布置粒子 2、计算t–>tT时刻后粒子的位置,这里粒子任意时刻的速度有速度场插值得到 3、根据两个时刻的粒子位置计算得…

资讯 |智汇云舟受邀出席WGDC大会并获“年度最具创新力企业“奖项

5月17日,由泰伯网主办的第十二届WGDC2023全球地理信息开发者大会在北京隆重召开。开幕式上重磅发布了“2023年度最具创新力”榜单,智汇云舟凭借技术创新研发与应用方面取得的诸多突破,获数字孪生赛道“年度最具创新力企业"奖项。公司产品…

Django高级扩展之Paginator分页实现

在项目中很常见的一种功能,主要用于大量数据在一页显示过于冗长,分为一页显示多条数据,分隔为多页显示;并常与条件筛选结合使用。 目录 Paginator对象 创建对象 属性 方法 异常 Page对象 创建对象 属性 方法 Paginator与…

华为OD机试之阿里巴巴找黄金宝箱(I)

阿里巴巴找黄金宝箱(I) 题目描述 一贫如洗的樵夫阿里巴巴在去砍柴的路上,无意中发现了强盗集团的藏宝地,藏宝地有编号从0~N的箱子,每个箱子上面贴有一个数字,箱子中可能有一个黄金宝箱。 黄金宝箱满足排在它之前的所有箱子数字…

Kubernetes Pod 生命周期

Pod 生命周期是其从创建开始至终止退出的事件范围。 Pod 状态 Pending API server 创建了 Pod 对象并已存入 etcd 中,但尚未调度完成或仍处于从镜像仓库下载镜像的过程中。Running Pod 已经被调度至某节点,并且所有容器都已经被 kubelet 创建完成。Suc…

spss分析方法-聚类分析

聚类分析是根据研究对象的特征,按照一定标准对研究对象进行分类的一种分析方法。下面我们主要从下面四个方面来解说: 实际应用理论思想建立模型 分析结果 一、实际应用 聚类分析的目标就是在相似的基础上收集数据来分类。 聚类源于很多领域&#xf…

安装虚幻引擎

1、下载和安装 Epic Games启动程序。 下载地址:https://www.unrealengine.com/zh-CN/download 进入网站你可以看到下载信息和推荐系统要求等: 点击“下载启动程序”即可下载,或者直接点击下载链接:https://epicgames-download1.ak…

37岁985硕士,投上千份简历无人问津......该何去何从

前言 简单自我介绍一下,我老刘,37岁失业的985硕士 ,13年其实可以直接入编的我,其实都不需要考试了,可以直接入职,但是我并没有选择去,后面直接选择了新闻周刊干了四年,后面换了一家…