lr_save_datetime

本文介绍如何利用lr_save_datetime函数根据当前日期和时间生成唯一参数,并通过一个具体示例展示了如何将这些唯一参数应用于Web请求中,以实现自动化测试场景下的数据唯一性。

lr_save_datetime: assigns the current date and time to a parameter.

 

void lr_save_datetime(const char *format, int offset, const char *name);

 

lr_save_datetime retrieves tomorrow's date:

lr_save_datetime("Tomorrow is %B %d %Y", DATE_NOW + ONE_DAY, "next");
lr_output_message(lr_eval_string("{next}"));
If today is January 7th, 1999, these lines will return the message: Tomorrow is January 08 1999.

 

Following is one example to use lr_save_datetime to create unique parameters in my code.

Action()
{

lr_save_datetime("testSales%Y%m%d%H%M%S",TIME_NOW,"accountValue");

lr_output_message(lr_eval_string("Value={accountValue}"));

lr_start_transaction("CreateNewSales");

web_submit_data("user_management.do",
"Action=http://192.168.19.13/discountmgmttool/user_management.do?execution=e3s1",
"Method=POST",
"RecContentType=text/xml",
"Referer=http://192.168.19.13/discountmgmttool/user_management.do?execution=e3s1",
"Snapshot=t4.inf",
"Mode=HTML",
"EncodeAtSign=YES",
ITEMDATA,
"Name=AJAXREQUEST", "Value=_viewRoot", ENDITEM,
"Name=create_new_profile_form:account", lr_eval_string("Value={accountValue}"), ENDITEM,
"Name=create_new_profile_form:user_name", lr_eval_string("Value={accountValue}"), ENDITEM,
"Name=create_new_profile_form:user_email", lr_eval_string("Value={accountValue}@xxx.com"), ENDITEM,
"Name=create_new_profile_form:user_roles", "Value=ROLE_SALES", ENDITEM,
"Name=create_new_profile_form:user_title", "Value=Sales", ENDITEM,
"Name=create_new_profile_form:user_leader", "Value={testLeaderId}", ENDITEM,
"Name=create_new_profile_form_SUBMIT", "Value=1", ENDITEM,
"Name=javax.faces.ViewState", "Value={Siebel_Analytic_ViewState2}", ENDITEM,
"Name=create_new_profile_form:save_my_profile_btn", "Value=create_new_profile_form:save_my_profile_btn", ENDITEM,
LAST);

lr_end_transaction("CreateNewSales",LR_AUTO);

return 0;

}

 

请详细介绍下这段代码”import os # os.environ['CUDA_VISIBLE_DEVICES'] = '3' import argparse import pickle from collections import defaultdict from datetime import datetime import numpy as np from mmcv.cnn import get_model_complexity_info from torch.utils.tensorboard import SummaryWriter from visdom import Visdom from configs import cfg, update_config from dataset.multi_label.coco import COCO14 from dataset.augmentation import get_transform from metrics.ml_metrics import get_map_metrics, get_multilabel_metrics from metrics.pedestrian_metrics import get_pedestrian_metrics from models.model_ema import ModelEmaV2 from optim.adamw import AdamW from scheduler.cos_annealing_with_restart import CosineAnnealingLR_with_Restart from scheduler.cosine_lr import CosineLRScheduler from tools.distributed import distribute_bn from tools.vis import tb_visualizer_pedes import torch from torch.optim.lr_scheduler import ReduceLROnPlateau, MultiStepLR from torch.utils.data import DataLoader from batch_engine import valid_trainer, batch_trainer from dataset.pedes_attr.pedes import PedesAttr from models.base_block import FeatClassifier from models.model_factory import build_loss, build_classifier, build_backbone from tools.function import get_model_log_path, get_reload_weight, seperate_weight_decay from tools.utils import time_str, save_ckpt, ReDirectSTD, set_seed, str2bool from models.backbone import swin_transformer, resnet, bninception, vit from models.backbone.tresnet import tresnet from losses import bceloss, scaledbceloss from models import base_block # torch.backends.cudnn.benchmark = True # torch.autograd.set_detect_anomaly(True) torch.autograd.set_detect_anomaly(True) def main(cfg, args): set_seed(605) exp_dir = os.path.join('exp_result', cfg.DATASET.NAME) model_dir, log_dir = get_model_log_path(exp_dir, cfg.NAME) stdout_file = os.path.join(log_dir, f'stdout_{time_str()}.txt') save_model_path = os.path.join(model_dir, f'ckpt_max_{time_str()}.pth') visdom = None if cfg.VIS.VISDOM: visdom = Visdom(env=f'{cfg.DATASET.NAME}_' + cfg.NAME, port=8401) assert visdom.check_connection() writer = None if cfg.VIS.TENSORBOARD.ENABLE: current_time = datetime.now().strftime('%b%d_%H-%M-%S') writer_dir = os.path.join(exp_dir, cfg.NAME, 'runs', current_time) writer = SummaryWriter(log_dir=writer_dir) if cfg.REDIRECTOR: print('redirector stdout') ReDirectSTD(stdout_file, 'stdout', False) """ the reason for args usage is CfgNode is immutable """ if 'WORLD_SIZE' in os.environ: args.distributed = int(os.environ['WORLD_SIZE']) > 1 else: args.distributed = None args.world_size = 1 args.rank = 0 # global rank if args.distributed: args.device = 'cuda:%d' % args.local_rank torch.cuda.set_device(args.local_rank) torch.distributed.init_process_group(backend='nccl', init_method='env://') args.world_size = torch.distributed.get_world_size() args.rank = torch.distributed.get_rank() print(f'use GPU{args.device} for training') print(args.world_size, args.rank) if args.local_rank == 0: print(cfg) train_tsfm, valid_tsfm = get_transform(cfg) if args.local_rank == 0: print(train_tsfm) if cfg.DATASET.TYPE == 'pedes': train_set = PedesAttr(cfg=cfg, split=cfg.DATASET.TRAIN_SPLIT, transform=train_tsfm, target_transform=cfg.DATASET.TARGETTRANSFORM) valid_set = PedesAttr(cfg=cfg, split=cfg.DATASET.VAL_SPLIT, transform=valid_tsfm, target_transform=cfg.DATASET.TARGETTRANSFORM) elif cfg.DATASET.TYPE == 'multi_label': train_set = COCO14(cfg=cfg, split=cfg.DATASET.TRAIN_SPLIT, transform=train_tsfm, target_transform=cfg.DATASET.TARGETTRANSFORM) valid_set = COCO14(cfg=cfg, split=cfg.DATASET.VAL_SPLIT, transform=valid_tsfm, target_transform=cfg.DATASET.TARGETTRANSFORM) if args.distributed: train_sampler = torch.utils.data.distributed.DistributedSampler(train_set) else: train_sampler = None train_loader = DataLoader( dataset=train_set, batch_size=cfg.TRAIN.BATCH_SIZE, sampler=train_sampler, shuffle=train_sampler is None, num_workers=4, pin_memory=True, drop_last=True, ) valid_loader = DataLoader( dataset=valid_set, batch_size=cfg.TRAIN.BATCH_SIZE, shuffle=False, num_workers=4, pin_memory=True, ) if args.local_rank == 0: print('-' * 60) print(f'{cfg.DATASET.NAME} attr_num : {train_set.attr_num}, eval_attr_num : {train_set.eval_attr_num} ' f'{cfg.DATASET.TRAIN_SPLIT} set: {len(train_loader.dataset)}, ' f'{cfg.DATASET.TEST_SPLIT} set: {len(valid_loader.dataset)}, ' ) labels = train_set.label label_ratio = labels.mean(0) if cfg.LOSS.SAMPLE_WEIGHT else None backbone, c_output = build_backbone(cfg.BACKBONE.TYPE, cfg.BACKBONE.MULTISCALE) classifier = build_classifier(cfg.CLASSIFIER.NAME)( nattr=train_set.attr_num, c_in=c_output, bn=cfg.CLASSIFIER.BN, pool=cfg.CLASSIFIER.POOLING, scale =cfg.CLASSIFIER.SCALE ) model = FeatClassifier(backbone, classifier, bn_wd=cfg.TRAIN.BN_WD) if args.local_rank == 0: print(f"backbone: {cfg.BACKBONE.TYPE}, classifier: {cfg.CLASSIFIER.NAME}") print(f"model_name: {cfg.NAME}") # flops, params = get_model_complexity_info(model, (3, 256, 128), print_per_layer_stat=True) # print('{:<30} {:<8}'.format('Computational complexity: ', flops)) # print('{:<30} {:<8}'.format('Number of parameters: ', params)) model = model.cuda() if args.distributed: model = torch.nn.SyncBatchNorm.convert_sync_batchnorm(model) model = torch.nn.parallel.DistributedDataParallel(model, device_ids=[args.local_rank]) else: model = torch.nn.DataParallel(model) model_ema = None if cfg.TRAIN.EMA.ENABLE: # Important to create EMA model after cuda(), DP wrapper, and AMP but before SyncBN and DDP wrapper model_ema = ModelEmaV2( model, decay=cfg.TRAIN.EMA.DECAY, device='cpu' if cfg.TRAIN.EMA.FORCE_CPU else None) if cfg.RELOAD.TYPE: model = get_reload_weight(model_dir, model, pth=cfg.RELOAD.PTH) loss_weight = cfg.LOSS.LOSS_WEIGHT criterion = build_loss(cfg.LOSS.TYPE)( sample_weight=label_ratio, scale=cfg.CLASSIFIER.SCALE, size_sum=cfg.LOSS.SIZESUM, tb_writer=writer) criterion = criterion.cuda() if cfg.TRAIN.BN_WD: param_groups = [{'params': model.module.finetune_params(), 'lr': cfg.TRAIN.LR_SCHEDULER.LR_FT, 'weight_decay': cfg.TRAIN.OPTIMIZER.WEIGHT_DECAY}, {'params': model.module.fresh_params(), 'lr': cfg.TRAIN.LR_SCHEDULER.LR_NEW, 'weight_decay': cfg.TRAIN.OPTIMIZER.WEIGHT_DECAY}] else: # bn parameters are not applied with weight decay ft_params = seperate_weight_decay( model.module.finetune_params(), lr=cfg.TRAIN.LR_SCHEDULER.LR_FT, weight_decay=cfg.TRAIN.OPTIMIZER.WEIGHT_DECAY) fresh_params = seperate_weight_decay( model.module.fresh_params(), lr=cfg.TRAIN.LR_SCHEDULER.LR_NEW, weight_decay=cfg.TRAIN.OPTIMIZER.WEIGHT_DECAY) param_groups = ft_params + fresh_params if cfg.TRAIN.OPTIMIZER.TYPE.lower() == 'sgd': optimizer = torch.optim.SGD(param_groups, momentum=cfg.TRAIN.OPTIMIZER.MOMENTUM) elif cfg.TRAIN.OPTIMIZER.TYPE.lower() == 'adam': optimizer = torch.optim.Adam(param_groups) elif cfg.TRAIN.OPTIMIZER.TYPE.lower() == 'adamw': optimizer = AdamW(param_groups) else: assert None, f'{cfg.TRAIN.OPTIMIZER.TYPE} is not implemented' if cfg.TRAIN.LR_SCHEDULER.TYPE == 'plateau': lr_scheduler = ReduceLROnPlateau(optimizer, factor=0.1, patience=4) if cfg.CLASSIFIER.BN: assert False, 'BN can not compatible with ReduceLROnPlateau' elif cfg.TRAIN.LR_SCHEDULER.TYPE == 'multistep': lr_scheduler = MultiStepLR(optimizer, milestones=cfg.TRAIN.LR_SCHEDULER.LR_STEP, gamma=0.1) elif cfg.TRAIN.LR_SCHEDULER.TYPE == 'annealing_cosine': lr_scheduler = CosineAnnealingLR_with_Restart( optimizer, T_max=(cfg.TRAIN.MAX_EPOCH + 5) * len(train_loader), T_mult=1, eta_min=cfg.TRAIN.LR_SCHEDULER.LR_NEW * 0.001 ) elif cfg.TRAIN.LR_SCHEDULER.TYPE == 'warmup_cosine': lr_scheduler = CosineLRScheduler( optimizer, t_initial=cfg.TRAIN.MAX_EPOCH, lr_min=1e-5, # cosine lr 最终回落的位置 warmup_lr_init=1e-4, warmup_t=cfg.TRAIN.MAX_EPOCH * cfg.TRAIN.LR_SCHEDULER.WMUP_COEF, ) else: assert False, f'{cfg.LR_SCHEDULER.TYPE} has not been achieved yet' best_metric, epoch = trainer(cfg, args, epoch=cfg.TRAIN.MAX_EPOCH, model=model, model_ema=model_ema, train_loader=train_loader, valid_loader=valid_loader, criterion=criterion, optimizer=optimizer, lr_scheduler=lr_scheduler, path=save_model_path, loss_w=loss_weight, viz=visdom, tb_writer=writer) if args.local_rank == 0: print(f'{cfg.NAME}, best_metrc : {best_metric} in epoch{epoch}') def trainer(cfg, args, epoch, model, model_ema, train_loader, valid_loader, criterion, optimizer, lr_scheduler, path, loss_w, viz, tb_writer): maximum = float(-np.inf) best_epoch = 0 result_list = defaultdict() result_path = path result_path = result_path.replace('ckpt_max', 'metric') result_path = result_path.replace('pth', 'pkl') for e in range(epoch): if args.distributed: train_loader.sampler.set_epoch(epoch) lr = optimizer.param_groups[1]['lr'] train_loss, train_gt, train_probs, train_imgs, train_logits, train_loss_mtr = batch_trainer( cfg, args=args, epoch=e, model=model, model_ema=model_ema, train_loader=train_loader, criterion=criterion, optimizer=optimizer, loss_w=loss_w, scheduler=lr_scheduler if cfg.TRAIN.LR_SCHEDULER.TYPE == 'annealing_cosine' else None, ) if args.distributed: if args.local_rank == 0: print("Distributing BatchNorm running means and vars") distribute_bn(model, args.world_size, args.dist_bn == 'reduce') if model_ema is not None and not cfg.TRAIN.EMA.FORCE_CPU: if args.local_rank == 0: print('using model_ema to validate') if args.distributed: distribute_bn(model_ema, args.world_size, args.dist_bn == 'reduce') valid_loss, valid_gt, valid_probs, valid_imgs, valid_logits, valid_loss_mtr = valid_trainer( cfg, args=args, epoch=e, model=model_ema.module, valid_loader=valid_loader, criterion=criterion, loss_w=loss_w ) else: valid_loss, valid_gt, valid_probs, valid_imgs, valid_logits, valid_loss_mtr = valid_trainer( cfg, args=args, epoch=e, model=model, valid_loader=valid_loader, criterion=criterion, loss_w=loss_w ) if cfg.TRAIN.LR_SCHEDULER.TYPE == 'plateau': lr_scheduler.step(metrics=valid_loss) elif cfg.TRAIN.LR_SCHEDULER.TYPE == 'warmup_cosine': lr_scheduler.step(epoch=e + 1) elif cfg.TRAIN.LR_SCHEDULER.TYPE == 'multistep': lr_scheduler.step() if cfg.METRIC.TYPE == 'pedestrian': train_result = get_pedestrian_metrics(train_gt, train_probs, index=None, cfg=cfg) valid_result = get_pedestrian_metrics(valid_gt, valid_probs, index=None, cfg=cfg) if args.local_rank == 0: print(f'Evaluation on train set, train losses {train_loss}\n', 'ma: {:.4f}, label_f1: {:.4f}, pos_recall: {:.4f} , neg_recall: {:.4f} \n'.format( train_result.ma, np.mean(train_result.label_f1), np.mean(train_result.label_pos_recall), np.mean(train_result.label_neg_recall)), 'Acc: {:.4f}, Prec: {:.4f}, Rec: {:.4f}, F1: {:.4f}'.format( train_result.instance_acc, train_result.instance_prec, train_result.instance_recall, train_result.instance_f1)) print(f'Evaluation on test set, valid losses {valid_loss}\n', 'ma: {:.4f}, label_f1: {:.4f}, pos_recall: {:.4f} , neg_recall: {:.4f} \n'.format( valid_result.ma, np.mean(valid_result.label_f1), np.mean(valid_result.label_pos_recall), np.mean(valid_result.label_neg_recall)), 'Acc: {:.4f}, Prec: {:.4f}, Rec: {:.4f}, F1: {:.4f}'.format( valid_result.instance_acc, valid_result.instance_prec, valid_result.instance_recall, valid_result.instance_f1)) print(f'{time_str()}') print('-' * 60) if args.local_rank == 0: tb_visualizer_pedes(tb_writer, lr, e, train_loss, valid_loss, train_result, valid_result, train_gt, valid_gt, train_loss_mtr, valid_loss_mtr, model, train_loader.dataset.attr_id) cur_metric = valid_result.ma if cur_metric > maximum: maximum = cur_metric best_epoch = e save_ckpt(model, path, e, maximum) result_list[e] = { 'train_result': train_result, # 'train_map': train_map, 'valid_result': valid_result, # 'valid_map': valid_map, 'train_gt': train_gt, 'train_probs': train_probs, 'valid_gt': valid_gt, 'valid_probs': valid_probs, 'train_imgs': train_imgs, 'valid_imgs': valid_imgs } elif cfg.METRIC.TYPE == 'multi_label': train_metric = get_multilabel_metrics(train_gt, train_probs) valid_metric = get_multilabel_metrics(valid_gt, valid_probs) if args.local_rank == 0: print( 'Train Performance : mAP: {:.4f}, OP: {:.4f}, OR: {:.4f}, OF1: {:.4f} CP: {:.4f}, CR: {:.4f}, ' 'CF1: {:.4f}'.format(train_metric.map, train_metric.OP, train_metric.OR, train_metric.OF1, train_metric.CP, train_metric.CR, train_metric.CF1)) print( 'Test Performance : mAP: {:.4f}, OP: {:.4f}, OR: {:.4f}, OF1: {:.4f} CP: {:.4f}, CR: {:.4f}, ' 'CF1: {:.4f}'.format(valid_metric.map, valid_metric.OP, valid_metric.OR, valid_metric.OF1, valid_metric.CP, valid_metric.CR, valid_metric.CF1)) print(f'{time_str()}') print('-' * 60) tb_writer.add_scalars('train/lr', {'lr': lr}, e) tb_writer.add_scalars('train/losses', {'train': train_loss, 'test': valid_loss}, e) tb_writer.add_scalars('train/perf', {'mAP': train_metric.map, 'OP': train_metric.OP, 'OR': train_metric.OR, 'OF1': train_metric.OF1, 'CP': train_metric.CP, 'CR': train_metric.CR, 'CF1': train_metric.CF1}, e) tb_writer.add_scalars('test/perf', {'mAP': valid_metric.map, 'OP': valid_metric.OP, 'OR': valid_metric.OR, 'OF1': valid_metric.OF1, 'CP': valid_metric.CP, 'CR': valid_metric.CR, 'CF1': valid_metric.CF1}, e) cur_metric = valid_metric.map if cur_metric > maximum: maximum = cur_metric best_epoch = e save_ckpt(model, path, e, maximum) result_list[e] = { 'train_result': train_metric, 'valid_result': valid_metric, 'train_gt': train_gt, 'train_probs': train_probs, 'valid_gt': valid_gt, 'valid_probs': valid_probs } else: assert False, f'{cfg.METRIC.TYPE} is unavailable' with open(result_path, 'wb') as f: pickle.dump(result_list, f) return maximum, best_epoch def argument_parser(): parser = argparse.ArgumentParser(description="attribute recognition", formatter_class=argparse.ArgumentDefaultsHelpFormatter) parser.add_argument( "--cfg", help="decide which cfg to use", type=str, default="./configs/pedes_baseline/pa100k.yaml", ) parser.add_argument("--debug", type=str2bool, default="true") parser.add_argument('--local_rank', help='node rank for distributed training', default=0, type=int) parser.add_argument('--dist_bn', type=str, default='', help='Distribute BatchNorm stats between nodes after each epoch ("broadcast", "reduce", or "")') args = parser.parse_args() return args if __name__ == '__main__': args = argument_parser() update_config(cfg, args) main(cfg, args) “
最新发布
09-04
<think> 我们详细分析这段代码。这段代码是一个用于多属性识别(如行人属性识别或图像多标签分类)的深度学习训练流程。它使用了PyTorch框架,并支持分布式训练、多种学习率调度器、指数移动平均(EMA)模型、多种数据集和评估指标等。 代码结构分析: 1. 导入必要的库和模块。 2. 定义主函数main(),完成整个训练流程的配置和初始化。 3. 定义训练函数trainer(),执行多个epoch的训练和验证。 4. 定义参数解析函数argument_parser(),用于命令行参数的解析。 5. 程序入口:解析参数,更新配置,调用main函数。 下面我们按函数和关键步骤详细解释。 一、主函数main(cfg, args): 1. 设置随机种子,确保实验可重复。 2. 设置实验结果的保存路径(model_dir, log_dir)。 3. 初始化Visdom和TensorBoard可视化工具(如果配置中启用)。 4. 重定向标准输出到文件(如果配置中启用)。 5. 分布式训练初始化(如果检测到多GPU环境): - 设置分布式环境,包括后端(nccl)、初始化方法(env://)、世界大小(world_size)和当前进程的rank。 6. 数据预处理变换:通过get_transform(cfg)获取训练和验证的数据增强方式。 7. 数据集加载: - 如果配置为行人属性数据集('pedes'),则使用PedesAttr类加载训练集和验证集。 - 如果配置为多标签数据集('multi_label'),则使用COCO14类加载。 8. 数据加载器: - 训练集使用DistributedSampler(分布式训练时)或随机打乱(单卡训练)。 - 验证集固定顺序。 9. 打印数据集信息(属性数量、训练集/验证集大小等)。 10. 计算标签比率(用于损失函数的样本权重)。 11. 构建模型: - 构建主干网络(backbone)和分类器(classifier),然后组合成FeatClassifier模型。 - 将模型放到GPU上,并转换为分布式数据并行(DDP)或数据并行(DP)。 12. 可选:构建指数移动平均模型(EMA)。 13. 可选:从检查点重新加载权重。 14. 构建损失函数(criterion),根据配置选择损失类型(如BCE损失),并传入标签比率等参数。 15. 设置优化器: - 根据配置选择优化器类型(SGD, Adam, AdamW)。 - 参数分组:如果启用BN的权重衰减(BN_WD),则将参数分为两组(finetune和fresh),分别设置不同的学习率和权重衰减;否则,将BN参数和其他参数分开(BN参数无权重衰减)。 16. 设置学习率调度器(lr_scheduler): - 可选择:ReduceLROnPlateau(当指标停止提升时降低学习率)、MultiStepLR(在指定epoch步降低)、带重启的余弦退火(CosineAnnealingLR_with_Restart)、带热重启的余弦退火(CosineLRScheduler)等。 17. 调用trainer函数进行训练。 二、训练函数trainer(cfg, args, epoch, model, model_ema, train_loader, valid_loader, criterion, optimizer, lr_scheduler, path, loss_w, viz, tb_writer): 1. 初始化最佳指标和最佳epoch。 2. 循环训练epoch次: a. 如果是分布式训练,设置train_loader的采样器epoch。 b. 调用batch_trainer进行一个epoch的训练,返回训练损失、真实标签、预测概率等。 c. 如果是分布式训练,分发BatchNorm的统计信息(同步多个GPU上的BN层)。 d. 验证阶段: - 如果使用EMA模型,则用EMA模型验证;否则用原模型。 - 调用valid_trainer进行验证,返回验证集上的损失和预测结果。 e. 根据配置的学习率调度器类型更新学习率(例如,Plateau根据验证损失调整)。 f. 根据配置的评估指标类型(pedestrian或multi_label)计算训练集和验证集的评估结果。 - 行人属性(pedestrian)使用get_pedestrian_metrics计算指标(如ma, label_f1, 实例级准确率等)。 - 多标签(multi_label)使用get_multilabel_metrics计算指标(如mAP, OP, OR等)。 g. 在0号进程(主进程)上打印评估结果,并记录到TensorBoard。 h. 如果当前验证集的指标(如ma或mAP)优于历史最佳,则保存模型。 i. 将每个epoch的结果保存到字典result_list中,并序列化到pkl文件。 3. 返回最佳指标和对应的epoch。 三、参数解析函数argument_parser(): 定义命令行参数,如配置文件路径(--cfg)、调试模式(--debug)、分布式训练中的本地rank(--local_rank)等。 四、程序入口: 解析参数,更新配置(update_config),调用main函数。 关键点解释: 1. 分布式训练: 代码中通过检测环境变量'WORLD_SIZE'来判断是否处于分布式训练环境。如果是,则初始化分布式进程组,并将模型用DistributedDataParallel包装。 2. 数据并行: 如果没有分布式训练,则使用torch.nn.DataParallel进行单机多卡训练。 3. 指数移动平均(EMA): 如果启用,会创建模型参数的指数移动平均版本,用于验证。EMA有助于提高模型的鲁棒性。 4. 参数分组: 模型参数被分为两组:finetune_params(通常是预训练主干的参数)和fresh_params(新添加的分类器参数)。这两组参数可以设置不同的学习率和权重衰减。 5. 损失函数: 使用build_loss函数构建,支持多种损失,传入的sample_weight是每个属性的正样本比例,用于处理样本不平衡。 6. 学习率调度器: 支持多种调度器,其中带重启的余弦退火和带热重启的余弦退火能够帮助模型跳出局部最优。 7. 评估指标: 行人属性任务使用行人属性指标(包括label-level和instance-level的指标),多标签分类使用mAP等指标。 8. 结果保存: 每个epoch的结果(包括真实标签、预测概率、评估指标等)被保存为pkl文件。最佳模型权重保存为pth文件。 9. 可视化: 支持TensorBoard和Visdom,记录训练过程中的损失、学习率、评估指标等。 代码中一些细节说明: - torch.autograd.set_detect_anomaly(True) 用于在调试时检测梯度异常。 - 使用工具函数ReDirectSTD将标准输出重定向到文件,方便记录日志。 - 使用pickle将每个epoch的结果序列化保存,方便后续分析。 注意:batch_trainer和valid_trainer函数在另一个文件batch_engine.py中定义,负责实际的一个epoch的训练和验证逻辑。 由于代码较长,我们只对整体结构进行了分析。如果需要更深入的细节(如batch_trainer的实现),可以进一步询问。 </think> 以下是对这段代码的详细分析,这是一个完整的多属性分类(行人属性识别/多标签图像分类)深度学习训练框架的实现: ### 一、代码结构和核心功能 这段代码实现了一个高度可配置的多属性分类训练框架,主要功能包括: 1. **配置管理**:使用YAML配置文件定义所有训练参数 2. **分布式训练**:支持多GPU分布式训练 3. **模型构建**:灵活的主干网络和分类器组合 4. **训练流程**:完整的训练-验证循环 5. **可视化**:TensorBoard和Visdom支持 6. **模型保存**:最佳模型和训练结果保存 7. **多种评估指标**:支持行人属性和多标签分类的不同评估标准 ### 二、代码详细解析 #### 1. 初始化和配置设置(main函数前半部分) ```python def main(cfg, args): set_seed(605) # 设置随机种子确保可复现性 exp_dir = os.path.join('exp_result', cfg.DATASET.NAME) # 实验目录 # 创建模型和日志目录 model_dir, log_dir = get_model_log_path(exp_dir, cfg.NAME) stdout_file = os.path.join(log_dir, f'stdout_{time_str()}.txt') # 日志文件 # 可视化工具初始化 visdom = None if cfg.VIS.VISDOM: visdom = Visdom(env=f'{cfg.DATASET.NAME}_' + cfg.NAME, port=8401) writer = None if cfg.VIS.TENSORBOARD.ENABLE: writer = SummaryWriter(log_dir=writer_dir) # 重定向标准输出 if cfg.REDIRECTOR: ReDirectSTD(stdout_file, 'stdout', False) # 分布式训练初始化 if 'WORLD_SIZE' in os.environ: args.distributed = int(os.environ['WORLD_SIZE']) > 1 if args.distributed: torch.cuda.set_device(args.local_rank) torch.distributed.init_process_group(backend='nccl', init_method='env://') ``` #### 2. 数据集加载和预处理 ```python # 获取数据增强变换 train_tsfm, valid_tsfm = get_transform(cfg) # 加载数据集 if cfg.DATASET.TYPE == 'pedes': # 行人属性数据集 train_set = PedesAttr(cfg=cfg, split=cfg.DATASET.TRAIN_SPLIT, transform=train_tsfm) valid_set = PedesAttr(cfg=cfg, split=cfg.DATASET.VAL_SPLIT, transform=valid_tsfm) elif cfg.DATASET.TYPE == 'multi_label': # 多标签数据集 train_set = COCO14(cfg=cfg, split=cfg.DATASET.TRAIN_SPLIT, transform=train_tsfm) valid_set = COCO14(cfg=cfg, split=cfg.DATASET.VAL_SPLIT, transform=valid_tsfm) # 分布式采样器 if args.distributed: train_sampler = torch.utils.data.distributed.DistributedSampler(train_set) # 创建数据加载器 train_loader = DataLoader( dataset=train_set, batch_size=cfg.TRAIN.BATCH_SIZE, sampler=train_sampler, shuffle=train_sampler is None, num_workers=4, pin_memory=True, drop_last=True, ) ``` #### 3. 模型构建和优化器设置 ```python # 构建主干网络 backbone, c_output = build_backbone(cfg.BACKBONE.TYPE, cfg.BACKBONE.MULTISCALE) # 构建分类器 classifier = build_classifier(cfg.CLASSIFIER.NAME)( nattr=train_set.attr_num, # 属性数量 c_in=c_output, # 输入通道数 bn=cfg.CLASSIFIER.BN, # 是否使用BatchNorm pool=cfg.CLASSIFIER.POOLING, # 池化方式 scale=cfg.CLASSIFIER.SCALE # 缩放因子 ) # 组合成完整模型 model = FeatClassifier(backbone, classifier, bn_wd=cfg.TRAIN.BN_WD) # 分布式训练设置 if args.distributed: model = torch.nn.SyncBatchNorm.convert_sync_batchnorm(model) model = torch.nn.parallel.DistributedDataParallel(model, device_ids=[args.local_rank]) else: model = torch.nn.DataParallel(model) # EMA模型设置 model_ema = None if cfg.TRAIN.EMA.ENABLE: model_ema = ModelEmaV2( model, decay=cfg.TRAIN.EMA.DECAY, device='cpu' if cfg.TRAIN.EMA.FORCE_CPU else None) # 损失函数 criterion = build_loss(cfg.LOSS.TYPE)( sample_weight=label_ratio, # 样本权重 scale=cfg.CLASSIFIER.SCALE, size_sum=cfg.LOSS.SIZESUM, tb_writer=writer ) # 优化器设置 if cfg.TRAIN.OPTIMIZER.TYPE.lower() == 'adamw': optimizer = AdamW(param_groups) # ...其他优化器类型处理... # 学习率调度器 if cfg.TRAIN.LR_SCHEDULER.TYPE == 'warmup_cosine': lr_scheduler = CosineLRScheduler( optimizer, t_initial=cfg.TRAIN.MAX_EPOCH, lr_min=1e-5, warmup_lr_init=1e-4, warmup_t=cfg.TRAIN.MAX_EPOCH * cfg.TRAIN.LR_SCHEDULER.WMUP_COEF, ) # ...其他调度器类型处理... ``` #### 4. 训练循环(trainer函数) ```python def trainer(cfg, args, epoch, model, model_ema, train_loader, valid_loader, criterion, ...): # 初始化最佳指标 maximum = float(-np.inf) # 训练循环 for e in range(epoch): # 分布式训练设置 if args.distributed: train_loader.sampler.set_epoch(epoch) # 训练步骤 train_loss, train_gt, train_probs = batch_trainer( cfg, model, train_loader, criterion, optimizer, ... ) # 验证步骤 if model_ema is not None: valid_loss, valid_gt, valid_probs = valid_trainer( cfg, model_ema.module, valid_loader, criterion, ... ) # 学习率调整 if cfg.TRAIN.LR_SCHEDULER.TYPE == 'plateau': lr_scheduler.step(metrics=valid_loss) # 评估指标计算 if cfg.METRIC.TYPE == 'pedestrian': # 行人属性评估 valid_result = get_pedestrian_metrics(valid_gt, valid_probs, cfg=cfg) # 保存最佳模型 if valid_result.ma > maximum: maximum = valid_result.ma save_ckpt(model, path, e, maximum) elif cfg.METRIC.TYPE == 'multi_label': # 多标签分类评估 valid_metric = get_multilabel_metrics(valid_gt, valid_probs) # TensorBoard记录 tb_writer.add_scalars('test/perf', { 'mAP': valid_metric.map, 'OP': valid_metric.OP, # ...其他指标... }, e) return maximum, best_epoch ``` #### 5. 命令行参数处理 ```python def argument_parser(): parser = argparse.ArgumentParser(description="attribute recognition") parser.add_argument("--cfg", default="./configs/pedes_baseline/pa100k.yaml") parser.add_argument("--debug", type=str2bool, default="true") parser.add_argument('--local_rank', default=0, type=int) return parser.parse_args() ``` ### 三、关键组件详解 #### 1. 数据处理流程 1. **数据增强**:`get_transform(cfg)`根据配置生成训练/验证的增强策略 2. **数据集加载**: - `PedesAttr`:行人属性数据集 - `COCO14`:多标签分类数据集 3. **数据加载器**:支持分布式采样器,自动处理数据分片 #### 2. 模型架构 ```python class FeatClassifier(nn.Module): def __init__(self, backbone, classifier, bn_wd=True): super(FeatClassifier, self).__init__() self.backbone = backbone self.classifier = classifier self.bn_wd = bn_wd def forward(self, x, label=None): feat_map = self.backbone(x) logits = self.classifier(feat_map) return logits ``` #### 3. 分布式训练机制 ```python # 初始化分布式环境 torch.distributed.init_process_group(backend='nccl', init_method='env://') # 转换BN层为SyncBatchNorm model = torch.nn.SyncBatchNorm.convert_sync_batchnorm(model) # 包装为DDP模型 model = torch.nn.parallel.DistributedDataParallel( model, device_ids=[args.local_rank] ) ``` #### 4. EMA(指数移动平均)模型 ```python class ModelEmaV2(nn.Module): def __init__(self, model, decay=0.999, device=None): super(ModelEmaV2, self).__init__() self.module = deepcopy(model).eval() self.decay = decay self.device = device def update(self, model): with torch.no_grad(): for ema_param, model_param in zip(self.module.parameters(), model.parameters()): ema_param.data.mul_(self.decay).add_(model_param.data, alpha=1 - self.decay) ``` ### 四、训练流程优化技术 1. **学习率调度策略**: - `CosineAnnealingLR_with_Restart`:带重启的余弦退火 - `CosineLRScheduler`:带热身阶段的余弦退火 - `ReduceLROnPlateau`:基于验证损失的动态调整 2. **参数分组优化**: ```python # 微调参数和新参数使用不同学习率 param_groups = [ {'params': model.module.finetune_params(), 'lr': cfg.TRAIN.LR_SCHEDULER.LR_FT}, {'params': model.module.fresh_params(), 'lr': cfg.TRAIN.LR_SCHEDULER.LR_NEW} ] ``` 3. **损失函数定制**: - 支持样本加权处理类别不平衡 - 可配置的缩放因子和尺寸求和 ### 五、评估指标系统 #### 行人属性评估 (`get_pedestrian_metrics`) ```python def get_pedestrian_metrics(gt_label, preds_probs, index=None, cfg=None): # 计算实例级指标 instance_acc = (pred_label == gt_label).mean(axis=1).mean() # 计算标签级指标 label_pos_recall = recall_per_class(gt_label, pred_label, 'positive') label_neg_recall = recall_per_class(gt_label, pred_label, 'negative') # 平均准确率 ma = (label_pos_recall + label_neg_recall) / 2 return PedesMetrics( instance_acc=instance_acc, ma=ma.mean(), label_pos_recall=label_pos_recall, label_neg_recall=label_neg_recall ) ``` #### 多标签评估 (`get_multilabel_metrics`) ```python def get_multilabel_metrics(gt_label, preds_probs, threshold=0.5): pred_label = (preds_probs > threshold).astype(float) # 计算mAP ap_list = [] for i in range(gt_label.shape[1]): ap = average_precision_score(gt_label[:, i], preds_probs[:, i]) ap_list.append(ap) mAP = np.mean(ap_list) # 计算其他指标 OP = precision_score(gt_label, pred_label, average='samples') OR = recall_score(gt_label, pred_label, average='samples') OF1 = f1_score(gt_label, pred_label, average='samples') return MultiLabelMetrics( map=mAP, OP=OP, OR=OR, OF1=OF1 ) ``` ### 六、代码设计亮点 1. **高度模块化**:各组件(数据集、模型、损失函数等)均可通过配置灵活替换 2. **分布式训练优化**:完善的DDP支持,包含BN同步和分布式采样 3. **训练稳定性**:EMA模型、多种学习率调度策略、梯度异常检测 4. **全面评估指标**:支持多种任务类型的专用评估指标 5. **可视化集成**:同时支持TensorBoard和Visdom 6. **实验管理**:自动创建实验目录,保存模型和日志 7. **配置文件驱动**:所有超参数通过配置文件管理,提高可复现性 ### 七、执行流程总结 1. 解析命令行参数和配置文件 2. 初始化分布式训练环境(如果启用) 3. 准备数据集和数据加载器 4. 构建模型架构(主干+分类器) 5. 设置优化器和学习率调度器 6. 执行训练-验证循环 7. 定期评估模型并保存最佳权重 8. 记录训练指标和可视化结果 9. 保存最终模型和训练结果 这段代码为多属性分类任务提供了一个完整、高效且可扩展的训练框架,特别适合行人属性识别和多标签图像分类等任务。 --- ### 相关问题
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值