YOLOv5改进系列(2)——添加CBAM注意力机制

news2024/10/7 4:35:50

962f7cb1b48f44e29d9beb1d499d0530.gif【YOLOv5改进系列】前期回顾:

YOLOv5改进系列(0)——重要性能指标与训练结果评价及分析

YOLOv5改进系列(1)——添加SE注意力机制


目录

🚀一、CBAM注意力机制原理

1.1 CBAM方法介绍

1.2 通道注意力机制模块(CAM)

1.3 空间注意力机制模块(SAM)

🚀二、添加CBAM注意力机制方法(单独加)

2.1 添加顺序 

2.2 具体添加步骤 

第①步:在common.py中添加CBAM模块

第②步:在yolo.py文件里的parse_model函数加入类名

第③步:创建自定义的yaml文件 

第④步:验证是否加入成功

第⑤步:修改train.py中 ‘--cfg’默认参数

 🚀三、添加C3_CBAM注意力机制方法(在C3模块中添加)

第①步:在common.py中添加CBAMBottleneck和C3_CBAM模块

第②步:在yolo.py文件里的parse_model函数加入类名

第③步:创建自定义的yaml文件 

第④步:验证是否加入成功

第⑤步:修改train.py中 ‘--cfg’默认参数

🚀一、CBAM注意力机制原理

论文题目:《CBAM: Convolutional Block Attention Module》

论文地址:https://arxiv.org/pdf/1807.06521.pdf

代码实现:CBAM.PyTorch

1.1 CBAM方法介绍

CBAM注意力机制是由通道注意力机制(channel)空间注意力机制(spatial)组成。

在上一篇的SE中,我们学习了通道注意力机制(channel),而本篇的CBAM从通道channel 空间spatial 两个作用域出发,实现从通道到空间的顺序注意力结构。空间注意力可使神经网络更加关注图像中对分类起决定作用的像素区域而忽略无关紧要的区域,通道注意力则用于处理特征图通道的分配关系,同时对两个维度进行注意力分配增强了注意力机制对模型性能的提升效果。


1.2 通道注意力机制模块(CAM)

具体流程如下:
首先,将输入的特征图F(H×W×C)分别经过基于width和height的最大池化平均池化,对特征映射基于两个维度压缩,得到两个1×1×C的特征图

接着,再将最大池化平均池化的结果利用共享的全连接层(Shared MLP)进行处理,先通过一个全连接层下降通道数再通过另一个全连接恢复通道数

然后,将共享的全连接层所得到的结果进行相加再使用Sigmoid激活函数,进而生成最终的channel attention feature,即获得输入特征层每一个通道的权重(0~1之间)。

最后,将权重通过乘法逐通道加权到输入特征层上,生成Spatial attention模块需要的输入特征。

代码实现如下: 

#(1)通道注意力机制
class channel_attention(nn.Module):
    # 初始化, in_channel代表输入特征图的通道数, ratio代表第一个全连接的通道下降倍数
    def __init__(self, in_channel, ratio=4):
        # 继承父类初始化方法
        super(channel_attention, self).__init__()
        
        # 全局最大池化 [b,c,h,w]==>[b,c,1,1]
        self.max_pool = nn.AdaptiveMaxPool2d(output_size=1)
        # 全局平均池化 [b,c,h,w]==>[b,c,1,1]
        self.avg_pool = nn.AdaptiveAvgPool2d(output_size=1)
        
        # 第一个全连接层, 通道数下降4倍
        self.fc1 = nn.Linear(in_features=in_channel, out_features=in_channel//ratio, bias=False)
        # 第二个全连接层, 恢复通道数
        self.fc2 = nn.Linear(in_features=in_channel//ratio, out_features=in_channel, bias=False)
        
        # relu激活函数
        self.relu = nn.ReLU()
        # sigmoid激活函数
        self.sigmoid = nn.Sigmoid()
    
    # 前向传播
    def forward(self, inputs):
        # 获取输入特征图的shape
        b, c, h, w = inputs.shape
        
        # 输入图像做全局最大池化 [b,c,h,w]==>[b,c,1,1]
        max_pool = self.max_pool(inputs)
        # 输入图像的全局平均池化 [b,c,h,w]==>[b,c,1,1]
        avg_pool = self.avg_pool(inputs)
 
        # 调整池化结果的维度 [b,c,1,1]==>[b,c]
        max_pool = max_pool.view([b,c])
        avg_pool = avg_pool.view([b,c])
 
        # 第一个全连接层下降通道数 [b,c]==>[b,c//4]
        x_maxpool = self.fc1(max_pool)
        x_avgpool = self.fc1(avg_pool)
 
        # 激活函数
        x_maxpool = self.relu(x_maxpool)
        x_avgpool = self.relu(x_avgpool)
        
        # 第二个全连接层恢复通道数 [b,c//4]==>[b,c]
        x_maxpool = self.fc2(x_maxpool)
        x_avgpool = self.fc2(x_avgpool)
        
        # 将这两种池化结果相加 [b,c]==>[b,c]
        x = x_maxpool + x_avgpool
        # sigmoid函数权值归一化
        x = self.sigmoid(x)
        # 调整维度 [b,c]==>[b,c,1,1]
        x = x.view([b,c,1,1])
        # 输入特征图和通道权重相乘 [b,c,h,w]
        outputs = inputs * x
        
        return outputs

1.3 空间注意力机制模块(SAM)

具体流程如下:
将上面CAM模块输出的特征图F’作为本模块的输入特征图。

首先,对输入特征图在通道维度下做最大池化平均池化,将池化后的两张特征图在通道维度堆叠(concat)。

然后,经过一个7×7卷积(7×7比3×3效果要好)操作,降维为1个channel,即卷积核融合通道信息,特征图的shape从 [b,2,h,w] 变成 [b,1,h,w]

最后,将卷积后的结果经过 sigmoid 函数对特征图的空间权重归一化,再将输入特征图和权重相乘。

代码实现如下: 

#(2)空间注意力机制
class spatial_attention(nn.Module):
    # 初始化,卷积核大小为7*7
    def __init__(self, kernel_size=7):
        # 继承父类初始化方法
        super(spatial_attention, self).__init__()
        
        # 为了保持卷积前后的特征图shape相同,卷积时需要padding
        padding = kernel_size // 2
        # 7*7卷积融合通道信息 [b,2,h,w]==>[b,1,h,w]
        self.conv = nn.Conv2d(in_channels=2, out_channels=1, kernel_size=kernel_size,
                              padding=padding, bias=False)
        # sigmoid函数
        self.sigmoid = nn.Sigmoid()
    
    # 前向传播
    def forward(self, inputs):
        
        # 在通道维度上最大池化 [b,1,h,w]  keepdim保留原有深度
        # 返回值是在某维度的最大值和对应的索引
        x_maxpool, _ = torch.max(inputs, dim=1, keepdim=True)
        
        # 在通道维度上平均池化 [b,1,h,w]
        x_avgpool = torch.mean(inputs, dim=1, keepdim=True)
        # 池化后的结果在通道维度上堆叠 [b,2,h,w]
        x = torch.cat([x_maxpool, x_avgpool], dim=1)
        
        # 卷积融合通道信息 [b,2,h,w]==>[b,1,h,w]
        x = self.conv(x)
        # 空间权重归一化
        x = self.sigmoid(x)
        # 输入特征图和空间权重相乘
        outputs = inputs * x
        
        return outputs

🚀二、添加CBAM注意力机制方法(单独加)

2.1 添加顺序 

(1)models/common.py    -->  加入新增的网络结构

(2)     models/yolo.py       -->  设定网络结构的传参细节,将CBAM类名加入其中。(当新的自定义模块中存在输入输出维度时,要使用qw调整输出维度)
(3) models/yolov5*.yaml  -->  新建一个文件夹,如yolov5s_CBAM.yaml,修改现有模型结构配置文件。(当引入新的层时,要修改后续的结构中的from参数)
(4)         train.py                -->  修改‘--cfg’默认参数,训练时指定模型结构配置文件 


2.2 具体添加步骤 

第①步:在common.py中添加CBAM模块

将下面的CBAM代码复制粘贴到common.py文件的末尾

# CBAM
class ChannelAttention(nn.Module):
    def __init__(self, in_planes, ratio=16):
        super(ChannelAttention, self).__init__()
        self.avg_pool = nn.AdaptiveAvgPool2d(1)
        self.max_pool = nn.AdaptiveMaxPool2d(1)
        self.f1 = nn.Conv2d(in_planes, in_planes // ratio, 1, bias=False)
        self.relu = nn.ReLU()
        self.f2 = nn.Conv2d(in_planes // ratio, in_planes, 1, bias=False)
        self.sigmoid = nn.Sigmoid()
    def forward(self, x):
        avg_out = self.f2(self.relu(self.f1(self.avg_pool(x))))
        max_out = self.f2(self.relu(self.f1(self.max_pool(x))))
        out = self.sigmoid(avg_out + max_out)
        return out
    
class SpatialAttention(nn.Module):
    def __init__(self, kernel_size=7):
        super(SpatialAttention, self).__init__()
        assert kernel_size in (3, 7), 'kernel size must be 3 or 7'
        padding = 3 if kernel_size == 7 else 1
        # (特征图的大小-算子的size+2*padding)/步长+1
        self.conv = nn.Conv2d(2, 1, kernel_size, padding=padding, bias=False)
        self.sigmoid = nn.Sigmoid()
    def forward(self, x):
        # 1*h*w
        avg_out = torch.mean(x, dim=1, keepdim=True)
        max_out, _ = torch.max(x, dim=1, keepdim=True)
        x = torch.cat([avg_out, max_out], dim=1)
        #2*h*w
        x = self.conv(x)
        #1*h*w
        return self.sigmoid(x)
    
class CBAM(nn.Module):
    def __init__(self, c1, c2, ratio=16, kernel_size=7):  # ch_in, ch_out, number, shortcut, groups, expansion
        super(CBAM, self).__init__()
        self.channel_attention = ChannelAttention(c1, ratio)
        self.spatial_attention = SpatialAttention(kernel_size)
    def forward(self, x):
        out = self.channel_attention(x) * x
        # c*h*w
        # c*h*w * 1*h*w
        out = self.spatial_attention(out) * out
        return out

如下图所示:


第②步:在yolo.py文件里的parse_model函数加入类名

首先找到yolo.py里面parse_model函数的这一行

  然后把CBAM添加到这个注册表里面


第③步:创建自定义的yaml文件 

 首先在models文件夹下复制yolov5s.yaml 文件,粘贴并重命名为 yolov5s_CBAM.yaml 

 接着修改  yolov5s_CBAM.yaml  ,将CBAM模块加到我们想添加的位置。

注意力机制可以添加在backbone,Neck,Head等部分, 常见的有两种:一是在主干的 SPPF 前添加一层二是将Backbone中的C3全部替换

在这里我是用第一种:将 [-1,1,CBAM,[1024]]添加到 SPPF 的上一层,下一节使用第二种。即下图中所示位置:

 同样的下面的head也得修改,p4,p5以及最后detect的总层数都得+1

 这里我们要把后面两个Concat的from系数分别由[ − 1 , 14 ] , [ − 1 , 10 ]改为[ − 1 , 15 ] [ − 1 , 11 ]。然后将Detect原始的from系数[ 17 , 20 , 23 ]要改为[ 18 , 21 , 24 ]


第④步:验证是否加入成功

yolo.py 文件里面配置改为我们刚才自定义的yolov5s_CBAM.yaml 

 然后运行yolo.py 

  找到CBAM这一层,就说明我们添加成功啦!


第⑤步:修改train.py中 ‘--cfg’默认参数

我们先找到 train.py 文件的parse_opt函数,然后将第二行‘--cfg’ default改为'models/yolov5s_CBAM.yaml',然后就可以开始训练啦~


🚀三、添加C3_CBAM注意力机制方法(在C3模块中添加)

上面是单独加注意力层,接下来的方法是在C3模块中加入注意力层。

刚才也提到了,这个策略是CBAM注意力机制添加到Bottleneck,替换Backbone中的所有C3模块。

(因为步骤和上面相同,所以接下来只放重要步骤噢~) 

第①步:在common.py中添加CBAMBottleneck和C3_CBAM模块

将下面的代码复制粘贴到common.py文件的末尾

# CBAM
class ChannelAttention(nn.Module):
    def __init__(self, in_planes, ratio=16):
        super(ChannelAttention, self).__init__()
        self.avg_pool = nn.AdaptiveAvgPool2d(1)
        self.max_pool = nn.AdaptiveMaxPool2d(1)
        self.f1 = nn.Conv2d(in_planes, in_planes // ratio, 1, bias=False)
        self.relu = nn.ReLU()
        self.f2 = nn.Conv2d(in_planes // ratio, in_planes, 1, bias=False)
        self.sigmoid = nn.Sigmoid()
    def forward(self, x):
        avg_out = self.f2(self.relu(self.f1(self.avg_pool(x))))
        max_out = self.f2(self.relu(self.f1(self.max_pool(x))))
        out = self.sigmoid(avg_out + max_out)
        return out
    
class SpatialAttention(nn.Module):
    def __init__(self, kernel_size=7):
        super(SpatialAttention, self).__init__()
        assert kernel_size in (3, 7), 'kernel size must be 3 or 7'
        padding = 3 if kernel_size == 7 else 1
        # (特征图的大小-算子的size+2*padding)/步长+1
        self.conv = nn.Conv2d(2, 1, kernel_size, padding=padding, bias=False)
        self.sigmoid = nn.Sigmoid()
    def forward(self, x):
        # 1*h*w
        avg_out = torch.mean(x, dim=1, keepdim=True)
        max_out, _ = torch.max(x, dim=1, keepdim=True)
        x = torch.cat([avg_out, max_out], dim=1)
        #2*h*w
        x = self.conv(x)
        #1*h*w
        return self.sigmoid(x)

class CBAMBottleneck(nn.Module):
    # ch_in, ch_out, shortcut, groups, expansion, ratio, kernel_size
    def __init__(self, c1, c2, shortcut=True, g=1, e=0.5, ratio=16, kernel_size=7):
        super(CBAMBottleneck, self).__init__()
        c_ = int(c2 * e)  # hidden channels
        self.cv1 = Conv(c1, c_, 1, 1)
        self.cv2 = Conv(c_, c2, 3, 1, g=g)
        self.add = shortcut and c1 == c2
        # 加入CBAM模块
        self.channel_attention = ChannelAttention(c2, ratio)
        self.spatial_attention = SpatialAttention(kernel_size)
    
    def forward(self, x):
        # 考虑加入CBAM模块的位置:bottleneck模块刚开始时、bottleneck模块中shortcut之前,这里选择在shortcut之前
        x2 = self.cv2(self.cv1(x))  # x和x2的channel数相同
        # 在bottleneck模块中shortcut之前加入CBAM模块
        out = self.channel_attention(x2) * x2
        # print('outchannels:{}'.format(out.shape))
        out = self.spatial_attention(out) * out
        return x + out if self.add else out


class C3_CBAM(C3):
    # C3 module with CBAMBottleneck()
    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(*(CBAMBottleneck(c_, c_, shortcut, g, e=1.0) for _ in range(n)))


第②步:在yolo.py文件里的parse_model函数加入类名

yolo.pyparse_model函数中,加入CBAMBottleneck, C3_CBAM两个模块


第③步:创建自定义的yaml文件 

按照上面的步骤创建yolov5s_C3_CBAM.yaml文件,替换4个C3模块

# YOLOv5 🚀 by Ultralytics, GPL-3.0 license

# Parameters
nc: 80  # 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

# YOLOv5 v6.0 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_CBAM, [128]],
   [-1, 1, Conv, [256, 3, 2]],  # 3-P3/8
   [-1, 6, C3_CBAM, [256]],
   [-1, 1, Conv, [512, 3, 2]],  # 5-P4/16
   [-1, 3, C3_CBAM, [512]],
   [-1, 1, Conv, [1024, 3, 2]],  # 7-P5/32
   [-1, 3, C3_CBAM, [1024]],
   [-1, 1, SPPF, [1024, 5]],  # 9
  ]

# YOLOv5 v6.0 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)
  ]

第④步:验证是否加入成功

yolo.py 文件里面配置改为我们刚才自定义的yolov5s_C3_CBAM.yaml,然后运行

这样就OK啦~


第⑤步:修改train.py中 ‘--cfg’默认参数

接下来的训练就和上面一样,不再叙述啦~

完结~撒花✿✿ヽ(°▽°)ノ✿


PS:加入不同的位置效果不同,这两个我各训练100轮看了下效果,对于我的数据集来说,第2种比第1种mAP增加0.009。


🌟本人YOLOv5系列导航

962f7cb1b48f44e29d9beb1d499d0530.gif​   🍀YOLOv5源码详解系列:  

YOLOv5源码逐行超详细注释与解读(1)——项目目录结构解析

​​​​​​YOLOv5源码逐行超详细注释与解读(2)——推理部分detect.py

YOLOv5源码逐行超详细注释与解读(3)——训练部分train.py

YOLOv5源码逐行超详细注释与解读(4)——验证部分val(test).py

YOLOv5源码逐行超详细注释与解读(5)——配置文件yolov5s.yaml

YOLOv5源码逐行超详细注释与解读(6)——网络结构(1)yolo.py

YOLOv5源码逐行超详细注释与解读(7)——网络结构(2)common.py

962f7cb1b48f44e29d9beb1d499d0530.gif​​   🍀YOLOv5入门实践系列:  

YOLOv5入门实践(1)——手把手带你环境配置搭建

YOLOv5入门实践(2)——手把手教你利用labelimg标注数据集

YOLOv5入门实践(3)——手把手教你划分自己的数据集

YOLOv5入门实践(4)——手把手教你训练自己的数据集

YOLOv5入门实践(5)——从零开始,手把手教你训练自己的目标检测模型(包含pyqt5界面)

本文参考(感谢大佬们):

b站:【YOLOv5 v6.1添加SE,CA,CBAM,ECA注意力机制教学,即插即用】

CSDN: 【深度学习】(1) CNN中的注意力机制(SE、ECA、CBAM)_立Sir的博客-CSDN博客

(强推) 手把手带你YOLOv5 (v6.1)添加注意力机制(二)(在C3模块中加入注意力机制)_迪菲赫尔曼的博客-CSDN博客

​ 

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

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

相关文章

Android车载应用开发和移动应用开发有什么区别?

背景 2022年对我来说是非常糟糕的一年,因为疫情原因导致公司收益不好,所有我被辞退了,离职之后呢有尝试过头简历面试,发现面试的机会很少,经过一段时间的沉淀后,我萌生了转行做车载的想法。为什么会产生这…

Python数据结构与算法

笔记——Python数据结构与算法 一、栈和队列 1.1 栈的定义 栈、队列、双端队列和列表都是有序的数据集合, 其元素的顺序取决于添加顺序或移除顺序。一旦某个元素被添加进来,它与前后元素的相对位置将保持不变。这样的数据集合经常被称为线性数据结构。…

【信息系统项目管理师】计算题-2023年5月8日、9日总结

8日没做太多内容,主要做了计算题。关于上午计算专题,运筹学相关内容。 涉及计算题内容的确不少,风险,决策,加权,采购,沟通渠道,盈亏平衡点。 运筹学涉及最短路径,图论&am…

前端:20 个常见的前端算法题

现在面试中,算法出现的频率越来越高了,大厂基本必考 今天给大家带来 20 个常见的前端算法题,重要的地方已添加注释,如有不正确的地方,欢迎多多指正 💕 1、两数之和 题目: 给定一个数组 nums …

ISO 14229、ISO 15765、ISO 11898的区别

ISO 14229、ISO 15765、ISO 11898的区别 浅析ISO 14229、ISO 15765、ISO 11898协议的区别 ISO 14229、ISO 15765、ISO 11898的区别一、前言二、ISO 11898三、ISO 14229四、ISO 15765五、ISO 26262六、ISO 27145-4**总结:ISO 14229、ISO 15765 和 ISO 11898的区别&am…

logstash多conf问题--ELK文章4

现象 问题:配置多个conf后,所有的输入源都输入到所有的输出源中,并且filter中配置单个但是影响全局 Logstash的pipeline配置了多个个conf,也就是多个输入源。如果不做任何处理,那么所有的Filter,Output都会…

应急响应之系统排查方法

应急响应之系统排查方法 1.系统基本信息Windows系统排查Linux系统排查2.用户信息Windows用户排查Linux用户排查3.启动项Windows启动项排查Linux启动项排查4.计划任务Windows计划任务排查Linux计划任务排查1.系统基本信息 Windows系统排查 windows应急响应,使用微软自带的工具…

Android 音频开发——Radio简介(一)

一、概述 本地FM(收音机)应用不同于现在的网络FM(喜马拉雅,考拉等),是由 Android 手机硬件的调谐芯片,通过接收和解调电台发出的电磁波的调频(FM)或调幅(AM)信号,从而播放和展示相应的节目信息。所以本地收音机应用一般不需要联网,其核心依赖于硬件调谐芯片。所以…

OPC UA/DA协议库open62541的源码编译及案例测试

目录 一、下载源码 二、编译源码 三、案例编译 3.1 工程目录 3.2 server端 3.3 client端 3.4 程序运行 四、补充open62541库的linux编译 4.1 cmake版本升级 4.2 python3支持 4.3 open62541源码编译 一、下载源码 win10系统下,安装git工具,下载open62…

带货主播薪酬大降30%,洗牌潮来袭,直播带货红利是否会消退

近年来,随着直播带货市场的迅猛发展,各种直播平台如同春天的雨后蘑菇一般层出不穷。尤其是那些顶尖主播们,凭借着超强的吸金能力和惊人的收入,吸引了无数人的目光。 然而就在今年,这些主播们的薪酬出现了大幅下滑&…

基于springboot基于Java的超市进销存系统

开发技术与环境配置 以Java语言为开发工具,利用了当前先进的springboot框架,以MyEclipse10为系统开发工具,MySQL为后台数据库,开发的一个超市进销存系统。 SpringBoot框架 SpringBoot是一个全新开源的轻量级框架。基于Spring4.…

腾讯云轻量应用服务器使用限制(买前必看)

腾讯云轻量应用服务器和云服务器CVM相比具有一些限制,比如轻量服务器不支持更换内网IP地址,轻量服务器只能套餐整体升级且不支持降配,轻量不支持用户自定义配置私有网络VPC,还有如实例配额、云硬盘配额、备案限制和内网连通性等限…

【OpenCV-Python】——哈里斯/Shi-Tomas角检测FAST/SIFT/ORB特征点检测暴力/FLANN匹配器对象查找

目录 前言: 1、角检测 1.1 哈里斯角检测 1.2 优化哈里斯角 1.3 Shi-Tomasi角检测 2、特征点检测 2.1 FAST特征点检测 2.2 SIFT特征检测 2.3 ORB特征检测 3、特征匹配 3.1 暴力匹配器 3.2 FLANN匹配器 4、对象查找 总结: 前言: 图…

【实战】MySQL百万数据优化

业务场景 一般在项目开发中会有很多的统计数据需要进行上报分析,一般在分析过后会在后台展示出来给运营和产品进行「分页查看」,「最常见的一种就是根据日期进行筛选」。这种统计数据随着时间的推移数据量会慢慢的变大,达到百万、千万条数据只…

即时通讯APP开发费用成本多少?

移动互联网的发展,为人们的通讯交流提供了非常多的便利,一些即时通讯APP的出现,将人与人的距离再一次缩短。通过即时通讯APP软件,人们可以随时随地了解身边发生的新鲜事物,以及和朋友探讨各类趣事,甚至可以…

Kubernetes高可用集群

Kubernetes高可用集群 0、服务器信息 服务器为腾讯云服务器(按需计费,按流量计费,并且将IP转换为弹性IP,使用结束可关机仅收取硬盘等固定资源费用) 服务器名称IP描述Kuernetes-149.235.103.143master1Kuernetes-249…

黑熊“光临”美国小学,校长惊慌失措,转身就跑

最近,一则有趣的视频在网络上传播。一个平凡的清晨,美国西维吉尼亚州尼古拉斯县的萨拉小学校长马什遭遇了一次前所未有的惊险经历。 这位校长本想打开垃圾桶,却突然发现一头黑熊钻出了垃圾桶,这让他吓得跑得飞快。与此同时&#…

K8S通过Ansible安装集群

K8S通过Ansible安装集群 K8S集群安装可参考https://gitee.com/open-hand/kubeadm-ha.git、https://github.com/easzlab/kubeasz.git 安装高可用集群 git clone https://gitee.com/open-hand/kubeadm-ha.git && cd kubeadm-ha升级内核,非必需,默认不升级&…

二叉树原理

二叉树原理TOC 二叉树是公认的难解型数据结构,这里试着系统化的讨论一些与二叉树有关的内容: 二叉树的数据标记方法:层次数据(int t[N];),左右树标记(bool lr:0standForL, 1standForR)&#xf…

算法之路--冒泡排序算法

写在前面 很早就想系统梳理所接触的所有算法,但是只是存在于一个想法阶段,懒惰使人遗忘不是么。作为一个软件开发人员,绕不开算法与数据结构,既然绕不开,何不逐一分析学习透彻,与君共勉之。 冒泡排序算法&a…