Ejemplo n.º 1
0
    def find_node(self,
                  ia_op_exec_context: InputAgnosticOperationExecutionContext,
                  tensor_metas: List[TensorMeta],
                  tm_comparators: List[TensorMetaComparator]) -> NNCFNode:
        op_exec_context = OperationExecutionContext(
            ia_op_exec_context.operator_name,
            ia_op_exec_context.scope_in_model,
            ia_op_exec_context.call_order,
            tensor_metas,
            tm_comparators=tm_comparators)
        nncf_node_candidates = []
        node_candidates = self._find_nodes_with_matching_context_and_inputs(
            op_exec_context)
        if not node_candidates:
            node_candidates = self._find_nodes_with_matching_context_among_inputless(
                op_exec_context)

        for nx_node in node_candidates.values():
            nncf_node_candidates.append(
                NNCFNode(nx_node[NNCFGraph.ID_NODE_ATTR], op_exec_context))
        result = None
        if len(nncf_node_candidates) == 1:
            result = nncf_node_candidates[0]
        if len(nncf_node_candidates) > 1:
            nncf_logger.warning("More than one node matches input")
            result = nncf_node_candidates[0]

        return result
Ejemplo n.º 2
0
    def apply_minmax_init(self,
                          min_values,
                          max_values,
                          log_module_name: str = None):
        if self.initialized:
            nncf_logger.debug(
                "Skipped initializing {} - loaded from checkpoint".format(
                    log_module_name))
            return
        if torch.any(torch.eq(min_values, np.inf)) or torch.any(
                torch.eq(max_values, -np.inf)):
            raise AttributeError(
                'Statistics is not collected for {}'.format(log_module_name))
        sign = torch.any(torch.lt(min_values, 0))
        if self.signedness_to_force is not None and sign != self.signedness_to_force:
            nncf_logger.warning("Forcing signed to {} for module {}".format(
                self.signedness_to_force, log_module_name))
            sign = self.signedness_to_force
        self.signed = int(sign)

        abs_max = torch.max(torch.abs(max_values), torch.abs(min_values))
        SCALE_LOWER_THRESHOLD = 0.1
        self.scale.fill_(SCALE_LOWER_THRESHOLD)
        self.scale.masked_scatter_(torch.gt(abs_max, SCALE_LOWER_THRESHOLD),
                                   abs_max)

        nncf_logger.info("Set sign: {} and scale: {} for {}".format(
            self.signed, get_flat_tensor_contents_string(self.scale),
            log_module_name))
Ejemplo n.º 3
0
    def run_batchnorm_adaptation(self, config):
        initializer_params = config.get("initializer", {})
        init_bn_adapt_config = initializer_params.get('batchnorm_adaptation', {})
        num_bn_adaptation_samples = init_bn_adapt_config.get('num_bn_adaptation_samples', 0)
        num_bn_forget_samples = init_bn_adapt_config.get('num_bn_forget_samples', 0)
        try:
            bn_adaptation_args = config.get_extra_struct(BNAdaptationInitArgs)
            has_bn_adapt_init_args = True
        except KeyError:
            has_bn_adapt_init_args = False

        if not init_bn_adapt_config:
            if has_bn_adapt_init_args:
                nncf_logger.warning("Enabling quantization batch norm adaptation with default parameters.")
                num_bn_adaptation_samples = 2000
                num_bn_forget_samples = 1000

        if num_bn_adaptation_samples < 0:
            raise AttributeError('Number of adaptation samples must be >= 0')
        if num_bn_adaptation_samples > 0:
            if not has_bn_adapt_init_args:
                nncf_logger.info(
                    'Could not run batchnorm adaptation '
                    'as the adaptation data loader is not provided as an extra struct. '
                    'Refer to `NNCFConfig.register_extra_structs` and the `BNAdaptationInitArgs` class')
                return
            batch_size = bn_adaptation_args.data_loader.batch_size
            num_bn_forget_steps = numpy.ceil(num_bn_forget_samples / batch_size)
            num_bn_adaptation_steps = numpy.ceil(num_bn_adaptation_samples / batch_size)
            bn_adaptation_runner = DataLoaderBNAdaptationRunner(self._model, bn_adaptation_args.device,
                                                                num_bn_forget_steps)
            bn_adaptation_runner.run(bn_adaptation_args.data_loader, num_bn_adaptation_steps)
Ejemplo n.º 4
0
    def _apply_minmax_init(self,
                           min_values,
                           max_values,
                           log_module_name: str = None):
        if torch.any(torch.eq(min_values, np.inf)) or torch.any(
                torch.eq(max_values, -np.inf)):
            raise AttributeError(
                'Statistics is not collected for {}'.format(log_module_name))
        sign = torch.any(torch.lt(min_values, 0))
        if self.signedness_to_force is not None and sign != self.signedness_to_force:
            nncf_logger.warning("Forcing signed to {} for module {}".format(
                self.signedness_to_force, log_module_name))
            sign = self.signedness_to_force
        self.signed = int(sign)

        abs_max = torch.max(torch.abs(max_values), torch.abs(min_values))
        SCALE_LOWER_THRESHOLD = 0.1
        mask = torch.gt(abs_max, SCALE_LOWER_THRESHOLD)
        self._scale_param_storage.data = torch.where(
            mask, abs_max,
            SCALE_LOWER_THRESHOLD * torch.ones_like(self._scale_param_storage))
        if self._is_using_log_scale_storage:
            self._scale_param_storage.data.log_()

        nncf_logger.info("Set sign: {} and scale: {} for {}".format(
            self.signed, get_flat_tensor_contents_string(self.scale),
            log_module_name))
Ejemplo n.º 5
0
    def find_node(self,
                  ia_op_exec_context: InputAgnosticOperationExecutionContext,
                  tensor_metas: List[TensorMeta],
                  tm_comparators: List[TensorMetaComparator]) -> NNCFNode:
        nncf_node_candidates = []
        iter_scopes = self._get_iteration_scopes(
            ia_op_exec_context.scope_in_model)
        # compare meta information about first input nodes during the matching. During the iteration some nodes may
        # change number of inputs, e.g. on concat of hidden outputs
        input_matcher = FirstInputsMatcher()
        op_exec_context = OperationExecutionContext(
            ia_op_exec_context.operator_name,
            ia_op_exec_context.scope_in_model,
            ia_op_exec_context.call_order,
            tensor_metas,
            input_matcher=input_matcher,
            tm_comparators=tm_comparators)
        node_candidates = self._find_nodes_with_matching_context_and_inputs(
            op_exec_context)
        if not node_candidates:
            op_exec_context = OperationExecutionContext(
                ia_op_exec_context.operator_name,
                ia_op_exec_context.scope_in_model,
                ia_op_exec_context.call_order,
                tensor_metas,
                tm_comparators=tm_comparators)
            node_candidates = self._find_nodes_with_matching_context_among_inputless(
                op_exec_context)
            if not node_candidates and iter_scopes:
                # ignore information about node creator and index of input
                comparators = tm_comparators + [
                    ShapeOnlyTensorMetaComparator()
                ]
                op_exec_context = OperationExecutionContext(
                    ia_op_exec_context.operator_name,
                    ia_op_exec_context.scope_in_model,
                    ia_op_exec_context.call_order,
                    tensor_metas,
                    tm_comparators=comparators)
                # match with starting points of iteration
                iter_nodes = self._match_first_iteration_nodes(
                    op_exec_context, iter_scopes)
                for node in iter_nodes.items():
                    nncf_node_candidates.append(node[1])

        for nx_node in node_candidates.values():
            nncf_node_candidates.append(
                NNCFNode(nx_node[NNCFGraph.ID_NODE_ATTR], op_exec_context))

        result = None
        if len(nncf_node_candidates) == 1:
            result = nncf_node_candidates[0]
        if len(nncf_node_candidates) > 1:
            nncf_logger.warning("More than one node matches input")
            result = nncf_node_candidates[0]

        return result
 def check_parameter_size(key, saved_value, num_loaded_layers):
     saved_size = saved_value.size()
     size = state_dict[key].size()
     if saved_size == size:
         new_dict[key] = saved_value
         return num_loaded_layers + 1
     nncf_logger.warning("Different size of value of '{}' in dictionary ({}) and in resuming model ({})"
                         .format(key, saved_size, size, ))
     skipped_keys.append(key)
     return num_loaded_layers
Ejemplo n.º 7
0
 def __init__(self, sparsity_algo, params=None):
     super().__init__(sparsity_algo, params)
     self.power = self._params.get('power', 0.9)
     self.concave = self._params.get('concave', False)
     self._update_per_optimizer_step = self._params.get('update_per_optimizer_step', False)
     if self._update_per_optimizer_step:
         self._steps_per_epoch = self._params.get('steps_per_epoch')
         if self._steps_per_epoch is None:
             logger.warning("Optimizer set to update sparsity level per optimizer step,"
                            "but steps_per_epoch was not set in config. Will only start updating "
                            "sparsity level after measuring the actual steps per epoch as signaled "
                            "by a .epoch_step() call.")
Ejemplo n.º 8
0
 def visualize_graph(self, path):
     out_graph = self._get_graph_for_visualization()
     nx.drawing.nx_pydot.write_dot(out_graph, path)
     try:
         A = to_agraph(out_graph)
         A.layout('dot')
         png_path = os.path.splitext(path)[0]+'.png'
         A.draw(png_path)
     except ImportError:
         nncf_logger.warning("Graphviz is not installed - only the .dot model visualization format will be used. "
                             "Install pygraphviz into your Python environment and graphviz system-wide to enable "
                             "PNG rendering.")
Ejemplo n.º 9
0
 def get_model_size(self, per_quantizer_bw: Dict[QuantizerId,
                                                 int]) -> np.int64:
     model_size = 0
     for qid, nparam in self._nparam_map.items():
         if qid in per_quantizer_bw:
             model_size += nparam * per_quantizer_bw[qid]
         else:
             logger.warning(
                 "[ModelSizeCalculator] Missing Bitwidth of QID: {}, using {} bits"
                 .format(str(qid), ModelSizeCalculator.FLOAT_BITWIDTH))
             model_size += nparam * ModelSizeCalculator.FLOAT_BITWIDTH
     return model_size
Ejemplo n.º 10
0
 def get_class_by_type_name(type_name):
     """
     Return class of metaop that corresponds to type_name type.
     """
     cls = PRUNING_OPERATOR_METATYPES.get_operator_metatype_by_op_name(
         type_name)
     if cls is None:
         nncf_logger.warning(
             "Layer {} is not pruneable - will not propagate pruned filters through it"
             .format(type_name))
         cls = StopMaskForwardOps
     return cls
Ejemplo n.º 11
0
 def check_parameter_size(key, value_to_load, num_loaded_layers):
     size_of_value_to_load = value_to_load.size()
     size = model_state_dict[key].size()
     if size_of_value_to_load == size:
         new_dict[key] = value_to_load
         return num_loaded_layers + 1
     nncf_logger.warning(
         "Different size of value of '{}' in resuming dictionary ({}) and in model ({})"
         .format(
             key,
             size_of_value_to_load,
             size,
         ))
     skipped_keys.append(key)
     return num_loaded_layers
def process_problematic_keys(is_resume, issues, is_all_saved_loaded):
    error_msgs = []

    def add_error_msg(name, keys):
        error_msgs.insert(
            0, '{} key(s):\n{}. '.format(name,
                                         ',\n'.join('\t\t"{}"'.format(k) for k in keys)))

    for name, keys in issues.items():
        is_missing = name == 'Missing'
        if keys and (not is_missing or is_missing and (is_resume or not is_all_saved_loaded)):
            add_error_msg(name, keys)
    if error_msgs:
        error_msg = 'Error(s) when loading model parameters:\n\t{}'.format("\n\t".join(error_msgs))
        if is_resume:
            raise RuntimeError(error_msg)
        nncf_logger.warning(error_msg)
Ejemplo n.º 13
0
    def epoch_step(self, next_epoch=None):
        if self._update_per_optimizer_step:
            if self.current_epoch == 0 and self._steps_in_current_epoch > 0 and self._steps_per_epoch is None:
                self._steps_per_epoch = self._steps_in_current_epoch

                # Reset step and epoch step counters
                next_epoch = -1
            if self._steps_in_current_epoch != self._steps_per_epoch and self._steps_in_current_epoch > 0:
                self._steps_per_epoch = self._steps_in_current_epoch
                logger.warning("Actual optimizer steps per epoch is different than what is "
                               "specified by scheduler parameters! Scheduling may be incorrect. "
                               "Setting scheduler's global step count to (current epoch) * "
                               "(actual steps per epoch)")
                self.step(self._steps_per_epoch * (self.current_epoch - 1))

        self._steps_in_current_epoch = 0
        super().epoch_step(next_epoch)
Ejemplo n.º 14
0
    def wrap_inputs(self, model_args, model_kwargs):
        bound_model_params = self._fwd_signature.bind(*model_args,
                                                      **model_kwargs)
        for param_name in self._fwd_params_to_input_infos_odict:
            param_kind = self._fwd_signature.parameters[param_name].kind
            if param_kind is Parameter.VAR_POSITIONAL or param_kind is Parameter.VAR_KEYWORD:
                nncf_logger.warning(
                    "An input_info tensor was bound to a *args or **kwargs variadic parameter in the"
                    "forward's signature! This is currently unsupported by NNCF. Input compression may "
                    "be incorrect.")
                # Currently won't support input info mapping to *args or **kwargs-mapped parameters
                continue

            if param_name not in bound_model_params.arguments:
                nncf_logger.warning(
                    "A call to a compressed model's forward occured without one of the params"
                    "specified in input_infos! Input compression may be incorrect. Trying to recover "
                    "by wrapping the default value for the parameter.")
                bound_model_params.apply_defaults()

            potential_tensor = bound_model_params.arguments[param_name]
            if potential_tensor is not None:
                bound_model_params.arguments[param_name] = nncf_model_input(
                    bound_model_params.arguments[param_name])
            else:
                # Default was None - cannot wrap as-is. Will wrap a dummy tensor as specified in
                # input infos - will conserve the call order of nncf_model_input nodes,
                # and the post-hooks for the input node will execute. The result won't go anywhere, though.
                nncf_logger.warning(
                    "Wrapping a dummy tensor for input {}".format(
                        'param_name'))
                info_for_missing_input = self._fwd_params_to_input_infos_odict[
                    param_name]
                device = 'cuda'
                if self._module_ref_for_device is not None:
                    device = next(
                        self._module_ref_for_device.parameters()).device
                dummy_tensor = create_mock_tensor(info_for_missing_input,
                                                  device)
                _ = nncf_model_input(dummy_tensor)

        return bound_model_params.args, bound_model_params.kwargs
Ejemplo n.º 15
0
    def apply_init(self) -> SingleConfigQuantizerSetup:
        from nncf.automl.environment.quantization_env import QuantizationEnv
        from nncf.automl.agent.ddpg.ddpg import DDPG
        from nncf.debug import DEBUG_LOG_DIR

        if self._dump_autoq_data or is_debug():
            dump_dir = self._init_args.config.get('log_dir', None)
            if dump_dir is None:
                dump_dir = DEBUG_LOG_DIR
            self.dump_dir = Path(dump_dir) / Path("autoq_agent_dump")
            self.dump_dir.mkdir(parents=True, exist_ok=True)

            self.policy_dict = OrderedDict() #key: episode
            self.best_policy_dict = OrderedDict() #key: episode

            self._init_args.config['episodic_nncfcfg'] = self.dump_dir / "episodic_nncfcfg"
            os.makedirs(self._init_args.config['episodic_nncfcfg'], exist_ok=True)

            try:
                from torch.utils.tensorboard import SummaryWriter
                self.tb_writer = SummaryWriter(self.dump_dir)
                # log compression config to tensorboard
                self.tb_writer.add_text('AutoQ/run_config',
                                         json.dumps(self._init_args.config['compression'],
                                         indent=4, sort_keys=False).replace("\n", "\n\n"), 0)
            except ModuleNotFoundError:
                logger.warning("Tensorboard installation not found! Install tensorboard Python package "
                               "in order for AutoQ tensorboard statistics data to be dumped")

        start_ts = datetime.now()

        from nncf.automl.environment.quantization_env import QuantizationEnvParams
        env_params = QuantizationEnvParams(compression_ratio=self._params.compression_ratio,
            eval_subset_ratio=self._params.eval_subset_ratio,
            skip_constraint=self._params.skip_constraint,
            finetune=self._params.finetune,
            bits=self._params.bits,
            dump_init_precision_data=self._dump_autoq_data,
            log_dir=Path(DEBUG_LOG_DIR) / Path("autoq"))

        # Instantiate Quantization Environment
        env = QuantizationEnv(
            self._model,
            self.quantization_controller,
            self._hw_precision_constraints,
            self._init_args.data_loader,
            self._init_args.eval_fn,
            hw_config_type=self._hw_cfg_type,
            params=env_params)

        nb_state = len(env.state_list)
        nb_action = 1

        # Instantiate Automation Agent
        agent = DDPG(nb_state, nb_action, self._iter_number, hparam_override=self._ddpg_hparams_override)

        if self._dump_autoq_data and self.tb_writer is not None:
            self.tb_writer.add_text('AutoQ/state_embedding', env.master_df[env.state_list].to_markdown())

        best_policy, best_reward = self._search(agent, env)

        end_ts = datetime.now()

        final_qid_vs_qconfig_map = env.select_config_for_actions(best_policy)

        final_quantizer_setup = self.quantization_controller.get_quantizer_setup_for_current_state()
        for qp_id, qconf in final_qid_vs_qconfig_map.items():
            final_quantizer_setup.quantization_points[qp_id].qconfig = qconf

        logger.info('[AutoQ] best_reward: {}'.format(best_reward))
        logger.info('[AutoQ] best_policy: {}'.format(best_policy))
        logger.info("[AutoQ] Search Complete")
        logger.info("[AutoQ] Elapsed time of AutoQ Precision Initialization (): {}".format(end_ts-start_ts))
        return final_quantizer_setup