经典目标检测YOLO系列(一)复现YOLOV1(5)模型的训练及验证

经典目标检测YOLO系列(一)复现YOLOV1(5)模型的训练及验证

之前,我们依据《YOLO目标检测》(ISBN:9787115627094)一书,提出了新的YOLOV1架构,继续按照此书进行YOLOV1的复现。
经典目标检测YOLO系列(一)YOLOV1的复现(1)总体架构

经典目标检测YOLO系列(一)复现YOLOV1(2)反解边界框及后处理

经典目标检测YOLO系列(一)复现YOLOV1(3)正样本的匹配及损失函数的实现

经典目标检测YOLO系列(一)复现YOLOV1(4)VOC2007数据集的读取及预处理

1 YOLOV1模型的训练

1.1 Yolov8Trainer类

  • 作者为了代码具有更好的复用性,参考一些开源的工作单独实现了若干个训练所用的Trainer类。YOLOv1训练使用的是engine.py下的Yolov8Trainer类。初始化代码如下所示:
  • 初始化代码中,关键的超参数配置都参考了YOLOv5和YOLOv8的项目代码:
    • 优化器使用SGD,其中的momentum参数设置为0.937,weight decay设置为5e-4;
    • 使用梯度累加的策略。当设定的batch size小于64时,会自动计算梯度累加的次数,比如batch size=16时,会累加4次梯度来近似batch size=64的效果;
    • 初始学习率设置为0.01,并采用线性梯度衰减策略;
    • 使用当前YOLO系列很流行的Model EMA(指数移动平均)策略。
      • 指数移动平均(exponential moving average),也叫做权重移动平均(weighted moving average),
        可以用来估计变量的局部均值,使得变量的更新与一段时间内的历史取值有关。
      • 在采用 SGD 或者其他的一些优化算法 (Adam, Momentum) 训练神经网络时,通常会使用EMA的方法。 它的意义在于利用滑动平均的参数来提高模型在测试数据上的健壮性。
      • 简单来讲就是,通过在模型学习过程中融合更多的历史状态,从而达到更好的优化效果。
      • 【炼丹技巧】指数移动平均(EMA)的原理及PyTorch实现 - 知乎 (zhihu.com)
# RT-ODLab/engine.py# YOLOv8 Trainer
class Yolov8Trainer(object):def __init__(self, args, data_cfg, model_cfg, trans_cfg, device, model, criterion, world_size):# ------------------- basic parameters -------------------self.args = argsself.epoch = 0self.best_map = -1.self.device = deviceself.criterion = criterionself.world_size = world_sizeself.heavy_eval = Falseself.last_opt_step = 0self.clip_grad = 10# weak augmentatino stageself.second_stage = Falseself.third_stage = Falseself.second_stage_epoch = args.no_aug_epochself.third_stage_epoch = args.no_aug_epoch // 2# path to save modelself.path_to_save = os.path.join(args.save_folder, args.dataset, args.model)os.makedirs(self.path_to_save, exist_ok=True)# ---------------------------- Hyperparameters refer to YOLOv8 ----------------------------self.optimizer_dict = {'optimizer': 'sgd', 'momentum': 0.937, 'weight_decay': 5e-4, 'lr0': 0.01}self.ema_dict = {'ema_decay': 0.9999, 'ema_tau': 2000}self.lr_schedule_dict = {'scheduler': 'linear', 'lrf': 0.01}self.warmup_dict = {'warmup_momentum': 0.8, 'warmup_bias_lr': 0.1}        # ---------------------------- Build Dataset & Model & Trans. Config ----------------------------self.data_cfg = data_cfgself.model_cfg = model_cfgself.trans_cfg = trans_cfg# ---------------------------- Build Transform ----------------------------self.train_transform, self.trans_cfg = build_transform(args=args, trans_config=self.trans_cfg, max_stride=model_cfg['max_stride'], is_train=True)self.val_transform, _ = build_transform(args=args, trans_config=self.trans_cfg, max_stride=model_cfg['max_stride'], is_train=False)# ---------------------------- Build Dataset & Dataloader ----------------------------self.dataset, self.dataset_info = build_dataset(self.args, self.data_cfg, self.trans_cfg, self.train_transform, is_train=True)self.train_loader = build_dataloader(self.args, self.dataset, self.args.batch_size // self.world_size, CollateFunc())# ---------------------------- Build Evaluator ----------------------------self.evaluator = build_evluator(self.args, self.data_cfg, self.val_transform, self.device)# ---------------------------- Build Grad. Scaler ----------------------------self.scaler = torch.cuda.amp.GradScaler(enabled=self.args.fp16)# ---------------------------- Build Optimizer ----------------------------accumulate = max(1, round(64 / self.args.batch_size))print('Grad Accumulate: {}'.format(accumulate))self.optimizer_dict['weight_decay'] *= self.args.batch_size * accumulate / 64self.optimizer, self.start_epoch = build_yolo_optimizer(self.optimizer_dict, model, self.args.resume)# ---------------------------- Build LR Scheduler ----------------------------self.lr_scheduler, self.lf = build_lr_scheduler(self.lr_schedule_dict, self.optimizer, self.args.max_epoch)self.lr_scheduler.last_epoch = self.start_epoch - 1  # do not moveif self.args.resume and self.args.resume != 'None':self.lr_scheduler.step()# ---------------------------- Build Model-EMA ----------------------------if self.args.ema and distributed_utils.get_rank() in [-1, 0]:print('Build ModelEMA ...')self.model_ema = ModelEMA(self.ema_dict, model, self.start_epoch * len(self.train_loader))else:self.model_ema = None

1.1.1 Yolov8Trainer的train函数

训练分为三个阶段:

  • 在第一阶段,我们采用设定好的数据增强手段来处理输出,并去训练模型,这一阶段会很长,几乎占据了训练的绝大部分的时间开销。
    • 比如设定总的epoch=300,从0~280 epoch都是第一训练阶段;
  • 在第二阶段,我们会关闭【马赛克增强】和【混合增强】,因为这两个数据增强会产生很多几乎不符合真实图像的数据,模型可能会从中学习到一些不合理的特征。
    • 这一阶段会很短,比如只有10个epoch,仍以epoch=300的设定为例,从280~290epoch是第二阶段
    • 这一策略是借鉴于YOLOX工作,不过不同于YOLOX的是,在第二阶段,我们虽然关闭了两个强大的增强,但仍旧【保留了较弱的空间扰动和颜色扰动的两个增强】;
  • 在第三阶段,即从第二阶段结束开始,一直到整个训练结束
    • 这个阶段也很短,仍以epoch=300的设定为例,从290~300 epoch是第三阶段
    • 在这一阶段中,我们【关闭所有的数据增强】,只使用Resize和Normalize操作。
    • Note:
      不过,就我们所训练的YOLOv1而言,我们不会用到马赛克增强和混合增强,所以第二阶段和第一阶段是一样的,只有到了第三阶段去关闭所有的数据增强。
# RT-ODLab/engine.pydef train(self, model):for epoch in range(self.start_epoch, self.args.max_epoch):if self.args.distributed:self.train_loader.batch_sampler.sampler.set_epoch(epoch)# check second stageif epoch >= (self.args.max_epoch - self.second_stage_epoch - 1) and not self.second_stage:self.check_second_stage()  # 关闭【马赛克增强】和【混合增强】# save model of the last mosaic epochweight_name = '{}_last_mosaic_epoch.pth'.format(self.args.model)checkpoint_path = os.path.join(self.path_to_save, weight_name)print('Saving state of the last Mosaic epoch-{}.'.format(self.epoch))torch.save({'model': model.state_dict(),'mAP': round(self.evaluator.map*100, 1),'optimizer': self.optimizer.state_dict(),'epoch': self.epoch,'args': self.args}, checkpoint_path)# check third stageif epoch >= (self.args.max_epoch - self.third_stage_epoch - 1) and not self.third_stage:self.check_third_stage()   # 【关闭所有的数据增强】# save model of the last mosaic epochweight_name = '{}_last_weak_augment_epoch.pth'.format(self.args.model)checkpoint_path = os.path.join(self.path_to_save, weight_name)print('Saving state of the last weak augment epoch-{}.'.format(self.epoch))torch.save({'model': model.state_dict(),'mAP': round(self.evaluator.map*100, 1),'optimizer': self.optimizer.state_dict(),'epoch': self.epoch,'args': self.args}, checkpoint_path)# train one epochself.epoch = epochself.train_one_epoch(model)# eval one epochif self.heavy_eval:model_eval = model.module if self.args.distributed else modelself.eval(model_eval)else:model_eval = model.module if self.args.distributed else modelif (epoch % self.args.eval_epoch) == 0 or (epoch == self.args.max_epoch - 1):self.eval(model_eval)if self.args.debug:print("For debug mode, we only train 1 epoch")break

1.1.2 Yolov8Trainer的train_one_epoch函数

 # RT-ODLab/engine.pydef train_one_epoch(self, model):# basic parametersepoch_size = len(self.train_loader)img_size = self.args.img_sizet0 = time.time()nw = epoch_size * self.args.wp_epochaccumulate = max(1, round(64 / self.args.batch_size))# train one epochfor iter_i, (images, targets) in enumerate(self.train_loader):ni = iter_i + self.epoch * epoch_size# Warmup"""在第一个epoch阶段,会有一段Warmup的过程,即缓慢的将所有的weight的学习率从0线性增加至设定的初始学习率0.01,而所有的bias的学习率则从0.1线性降低至设定的初始学习率0.01,并且,SGD优化器的中的momentum参数也会从默认的warmup_momentum线性增加至设定的0.937。这一warmup策略是遵循了YOLOv5的设定,默认会持续3个epoch。"""if ni <= nw:xi = [0, nw]  # x interpaccumulate = max(1, np.interp(ni, xi, [1, 64 / self.args.batch_size]).round())for j, x in enumerate(self.optimizer.param_groups):# bias lr falls from 0.1 to lr0, all other lrs rise from 0.0 to lr0x['lr'] = np.interp(ni, xi, [self.warmup_dict['warmup_bias_lr'] if j == 0 else 0.0, x['initial_lr'] * self.lf(self.epoch)])if 'momentum' in x:x['momentum'] = np.interp(ni, xi, [self.warmup_dict['warmup_momentum'], self.optimizer_dict['momentum']])# to device# 随后,我们将读取进来的数据放置制定的设备上,比如GPU,并对输入的图像做简单的归一化(除以255)# 注意,这种归一化等价于设定均值和方差为别为0和255。# 之所以这么做是因为均值和方差往往依赖于任务场景的数据,通常不具备泛化性,所以我们不采用统计均值和方差的方法。images = images.to(self.device, non_blocking=True).float() / 255.# Multi scale# 完成这一步后,我们就可以调用类内的rescale_image_targets函数对这批图像做多尺度处理的操作# 即随机将这批图像Resize成其他的尺寸,比如都从640x640调整成512x512# 最后用类内的refine_targets 函数滤出掉无效的目标if self.args.multi_scale:images, targets, img_size = self.rescale_image_targets(images, targets, self.model_cfg['stride'], self.args.min_box_size, self.model_cfg['multi_scale'])else:targets = self.refine_targets(targets, self.args.min_box_size)# visualize train targetsif self.args.vis_tgt:vis_data(images*255, targets)# 然后,我们就可以将这片数据去交给模型做训练阶段的推理(没有后处理阶段),并做标签分配和损失计算,# 当迭代次数达到了梯度累加指定的次数后,就可以反向传播梯度和更新模型的参数。# inferencewith torch.cuda.amp.autocast(enabled=self.args.fp16):# 调用模型获取训练阶段的推理结果outputs = model(images)# loss 标签分配及计算损失loss_dict = self.criterion(outputs=outputs, targets=targets, epoch=self.epoch)losses = loss_dict['losses']losses *= images.shape[0]  # loss * bs# reduce            loss_dict_reduced = distributed_utils.reduce_dict(loss_dict)# gradient averaged between devices in DDP modelosses *= distributed_utils.get_world_size()# backwardself.scaler.scale(losses).backward()# Optimizeif ni - self.last_opt_step >= accumulate:if self.clip_grad > 0:# unscale gradientsself.scaler.unscale_(self.optimizer)# clip gradientstorch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=self.clip_grad)# optimizer.stepself.scaler.step(self.optimizer)self.scaler.update()self.optimizer.zero_grad()# emaif self.model_ema is not None:self.model_ema.update(model)self.last_opt_step = ni# displayif distributed_utils.is_main_process() and iter_i % 10 == 0:t1 = time.time()cur_lr = [param_group['lr']  for param_group in self.optimizer.param_groups]# basic inforlog =  '[Epoch: {}/{}]'.format(self.epoch, self.args.max_epoch)log += '[Iter: {}/{}]'.format(iter_i, epoch_size)log += '[lr: {:.6f}]'.format(cur_lr[2])# loss inforfor k in loss_dict_reduced.keys():log += '[{}: {:.2f}]'.format(k, loss_dict_reduced[k])# other inforlog += '[time: {:.2f}]'.format(t1 - t0)log += '[size: {}]'.format(img_size)# print log inforprint(log, flush=True)t0 = time.time()if self.args.debug:print("For debug mode, we only train 1 iteration")breakself.lr_scheduler.step()

1.1.3 开始训练

训练的主程序代码在train.py文件中,进入项目文件,可以执行下面的脚本进行训练:

nohup python -u train.py        \--cuda -d voc      \-m yolov1          \-bs 32             \-size 640          \--wp_epoch 3       \--max_epoch 150    \--eval_epoch 10    \--no_aug_epoch 10  \--ema              \--fp16             \--multi_scale      \--num_workers 8 1>./logs/yolo_v1_train_log.txt 2>./logs/yolo_v1_warning_log.txt &

命令行参数的意义:

  • --cuda表示我们调用GPU来训练模型,切记,一定要输入“–cuda”,否则程序只调用CPU来训练,那将是个极其漫长的训练过程;
  • -d是数据集类别,比如voc表示使用VOC数据集,coco表示使用COCO数据集;
  • --root是存放数据集的路径;
  • -bs 32表示训练期间的batch size为32;读者可以根据自己的GPU显存来修改,如-bs 8或者-bs 16等;
  • --size表示输入图像的最长边的尺寸;
  • --wp_epoch是warmup阶段的时长;
  • --max_epoch是训练的总epoch数量;
  • --eval_epoch是在训练阶段中去测试模型的性能的节点;
  • --no_aug_epoch表示最后的多少epoch会关闭数据增强;
  • --ema表示启动Model EMA策略;
  • --fp16表示开启混合精度策略;
  • --multi_scale表示开启多尺度训练策略;
  • 加上nohup让程序可以后台运行,同时保存训练日志到./logs/yolo_v1_train_log.txt文件,报错日志到./logs/yolo_v1_warning_log.txt文件中,我们可以通过tail -f ./logs/yolo_v1_train_log.txt查看训练过程中的日志。
  • 用2080Ti 训练YOLOv1花费时间1h~2h

可以利用nvidia-smi查看GPU利用率等相关信息:

(base) root@autodl-container-adbc11ae52-f2ebff02:~/data/VOCdevkit/VOC2007/ImageSets/Main# nvidia-smi 
Sat Jan 13 08:51:05 2024       
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 525.89.02    Driver Version: 525.89.02    CUDA Version: 12.0     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|                               |                      |               MIG M. |
|===============================+======================+======================|
|   0  NVIDIA GeForce ...  On   | 00000000:B4:00.0 Off |                  N/A |
| 39%   67C    P2   212W / 250W |   9230MiB / 11264MiB |     93%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------++-----------------------------------------------------------------------------+
| Processes:                                                                  |
|  GPU   GI   CI        PID   Type   Process name                  GPU Memory |
|        ID   ID                                                   Usage      |
|=============================================================================|
+-----------------------------------------------------------------------------+
  • 第一部分参数解释

    • 都是版本号,有 NVIDIA-SMI 版本号、CUDA 驱动版本号 和 CUDA 版本号。
    • 这里需要注意的是,这里的 CUDA 版本号并不一定是你自己安装的 CUDA 的版本号,而是这个 CUDA 驱动能够支持的最高的 CUDA 版本。如果要查看你自己安装的 CUDA 版本号,可以使用 nvcc -V 进行查看。
  • 第二部分参数解释

    • 1、“GPU 型号及序号”

      • 比如上图中我这里就是只有一块卡,所以序号是 “0”,然后型号是英伟达 GeForce;
      • “Fan” 风扇表示你的 GPU 是主动散热还是被动散热的,比如像 RTX 那些消费级显卡一般都是主动散热的,从物理形态上来看就是带风扇的;A10 是推理计算卡,是被动散热的,所以风扇一直其实都会是 0% (相当于 N/A);
    • 2、“Temp” 温度,这个很好理解就是目前显卡的温度是67 ℃

      • “温度” 这个指标是需要在执行程序的时候经常关注的,特别是对于被动散热的卡,要是机器的散热做不好,就容易使卡达到降频保护的温度,从而卡的性能会下降,跑出来的性能就不准确了;
    • 3、“Perf” 表示显卡的性能状态,有 p0 ~ p12,性能状态从高到底

      • 也就是 p0 表示性能最大,p12 表示性能最小
      • 在 linux 下可以使用 nvidia-settings 工具对这个参数进行配置;
    • 4、“Persistence-M” 表示持续模式状态,这个参数默认是关闭的,若打开能够让 GPU 更加快速地响应任务,但相应待机功耗也会增加;

    • 5、“Pwr: Usage/Cap” 表示显卡的功耗,可以看到我这里 2080TI 的显卡功耗是 250W,而前面的 212W 是根据执行的任务动态变化的,是卡上所有执行进程所占显存的和;

    • 6、“Bus-Id” 表示 GPU 的总线地址;

    • 7、“Disp.A” 全称是 “Display Active” 表示显卡的显示功能是否初始化,这其实也比较好理解。

      • 如果当前的 GPU 正驱动一个活动的显示器,那么这个参数就会是 On,反之为 Off。
      • 我这里的是不带显示功能的,所以这个参数就是 Off 了;
    • 8、Memory-Usage 表示显存占用情况

      • 可以看到我这里的显存是 11264 MiB,前面的 9230 MiB 也是随着执行的程序动态变化的
      • 但是不能超过 11264 MiB,不然就会报 CUDA 显存不足的错误;
    • 9、Volatile GPU-UTil 表示 GPU 的使用率。

      • GPU使用率 和 显存使用率】 是两个比较重要的指标,一般来说,GPU 使用率高的时候显存使用率也会比较高
      • 但反过来,显存使用率高的时候 GPU 使用率并不一定会高。
        比如在大量数据传输的时候,显存是被占用的,但是 GPU 本身还没开始计算,这个时候的显存占用是高的,但 GPU 使用率并不一定高,这种情况会经常出现在访存密集型的计算中;
    • 10、“ECC” 全称为 Error Checking and Correcting,表示是否开启错误检查和纠错技术,“0” 代表关闭,“1” 代表开启。

      • ECC 开启后功能可以用于保护 GPU 硬件,帮助其免受硬件故障和问题的影响。因为它还可以检查内存模块中的错误,并帮助 GPU 从错误中恢复。
      • 此外,ECC 还有助于降低电力消耗,因为它只需要较少的功耗就可以处理大量的内存纠错;
    • 11、“Compute M” 表示计算模式,NVIDIA GPU 有 Default、Exclusive Process、Exclusive Thread、Prohibited 四种计算模式,

      • 一般就用默认的 Default 就可以了。
      • 在 Default 模式下,多个进程可以同时共享不同的 GPU;
    • 12、最后一个是 “MIG M”,这是安培架构新提出的功能,可以用于创建 MIG 实例,在用户层实现 GPU 的物理隔离。

      • 一看我这 “MIG M” 是 “N/A”,这说明在 2080TI 上是不支持这个功能的呀。
  • 第三部分参数解释
    正在执行的 CUDA 程序的一些状态信息,特别要关注的是 “PID” 和 “GPU Memory Usage” 这两个参数,其他就不多说了

我这里在AutoDL的云GPU上进行训练的,平台上也监控了GPU相关性能信息:
在这里插入图片描述
如果自己电脑没有显卡,可以去colab白嫖,也可以租一个GPU,例如AutoDL平台,可以参考下面博客租借

AutoDL平台租借GPU详解

二、模型的验证

2.1 mAP指标计算

  • 训练结束后,模型默认保存在weights/voc/yolov1/文件夹下,名为yolov1_voc_best.pth,保存了训练阶段在测试集上mAP指标最高的模型。

  • 运行项目中所提供的eval.py文件可以验证模型的性能,具体命令如下行所示

  • 可以给定不同的图像尺寸来测试实现的YOLOv1在不同输入尺寸下的性能

    python eval.py \
    --cuda -d voc \
    --root path/to/voc -m yolov1 \
    --weight path/to/yolov1_voc_best.pth \
    -size 416
    
  • 也可以可视化训练好的模型

    python test.py \
    --cuda -d voc \
    --root path/to/voc -m yolov1 
    --weight path/to/yolov1_voc_best.pth \
    -size 416 -vt 0.3 \
    --show# -size表示输入图像的最大边尺寸
    # -vt是可视化的置信度阈值,只有高于此值的才会被可视化出来
    # --show表示展示检测结果的可视化图片
    

    作者训练好的模型,在VOC2007测试集测试指标如下:

    从表格中可以看到,在同样的输入尺寸448×448下,改进后的 YOLOv1 实现了更高的性能:73.2% 的 mAP,超过了官方的 YOLOv1。

模型输入尺寸mAP(%)
YOLOv1*(官方)44863.4
YOLOv141671.9
YOLOv144873.2
YOLOv151274.9
YOLOv164076.7

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python Flask教程

Flask Doc: https://rest-apis-flask.teclado.com/docs/course_intro/what_is_rest_api/Github: https://github.com/tecladocode/rest-apis-flask-python 1. 最简单的应用 最小应用 from flask import Flaskapp Flask(__name__)app.route("/") def hello_world()…

18 串口通讯

文章目录 18.0 前言18.1 串口通讯协议简介18.1.1 物理层 18.2 RT1052 的 LPUART 简介18.3 UART 功能框图18.3.1 中断控制 18.4 UART 初始化结构体详解18.4.1 baudRate_Bps18.4.2 parityMode18.4.3 dataBitsCount18.4.4 isMsb18.4.5 stopBitCount18.4.6 txFifoWatermark与rxFifo…

Kubernetes 集群管理—日志架构

日志架构 应用日志可以让你了解应用内部的运行状况。日志对调试问题和监控集群活动非常有用。 大部分现代化应用都有某种日志记录机制。同样地&#xff0c;容器引擎也被设计成支持日志记录。 针对容器化应用&#xff0c;最简单且最广泛采用的日志记录方式就是写入标准输出和标…

RIP【新华三与华为区别】

【介绍】 rip分为rip 1 与 rip 2 &#xff0c;rip 2 是对 rip 1 的一种升级&#xff0c;rip 2 可以进行认证等功能 【命令】 新华三&#xff1a; [HC3-R1] rip #启用rip [HC3-R1-rip] version 2 #告知rip 版本号 [HC3-R1-rip] network 192.168.1.0 #宣告其网段 [HC3-R1-rip] …

ES分词器

Analysis&#xff1a;文本分析是把全文本转换一系列单词的过程&#xff0c;也叫分词。Analysis是通过Analyzer(分词器)来实现的。 1.Analyzer组成 注意&#xff1a;在ES中默认使用标准分词器&#xff1a;StandardAnalyzer。特点是&#xff1a;中文是单字分词&#xff0c;英文是…

Docker 容器之间的互相通信

Docker容器之间的互相通信 步骤一&#xff1a;创建自定义网络 首先&#xff0c;我们需要创建一个自定义网络&#xff0c;以便容器可以连接到这个网络上&#xff0c;从而实现互相通信。在命令行中执行以下命令&#xff1a; # 创建 docker network create ddz # 查看 docker n…

costmap_2d包介绍

文章目录 一. costmap_2d包介绍二. Costmap包的执行入口-- move_base中调用三. Costmap包的初始化以及维护3.1 Costmap2DROS类3.1.1 构造函数 Costmap2DROS::Costmap2DROS3.1.2 地图更新线程 Costmap2DROS::mapUpdateLoop3.1.3 地图更新 Costmap2DROS::updateMap()3.1.4 激活各…

openssl3.2 - 在VS2019下源码调试openssl.exe

文章目录 openssl3.2 - 在VS2019下源码调试openssl.exe概述笔记先看一个用.bat调用openssl干活的实例VS2019调试参数设置设置 - 命令参数设置 - 工作目录设置 - 环境变量将命令行中需要的文件拷贝到exe目录单步调试备注END openssl3.2 - 在VS2019下源码调试openssl.exe 概述 …

多租户体系实现

文章目录 核心思路方案选择设计考量安全性扩展性通用性易用性 具体实现租户信息透传透传变量名命名规范应用内透传应用间透传 数据层租户隔离MySQL存储方案&#xff1a;多租户Mybatis插件Mybatis插件特点使用多租户Mybatis插件的优势参考文档 应用场景 经过工作中的一处场景启发…

PLC编程中ST语言操作符的使用方法

ST&#xff08;Structured Text&#xff09;语言操作符主要用于PLC编程&#xff0c;主要包括算术运算符、比较运算符和逻辑运算符等。 算术运算符包括加&#xff08;&#xff09;、减&#xff08;-&#xff09;、乘&#xff08;*&#xff09;、除&#xff08;/&#xff09;和指…

中国1981-2023年逐年每15天8km植被指数数据集

摘要 中国1981-2023年逐年每15天8km植被指数数据集来源于GIMMS NDVI数据&#xff0c;包括了1981年7月&#xff0d;2023年12月的长时间序列逐年每15天植被指数变化&#xff0c;格式为arcgis grid格式&#xff0c;投影为WGS84&#xff0c;其时间分辨率是15天&#xff0c;空间分辨…

什么是云服务器,阿里云优势如何?

阿里云服务器ECS英文全程Elastic Compute Service&#xff0c;云服务器ECS是一种安全可靠、弹性可伸缩的云计算服务&#xff0c;阿里云提供多种云服务器ECS实例规格&#xff0c;如经济型e实例、通用算力型u1、ECS计算型c7、通用型g7、GPU实例等&#xff0c;阿里云百科aliyunbai…

C/C++--ProtoBuf使用

一.什么是ProtoBuf 1.序列化和反序列化概念 序列化&#xff1a;把对象转变为字节序列的过程&#xff0c;称为系列化。 反序列化&#xff1a;把字节序列的内容恢复为对象的过程&#xff0c;称为反序列化。 2.什么情况下需要序列化和反序列化 存储数据&#xff1a;将内存中的对象…

Vulnhub靶机:driftingblues 6

一、介绍 运行环境&#xff1a;Virtualbox 攻击机&#xff1a;kali&#xff08;10.0.2.15&#xff09; 靶机&#xff1a;driftingblues6&#xff08;10.0.2.22&#xff09; 目标&#xff1a;获取靶机root权限和flag 靶机下载地址&#xff1a;https://www.vulnhub.com/entr…

洛谷 P9868 [NOIP2023] 词典

原文链接&#xff1a;NOIP真题第四讲&#xff1a;词典 题目来源&#xff1a;2023 年 NOIP T1 本题考察点&#xff1a;【贪心、枚举、模拟】 前置知识 字典序&#xff1a;指按照a、b、c、...、z的顺序&#xff0c;即a<b<c<...<z&#xff1b; 一、题目及链接 题…

如何用ChatGPT写教案设计?以“沁园春雪”为例

1. 引言 随着人工智能技术的飞速发展&#xff0c;ChatGPT已成为教育领域的一大创新工具。ChatGPT不仅能够模拟人类对话&#xff0c;还可以帮助设计互动丰富、内容丰富的教案。本文将探索如何利用ChatGPT进行教案教学设计&#xff0c;特别是通过“沁园春雪”这一案例&#xff0…

项目压测优化实践思路

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是爱吃芝士的土豆倪&#xff0c;24届校招生Java选手&#xff0c;很高兴认识大家&#x1f4d5;系列专栏&#xff1a;Spring原理、JUC原理、Kafka原理、分布式技术原理、数据库技术&#x1f525;如果感觉博主的文章还不错的…

RuntimeError: CUDA error: device-side assert triggered

授人以鱼不如授人以渔 解决步骤 记录下解决步骤…cuda报错真要人命 首先根据终端的提示 他说让你加这个来定位具体的python代码错哪了&#xff0c;所以咱们就加。 我这里启动命令是&#xff1a; accelerate launch --config_file "utils/acc_configs/accelerate_con…

官方认可!360荣获“科技产业高质量发展突出贡献企业”称号

近日&#xff0c;2023年度朝阳区高质量发展突出贡献企业表彰大会在北京成功召开。会上&#xff0c;朝阳区管委会&#xff08;区科信局&#xff09;对朝阳区做出积极贡献的企业单位进行表彰&#xff0c;360数字安全集团作为数字安全的领导者&#xff0c;在技术能力、研发创新和实…

windows系统下docker软件中使用ubuntu发行版本的linux系统

1.软件下载 官网下载地址 下载安装之后&#xff0c;再去微软商店下载wsl软件&#xff0c;可以直接用&#xff0c;或者也可以使用命令行拉取&#xff08;下面会讲&#xff09; 2.在docker里面创建容器的两种方法 2.1.命令行创建 前言&#xff1a;输入 winr 打开命令行进行下面…