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()
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
检查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'])
定义了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))
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}')
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))
(2)Evolve hyperparameters
parser.add_argument('--evolve', type=int, nargs='?', const=300, help='evolve hyperparameters for x generations')
使用定义好的yaml文件进行超参数进化
超参数进化超参数进化列表,括号里分别为(突变规模, 最小值,最大值)
(3)select_device
还是提一嘴这个函数,先给在yolov5中的使用
device = select_device(opt.device, batch_size=opt.batch_size)
opt.device实际上我在yaml文件中并没有对这个赋值,所以看了一下函数内部实际上是会自动选择cuda或者是cpu。会将cpu/cuda添加到环境变量中,像这样:
if cpu:
os.environ['CUDA_VISIBLE_DEVICES'] = '-1'
# force torch.cuda.is_available() = False
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
首先明确一个观点:__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:把绝对路径转换为相对路径