• 还是得从代码角度看yolov5(1)


    train

    【参考文章】:又是经典写到一半发现别人写的更好

    基础函数

    (1)setattr

    setattr(opt, k, v)

    将给定对象上的命名属性设置为指定值。
    等价于opt.k=v

    (2)getattr

    callback=getattr(loggers, k)

    getattr(对象,名称[,默认值])->值
    从对象中获取命名属性;getattr(x,‘y’)等价于x.y
    当给定默认参数时,当属性未指定时返回该参数
    存在如果没有它,则会在这种情况下引发异常。

    (3)register_action

    callbacks.register_action(k, callback=getattr(loggers, k))

    注册器机制的引入是为了使工程的扩展性变得更好。当产品增加某个功能需要增加一些新函数或者类时,它可以保证我们可以复用之前的逻辑。
    注册器的相关内容

    (4)parse_known_args

    opt = parser.parse_known_args()[0] if known else parser.parse_args()
    
    • 1

    known在def parse_opt(known=False)
    parse_known_args()返回的是一个有两个元素的元组,第一个元素是NameSpace,第二个是一个列表。
    NameSpace:如果用命令行输入则按照指定的值,否则则使用默认值
    列表:将传入的参数以空格为分节符,还没有使用add_argument进行定义的将会以以字符串的形式存放在列表中

    关于parse_known_args的解析这篇写的可以的

    (4)check_dataset

    在yolov5的使用

    data_dict = data_dict or check_dataset(data)  # check if None
    
    
    • 1
    • 2

    检查data,实际上是传入yaml文件,将yaml文件内容转化为字典类型进行存放,最终返回的是字典类型
    此处的data文件的来源是:

    parser.add_argument(‘–data’, type=str, default=ROOT / ‘data/coco128.yaml’, help=‘dataset.yaml path’)

    格式上的问题

    (1)if-else格式

    才识浅陋第一次看见这种结构记录一下

    nc = 1 if single_cls else int(data_dict['nc'])
    
    • 1

    定义了single_cls(bool)则nc=1否则是int(data_dict[‘nc’])

    (2)分布式训练

    一般不做分布式训练因此我觉得只要了解这个概念就行

    RANK == -1:single-GPU only

    LOCAL_RANK = int(os.getenv('LOCAL_RANK', -1))  # https://pytorch.org/docs/stable/elastic/run.html
    RANK = int(os.getenv('RANK', -1))
    WORLD_SIZE = int(os.getenv('WORLD_SIZE', 1))
    
    • 1
    • 2
    • 3

    world_size:全局进程个数。
    rank:表示进程序号,用于进程间通信,可以用于表示进程的优先级。我们一般设置 rank=0 的主机为 master 节点。
    local_rank:进程内 GPU 编号,非显式参数,由 torch.distributed.launch 内部指定。比方说, rank=3,local_rank=0 表示第 3 个进程内的第 1 块 GPU

    具体定义了这几个变量含义
    包括后面的DDP mode都是自定义相关的内容

    自定义main函数

    (1) Resume

    resume通过if和else分为两个部分,if部分是判断是否断点恢复训练

    # Resume
      if opt.resume and not check_wandb_resume(opt) and not opt.evolve:  # 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'
            with open(Path(ckpt).parent.parent / 'opt.yaml', errors='ignore') as f:
                opt = argparse.Namespace(**yaml.safe_load(f))  # replace
            opt.cfg, opt.weights, opt.resume = '', ckpt, True  # reinstate
            LOGGER.info(f'Resuming training from {ckpt}')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    else部分:不进行断点恢复(正常训练等情况)都会进入这个分支

    else:
        opt.data, opt.cfg, opt.hyp, opt.weights, opt.project = \
            check_file(opt.data), check_yaml(opt.cfg), check_yaml(opt.hyp), str(opt.weights), str(opt.project)  # checks
        assert len(opt.cfg) or len(opt.weights), 'either --cfg or --weights must be specified'
        if opt.evolve:
            if opt.project == str(ROOT / 'runs/train'):  # if default project name, rename to runs/evolve
                opt.project = str(ROOT / 'runs/evolve')
            opt.exist_ok, opt.resume = opt.resume, False  # pass resume to exist_ok and disable resume
        opt.save_dir = str(increment_path(Path(opt.project) / opt.name, exist_ok=opt.exist_ok))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    (2)Evolve hyperparameters

    parser.add_argument('--evolve', type=int, nargs='?', const=300, help='evolve hyperparameters for x generations')
    
    • 1

    使用定义好的yaml文件进行超参数进化

    超参数进化超参数进化列表,括号里分别为(突变规模, 最小值,最大值)

    在这里插入图片描述

    (3)select_device

    还是提一嘴这个函数,先给在yolov5中的使用

    device = select_device(opt.device, batch_size=opt.batch_size)
    
    • 1

    opt.device实际上我在yaml文件中并没有对这个赋值,所以看了一下函数内部实际上是会自动选择cuda或者是cpu。会将cpu/cuda添加到环境变量中,像这样:

    if cpu:
        os.environ['CUDA_VISIBLE_DEVICES'] = '-1'  
        # force torch.cuda.is_available() = False
    
    • 1
    • 2
    • 3

    predict

    由于下面会提及我的结构,先给出结构

    在这里插入图片描述

    解读

    FILE = Path(__file__).resolve()
    ROOT = FILE.parents[0]  # YOLOv5 root directory
    if str(ROOT) not in sys.path:
        sys.path.append(str(ROOT))  # add ROOT to PATH
    ROOT = Path(os.path.relpath(ROOT, Path.cwd()))  # relative
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    首先明确一个观点:__file__表示当前python脚本运行的路径,而且不一定返回绝对路径
    (具体内容看这篇blog)

    如果当前文件包含在sys.path里面,那么,__file__返回一个相对路径!
    如果当前文件不包含在sys.path里面,那么__file__返回一个绝对路径!

    以此处为例在我的文件夹中的__file__应该是

    D:\computervision\deep_learning_model\yolov5-6.1\detect.py

    resolve() :将返回当前工作目录的绝对路径
    使路径成为绝对路径,解析途中的所有符号链接,并规范化它(例如在Windows)。

    parents返回所有上级(祖先)目录列表,[上级目录, 上上级目录, …, 根目录],祖先是父、祖父、曾祖父,依此类推,一直到根目录为止;
    [上级目录, 上上级目录, …, 根目录]依次对应parents[0],parents[1],…

    ROOT = FILE.parents[0]得到的路径是 D:\computervision\deep_learning_model\yolov5-6.1

    cwd():python中cwd其实就是current work dir 的意思,就是当前工作目录,返回当前工作目录
    relpath:把绝对路径转换为相对路径

  • 相关阅读:
    WPF中数据绑定验证深入讲解
    java数据结构与算法刷题-----LeetCode58:最后一个单词的长度
    开发 Diffusers 库的道德行为指南
    解密负载均衡技术和负载均衡算法
    兑换码设计
    云计算中的过度授权:安全隐患与应对策略
    【Java基础】变量、标识符及类型转换
    数据结构与算法(java)--哈希表
    Android versionCode会变成指定数值加001、002、003等后缀
    测试项目中的风险管理
  • 原文地址:https://blog.csdn.net/weixin_50862344/article/details/126006494