分不清楚蝴蝶的种类,让AI来告诉你,基于YOLOv5开发构建轻量级蝴蝶细粒度目标检测识别系统

news2024/11/15 7:58:58

小时候有时间就很喜欢趁着下课放学的时间去抓蝴蝶玩,五彩多样的蝴蝶让人应接不暇,现在早就过了那个天真玩耍的年纪了,如今看到蝴蝶的第一反应就是这是什么蝴蝶,没有专业的知识支撑很难识别出来具体的种类,本文的主要目的就是想要基于深度学习目标检测模型来尝试开发构建蝴蝶细粒度目标检测识别模型,首先看小效果图,如下所示:

 接下来看下我准备的数据集,一共收集到了20种不同类别的蝴蝶图像数据集,之后人工标注数据得到所需要的数据集。

 以下是20种蝴蝶的信息解释:

  1. Atrophaneura_horishanus: 科学名称为Atrophaneura horishanus,属于阿特罗法尼乌拉亚属(Atrophaneura),可能是一种热带地区的蝴蝶。

  2. Atrophaneura_varuna: 科学名称为Atrophaneura varuna,同样属于阿特罗法尼乌拉亚属(Atrophaneura),可能是另一种热带地区的蝴蝶。

  3. Byasa_alcinous: 科学名称为Byasa alcinous,属于拟鳞蛱蝶属(Byasa),可能是一种大型的蛱蝶,分布在某些地区。

  4. Byasa_dasarada: 科学名称为Byasa dasarada,同样属于拟鳞蛱蝶属(Byasa),可能是另一种大型的蛱蝶,具有特定的地理分布。

  5. Byasa_polyeuctes: 科学名称为Byasa polyeuctes,仍然属于拟鳞蛱蝶属(Byasa),可能是另一种大型的蛱蝶,具有不同的特征和分布。

  6. Graphium_agamemnon: 科学名称为Graphium agamemnon,属于凤蝶属(Graphium),可能是一种美丽的凤蝶,具有独特的花纹和颜色。

  7. Graphium_cloanthus: 科学名称为Graphium cloanthus,同样属于凤蝶属(Graphium),可能是另一种引人注目的凤蝶,分布在某些地区。

  8. Graphium_sarpedon: 科学名称为Graphium sarpedon,仍然属于凤蝶属(Graphium),可能是另一种迷人的凤蝶,栖息于特定的地理区域。

  9. Iphiclides_podalirius: 科学名称为Iphiclides podalirius,属于斑蝶属(Iphiclides),可能是一种优雅的斑蝶,具有独特的外观和分布。

  10. Lamproptera_curius: 科学名称为Lamproptera curius,属于亮蛱蝶属(Lamproptera),可能是一种光彩夺目的蛱蝶,展示出令人惊叹的色彩。

  11. Lamproptera_meges: 科学名称为Lamproptera meges,同样属于亮蛱蝶属(Lamproptera),可能是另一种华丽的蛱蝶,有着不同的特点和分布。

  12. Losaria_coon: 科学名称为Losaria coon,属于斑尺蛾属(Losaria),可能是一种斑尺蛾,具有独特的形态和分布。

  13. Meandrusa_payeni: 科学名称为Meandrusa payeni,属于猫眼蝶属(Meandrusa),可能是一种稀有的猫眼蝶,栖息在某些特定的地区。

  14. Meandrusa_sciron: 科学名称为Meandrusa sciron,同样属于猫眼蝶属(Meandrusa),可能是另一种美丽的猫眼蝶,具有不同的特征和分布。

  15. Pachliopta_aristolochiae: 科学名称为Pachliopta aristolochiae,属于斗羽蝶属(Pachliopta),可能是一种特殊的斗羽蝶,以其对防御性植物毒素的耐受能力而闻名。

  16. Papilio_alcmenor: 科学名称为Papilio alcmenor,属于大鳞尾属(Papilio),可能是一种大型的鳞尾蝶,具有独特的翅膀形态和花纹。

  17. Papilio_arcturus: 科学名称为Papilio arcturus,同样属于大鳞尾属(Papilio),可能是另一种引人注目的鳞尾蝶,栖息于特定的地理区域。

  18. Papilio_bianor: 科学名称为Papilio bianor,仍然属于大鳞尾属(Papilio),可能是另一种迷人的鳞尾蝶,具有不同的特征和分布。

  19. Papilio_dialis: 科学名称为Papilio dialis,属于大鳞尾属(Papilio),可能是一种华丽的鳞尾蝶,展示出令人惊叹的色彩以及翅膀上的花纹。

  20. Papilio_hermosanus: 科学名称为Papilio hermosanus,同样属于大鳞尾属(Papilio),可能是一种壮丽的鳞尾蝶,有着不同的特点和分布。

详细对应信息如下所示:

{
    "0": "Atrophaneura_horishanus",
    "1": "Atrophaneura_varuna",
    "2": "Byasa_alcinous",
    "3": "Byasa_dasarada",
    "4": "Byasa_polyeuctes",
    "5": "Graphium_agamemnon",
    "6": "Graphium_cloanthus",
    "7": "Graphium_sarpedon",
    "8": "Iphiclides_podalirius",
    "9": "Lamproptera_curius",
    "10": "Lamproptera_meges",
    "11": "Losaria_coon",
    "12": "Meandrusa_payeni",
    "13": "Meandrusa_sciron",
    "14": "Pachliopta_aristolochiae",
    "15": "Papilio_alcmenor",
    "16": "Papilio_arcturus",
    "17": "Papilio_bianor",
    "18": "Papilio_dialis",
    "19": "Papilio_hermosanus"
}

这里我选用的模型是yolov5s系列的模型,如下所示:

# YOLOv5 🚀 by Ultralytics, GPL-3.0 license

# Parameters
nc: 20  # number of classes
depth_multiple: 0.33  # model depth multiple
width_multiple: 0.50  # layer channel multiple
anchors:
  - [10,13, 16,30, 33,23]  # P3/8
  - [30,61, 62,45, 59,119]  # P4/16
  - [116,90, 156,198, 373,326]  # P5/32

#Backbone
backbone:
  # [from, number, module, args]
  [[-1, 1, Conv, [64, 6, 2, 2]],  # 0-P1/2
   [-1, 1, Conv, [128, 3, 2]],  # 1-P2/4
   [-1, 3, C3, [128]],
   [-1, 1, Conv, [256, 3, 2]],  # 3-P3/8
   [-1, 6, C3, [256]],
   [-1, 1, Conv, [512, 3, 2]],  # 5-P4/16
   [-1, 9, C3, [512]],
   [-1, 1, Conv, [1024, 3, 2]],  # 7-P5/32
   [-1, 3, C3, [1024]],
   [-1, 1, SPPF, [1024, 5]],  # 9
  ]

#Head
head:
  [[-1, 1, Conv, [512, 1, 1]],
   [-1, 1, nn.Upsample, [None, 2, 'nearest']],
   [[-1, 6], 1, Concat, [1]],  # cat backbone P4
   [-1, 3, C3, [512, False]],  # 13

   [-1, 1, Conv, [256, 1, 1]],
   [-1, 1, nn.Upsample, [None, 2, 'nearest']],
   [[-1, 4], 1, Concat, [1]],  # cat backbone P3
   [-1, 3, C3, [256, False]],  # 17 (P3/8-small)

   [-1, 1, Conv, [256, 3, 2]],
   [[-1, 14], 1, Concat, [1]],  # cat head P4
   [-1, 3, C3, [512, False]],  # 20 (P4/16-medium)

   [-1, 1, Conv, [512, 3, 2]],
   [[-1, 10], 1, Concat, [1]],  # cat head P5
   [-1, 3, C3, [1024, False]],  # 23 (P5/32-large)

   [[17, 20, 23], 1, Detect, [nc, anchors]],  # Detect(P3, P4, P5)
  ]

默认100次epoch的迭代计算,计算日志输出如下所示:

 模型训练完成后对模型进行评估计算,如下所示:

Validating runs/train/yolov5s/weights/best.pt...
Fusing layers... 
YOLOv5s summary: 157 layers, 7064065 parameters, 0 gradients, 15.9 GFLOPs
                 Class     Images  Instances          P          R      mAP50   mAP50-95: 100%|??????????| 8/8 [00:04<00:00,  1.64it/s]
                   all        242        259      0.942      0.904      0.965      0.756
Atrophaneura_horishanus        242          8      0.964          1      0.995      0.762
   Atrophaneura_varuna        242          7          1       0.89      0.995      0.839
        Byasa_alcinous        242         18      0.888          1      0.989      0.767
        Byasa_dasarada        242         15      0.822      0.733      0.887      0.707
      Byasa_polyeuctes        242         16      0.924      0.762      0.902      0.722
    Graphium_agamemnon        242         10      0.929          1      0.995      0.849
    Graphium_cloanthus        242         20          1      0.919      0.971      0.731
     Graphium_sarpedon        242          6      0.811          1      0.901      0.726
 Iphiclides_podalirius        242         16      0.986          1      0.995      0.764
    Lamproptera_curius        242         13      0.976      0.769      0.876      0.612
     Lamproptera_meges        242         20          1      0.809      0.981       0.62
          Losaria_coon        242         11          1      0.842      0.995      0.795
      Meandrusa_payeni        242          8      0.987          1      0.995      0.738
      Meandrusa_sciron        242          9          1      0.938      0.995      0.759
Pachliopta_aristolochiae        242         19          1      0.767       0.96      0.732
      Papilio_alcmenor        242         11      0.899          1      0.995      0.861
      Papilio_arcturus        242         14      0.988      0.857      0.966      0.856
        Papilio_bianor        242         11      0.885          1       0.98       0.84
        Papilio_dialis        242         16      0.877      0.892      0.949      0.748
    Papilio_hermosanus        242         11      0.895      0.909      0.973      0.698
Results saved to runs/train/yolov5s

接下来看下具体的指标详情,如下所示:

【混淆矩阵】

混淆矩阵(Confusion Matrix)是在机器学习领域中常用的评估分类模型性能的工具。它以矩阵的形式展示了分类模型预测结果与实际标签之间的差异。

混淆矩阵包括四个关键术语:

  • 真正例(True Positive, TP):模型正确地将正例预测为正例。
  • 假正例(False Positive, FP):模型错误地将负例预测为正例。
  • 假反例(False Negative, FN):模型错误地将正例预测为负例。
  • 真反例(True Negative, TN):模型正确地将负例预测为负例。

混淆矩阵的一般形式如下:

                 预测正例     预测负例
实际正例        TP             FN
实际负例        FP             TN

可以根据混淆矩阵的内容计算许多有用的评估指标,例如:

  • 准确率(Accuracy):分类正确的样本占总样本数的比例,计算公式为 (TP + TN) / (TP + TN + FP + FN)
  • 精确率(Precision):预测为正例的样本中真实为正例的比例,计算公式为 TP / (TP + FP)
  • 召回率(Recall):真实为正例的样本中被正确预测为正例的比例,计算公式为 TP / (TP + FN)
  • F1分数(F1 Score):精确率和召回率的综合评估指标,计算公式为 2 * (Precision * Recall) / (Precision + Recall)

混淆矩阵可用于直观地理解分类模型在不同类别上的性能,并帮助确定是否存在误分类的情况。根据具体应用场景,可以选择适当的评估指标来衡量分类模型的效果。

 【F1值曲线】

F1值曲线是一种用于评估二分类模型在不同阈值下的性能的可视化工具。它通过绘制不同阈值下的精确率(Precision)、召回率(Recall)和F1分数的关系图来帮助我们理解模型的整体性能。

F1分数是精确率和召回率的调和平均值,它综合考虑了两者的性能指标。F1值曲线可以帮助我们确定在不同精确率和召回率之间找到一个平衡点,以选择最佳的阈值。

绘制F1值曲线的步骤如下:

  1. 使用不同的阈值将预测概率转换为二进制类别标签。通常,当预测概率大于阈值时,样本被分类为正例,否则分类为负例。
  2. 对于每个阈值,计算相应的精确率、召回率和F1分数。
  3. 将每个阈值下的精确率、召回率和F1分数绘制在同一个图表上,形成F1值曲线。
  4. 根据F1值曲线的形状和变化趋势,可以选择适当的阈值以达到所需的性能要求。

F1值曲线通常与接收者操作特征曲线(ROC曲线)一起使用,以帮助评估和比较不同模型的性能。它们提供了更全面的分类器性能分析,可以根据具体应用场景来选择合适的模型和阈值设置。

 【Precision曲线】

精确率曲线(Precision-Recall Curve)是一种用于评估二分类模型在不同阈值下的精确率性能的可视化工具。它通过绘制不同阈值下的精确率和召回率之间的关系图来帮助我们了解模型在不同阈值下的表现。

精确率(Precision)是指被正确预测为正例的样本数占所有预测为正例的样本数的比例。召回率(Recall)是指被正确预测为正例的样本数占所有实际为正例的样本数的比例。

绘制精确率曲线的步骤如下:

  1. 使用不同的阈值将预测概率转换为二进制类别标签。通常,当预测概率大于阈值时,样本被分类为正例,否则分类为负例。
  2. 对于每个阈值,计算相应的精确率和召回率。
  3. 将每个阈值下的精确率和召回率绘制在同一个图表上,形成精确率曲线。
  4. 根据精确率曲线的形状和变化趋势,可以选择适当的阈值以达到所需的性能要求。

通过观察精确率曲线,我们可以根据需求确定最佳的阈值,以平衡精确率和召回率。较高的精确率意味着较少的误报,而较高的召回率则表示较少的漏报。根据具体的业务需求和成本权衡,可以在曲线上选择合适的操作点或阈值。

精确率曲线通常与召回率曲线(Recall Curve)一起使用,以提供更全面的分类器性能分析,并帮助评估和比较不同模型的性能。

 【召回率曲线】

召回率曲线(Recall Curve)是一种用于评估二分类模型在不同阈值下的召回率性能的可视化工具。它通过绘制不同阈值下的召回率和对应的精确率之间的关系图来帮助我们了解模型在不同阈值下的表现。

召回率(Recall)是指被正确预测为正例的样本数占所有实际为正例的样本数的比例。召回率也被称为灵敏度(Sensitivity)或真正例率(True Positive Rate)。

绘制召回率曲线的步骤如下:

  1. 使用不同的阈值将预测概率转换为二进制类别标签。通常,当预测概率大于阈值时,样本被分类为正例,否则分类为负例。
  2. 对于每个阈值,计算相应的召回率和对应的精确率。
  3. 将每个阈值下的召回率和精确率绘制在同一个图表上,形成召回率曲线。
  4. 根据召回率曲线的形状和变化趋势,可以选择适当的阈值以达到所需的性能要求。

通过观察召回率曲线,我们可以根据需求确定最佳的阈值,以平衡召回率和精确率。较高的召回率表示较少的漏报,而较高的精确率意味着较少的误报。根据具体的业务需求和成本权衡,可以在曲线上选择合适的操作点或阈值。

召回率曲线通常与精确率曲线(Precision Curve)一起使用,以提供更全面的分类器性能分析,并帮助评估和比较不同模型的性能。

 【PR曲线】

精确率-召回率曲线(Precision-Recall Curve)是一种用于评估二分类模型性能的可视化工具。它通过绘制不同阈值下的精确率(Precision)和召回率(Recall)之间的关系图来帮助我们了解模型在不同阈值下的表现。

精确率是指被正确预测为正例的样本数占所有预测为正例的样本数的比例。召回率是指被正确预测为正例的样本数占所有实际为正例的样本数的比例。

绘制精确率-召回率曲线的步骤如下:

使用不同的阈值将预测概率转换为二进制类别标签。通常,当预测概率大于阈值时,样本被分类为正例,否则分类为负例。
对于每个阈值,计算相应的精确率和召回率。
将每个阈值下的精确率和召回率绘制在同一个图表上,形成精确率-召回率曲线。
根据曲线的形状和变化趋势,可以选择适当的阈值以达到所需的性能要求。
精确率-召回率曲线提供了更全面的模型性能分析,特别适用于处理不平衡数据集和关注正例预测的场景。曲线下面积(Area Under the Curve, AUC)可以作为评估模型性能的指标,AUC值越高表示模型的性能越好。

通过观察精确率-召回率曲线,我们可以根据需求选择合适的阈值来权衡精确率和召回率之间的平衡点。根据具体的业务需求和成本权衡,可以在曲线上选择合适的操作点或阈值。

 模型训练过程可视化如下所示:

 【数据类别标签可视化】如下所示:

 为了进一步分析计算模型检测识别的原理,这里集成了GradCAM进行热力图计算与可视化,实例效果如下所示:

 GradCAM核心代码实现如下所示:

import time
import torch
import torch.nn.functional as F


def find_yolo_layer(model, layer_name):
    """Find yolov5 layer to calculate GradCAM and GradCAM++

    Args:
        model: yolov5 model.
        layer_name (str): the name of layer with its hierarchical information.

    Return:
        target_layer: found layer
    """
    hierarchy = layer_name.split('_')
    target_layer = model.model._modules[hierarchy[0]]

    for h in hierarchy[1:]:
        target_layer = target_layer._modules[h]
    return target_layer


class YOLOV5GradCAM:

    def __init__(self, model, layer_name, img_size=(640, 640)):
        self.model = model
        self.gradients = dict()
        self.activations = dict()

        def backward_hook(module, grad_input, grad_output):
            self.gradients['value'] = grad_output[0]
            return None

        def forward_hook(module, input, output):
            self.activations['value'] = output
            return None

        target_layer = find_yolo_layer(self.model, layer_name)
        target_layer.register_forward_hook(forward_hook)
        target_layer.register_backward_hook(backward_hook)

        device = 'cuda' if next(self.model.model.parameters()).is_cuda else 'cpu'
        self.model(torch.zeros(1, 3, *img_size, device=device))
        print('[INFO] saliency_map size :', self.activations['value'].shape[2:])

    def forward(self, input_img, class_idx=True):
        """
        Args:
            input_img: input image with shape of (1, 3, H, W)
        Return:
            mask: saliency map of the same spatial dimension with input
            logit: model output
            preds: The object predictions
        """
        saliency_maps = []
        b, c, h, w = input_img.size()
        tic = time.time()
        preds, logits = self.model(input_img)
        print("[INFO] model-forward took: ", round(time.time() - tic, 4), 'seconds')
        for logit, cls, cls_name in zip(logits[0], preds[1][0], preds[2][0]):
            if class_idx:
                score = logit[cls]
            else:
                score = logit.max()
            self.model.zero_grad()
            tic = time.time()
            score.backward(retain_graph=True)
            print(f"[INFO] {cls_name}, model-backward took: ", round(time.time() - tic, 4), 'seconds')
            gradients = self.gradients['value']
            print("gradients_shape: ", gradients.shape)
            activations = self.activations['value']
            print("activations_shape: ", activations.shape)
            b, k, u, v = gradients.size()
            k = 512
            alpha = gradients.view(b, k, -1).mean(2)
            print("alpha_shape: ", alpha.shape)
            weights = alpha.view(b, k, 1, 1)
            print("weights_shape: ", weights.shape)
            print("activations_shape: ", activations.shape)
            a,b,c,d=activations.shape
            #activations = torch.reshape(activations, (1,32,c*4,d*4))
            print("activations_shape: ", activations.shape)
            saliency_map = (weights * activations).sum(1, keepdim=True)
            saliency_map = F.relu(saliency_map)
            saliency_map = F.upsample(saliency_map, size=(h, w), mode='bilinear', align_corners=False)
            saliency_map_min, saliency_map_max = saliency_map.min(), saliency_map.max()
            saliency_map = (saliency_map - saliency_map_min).div(saliency_map_max - saliency_map_min).data
            saliency_maps.append(saliency_map)
        return saliency_maps, logits, preds

    def __call__(self, input_img):
        return self.forward(input_img)

可视化界面实例推理如下所示:

 

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

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

相关文章

电脑bios使用的uefi启动,系统盘使用的mbr格式,为什么安装完系统无法进入?

概要 在电脑的安装系统过程中&#xff0c;我们会遇到一些问题&#xff0c;比如说使用UEFI启动但是磁盘分区使用MBR格式&#xff0c;导致系统安装完成之后无法引导。这是因为UEFI启动只支持GPT格式的磁盘分区。 在本文中&#xff0c;我们将探讨如何将磁盘转换成GPT格式&#xf…

一种新的程序在线升级的实现(学习)

介绍一种新的程序在线升级方法&#xff0c;以及程序在线升级在嵌入式系统中的应用和实现。设计将从系统原理和实现介绍该程序在线升级方法。利用串口作为通讯方式&#xff0c;自定义通讯协议。 上位机将升级文件传给下位机&#xff0c;下位机将数据解析后存储在内部Flash中。解…

用矩阵处理3D变换

Rotation 也可以把三个旋转矩阵合并为一个综合旋转矩阵: 平移和旋转结合 有时我们想要将平移和旋转结合起来&#xff0c;这样我们就可以在一次操作中同时进行两者&#xff0c;但是我们不能用3x3矩阵来做3D平移&#xff0c;只能用4x4矩阵来做&#xff0c;如下所定义&#xff1a…

尚硅谷Docker实战教程-笔记07【Docker常规安装简介】

尚硅谷大数据技术-教程-学习路线-笔记汇总表【课程资料下载】视频地址&#xff1a;尚硅谷Docker实战教程&#xff08;docker教程天花板&#xff09;_哔哩哔哩_bilibili 尚硅谷Docker实战教程-笔记01【理念简介、官网介绍、平台入门图解、平台架构图解】尚硅谷Docker实战教程-笔…

嵌入式基础知识-总线带宽

带宽&#xff0c;最容易想到的是上网用的网络带宽&#xff0c;在嵌入式软件开发中&#xff0c;也会用到带宽&#xff0c;这个带宽的含义就不一样了&#xff0c;区别是什么&#xff1f;本篇就来介绍一下&#xff0c;并通过一些例子来进行带宽的计算。 先来简单看下不同领域的带…

交叉熵和softmax

交叉熵cross-entropy loss 最大化似然函数&#xff0c;最小化负的似然对数函数 最终的交叉熵损失函数&#xff0c;最小化该loss&#xff1a; nn.BCELoss def criterion(yhat, y):out -1 * torch.mean(y*torch.log(yhat) (1-y) * torch.log(1-yhat))return outsoftmax

51单片机一氧化碳烟雾报警器mq2MQ7ADC0832采集

实践制作DIY- GC0152--- 一氧化碳烟雾报警器 基于51单片机设计------- 一氧化碳烟雾报警器 二、功能介绍&#xff1a; STC89C52单片机lcd1602adc0832mq2烟雾传感器mq7烟雾传感器蜂鸣器2个按键设定烟雾报警阈值 2个按键设定一氧化碳报警阈值 1.通过ADC0832采集MQ2烟雾输出的电…

嵌入式系统中STM32时钟系统详解

1. STM32的时钟源主要有&#xff1a; 内部时钟 外部时钟 锁相环倍频输出时钟 1.1 详细介绍 HSI(内部高速时钟) 它是RC振荡器&#xff0c;频率可以达到8MHZ&#xff0c;可作为系统时钟和PLL锁相环的输入 HSE&#xff08;外部高速时钟&#xff09; 接入晶振范围是4-16MHZ&a…

数据结构--线性表(顺序表、单链表、双链表、循环链表、静态链表)

前言 学习所记录&#xff0c;如果能对你有帮助&#xff0c;那就泰裤辣。 目录 1.线性表概念 定义 基本操作 2.顺序表 定义 顺序表的实现--静态分配 动态分配 顺序表的特点 顺序表的插入和删除 顺序表的查找 按位查找 按值查找 3.单链表 定义 单链表的初始化 不带…

112.(cesium篇)cesium地球自转+文字上下滚动

地图之家总目录(订阅之前必须详细了解该博客) 地图之家:cesium+leaflet+echart+地图数据+地图工具等相关内容的介绍 文章末尾处提供保证可运行完整代码包,运行如有问题,可“私信”博主。 效果如下所示: 112.(cesium篇)cesium地球自转+文字上下滚动 下面献上完整代码…

Kubernetes(k8s)集群搭建,完整无坑,不需要科学上网~

文章目录 写在前面一、准备三个centos7虚拟机1、创建Vagrantfile2、启动三台虚拟机3、配置centos7支持ssh登录&#xff08;所有机器&#xff09;4、修改 linux 的 yum 源&#xff08;所有机器&#xff09;5、更新并安装依赖&#xff08;所有机器&#xff09;6、安装docker&…

SPI硬件实现-GD32

SPI硬件实现-GD32 #include "w25qxx_spi.h"void w25qxx_init(void){// 使能外设时钟w25qxx_rcu_init();// IO口进行配置,使之复用为SPI0, PA4\PA5\PA6\PA7,NSS\SCK\MISO\MOSIw25qxx_io_init();// SPI0初始化w25qxx_spi_init();spi_enable(SPI0); }// 使能外设时钟…

HTTP/HTTPS 简介||HTTP 消息结构

HTTP/HTTPS 简介 HTTP 协议是 Hyper Text Transfer Protocol&#xff08;超文本传输协议&#xff09;的缩写&#xff0c;是用于从万维网&#xff08; WWW:World Wide Web &#xff09;服务器传输超文本到本地浏览器的传送协议。 HTTP 是一个基于 TCP/IP 通信协议来传递数据&a…

〖动态规划60题〗泰波纳契数列模型

文章目录 1.第N个泰波那契数&#xff08;简单&#xff09;解题流程1. 状态表示2. 状态转移方程3. 初始化dp表4. 填表顺序5. 返回值 代码编写 2.三步问题解题流程1. 状态表示2. 状态转移方程3. 初始化dp表4. 填表顺序5. 返回值 代码编写 3.使用最小花费爬楼梯解题流程1. 状态表示…

Redis高可用——主从复制

redis的主从复制 一、Redis 主从复制1.主从复制的作用&#xff1a;2.主从复制流程&#xff1a; 二、搭建Redis 主从复制1.安装 Redis①.环境准备②.修改内核参数③.安装redis④.创建redis工作目录⑤.环境变量⑥.定义systemd服务管理脚本 2.修改 Redis 配置文件&#xff08;Mast…

阅读源码技巧

目录 搭建 Demo方法论之关注调用栈方法论之死盯日志方法论之查看被调用的地方探索答案作业是的,正如标题描述的这样,我试图通过这篇文章,教会你如何阅读源码。 事情大概是这样的,前段时间,我收到了一个读者发来的类似于这样的示例代码: 他说他知道这三个案例的回滚情况是…

详解TCP

目录 1.TCP概论 1.1.什么是TCP 1.2.TCP连接的建立过程 1.3.TCP的传输过程 1.4.TCP连接的释放过程 2.JAVA中的TCP 3.TCP带来的一些性能问题 1.TCP概论 1.1.什么是TCP 为了保证所有设备能相互通信&#xff0c;从而建立了一张互联互通的计算机网络&#xff0c;在这张大网…

使用java.lang.Record类删除样板代码

样板是一个源自钢铁制造业的术语&#xff0c;其中形成模具以铸造类似的物体。在编程世界中&#xff0c;样板代码是代码的一部分&#xff0c;项目里面使用的地方很多&#xff0c;但是通常创建完成之后就很少或者就不会更改了。在Java中&#xff0c;不可变的数据载体类用于与数据…

SpringBoot第13讲:SpringBoot接口如何参数校验国际化

SpringBoot第13讲&#xff1a;SpringBoot接口 - 如何参数校验国际化 本文是SpringBoot第13讲&#xff0c;上文我们学习了如何对SpringBoot接口进行参数校验&#xff0c;但是如果需要有国际化的信息&#xff08;比如返回校验结果有中英文&#xff09;&#xff0c;应该如何优雅处…

【C++】vector介绍及使用

&#x1f680; 作者简介&#xff1a;一名在后端领域学习&#xff0c;并渴望能够学有所成的追梦人。 &#x1f681; 个人主页&#xff1a;不 良 &#x1f525; 系列专栏&#xff1a;&#x1f6f8;C &#x1f6f9;Linux &#x1f4d5; 学习格言&#xff1a;博观而约取&#xff0…