NNI调试记录-HPO

HPO(超参优化)

超参概念很泛,LR、Batchsize、MaxEpoch以及模型结构等都可以作为超参优化,搜索空间定义如下(下面只是部分参数,具体根据个人模型来配置):

search_space = {                                                                                                                                                                                                                         
   'LR': {'_type': 'loguniform', '_value': [0.0005, 0.05]},                                                                                                                                                                             
   'WD': {'_type': 'loguniform', '_value': [5e-4, 5e-3, 5e-2, 0.5]},                                                                                                                                                                    
   'PoolType':{'_type': 'choice', '_value': ['GlobalAvgPool', 'GlobalMaxPool', 'AdaptiveAvgMaxPool']},                                                                                                                                  
   'BatchSize':{'_type': 'choice', '_value': [128, 256, 512, 1024]},                                                                                                                                                                    
   'MaxEpoch':{'_type': 'choice', '_value': [201, 301, 401, 501]},                                                                                                                                                                      
   'Optimizer': {'_type': 'choice', '_value': ['Adam', 'AdamW']},                                                                                                                                                                       
   'HeadMargin':{'_type': 'loguniform', '_value': [0.1, 2.0]},                                                                                                                                                                          
   'HeadScale':{'_type': 'choice', '_value': [16, 32, 64, 128, 256]},                                                                                                                                                                   
} 

这些param的传递也很简单,直接替换掉config里面的变量值就行:

def setup(args):
    """
    Create configs and perform basic setups.
    """
    cfg = get_cfg()
    cfg.merge_from_file(args.config_file)
    cfg.merge_from_list(args.opts)
    nni_param = [
            'SOLVER.BASE_LR', params['LR'],
            'SOLVER.WEIGHT_DECAY', params['WD'],
            'SOLVER.IMS_PER_BATCH', params['BatchSize'],
            'MODEL.HEADS.POOL_LAYER', params['PoolType'],
            'SOLVER.MAX_EPOCH', params['MaxEpoch'],
            'SOLVER.OPT', params['Optimizer'],
            'MODEL.HEADS.MARGIN', params['HEADMargin'],
            'MODEL.HEADS.SCALE', params['HeadScale'],
            ]
    cfg.merge_from_list(nni_param)
    cfg.freeze()
    default_setup(cfg, args)
    return cfg

experiment配置

按照官方的教程,配置好参数更新,以及精度上报就行了。
其中最重要的部分是experiment的配置,demo如下:

from nni.experiment import Experiment

experiment = Experiment('local')
search_space = {
    'LR': {'_type': 'loguniform', '_value': [0.0005, 0.05]},
    'WD': {'_type': 'loguniform', '_value': [5e-4, 5e-3, 5e-2, 0.5]},
    'PoolType':{'_type': 'choice', '_value': ['GlobalAvgPool', 'GlobalMaxPool', 'AdaptiveAvgMaxPool']}, 
    'BatchSize':{'_type': 'choice', '_value': [128, 256, 512, 1024]}, 
    'MaxEpoch':{'_type': 'choice', '_value': [201, 301, 401, 501]}, 
    'Optimizer': {'_type': 'choice', '_value': ['Adam', 'AdamW']},
    'HeadMargin':{'_type': 'loguniform', '_value': [0.1, 2.0]}, 
    'HeadScale':{'_type': 'choice', '_value': [16, 32, 64, 128, 256]}, 
}
experiment.config.trial_command = 'python3 tools/train_net.py --config-file ./configs/Market1501/sbs_elan_aug.yml --num-gpus 1 MODEL.BACKBONE.WITH_IBN False OUTPUT_DIR logs/market1501/sbs_elan_96x32_22222_cropbody_nni_v1 TEST.EVAL_PERIOD 40'

experiment.config.trial_code_directory = '.'

experiment.config.search_space = search_space
experiment.config.tuner.name = 'TPE'
experiment.config.tuner.class_args['optimize_mode'] = 'maximize'
experiment.config.max_trial_number = 80
experiment.config.trial_concurrency = 4
experiment.config.trial_gpu_number = 1 
experiment.config.training_service.use_active_gpu = False

experiment.run(8081)
experiment.stop()

注意几个参数:

  1. 常用的服务类型,local,本地的单机单卡,单机多卡, remote,多机多卡,还有AML等;
  2. experiment.config.trial_command,就是脚本的执行命令,这个有点重要,特别是关于是否需要配置CUDA_VISIBLE_DEVICE,需要结合后面几个参数;
  3. experiment.config.max_trial_number,最大尝试次数,建议是>=20,因为tuner有个warmup的过程大概是20次;
  4. experiment.config.trial_concurrency ,并发数,指的是同时最大可进行的trial数;
  5. experiment.config.trial_gpu_number, 每个trail占用的GPU数,这个从字面看有点歧义;
  6. experiment.config.training_service.use_active_gpu,这个有点复杂,官方有解释,其实就是是否使用被其他进程占用的GPU,这个按需确定,如果是多人共享的服务器的话最好false;

关于多并发

  • 多并发涉及四个参数:
    a. trial_concurrency
    b.trial_gpu_number
    c.maxTrialNumberPerGpu
    d.training_service.use_active_gpu
  • 假设单机四卡GPU空载机器一台,参数设置trial_concurrency=4,trial_gpu_number=1,结合上面的解释即并发4任务,每个任务占用一块GPU,四个任务都可以并发执行。修改trial_gpu_number=2,每个任务占用两块,卡不够了,这个时候根据maxTrialNumberPerGpu(默认为1)来确定,如果maxTrialNumberPerGpu=2,且资源足够,则四个任务可以并发,每个任务两卡,其中每块卡上运行2个任务;
  • 同样的假设,参数设置trial_concurrency=4,trial_gpu_number=1,发只有1个任务runing,其他3个任务waiting,则看下experiment.config.trial_command运行的指令配置了CUDA_VISIBLE_DEVICE,删掉就正常了;
  • training_service.use_active_gpu,这个会影响到多并发,直接设为True最方便,但是有点不礼貌。

关于模型保存

  • . nni貌似是不支持trials的checkpoints保存,没找到参数控制(可能支持,但我查遍了API没有发现,仅有个无效的save_checkpoint());
  • . 训练脚本能保存Log和模型,但各trials会覆盖;

简单解决方法:

  1. 在训练脚本中获取当前pid
import os
curr_pid = os.getpid()
  1. 修改log的保存路径,把pid缀在后面
log_dir += f'_{curr_pid}'

关于有多个metric

多个metric,可能是多个不同的指标比如rank-1, rank-5,MAP等,或者是多个数据集的同一个指标,这个时候在调用nni.report_final_result或者nni.report_intermediate_result的时候需要注意下,原文如下:

metric should either be a float, or a dict that metric[‘default’] is a
float. If metric is a dict, metric[‘default’] will be used by tuner,
and other items can be visualized with web portal.

按照字面来做那就是,多个指标就传入一个字典,这个字典要有个’default‘这个key:

            nni.report_intermediate_result({
                'default': weighted_rank1,
                'OccludedDuke-Rank1': results['OccludedDuke']['Rank-1'],
                'Market1501-Rank1': results['Market1501']['Rank-1'],
                })   

Tuner

Tuner就是一套搜索逻辑,有简单有复杂,有利有弊,官方提供了以下几个:

  1. 简单粗暴
    1.1 Random。 随机采样;
    1.2 Grid。网格搜索,时间换空间,但是往往能找到最优参数;
    1.3 Batch。提供若干组确定的参数配置,然后在里面评估出最有的一组,如果有比较强且充足的先验的话这个比较适用;
  2. 贝叶斯
    2.1 TPE。默认的方法,轻量,支持所有搜索类型,但是不能利用参数之间的关系;
    2.2 SMAC。只支持choice, randint, uniform, loguniform, quniform这些类型;
    2.3 Metis。 能够同时输出(a) current prediction of optimal configuration, and (b) suggestion for the next trial。简单的理解,就是会告诉你下一次尝试的方向;
    2.4 GP。基于Gaussian Process。
  3. 启发式
    3.1 Anneal。随机搜索的升级版。
    3.2 PBT。基于population,可以在给定的computational budget寻找最有参数。

官方提供的tuner数量很多,大部分情况下TPE、GP这些算法就够了,使用范围广,且不需要配置tuner的参数,往往也能取得不错的效果。如果时间充足或者机器很足,直接用grid,简单粗暴效果好,当然,甚至可以写一个shell脚本,把tuner也当作搜索空间,那就真正实现完全搜索了。
具体对比可以参考 Hyper Parameter Optimization Comparison

  • 21
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

@daviiid

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值