YOLOv5模型学习记录

news2024/11/25 12:29:20

新年伊始,YOLOv8横空出世,这个还未开源时便引发界内广泛热议的目标检测算法,一经问世便再次引发热潮,而作为与其师出同源的YOLOv5,自然要拿来与其比较一番。接下来我们便来学习一下吧。

模型结构

首先便是模型结构了,其实在博主看来模型结构再复杂,一切都是有迹可循的,毕竟万变不离其宗,其就是有多个网络层组合得到的,了解每个层的构成,每个层的参数联系也就可以将这个模型结构搞清楚了。

YOLOv5 网络结构是标准的 CSPDarknet + PAFPN + 非解耦 Head。

YOLOv5 网络结构大小由 deepen_factorwiden_factor 两个参数决定。其中 deepen_factor 控制网络结构深度,即 CSPLayer 中 DarknetBottleneck 模块堆叠的数量;widen_factor 控制网络结构宽度,即模块输出特征图的通道数。以 YOLOv5-l 为例,其 deepen_factor = widen_factor = 1.0 。P5模型如下图所示:

在这里插入图片描述
根据上图,我们来分析具体模型结构。

Backbone模块

在 MMYOLO 中 CSPDarknet 继承自 BaseBackbone,整体结构和 ResNet 类似。P5 模型共 5 层结构,包含 1 个 Stem Layer 和 4 个 Stage Layer:

  1. Stem Layer 是 1 个 6x6 kernel 的 ConvModule,相较于 v6.1 版本之前的 Focus 模块更加高效。
  2. 除了最后一个 Stage Layer,其他均由 1 个 ConvModule 和 1 个 CSPLayer 组成。如上图 Details部分所示。 其中 ConvModule 为 3x3的 Conv2d + BatchNorm + SiLU 激活函数。CSPLayer 即YOLOv5 官方仓库中的 C3 模块,由 3 个 ConvModule + n DarknetBottleneck(带残差连接)组成。
  3. 最后一个 Stage Layer 在最后增加了 SPPF 模块。SPPF 模块是将输入串行通过多个 5x5 大小的 MaxPool2d层,与 SPP 模块效果相同,但速度更快。
  4. P5 模型会在 Stage Layer 2-4 之后分别输出一个特征图进入 Neck 结构。以 640x640 输入图片为例,其输出特征为(B,256,80,80)、(B,512,40,40) 和 (B,1024,20,20),对应的 stride 分别为 8/16/32。
  5. P6 模型会在 Stage Layer 2-5 之后分别输出一个特征图进入 Neck 结构。P6模型图如下所示:以 1280x1280 输入图片为例,其输出特征为 (B,256,160,160)、(B,512,80,80)、(B,768,40,40) 和 (B,1024,20,20),对应的 stride 分别为 8/16/32/64。

Neck模块

YOLOv5 官方仓库的配置文件中并没有 Neck 部分,为方便用户与其他目标检测网络结构相对应,我们将官方仓库的 Head 拆分成 PAFPN 和 Head 两部分。

基于 BaseYOLONeck 结构,YOLOv5 Neck 也是遵循同一套构建流程,对于不存在的模块,我们采用 nn.Identity 代替。

Neck 模块输出的特征图和 Backbone 完全一致。即 P5 模型为 (B,256,80,80)、 (B,512,40,40) 和 (B,1024,20,20);P6 模型为 (B,256,160,160)、(B,512,80,80)、(B,768,40,40) 和 (B,1024,20,20)。

Head模块

YOLOv5 Head 结构和 YOLOv3 完全一样,为 非解耦 Head。Head 模块只包括 3 个不共享权重的卷积,用于将输入特征图进行变换而已。

前面的 PAFPN 依然是输出 3 个不同尺度的特征图,shape 为 (B,256,80,80)、 (B,512,40,40) 和 (B,1024,20,20)。 由于 YOLOv5 是非解耦输出,即分类和 bbox 检测等都是在同一个卷积的不同通道中完成。以 COCO 80 类为例:

P5 模型在输入为 640x640 分辨率情况下,其 Head 模块输出的 shape 分别为 (B, 3x(4+1+80),80,80), (B, 3x(4+1+80),40,40) 和 (B, 3x(4+1+80),20,20)。

P6 模型在输入为 1280x1280 分辨率情况下,其 Head 模块输出的 shape 分别为 (B, 3x(4+1+80),160,160), (B, 3x(4+1+80),80,80), (B, 3x(4+1+80),40,40) 和 (B, 3x(4+1+80),20,20)。 其中 3 表示 3 个 anchor,4 表示 bbox 预测分支,1 表示 obj 预测分支,80 表示 COCO 数据集类别预测分支。

通常来说,目标检测算法都可以分成模型结构、数据增强、loss 计算等组件。YOLOv5自然也不例外。

在这里插入图片描述

数据增强模块

该模块作用于Dataloader部分,本应在模型结构(backbone+neck+head)之前讲述的,倒也无伤大雅。
YOLOv5 目标检测算法中使用的数据增强比较多,包括:

Mosaic 马赛克
RandomAffine 随机仿射变换
MixUp 图像模糊等采用
Albu 库实现的变换
HSV 颜色空间增强
随机水平翻转

其中 Mosaic 数据增强概率为 1,表示一定会触发,而对于 small 和 nano 两个版本的模型不使用 MixUp,其他的 l/m/x 系列模型则采用了 0.1 的概率触发 MixUp。小模型能力有限,一般不会采用 MixUp 等强数据增强策略。

其核心的 Mosaic + RandomAffine + MixUp 过程简要绘制如下:

在这里插入图片描述

下面对以上技术进行简要介绍:

Mosaic 马赛克

在这里插入图片描述
Mosaic 属于混合类数据增强,因为它在运行时候需要 4 张图片拼接,变相的相当于增加了训练的 batch size。其运行过程简要概况为:

  1. 随机生成拼接后 4 张图的交接中心点坐标,此时就相当于确定了 4 张拼接图片的交接点
  2. 随机选出另外 3 张图片的索引以及读取对应的标注
  3. 对每张图片采用保持宽高比的 resize 操作将其缩放到指定大小
  4. 按照上下左右规则,计算每张图片在待输出图片中应该放置的位置,因为图片可能出界故还需要计算裁剪坐标
  5. 利用裁剪坐标将缩放后的图片裁剪,然后贴到前面计算出的位置,其余位置全部补 114 像素值
  6. 对每张图片的标注也进行相应处理

注意:由于拼接了 4 张图,所以输出图片面积会扩大 4 倍,从 640x640 变成 1280x1280,因此要想恢复为 640x640, 必须要再接一个 RandomAffine 随机仿射变换,否则图片面积就一直是扩大 4 倍的。

RandomAffine 随机仿射变换

在这里插入图片描述
随机仿射变换有两个目的:

  1. 对图片进行随机几何仿射变换
  2. 将 Mosaic 输出的扩大 4 倍的图片还原为 640x640 尺寸

随机仿射变换包括平移、旋转、缩放、错切等几何增强操作,同时由于 Mosaic 和 RandomAffine 属于比较强的增强操作,会引入较大噪声,因此需要对增强后的标注进行处理,过滤规则为:

  1. 增强后的 gt bbox 宽高要大于 wh_thr
  2. 增强后的 gt bbox 面积和增强前的 gt bbox 面积比要大于 ar_thr,防止增强太严重
  3. 最大宽高比要小于 area_thr,防止宽高比改变太多

由于旋转后标注框会变大导致不准确,因此目标检测里面很少会使用旋转数据增强。

MixUp

MixUp 和 Mosaic 类似也属于混合图片类增强方法。随机选出另外一张图后将两图再随机混合。具体实现方法有多种,常见的做法是要么将 label 直接拼接起来,要么将 label 也采用 alpha 方法混合。原作者的做法非常简单,对 label 即直接拼接,而图片通过分布采样混合。

需要特别注意的是: YOLOv5 实现的 MixUp 中,随机出来的另一张图也需要经过 Mosaic 马赛克 + RandomAffine 随机仿射变换 的增强后才能混合。这个和其他开源库实现可能不太一样。

图像模糊和其他数据增强策略

剩下的数据增强包括

  1. 图像模糊等采用 Albu 库实现的变换
  2. HSV 颜色空间增强
  3. 随机水平翻转

MMDetection 开源库中已经对 Albu 第三方数据增强库进行了封装,使用户可以简单的通过配置即可使用 Albu 库中提供的任何数据增强功能。而 HSV 颜色空间增强和随机水平翻转都是属于比较常规的数据增强,大家可自行查找。

正负样本匹配策略

正负样本匹配策略的核心是确定预测特征图的所有位置中哪些位置应该是正样本,哪些是负样本,甚至有些是忽略样本。 匹配策略是目标检测算法的核心,一个好的匹配策略可以显著提升算法性能。
YOLOV5 的匹配策略简单总结为:采用了 anchor 和 gt_bbox 的 shape 匹配度作为划分规则,同时引入跨邻域网格策略来增加正样本。 其主要包括如下两个核心步骤:

对于任何一个输出层,抛弃了常用的基于 Max IoU 匹配的规则,而是直接采用 shape 规则匹配,也就是该 GT Bbox 和当前层的 Anchor 计算宽高比,如果宽高比例大于设定阈值,则说明该 GT Bbox 和 Anchor 匹配度不够,将该 GT Bbox 暂时丢掉,在该层预测中该 GT Bbox 对应的网格内的预测位置认为是负样本。

对于剩下的 GT Bbox(也就是匹配上的 GT Bbox),计算其落在哪个网格内,同时利用四舍五入规则,找出最近的两个网格,将这三个网格都认为是负责预测该 GT Bbox 的,可以粗略估计正样本数相比之前的 YOLO 系列,至少增加了三倍。

下面会对每个部分进行详细说明,部分描述和图示直接或间接参考自官方 Repo。

Anchor 设置

YOLOv5 是 Anchor-based 的目标检测算法,其 Anchor size 的获取方式与 YOLOv3 类似,也是使用聚类获得,其不同之处在于聚类使用的标准不再是基于 IoU 的,而是使用形状上的宽高比作为聚类准则(即 shape-match )。

在用户更换了数据集后,可以使用 MMYOLO 里带有的 Anchor 分析工具,对自己的数据集进行分析,确定合适的 Anchor size。

python tools/analysis_tools/optimize_anchors.py ${CONFIG} --algorithm v5-k-means
 --input-shape ${INPUT_SHAPE [WIDTH HEIGHT]} --output-dir ${OUTPUT_DIR}

然后在 config 文件 里修改默认 Anchor size:

anchors = [[(10, 13), (16, 30), (33, 23)], [(30, 61), (62, 45), (59, 119)],
           [(116, 90), (156, 198), (373, 326)]]

Bbox 编解码过程

在 Anchor-based 算法中,预测框通常会基于 Anchor 进行变换,然后预测变换量,这对应 GT Bbox 编码过程,而在预测后需要进行 Pred Bbox 解码,还原为真实尺度的 Bbox,这对应 Pred Bbox 解码过程。

在 YOLOv3 中,回归公式为:
在这里插入图片描述
公式中
在这里插入图片描述

而在 YOLOv5 中,回归公式为:
在这里插入图片描述
改进之处主要有以下两点:

中心点坐标范围从 (0, 1) 调整至 (-0.5, 1.5)

宽高范围从(0,正无穷)调整至(0.4awh
这个改进具有以下好处:

新的中心点设置能更好的预测到 0 和 1。这有助于更精准回归出 box 坐标。

在这里插入图片描述

宽高回归公式中 exp(x) 是无界的,这会导致梯度失去控制,造成训练不稳定。YOLOv5 中改进后的宽高回归公式优化了此问题。
在这里插入图片描述

匹配策略

在 MMYOLO 设计中,无论网络是 Anchor-based 还是 Anchor-free,我们统一使用 prior 称呼 Anchor。

正样本匹配包含以下两步:
“比例”比较
将 GT Bbox 的 WH 与 Prior 的 WH 进行“比例”比较。
比较流程:

在这里插入图片描述
此处我们用一个 GT Bbox 与 P3 特征图的 Prior(产生3个Anchor)进行匹配的案例进行讲解和图示:

在这里插入图片描述
依然沿用上面的例子:

GT Bbox (cx, cy, w, h) 值为 (26, 37, 36, 24),

Prior WH 值为 [(15, 5), (24, 16), (16, 24)],在 P3 特征图上,stride 为 8。通过计算,prior2 和 prior3 能够 match。

计算过程如下:

将 GT Bbox 的中心点坐标对应到 P3 的 grid 上
在这里插入图片描述
在这里插入图片描述

将 GT Bbox 中心点所在的 grid 分成四个象限,由于中心点落在了左下角的象限当中,那么会将物体的左、下两个 grid 也认为是正样本

在这里插入图片描述

下图展示中心点落到不同位置时的正样本分配情况:
在这里插入图片描述

那么 YOLOv5 的 Assign 方式具体带来了哪些改进?

  • 一个 GT Bbox 能够匹配多个 Prior
  • 一个 GT Bbox 和一个Prior 匹配时,能分配 1-3 个正样本

以上策略能适度缓解目标检测中常见的正负样本不均衡问题。

而 YOLOv5 中的回归方式,和 Assign 方式是相互呼应的:

中心点回归方式:

在这里插入图片描述

WH 回归方式:

在这里插入图片描述

Loss 设计

YOLOv5 中总共包含 3 个 Loss,分别为:

  1. Classes loss:使用的是 BCE loss
  2. Objectness loss:使用的是 BCE loss
  3. Location loss:使用的是 CIoU loss

三个 loss 按照一定比例汇总:
在这里插入图片描述
P3、P4、P5 层对应的 Objectness loss 按照不同权重进行相加,默认的设置是

obj_level_weights=[4., 1., 0.4]

在这里插入图片描述

优化策略和训练过程

YOLOv5 对每个优化器的参数组进行非常精细的控制,简单来说包括如下部分。

优化器分组

将优化参数分成 Conv/Bias/BN 三组,在 WarmUp 阶段,不同组采用不同的 lr 以及 momentum 更新曲线。 同时在 WarmUp 阶段采用的是 iter-based 更新策略,而在非 WarmUp 阶段则变成 epoch-based 更新策略,可谓是 trick 十足。

MMYOLO 中是采用 YOLOv5 OptimizerConstructor 优化器构造器实现优化器参数分组。优化器构造器的作用就是对一些特殊的参数组初始化过程进行精细化控制,因此可以很好的满足需求。

而不同的参数组采用不同的调度曲线功能则是通过 YOLOv5 ParamSchedulerHook 实现。

weight decay 参数自适应

针对不同的 batch size 采用了不同的 weight decay 策略,具体来说为:

  • 当训练 batch size <= 64 时,weight decay 不变
  • 当训练 batch size > 64 时,weight decay 会根据总 batch size 进行线性缩放

MMYOLO 也是通过 YOLOv5 OptimizerConstructor 实现。

梯度累加

为了最大化不同 batch size 情况下的性能,作者设置总 batch size 小于 64 时候会自动开启梯度累加功能。

训练过程和大部分 YOLO 类似,包括如下策略:

  • 没有使用预训练权重
  • 没有采用多尺度训练策略,同时可以开启 cudnn.benchmark 进一步加速训练
  • 使用了 EMA 策略平滑模型
  • 默认采用 AMP 自动混合精度训练

需要特意说明的是:YOLOv5 官方对于 small 模型是采用单卡 v100 训练,bs 为 128,而 m/l/x 等是采用不同数目的多卡实现的, 这种训练策略不太规范,为此在 MMYOLO 中全部采用了 8 卡,每卡 16 bs 的设置,同时为了避免性能差异,训练时候开启了 SyncBN。

推理和后处理过程

YOLOv5 后处理过程和 YOLOv3 非常类似,实际上 YOLO 系列的后处理逻辑都是类似的。

核心控制参数

multi_label

对于多类别预测来说需要考虑是否是多标签任务,也就是同一个预测位置会预测的多个类别概率,和是否当作单类处理。因为 YOLOv5 采用 sigmoid 预测模式,在考虑多标签情况下可能会出现一个物体检测出两个不同类别的框,这有助于评估指标 mAP,但是不利于实际应用。 因此在需要算评估指标时候 multi_label 是 True,而推理或者实际应用时候是 False

score_thr 和 nms_thr

score_thr 阈值用于过滤类别分值,低于分值的检测框当做背景处理,nms_thr 是 nms 时阈值。同样的,在计算评估指标 mAP 阶段可以将 score_thr 设置的非常低,这通常能够提高召回率,从而提升 mAP,但是对于实际应用来说没有意义,且会导致推理过程极慢。为此在测试和推理阶段会设置不同的阈值。

nms_pre 和 max_per_img

nms_pre 表示 nms 前的最大保留检测框数目,这通常是为了防止 nms 运行时候输入框过多导致速度过慢问题,默认值是 30000。 max_per_img 表示最终保留的最大检测框数目,通常设置为 300。

以 COCO 80 类为例,假设输入图片大小为 640x640
在这里插入图片描述
其推理和后处理过程为:

维度变换

YOLOv5 输出特征图尺度为 80x80、40x40 和 20x20 的三个特征图,每个位置共 3 个 anchor,因此输出特征图通道为 3x(5+80)=255。 YOLOv5 是非解耦输出头,而其他大部分算法都是解耦输出头,为了统一后处理逻辑,我们提前将其进行解耦,分成了类别预测分支、bbox 预测分支和 obj 预测分支。

将三个不同尺度的类别预测分支、bbox 预测分支和 obj 预测分支进行拼接,并进行维度变换。为了后续方便处理,会将原先的通道维度置换到最后,类别预测分支、bbox 预测分支和 obj 预测分支的 shape 分别为 (b, 3x80x80+3x40x40+3x20x20, 80)=(b,25200,80),(b,25200,4),(b,25200,1)。

解码还原到原图尺度

分类预测分支和 obj 分支需要进行 sigmoid 计算,而 bbox 预测分支需要进行解码,还原为真实的原图解码后 xyxy 格式

第一次阈值过滤

遍历 batch 中的每张图,然后用 score_thr 对类别预测分值进行阈值过滤,去掉低于 score_thr 的预测结果

第二次阈值过滤

将 obj 预测分值和过滤后的类别预测分值相乘,然后依然采用 score_thr 进行阈值过滤。 在这过程中还需要考虑 multi_label 和 nms_pre,确保过滤后的检测框数目不会多于nms_pre。

还原到原图尺度和 nms

基于前处理过程,将剩下的检测框还原到网络输出前的原图尺度,然后进行 nms 即可。最终输出的检测框不能多于 max_per_img。

batch shape 策略

为了加速验证集的推理过程,作者提出了 batch shape 策略,其核心原则是:确保在 batch 推理过程中同一个 batch 内的图片 pad 像素最少,不要求整个验证过程中所有 batch 的图片尺度一样。

其大概流程是:将整个测试或者验证数据的宽高比进行排序,然后依据 batch 设置将排序后的图片组成一个 batch, 同时计算这个 batch 内最佳的 batch shape,防止 pad 像素过多。最佳 batch shape 计算原则为在保持宽高比的情况下进行 pad,不追求正方形图片输出。

    image_shapes = []
    for data_info in data_list:
        image_shapes.append((data_info['width'], data_info['height']))

    image_shapes = np.array(image_shapes, dtype=np.float64)

    n = len(image_shapes)  # number of images
    batch_index = np.floor(np.arange(n) / self.batch_size).astype(
        np.int64)  # batch index
    number_of_batches = batch_index[-1] + 1  # number of batches

    aspect_ratio = image_shapes[:, 1] / image_shapes[:, 0]  # aspect ratio
    irect = aspect_ratio.argsort()

    data_list = [data_list[i] for i in irect]

    aspect_ratio = aspect_ratio[irect]
    # Set training image shapes
    shapes = [[1, 1]] * number_of_batches
    for i in range(number_of_batches):
        aspect_ratio_index = aspect_ratio[batch_index == i]
        min_index, max_index = aspect_ratio_index.min(
        ), aspect_ratio_index.max()
        if max_index < 1:
            shapes[i] = [max_index, 1]
        elif min_index > 1:
            shapes[i] = [1, 1 / min_index]

    batch_shapes = np.ceil(
        np.array(shapes) * self.img_size / self.size_divisor +
        self.pad).astype(np.int64) * self.size_divisor

    for i, data_info in enumerate(data_list):
        data_info['batch_shape'] = batch_shapes[batch_index[i]]

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

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

相关文章

Lambda原理及应用

Lambda原理及应用 Lambda介绍 Lambda 是 JDK8 以后版本推出的一个新特性&#xff0c;也是一个重要的版本更新&#xff0c;利用 Lambda 可以简化内部类&#xff0c;可以更方便的进行集合的运算&#xff0c;让你的代码看起来更加简洁,也能提升代码的运行效率。 Lambda语法 非…

优先级队列(堆)  堆排序

前面介绍过队列&#xff0c;队列是一种先进先出(FIFO)的数据结构&#xff0c;但有些情况下&#xff0c;操作的数据可能带有优先级&#xff0c;一般出队列时&#xff0c;可能需要优先级高的元素先出队列&#xff0c;该中场景下&#xff0c;使用队列显然不合适&#xff0c;比如&a…

【数据库】MySQL

时间戳 可以在创建表的时候&#xff0c;创建时间戳 mysql数据库怎么加入时间戳_帅气的黑桃J的博客-CSDN博客_mysql 插入时间戳 数据库表的命名规范 数据库表字段命名规范 - 腾讯云开发者社区-腾讯云 (tencent.com) MySql 的大小写问题 以下是MySQL详细的大小写区分规则&am…

《爆肝整理》保姆级系列教程python接口自动化(二十三)--unittest断言——上(详解)

简介 在测试用例中&#xff0c;执行完测试用例后&#xff0c;最后一步是判断测试结果是 pass 还是 fail&#xff0c;自动化测试脚本里面一般把这种生成测试结果的方法称为断言&#xff08;assert&#xff09;。用 unittest 组件测试用例的时候&#xff0c;断言的方法还是很多的…

Spring系列-9 Async注解使用与原理

背景&#xff1a; 本文作为Spring系列的第九篇&#xff0c;介绍Async注解的使用、注意事项和实现原理&#xff0c;原理部分会结合Spring框架代码进行。 本文可以和Spring系列-8 AOP原理进行比较阅读 1.使用方式 Async一般注解在方法上&#xff0c;用于实现方法的异步&#xf…

无源晶振匹配电容—计算方法

以前有写过一篇文章“晶振”简单介绍了晶振的一些简单参数&#xff0c;今天我们来说下无源晶振的匹配电容计算方法&#xff1a; 如上图&#xff0c;是常见的的无源晶振常见接法&#xff0c;而今天来说到就是这种常见电路的电容计算方法&#xff0c;有两种&#xff1a; A&#…

CUDA 内存系统

CUDA 内存系统 本文主要是针对<cuda c编程权威指南>的总结,由于原书出版的时候cuda刚刚出到cuda6,之后的cuda版本可能有更新,可能需要我翻一翻文档,待更新. 内存系统架构图 常见的内存作用域与生存期 新特性 早期的 Kepler 架构中一个颇为好用的特性就是 CUDA 程序员可…

没有公网ip怎么外网访问nas?快解析内网端口映射到公网

对于NAS用户而言&#xff0c;外网访问是永远绕不开的话题。拥有NAS后的第一个问题&#xff0c;就是搞定NAS的外网访问。不过众所周知&#xff0c;并不是所有的小伙伴都能得到公网IP&#xff0c;由于IPV4资源的枯竭&#xff0c;一般不会被分配到公网IP。公网IP在很大程度上除了让…

文件的打开关闭和顺序读写

目录 一、文件的打开与关闭 &#xff08;一&#xff09;文件指针 &#xff08;二&#xff09; 文件的打开和关闭 二、文件的顺序读写 &#xff08;一&#xff09;fputc 1. 介绍 2. 举例 &#xff08;二&#xff09;fgetc 1. 介绍 2. 举例1 3. 举例2 &#xff08;三&…

长尾关键词使用方法,通过什么方式挖掘长尾关键词?

当你在搜索引擎的搜索栏中输入有关如何使用长尾关键词的查询时&#xff0c;你可能希望有简单快捷的方式出现在搜索结果中&#xff0c;可以帮助你更好地应用seo。 不过&#xff0c;这里要记住一件事&#xff1a;SEO 策略只会为你的网站带来流量&#xff1b;在你的产品良好之前&a…

VS编译系统 实用调试技巧

目录什么是bug?调试是什么&#xff1f;有多重要&#xff1f;debug和release的介绍windows环境调试介绍、一些调试实例如何写出&#xff08;易于调试&#xff09;的代码编程常见的错误什么是bug?其实bug在英文翻译中有表示臭虫的含义&#xff0c;因为第一次被发现的导致计算机…

【Linux驱动开发100问】什么是模块?如何编写和使用模块?

&#x1f947;今日学习目标&#xff1a;什么是Linux内核&#xff1f; &#x1f935;‍♂️ 创作者&#xff1a;JamesBin ⏰预计时间&#xff1a;10分钟 &#x1f389;个人主页&#xff1a;嵌入式悦翔园个人主页 &#x1f341;专栏介绍&#xff1a;Linux驱动开发100问 什么是模块…

堆的基本存储

一、概念及其介绍堆(Heap)是计算机科学中一类特殊的数据结构的统称。堆通常是一个可以被看做一棵完全二叉树的数组对象。堆满足下列性质&#xff1a;堆中某个节点的值总是不大于或不小于其父节点的值。堆总是一棵完全二叉树。二、适用说明堆是利用完全二叉树的结构来维护一组数…

css 画图之质感盒子

前言 css 众所周知可以做很多的事情&#xff0c;比如&#xff1a;界面效果、特效、独特的样式等。今天给各位朋友带来的是以box-shadow来画一个很有质感效果的一个盒子。 之前在网上冲浪的时候&#xff0c;发现了这样的一个效果&#xff0c;所以来记录一下。 下面是实现后的…

Zookeeper源码环境搭建

前言 一、IEDA导入zk源码 git clone -b release-3.7.0 gitgithub.com:apache/zookeeper.git二、切换到稳定分支 通过命令行切换zk分支到3.8.1稳定版。 git checkout -b branch-3.8.1三、编译项目 执行maven命令编译项目 mvn clean install -Dmaven.test.skiptrue三、集群搭…

【计算机网络】高并发业务必备的Linux网络IO模型

IO的操作也就是应用程序从TCP缓冲区中读取数据的时候。网络I/O的本质是socket的读取&#xff0c;socket在linux中被抽象为流&#xff0c;I/O可以理解为对流的操作。对于一次I/O访问&#xff0c;数据会先被拷贝到操作系统的内核的缓冲区中&#xff0c;然后才会从操作系统内核的缓…

Java EE|TCP/IP协议栈之应用层协议DNS详解

文章目录一、对DNS的感性认识简介特点一些常见疑问二、DNSDNS域名结构域名的分级三、域名服务器四、域名解析过程参考一、对DNS的感性认识 简介 DNS&#xff0c;即Domain Name System,是域名系统的简称。它是Internet上解决网上机器命名的一种系统。 TCP/IP中的IP地址是由四…

C语言结构体对齐

1. 结构体对齐 要点 变量只能存储在他的长度的整数倍地址上结构体整体对齐跟他的最长的字段整数倍对齐 栗子1 struct Example1 {char a; //1个字节int c; //4个字节short b; //2个字节 };std::cout << sizeof(Example1 ) << std::endl; // 12 std::cout &…

JVM篇之垃圾回收

一.如何判断对象可以回收 1.引用计数法 只要一个对象被其他变量所引用&#xff0c;就让它的计数加1&#xff0c;被引用了两次就让它的计数变成2&#xff0c;当这个变量的计数变成0时&#xff0c;就可以被垃圾回收&#xff1b; 弊端&#xff1a;当出现如下图的情况&#xff0…

4.OCR文本识别Connectionist Temporal Classification(CTC)算法

文章目录1.基础介绍2.Connectionist Temporal Classification(CTC)算法2.1 什么是Temporal Classification2.2 CTC问题描述2.2关于对齐2.3 前向后向算法2.4 推理时3.pytorch中的CTCLOSS参考资料欢迎访问个人网络日志&#x1f339;&#x1f339;知行空间&#x1f339;&#x1f3…