# Copyright (c) OpenMMLab. All rights reserved. import copy import pytest import torch.nn as nn import mmcv from mmcv.utils import IS_IPU_AVAILABLE if IS_IPU_AVAILABLE: from poptorch.options import _IExecutionStrategy from mmcv.device.ipu import cfg2options from mmcv.device.ipu.utils import (build_from_cfg_with_wrapper, model_sharding) skip_no_ipu = pytest.mark.skipif( not IS_IPU_AVAILABLE, reason='test case under ipu environment') class ToyModel(nn.Module): def __init__(self): super().__init__() self.conv = nn.Conv2d(3, 3, 1) self.bn = nn.BatchNorm2d(3) self.relu = nn.ReLU6() @skip_no_ipu def test_build_from_cfg(): BACKBONES = mmcv.Registry('backbone') @BACKBONES.register_module() class ResNet: def __init__(self, depth, stages=4): self.depth = depth self.stages = stages @BACKBONES.register_module() class ResNeXt: def __init__(self, depth, stages=4): self.depth = depth self.stages = stages cfg = dict(type='ResNet', depth=50) model = build_from_cfg_with_wrapper(cfg, BACKBONES) assert isinstance(model, ResNet) assert model.depth == 50 and model.stages == 4 cfg = dict(type='ResNet', depth=50) model = build_from_cfg_with_wrapper( cfg, BACKBONES, default_args={'stages': 3}) assert isinstance(model, ResNet) assert model.depth == 50 and model.stages == 3 cfg = dict(type='ResNeXt', depth=50, stages=3) model = build_from_cfg_with_wrapper(cfg, BACKBONES) assert isinstance(model, ResNeXt) assert model.depth == 50 and model.stages == 3 cfg = dict(type=ResNet, depth=50) model = build_from_cfg_with_wrapper(cfg, BACKBONES) assert isinstance(model, ResNet) assert model.depth == 50 and model.stages == 4 # type defined using default_args cfg = dict(depth=50) model = build_from_cfg_with_wrapper( cfg, BACKBONES, default_args=dict(type='ResNet')) assert isinstance(model, ResNet) assert model.depth == 50 and model.stages == 4 cfg = dict(depth=50) model = build_from_cfg_with_wrapper( cfg, BACKBONES, default_args=dict(type=ResNet)) assert isinstance(model, ResNet) assert model.depth == 50 and model.stages == 4 # not a registry with pytest.raises(TypeError): cfg = dict(type='VGG') model = build_from_cfg_with_wrapper(cfg, 'BACKBONES') # non-registered class with pytest.raises(KeyError): cfg = dict(type='VGG') model = build_from_cfg_with_wrapper(cfg, BACKBONES) # default_args must be a dict or None with pytest.raises(TypeError): cfg = dict(type='ResNet', depth=50) model = build_from_cfg_with_wrapper(cfg, BACKBONES, default_args=1) # cfg['type'] should be a str or class with pytest.raises(TypeError): cfg = dict(type=1000) model = build_from_cfg_with_wrapper(cfg, BACKBONES) # cfg should contain the key "type" with pytest.raises(KeyError, match='must contain the key "type"'): cfg = dict(depth=50, stages=4) model = build_from_cfg_with_wrapper(cfg, BACKBONES) # cfg or default_args should contain the key "type" with pytest.raises(KeyError, match='must contain the key "type"'): cfg = dict(depth=50) model = build_from_cfg_with_wrapper( cfg, BACKBONES, default_args=dict(stages=4)) # incorrect registry type with pytest.raises(TypeError): cfg = dict(type='ResNet', depth=50) model = build_from_cfg_with_wrapper(cfg, 'BACKBONES') # incorrect default_args type with pytest.raises(TypeError): cfg = dict(type='ResNet', depth=50) model = build_from_cfg_with_wrapper(cfg, BACKBONES, default_args=0) # incorrect arguments with pytest.raises(TypeError): cfg = dict(type='ResNet', non_existing_arg=50) model = build_from_cfg_with_wrapper(cfg, BACKBONES) # cfg not dict with pytest.raises(TypeError): cfg = [] model = build_from_cfg_with_wrapper(cfg, BACKBONES) @skip_no_ipu def test_cast_to_options(): options_cfg = dict( randomSeed=888, enableExecutableCaching='cache_engine', train_cfg=dict( executionStrategy='SameAsIpu', Training=dict(gradientAccumulation=8), availableMemoryProportion=[0.3, 0.3, 0.3, 0.3], ), eval_cfg=dict(deviceIterations=1, ), ) ipu_options = cfg2options(copy.deepcopy(options_cfg)) assert 'training' in ipu_options assert 'inference' in ipu_options assert ipu_options['training']._values['random_seed'] == 888 assert ipu_options['training']._values['replication_factor'] == 1 assert ipu_options['training']._values['available_memory_proportion'] == { 0: 0.3, 1: 0.3, 2: 0.3, 3: 0.3 } assert ipu_options['training']._popart.options[ 'cachePath'] == 'cache_engine' assert isinstance(ipu_options['training']._execution_strategy, _IExecutionStrategy) assert ipu_options['inference']._values['device_iterations'] == 1 with pytest.raises(NotImplementedError, match='cfg type'): _options_cfg = copy.deepcopy(options_cfg) _options_cfg['randomSeed'] = (1, 3) cfg2options(_options_cfg) with pytest.raises(NotImplementedError, match='options_node type'): _options_cfg = copy.deepcopy(options_cfg) _options_cfg['train_cfg']['Precision'] = {'autocast_policy': 123} cfg2options(_options_cfg) @skip_no_ipu def test_model_sharding(): model = ToyModel() split_edges = [dict(layer_to_call='666', ipu_id=0)] with pytest.raises(RuntimeError, match='split_edges:'): model_sharding(model, split_edges) model = ToyModel() split_edges = [ dict(layer_to_call='conv', ipu_id=0), dict(layer_to_call=1, ipu_id=0) ] with pytest.raises(ValueError, match='The same layer is referenced'): model_sharding(model, split_edges) model = ToyModel() split_edges = [dict(layer_to_call='conv', ipu_id=0)] model_sharding(model, split_edges)