目标检测-YOLOv7代码及训练

2023-11-13

目录

论文及代码下载

代码结构

代码学习

1 train.py

1.1 参数

1.2 Resume 训练中断后继续训练

1.3 Train 训练

2 yolov7.yaml

2.1 parameters and anchors

2.2 网络结构

3 detect.py

3.1 参数

3.2 绘制检测框 plot_one_box()

训练流程

1 数据集准备

1.0 数据集处理前的数据集结构

1.1 VOC格式转YOLO格式

1.2 划分训练集、验证集、测试集

1.3 生成yolov7需要的文件夹格式

1.4 创建最终训练需要的train.txt和val.txt文件

1.5 最终数据集结构

2 需要修改的参数

2.1 --weights

2.2 --cfg

2.3 --data

2.4 --hyp

2.5 --epochs

2.6 --batch-size

2.7 --device

2.8 --project

2.9 --workers


论文及代码下载

论文:https://arxiv.org/abs/2207.02696

代码:https://github.com/WongKinYiu/yolov7

在实际使用过程中,我用的YOLOAir库,和YOLOv7源码使用方法是一样的

yoloair2:https://github.com/iscyy/yoloair2

代码结构

.
├── cfg(存放yaml文件)
│   ├── baseline
│   ├── deploy
│   ├── training
│   └── improved
├── configs(也存放了yaml文件,但经过了改进)
│   ├── attention
│   ├── attention_v7
│   ├── backbone
│   ├── head-Improved
│   └── ......
├── data(数据集信息和模型训练超参数)
│   ├── coco.yaml
│   ├── coco128.yaml
│   ├── hyp.scratch.custom.yaml
│   ├── hyp.scratch.p5.yaml
│   ├── hyp.scratch.p6.yaml
│   └── hyp.scratch.tiny.yaml
├── deploy
│   └── triton-inference-server
│       ├── data
│       │   ├── dog.jpg
│       │   └── dog_result.jpg
│       ├── boundingbox.py
│       ├── client.py
│       ├── labels.py
│       ├── processing.py
│       ├── render.py
│       └── README.md
├── figure(一些图片)
├── inference(存放detect用的图片)
│   └── images
├── models(存放网络结构)
│   ├── Models
│   │   ├── Attention
│   │   └── **.py
│   ├── Detect
│   ├── __init__.py
│   ├── common.py
│   ├── commonv5.py
│   ├── experimental.py
│   ├── module.py
│   └── yolo.py
├── runs(模型训练时的输出)
├── scripts(获得COCO 数据集)
│   └── get_coco.sh
├── utils
│   ├── aws
│   │   ├── __init__.py
│   │   ├── mime.sh
│   │   ├── resume.py
│   │   └── userdata.sh
│   ├── google_app_engine
│   │   ├── additional_requirements.txt
│   │   ├── app.yaml
│   │   └── Dockerfile
│   ├── wandb_logging
│   │   ├── __init__.py
│   │   ├── log_dataset.py
│   │   └── wandb_utils.py
│   ├── __init__.py
│   ├── activations.py(激活函数)
│   ├── add_nms.py
│   ├── autoanchor.py
│   ├── datasets.py(数据的读取和加载)
│   ├── general.py
│   ├── google_utils.py
│   ├── loss.py(损失函数)
│   ├── metrics.py(衡量指标)
│   ├── plots.py(画图)
│   └── torch_utils.py
├── train_aux.py(使用较大的预训练权重)
├── train.py
├── test.py(测试)
├── detect.py(检测)
├── export.py
├── hubconf.py
├── LICENSE.md
├── README.md
├── README_EN.md
└── requirements.txt

代码学习

(仅自己训练过程中去了解过的内容)

1 train.py

1.1 参数

if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('--weights', type=str, default='weight\yolov7.pt', help='initial weights path')   # 初始化权重文件,如果有预训练模型,可以直接在此加载
    parser.add_argument('--cfg', type=str, default='cfg/training\yolov7.yaml', help='model.yaml path')                      # 网络结构配置文件
    parser.add_argument('--data', type=str, default='data\WindTurbineBlades.yaml', help='data.yaml path')     # 训练数据集配置文件
    parser.add_argument('--hyp', type=str, default='data/hyp.scratch.p5.yaml', help='hyperparameters path')         # 超参数配置文件
    parser.add_argument('--epochs', type=int, default=100)                # 训练迭代次数
    parser.add_argument('--batch-size', type=int, default=32, help='total batch size for all GPUs')                  # 训练批次大小
    parser.add_argument('--img-size', nargs='+', type=int, default=[640, 640], help='[train, test] image sizes')    # 训练图片大小
    parser.add_argument('--rect', action='store_true', help='rectangular training')             # 是否采用矩形训练,默认False
    parser.add_argument('--resume', nargs='?', const=True, default=False, help='resume most recent training')       # 是否继续进行训练,如果设置成True,那么会自动寻找最近训练权重文件
    parser.add_argument('--nosave', action='store_true', help='only save final checkpoint')     # 不保存权重文件,默认False
    parser.add_argument('--notest', action='store_true', help='only test final epoch')          # 不进行test,默认False
    parser.add_argument('--noautoanchor', action='store_true', help='disable autoanchor check') # 不自动调整anchor,默认False
    parser.add_argument('--evolve', action='store_true', help='evolve hyperparameters')         # 是否进行超参数优化,默认是False,开启该选项,会加大训练时间,一般不需要
    parser.add_argument('--bucket', type=str, default='', help='gsutil bucket')                 # 谷歌云盘bucket,一般不会用到
    parser.add_argument('--cache-images', action='store_true', help='cache images for faster training')             # 是否提前将训练数据进行缓存,默认是False
    parser.add_argument('--image-weights', action='store_true', help='use weighted image selection for training')   # 训练的时候是否选择图片权重进行训练
    parser.add_argument('--device', default='0', 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%%')    # 是否进行多尺度训练,默认False
    parser.add_argument('--single-cls', action='store_true', help='train multi-class data as single-class')         # 训练数据集是否只有一类
    parser.add_argument('--adam', action='store_true', help='use torch.optim.Adam() optimizer')                     # 是否使用adam优化器
    parser.add_argument('--sync-bn', action='store_true', help='use SyncBatchNorm, only available in DDP mode')     # 是否使用跨卡同步BN,在DDP模式使用
    parser.add_argument('--local_rank', type=int, default=-1, help='DDP parameter, do not modify')                  # DDP参数
    parser.add_argument('--workers', type=int, default=0, help='maximum number of dataloader workers')              # dataloader的最大worker数量
    parser.add_argument('--project', default='runs/train', help='save to project/name')                             # 训练结果保存路径
    parser.add_argument('--entity', default=None, help='W&B entity')    # wandb库对应的东西,一般不用管
    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('--linear-lr', action='store_true', help='linear LR')                   # 用于对学习速率进行调整,默认为 false,含义是通过余弦函数来降低学习率。
    parser.add_argument('--label-smoothing', type=float, default=0.0, help='Label smoothing epsilon')               # 是否做标签平滑,防止出现过拟合
    parser.add_argument('--upload_dataset', action='store_true', help='Upload dataset as W&B artifact table')       # wandb库对应的东西
    parser.add_argument('--bbox_interval', type=int, default=-1, help='Set bounding-box image logging interval for W&B')    # wandb库对应的东西
    parser.add_argument('--save_period', type=int, default=-1, help='Log model after every "save_period" epoch')            # 用于记录训练日志信息
    parser.add_argument('--artifact_alias', type=str, default="latest", help='version of dataset artifact to be used')      # 这一行参数表达的是想实现但还未实现的一个内容,忽略即可
    parser.add_argument('--freeze', nargs='+', type=int, default=[0], help='Freeze layers: backbone of yolov7=50, first3=0 1 2')    # 冻结训练,默认不冻结
    opt = parser.parse_args()

1.2 Resume 训练中断后继续训练

# Resume 
wandb_run = check_wandb_resume(opt)
    if opt.resume and not wandb_run:  # resume an interrupted run
        ckpt = opt.resume if isinstance(opt.resume, str) else get_latest_run()  # specified or most recent path
        assert os.path.isfile(ckpt), 'ERROR: --resume checkpoint does not exist'
        apriori = opt.global_rank, opt.local_rank
        with open(Path(ckpt).parent.parent / 'opt.yaml') as f:
            opt = argparse.Namespace(**yaml.load(f, Loader=yaml.SafeLoader))  # replace
        opt.cfg, opt.weights, opt.resume, opt.batch_size, opt.global_rank, opt.local_rank = '', ckpt, True, opt.total_batch_size, *apriori  # reinstate
        logger.info('Resuming training from %s' % ckpt)
    else:
        # opt.hyp = opt.hyp or ('hyp.finetune.yaml' if opt.weights else 'hyp.scratch.yaml')
        opt.data, opt.cfg, opt.hyp = check_file(opt.data), check_file(opt.cfg), check_file(opt.hyp)  # check files
        assert len(opt.cfg) or len(opt.weights), 'either --cfg or --weights must be specified'
        opt.img_size.extend([opt.img_size[-1]] * (2 - len(opt.img_size)))  # extend to 2 sizes (train, test)
        opt.name = 'evolve' if opt.evolve else opt.name
        opt.save_dir = increment_path(Path(opt.project) / opt.name, exist_ok=opt.exist_ok | opt.evolve)  # increment run

若训练过程中训练中断,将Resume改为True,程序会自动寻找最后一轮的权重,然后继续训练。

check_wandb_resume()检查训练是否恢复,wandb是一个用于深度学习实验跟踪、可视化和协作的工具和平台。

1.3 Train 训练

# Train
    logger.info(opt)
    if not opt.evolve:
        tb_writer = None  # init loggers
        if opt.global_rank in [-1, 0]:
            prefix = colorstr('tensorboard: ')
            logger.info(f"{prefix}Start with 'tensorboard --logdir {opt.project}', view at http://localhost:6006/")
            tb_writer = SummaryWriter(opt.save_dir)  # Tensorboard
        train(hyp, opt, device, tb_writer)

    # Evolve hyperparameters (optional) # tc
    else:
    ......

判断是否进行超参数优化,一般都是False;else后面的是超参数优化的参数,这之后的代码全是关于超参数优化的

if opt.global_rank in [-1, 0]检查是否使用分布式训练,-1表示没有,global_rank默认为-1。该条件语句内容是对Tensorboard进行设置。

train()函数,开始深度学习模型的训练

2 yolov7.yaml

2.1 parameters and anchors

# parameters
nc: 80  # number of classes
depth_multiple: 1.0  # model depth multiple
width_multiple: 1.0  # layer channel multiple

# anchors
anchors:
  - [12,16, 19,36, 40,28]  # P3/8
  - [36,75, 76,55, 72,146]  # P4/16
  - [142,110, 192,243, 459,401]  # P5/32

depth_multiple:控制模型深度,与number相乘(红色框位置),表示该层重复的次数

layer channel multiple:控制输出通道数(黄色框位置),与args第一个参数相乘

2.2 网络结构

from:表示其输入来自哪一层。

-1表示该层输入是上一层的输出,-2表示该层输入是上上层的输出,以此类推。若为正数则为正数第几层。[-1, -3, -5, -6]为倒数第一层、倒数第三层、倒数第五层、倒数第六层的输出。

number:表示该层的层数

module:表示使用的模块

args:表示模块的参数

从左到右为:输出通道数,卷积核大小,卷积核步长

# yolov7 backbone
backbone:
  # [from, number, module, args]
  [[-1, 1, Conv, [32, 3, 1]],  # 0
  
   [-1, 1, Conv, [64, 3, 2]],  # 1-P1/2      
   [-1, 1, Conv, [64, 3, 1]],
   
   [-1, 1, Conv, [128, 3, 2]],  # 3-P2/4  
   [-1, 1, Conv, [64, 1, 1]],    # 下面表示的就是ELAN模块
   [-2, 1, Conv, [64, 1, 1]],
   [-1, 1, Conv, [64, 3, 1]],
   [-1, 1, Conv, [64, 3, 1]],
   [-1, 1, Conv, [64, 3, 1]],
   [-1, 1, Conv, [64, 3, 1]],
   [[-1, -3, -5, -6], 1, Concat, [1]],
   [-1, 1, Conv, [256, 1, 1]],  # 11

3 detect.py

3.1 参数

if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('--weights', nargs='+', type=str, default='runs\train\exp\weights\best.pt', help='model.pt path(s)')    # 训练好的权重
    parser.add_argument('--source', type=str, default='inference', help='source')  # file/folder, 0 for webcam 测试的图片/图片文件夹/摄像头接口
    parser.add_argument('--img-size', type=int, default=640, help='inference size (pixels)')    # 图片大小
    parser.add_argument('--conf-thres', type=float, default=0.25, help='object confidence threshold')    # 置信度的阈值
    parser.add_argument('--iou-thres', type=float, default=0.45, help='IOU threshold for NMS')    # iou阈值
    parser.add_argument('--device', default='0', help='cuda device, i.e. 0 or 0,1,2,3 or cpu')
    parser.add_argument('--view-img', action='store_true', help='display 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')    # 是否保存测试时目标的置信度,要和save-txt一起使用
    parser.add_argument('--nosave', action='store_true', help='do not save images/videos')    # 是否保存测试图像
    parser.add_argument('--classes', nargs='+', type=int, help='filter by class: --class 0, or --class 0 2 3')    # 是否只检测特定的某一类或几类,如classes 0就只检测数据集中yolo标签为0的
    parser.add_argument('--agnostic-nms', action='store_true', help='class-agnostic NMS')    # 增强版的nms
    parser.add_argument('--augment', action='store_true', help='augmented inference')
    parser.add_argument('--update', action='store_true', help='update all models')
    parser.add_argument('--project', default='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('--no-trace', action='store_true', help='don`t trace model')

3.2 绘制检测框 plot_one_box()

该函数在utils/plots.py中

def plot_one_box(x, img, color=None, label=None, line_thickness=3):
    # Plots one bounding box on image img
    # 计算了绘制线条的粗细
    tl = line_thickness or round(0.002 * (img.shape[0] + img.shape[1]) / 2) + 1  # line/font thickness
    color = color or [random.randint(0, 255) for _ in range(3)]
    # 计算了边界框的两个对角点的坐标c1和c2,将x中的坐标信息四舍五入为整数,用于在图像上绘制边界框
    c1, c2 = (int(x[0]), int(x[1])), (int(x[2]), int(x[3])) 
    # 使用OpenCV的cv2.rectangle函数在图像上绘制一个矩形边界框
    cv2.rectangle(img, c1, c2, color, thickness=tl, lineType=cv2.LINE_AA)
    if label:    # 是否提供了标签文本
        # 计算了标签文本的字体线条粗细
        tf = max(tl - 1, 1)  # font thickness
        # cv2.getTextSize()函数能得到文字绘制出来将有多大
        t_size = cv2.getTextSize(label, 0, fontScale=tl / 3, thickness=tf)[0]
        c2 = c1[0] + t_size[0], c1[1] - t_size[1] - 3
        # 绘制一个填充矩形,用于容纳标签文本。这样可以在标签文本的位置上创建一个背景,使文本更容易阅读
        cv2.rectangle(img, c1, c2, color, -1, cv2.LINE_AA)  # filled
        # 将标签文本添加到图像上,参数为:图像,标签文本、文本框的左下角、字体、字体大小、颜色、字体线条粗细、线型
        cv2.putText(img, label, (c1[0], c1[1] - 2), 0, tl / 3, [225, 255, 255], thickness=tf, lineType=cv2.LINE_AA)

x:边界框坐标的列表,(x_min, y_min, x_max, y_max),表示左上角和右下角的坐标

img:要绘制边界框的图像

color:边界框和标签的颜色,默认是随机的

label:表示要添加到边界框上的标签文本

line_thickness:线条粗细,默认为3。设为None线条粗细就跟随图像大小变化

训练流程

1 数据集准备

本人标注得到的数据集是VOC格式的

1.0 数据集处理前的数据集结构

VOC
├── Annotations               标注(XML文件)
└── JPEGImages                图像

1.1 VOC格式转YOLO格式

(!!!要将‘.../VOC’改为自己的路径)

'''第一步:将xml转化成txt'''
import shutil
import os.path
import xml.etree.ElementTree as ET


# 1. 将这个地方改成自己类别的列表
class_names = ['**', '**', '**']
# 2. 将路径修改
xmldir='.../VOC/Annotations/'
imagedir=".../VOC/JPEGImages/"
txtdir='.../VOC/xml2txt/'

xmlpath=os.path.join(xmldir)# 原xml路径
imagepath=os.path.join(imagedir)# 原img路径
txtpath=os.path.join(txtdir)# 转换后txt文件存放路径

files = []
if not os.path.exists(txtpath):
    os.makedirs(txtpath)
else:
    shutil.rmtree(txtpath)
    os.makedirs(txtpath)

image_list=os.listdir(imagepath)
postfixes = set(['.' + i.split('.')[1] for i in image_list])    #考虑到可能有不同后缀的图片,提取所有后缀

for root, dirs, files in os.walk(xmlpath):  #用来
    None
number = len(files)
i = 0
delete=[]
while i < number:

    filename = files[i][0:-4]
    xml_name = filename + ".xml"
    txt_name = filename + ".txt"
    xml_file_name = os.path.join(xmlpath,xml_name)
    txt_file_name = os.path.join(txtpath,txt_name)
    try:
        xml_file = open(xml_file_name, 'r', encoding='utf-8')   #注意加个utf8编码就是,不然会报gbk的错
        tree = ET.parse(xml_file)
        root = tree.getroot()
        # filename = root.find('name').text

        # image_name = root.find('filename').text
        w = int(root.find('size').find('width').text)
        h = int(root.find('size').find('height').text)

        f_txt = open(txt_file_name, 'w+')
        content = ""

        first = True

        for obj in root.iter('object'):

            name = obj.find('name').text
            class_num = class_names.index(name)
            # class_num = 0

            xmlbox = obj.find('bndbox')

            x1 = int(xmlbox.find('xmin').text)
            x2 = int(xmlbox.find('xmax').text)
            y1 = int(xmlbox.find('ymin').text)
            y2 = int(xmlbox.find('ymax').text)

            if first:
                content += str(class_num) + " " + \
                           str((x1 + x2) / 2 / w) + " " + str((y1 + y2) / 2 / h) + " " + \
                           str((x2 - x1) / w) + " " + str((y2 - y1) / h)
                first = False
            else:
                content += "\n" + \
                           str(class_num) + " " + \
                           str((x1 + x2) / 2 / w) + " " + str((y1 + y2) / 2 / h) + " " + \
                           str((x2 - x1) / w) + " " + str((y2 - y1) / h)

        # print(str(i / (number - 1) * 100) + "%\n")
        print(content)
        f_txt.write(content)
        f_txt.close()
        xml_file.close()
        i += 1

    except ZeroDivisionError as zeroE:
        print(xml_name+'转化失败!')
        i += 1

        # #利用os库把xml_name文件复制到test文件夹下
        # shutil.move(xmldir + xml_name, 'test/' + xml_name)
        # shutil.move(imagedir + filename+'.jpg', 'test/' + filename+'.jpg')


        # 删除对应的label和images
        os.remove(os.path.join(xmlpath, xml_name))
        print(xml_name + '删除成功!')
        for postfix in postfixes:
            if (filename + postfix) in image_list:
                img_name = filename + postfix
        os.remove(os.path.join(imagepath, img_name))
        print(img_name + '删除成功!')
        delete.append((img_name, xml_name))
        i += 1

print('总图片数量:',number)
print(f'成功转化{number-len(delete)}张图片')
print(f'共删除{len(delete)}张图片和标签:')
for each in delete:
    print(each)

1.2 划分训练集、验证集、测试集

'''第二步:划分train.txt,val.txt,test.txt
注意trainval.txt是作为辅助'''
import os
import random
import shutil

random.seed(0)
# 1. 将路径修改为自己的
xmlfilepath = '.../VOC/Annotations/'
saveBasePath = '.../VOC/ImageSets/Main/'
if not os.path.exists(saveBasePath):
    os.makedirs(saveBasePath)
else:
    shutil.rmtree(saveBasePath)
    os.makedirs(saveBasePath)
# ----------------------------------------------------------------------#
#   想要增加测试集修改trainval_percent
#   train_percent不需要修改
# ----------------------------------------------------------------------#
trainval_percent = 0.9
train_percent = 0.8

temp_xml = os.listdir(xmlfilepath)
total_xml = []
for xml in temp_xml:
    if xml.endswith(".xml"):
        total_xml.append(xml)

num = len(total_xml)
list = range(num)
tv = int(num * trainval_percent)
tr = int(tv * train_percent)
trainval = random.sample(list, tv)
train = random.sample(trainval, tr)

print("train and val size", tv)
print("traub suze", tr)

if not os.path.exists(saveBasePath):
    os.mkdir(saveBasePath)
ftrainval = open(os.path.join(saveBasePath, 'trainval.txt'), 'w')
ftest = open(os.path.join(saveBasePath, 'test.txt'), 'w')
ftrain = open(os.path.join(saveBasePath, 'train.txt'), 'w')
fval = open(os.path.join(saveBasePath, 'val.txt'), 'w')

for i in list:
    name = total_xml[i][:-4] + '\n'
    if i in trainval:
        ftrainval.write(name)
        if i in train:
            ftrain.write(name)
        else:
            fval.write(name)
    else:
        ftest.write(name)

ftrainval.close()
ftrain.close()
fval.close()
ftest.close()

1.3 生成yolov7需要的文件夹格式

'''第三步:复制出图片和标签,生成yolov7需要的文件夹格式
----images
--train
--val
--test(可选)

----labels
--train
--val
--test(可选)'''

import os
import shutil
from tqdm import tqdm

SPLIT_PATH = '.../VOC/ImageSets/Main/'
IMGS_PATH = ".../VOC/JPEGImages/"
TXTS_PATH = ".../VOC/xml2txt/"

TO_IMGS_PATH = '.../VOC/images'
TO_TXTS_PATH = '.../VOC/labels'

data_split = ['train.txt', 'val.txt', 'test.txt']  
to_split = ['train', 'val', 'test']            

image_list=os.listdir(IMGS_PATH)
postfixes = set(['.' + i.split('.')[1] for i in image_list])    #考虑到可能有不同后缀的图片,提取所有后缀

for index, split in enumerate(data_split):
    split_path = os.path.join(SPLIT_PATH, split)

    to_imgs_path = os.path.join(TO_IMGS_PATH, to_split[index])
    if not os.path.exists(to_imgs_path):
        os.makedirs(to_imgs_path)
    else:
        shutil.rmtree(to_imgs_path)
        os.makedirs(to_imgs_path)

    to_txts_path = os.path.join(TO_TXTS_PATH, to_split[index])
    if not os.path.exists(to_txts_path):
        os.makedirs(to_txts_path)
    else:
        shutil.rmtree(to_txts_path)
        os.makedirs(to_txts_path)

    f = open(split_path, 'r')
    count = 1

    for line in tqdm(f.readlines(), desc="{} is copying".format(to_split[index])):
        # 复制图片
        for postfix in postfixes:
            if (line.strip()+ postfix) in image_list:
                img_name=line.strip()+ postfix
        src_img_path = os.path.join(IMGS_PATH, img_name)
        dst_img_path = os.path.join(to_imgs_path,img_name)
        if os.path.exists(src_img_path):
            shutil.copyfile(src_img_path, dst_img_path)
        else:
            print("error file: {}".format(src_img_path))

        # 复制txt标注文件
        src_txt_path = os.path.join(TXTS_PATH, line.strip() + '.txt')
        dst_txt_path = os.path.join(to_txts_path, line.strip() + '.txt')
        if os.path.exists(src_txt_path):
            shutil.copyfile(src_txt_path, dst_txt_path)
        else:
            print("error file: {}".format(src_txt_path))

1.4 创建最终训练需要的train.txt和val.txt文件

'''第四步:创建出最终训练需要的train.txt和val.txt文件 '''
import os
def listdir(path, list_name):  # 传入存储的list
    for file in os.listdir(path):
        file_path = os.path.join(path, file)
        if os.path.isdir(file_path):
            listdir(file_path, list_name)
        else:
            list_name.append(file_path)


list_name = []
train_path = '.../VOC/images/train'  # 文件夹路径,把images下的train/val/test(可选)文件夹处理就行 。注意这里开头的/不能少!!!
listdir(train_path, list_name)
print(list_name)

with open('.../VOC/train.txt', 'w') as f:  # 要存入的txt
    write = ''
    for i in list_name:
        write = write + str(i) + '\n'
    f.write(write)


list_name = []
val_path = '.../VOC/images/val'  # 文件夹路径,把images下的train/val/test(可选)文件夹处理就行 。注意这里开头的/不能少!!!
listdir(val_path, list_name)
print(list_name)
with open('.../VOC/val.txt', 'w') as f2:  # 要存入的txt
    write = ''
    for i in list_name:
        write = write + str(i) + '\n'
    f2.write(write)

1.5 最终数据集结构

VOC
├── Annotations               所有的图像标注信息(XML文件)
├── JPEGImages                所有图像文件
├── ImageSets        
│   └── Main
│         ├── train.txt       训练集
│         ├── val.txt         验证集
│         ├── trainval.txt    训练集+验证集
│         └── test.txt        测试集
├── images        
│   ├── train
│   ├── val
│   └── test
├── labels        
│   ├── train
│   ├── val
│   └── test
├── xml2txt                   YOLO格式标注
├── train.txt                 用于训练的train.txt
└── val.txt                   用于训练的val.txt

2 需要修改的参数

2.1 --weights

根据自己所选的网络选择预训练权重,也可以为空(即不使用预训练模型)

2.2 --cfg

选择网络结构,同时要修改对应yaml文件中的内容

# parameters
nc: 80  # number of classes    这里要修改为自己的类别数
depth_multiple: 1.0  # model depth multiple
width_multiple: 1.0  # layer channel multiple

# anchors    根据自己需要看要不要修改
anchors:
  - [12,16, 19,36, 40,28]  # P3/8
  - [36,75, 76,55, 72,146]  # P4/16
  - [142,110, 192,243, 459,401]  # P5/32

2.3 --data

数据集配置文件,将路径改为自己需要的,同时修改yaml文件中的内容

# Train/val/test sets as 1) dir: path/to/imgs, 2) file: path/to/imgs.txt, or 3) list: [path/to/imgs1, path/to/imgs2, ..]
# path: coco128  # dataset root dir
train: .../VOC/train.txt  # 数据集准备1.4中生成的
val: .../VOC/val.txt  # 数据集准备1.4中生成的
# test:  # test images (optional)

# Classes
nc: 80  # number of classes 修改为自己的类别数
names:  ['**', '**', '**']  # class names 修改为自己的类别,要和数据集准备1.1部分类别顺序一致

2.4 --hyp

网络超参数,路径改为自己需要的对应yaml文件

2.5 --epochs

2.6 --batch-size

2.7 --device

训练时用的设备,没有gpu就写cpu,有就写编号

2.8 --project

训练结果保存路径,一般不用改,要想放到别的文件夹下就改成自己的路径

2.9 --workers

是否多线程读取数据,越大cpu读取速度越快。

单张图像太大的时候可以填大一点,一般为0、2、4、8、16

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

目标检测-YOLOv7代码及训练 的相关文章

随机推荐

  • 一文读懂MySQL事务:提升你的数据库操作技能

    概述 事务可以保证多个操作原子性 要么全成功 要么全失败 对于数据库来说事务保证批量的DML要么全成功 要么全失败 事务具有四个特征ACID 原子性 Atomicity 整个事务中的所有操作 必须作为一个单元全部完成 或全部取消 一致性 C
  • pytdx接口API说明

    标准行情接口API pytdx hq 下面是如何在程序里面调用本接口 首先需要引入 from pytdx hq import TdxHq API 然后 创建对象 api TdxHq API 之后 通常是如下的格式 if api connec
  • python可以在多种平台运行 这体现了python语言的_Python快速编程入门课后习题答案...

    目录 前言 第一章 一 填空题 二 判断题 三 选择题 第二章 一 填空题 二 判断题 三 选择题 第三章 一 填空题 二 判断题 三 选择题 第四章 一 单选题 二 判断题 三 填空题 第五章 一 选择题 二 判断题 三 填空题 第六章
  • 3_服务容错_保护断路器_@EnableCircuitBreaker和引入@SpringCloudApplication

    前言 前面做了一个简单的服务 注册 服务发现 服务提供者和消费者的项目 现在我们还是准备之前的项目代码 1 服务容错保护 准备的项目工程 1 服务注册中心 端口为1111 2 服务提供者 端口为8080 8081 3 服务消费者 端口为90
  • 华为OD机试真题- 天然蓄水库【2023】【JAVA、Python、C++】

    题目描述 描述 公元2919年 人类终于发现了一颗宜居星球 X星 现想在X星一片连绵起伏的山脉间建一个天热蓄水库 如何选取水库边界 使蓄水量最大 要求 山脉用正整数数组s表示 每个元素代表山脉的高度 选取山脉上两个点作为蓄水库的边界 则边界
  • Springboot连接FISCO-BCOS链

    依赖 FISCO BOCS 2 5 0 JAVA 1 8 Spring Boot 2 0 2 官方文档 前期准备 部署FISCO BOCS 2 5 0 引入SDK
  • ue 清理缓存_mpvue应用之组件数据缓存清理

    题图 被mpvue周期搞疯的你 之前介绍过美团外卖C端 H5 小程序页面大量基于mpvue框架的能力 使用Vue组件打通了原生小程序页面和H5页面的隔阂 再结合小程序自带的原生组件功能 就构成了整个外卖跨端应用的终端部分架构 但是随着页面规
  • 唐诗三百首出现最多的字是什么?大数据分析告诉你

    一个人要是长时间一直做一件事 思维容易固化 就像那些从太空回来的宇航员 吃饭时容易丢开拿在手里的勺子 当勺子掉在地上时 哦 我已经回到地球了 所以 适当尝试新鲜事物 既是对大脑的放松 也是开阔思路的好机会 好方法 那这次 博主尝试的新事物是
  • 腾讯面经 集合

    腾讯机器学习复试 gbtd和xgboost区别和优缺点 XGBoost是GBDT一个工程化的实现 第一 GBDT将目标函数泰勒展开到一阶 而xgboost将目标函数泰勒展开到了二阶 使用泰勒展开到二阶的原因 统一损失函数求导的形式以支持自定
  • CFLAGS详解

    Makefile选项CFLAGS LDFLAGS LIBS CFLAGS 表示用于 C 编译器的选项 CXXFLAGS 表示用于 C 编译器的选项 这两个变量实际上涵盖了编译和汇编两个步骤 CFLAGS 指定头文件 h文件 的路径 如 CF
  • XXXXXXXXXXXXX

    Netflix主机性能监视工具Vector http www chinaz com web 2015 0414 398451 shtml 云雀科技是总部位于西雅图的新兴的云服务公司 利用业界 最新技术打造下一代具有高密度动态计算能力的云平台
  • 初学网络安全不可不知的:10款开源安全工具

    随着互联网的不断发展 安全问题也越来越受到企业的重视 但安全问题往往需要大量资金的投入 例如聘请安全工程师 产品研发 测试等流程 这对于那些原本就资金紧缺的企业而言 是绝对无法接受的 因此 为了减少在这方面的资金投入 许多安全人员都会选择使
  • keyframes介绍与调用动画方法

    keyframes介绍 keyframes changecolor 0 background red 50 background red 100 background green 在一个 keyframes 中的样式规则可以由多个百分比构成
  • APB总线详解及手撕代码

    本文的参考资料为官方文档AMBA 3 APB Protocol specification 文档下载地址 https pan baidu com s 1Vsj4RdyCLan6jE quAsEuw pwd w5bi 提取码 w5bi APB
  • Spring Bean如何保证并发安全

    1 可以设置Bean的作用域为原型 这样每次从容器中获取到的Bean就是一个新的实例 避免了多线程共享同一个对象实例的问题 2 不改变Bean作用域的情况下 可以避免在Bean中存可变状态的声明 尽量使用局部变量或使用线程安全的数据结构 3
  • Visual Station 2022的头文件包含目录设置的区别

    1 前言 对vs的包含头文件的组织 你困惑吗 困惑 有点困惑 BullS 在没喊出 BullSHI 之前 我先查了很多博客 结果 有的说 VC Directories gt Include Directories 适用于当前项目 C C g
  • BUUCTF [ACTF2020 新生赛]Exec

    1 刚打开的时候如图所示 先随便用一个地址127 0 0 1 2 然后查下目录127 0 0 1 ls ls看文件目录 发现flag 直接抓127 0 0 1 cat flag 拿到flag flag fdc896b1 e032 4e87
  • 如何训练自己的tokenizer

    训练自己的 tokenizer 通常需要以下几个步骤 准备数据 选择一些文本数据作为训练数据 并将其按照一定的方式拆分成若干个 token 例如将句子按照空格拆分成单词 选择模型 选择一种适合你的任务的模型 例如基于字符的模型或基于单词的模
  • electron 生成 arm64 的包

    vue electron https blog csdn net qq1195566313 article details 131713875 打包配置修改如下 electronBuilder build config appId com
  • 目标检测-YOLOv7代码及训练

    目录 论文及代码下载 代码结构 代码学习 1 train py 1 1 参数 1 2 Resume 训练中断后继续训练 1 3 Train 训练 2 yolov7 yaml 2 1 parameters and anchors 2 2 网络