经典目标检测YOLO系列(一)YOLOV1的复现(1)总体架构
经典目标检测YOLO系列(一)实现YOLOV1网络(1)总体架构
实现原版的YOLOv1并没有多大的意义,因此,根据《YOLO目标检测》(ISBN:9787115627094)
一书,在不脱离YOLOv1的大部分核心理念的前提下,重构一款较新的YOLOv1检测器,来对YOLOV1有更加深刻的认识。
书中源码连接:GitHub - yjh0410/RT-ODLab: YOLO Tutorial
对比原始YOLOV1网络,主要改进点如下:
- 将主干网络替换为ResNet-18
- 添加了后续YOLO中使用的neck,即SPPF模块
- 删除全连接层,修改为
检测头+预测层
的组合,并且使用普遍用在RetinaNet、FCOS、YOLOX等通用目标检测网络中的解耦检测头(Decoupled head)
- 修改损失函数,将YOLOV1原本的MSE loss,分类分支替换为BCE loss,回归分支替换为GIou loss。
我们按照现在目标检测的整体架构,来搭建自己的YOLOV1网络,概览图如下:
1、替换主干网络
- 使用ResNet-18作为主干网络(backbone),替换YOLOV1原版的GoogLeNet风格的主干网络。替换后,图片的下采样从64倍,降低为32倍。我们更改图像大小由原版中的448×448变为416×416。
- 这里不同于分类网络,我们去掉网络中的
平均池化层
以及分类层
。一张图片经过主干网络得到13×13×512的特征图,相对于原本输出的7×7网格,这里输出的网格要更加精细。
# RT-ODLab/models/detectors/yolov1/yolov1_backbone.pyimport torch
import torch.nn as nn
import torch.utils.model_zoo as model_zoo# 只会导入 __all__ 列出的成员,可以其他成员都被排除在外
__all__ = ['ResNet', 'resnet18', 'resnet34', 'resnet50', 'resnet101', 'resnet152']model_urls = {'resnet18': 'https://download.pytorch.org/models/resnet18-5c106cde.pth','resnet34': 'https://download.pytorch.org/models/resnet34-333f7ec4.pth','resnet50': 'https://download.pytorch.org/models/resnet50-19c8e357.pth','resnet101': 'https://download.pytorch.org/models/resnet101-5d3b4d8f.pth','resnet152': 'https://download.pytorch.org/models/resnet152-b121ed2d.pth',
}# --------------------- Basic Module -----------------------
def conv3x3(in_planes, out_planes, stride=1):"""3x3 convolution with padding"""return nn.Conv2d(in_planes, out_planes, kernel_size=3, stride=stride,padding=1, bias=False)def conv1x1(in_planes, out_planes, stride=1):"""1x1 convolution"""return nn.Conv2d(in_planes, out_planes, kernel_size=1, stride=stride, bias=False)class BasicBlock(nn.Module):expansion = 1def __init__(self, inplanes, planes, stride=1, downsample=None):super(BasicBlock, self).__init__()self.conv1 = conv3x3(inplanes, planes, stride)self.bn1 = nn.BatchNorm2d(planes)self.relu = nn.ReLU(inplace=True)self.conv2 = conv3x3(planes, planes)self.bn2 = nn.BatchNorm2d(planes)self.downsample = downsampleself.stride = stridedef forward(self, x):identity = xout = self.conv1(x)out = self.bn1(out)out = self.relu(out)out = self.conv2(out)out = self.bn2(out)if self.downsample is not None:identity = self.downsample(x)out += identityout = self.relu(out)return outclass Bottleneck(nn.Module):expansion = 4def __init__(self, inplanes, planes, stride=1, downsample=None):super(Bottleneck, self).__init__()self.conv1 = conv1x1(inplanes, planes)self.bn1 = nn.BatchNorm2d(planes)self.conv2 = conv3x3(planes, planes, stride)self.bn2 = nn.BatchNorm2d(planes)self.conv3 = conv1x1(planes, planes * self.expansion)self.bn3 = nn.BatchNorm2d(planes * self.expansion)self.relu = nn.ReLU(inplace=True)self.downsample = downsampleself.stride = stridedef forward(self, x):identity = xout = self.conv1(x)out = self.bn1(out)out = self.relu(out)out = self.conv2(out)out = self.bn2(out)out = self.relu(out)out = self.conv3(out)out = self.bn3(out)if self.downsample is not None:identity = self.downsample(x)out += identityout = self.relu(out)return out# --------------------- ResNet -----------------------
class ResNet(nn.Module):def __init__(self, block, layers, zero_init_residual=False):super(ResNet, self).__init__()self.inplanes = 64self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3,bias=False)self.bn1 = nn.BatchNorm2d(64)self.relu = nn.ReLU(inplace=True)self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)self.layer1 = self._make_layer(block, 64, layers[0])self.layer2 = self._make_layer(block, 128, layers[1], stride=2)self.layer3 = self._make_layer(block, 256, layers[2], stride=2)self.layer4 = self._make_layer(block, 512, layers[3], stride=2)for m in self.modules():if isinstance(m, nn.Conv2d):nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')elif isinstance(m, nn.BatchNorm2d):nn.init.constant_(m.weight, 1)nn.init.constant_(m.bias, 0)# Zero-initialize the last BN in each residual branch,# so that the residual branch starts with zeros, and each residual block behaves like an identity.# This improves the model by 0.2~0.3% according to https://arxiv.org/abs/1706.02677if zero_init_residual:for m in self.modules():if isinstance(m, Bottleneck):nn.init.constant_(m.bn3.weight, 0)elif isinstance(m, BasicBlock):nn.init.constant_(m.bn2.weight, 0)def _make_layer(self, block, planes, blocks, stride=1):downsample = Noneif stride != 1 or self.inplanes != planes * block.expansion:downsample = nn.Sequential(conv1x1(self.inplanes, planes * block.expansion, stride),nn.BatchNorm2d(planes * block.expansion),)layers = []layers.append(block(self.inplanes, planes, stride, downsample))self.inplanes = planes * block.expansionfor _ in range(1, blocks):layers.append(block(self.inplanes, planes))return nn.Sequential(*layers)def forward(self, x):"""Input:x: (Tensor) -> [B, C, H, W]Output:c5: (Tensor) -> [B, C, H/32, W/32]"""c1 = self.conv1(x) # [B, C, H/2, W/2]c1 = self.bn1(c1) # [B, C, H/2, W/2]c1 = self.relu(c1) # [B, C, H/2, W/2]c2 = self.maxpool(c1) # [B, C, H/4, W/4]c2 = self.layer1(c2) # [B, C, H/4, W/4]c3 = self.layer2(c2) # [B, C, H/8, W/8]c4 = self.layer3(c3) # [B, C, H/16, W/16]c5 = self.layer4(c4) # [B, C, H/32, W/32]return c5# --------------------- Fsnctions -----------------------
def resnet18(pretrained=False, **kwargs):"""Constructs a ResNet-18 model.Args:pretrained (bool): If True, returns a model pre-trained on ImageNet"""model = ResNet(BasicBlock, [2, 2, 2, 2], **kwargs)if pretrained:# strict = False as we don't need fc layer params.model.load_state_dict(model_zoo.load_url(model_urls['resnet18']), strict=False)return modeldef resnet34(pretrained=False, **kwargs):"""Constructs a ResNet-34 model.Args:pretrained (bool): If True, returns a model pre-trained on ImageNet"""model = ResNet(BasicBlock, [3, 4, 6, 3], **kwargs)if pretrained:model.load_state_dict(model_zoo.load_url(model_urls['resnet34']), strict=False)return modeldef resnet50(pretrained=False, **kwargs):"""Constructs a ResNet-50 model.Args:pretrained (bool): If True, returns a model pre-trained on ImageNet"""model = ResNet(Bottleneck, [3, 4, 6, 3], **kwargs)if pretrained:model.load_state_dict(model_zoo.load_url(model_urls['resnet50']), strict=False)return modeldef resnet101(pretrained=False, **kwargs):"""Constructs a ResNet-101 model.Args:pretrained (bool): If True, returns a model pre-trained on ImageNet"""model = ResNet(Bottleneck, [3, 4, 23, 3], **kwargs)if pretrained:model.load_state_dict(model_zoo.load_url(model_urls['resnet101']), strict=False)return modeldef resnet152(pretrained=False, **kwargs):"""Constructs a ResNet-152 model.Args:pretrained (bool): If True, returns a model pre-trained on ImageNet"""model = ResNet(Bottleneck, [3, 8, 36, 3], **kwargs)if pretrained:model.load_state_dict(model_zoo.load_url(model_urls['resnet152']))return model## build resnet
def build_backbone(model_name='resnet18', pretrained=False):if model_name == 'resnet18':model = resnet18(pretrained)feat_dim = 512elif model_name == 'resnet34':model = resnet34(pretrained)feat_dim = 512elif model_name == 'resnet50':model = resnet34(pretrained)feat_dim = 2048elif model_name == 'resnet101':model = resnet34(pretrained)feat_dim = 2048return model, feat_dimif __name__=='__main__':model, feat_dim = build_backbone(model_name='resnet18', pretrained=True)print(model)input = torch.randn(1, 3, 416, 416)print(model(input).shape)
2、添加neck
-
在原版的YOLOv1中,是没有Neck网络的概念的,但随着目标检测领域的发展,相关框架的成熟,一个通用目标检测网络结构可以被划分为Backbone、Neck、Head三大部分。当前的YOLO工作也符合这一设计。
-
因此,我们遵循当前主流的设计理念,为我们的YOLOv1添加一个Neck网络。这里,我们选择YOLOV5版本中所用的SPPF模块。
SPPF主要的代码如下:
# RT-ODLab/models/detectors/yolov1/yolov1_neck.pyimport torch
import torch.nn as nn
from .yolov1_basic import Conv# Spatial Pyramid Pooling - Fast (SPPF) layer for YOLOv5 by Glenn Jocher
class SPPF(nn.Module):"""This code referenced to https://github.com/ultralytics/yolov5"""def __init__(self, in_dim, out_dim, expand_ratio=0.5, pooling_size=5, act_type='lrelu', norm_type='BN'):super().__init__()inter_dim = int(in_dim * expand_ratio)self.out_dim = out_dim# 1×1卷积,通道降维self.cv1 = Conv(in_dim, inter_dim, k=1, act_type=act_type, norm_type=norm_type)# 1×1卷积,通道降维self.cv2 = Conv(inter_dim * 4, out_dim, k=1, act_type=act_type, norm_type=norm_type)# 5×5最大池化self.m = nn.MaxPool2d(kernel_size=pooling_size, stride=1, padding=pooling_size // 2)def forward(self, x):# 1、经过Conv-BN-LeakyReLUx = self.cv1(x)# 2、串行经过第1个5×5的最大池化y1 = self.m(x)# 3、串行经过第2个5×5的最大池化y2 = self.m(y1)# 4、串行经过第3个5×5的最大池化y3 = self.m(y2)# 5、将上面4个得到的结果concatconcat_y = torch.cat((x, y1, y2, y3), 1)# 6、再经过一个Conv-BN-LeakyReLUreturn self.cv2(concat_y)def build_neck(cfg, in_dim, out_dim):model = cfg['neck']print('==============================')print('Neck: {}'.format(model))# build neckif model == 'sppf':neck = SPPF(in_dim=in_dim,out_dim=out_dim,expand_ratio=cfg['expand_ratio'], pooling_size=cfg['pooling_size'],act_type=cfg['neck_act'],norm_type=cfg['neck_norm'])return neck
3、Detection Head网络
- 原版的YOLOv1采用了全连接层来完成最终的处理,即将此前卷积输出的二维 H×W 特征图拉平(flatten操作)成一维 HW 向量,然后接全连接层得到4096维的一维向量。
flatten操作会破坏特征的空间结构信息
,因此,我们抛掉这里的flatten操作,改用当下主流的基于卷积的检测头。- 具体来说,我们使用普遍用在RetinaNet、FCOS、YOLOX等通用目标检测网络中的
解耦检测头(Decoupled head)
,即使用两条并行的分支,分别用于提取类别特征和位置特征,两条分支都由卷积层构成。 - 下图展示了我们所采用的Decoupled head以及后面的预测层结构。
# RT-ODLab/models/detectors/yolov1/yolov1_head.pyimport torch
import torch.nn as nn
try:from .yolov1_basic import Conv
except:from yolov1_basic import Convclass DecoupledHead(nn.Module):def __init__(self, cfg, in_dim, out_dim, num_classes=80):super().__init__()print('==============================')print('Head: Decoupled Head')self.in_dim = in_dimself.num_cls_head=cfg['num_cls_head']self.num_reg_head=cfg['num_reg_head']self.act_type=cfg['head_act']self.norm_type=cfg['head_norm']# cls headcls_feats = []self.cls_out_dim = max(out_dim, num_classes)for i in range(cfg['num_cls_head']):if i == 0:cls_feats.append(Conv(in_dim, self.cls_out_dim, k=3, p=1, s=1, act_type=self.act_type,norm_type=self.norm_type,depthwise=cfg['head_depthwise']))else:cls_feats.append(Conv(self.cls_out_dim, self.cls_out_dim, k=3, p=1, s=1, act_type=self.act_type,norm_type=self.norm_type,depthwise=cfg['head_depthwise']))# reg headreg_feats = []self.reg_out_dim = max(out_dim, 64)for i in range(cfg['num_reg_head']):if i == 0:reg_feats.append(Conv(in_dim, self.reg_out_dim, k=3, p=1, s=1, act_type=self.act_type,norm_type=self.norm_type,depthwise=cfg['head_depthwise']))else:reg_feats.append(Conv(self.reg_out_dim, self.reg_out_dim, k=3, p=1, s=1, act_type=self.act_type,norm_type=self.norm_type,depthwise=cfg['head_depthwise']))self.cls_feats = nn.Sequential(*cls_feats)self.reg_feats = nn.Sequential(*reg_feats)def forward(self, x):"""in_feats: (Tensor) [B, C, H, W]"""cls_feats = self.cls_feats(x)reg_feats = self.reg_feats(x)return cls_feats, reg_feats# build detection head
def build_head(cfg, in_dim, out_dim, num_classes=80):head = DecoupledHead(cfg, in_dim, out_dim, num_classes) return head
4、预测层
检测头最后部分的预测层(Prediction layer)
也要做相应的修改。
我们只要求YOLOv1在每一个网格只需要预测一个bbox
,而非两个甚至更多的bbox。尽管原版的YOLOv1在每个网格预测两个bbox,但在推理阶段,每个网格最终只输出一个bbox,从结果上来看,这和每个网格只预测一个bbox是一样的。objectness的预测
。我们在Decoupled head的类别分支的输出后面接一层1x1卷积去做objectness的预测,并在最后使用Sigmoid函数来输出objectness预测值。classificaton预测
。遵循当下YOLO系列常用的方法,我们同样采用Sigmoid函数来输出classification预测值,即每个类别的置信度都是0~1。classificaton预测和objectness预测都采用Decoupled的同一分支。bbox regression预测
。另一分支的位置特征就被用于预测边界框的偏移量,即我们使用另一层1x1卷积去处理检测头的位置特征,得到每个网格的边界框的偏移量预测。
## 预测层
self.obj_pred = nn.Conv2d(head_dim, 1, kernel_size=1)
self.cls_pred = nn.Conv2d(head_dim, num_classes, kernel_size=1)
self.reg_pred = nn.Conv2d(head_dim, 4, kernel_size=1)
5、改进YOLO的详细网络图
- 不同于原版的YOLOv1,我们在不偏离原版的大部分核心理念的前提下,做了更加符合当下的设计理念的修改,包括使用更好的Backbone、添加Neck模块、修改检测头等。
- 改进YOLO的详细网络图如下
# RT-ODLab/models/detectors/yolov1/yolov1.pyimport torch
import torch.nn as nn
import numpy as npfrom utils.misc import multiclass_nmsfrom .yolov1_backbone import build_backbone
from .yolov1_neck import build_neck
from .yolov1_head import build_head# YOLOv1
class YOLOv1(nn.Module):def __init__(self,cfg,device,img_size=None,num_classes=20,conf_thresh=0.01,nms_thresh=0.5,trainable=False,deploy=False,nms_class_agnostic :bool = False):super(YOLOv1, self).__init__()# ------------------------- 基础参数 ---------------------------self.cfg = cfg # 模型配置文件self.img_size = img_size # 输入图像大小self.device = device # cuda或者是cpuself.num_classes = num_classes # 类别的数量self.trainable = trainable # 训练的标记self.conf_thresh = conf_thresh # 得分阈值self.nms_thresh = nms_thresh # NMS阈值self.stride = 32 # 网络的最大步长self.deploy = deployself.nms_class_agnostic = nms_class_agnostic# ----------------------- 模型网络结构 -------------------------## 主干网络self.backbone, feat_dim = build_backbone(cfg['backbone'],trainable & cfg['pretrained'])## 颈部网络self.neck = build_neck(cfg, feat_dim, out_dim=512)head_dim = self.neck.out_dim## 检测头self.head = build_head(cfg, head_dim, head_dim, num_classes)## 预测层self.obj_pred = nn.Conv2d(head_dim, 1, kernel_size=1)self.cls_pred = nn.Conv2d(head_dim, num_classes, kernel_size=1)self.reg_pred = nn.Conv2d(head_dim, 4, kernel_size=1)def create_grid(self, fmp_size):""" 用于生成G矩阵,其中每个元素都是特征图上的像素坐标。"""passdef decode_boxes(self, pred, fmp_size):"""将txtytwth转换为常用的x1y1x2y2形式。pred:回归预测参数fmp_size:特征图宽和高"""passdef postprocess(self, bboxes, scores):"""后处理代码,包括阈值筛选和非极大值抑制1、滤掉低得分(边界框的score低于给定的阈值)的预测边界框;2、滤掉那些针对同一目标的冗余检测。Input:bboxes: [HxW, 4]scores: [HxW, num_classes]Output:bboxes: [N, 4]score: [N,]labels: [N,]"""pass@torch.no_grad()def inference(self, x):# 测试阶段的前向推理代码# 主干网络feat = self.backbone(x)# 颈部网络feat = self.neck(feat)# 检测头cls_feat, reg_feat = self.head(feat)# 预测层obj_pred = self.obj_pred(cls_feat)cls_pred = self.cls_pred(cls_feat)reg_pred = self.reg_pred(reg_feat)fmp_size = obj_pred.shape[-2:]# 对 pred 的size做一些view调整,便于后续的处理# [B, C, H, W] -> [B, H, W, C] -> [B, H*W, C]obj_pred = obj_pred.permute(0, 2, 3, 1).contiguous().flatten(1, 2)cls_pred = cls_pred.permute(0, 2, 3, 1).contiguous().flatten(1, 2)reg_pred = reg_pred.permute(0, 2, 3, 1).contiguous().flatten(1, 2)# 测试时,笔者默认batch是1,# 因此,我们不需要用batch这个维度,用[0]将其取走。obj_pred = obj_pred[0] # [H*W, 1]cls_pred = cls_pred[0] # [H*W, NC]reg_pred = reg_pred[0] # [H*W, 4]# 每个边界框的得分scores = torch.sqrt(obj_pred.sigmoid() * cls_pred.sigmoid())# 解算边界框, 并归一化边界框: [H*W, 4]bboxes = self.decode_boxes(reg_pred, fmp_size)if self.deploy:# [n_anchors_all, 4 + C]outputs = torch.cat([bboxes, scores], dim=-1)return outputselse:# 将预测放在cpu处理上,以便进行后处理scores = scores.cpu().numpy()bboxes = bboxes.cpu().numpy()# 后处理bboxes, scores, labels = self.postprocess(bboxes, scores)return bboxes, scores, labelsdef forward(self, x):# 训练阶段的前向推理代码if not self.trainable:return self.inference(x)else:# 主干网络feat = self.backbone(x)# 颈部网络feat = self.neck(feat)# 检测头cls_feat, reg_feat = self.head(feat)# 预测层obj_pred = self.obj_pred(cls_feat)cls_pred = self.cls_pred(cls_feat)reg_pred = self.reg_pred(reg_feat)fmp_size = obj_pred.shape[-2:]# 对 pred 的size做一些view调整,便于后续的处理# [B, C, H, W] -> [B, H, W, C] -> [B, H*W, C]obj_pred = obj_pred.permute(0, 2, 3, 1).contiguous().flatten(1, 2)cls_pred = cls_pred.permute(0, 2, 3, 1).contiguous().flatten(1, 2)reg_pred = reg_pred.permute(0, 2, 3, 1).contiguous().flatten(1, 2)# decode bboxbox_pred = self.decode_boxes(reg_pred, fmp_size)# 网络输出outputs = {"pred_obj": obj_pred, # (Tensor) [B, M, 1]"pred_cls": cls_pred, # (Tensor) [B, M, C]"pred_box": box_pred, # (Tensor) [B, M, 4]"stride": self.stride, # (Int)"fmp_size": fmp_size # (List) [fmp_h, fmp_w]} return outputs
到此,我们完成了YOLOv1网络的搭建,并且实现了前向推理。但是,在推理的代码中还遗留了几个重要的问题尚待处理:
- 如何从边界框偏移量reg_pred解耦出边界框坐标box_pred?
- 如何实现后处理操作?
- 如何计算训练阶段的损失?
当然还有数据集的加载,数据集如何增强,如何选择正样本进行训练等内容。
相关文章:
经典目标检测YOLO系列(一)YOLOV1的复现(1)总体架构
经典目标检测YOLO系列(一)实现YOLOV1网络(1)总体架构 实现原版的YOLOv1并没有多大的意义,因此,根据《YOLO目标检测》(ISBN:9787115627094)一书,在不脱离YOLOv1的大部分核心理念的前提下,重构一款较新的YOLOv1检测器,来…...
《设计模式》之策略模式
策略模式定义 比如对象的某个行为,在不同场景有不同实现方式,可以将这些行为的具体实现定义为一组策略,每个实现类实现种策略,在不同场景使用不同的实现,并且可以自由切换策略。 策略模式结构 策略模式需要一个策略…...
Django文章标签推荐
当博客文章实现了标签后,可以用它们做很多有趣的事情。关于标签的更多内容,请看 Django集成第三方标签功能-CSDN博客 使用标签,我们可以很好地对博客文章进行分类。类似主题的帖子会有几个共同的标签。下一步将构建一个功能,以显…...
Git、TortoiseGit进阶
1.安装Git、TortoiseGit和汉化包 Git官网: Git TortoiseGit和汉化包: Download – TortoiseGit – Windows Shell Interface to Git 2.常用命令 创建仓库命令 git init初始化仓库git clone拷贝一份远程仓库,也就是下载一个项目。提交与修改 git add添加文件到暂存区git…...
山区老人爱的礼物丨守护银龄,情暖寒冬
为让山区老人们在寒冷的冬天感受到来自社会的温暖,新年伊始,北京传益千里携手志愿者再次走进酉阳土家族苗族自治县木叶乡分发新的一轮山区老人爱的礼物,让更多的物资走向有需要的人群。 中午阳光正好,志愿者们走进山林中的人家&am…...
【计算机算法设计与分析】n皇后问题(C++_回溯法)
文章目录 题目描述测试样例算法原理算法实现参考资料 题目描述 在nxn格的棋盘上放置彼此不受攻击的n格皇后。按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。n后问题等价于在nxn格的棋盘上放置n个皇后,任何2个皇后不放在同…...
Calendar日历类型常见方法
Calendar日历类型常见方法: 概括:1.get( )方法2、set( ) 设置时间3、常用的add方法4、after()方法表示的时间是否在指定时间之后, before( ) 方法则之前, 返回判断结果4.1、compareTo比较器 概括: Calendar类是一个抽…...
Docker-Compose部署Redis(v7.2)主从模式
文章目录 一、前提准备1. redis配置文件2. 下载redis镜像3. 文件夹结构 二、docker-compose三、主从配置1.主节点配置文件2.从节点配置文件 四、运行五、测试 环境 docker desktop for windows 4.23.0redis 7.2 一、前提准备 1. redis配置文件 因为Redis 7.2 docker镜像里面…...
Spring国际化的应用及原理详解
1. 简介 Spring国际化(Spring Internationalization,简称i18n)是Spring框架提供的一种机制,用于支持多语言的应用程序。它使得开发者能够轻松地在应用程序中实现不同语言的支持,从而满足全球化的需求。通过Spring国际…...
Existing installation is up to date
这个报错是之前安装的docker没有删除干净 解决方法: 打开注册表编辑器 然后再搜索栏:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Docker Desktop 回车 找到Docker Desktop文件夹后,右键删除 重新安装Docker…...
windows安装kafka以及kafka管理工具推荐
windows安装 1.下载地址 下载地址 下载最新版本的.tgz文件解压 2.修改配置 修改config目录下的zookeeper.properties中的dataDir属性 server.properties文件中的log.dir属性 3.启动zookeeper 进入到bin\windows\下的用cmd输入zookeeper-server-start.bat ..\..\config\zo…...
面向对象的三大特征之一多态
多态 概念 多态是同一个对象,在不同时刻表现出来不同的形态,称之为多态。 例如:水,我们把水理解成为一个对象,而水会有不同的形态,比如 液态水、冰块、水蒸气 多态的前提 有继承/实现关系(继承…...
vue3中标签form插件
想写一个系统,对八字进行标注,比如格局,有些八字就有很多格局,于是就想着使用el-tag但是,form表单中如何处理呢? 这个时候,就需要自己写一个,modelValue是表单的默认属性 <template><…...
企业数字化转型:1个核心、2种力量、3个关键点、4大转型、5大平台
引言 企业数字化转型源于当今数字化时代的巨大变革。随着科技的飞速发展和全球市场的日益竞争,企业们正面临着前所未有的挑战和机遇。这些挑战包括消费者行为的变化、新技术的涌现以及市场竞争的加剧。在这种环境下,传统的商业模式和运营方式已经不再适…...
Agilent安捷伦E4990A阻抗分析仪20Hz
Agilent安捷伦E4990A阻抗分析仪性能卓越,适用于元器件、半导体和材料测量。它具有宽广的频率范围,从20Hz到120MHz,能够适应各种不同的阻抗测量需求。在宽阻抗范围内,该仪器能够提供出色的0.045%(典型值)基本…...
性能优化-OpenMP概述(一)-宏观全面理解OpenMP
本文旨在从宏观角度来介绍OpenMP的原理、编程模型、以及在各个领域的应用、使用、希望读者能够从本文整体上了解OpenMP。 🎬个人简介:一个全栈工程师的升级之路! 📋个人专栏:高性能(HPC)开发基础…...
Prometheus实战篇:Prometheus监控nginx
准备环境 在此专栏的前几篇文章中已经准备了一台服务器作为我们进行环境的准备.大家也可以通过虚拟机创建俩台服务器,一台作为Prometheus的安装另外一台进行其他软件安装并且进行监控的服务器. 这里我就不赘述nginx的安装教程,相信大家都可以搜到,使用docker或者直接通过安装包…...
JVM加载class文件的原理机制
1、JVM 简介 JVM 是我们Javaer 的最基本功底了,刚开始学Java 的时候,一般都是从“Hello World ”开始的,然后会写个复杂点class ,然后再找一些开源框架,比如Spring ,Hibernate 等等,再然后就开发…...
如何使用CapSolver解决Web爬虫中遇到的CAPTCHA问题
Web爬取是一种强大的技术,用于从网站中提取数据,但经常会遇到一个常见障碍,即CAPTCHA。CAPTCHA是“Completely Automated Public Turing test to tell Computers and Humans Apart”的缩写,旨在防止自动机器人访问网站。然而&…...
杰发科技AC7801——IO模拟IIC注意事项
7801的参考手册没有说清楚 7840说明了用开漏 使用办法...
展台搭建与设计都有哪些思路
1、现代简约 设计理念强调简洁、线条清晰和空间布局,突出产品本身,使展台干净整洁,适合展示高科技、现代化的产品。 2、自然生态 利用植物、木材等自然元素,营造与自然和谐共处的氛围,适合健康、环保、生态产品。 3、品…...
解决mock单元测试中 无法获取实体类xxx对应的表名
错误描述:在执行单元测试时,执行到new Example时抛出异常,提示无法获取实体类xxx对应的表名 Example example new Example(ServeSubscribeRecord.class);Example.Criteria criteria example.createCriteria();criteria.andEqualTo("se…...
arm64虚拟化技术与kvm实现原理分享
文章目录 1 简介2 arm64 虚拟化相关硬件支持2.1 arm64 cpu 虚拟化基本原理及硬件支持2.2 系统寄存器捕获和虚拟寄存器支持2.3 VHE 特性支持2.4 内存虚拟化支持2.5 IO 虚拟化支持2.6 DMA 虚拟化支持2.7 中断虚拟化支持2.8 定时器虚拟化支持 3 arm64 kvm 初始化流程3.1 初始化总体…...
选择 省市区 组件数据 基于vue3 + elment-plus
h5 <el-cascader v-model"form.area" :props"{value: label,label: label }" :options"jsonData" change"handleChange" style"width: 100%;" /> script import jsonData from /utils/city.json; 选完省市区 数据是一…...
了解 nextTick
一. 什么是 nextTick 简单的说,nextTick 方法是在 Vue.js 中常见的一种异步更新 DOM 的机制。它的原理是利用 JavaScript 的事件循环机制以及浏览器的渲染流程来实现延迟执行 DOM 更新操作。 它的出现主要是为了解决 Vue 的异步更新导致的 DOM 更新后的操作问题。…...
C++精进之路(十六)string类和标准模板库
C提供了一组功能强大的库,这些库提供了很多常⻅编程问题的解决方案以及简化其他问题的工具。 string 类为将字符串作为对象来处理提供了一种方便的方法。string 类提供了自动内存管理功能以及众多处 理字符串的方法和函数。例如,这些方法和函数让您能够合…...
【23.12.29期--Redis缓存篇】谈一谈Redis的集群模式
谈一谈Redis的集群模式 ✔️ 谈一谈Redis的集群模式✔️主从模式✔️ 特点✔️Redis主从模式Demo ✔️哨兵模式✔️Redis哨兵模式Demo✔️特点 ✔️Cluster模式✔️Redis Cluster模式Demo✔️特点 ✔️ 谈一谈Redis的集群模式 Redis有三种主要的集群模式,用于在分布…...
【算法挨揍日记】day34——647. 回文子串、5. 最长回文子串
647. 回文子串 647. 回文子串 题目描述: 给你一个字符串 s ,请你统计并返回这个字符串中 回文子串 的数目。 回文字符串 是正着读和倒过来读一样的字符串。 子字符串 是字符串中的由连续字符组成的一个序列。 具有不同开始位置或结束位置的子串&am…...
欧科云链研究院:奔赴2024,Web3与AI共振引爆数字时代潘多拉魔盒
出品|欧科云链研究院 2024年,Web3与AI两个数字科技的巅峰碰撞,欧科云链研究院探索AI与Web3的技术融合,与澎湃科技联合发布2024年展望,原标题为《2024年展望:Web3与AI共振引爆可信数字社会》,共…...
【Py/Java/C++三种语言OD2023C卷真题】20天拿下华为OD笔试之【数学】2023C-素数之积【欧弟算法】全网注释最详细分类最全的华为OD真题题解
文章目录 题目描述与示例题目描述输入描述输出描述示例输入输出说明 解题思路暴力解质数筛 代码PythonJavaC时空复杂度 华为OD算法/大厂面试高频题算法练习冲刺训练 题目描述与示例 题目描述 RSA加密算法在网络安全世界中无处不在,它利用了极大些数因数分解的闲难…...
自己做社交网站/免费网站服务器安全软件下载
项目一 小学生测验 16学时问题描述:面向小学1~2年级学生,随机选择两个整数的加减法形成算式要求学生解答。1.基本要求(1)电脑随机出10道题,每题10分,程序结束时显示学生得分;(2)确保算式没有超出1~…...
商城网站怎么优化/最近时事热点新闻评论及点评
[TOC]Android RecyclerView 二级列表实现2017.5.16 添加demogit简述在开发 Android APP 的时候,难免会需要实现二级列表的情况,而在自己的项目中使用的列表是 android.support.v7.widget 包里面的 RecyclerView,好处是可以根据情况实现不同样…...
密云住房和城乡建设部网站首页/培训机构哪家好
这些题目都是大一刚入学时学习C语言的课后作业,在OJ上看到还保留着,就都整理下发出来吧......(只有题和代码)【问题描述】编写程序,打开一篇英文文章(存在当前目录下的文件in.txt中),为该文章生成词汇表(存到当前目录下的另一个文…...
table做的电脑端网站改成手机板/南宁优化网站网络服务
路由分为静态路由和动态路由,其相应的路由表称为静态路由表和动态路由表。静态路由表由网络管理员在系统安装时根据网络的配置情况预先设定,网络结构发生变化后由网络管理员手工修改路由表。动态路由随网络运行情况的变化而变化,路由器根据路…...
南京便宜网站建设/百度关键词推广工具
好,能跑起来就说明两个问题,一,加载器成功了,二,我没蒙错。接下来就开始来点稍干的了,先看代码public function onRequest($request,$response){$response->end("hello");}这个 onRequest 是H…...
建网站公司是如何赚钱/设计个人网站
针对这个问题我们可以转化一下思路:一个JVM进程,在什么情况下会正常退出? 大部分人应该都知道使用System.exit()或Runtime.exit()可以直接导致当前JVM进程退出,但是仔细想想这个好像跟SpringBoot没啥关系哈 另外一个可能会导致进…...