yolov5中train.py代码注释详解与使用教程

1. yolov5 train.py代码注释详解

1.1 训练器的初始化

在train.py代码中,我们首先会看到的是训练器的初始化部分,包括一些参数的设置,比如学习率、momentum、weight_decay等。

# 参数设置

parser = argparse.ArgumentParser()

parser.add_argument('--epochs', type=int, default=300) # 训练轮数

parser.add_argument('--batch-size', type=int, default=16) # batch size大小

parser.add_argument('--cfg', type=str, default='./models/yolov5s.yaml', help='模型配置文件路径') # 模型配置路径

parser.add_argument('--data', type=str, default='./data/coco128.yaml', help='数据配置文件路径') # 数据集配置路径

parser.add_argument('--weights', type=str, default='./weights/yolov5s.pt', help='初始化权重文件路径') # 模型初始权重路径

parser.add_argument('--device', default='', help='cuda设备号 eg. 0 or 0,1 or cpu') # 运行设备 cuda or cpu

parser.add_argument('--img-size', nargs='+', type=int, default=[640, 640], help='训练时图像大小') # 图像输入大小

parser.add_argument('--rect', action='store_true', help='矩形输入 размер')

parser.add_argument('--resume', nargs='?', const=True, default=False, help='从checkpoint恢复调整训练位置')

parser.add_argument('--nosave', action='store_true', help='只保存最终权重')

parser.add_argument('--notest', action='store_true', help='不进行测试')

parser.add_argument('--evolve', action='store_true', help='进化操作') # 进化操作,优化超参数

parser.add_argument('--bucket', type=str, default='', help='gsutil所在的S3路径(gs://bucket 或 s3://bucket)')

parser.add_argument('--cache-images', action='store_true', help='进行预加载(缓存)')

parser.add_argument('--hyp', type=str, default='data/hyp.scratch.yaml', help='超参数路径')

parser.add_argument('--project', default='runs/train', help='tensorboard保存路径')

parser.add_argument('--name', default='exp', help='tensorboard保存名称') # tensorboard记录名称

parser.add_argument('--exist-ok', action='store_true', help='文件已存在时运行报错')

parser.add_argument('--quad', action='store_true', help='quad transform')

parser.add_argument('--linear-lr', action='store_true', help='linear LR')

opt = parser.parse_args()

这些参数设置会影响模型的训练结果,比如学习率的大小决定了模型的训练速度和稳定性,而momentum和weight_decay则可以优化损失函数的下降和防止过拟合等问题。

1.2 数据读取和模型加载

刚刚设置好了训练器的参数,我们接下来要加载数据集,并且载入模型,为训练做好准备。

def train(hyp, opt, device):

# 加载模型配置

model_cfg = opt.cfg

assert model_cfg.endswith('.yaml'), '仅支持yaml模型配置文件'

with open(model_cfg) as f:

model = Model(yaml.safe_load(f), ch=3, nc=int(opt.data.split('.')[-1]))

# 加载模型的预训练权重模型

ckpt = opt.weights if opt.weights.endswith('.pt') else last_checkpoint(opt.weights)

start_epoch, best_fitness = 0, 0.0

if ckpt is not None:

# 加载权重模型的状态

ckpt = torch.load(ckpt, map_location=device)

# 从检查点中加载学习率、轮次和最佳的fitness

if opt.resume or opt.transfer:

start_epoch = ckpt['epoch'] + 1

# 忽略学习率为“None”的CKPT参数

hyp['lr0'] = hyp.get('lr0', 0) or 0.01

# 我们不要恢复best_fitness,除非我们从检查点剪切或转移

best_fitness = ckpt['best_fitness'] if 'best_fitness' in ckpt else 0.0

# 加载模型权重

state_dict = ckpt['model'].float().state_dict()

# 加载权重

try:

# 尝试加载所有模型参数

model.float().state_dict().update(state_dict)

except:

# 加载模型参数失败,则只加载最后一层的权重数据

model_info(model)

for k, v in state_dict.items():

k = k.replace('model.', '')

model.load_state_dict(state_dict, strict=False)

if opt.transfer: # 从预处理函数中进行transfer装换

print(f'Transferring {ckpt["model"].yaml["nc"]} -> {model.nc}...')

model = model.transfer(ckpt['model'], ip_size=tuple(opt.img_size))

del ckpt, state_dict

# 加载数据集

with open(opt.data) as f:

data_dict = yaml.load(f, Loader=yaml.FullLoader)

# 定义数据集以及数据增强器

train_path = data_dict['train']

val_path = data_dict['val']

nc = int(data_dict['nc'])

hyp['cls'] *= nc / 80.0

train = LoadImagesAndLabels(train_path, opt.img_size, opt.batch_size, augment=True, hyp=hyp, rect=opt.rect, cache_images=opt.cache_images)

val = LoadImagesAndLabels(val_path, opt.img_size, opt.batch_size * 2, augment=False, hyp=hyp, rect=True)

# 开启数据集缓存(可加速数据加载)

if opt.cache_images:

cache = CacheImages(train, path=os.path.join(os.getcwd(), 'train.cache'), mmap=False)

train = ConcatDataset([cache.create(i) for i in range(len(train.sources))])

val = CacheImages(val, path=os.path.join(os.getcwd(), 'val.cache'), mmap=False)

return model, train, val, start_epoch, best_fitness

这部分代码主要是加载模型的配置文件,并将其传入Model初始化方法中,以此构建模型。此外,还会根据接下来要训练的数据集设置数据集的路径,并且构建一个数据增强器,提高数据的多样性,增强模型的泛化能力。

1.3 模型训练

接下来是模型的训练部分,这部分代码的主要作用是根据前面已经设置好的参数,开始对模型进行训练并优化权重。

def train(hyp, opt, device):

# ...

# 训练器的初始化

t0 = time.time()

nw = max(round(hyp['warmup_epochs'] * len(train) / opt.batch_size), 1000) # warmup运行时间

# 完整的训练逻辑

print(f'{prefix}开始训练{epochs}个epochs,batch_size = {opt.batch_size},img_sz = {train.shapes[-1][0]}')

for epoch in range(start_epoch, epochs):

t1 = time.time()

# 在EPOCHS末尾的超参数变化LR_POLY,MOMSCHED

adjust_learning_rate(optimizer, epoch, hyp['epochs'], hyp['lr0'])

adjust_momentum(optimizer, epoch, hyp['epochs'], hyp['momentum'])

#训练单次epoch(一遍图片)

train_losses = []

nbs = len(train) # 在不同的图片之间切换的次数(”,“数属于data.yaml中,图片的数量除以batch size,batch size是16)

for i, (imgs, targets, _, _) in enumerate(train):

# 在warmup期间,调整LR,从0逐渐达到LR0

ni = i + nbs * epoch

if ni <= nw:

xi = [0, nw] # 学习率变化的范围

# 我们在这里用imag的第二(initial_lr)参数来调整LR变化的幅度(lf)

# 这是为了避免数据依赖性超出一个EPOCH,保证了一致性集成运算

lf = lambda x: 1.0 * (1 - x / nw) ** 0.9 # \x\ 0-nw 之间进行线性降低

hyp['lr'] = opt.hyp['lr0'] * lf(ni / (nw - 1) if nw > 1 else 1) # 迭代周期t

# 更新网络学习(前向和反向)

loss, _ = model(imgs.to(device), targets.to(device)) # 运行 loss = model(imgs, targets)

# 记录训练损失

loss.backward() # 对权重进行反向、偏向、反向传播

if ni <= nw:

# 在warmup期间权重不会改变

accumulate(model_grads, model) # 累加渐变

if ni % accumulate == 0:

# 使用更新后的(已累积过的梯度)梯度更新,再将梯度重设为0

optimizer.step()

optimizer.zero_grad()

# 规范化损失

loss = loss.item()

train_losses.append(loss)

# 打印训练结果

if rank in [-1, 0]:

# 进度条日志记录logger[master].tensorboard

s = ('%8s%12s' + '%10.3g' * 6) % (

f'{epoch}/{epochs - 1}', f'{i}/{nbs - 1}', *losses.avg, hyp['lr'], imgs.shape[-1])

pbar.set_description(s)

# 保存训练结果到tensorboard

n_iter = epoch * nbs + i

tb_writer.add_scalar('loss', loss, n_iter)

tb_writer.add_scalar('lr', hyp['lr'], n_iter)

tb_writer.add_scalar('n_img', imgs.shape[0], n_iter)

# 内存管理gc

if gc and ni % 3 == 0:

#gc.collect()

torch.cuda.empty_cache()

# 进行一次验证

results, maps = val.run(data_dict, epoch, img_size=img_size, batch_size=batch_size * 2, adam=None)

if rank in [-1, 0]:

tb_writer.add_scalar('mAP', maps, epoch)

# 保存最优结果

fitness = sum([k[-1] for k in results.values()]) / len(results)

if fitness > best_fitness:

best_fitness = fitness

# 保存权重模型

if rank in [-1, 0]:

save = not opt.nosave or (epoch == epochs - 1)

if save:

with open(f'{opt.name}.pt', 'wb') as f:

f.write(get_latest_run()[-1]) # 最新的运行模型

# 保存最优参数,并显示

if best_fitness == fitness and epoch > 0:

with open(f'{opt.name}_best.pt', 'wb') as f:

f.write(get_latest_run()[-1])

print(f'已经保存最优模型的参数到 {opt.name}_best.pt...')

# CPU释放

del results, maps

#gc.collect();torch.cuda.empty_cache() # 内存管理gc

# 训练器运行时间记录

if rank in [-1, 0]:

tb_writer.close()

dist.destroy_process_group() if worlds_size > 1 else None

torch.cuda.empty_cache()

print(f"{prefix}训练完毕, 共花费{(time.time() - t0) / 3600:.2f}小时")

可以看到,在训练过程中,每个epoch都需要调整学习率和动量等超参数,以优化模型参数的更新。训练结束后,我们需要保存最优的权重,以便后续的使用。

2. yolov5 train.py使用教程

2.1 环境安装

在使用yolov5 train.py之前,需要确保计算机已经安装好了必要的环境,比如CUDA、cuDNN等,同时在Python中需要安装好torch和torchvision,可以执行以下代码尝试:

# 安装PyTorch

pip install torch torchvision

# 安装yolov5的依赖库

pip install -U -r requirements.txt

2.2 数据准备

在运行训练之前,需要准备训练数据集,需要满足以下条件:

- 数据集图片需要处理为统一的尺寸(比如640x640)。

- 数据集需要按照一定的目录结构组织,比如:

data/

├── train/

│ ├── 1.jpg

│ ├── 1.txt

│ ├── 2.jpg

│ ├── 2.txt

│ ├── ...

│ ├── classes.txt

│ └── train.txt

└── val/

├── 1.jpg

├── 1.txt

├── 2.jpg

├── 2.txt

├── ...

├── classes.txt

└── val.txt

# 其中,train.txt和val.txt保存的是图片路径和标注信息的映射

# classes.txt保存了类别信息

在上述文件夹中,train/表示训练集,val/表示验证集。每个图片都对应一个txt文件,里面保存了该图片上各个物体的位置信息与类别信息。

2.3 模型训练

准备好数据之后,我们就可以开始进行模型训练了。这里,我们以训练yolov5s模型为例,可以执行以下代码:

# 设置参数

$ python train.py --data /path/to/data --cfg ./models/yolov5s.yaml --weights '' --batch-size 16

# 进行训练:10 epochs以及正常的batch_size为16和img_size为640,使用默认数据集、模型配置和超参数

$ python train.py --epochs 10

# 继续历史训练

$ python train.py --resume

# 实时迭代超参数(超参数优化)

$ python train.py --evolve

# 多GPU训练

$ python train.py --device 0,1,2,3

# 更多参数运行方式详见README.md

2.4 模型应用

模型训练完成之后,我们可以使用训练好的权重文件进行模型应用。整个过程比较简单,只需要调用detect.py文件,并设置好相应参数即可,可以执行以下代码:

$ python detect.py --source /path/to/image/or/video --weights /path/to/weights --conf 0.25 ...

这里,source表示要进行检测的图片或视频路径,weights表示训练好的权重文件。conf参数是检测的置信度阈值,当检测结果的置信度小于该阈值时,该检测结果会被过滤掉。

3.5 结语

yolov5是一种基于深度学习的目标检测算法,它具有模型轻量化、速度快等优点

后端开发标签