【YOLOv3】源码(train.py)
概述
主要模块分析
- 参数解析与初始化
- 功能:解析命令行参数,设置训练配置
- 项目经理制定详细的施工计划和资源分配
- 日志记录与监控
- 功能:初始化日志记录器,配置监控系统
- 项目经理使用监控和记录工具,实时跟踪施工进度和质量
- 模型与数据加载
- 功能:加载模型权重和配置文件,准备训练数据
- 项目经理选择建筑设计方案,准备施工材料和组织施工队伍
- 优化器与学习率调度器设置
- 功能:设置优化器和学习率调度器,指导模型参数更新
- 项目经理分配施工资源,制定施工进度计划
- 训练循环
- 功能:执行模型的前向传播、损失计算、反向传播和参数更新
- 施工队每日执行施工任务,项目经理监控进度和质量
- 验证与评估
- 功能:定期验证模型性能,评估训练效果
- 项目经理进行阶段性质量检查,评估施工质量和进度
- 模型保存与早停机制
- 功能:保存模型状态,应用早停机制优化训练过程
- 项目经理记录施工进度和质量,决定是否调整或终止施工计划
主要模块
参数解析与初始化
一般在训练模型的时候,需要在这里调整相应的参数,这类似于建筑项目经理制定详细的施工计划和资源分配
常用设置参数
- --weights:模型初始权重路径,通常设置为预训练模型路径,例如YOLOve.pt
- --cfg:模型结构的 YAML 配置文件路径,例如yolov3.yaml
- --data:数据集配置文件路径,定义训练/验证数据集的路径和类别等信息
- --hyp:超参数配置文件路径,控制训练的优化器、学习率等超参数
- --epochs:训练的总轮数,决定训练时长
- --batch-size:批量大小,影响内存占用和训练速度
- --imgsz:输入图像的尺寸
- --device:指定使用的设备,0就表示GPU0
- --adam:是否使用 Adam 优化器(默认使用 SGD)
W&B 参数(类似项目中的监控和记录工具)
- --entity:设置 W&B 的实体名称,用于项目关联
- --upload_dataset:是否将数据集上传到 W&B Artifact Table
- --bbox_interval:控制目标框日志记录的间隔
def parse_opt(known=False):"""函数功能:用于解析命令行参数,设置训练、验证和测试时的超参数及其他相关配置。参数:known (bool): 是否只解析已知的命令行参数。如果为 True,则返回已知参数,忽略其他参数。返回:argparse.Namespace: 包含解析后参数的命名空间对象 `opt`。"""import argparse# 创建 ArgumentParser 对象parser = argparse.ArgumentParser()# ---------------------------- 常用参数配置 ----------------------------------# 权重文件路径parser.add_argument('--weights', type=str, default=ROOT / 'weight/yolov3.pt',help='initial weights path (初始权重文件路径)')# 模型配置文件路径parser.add_argument('--cfg', type=str, default='models/yolov3.yaml',help='model.yaml path (模型结构配置文件路径)')# 数据集配置文件路径parser.add_argument('--data', type=str, default=ROOT / 'data/you.yaml',help='dataset.yaml path (数据集配置文件路径)')# 超参数配置文件路径parser.add_argument('--hyp', type=str, default=ROOT / 'data/hyps/hyp.scratch.yaml',help='hyperparameters path (超参数配置文件路径)')# 训练周期数parser.add_argument('--epochs', type=int, default=20,help='Number of epochs to train (训练的总轮数)')# 批量大小parser.add_argument('--batch-size', type=int, default=4,help='Total batch size for all GPUs, -1 for autobatch (总的批量大小)')# 图像大小parser.add_argument('--imgsz', '--img', '--img-size', type=int, default=416,help='train, val image size (pixels) (训练和验证时图像的输入尺寸)')# 是否使用矩形训练parser.add_argument('--rect', action='store_true', default=True,help='rectangular training (是否使用矩形训练)')# 是否恢复最近一次的训练parser.add_argument('--resume', nargs='?', const=True, default="",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 (只在最后一轮进行验证)')# 是否禁用自动生成 anchorsparser.add_argument('--noautoanchor', action='store_true',help='disable autoanchor check (禁用自动生成 anchor 的功能)')# 超参数进化parser.add_argument('--evolve', type=int, nargs='?', const=300,help='evolve hyperparameters for x generations (超参数进化代数)')# Google Cloud Bucketparser.add_argument('--bucket', type=str, default='',help='gsutil bucket (Google 云存储桶路径)')# 是否缓存数据集到 RAM 或磁盘parser.add_argument('--cache', type=str, nargs='?', const='ram', default=True,help='--cache images in "ram" (default) or "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 (单类别训练)')# 是否使用 Adam 优化器parser.add_argument('--adam', action='store_true',help='use torch.optim.Adam() optimizer (使用 Adam 优化器)')# 是否启用同步 BatchNormparser.add_argument('--sync-bn', action='store_true',help='use SyncBatchNorm, only available in DDP mode (同步 BatchNorm,仅适用于 DDP 模式)')# 数据加载器的最大工作线程数parser.add_argument('--workers', type=int, default=1,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('--linear-lr', action='store_true',help='linear LR (启用线性学习率)')# 标签平滑参数parser.add_argument('--label-smoothing', type=float, default=0.0,help='Label smoothing epsilon (标签平滑参数 epsilon)')# 提前停止的容忍轮数parser.add_argument('--patience', type=int, default=1000,help='EarlyStopping patience (epochs without improvement) (提前停止的容忍轮数)')# 冻结的层数parser.add_argument('--freeze', type=int, default=0,help='Number of layers to freeze. backbone=10, all=24 (冻结层数)')# 检查点保存间隔parser.add_argument('--save-period', type=int, default=-1,help='Save checkpoint every x epochs (disabled if < 1) (每隔几轮保存一次检查点)')# 本地进程排名(DDP 模式用)parser.add_argument('--local_rank', type=int, default=-1,help='DDP parameter, do not modify (DDP 模式的进程排名)')# ---------------------------- W&B(Weights & Biases)参数配置 ----------------------------parser.add_argument('--entity', default=None,help='W&B: Entity (W&B 实体名称)')parser.add_argument('--upload_dataset', action='store_true',help='W&B: Upload dataset as artifact table (上传数据集到 W&B Artifact Table)')parser.add_argument('--bbox_interval', type=int, default=-1,help='W&B: Set bounding-box image logging interval (设置目标框日志记录间隔)')parser.add_argument('--artifact_alias', type=str, default='latest',help='W&B: Version of dataset artifact to use (使用的数据集版本别名)')# ---------------------------- 参数解析 ----------------------------opt = parser.parse_known_args()[0] if known else parser.parse_args()return opt
日志记录与监控
初始化日志记录器,配置日志系统,并注册回调函数。这类似于建筑项目中的监控和记录系统,用于实时跟踪施工进度和质量
# 判断是否是主进程(RANK == -1 表示单机训练,RANK == 0 表示分布式训练的主进程)
if RANK in [-1, 0]: # **Step 1: 初始化日志记录器**# 创建 Loggers 对象,用于管理训练过程的日志(包括本地日志和 W&B 日志)。# 参数说明:# - `save_dir`: 日志文件和模型保存的路径。# - `weights`: 模型权重文件的路径。# - `opt`: 训练过程中所有配置的参数。# - `hyp`: 超参数配置。# - `LOGGER`: 用于打印日志到控制台的日志记录器。loggers = Loggers(save_dir, weights, opt, hyp, LOGGER)# **Step 2: W&B 特定处理**# 如果启用了 W&B(Weights & Biases)日志记录功能if loggers.wandb:# 获取 W&B 数据字典(用于记录训练数据相关信息)data_dict = loggers.wandb.data_dict# 如果恢复训练(`resume` 参数为 True)if resume:# 使用恢复的权重、训练轮数和超参数,覆盖当前的 opt 配置weights, epochs, hyp = opt.weights, opt.epochs, opt.hyp# **Step 3: 注册回调函数**# 遍历 `loggers` 中所有的方法(`methods(loggers)` 返回可用方法的列表)for k in methods(loggers):# 将每个方法作为回调函数注册到 `callbacks` 中# 参数说明:# - `k`: 回调方法的名称(如 `on_train_start`, `on_epoch_end` 等)。# - `callback`: 对应的回调函数(通过 `getattr` 获取 `loggers` 中的方法)。callbacks.register_action(k, callback=getattr(loggers, k))
模型与数据加载
加载模型权重和配置文件,设置模型参数,加载训练数据。这类似于建筑项目中选择建筑设计方案、准备施工材料和组织施工队伍
分析:该部分代码属于yolov3结构中的哪个阶段?
主要发生在训练前的准备工作,也就是还没有进入模型的前向传播或者反向传播阶段
运行逻辑分析
- 模型加载与构建
- 如果提供预训练权重,加载模型并初始化参数
- 如果没有提供权重,则根据配置文件构建新模型
- 冻结层设置
- 固定部分参数(如 Backbone 层),以适应迁移学习或微调场景
- 训练数据准备
- 创建数据加载器,支持多线程加载、数据增强和分布式训练
分析:冻结层的使用场景
- 冻结Backbone
- 例如之前已经从大规模的数据中学到了一些通用特则会给你,那么通过冻结Backbone的参数,仅仅训练Detection Head用于适配新的任务和类别即可
- 冻结全部层
- 这种场景仅仅适合在微调检测头的时候使用
- 对于小规模数据集(如只有少量的目标类别),可以冻结所有 Backbone 层,只训练最后的预测头
- 根据自己的需求进行冻结,冻结就是利用已经预训练的特征提取能力,然后让其在新的数据集上可以实现高效的训练
# ------------------------------- 模型部分 -------------------------------# 检查权重文件的后缀是否为 .pt(PyTorch 模型格式)
check_suffix(weights, '.pt') # 判断是否加载预训练模型
pretrained = weights.endswith('.pt') if pretrained: # 如果加载的是预训练模型# 确保在分布式训练中只由一个进程下载权重文件,避免冲突with torch_distributed_zero_first(LOCAL_RANK):weights = attempt_download(weights) # 下载或加载指定的权重文件# 加载权重文件到内存,并指定加载到的设备(如 GPU 或 CPU)ckpt = torch.load(weights, map_location=device) # 创建模型对象# - 如果提供了 cfg 文件,则使用 cfg 文件构建模型# - 否则,使用权重文件中的模型配置(`ckpt['model'].yaml`)model = Model(cfg or ckpt['model'].yaml, ch=3, nc=nc, anchors=hyp.get('anchors')).to(device) # 定义要排除的参数(如 anchor 参数):# - 如果提供了 cfg 文件或 hyperparameters 中指定了 anchor 配置,并且不是恢复训练模式,则排除 anchor 参数。exclude = ['anchor'] if (cfg or hyp.get('anchors')) and not resume else []# 加载预训练模型的参数csd = ckpt['model'].float().state_dict() # 从权重文件中提取模型的状态字典csd = intersect_dicts(csd, model.state_dict(), exclude=exclude) # 匹配当前模型的参数,并排除指定的参数model.load_state_dict(csd, strict=False) # 将预训练权重加载到模型中,允许部分参数不匹配# 打印日志:显示加载的参数数量与模型参数总数量LOGGER.info(f'从 {weights} 转移了 {len(csd)}/{len(model.state_dict())} 项') else: # 如果没有加载预训练模型,则从头构建一个新模型model = Model(cfg, ch=3, nc=nc, anchors=hyp.get('anchors')).to(device) # ------------------------------- 冻结层部分 -------------------------------# 冻结部分模型层的参数,以防止它们在训练过程中更新
# 生成冻结层的层名前缀列表,例如:['model.0.', 'model.1.', ..., 'model.N.']
freeze = [f'model.{x}.' for x in range(freeze)] # 遍历模型中的所有参数(键值对:参数名称和参数值)
for k, v in model.named_parameters():v.requires_grad = True # 默认所有参数可训练# 如果当前参数的名称包含在冻结层列表中if any(x in k for x in freeze):LOGGER.info(f'冻结 {k}') # 打印冻结的参数名称v.requires_grad = False # 禁止该参数的梯度更新(冻结参数)# ------------------------------- 数据加载部分 -------------------------------# 创建训练数据加载器(train_loader)和数据集对象(dataset)
train_loader, dataset = create_dataloader(train_path, # 训练数据的路径imgsz, # 输入图像的大小batch_size // WORLD_SIZE, # 每个 GPU 的批量大小(在分布式训练中,批量大小会被划分)gs, # 网格大小(grid size),用于确保图像大小是网格的倍数single_cls, # 是否将多类数据当作单类数据处理hyp=hyp, # 超参数配置augment=True, # 是否进行数据增强cache=opt.cache, # 是否缓存数据到内存或磁盘rect=opt.rect, # 是否使用矩形训练rank=LOCAL_RANK, # 分布式训练时的本地进程编号workers=workers, # 数据加载线程数image_weights=opt.image_weights, # 是否加权选择图像quad=opt.quad, # 是否启用四元数据加载器prefix=colorstr('train: '), # 日志前缀shuffle=True # 是否对数据进行随机打乱
)
优化器与学习率调度器设置
理解优化器和学习率调度器
- 优化器设置:类似于项目经理分配施工资源(如劳动力、设备),选择适当的施工方法(如快速建造或精细施工)
- 学习率调度器:对应于施工进度计划,决定资源的使用速度和调整施工节奏,以确保建筑按时完成且质量达标
# ------------------------------- 优化器设置 -------------------------------# 计算梯度累积步数(Accumulate Step)
# nbs: 基准批量大小(64,是一个参考值)
nbs = 64
# 计算当前的累积步数,公式为:基准批量大小 / 当前批量大小(最小值为 1)
accumulate = max(round(nbs / batch_size), 1) # 根据批量大小和累积步数调整权重衰减(Weight Decay)
# 如果批量大小变大,适当放大权重衰减;反之则缩小权重衰减。
hyp['weight_decay'] *= batch_size * accumulate / nbs
LOGGER.info(f"Scaled weight_decay = {hyp['weight_decay']}") # 打印调整后的权重衰减值# ------------------------------- 参数分组 -------------------------------# 将模型的参数分为三类:
# g0: BatchNorm 的权重
# g1: 卷积层或全连接层的权重
# g2: 偏置(bias)
g0, g1, g2 = [], [], [] # 初始化三个参数组
for v in model.modules(): # 遍历模型中的每个模块# 如果模块有偏置参数(bias),并且是 nn.Parameter 类型,则将其加入 g2if hasattr(v, 'bias') and isinstance(v.bias, nn.Parameter):g2.append(v.bias)# 如果模块是 BatchNorm2d,则将其权重加入 g0if isinstance(v, nn.BatchNorm2d):g0.append(v.weight)# 如果模块有权重参数(weight),并且是 nn.Parameter 类型,则将其加入 g1elif hasattr(v, 'weight') and isinstance(v.weight, nn.Parameter):g1.append(v.weight)# ------------------------------- 优化器设置 -------------------------------# 如果使用 Adam 优化器
if opt.adam:# 创建 Adam 优化器optimizer = Adam(g0, lr=hyp['lr0'], betas=(hyp['momentum'], 0.999))
else:# 否则使用 SGD 优化器optimizer = SGD(g0, lr=hyp['lr0'], momentum=hyp['momentum'], nesterov=True) # 使用 Nesterov 动量# 为优化器添加参数组
# 添加 g1 参数组,并设置 weight_decay 为超参数中的值
optimizer.add_param_group({'params': g1, 'weight_decay': hyp['weight_decay']})
# 添加 g2 参数组(偏置),不使用权重衰减
optimizer.add_param_group({'params': g2}) # ------------------------------- 学习率调度器 -------------------------------# 定义学习率调度器的变化方式(scheduler)
if opt.linear_lr:# 如果使用线性学习率,定义线性衰减函数# 公式:初始学习率从 (1 - x) 减少到 lrf(最低学习率比例)lf = lambda x: (1 - x / (epochs - 1)) * (1.0 - hyp['lrf']) + hyp['lrf']
else:# 否则使用 One-Cycle 学习率调度器lf = one_cycle(1, hyp['lrf'], epochs) # 创建学习率调度器,基于上述的学习率变化函数 lf
scheduler = lr_scheduler.LambdaLR(optimizer, lr_lambda=lf)
训练循环(核心)
主要阶段总结
- 训练循环:类似于施工队每日的施工任务,包括材料的使用、施工进度的监控、质量的检查以及资源的优化配置
- 热身阶段:类似于施工队初期的准备工作,逐步适应施工环境和进度
- 多尺度训练:类似于根据不同施工需求调整施工方法和材料,以适应不同的建筑部分和设计要求
- 前向传播与损失计算:对应于施工过程中的质量检查和评估,确保每一步施工符合设计标准
- 反向传播与优化:类似于根据质量检查结果调整施工方法和资源分配,以提高施工效率和建筑质量
- 日志记录:类似于施工日志和进度报告,实时记录施工进展和遇到的问题
- 学习率调度:对应于施工进度的动态调整和优化,根据施工进展和质量要求调整施工节奏
- 验证与评估:类似于阶段性质量检查和最终验收,确保建筑物的整体质量和功能
# ---------------------- 训练循环 ----------------------
for epoch in range(start_epoch, epochs): # 遍历每个 epochmodel.train() # 设置模型为训练模式if opt.image_weights: # 如果启用了类别权重调整# 根据类别权重和映射关系调整样本权重cw = model.class_weights.cpu().numpy() * (1 - maps) ** 2 / nc iw = labels_to_image_weights(dataset.labels, nc=nc, class_weights=cw) dataset.indices = random.choices(range(dataset.n), weights=iw, k=dataset.n) # 根据权重重采样数据集mloss = torch.zeros(3, device=device) # 初始化平均损失记录 (box_loss, obj_loss, cls_loss)if RANK != -1: # 如果是分布式训练模式train_loader.sampler.set_epoch(epoch) # 设置当前 epoch,确保分布式训练的数据加载一致# 进度条设置pbar = enumerate(train_loader) # 枚举数据加载器print(('\n' + '%10s' * 7) % ('Epoch', 'gpu_mem', 'box', 'obj', 'cls', 'labels', 'img_size')) # 打印标题行if RANK in [-1, 0]: # 如果是主进程# 显示训练进度条pbar = tqdm(pbar, total=nb, ncols=NCOLS, bar_format='{l_bar}{bar:10}{r_bar}{bar:-10b}') optimizer.zero_grad() # 优化器梯度清零for i, (imgs, targets, paths, _) in pbar: # 遍历批次数据ni = i + nb * epoch # 计算全局迭代步数imgs = imgs.to(device, non_blocking=True).float() / 255 # 将图像归一化到 [0,1] 并移到设备上# -------------------- 热身阶段 --------------------if ni <= nw: # 如果在热身阶段xi = [0, nw] # 热身范围# 动态调整累积步数(accumulate)和学习率accumulate = max(1, np.interp(ni, xi, [1, nbs / batch_size]).round()) for j, x in enumerate(optimizer.param_groups): # 遍历优化器的参数组# 动态调整学习率x['lr'] = np.interp(ni, xi, [hyp['warmup_bias_lr'] if j == 2 else 0.0, x['initial_lr'] * lf(epoch)])# 动态调整动量if 'momentum' in x:x['momentum'] = np.interp(ni, xi, [hyp['warmup_momentum'], hyp['momentum']])# -------------------- 多尺度训练 --------------------if opt.multi_scale: # 如果启用了多尺度训练# 随机生成新的训练图像尺寸sz = random.randrange(imgsz * 0.5, imgsz * 1.5 + gs) // gs * gs sf = sz / max(imgs.shape[2:]) # 缩放因子if sf != 1: # 如果需要缩放# 计算新的图像尺寸并调整ns = [math.ceil(x * sf / gs) * gs for x in imgs.shape[2:]] imgs = nn.functional.interpolate(imgs, size=ns, mode='bilinear', align_corners=False) # -------------------- 前向传播 --------------------with amp.autocast(enabled=cuda): # 混合精度加速pred = model(imgs) # 模型前向传播loss, loss_items = compute_loss(pred, targets.to(device)) # 计算损失if RANK != -1: # 如果是分布式训练loss *= WORLD_SIZE # 按照分布式规模调整损失# -------------------- 反向传播 --------------------scaler.scale(loss).backward() # 使用梯度缩放反向传播# -------------------- 参数更新 --------------------if ni - last_opt_step >= accumulate: # 如果满足累积步数条件scaler.step(optimizer) # 更新优化器参数scaler.update() # 更新梯度缩放比例optimizer.zero_grad() # 清零梯度if ema: # 如果启用了 EMAema.update(model) # 更新模型的指数移动平均last_opt_step = ni # 更新最后一次优化步数# -------------------- 日志记录 --------------------if RANK in [-1, 0]: # 如果是主进程# 动态更新平均损失mloss = (mloss * i + loss_items) / (i + 1) mem = f'{torch.cuda.memory_reserved() / 1E9 if torch.cuda.is_available() else 0:.3g}G' # 显存使用量# 更新进度条显示内容pbar.set_description(('%10s' * 2 + '%10.4g' * 5) % (f'{epoch}/{epochs - 1}', mem, *mloss, targets.shape[0], imgs.shape[-1]))# -------------------- 学习率调度 --------------------lr = [x['lr'] for x in optimizer.param_groups] # 获取当前学习率scheduler.step() # 更新学习率调度器# -------------------- 评估与保存 --------------------if RANK in [-1, 0]: # 如果是主进程callbacks.run('on_train_epoch_end', epoch=epoch) # 运行训练结束的回调ema.update_attr(model, include=['yaml', 'nc', 'hyp', 'names', 'stride', 'class_weights']) # 更新 EMA 属性final_epoch = (epoch + 1 == epochs) or stopper.possible_stop # 检查是否是最后一个 epochif not noval or final_epoch: # 如果需要验证# 运行验证,并获取验证结果results, maps, _ = val.run(data_dict,batch_size=batch_size // WORLD_SIZE * 2,imgsz=imgsz,model=ema.ema,single_cls=single_cls,dataloader=val_loader,save_dir=save_dir,plots=False,callbacks=callbacks,compute_loss=compute_loss)# 更新最佳 mAPfi = fitness(np.array(results).reshape(1, -1)) # 计算当前结果的 fitnessif fi > best_fitness: # 如果当前 fitness 是最优的best_fitness = fi # 更新最佳 fitnesslog_vals = list(mloss) + list(results) + lr # 记录日志值callbacks.run('on_fit_epoch_end', log_vals, epoch, best_fitness, fi) # 运行回调# 保存模型if (not nosave) or (final_epoch and not evolve): # 如果需要保存模型ckpt = {'epoch': epoch, # 记录当前 epoch'best_fitness': best_fitness, # 最佳 fitness'model': deepcopy(de_parallel(model)).half(), # 模型参数'ema': deepcopy(ema.ema).half(), # EMA 参数'updates': ema.updates, # EMA 更新次数'optimizer': optimizer.state_dict(), # 优化器状态'wandb_id': loggers.wandb.wandb_run.id if loggers.wandb else None, # W&B 运行 ID'date': datetime.now().isoformat()} # 保存日期torch.save(ckpt, last) # 保存为最后一次权重文件if best_fitness == fi: # 如果当前 fitness 是最优的torch.save(ckpt, best) # 保存为最佳权重文件if (epoch > 0) and (opt.save_period > 0) and (epoch % opt.save_period == 0): # 按周期保存权重torch.save(ckpt, w / f'epoch{epoch}.pt') del ckpt # 删除检查点,释放内存callbacks.run('on_model_save', last, epoch, final_epoch, best_fitness, fi) # 运行回调# 提前停止(仅在单 GPU 模式下)if RANK == -1 and stopper(epoch=epoch, fitness=fi): # 如果满足提前停止条件break # 停止训练
验证与评估
在每个训练周期结束后,对模型进行验证,评估其在验证集上的性能(如mAP)。这类似于建筑项目中的阶段性质量检查和评估,确保施工质量符合设计要求
if RANK in [-1, 0]: # 检查当前设备是否为主进程(单GPU模式或主节点)# 调用验证函数 val.run(),对当前模型在验证集上的性能进行评估results, maps, _ = val.run(data_dict, # 数据集的配置信息,包含训练、验证和测试数据的路径batch_size=batch_size // WORLD_SIZE * 2, # 验证集的批次大小,调整为全局批量大小(batch_size)除以总进程数 WORLD_SIZE,再乘以 2imgsz=imgsz, # 输入图像的尺寸model=ema.ema, # 使用 EMA(指数移动平均)模型的权重进行评估,以获得更平滑和稳定的验证性能single_cls=single_cls, # 是否将多类别数据视为单类别任务,用于单类别检测dataloader=val_loader, # 验证集的数据加载器save_dir=save_dir, # 保存结果的路径,用于存储验证过程的日志或可视化图表plots=False, # 是否生成验证结果的可视化图表,设置为 False 表示不生成callbacks=callbacks, # 回调函数,用于扩展验证过程,例如记录日志或自定义处理compute_loss=compute_loss # 损失函数,用于计算验证过程中的损失值)
模型保存与早停机制
据训练过程中的表现,保存当前模型的状态(如最佳模型、最新模型等)。同时,通过早停机制,在模型性能不再提升时提前终止训练
- 模型保存:项目经理定期记录施工进度和质量状况,保存关键的施工记录和里程碑
- 早停机制:如果发现施工质量无法满足要求,或项目进度严重滞后,项目经理决定提前终止或调整施工计划,以避免资源浪费和进一步的问题
# 保存模型
if (not nosave) or (final_epoch and not evolve): # 检查是否需要保存模型# 构建一个检查点字典,用于保存模型的状态和相关信息ckpt = {'epoch': epoch, # 当前的训练轮次'best_fitness': best_fitness, # 当前训练过程中模型的最佳 fitness(如 mAP)'model': deepcopy(de_parallel(model)).half(), # 深拷贝模型的状态,转换为半精度以减少存储需求'ema': deepcopy(ema.ema).half(), # 深拷贝 EMA(指数移动平均)模型的状态'updates': ema.updates, # EMA 更新的次数'optimizer': optimizer.state_dict(), # 优化器的状态字典(保存优化器参数和学习率等信息)'wandb_id': loggers.wandb.wandb_run.id if loggers.wandb else None, # W&B 运行 ID(如果使用 W&B)'date': datetime.now().isoformat() # 保存当前时间的时间戳,用于记录模型保存时间}# 保存最新的模型权重到指定路径 `last`torch.save(ckpt, last)# 如果当前的 fitness 指标是最佳值,则保存模型到 `best` 路径if best_fitness == fi:torch.save(ckpt, best)# 如果训练轮次大于 0 且保存周期 `save_period` 大于 0,并且当前轮次是保存周期的倍数# 则保存当前轮次的模型权重到以 `epoch{轮次}.pt` 命名的文件if (epoch > 0) and (opt.save_period > 0) and (epoch % opt.save_period == 0):torch.save(ckpt, w / f'epoch{epoch}.pt') # 保存到指定路径# 删除保存的检查点对象,以释放内存del ckpt# 触发 `on_model_save` 回调函数,通知其他组件模型已保存callbacks.run('on_model_save', last, epoch, final_epoch, best_fitness, fi)# 单 GPU 模式下的提前停止机制
if RANK == -1 and stopper(epoch=epoch, fitness=fi): # 如果是单 GPU 模式,并且达到提前停止条件break # 停止训练,结束当前循环
相关文章:
【YOLOv3】源码(train.py)
概述 主要模块分析 参数解析与初始化 功能:解析命令行参数,设置训练配置项目经理制定详细的施工计划和资源分配日志记录与监控 功能:初始化日志记录器,配置监控系统项目经理使用监控和记录工具,实时跟踪施工进度和质量…...
一维、线性卡尔曼滤波的例程(MATLAB)
这段 MATLAB 代码实现了一维线性卡尔曼滤波器的基本功能,用于估计在存在噪声的情况下目标状态的真实值 文章目录 一维线性卡尔曼滤波代码运行代码介绍1. **初始化部分**2. **数据生成**3. **卡尔曼滤波器实现**4. **结果可视化**5. **统计输出** 源代码 总结 一维线…...
【Rust自学】6.2. Option枚举
喜欢的话别忘了点赞、收藏加关注哦,对接下来的教程有兴趣的可以关注专栏。谢谢喵!(・ω・) 6.2.1. 什么是Option枚举 它定义于标准库中,在Prelude(预导入模块)中,负责描述这样的场景…...
unity学习1:第1个template的项目platformer 学习
目录 0 教训,不要学生思路:路径依赖 1 从unity的编辑器里直接下载一个template 2 第一个下马威:下载到本地的这个模板项目第一次运行就报错, 其次关了重进就好了 2.1 报错 2.2 解决 2.3 解决 3 第2个拦路虎: 项目的声音大小…...
初识 Conda:一站式包管理和环境管理工具
文章目录 1. 什么是 Conda?2. 为什么选择 Conda?3. Conda 的安装3.1 安装步骤(以 Miniconda 为例) 4. Conda 的核心功能4.1 包管理4.2 环境管理4.3 Conda Forge4.4 设置国内镜像 5. 常见使用场景5.1 数据科学项目5.2 离线安装5.3 …...
vue.js 组件化开发 根组件
Vue.js是一个用于构建用户界面的渐进式JavaScript框架。组件化开发是Vue.js的核心理念之一,它允许开发者将部分代码封装为可重用的组件,从而提高代码的复用性和可维护性。而根组件是Vue.js应用的最顶层组件,它包含了其他所有的组件。 下面详…...
ASP.NET WebForms:实现全局异常捕获与处理的最佳实践
在ASP.NET WebForms中,你可以通过以下方法来统一捕获后台异常: 1. 在Global.asax中使用Application_Error Global.asax文件允许你处理应用程序级别的异常。你可以在Application_Error事件中捕获所有未处理的异常,并根据需要记录或处理它们。…...
vue3配置测试环境、开发环境、生产环境
第一步:在src同级新建 .env.production 、.env.test 、.env.development文件 第二步:在文件中配置开发环境、生产环境、测试环境 // 开发环境 .env.developmentNODE_ENV developmentVUE_APP_MODE development outputDir dist_dev // 打出包的名称VUE_…...
农历节日倒计时:基于Python的公历与农历日期转换及节日查询小程序(升级版)
农历节日倒计时:基于Python的公历与农历日期转换及节日查询小程序升级版 调整的功能 上一个小程序只是能计算当年的农历节日的间隔时间,那么这次修改一下,任意年份的农历节日都可以,并且能输出农历节日对应的阳历日期࿰…...
linux Python环境部署
登录Python官网去下载对应的版本:Python下载地址 在data目录下创建python文件夹 mkdir python上传下载的安装包 Python-3.8.18.tgz解压 tar -xf Python-3.8.18.tgz进入解压后的目录 cd Python-3.8.18/编译安装 ./configure --prefix/data/python38 make &&…...
Python基础语法知识——数据类型的查询、数据类型转化
今天第一次学习python,之前学习过C,感觉学习起来还可以,就是刚用的时候有点手残,想的是python代码,结果写出来就是C,本人决定每天抽出时间写点。同时继续更新NX二次开发专栏学习,话不多说,晚上的…...
命令行之巅:Linux Shell编程的至高艺术(中)
文章一览 前言一、输入/输出及重定向命令1.1 输入/输出命令1.1.1 read命令1.1.2 echo命令 1.2 输入/输出重定向1.3 重定向深入讲解1.4 Here Document1.4.1 /dev/null 文件 二、shell特殊字符和命令语法2.1 引号2.1.1 双引号2.1.2 单引号2.1.3 倒引号 2.2 注释、管道线和后台命令…...
利用Gurobi追溯模型不可行原因的四种方案及详细案例
文章目录 1. 引言2. 追溯不可行集的四种方法2.1 通过约束增减进行判断2.2 通过computeIIS函数获得冲突集2.3 利用 feasRelaxS() 或 feasRelax() 函数辅助排查2.4 利用 IIS Force 属性1. 引言 模型不可行是一个让工程师头疼的问题,对于复杂模型而言,导致模型不可行的原因可能…...
「matplotlib」绘制图线和数据点的样式风格和颜色表大全
绘制图线和数据点的样式风格和颜色表大全 显示图例 legend() 属性 linestyle 属性 marker 属性color 1、legend() 显示坐标轴中图线的对应标注的显示位置plt.legend(loc) locloc codebest (default)0upper right1upper left2lower left3lower rig…...
【基础还得练】 KKT 条件
优秀教程-真正理解拉格朗日乘子法和 KKT 条件: link优秀教程-最优化(6):一般约束优化问题的最优性理论: link KKT条件(Karush-Kuhn-Tucker条件)是非线性规划中的一组必要条件,在某些情况下也是最优解的充分…...
Node.JS 版本管理工具 Fnm 安装及配置(Windows)
安装流程可参考:fnm 安装及配置(Windows)_fnm安装-CSDN博客 然后就是在git bash如何生效 在 Git Bash 中使用 fnm 需要确保你正确设置了环境变量。你可以按照以下步骤进行配置: 1. **打开 Git Bash**: 启动 Git Bash。 2. **编辑 .bas…...
STM32-笔记11-手写带操作系统的延时函数
1、为什么带操作系统的延时函数,和笔记10上的延时函数不能使用同一种? 因为笔记10的延时函数在每次调用的时候,会一直开关定时器,而在FreeRTOS操作系统中,SysTick定时器当作时基使用。 时基是一个时间显示的基本单位。…...
CCNP_SEC_ASA 第六天作业
实验需求: 为保障内部用户能够访问Internet,请把10.1.1.0/24网络动态转换到外部地址池202.100.1.100-202.100.1.200,如果地址池耗尽后,PAT到Outside接口 提示:需要看到如下输出信息 Inside#telnet 202.100.1.1 Trying …...
Python小括号( )、中括号[ ]和大括号{}代表什么
python语言最常见的括号有三种,分别是:小括号( )、中括号[ ]和大括号也叫做花括号{ },分别用来代表不同的python基本内置数据类型。 小括号():struct结构体,但不能改值 python中的小括号( )&am…...
electron node-api addon开发
解决方案入口 拷贝日志以及json等第三方源码 增加包含目录 编写接口 默认模板已经有一个回调函数了 照葫芦画瓢就行 其中几个重要的点要注意 1.参数传入 比如如下的例子: 头文件定义: public:下增加 Napi::Value StartAnswer (const Napi::Callb…...
vue之axios基本使用
文章目录 1. axios 网络请求库2. axiosvue 1. axios 网络请求库 <body> <input type"button" value"get请求" class"get"> <input type"button" value"post请求" class"post"> <!-- 官网提供…...
uniapp中wx.getFuzzyLocation报错如何解决
一、用wx.getLocation接口审核不通过 用uniapp开发小程序时难免需要获取当前地理位置。 代码如下: uni.getLocation({type: wgs84,success: function (res) {console.log(当前位置的经度: res.longitude);console.log(当前位置的纬度: r…...
Peter Lax线性代数教材:Linear Algebra and Its Applications 2nd Ed
这本线性代数教材,印象很深刻,记得是高中时期自学线性代数的时候看过。这本书跟Gilbert Strang的教材MIT线性代数教材:Linear Algebra and Its Applications相比,内容没这么紧凑,而且表述也更加代数风格,很…...
vue3封装而成的APP ,在版本更新后,页面显示空白
一、问题展示 更新之后页面空白,打不开 ,主要是由于缓存造成的 二、解决办法 1、随机数代码实现 使用随机数来动态的生成静态资源目录名可以避免浏览器缓存,但同时每次也会导致浏览器每次都下载最新的资源。如果静态资源过大,可…...
技术栈整理
java系列: javaSE, javaEE,jdk8,jdk17,jdk21 springframework(4, 5, 6) springboot(2, 3) mybatis mybatisplus hibernate springjpa,springjdbc springmvc, springwebflux,structs springsecurity dubbo zookeeper s…...
初始c语言第一个c语言项目
第一个c语言项目 //c语言中一定要有main函数 //主函数//printf是一个库函数 //专门用来打印数据//std 标准 //i-input //o-output // #define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h>//c语言规定main函数是程序的入口 //标准的主函数的写法 int main() {printf(&qu…...
CSS实现无限滚动的列表
CSS本身不能实现无限滚动的列表,无限滚动通常需要结合HTML、CSS和JavaScript来完成。以下是一个简单的实现无限滚动的列表的示例,它使用了JavaScript来动态加载和展示内容。 <!DOCTYPE html> <html lang"en"> <head> <me…...
【ES6复习笔记】Spread 扩展运算符(8)
在现代前端开发中,JavaScript 的扩展运算符(Spread Operator)是一个非常有用的特性,它允许你将数组或对象展开,以便在函数调用、数组拼接、对象复制等场景中更方便地处理数据。扩展运算符(spread࿰…...
【Rust自学】7.3. use关键字 Pt.1:use的使用与as关键字
喜欢的话别忘了点赞、收藏加关注哦,对接下来的教程有兴趣的可以关注专栏。谢谢喵!(・ω・) 7.3.1. use的作用 use的作用是将路径导入到当前作用域内。而引入的内容仍然是遵守私有性原则,也就是只有公共的部分引入进来才…...
Vscode + gdbserver远程调试开发板指南:
本章目录 步骤环境准备网络配置vscode配置步骤 (全图示例)开发板配置开始调试注意: 每次断开之后,开发板都需要重新启动gdbserver才可调试。 参考链接: 步骤 环境准备 将交叉编译链路径加入$PATH变量:确保系统能够找到所需的工具。 export PATH$PATH:/p…...
python爬虫----爬取视频实战
python爬虫-爬取视频 本次爬取,还是运用的是requests方法 首先进入此网站中,选取你想要爬取的视频,进入视频页面,按F12,将网络中的名称栏向上拉找到第一个并点击,可以在标头中,找到后续我们想要…...
shardingsphere分库分表项目实践5-自己用java写一个sql解析器+完整项目源码
前1节我们介绍了 shardingsphere 分表分库的sql解析与重写: shardingsphere分库分表项目实践4-sql解析&重写-CSDN博客 那么shardingsphere sql 解析底层究竟是怎么实现的呢,其实它直接用了著名的开源软件 antlr . antlr 介绍: ANTLR&a…...
【Rust自学】7.2. 路径(Path)Pt.1:相对路径、绝对路径与pub关键字
喜欢的话别忘了点赞、收藏加关注哦,对接下来的教程有兴趣的可以关注专栏。谢谢喵!(・ω・) 7.2.1. 路径的简介 在Rust里,如果想要找到模块里的某个东西,就必须知道并使用它的路径。Rust中的路径就跟文件系统…...
ubuntu虚拟机中搭建python开发环境
1.Anaconda安装 1)Anaconda下载 清华大学开源软件镜像站下载地址:https://mirrors.tuna.tsinghua.edu.cn/anaconda/archive/ 官网:https://www.anaconda.com/distribution/ 2)运行 .sh 文件 bash Anaconda3-5.2.0-Linux-x86_…...
OpenHarmony-5.PM 子系统(2)
电池服务组件OpenHarmony-4.1-Release 1.电池服务组件 Battery Manager 提供了电池信息查询的接口,同时开发者也可以通过公共事件监听电池状态和充放电状态的变化。电池服务组件提供如下功能: 电池信息查询。充放电状态查询。关机充电。 电池服务组件架…...
三相电的相电压、线电压、额定值、有效值,变比,零序电压,零序电流,三相三线制的三角形连接,三相四线制的星形连接
在二次设备配置中经常有根电压系统相关的名词,本身不是学电气的,有些名词经常查了忘,后续工作所有遇到跟电气相关的知识总结在此帖,便于后续直接查看,避免每次都要重新查、重新梳理。 相电压和线电压的关系是根号3倍&a…...
基于 Slf4j 和 AOP 的自动化方法执行时间日志记录方案
前言 其实这个需求很简单,但是这个需求又是项目中必不可少的,尤其对于性能调优这块,但是使用哪种方式更加方便呢,这就是本篇博文需要讨论的重点 系统时间 可以通过 System.currentTimeMillis() 或 System.nanoTime() 来实现。 …...
C语言-数据结构-树
目录 一,树 1,基本术语: 2,树的性质: 3,树的遍历: 4,森林的遍历: 5,树和二叉树的转换: 6,二叉树和森林的转化: 7,树的存储: 1,双亲表示法 2、孩子链存储结构 3、孩子兄弟链存储结构 二,二叉树: 1,二叉树的性质: 2,二叉树的遍历: 3,二叉树的存储结构: 顺序存储:…...
探究音频丢字位置和丢字时间对pesq分数的影响
丢字的本质 丢字的本质是在一段音频中一小段数据变为0 丢字对主观感受的影响 1. 丢字位置 丢字的位置对感知效果有很大影响。如果丢字发生在音频信号的静音部分或低能量部分,感知可能不明显;而如果丢字发生在高能量部分或关键音素上,感知…...
【Linux】flock 文件级别的锁定
flock 是 Linux/Unix 系统中的一个命令,用于实现文件级别的锁定。它允许你在多个进程之间共享对文件的访问,但确保在同一时间只有一个进程可以访问文件,避免竞态条件(race conditions)和数据不一致问题。 flock 的基本…...
每天40分玩转Django:Django部署概述
一、Django部署概述 在开发阶段,我们通常使用Django内置的轻量级开发服务器runserver。但在生产环境中,为了应对大量并发请求,需要使用高性能的WSGI服务器,如Gunicorn、uWSGI等。同时还要配置Nginx等Web服务器作为反向代理,实现负载均衡、静态文件处理等。下面是Django部署的整…...
【unity c#】深入理解string,以及不同方式构造类与反射的性能测试(基于BenchmarkDotNet)
出这篇文章的主要一个原因就是ai回答的性能差异和实际测试完全不同,比如说是先获取构造函数再构造比Activator.CreateInstance(type)快,实际却相反 对测试结果的评价基于5.0,因为找不到unity6确切使用的net版本,根据c#9推测是net5…...
防抖和节流的方法详解和CSS文本溢出小知识
1.防抖 防抖:在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时 场景:搜索框输入发请求,一些拖拽盒子,或者滚动事件 防抖的实现:利用间歇函数 const timer =setTimeout(callback函数,间隔时间) //执行完会返回自身的这个定时器的编号//事件所触发的函…...
深度解析:Maven 和 Gradle 的使用比较及常见仓库推荐
Maven 和 Gradle 是 Java 项目中最常用的构建工具。它们各有优势,适用于不同的场景。本文将对两者进行详细的对比,并推荐一些常用的 Maven 和 Gradle 仓库,帮助开发者高效管理依赖。 一、Maven 和 Gradle 的使用比较 1.1 基本介绍 Maven 基…...
CSS---实现盒元素div内input/textarea的focus状态时给父元素加属性!
注意兼容性,低版本浏览器无效 要实现当 textarea 文本框获得焦点时,自动给其父元素添加类名或样式,您可以使用 CSS 的 :focus-within 伪类选择器。这个选择器会在元素本身或其任何子元素获得焦点时应用样式。 示例代码 假设您有以下 HTML 结…...
施耐德变频器ATV320系列技术优势:创新与安全并重
在工业自动化领域,追求高效、安全与智能已成为不可阻挡的趋势。施耐德变频器ATV320系列凭借其强大的设计标准和全球认证,成为能够帮助企业降低安装成本,提高设备性能的创新解决方案。 【全球认证,品质保障】ATV320 系列秉持施耐德…...
Dynamo:Amazon 的高可用键值仓储
大家觉得有意义和帮助记得及时关注和点赞!!! Dynamo 是 Amazon 的高可用分布式键值存储(key/value storage)系统。这篇论文发表 的时候(2007)它还只是一个内部服务,现在(改名为 DynamoDB)已经发…...
前端开发 -- 自动回复机器人【附完整源码】
一:效果展示 本项目实现了一个简单的网页聊天界面,用户可以在输入框中输入消息,并点击发送按钮或按下回车键来发送消息。机器人会根据用户发送的消息内容,通过关键字匹配来生成自动回复。 二:源代码分享 <!DOCTYP…...
离散数学 群(半群,群,交换群,循环群,对称群,置换群,置换,交代群,轮换)详细,复习笔记
半群: 设G是一个非空集合,若为G上的二元代数运算,且满足结合律,则称该代数系统(G,)为半群 性质:非空,封闭,结合律 独异点: 含有单位元的半裙 练习题: 例:设S是一个非空集合,p(S)是S的幂集,∩和U是p(S…...
Android Java 版本的 MSAA OpenGL ES 多重采样
最近多次被小伙伴问到 OpenGL 多重采样,其实前面文章里多次讲过了,就是构建2个缓冲区,多重采样缓冲区和目标解析缓冲区。 代码流程 // Framebuffer IDs private int msaaFBO; private int msaaColorBuffer; private int msaaDepthBuffer;pr…...