Пример #1
0
def test_can_load_quant_algo__with_defaults():
    model = BasicConvTestModel()
    config = get_quantization_config_without_range_init()
    register_bn_adaptation_init_args(config)
    builder = create_compression_algorithm_builder(config)
    assert isinstance(builder, QuantizationBuilder)

    quant_model, _ = create_compressed_model_and_algo_for_test(
        deepcopy(model), config)

    model_conv = get_all_modules_by_type(model, 'Conv2d')
    quant_model_conv = get_all_modules_by_type(
        quant_model.get_nncf_wrapped_model(), 'NNCFConv2d')
    assert len(model_conv) == len(quant_model_conv)

    for module_scope, _ in model_conv.items():
        quant_scope = deepcopy(module_scope)  # type: Scope
        quant_scope.pop()
        quant_scope.push(ScopeElement('NNCFConv2d', 'conv'))
        assert quant_scope in quant_model_conv.keys()

        store = []
        for op in quant_model_conv[quant_scope].pre_ops.values():
            if isinstance(op, (UpdateInputs, UpdateWeight)) and isinstance(
                    op.operand, SymmetricQuantizer):
                assert op.__class__.__name__ not in store
                store.append(op.__class__.__name__)
        assert UpdateWeight.__name__ in store
Пример #2
0
def test_quantization_configs__with_defaults():
    model = BasicConvTestModel()
    config = get_quantization_config_without_range_init()
    register_bn_adaptation_init_args(config)
    _, compression_ctrl = create_compressed_model_and_algo_for_test(
        model, config)

    assert isinstance(compression_ctrl, QuantizationController)
    weight_quantizers = compression_ctrl.weight_quantizers
    activation_quantizer_infos = compression_ctrl.non_weight_quantizers

    ref_weight_qspec = PTQuantizerSpec(
        num_bits=8,
        mode=QuantizationMode.SYMMETRIC,
        signedness_to_force=True,
        narrow_range=True,
        half_range=False,
        scale_shape=model.wq_scale_shape_per_channel,
        logarithm_scale=False)
    for wq_info in weight_quantizers.values():
        compare_qspecs(ref_weight_qspec, wq_info.quantizer_module_ref)

    ref_activation_qspec = PTQuantizerSpec(num_bits=8,
                                           mode=QuantizationMode.SYMMETRIC,
                                           signedness_to_force=None,
                                           narrow_range=False,
                                           half_range=False,
                                           scale_shape=(1, ),
                                           logarithm_scale=False)
    for aq_info in activation_quantizer_infos.values():
        compare_qspecs(ref_activation_qspec, aq_info.quantizer_module_ref)
Пример #3
0
def test_quantize_outputs_with_scope_overrides():
    config = get_quantization_config_without_range_init()
    config["input_info"] = [{
        "sample_size": [2, 3, 32, 32],
    }]
    model = QuantizeOutputsTestModel()
    config['compression']['quantize_outputs'] = True
    config['target_device'] = "TRIAL"
    config['compression']['scope_overrides'] = {
        "activations": {
            "/nncf_model_output_0": {
                "bits": 4,
                "mode": "asymmetric",
            }
        }
    }
    register_bn_adaptation_init_args(config)
    model, ctrl = create_compressed_model_and_algo_for_test(model, config)
    output_quantizers =\
        [q for qid, q in ctrl.all_quantizations.items() if isinstance(qid, NonWeightQuantizerId)]
    for q in output_quantizers[1:]:
        assert q.num_bits == 8
        assert isinstance(q, SymmetricQuantizer)

    assert output_quantizers[0].num_bits == 4
    assert isinstance(output_quantizers[0], AsymmetricQuantizer)
Пример #4
0
 def create_autoq_test_config(batch_size=10,
                              image_size=10,
                              num_channels=3,
                              num_init_samples=1):
     config = get_quantization_config_without_range_init()
     config['input_info'] = {
         "sample_size": [batch_size, num_channels, image_size, image_size],
     }
     config['batch_size'] = batch_size
     config['compression'].update({
         'initializer': {
             'precision': {
                 "type": "autoq",
                 "bits": [2, 4, 8],
                 "iter_number": 3,
                 "compression_ratio": 0.15,
                 "eval_subset_ratio": 1.0,
                 "warmup_iter_number": 2
             },
             'range': {
                 'num_init_samples': num_init_samples
             },
             'batchnorm_adaptation': {
                 'num_bn_adaptation_samples': 0
             }
         }
     })
     return config
 def create_hawq_test_config(batch_size=10,
                             num_data_points=100,
                             image_size=10):
     config = get_quantization_config_without_range_init()
     config['input_info'] = {
         "sample_size": [batch_size, 3, image_size, image_size],
     }
     config['batch_size'] = batch_size
     config['compression'].update({
         'initializer': {
             'precision': {
                 "type": "hawq",
                 "bits": [4, 8, 6],
                 "num_data_points": num_data_points,
                 "iter_number": 1,
                 "tolerance": 1e-2
             },
             'range': {
                 'num_init_samples': 1
             },
             'batchnorm_adaptation': {
                 'num_bn_adaptation_samples': 0
             }
         }
     })
     return config
Пример #6
0
def test_quantize_outputs():
    config = get_quantization_config_without_range_init()
    config["input_info"] = [{
        "sample_size": [2, 3, 32, 32],
    }]
    model = QuantizeOutputsTestModel()
    config['compression']['quantize_outputs'] = True
    register_bn_adaptation_init_args(config)
    model, qctrl = create_compressed_model_and_algo_for_test(model, config)
    REF_QUANTIZED_OUTPUT_MODULE_SCOPES = [
        'QuantizeOutputsTestModel/NNCFConv2d[conv1]/conv2d_0|OUTPUT',
        'QuantizeOutputsTestModel/NNCFConv2d[conv2]/conv2d_0|OUTPUT',
        'QuantizeOutputsTestModel/NNCFConv2d[conv3]/conv2d_0|OUTPUT',
        'QuantizeOutputsTestModel/NNCFConv2d[conv4]/conv2d_0|OUTPUT'
    ]
    actual_output_quantizer_str_scopes =\
         [str(aq_id) for aq_id in qctrl.non_weight_quantizers if 'nncf_model_input' not in str(aq_id)]
    assert len(REF_QUANTIZED_OUTPUT_MODULE_SCOPES) == len(
        actual_output_quantizer_str_scopes)

    for ref_qinput_scope_str in REF_QUANTIZED_OUTPUT_MODULE_SCOPES:
        matches = []
        for aq_id in qctrl.non_weight_quantizers:
            if str(aq_id) == ref_qinput_scope_str:
                matches.append(aq_id)
        assert len(matches) == 1
        quantizer = qctrl.non_weight_quantizers[
            matches[0]].quantizer_module_ref
        assert isinstance(quantizer, SymmetricQuantizer)
 def __init__(self):
     self.model_creator = AddTwoConv
     config = get_quantization_config_without_range_init()
     config['compression']['initializer'].update({
         "precision": {
             "bitwidth_per_scope":
             [[2, 'AddTwoConv/NNCFConv2d[conv1]/conv2d_0|WEIGHT'],
              [4, 'AddTwoConv/NNCFConv2d[conv2]/conv2d_0|WEIGHT']]
         }
     })
     config['target_device'] = 'TRIAL'
     config['compression']["activations"] = {"bits": 6}
     self.config = config
     self.ref_bits = [
         (WeightQuantizerId(
             target_node_name='AddTwoConv/NNCFConv2d[conv1]/conv2d_0'), 2),
         (WeightQuantizerId(
             target_node_name='AddTwoConv/NNCFConv2d[conv2]/conv2d_0'), 4),
         (NonWeightQuantizerId(
             target_node_name='AddTwoConv/NNCFConv2d[conv2]/conv2d_0'), 6),
         (NonWeightQuantizerId(
             target_node_name='AddTwoConv/NNCFConv2d[conv1]/conv2d_0'), 6),
         (NonWeightQuantizerId('/nncf_model_input_0'), 6)
     ]
     self.expected_stats = BitwidthDistributionStatistics(
         num_wq_per_bitwidth={
             4: 1,
             2: 1
         }, num_aq_per_bitwidth={6: 3})
     self.config_to_resume = None
Пример #8
0
    def test_unified_scales_are_identical_in_onnx(self, tmp_path):
        # pylint:disable=no-member
        nncf_config = get_quantization_config_without_range_init(model_size=1)
        nncf_config["compression"]["quantize_outputs"] = True
        nncf_config["input_info"] = [
            {
                "sample_size": [1, 1, 1, 2],
            },
        ]
        nncf_config["target_device"] = "VPU"
        register_bn_adaptation_init_args(nncf_config)

        compressed_model, compression_ctrl = create_compressed_model_and_algo_for_test(
            SimplerModelForUnifiedScalesTesting(), nncf_config)

        with torch.no_grad():
            for quant_info in compression_ctrl.non_weight_quantizers.values():
                if isinstance(quant_info.quantizer_module_ref,
                              AsymmetricQuantizer):
                    quant_info.quantizer_module_ref.input_range *= torch.abs(
                        torch.rand_like(
                            quant_info.quantizer_module_ref.input_range))
                else:
                    quant_info.quantizer_module_ref.scale *= torch.abs(
                        torch.rand_like(quant_info.quantizer_module_ref.scale))

        test_input1 = torch.ones([1, 1, 1, 2])
        compressed_model.forward(test_input1)

        onnx_path = str(tmp_path / "model.onnx")
        compression_ctrl.export_model(onnx_path)

        onnx_model = onnx.load(onnx_path)

        fq_nodes = TestsWithONNXInspection.get_fq_nodes(onnx_model)
        eltwise_dominator_predicate = partial(
            TestsWithONNXInspection.immediately_dominates_add_or_mul,
            graph=onnx_model.graph)
        eltwise_fq_nodes = list(filter(eltwise_dominator_predicate, fq_nodes))

        cat_dominator_predicate = partial(
            TestsWithONNXInspection.immediately_dominates_cat,
            graph=onnx_model.graph)
        cat_fq_nodes = list(filter(cat_dominator_predicate, fq_nodes))

        fq_nodes_grouped_by_output = TestsWithONNXInspection.group_nodes_by_output_target(
            eltwise_fq_nodes + cat_fq_nodes, onnx_model.graph)

        for unified_scale_group in fq_nodes_grouped_by_output:
            inputs = [
                resolve_constant_node_inputs_to_values(fq_node,
                                                       onnx_model.graph)
                for fq_node in unified_scale_group
            ]
            for inputs_dict in inputs[1:]:
                curr_values = list(inputs_dict.values())
                ref_values = list(inputs[0].values())
                assert curr_values == ref_values  # All inputs for unified scale quantizers must be equal
Пример #9
0
def test_debug_mode():
    config = get_quantization_config_without_range_init()
    register_bn_adaptation_init_args(config)
    model = BasicConvTestModel()
    with nncf_debug():
        model, _ = create_compressed_model_and_algo_for_test(model, config)
        model.forward(
            torch.zeros(BasicConvTestModel.INPUT_SIZE,
                        device=next(model.parameters()).device))
Пример #10
0
    def test_weight_and_act_quantizer_scale_unification(self, tmp_path):
        # pylint:disable=no-member
        nncf_config = get_quantization_config_without_range_init(model_size=1)
        nncf_config["input_info"] = [
            {
                "sample_size": [1, 5],
                "type": "long",
                "filler": "zeros"
            },
        ]
        nncf_config["target_device"] = "VPU"
        register_bn_adaptation_init_args(nncf_config)

        compressed_model, compression_ctrl = create_compressed_model_and_algo_for_test(
            TwoEmbeddingAddModel(), nncf_config)

        with torch.no_grad():
            for quant_module in compression_ctrl.all_quantizations.values():
                if isinstance(quant_module, AsymmetricQuantizer):
                    quant_module.input_range *= torch.abs(
                        torch.rand_like(quant_module.input_range))
                else:
                    quant_module.scale *= torch.abs(
                        torch.rand_like(quant_module.scale))

        test_input1 = torch.ones([1, 5], dtype=torch.long)
        compressed_model.forward(test_input1)

        onnx_path = str(tmp_path / "model.onnx")
        compression_ctrl.export_model(onnx_path)

        onnx_model = onnx.load(onnx_path)

        fq_nodes = TestsWithONNXInspection.get_fq_nodes(onnx_model)
        eltwise_dominator_predicate = partial(
            TestsWithONNXInspection.immediately_dominates_add_or_mul,
            graph=onnx_model.graph)
        embedding_dominator_predicate = partial(
            TestsWithONNXInspection.immediately_dominates_embedding,
            graph=onnx_model.graph)
        eltwise_fq_nodes = list(filter(eltwise_dominator_predicate, fq_nodes))
        embedding_weight_fq_nodes = list(
            filter(embedding_dominator_predicate, fq_nodes))

        fq_nodes_with_expected_unified_scales = embedding_weight_fq_nodes + eltwise_fq_nodes

        unified_fq_node_inputs = [
            resolve_constant_node_inputs_to_values(fq_node, onnx_model.graph)
            for fq_node in fq_nodes_with_expected_unified_scales
        ]
        for inputs_dict in unified_fq_node_inputs[1:]:
            curr_values = list(inputs_dict.values())
            ref_values = list(unified_fq_node_inputs[0].values())
            assert curr_values == ref_values  # All inputs for unified scale quantizers must be equal
Пример #11
0
def test_can_create_quant_loss_and_scheduler():
    config = get_quantization_config_without_range_init()
    register_bn_adaptation_init_args(config)
    _, compression_ctrl = create_compressed_model_and_algo_for_test(
        BasicConvTestModel(), config)

    loss = compression_ctrl.loss
    assert isinstance(loss, PTCompressionLoss)

    scheduler = compression_ctrl.scheduler
    assert isinstance(scheduler, CompressionScheduler)
Пример #12
0
def test_mock_dump_checkpoint(aa_config):
    is_called_dump_checkpoint_fn = False

    def mock_dump_checkpoint_fn(model, compression_controller,
                                accuracy_aware_runner, aa_log_dir):
        from nncf.api.compression import CompressionAlgorithmController
        from nncf.common.accuracy_aware_training.runner import TrainingRunner
        assert isinstance(model, torch.nn.Module)
        assert isinstance(compression_controller,
                          CompressionAlgorithmController)
        assert isinstance(accuracy_aware_runner, TrainingRunner)
        assert isinstance(aa_log_dir, str)
        nonlocal is_called_dump_checkpoint_fn
        is_called_dump_checkpoint_fn = True

    config = get_quantization_config_without_range_init(LeNet.INPUT_SIZE[-1])
    train_loader = create_ones_mock_dataloader(aa_config, num_samples=10)
    model = LeNet()
    config.update(aa_config)

    def train_fn(compression_ctrl,
                 model,
                 epoch,
                 optimizer,
                 lr_scheduler,
                 train_loader=train_loader):
        pass

    def mock_validate_fn(model, init_step=False, epoch=0):
        return 80

    def configure_optimizers_fn():
        optimizer = SGD(model.parameters(), lr=0.001)
        return optimizer, None

    config = register_default_init_args(config,
                                        train_loader=train_loader,
                                        model_eval_fn=partial(mock_validate_fn,
                                                              init_step=True))

    model, compression_ctrl = create_compressed_model_and_algo_for_test(
        model, config)

    early_stopping_training_loop = EarlyExitCompressionTrainingLoop(
        config, compression_ctrl, dump_checkpoints=True)
    model = early_stopping_training_loop.run(
        model,
        train_epoch_fn=train_fn,
        validate_fn=partial(mock_validate_fn),
        configure_optimizers_fn=configure_optimizers_fn,
        dump_checkpoint_fn=mock_dump_checkpoint_fn)
    assert is_called_dump_checkpoint_fn
Пример #13
0
def test_quantizer_scale_linking(mocker):
    nncf_config = get_quantization_config_without_range_init(model_size=1)
    nncf_config["input_info"] = [{
        "sample_size": [1, 1, 1, 1],
    }, {
        "sample_size": [1, 1, 1, 1],
    }]
    nncf_config["compression"]["activations"] = {
        "unified_scale_ops": [[
            # Note: Assuming that quantizers are attached as a post-op to the specified operation
            "EltwiseQuantizerLinkingTestModel/Path[path2]/__mul___0",
            "EltwiseQuantizerLinkingTestModel/Path[path2]/__add___0",
        ]],
        "ignored_scopes": [
            # Ignore path output averaging operations
            "EltwiseQuantizerLinkingTestModel/__add___0",
            "EltwiseQuantizerLinkingTestModel/__add___1",
            "EltwiseQuantizerLinkingTestModel/__add___2",
        ]
    }
    register_bn_adaptation_init_args(nncf_config)

    compressed_model, compression_ctrl = create_compressed_model_and_algo_for_test(
        EltwiseQuantizerLinkingTestModel(), nncf_config)

    # 18 inputs to quantize (14 regular + 4 linked),
    # 8 quantization points left after propagation, out of these 3 are linked
    assert len(compression_ctrl.non_weight_quantizers) == 6

    shared_quantizer_id = NonWeightQuantizerId(
        target_node_name="/nncf_model_input_0")

    non_shared_spies = []
    for aq_id, aq_info in compression_ctrl.non_weight_quantizers.items():
        quantizer = aq_info.quantizer_module_ref
        spy = mocker.spy(quantizer, 'forward')
        if aq_id == shared_quantizer_id:
            shared_spy = spy
        else:
            non_shared_spies.append(spy)

    test_input1 = torch.ones([1, 1, 1, 1])
    test_input2 = 2 * test_input1
    compressed_model(test_input1, test_input2)

    assert shared_spy.call_count == 3
    for non_shared_spy in non_shared_spies:
        assert non_shared_spy.call_count == 1
Пример #14
0
    def get_model_and_ctrl_with_applied_hw_config_quantization(
            model: torch.nn.Module,
            hw_config_dict: dict,
            should_be_quantize_inputs: bool = True):
        nncf_config = get_quantization_config_without_range_init(model_size=1)
        nncf_config["compression"].update(
            {"quantize_inputs": should_be_quantize_inputs})
        nncf_config["target_device"] = "ANY"  # for compatibility

        net = NNCFNetwork(model, input_infos=[ModelInputInfo([1, 2, 1, 1])])
        hw_config = PTHWConfig.from_dict(hw_config_dict)
        qbuilder = QuantizationBuilder(nncf_config, should_init=False)
        qbuilder.hw_config = hw_config
        net = qbuilder.apply_to(net)
        ctrl = qbuilder.build_controller(net)
        return net, ctrl
Пример #15
0
def test_quantization_configs__custom():
    model = BasicConvTestModel()

    config = get_quantization_config_without_range_init()
    config['compression'].update({
        "weights": {
            "mode": "asymmetric",
            "per_channel": True,
            "bits": 4
        },
        "activations": {
            "mode": "asymmetric",
            "bits": 4,
            "signed": True,
        },
    })
    config['target_device'] = 'TRIAL'
    register_bn_adaptation_init_args(config)
    _, compression_ctrl = create_compressed_model_and_algo_for_test(
        model, config)

    assert isinstance(compression_ctrl, QuantizationController)
    weight_quantizers = compression_ctrl.weight_quantizers
    activation_quantizer_infos = compression_ctrl.non_weight_quantizers

    ref_weight_qspec = PTQuantizerSpec(
        num_bits=4,
        mode=QuantizationMode.ASYMMETRIC,
        signedness_to_force=None,
        scale_shape=model.wq_scale_shape_per_channel,
        narrow_range=True,
        half_range=False,
        logarithm_scale=False)
    for wq_info in weight_quantizers.values():
        compare_qspecs(ref_weight_qspec, wq_info.quantizer_module_ref)

    ref_activation_qspec = PTQuantizerSpec(num_bits=4,
                                           mode=QuantizationMode.ASYMMETRIC,
                                           signedness_to_force=True,
                                           scale_shape=(1, ),
                                           narrow_range=False,
                                           half_range=False,
                                           logarithm_scale=False)

    for aq_info in activation_quantizer_infos.values():
        compare_qspecs(ref_activation_qspec, aq_info.quantizer_module_ref)
Пример #16
0
def test_quantize_inputs():
    model = QuantizeInputsTestModel()
    config = get_quantization_config_without_range_init()
    config["input_info"] = [{
        "sample_size": [2, 3, 32, 32],
    }, {
        "sample_size": [2, 3, 32, 32],
    }, {
        "sample_size": [2, 3, 32, 32],
    }, {
        "sample_size": [2, 3, 32, 32],
    }, {
        "sample_size": [2, 3, 32, 32],
    }]
    register_bn_adaptation_init_args(config)

    model, qctrl = create_compressed_model_and_algo_for_test(model, config)
    REF_QUANTIZED_INPUT_MODULE_SCOPES = [
        '/nncf_model_input_0|OUTPUT', '/nncf_model_input_1|OUTPUT',
        '/nncf_model_input_2|OUTPUT', '/nncf_model_input_3|OUTPUT',
        '/nncf_model_input_4|OUTPUT'
    ]

    actual_input_quantizer_str_scopes = []
    for aq_id, aq_info in qctrl.non_weight_quantizers.items():
        for target_point in aq_info.affected_insertions:
            quantizer_target_node_name = str(target_point.target_node_name)
            if 'nncf_model_input' in quantizer_target_node_name:
                actual_input_quantizer_str_scopes.append(
                    quantizer_target_node_name)

    assert len(REF_QUANTIZED_INPUT_MODULE_SCOPES) == len(
        actual_input_quantizer_str_scopes)
    for qinput_scope_str in actual_input_quantizer_str_scopes:
        matches = set()
        for aq_id, aq_info in qctrl.non_weight_quantizers.items():
            for target_point in aq_info.affected_insertions:
                if qinput_scope_str in str(target_point.target_node_name):
                    matches.add(aq_id)
        assert len(matches) == 1
        input_aq_id = next(iter(matches))
        quantizer = qctrl.non_weight_quantizers[
            input_aq_id].quantizer_module_ref
        assert isinstance(quantizer, SymmetricQuantizer)
def test_staged_quantization_saves_enabled_quantizers_in_state_dict(tmp_path):
    config = get_quantization_config_without_range_init()
    config["compression"]["params"] = {
        "activations_quant_start_epoch": 2,
        "weights_quant_start_epoch": 1
    }
    register_bn_adaptation_init_args(config)
    _, ctrl_save = create_compressed_model_and_algo_for_test(
        BasicConvTestModel(), config)
    ctrl_save.scheduler.epoch_step()
    ctrl_save.scheduler.epoch_step()
    compression_state = ctrl_save.get_compression_state()
    _, ctrl_load = create_compressed_model_and_algo_for_test(
        BasicConvTestModel(), config, compression_state=compression_state)
    for quantizer_info in ctrl_load.non_weight_quantizers.values():
        assert not quantizer_info.quantizer_module_ref.is_enabled_quantization(
        )
    for quantizer_info in ctrl_load.weight_quantizers.values():
        assert quantizer_info.quantizer_module_ref.is_enabled_quantization()
def disable_quantizer_gradients():
    config = get_quantization_config_without_range_init()
    config['input_info'] = {
        "sample_size": [2, 3, 10, 10],
    }
    register_bn_adaptation_init_args(config)
    model = MobileNetV2(num_classes=10)
    model, compression_ctrl = create_compressed_model_and_algo_for_test(
        model, config)
    original_requires_grad_per_param = get_requires_grad_per_param(model)
    quantization_types = [
        class_type.__name__
        for class_type in QUANTIZATION_MODULES.registry_dict.values()
    ]
    all_quantizations = get_all_modules_by_type(model, quantization_types)
    quantizers_switcher = QuantizersSwitcher(list(all_quantizations.values()))
    params_to_restore = HAWQPrecisionInitializer.disable_all_gradients_except_weights_of_quantized_modules(
        quantizers_switcher, compression_ctrl.weight_quantizers, model,
        get_skipped_quantized_weight_node_names())
    return quantizers_switcher, params_to_restore, model, compression_ctrl, original_requires_grad_per_param
Пример #19
0
def test_eltwise_unified_scales_for_vpu():
    nncf_config = get_quantization_config_without_range_init(model_size=1)
    nncf_config["input_info"] = [{
        "sample_size": [1, 1, 1, 1],
    }, {
        "sample_size": [1, 1, 1, 1],
    }]
    nncf_config["target_device"] = "VPU"
    register_bn_adaptation_init_args(nncf_config)

    _, compression_ctrl = create_compressed_model_and_algo_for_test(
        EltwiseQuantizerLinkingTestModel(), nncf_config)

    assert len(compression_ctrl.non_weight_quantizers) == 2

    total_quantizations = sum([
        len(info.affected_insertions)
        for info in compression_ctrl.non_weight_quantizers.values()
    ])
    assert total_quantizations == 8
Пример #20
0
def test_unified_scales_with_shared_nodes():
    nncf_config = get_quantization_config_without_range_init(model_size=1)
    nncf_config["input_info"] = [
        {
            "sample_size": [1, 5],
            "type": "long",
            "filler": "zeros"
        },
    ]
    nncf_config["target_device"] = "VPU"
    register_bn_adaptation_init_args(nncf_config)

    _, compression_ctrl = create_compressed_model_and_algo_for_test(
        SharedEmbeddingAddModel(),
        nncf_config)  # type: NNCFNetwork, QuantizationController

    assert len(compression_ctrl.weight_quantizers
               ) == 1  # The two embedding nodes point to a single shared layer
    assert len(compression_ctrl.non_weight_quantizers
               ) == 0  # The "add" operation has its inputs already quantized
Пример #21
0
def test_unified_scales_with_concat(target_device, model_creator,
                                    ref_aq_module_count, ref_quantizations):
    nncf_config = get_quantization_config_without_range_init(model_size=1)
    nncf_config["input_info"] = [{
        "sample_size": [1, 4, 1, 1],
    }, {
        "sample_size": [1, 4, 1, 1],
    }]

    nncf_config["target_device"] = target_device
    register_bn_adaptation_init_args(nncf_config)

    _, compression_ctrl = create_compressed_model_and_algo_for_test(
        model_creator(), nncf_config)

    assert len(compression_ctrl.non_weight_quantizers) == ref_aq_module_count

    total_quantizations = sum([
        len(info.affected_insertions)
        for info in compression_ctrl.non_weight_quantizers.values()
    ])
    assert total_quantizations == ref_quantizations
Пример #22
0
def test_load_state_sets_initialized_flag():
    config = get_quantization_config_without_range_init()
    register_bn_adaptation_init_args(config)

    model = TwoConvTestModel()
    quant_model, qctrl = create_compressed_model_and_algo_for_test(
        model, config)

    load_state(
        quant_model,
        {
            'module.features.0.0.pre_ops.0.op.signed_tensor': torch.tensor(
                [1.0]),  # quantizer of 1st conv's weights
            'module.features.1.0.pre_ops.0.op.scale': torch.ones(
                1, 1, 1, 1)  # quantizer of 2nd conv's weights
        })

    for wq_info in qctrl.weight_quantizers.values():
        assert wq_info.quantizer_module_ref.initialized

    for aq_info in qctrl.non_weight_quantizers.values():
        assert not aq_info.quantizer_module_ref.initialized
Пример #23
0
def test_quantizers_have_proper_narrow_range_set():
    class Model(nn.Module):
        def __init__(self, size=1):
            super().__init__()
            self.size = size
            self.conv = nn.Conv2d(size, size, size)

        def forward(self, x):
            return self.conv(x)

    model = Model()
    config = get_quantization_config_without_range_init(model_size=2)
    register_bn_adaptation_init_args(config)
    quant_model, _ = create_compressed_model_and_algo_for_test(model, config)

    for module in quant_model.modules():
        if isinstance(module, NNCFConv2d):
            for op in module.pre_ops.values():
                assert isinstance(op, (UpdateWeight, UpdateInputs))
                assert op.operand.narrow_range == isinstance(op, UpdateWeight)
    for _, aq in quant_model.get_compression_modules_by_type(
            ExtraCompressionModuleType.EXTERNAL_QUANTIZER).items():
        assert aq.narrow_range is False
def test_accuracy_aware_config(aa_config, must_raise):
    def mock_validate_fn(model):
        pass

    config = get_quantization_config_without_range_init(LeNet.INPUT_SIZE[-1])

    config.update({
        "accuracy_aware_training": {
            "mode": "adaptive_compression_level",
            "params": {
                "maximal_relative_accuracy_degradation": 1,
                "initial_training_phase_epochs": 1,
                "patience_epochs": 10
            }
        }
    })

    config.update(aa_config)

    train_loader = create_ones_mock_dataloader(config, num_samples=10)
    model = LeNet()

    config = register_default_init_args(config,
                                        train_loader=train_loader,
                                        model_eval_fn=mock_validate_fn)
    model, compression_ctrl = create_compressed_model_and_algo_for_test(
        model, config)

    if must_raise:
        with pytest.raises(RuntimeError):
            _ = create_accuracy_aware_training_loop(config,
                                                    compression_ctrl,
                                                    dump_checkpoints=False)
    else:
        _ = create_accuracy_aware_training_loop(config,
                                                compression_ctrl,
                                                dump_checkpoints=False)
Пример #25
0
def test_early_exit_with_mock_validation(max_accuracy_degradation,
                                         exit_epoch_number,
                                         maximal_total_epochs=100):
    epoch_counter = 0

    def mock_validate_fn(model, init_step=False, epoch=0):
        original_metric = 0.85
        if init_step:
            return original_metric
        nonlocal epoch_counter
        epoch_counter = epoch
        if "maximal_relative_accuracy_degradation" in max_accuracy_degradation:
            return original_metric * (1 - 0.01 * max_accuracy_degradation[
                'maximal_relative_accuracy_degradation']) * (epoch /
                                                             exit_epoch_number)
        return (original_metric - max_accuracy_degradation['maximal_absolute_accuracy_degradation']) * \
               epoch / exit_epoch_number

    config = get_quantization_config_without_range_init(LeNet.INPUT_SIZE[-1])

    params = {"maximal_total_epochs": maximal_total_epochs}
    params.update(max_accuracy_degradation)
    accuracy_aware_config = {
        "accuracy_aware_training": {
            "mode": "early_exit",
            "params": params
        }
    }

    config.update(accuracy_aware_config)

    train_loader = create_ones_mock_dataloader(config, num_samples=10)
    model = LeNet()

    config = register_default_init_args(config,
                                        train_loader=train_loader,
                                        model_eval_fn=partial(mock_validate_fn,
                                                              init_step=True))

    model, compression_ctrl = create_compressed_model_and_algo_for_test(
        model, config)

    def train_fn(compression_ctrl,
                 model,
                 epoch,
                 optimizer,
                 lr_scheduler,
                 train_loader=train_loader):
        pass

    def configure_optimizers_fn():
        return None, None

    early_stopping_training_loop = EarlyExitCompressionTrainingLoop(
        config, compression_ctrl, dump_checkpoints=False)
    model = early_stopping_training_loop.run(
        model,
        train_epoch_fn=train_fn,
        validate_fn=partial(mock_validate_fn),
        configure_optimizers_fn=configure_optimizers_fn)
    # Epoch number starts from 0
    assert epoch_counter == exit_epoch_number
def get_quantization_config_with_ignored_scope():
    config = get_quantization_config_without_range_init()
    config['compression']['ignored_scopes'] = 'ConvLinear/NNCFLinear[fc]'
    return config
Пример #27
0
def test_early_exit_training_loop(max_accuracy_degradation,
                                  num_steps=10,
                                  learning_rate=1e-3,
                                  maximal_total_epochs=100,
                                  init_finetuning_steps=10):
    def validate_fn(model, epoch=0, train_loader=None):
        with set_torch_seed():
            train_loader = iter(train_loader)
            loss = torch.FloatTensor([0])
            with torch.no_grad():
                for _ in range(num_steps):
                    x, y_gt = next(train_loader)
                    y = model(x)
                    loss += F.mse_loss(y.sum(), y_gt)
        return 1 - loss.item()

    config = get_quantization_config_without_range_init(LeNet.INPUT_SIZE[-1])
    params = {
        "maximal_total_epochs": maximal_total_epochs,
    }
    params.update(max_accuracy_degradation)

    accuracy_aware_config = {
        "accuracy_aware_training": {
            "mode": "early_exit",
            "params": params
        }
    }

    config.update(accuracy_aware_config)

    model, train_loader, compression_ctrl = create_finetuned_lenet_model_and_dataloader(
        config, validate_fn, init_finetuning_steps)

    def train_fn(compression_ctrl,
                 model,
                 epoch,
                 optimizer,
                 lr_scheduler,
                 train_loader=train_loader):
        with set_torch_seed():
            train_loader = iter(train_loader)
            for _ in range(num_steps):
                compression_ctrl.scheduler.step()
                optimizer.zero_grad()
                x, y_gt = next(train_loader)
                y = model(x)
                loss = F.mse_loss(y.sum(), y_gt)
                loss.backward()
                optimizer.step()

    def configure_optimizers_fn():
        optimizer = SGD(model.parameters(), lr=learning_rate)
        return optimizer, None

    early_stopping_training_loop = EarlyExitCompressionTrainingLoop(
        config, compression_ctrl)
    model = early_stopping_training_loop.run(
        model,
        train_epoch_fn=train_fn,
        validate_fn=partial(validate_fn, train_loader=train_loader),
        configure_optimizers_fn=configure_optimizers_fn)
    original_model_accuracy = model.original_model_accuracy
    compressed_model_accuracy = validate_fn(model, train_loader=train_loader)
    if "maximal_absolute_accuracy_degradation" in max_accuracy_degradation:
        assert (original_model_accuracy - compressed_model_accuracy) <= \
               max_accuracy_degradation["maximal_absolute_accuracy_degradation"]
    else:
        assert (original_model_accuracy - compressed_model_accuracy) / original_model_accuracy * 100 <= \
               max_accuracy_degradation["maximal_relative_accuracy_degradation"]
 def __init__(self, name: str):
     self.name = name
     self.nncf_config = get_quantization_config_without_range_init()
     self.expects_error = False
     self.model = BasicConvTestModel()
Пример #29
0
def test_shared_layers_are_weight_quantized_only_once():
    model = SharedLayersModel()
    config = get_quantization_config_without_range_init(model_size=1)
    register_bn_adaptation_init_args(config)
    model, qctrl = create_compressed_model_and_algo_for_test(model, config)
    assert len(qctrl.weight_quantizers) == 1
Пример #30
0
def get_basic_asym_quantization_config(model_size=4):
    config = get_quantization_config_without_range_init(model_size)
    config['compression']['activations'] = {"mode": "asymmetric"}
    config['compression']['initializer']['range'] = {"num_init_samples": 0}
    return config