def _compress_impl(self, dataloader: _Iterable, device: str) -> _nn.Module:
        """
        Compresses a model layerwise using the following steps:
        1) Compute inputs to the first layer which is set up for compression using input cacher
        2) For each layer, find submodules which are supported for compression and install compression
           hooks.
        3) Run forward pass through each layer, compute activation statistics and use them to
           compress weights.
        4) Compute updated outputs using compressed weights to propagate quantization error
           to the next layer and set them up as inputs to next layer.
        """
        inputs, kwarg_inputs = self._get_cached_inputs(dataloader, device)
        outputs = [None for _ in inputs]

        # compress the layers one by one
        for layer_idx, (parent_layer_name, layer) in enumerate(self._get_layers_to_compress()):
            layer.to(device)
            atomic_layers_dict = _get_atomic_layers(
                layer,
                layer_types=self._supported_modules,
                name_prefix=parent_layer_name,
            )

            # dict mapping layer_name -> compression algorithm object
            compression_algo_objects_dict = dict()

            # dict mapping layer_name -> forward hook handle
            layer_hooks = []

            for atomic_layer_name, atomic_layer in atomic_layers_dict.items():
                obj = self._init_and_config_layer(atomic_layer_name, atomic_layer)

                if obj is not None:
                    compression_algo_objects_dict[atomic_layer_name] = obj

                    layer_hooks.append(self._register_activation_processing_hook(atomic_layer, obj))

            # Compute statistics on the activations using the activation processing hooks
            outputs = self._forward_layer(
                layer,
                inputs,
                kwarg_inputs,
                outputs,
            )

            # Remove the activation processing hooks
            for h in layer_hooks:
                h.remove()

            # compress the layers
            _logger.info(f"Layer {layer_idx}")
            for (
                atomic_layer_name,
                compressor_algo,
            ) in compression_algo_objects_dict.items():
                _logger.info(f"Compressing {atomic_layer_name}")
                compressor_algo.compress()
                compressor_algo.cleanup()

            del compression_algo_objects_dict

            # feed the previous layer's outputs to this layer
            outputs = self._forward_layer(
                layer,
                inputs,
                kwarg_inputs,
                outputs,
            )

            # free memory
            layer.cpu()
            del layer
            _torch.cuda.empty_cache()

            # interchange inputs and outputs
            inputs, outputs = outputs, inputs

        _register_metadata_version(self._model)
        return self._model

        Compresses a model layerwise using the following steps:
        1) Compute inputs to the first layer which is set up for compression using input cacher
        2) For each layer, find submodules which are supported for compression and install compression
           hooks.
        3) Run forward pass through each layer, compute activation statistics and use them to
           compress weights.
        4) Compute updated outputs using compressed weights to propagate quantization error
           to the next layer and set them up as inputs to next layer.
        
coremltools/optimize/torch/layerwise_compression/layerwise_compressor.py