YOLOv9 实战指南:打造个性化视觉识别利器,从零开始训练你的专属测试集

论文地址:YOLOv9: Learning What You Want to Learn Using Programmable Gradient Information

GitHub:WongKinYiu/yolov9: Implementation of paper - YOLOv9: Learning What You Want to Learn Using Programmable Gradient Information (github.com)


一、摘要

今天的深度学习方法侧重于如何设计最合适的目标函数,从而使模型的预测结果能够最接近真实标签。同时,设计一个合适的体系结构,可以获取足够的信息进行预测。现有的方法忽略了输入数据在进行分层特征提取和空间转换时,会丢失大量信息。

本文研究数据通过深度网络传输时数据丢失的重要问题,即信息瓶颈和可逆函数。并且提出了可编程梯度信息(PGI)的概念,以应对深度网络实现多个目标所需的各种变化。PGI可以为目标任务提供完整的输入信息来计算目标函数,从而获得可靠的梯度信息来更新网络权值。

此外,还设计了一种基于梯度路径规划的新型轻量级网络体系结构——广义高效层聚合网络(GELAN)。GELAN的架构证实了PGI在轻量级模型上获得了更好的结果。并且基于MS COCO数据集的目标检测上验证了所提出的GELAN和PGI。结果表明,GELAN只使用传统的卷积算子来比基于深度卷积的先进方法获得更好的参数利用。

PGI可用于从轻量级到大型的各种模型。可以用来获得完整的信息,因此,从头开始训练的模型可以比使用大数据集预先训练的最先进的模型获得更好的结果。

在MS COCO数据集中,基于GELAN和PGI的目标检测方法在目标检测性能方面超过了以往所有的从头训练方法,在精度方面,新方法优于大数据集预训练的RT DETR,在参数利用方面也优于基于深度卷积的设计YOLO MS。

二、配置YOLOv9

2.1、下载yolov9

git clone https://github.com/WongKinYiu/yolov9.git

2.2、创建conda环境

conda create -n yolov9 python==3.8

然后切换到yolov9目录下,找到requirements.txt文件(yolov9需要的库)

# 1、激活yolov9
conda activate yolov9# 2、切换地址
cd yolov9# 3、安装相关库
pip install -r requirements.txt

注意:本文忽略了anaconda的安装和cuda、cudnn的安装过程

三、数据划分

环境都配置完成之后,开始对测试集进行划分,本文选择了一个insect测试集(包含7个类别['Leconte','Boerner','linnaeus','armandi','coleoptera','acuminatus','Linnaeus'])如下图所示

images:训练集和验证集

labels:训练集和验证集的标签

train.txt和val.txt:需要生成的列表(对于每个图片的地址)

test:测试集

train.cache和val.cache:训练过程中生成的

3.1、数据可视化

看下标签

<annotation><folder>xxx</folder><filename>1.jpeg</filename><path>/home/fion/桌面/xxx/1.jpeg</path><source><database>Unknown</database></source><size><width>1344</width><height>1344</height><depth>3</depth></size><segmented>0</segmented><object><name>Leconte</name><pose>Unspecified</pose><truncated>0</truncated><difficult>0</difficult><bndbox><xmin>473</xmin><ymin>578</ymin><xmax>612</xmax><ymax>727</ymax></bndbox></object><object><name>Boerner</name><pose>Unspecified</pose><truncated>0</truncated><difficult>0</difficult><bndbox><xmin>822</xmin><ymin>505</ymin><xmax>948</xmax><ymax>639</ymax></bndbox></object><object><name>linnaeus</name><pose>Unspecified</pose><truncated>0</truncated><difficult>0</difficult><bndbox><xmin>607</xmin><ymin>781</ymin><xmax>690</xmax><ymax>842</ymax></bndbox></object><object><name>armandi</name><pose>Unspecified</pose><truncated>0</truncated><difficult>0</difficult><bndbox><xmin>756</xmin><ymin>786</ymin><xmax>841</xmax><ymax>856</ymax></bndbox></object><object><name>coleoptera</name><pose>Unspecified</pose><truncated>0</truncated><difficult>0</difficult><bndbox><xmin>624</xmin><ymin>488</ymin><xmax>711</xmax><ymax>554</ymax></bndbox></object>
</annotation>

xml文件解析:

xml.etree.ElementTree库在Python中提供了多种处理XML数据的方法。以下是一些常见的用法:

1. 解析XML字符串:
   使用ET.fromstring(xml_string)可以从一个XML格式的字符串创建一个元素对象,并作为树的根节点。

2. 解析XML文件:
   使用ET.parse(file_path)可以加载并解析一个XML文件,返回一个树的根节点。

3. 获取元素标签和属性:
   使用element.tag获取元素的标签名称;使用`element.attrib`获取元素的属性字典。

4. 查找子元素:
   使用element.find(tag)查找第一个匹配的子元素;使用`element.findall(tag)`查找所有匹配的子元素。

5. 遍历元素:
   使用`element.iter()`或`element.iter(tag)`可以迭代遍历元素的所有后代元素。

等等......

这些是`xml.etree.ElementTree`库的一些基本用法,涵盖了解析、查询、修改和创建XML数据的常见操作。

import numpy as np
import os,cv2,systry:import xml.etree.cElementTree as ET
except ImportError:import xml.etree.ElementTree as ETdef plot_image():# 数据的类别INSECT_NAMES = ['Leconte','Boerner','linnaeus','armandi','coleoptera','acuminatus','Linnaeus']# 不同类别给不同颜色COLOR_NAMES = {'Leconte':(255,0,0),'Boerner':(0,255,0),'linnaeus':(0,0,255),'armandi':(0,0,0),'coleoptera':(255,255,0),'acuminatus':(148,0,211),'Linnaeus':(255,255,255)}img_dir = r"E:\YOLO_insects\insects\train\images"xml_dir = r"E:\YOLO_insects\insects\train\annotations\xmls"save_dir = r"E:\YOLO_insects\insects\imshow"if not os.path.exists(save_dir):os.makedirs(save_dir)for i in os.listdir(img_dir):img_path = os.path.join(img_dir, i)xml_path = os.path.join(xml_dir, i.split(".")[0]+".xml")img = cv2.imread(img_path)root = ET.parse(xml_path).getroot()for obj in root.findall('object'):difficult = obj.find('difficult').textcls = obj.find('name').textsize = root.find('size')w = int(size.find('width').text)h = int(size.find('height').text)if cls not in INSECT_NAMES or int(difficult) == 1:continuecls_id = INSECT_NAMES.index(cls)xml_box = obj.find('bndbox')b = (float(xml_box.find('xmin').text), float(xml_box.find('xmax').text),float(xml_box.find('ymin').text), float(xml_box.find('ymax').text))print(f"{cls}")cv2.rectangle(img, (int(b[0]),int(b[2])),(int(b[1]),int(b[3])), COLOR_NAMES[str(cls)],3)cv2.putText(img, cls,(int(b[0]),int(b[2])-5),cv2.FONT_HERSHEY_SIMPLEX,0.5,COLOR_NAMES[str(cls)],2,cv2.LINE_AA)cv2.imwrite(os.path.join(save_dir,i), img)

3.2、测试集标签转换

因为本文测试集已划分好了train、val和test,因此下面跳过了数据集划分部分,下面看下如何生成yolo所需的标签格式

代码部分:

import numpy as np
import os,cv2,systry:import xml.etree.cElementTree as ET
except ImportError:import xml.etree.ElementTree as ETdef convert(size, box):dw = 1. / (size[0])dh = 1. / (size[1])x = (box[0] + box[1]) / 2.0 - 1y = (box[2] + box[3]) / 2.0 - 1w = box[1] - box[0]h = box[3] - box[2]x = x * dww = w * dwy = y * dhh = h * dhreturn x, y, w, hdef generate_labels():train_dir = r'E:\YOLO_insects\insects\val\annotations\xmls'train_xml = './insects/labels/val'if not os.path.exists(train_xml):os.makedirs(train_xml,exist_ok=True)num = 0classes = ['Leconte','Boerner','linnaeus','armandi','coleoptera','acuminatus','Linnaeus']for file in os.listdir(train_dir):xml_path = os.path.join(train_dir,file)outfile = open(os.path.join(train_xml,file.split(".xml")[0]+".txt"),'w',encoding="utf8")num +=1print(xml_path)content = ET.parse(xml_path)root = content.getroot()size = root.find('size')w = int(size.find('width').text)h = int(size.find('height').text)channel = int(size.find('depth').text)print(f"size: {w}x{h}, channel: {channel}")for obj in root.iter('object'):difficult = obj.find('difficult').textcls = obj.find('name').text# classes.append(cls) # 可以查看有哪些类if cls not in classes or int(difficult) == 1:continuecls_id = classes.index(cls)xml_box = obj.find('bndbox')b = (float(xml_box.find('xmin').text), float(xml_box.find('xmax').text),float(xml_box.find('ymin').text),float(xml_box.find('ymax').text))b1, b2, b3, b4 = b# 标注越界修正if b2 > w:b2 = wif b4 > h:b4 = hb = (b1, b2, b3, b4)bbox = convert((w,h),b)print(bbox)outfile.write(str(cls_id) + " " + " ".join([str(i) for i in bbox]) + "\n")print(f"total_num:{num}")

标签生成之后,生成测试列表

def produce_txt(input_dir=r'E:\yolov9\data\insects\images\val', output_file=r"E:\yolov9\data\insects\val.txt"):with open(output_file, 'w') as f:for root, dirs, files in os.walk(input_dir):for file in files:file_path = os.path.join(root, file)f.write(file_path + '\n')

四、开始训练

4.1、在github里下载yolov9-c.pt文件

存放位置随意

4.2、创建对应的yaml配置文件

yolov9/data/insect.yaml
path: E:\yolov9\data\insects  # dataset root dir
train: train.txt  # train images (relative to 'path')
val: val.txt  # val images (relative to 'path')# Classes
names: ['Leconte','Boerner','linnaeus','armandi','coleoptera','acuminatus','Linnaeus']

4.3、修改yolov9-c.yaml文件中的类别数目

# YOLOv9# parameters
nc: 7  # number of classes
depth_multiple: 1.0  # model depth multiple
width_multiple: 1.0  # layer channel multiple
#activation: nn.LeakyReLU(0.1)
#activation: nn.ReLU()# anchors
anchors: 3

4.4、配置train_dual.py参数

--weights:设置下载好的yolov9-c.pt文件地址
--cfg:选择yolov9-c.yaml
--data:自己创建的配置文件
--imgsz:图像的尺寸:我这里图设置320,也可以设置640
--batch-size:批次大小,太大可能跑不了
--epoch:训练epoch次数
其他参数都是选择默认值
def parse_opt(known=False):parser = argparse.ArgumentParser()# parser.add_argument('--weights', type=str, default=ROOT / 'yolo.pt', help='initial weights path')# parser.add_argument('--cfg', type=str, default='', help='model.yaml path')parser.add_argument('--weights', type=str, default='weights/yolov9-c.pt', help='initial weights path')parser.add_argument('--cfg', type=str, default='models/detect/yolov9-c.yaml', help='model.yaml path')parser.add_argument('--data', type=str, default='data/insect.yaml', help='dataset.yaml path')parser.add_argument('--hyp', type=str, default='data/hyps/hyp.scratch-high.yaml', help='hyperparameters path')parser.add_argument('--epochs', type=int, default=20, help='total training epochs')parser.add_argument('--batch-size', type=int, default=8, help='total batch size for all GPUs, -1 for autobatch')parser.add_argument('--imgsz', '--img', '--img-size', type=int, default=320, help='train, val image size (pixels)')parser.add_argument('--rect', action='store_true', help='rectangular training')parser.add_argument('--resume', nargs='?', const=True, default=False, help='resume most recent training')parser.add_argument('--nosave', action='store_true', help='only save final checkpoint')parser.add_argument('--noval', action='store_true', help='only validate final epoch')parser.add_argument('--noautoanchor', action='store_true', help='disable AutoAnchor')parser.add_argument('--noplots', action='store_true', help='save no plot files')parser.add_argument('--evolve', type=int, nargs='?', const=300, help='evolve hyperparameters for x generations')parser.add_argument('--bucket', type=str, default='', help='gsutil bucket')parser.add_argument('--cache', type=str, nargs='?', const='ram', help='image --cache ram/disk')parser.add_argument('--image-weights', action='store_true', help='use weighted image selection for training')parser.add_argument('--device', default='', help='cuda device, i.e. 0 or 0,1,2,3 or cpu')parser.add_argument('--multi-scale', action='store_true', help='vary img-size +/- 50%%')parser.add_argument('--single-cls', action='store_true', help='train multi-class data as single-class')parser.add_argument('--optimizer', type=str, choices=['SGD', 'Adam', 'AdamW', 'LION'], default='SGD', help='optimizer')parser.add_argument('--sync-bn', action='store_true', help='use SyncBatchNorm, only available in DDP mode')parser.add_argument('--workers', type=int, default=0, help='max dataloader workers (per RANK in DDP mode)')parser.add_argument('--project', default=ROOT / 'runs/train', help='save to project/name')parser.add_argument('--name', default='exp', help='save to project/name')parser.add_argument('--exist-ok', action='store_true', help='existing project/name ok, do not increment')parser.add_argument('--quad', action='store_true', help='quad dataloader')parser.add_argument('--cos-lr', action='store_true', help='cosine LR scheduler')parser.add_argument('--flat-cos-lr', action='store_true', help='flat cosine LR scheduler')parser.add_argument('--fixed-lr', action='store_true', help='fixed LR scheduler')parser.add_argument('--label-smoothing', type=float, default=0.0, help='Label smoothing epsilon')parser.add_argument('--patience', type=int, default=10, help='EarlyStopping patience (epochs without improvement)')parser.add_argument('--freeze', nargs='+', type=int, default=[0], help='Freeze layers: backbone=10, first3=0 1 2')parser.add_argument('--save-period', type=int, default=-1, help='Save checkpoint every x epochs (disabled if < 1)')parser.add_argument('--seed', type=int, default=0, help='Global training seed')parser.add_argument('--local_rank', type=int, default=-1, help='Automatic DDP Multi-GPU argument, do not modify')parser.add_argument('--min-items', type=int, default=0, help='Experimental')parser.add_argument('--close-mosaic', type=int, default=0, help='Experimental')# Logger argumentsparser.add_argument('--entity', default=None, help='Entity')parser.add_argument('--upload_dataset', nargs='?', const=True, default=False, help='Upload data, "val" option')parser.add_argument('--bbox_interval', type=int, default=-1, help='Set bounding-box image logging interval')parser.add_argument('--artifact_alias', type=str, default='latest', help='Version of dataset artifact to use')return parser.parse_known_args()[0] if known else parser.parse_args()

4.5、开始训练

直接运行:train_dual.py

16 epochs completed in 6.767 hours.
Optimizer stripped from runs\train\exp3\weights\last.pt, 102.8MB
Optimizer stripped from runs\train\exp3\weights\best.pt, 102.8MBValidating runs\train\exp3\weights\best.pt...
Fusing layers...
yolov9-c summary: 604 layers, 50712138 parameters, 0 gradients, 236.7 GFLOPsClass     Images  Instances          P          R      mAP50   mAP50-95: 100%|██████████| 16/16 01:11all        245       1856      0.743      0.782      0.836      0.588Leconte        245        594       0.92      0.871      0.964      0.714Boerner        245        318       0.85      0.893      0.928      0.722armandi        245        231      0.611      0.754      0.712      0.497coleoptera        245        186      0.437      0.909      0.757      0.456acuminatus        245        235      0.763        0.8       0.86      0.555Linnaeus        245        292      0.877      0.464      0.796      0.584

训练过程中在runs/train目录下生成中间文件,如下图

五、模型测试

5.1、修改detect_dual.py文件

--weights:训练好的模型,选择best.pt
--source:设置测试图片地址,或者采用电脑摄像头,设置为0时
--data:自己创建的insect.yaml配置文件
--imgsz:这里选择320,和训练时保持一致
其他参数选择默认配置
def parse_opt():parser = argparse.ArgumentParser()parser.add_argument('--weights', nargs='+', type=str, default=ROOT / 'runs/train/exp3/weights/best.pt', help='model path or triton URL')# parser.add_argument('--source', type=str, default=ROOT / 'E:\yolov9\data\insects\\test', help='file/dir/URL/glob/screen/0(webcam)')parser.add_argument('--source', type=str, default=ROOT / 'E:\YOLO_insects\insects\\test\images',help='file/dir/URL/glob/screen/0(webcam)')parser.add_argument('--data', type=str, default=ROOT / 'data/insect.yaml', help='(optional) dataset.yaml path')parser.add_argument('--imgsz', '--img', '--img-size', nargs='+', type=int, default=[320], help='inference size h,w')parser.add_argument('--conf-thres', type=float, default=0.25, help='confidence threshold')parser.add_argument('--iou-thres', type=float, default=0.45, help='NMS IoU threshold')parser.add_argument('--max-det', type=int, default=1000, help='maximum detections per image')parser.add_argument('--device', default='', help='cuda device, i.e. 0 or 0,1,2,3 or cpu')parser.add_argument('--view-img', action='store_true', help='show results')parser.add_argument('--save-txt', action='store_true', help='save results to *.txt')parser.add_argument('--save-conf', action='store_true', help='save confidences in --save-txt labels')parser.add_argument('--save-crop', action='store_true', help='save cropped prediction boxes')parser.add_argument('--nosave', action='store_true', help='do not save images/videos')parser.add_argument('--classes', nargs='+', type=int, help='filter by class: --classes 0, or --classes 0 2 3')parser.add_argument('--agnostic-nms', action='store_true', help='class-agnostic NMS')parser.add_argument('--augment', action='store_true', help='augmented inference')parser.add_argument('--visualize', action='store_true', help='visualize features')parser.add_argument('--update', action='store_true', help='update all models')parser.add_argument('--project', default=ROOT / 'runs/detect', help='save results to project/name')parser.add_argument('--name', default='exp', help='save results to project/name')parser.add_argument('--exist-ok', action='store_true', help='existing project/name ok, do not increment')parser.add_argument('--line-thickness', default=3, type=int, help='bounding box thickness (pixels)')parser.add_argument('--hide-labels', default=False, action='store_true', help='hide labels')parser.add_argument('--hide-conf', default=False, action='store_true', help='hide confidences')parser.add_argument('--half', action='store_true', help='use FP16 half-precision inference')parser.add_argument('--dnn', action='store_true', help='use OpenCV DNN for ONNX inference')parser.add_argument('--vid-stride', type=int, default=1, help='video frame-rate stride')opt = parser.parse_args()opt.imgsz *= 2 if len(opt.imgsz) == 1 else 1  # expandprint_args(vars(opt))return opt

在runs\detect\exp目录下生成对应的测试效果图

六、遇到的问题

AttributeError: 'FreeTypeFont' object has no attribute 'getsize'

这个问题 主要是:

'FreeTypeFont' object has no attribute 'getsize', is caused by a compatibility problem with the new version of Pillow.

# 1、查看pillow的版本
conda list# 2、卸载高版本安装9.5版本
pip uninstall pillowpip install pillow==9.5

参考博客:

1、YOLOv9目标识别——详细记录训练环境配置与训练自己的数据集_yolov9训练自己的数据集-CSDN博客

2、YOLOv9如何训练自己的数据集(NEU-DET为案列)_yolov9训练自己的数据集-CSDN博客

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

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

相关文章

Linux---多线程(下)

前情提要&#xff1a;Linux---多线程(上) 七、互斥 临界资源&#xff1a;多线程执行流共享的资源就叫做临界资源临界区&#xff1a;每个线程内部&#xff0c;访问临界资源的代码&#xff0c;就叫做临界区互斥&#xff1a;任何时刻&#xff0c;互斥保证有且只有一个执行流进入临…

PL/SQL的词法单元

目录 字符集 标识符 分隔符 注释 oracle从入门到总裁:​​​​​​https://blog.csdn.net/weixin_67859959/article/details/135209645 PL/SQL块中的每一条语句都必须以分号结束。 一个SQL语句可以跨多行&#xff0c;但分号表示该语句的结束:一行中也可以有多条 SQL语句&…

3.28(迭代搜索算法 + java学习总结)

迭代加深搜索 迭代加深算法是一在DFS的基础上添加搜索深度限制的搜索方法&#xff1b; 其核心思想是从深度为0的地方开始搜索&#xff0c;然后逐步加深搜索深度&#xff0c;重新搜索一遍&#xff1b;这对于那些已知答案在浅层&#xff0c;但整个树或图存在极多分支的情况&#…

【前端Vue】HR-saas中台项目开发md文档第1篇:vuex基础-介绍,vuex基础-初始化功能【附代码文档】

HR-saas中台管理项目开发完整教程&#xff08;附代码资料&#xff09;主要内容讲述&#xff1a;vuex基础-介绍,vuex基础-初始化功能,vuex基础-state,vuex基础-mutations,vuex基础-actions,vuex基础-getters。项目课设计&#xff0c;人力资源的环境搭建vue-element-admin的了解和…

[flask]http请求//获取请求头信息+客户端信息

在网站中查询请求头信息&#xff0c;可以通过以下操作进行 右键然后选择检查 进入改页面后选择文档&#xff0c;刷新一下页面就好了 获取所有的请求头信息 print(request.headers, type(request.headers)) 在flask模块中&#xff0c;使用上面的输出函数就可以查看到有关于请求…

Qt 窗口MainWindow(上)

Qt 窗口是通过 QMainWindow 类来实现的。 QMainWindow 是一个为用户提供主窗口程序的类&#xff0c;继承自 QWidget 类&#xff0c;并且提供了⼀个预定义的布局。QMainWindow 包含一个菜单栏&#xff08;menubar&#xff09;、多个工具栏(toolbars)、多个浮动窗口&#xff08;…

第十四届蓝桥杯JavaA组省赛真题 - 特殊日期

解题思路&#xff1a; 暴力秒了 public class Main {public static void main(String[] args) {int cnt 0;for (int i 1900; i < 9999; i) {for (int j 1; j < 12; j) {for (int k 1; k < days(i, j); k) {if (sum(i) sum(j) sum(k)) cnt;}}}System.out.print…

安防监控视频汇聚平台EasyCVR启用图形验证码之后如何调用login接口?

视频综合管理平台EasyCVR视频监控系统支持多协议接入、兼容多类型设备&#xff0c;平台可以将区域内所有部署的监控设备进行统一接入与集中汇聚管理&#xff0c;实现对监控区域的实时高清视频监控、录像与存储、设备管理、云台控制、语音对讲、级联共享等&#xff0c;在监控中心…

【Vue】可拖拽侧边栏实现

在本篇博客中&#xff0c;我们将探讨如何在 Vue.js 项目中实现一个可拖拽的侧边栏。此功能可以通过修改 HTML 和 Vue 组件的脚本来实现。 首先&#xff0c;我们需要在 HTML 文件中定义侧边栏的容器和用于拖拽的元素。在 Vue 组件中&#xff0c;我们将使用 Vue 的响应式系统来追…

力扣73. 矩阵置零

Problem: 73. 矩阵置零 文章目录 题目描述思路复杂度Code 题目描述 思路 思路1&#xff1a;利用一个等大的矩阵判定 复制一个与原始矩阵一样大的矩阵temp&#xff0c;遍历temp时若temp[i][j] 0&#xff0c;则将martix对应的行与列均设置为0 思路2&#xff1a;利用两个一维矩阵…

【Linux】UnixBench介绍、分数调优思路以及测试2D3D的方法

一.简介 unixbench是一个用于测试unix系统性能的工具&#xff0c;也是一个比较通用的benchmark&#xff0c; 此测试的目的是对类Unix 系统提供一个基本的性能指示&#xff0c;很多测试用于系统性能的不同方面&#xff0c;这些测试的结果是一个指数值&#xff08;index value&am…

幻兽帕鲁服务器价格表_阿里云/腾讯云/京东云/华为云报价大全

2024年全网最全的幻兽帕鲁服务器租用价格表&#xff0c;阿里云幻兽帕鲁游戏服务器26元1个月、腾讯云32元一个月、京东云26元一个月、华为云24元1个月&#xff0c;阿腾云atengyun.com整理最新幻兽帕鲁专用4核16G、8核16G、8核32G游戏服务器租用价格表大全&#xff1a; 阿里云幻…

C++类的六个默认成员函数(详细解析与总结)

目录 前言&#xff1a; 一、构造函数 a.特点 b.注意事项 1.首先明确什么是默认构造函数 2.默认构造函数对内置类型与自定义类型的处理 c.总结 二、析构函数 a.特点 b.注意事项 1.什么时候写析构函数&#xff1f; 2.析构函数对内置类型与自定义类型的处理 c.总结 …

pythonselenium自动化测试实战项目

说明&#xff1a;本项目采用流程控制思想&#xff0c;未引用unittest&pytest等单元测试框架 一.项目介绍 目的 测试某官方网站登录功能模块可以正常使用 用例 1.输入格式正确的用户名和正确的密码&#xff0c;验证是否登录成功&#xff1b; 2.输入格式正确的用户名和不…

【面试经典 | 150】单词拆分

文章目录 Tag题目来源解题思路方法一&#xff1a;动态规划 写在最后 Tag 【动态规划】【字符串】 题目来源 139. 单词拆分 解题思路 方法一&#xff1a;动态规划 定义状态 定义 dp[i] 表示字符串 s 前 i 个字符组成的字符串&#xff08;s[0, ..., i-1]&#xff09;是否能被…

【Node.js】模块化

概述 Nodejs 模块化规范遵循两套规范&#xff1a; Common JSES Module Common JS 引入模块&#xff08;require&#xff09;支持四种格式 支持引入内置模块例如 http os fs child_process 等const fs require(fs)&#xff0c;高版本也可以使用const fs require(node:fs…

【Java SE】封装

&#x1f970;&#x1f970;&#x1f970;来都来了&#xff0c;不妨点个关注叭&#xff01; &#x1f449;博客主页&#xff1a;欢迎各位大佬!&#x1f448; 文章目录 1. 封装1.1 封装是什么1.2 封装的意义1.3 访问修饰限定符1.3.1 在Java中如何实现封装1.3.2 各种访问修饰限定…

SpringCloud微服务集成Dubbo

1、Dubbo介绍 Apache Dubbo 是一款易用、高性能的 WEB 和 RPC 框架,同时为构建企业级微服务提供服务发现、流量治理、可观测、认证鉴权等能力、工具与最佳实践。用于解决微服务架构下的服务治理与通信问题,官方提供了 Java、Golang 等多语言 SDK 实现。使用 Dubbo 开发的微服…

【二叉树】Leetcode 230. 二叉搜索树中第K小的元素【中等】

二叉搜索树中第K小的元素 给定一个二叉搜索树的根节点 root &#xff0c;和一个整数 k &#xff0c;请你设计一个算法查找其中第 k 个最小元素&#xff08;从 1 开始计数&#xff09;。 示例1&#xff1a; 输入&#xff1a;root [3,1,4,null,2], k 1 输出&#xff1a;1 解…

在 fstab文件中配置 UUID方式自动挂载数据盘、swap、目录(**)

linux如何挂在硬盘&#xff0c;自动挂载和手动挂载&#xff08;详细说明&#xff09;https://gitcode.csdn.net/65eedcea1a836825ed7a06f4.html 解决linux重启后磁盘挂载失效的问题 https://blog.csdn.net/sugarbliss/article/details/107033034 linux /etc/fstab 文件详细说…