CoolFace
Apppublic

Aluode/PerceptionLabPortable

sourceHugging Faceupdated 9mo agoView on Hugging Face
0likes
base.py393 linesDownload Raw Back to quantizers
1# Copyright 2024 The HuggingFace Inc. team. All rights reserved.2#3# Licensed under the Apache License, Version 2.0 (the "License");4# you may not use this file except in compliance with the License.5# You may obtain a copy of the License at6#7#     http://www.apache.org/licenses/LICENSE-2.08#9# Unless required by applicable law or agreed to in writing, software10# distributed under the License is distributed on an "AS IS" BASIS,11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.12# See the License for the specific language governing permissions and13# limitations under the License.14from abc import ABC, abstractmethod15from typing import TYPE_CHECKING, Any, Optional, Union16 17from ..utils import is_torch_available, logging18from ..utils.quantization_config import QuantizationConfigMixin, QuantizationMethod19from .quantizers_utils import get_module_from_name20 21 22if TYPE_CHECKING:23    from ..modeling_utils import PreTrainedModel24 25if is_torch_available():26    import torch27    from torch.nn import ModuleList28else:29    ModuleList = str30 31logger = logging.get_logger(__file__)32 33 34class HfQuantizer(ABC):35    """36    Abstract class of the HuggingFace quantizer. Supports for now quantizing HF transformers models for inference and/or quantization.37    This class is used only for transformers.PreTrainedModel.from_pretrained and cannot be easily used outside the scope of that method38    yet.39 40    Attributes41        quantization_config (`transformers.utils.quantization_config.QuantizationConfigMixin`):42            The quantization config that defines the quantization parameters of your model that you want to quantize.43        modules_to_not_convert (`list[str]`, *optional*):44            The list of module names to not convert when quantizing the model.45        required_packages (`list[str]`, *optional*):46            The list of required pip packages to install prior to using the quantizer47        requires_calibration (`bool`):48            Whether the quantization method requires to calibrate the model before using it.49        requires_parameters_quantization (`bool`):50            Whether the quantization method requires to create a new Parameter. For example, for bitsandbytes, it is51            required to create a new xxxParameter in order to properly quantize the model.52    """53 54    requires_calibration = False55    required_packages = None56    requires_parameters_quantization = False57 58    def __init__(self, quantization_config: QuantizationConfigMixin, **kwargs):59        self.quantization_config = quantization_config60 61        # -- Handle extra kwargs below --62        self.modules_to_not_convert = kwargs.pop("modules_to_not_convert", [])63        self.pre_quantized = kwargs.pop("pre_quantized", True)64 65        if not self.pre_quantized and self.requires_calibration:66            raise ValueError(67                f"The quantization method {quantization_config.quant_method} does require the model to be pre-quantized."68                f" You explicitly passed `pre_quantized=False` meaning your model weights are not quantized. Make sure to "69                f"pass `pre_quantized=True` while knowing what you are doing."70            )71 72    def update_torch_dtype(self, dtype: "torch.dtype") -> "torch.dtype":73        """74        Deprecared in favor of `update_dtype`!75 76        Args:77            dtype (`torch.dtype`):78                The input dtype that is passed in `from_pretrained`79        """80        logger.warning_once(81            "`update_torch_dtype` is deprecated in favor of `update_dtype`! It will be removed in version v4.57"82        )83        return self.update_dtype(dtype)84 85    def update_dtype(self, dtype: "torch.dtype") -> "torch.dtype":86        """87        Some quantization methods require to explicitly set the dtype of the model to a88        target dtype. You need to override this method in case you want to make sure that behavior is89        preserved90 91        Args:92            dtype (`torch.dtype`):93                The input dtype that is passed in `from_pretrained`94        """95        return dtype96 97    def update_device_map(self, device_map: Optional[dict[str, Any]]) -> Optional[dict[str, Any]]:98        """99        Override this method if you want to pass a override the existing device map with a new100        one. E.g. for bitsandbytes, since `accelerate` is a hard requirement, if no device_map is101        passed, the device_map is set to `"auto"``102 103        Args:104            device_map (`Union[dict, str]`, *optional*):105                The device_map that is passed through the `from_pretrained` method.106        """107        return device_map108 109    def adjust_target_dtype(self, dtype: "torch.dtype") -> "torch.dtype":110        """111        Override this method if you want to adjust the `target_dtype` variable used in `from_pretrained`112        to compute the device_map in case the device_map is a `str`. E.g. for bitsandbytes we force-set `target_dtype`113        to `torch.int8` and for 4-bit we pass a custom enum `accelerate.CustomDtype.int4`.114 115        Args:116            dtype (`torch.dtype`, *optional*):117                The dtype that is used to compute the device_map.118        """119        return dtype120 121    def update_missing_keys(self, model, missing_keys: list[str], prefix: str) -> list[str]:122        """123        Override this method if you want to adjust the `missing_keys`.124 125        Args:126            missing_keys (`list[str]`, *optional*):127                The list of missing keys in the checkpoint compared to the state dict of the model128        """129        return missing_keys130 131    def update_expected_keys(self, model, expected_keys: list[str], loaded_keys: list[str]) -> list[str]:132        """133        Override this method if you want to adjust the `update_expected_keys`.134 135        Args:136            expected_keys (`list[str]`, *optional*):137                The list of the expected keys in the initialized model.138            loaded_keys (`list[str]`, *optional*):139                The list of the loaded keys in the checkpoint.140        """141        return expected_keys142 143    def update_unexpected_keys(self, model, unexpected_keys: list[str]) -> list[str]:144        return unexpected_keys145 146    def get_special_dtypes_update(self, model, dtype: "torch.dtype") -> dict[str, "torch.dtype"]:147        """148        returns dtypes for modules that are not quantized - used for the computation of the device_map in case149        one passes a str as a device_map. The method will use the `modules_to_not_convert` that is modified150        in `_process_model_before_weight_loading`.151 152        Args:153            model (`~transformers.PreTrainedModel`):154                The model to quantize155            dtype (`torch.dtype`):156                The dtype passed in `from_pretrained` method.157        """158 159        return {160            name: dtype for name, _ in model.named_parameters() if any(m in name for m in self.modules_to_not_convert)161        }162 163    def adjust_max_memory(self, max_memory: dict[str, Union[int, str]]) -> dict[str, Union[int, str]]:164        """adjust max_memory argument for infer_auto_device_map() if extra memory is needed for quantization"""165        return max_memory166 167    def check_quantized_param(self, *args, **kwargs) -> bool:168        """DEPRECATED -> remove in v5"""169        logger.warning_once(170            "`check_quantized_param` is deprecated in favor of `param_needs_quantization`, which is a much "171            "more self.explanatory name for what the method achieves. It will be removed in v5"172        )173        return self.param_needs_quantization(*args, **kwargs)174 175    def param_needs_quantization(self, model: "PreTrainedModel", param_name: str, **kwargs) -> bool:176        """177        Check whether a given param needs quantization as defined by `create_quantized_param`.178        """179        return False180 181    def create_quantized_param(self, *args, **kwargs):182        """183        Take needed components from state_dict (those from which `param_needs_quantization` is True) and create184        quantized param.185        It usually also load the new param directly in the `model`.186        Note: only applicable if requires_parameters_quantization == True.187        """188        if not self.requires_parameters_quantization:189            raise AttributeError(190                f"`.create_quantized_param()` method is not supported by quantizer class {self.__class__.__name__}."191            )192 193    def validate_environment(self, *args, **kwargs):194        """195        This method is used to potentially check for potential conflicts with arguments that are196        passed in `from_pretrained`. You need to define it for all future quantizers that are integrated with transformers.197        If no explicit check are needed, simply return nothing.198        """199        return200 201    def update_tp_plan(self, config):202        "updates the tp plan for the scales"203        return config204 205    def update_ep_plan(self, config):206        "updates the tp plan for the scales"207        return config208 209    def preprocess_model(self, model: "PreTrainedModel", **kwargs):210        """211        Setting model attributes and/or converting model before weights loading. At this point212        the model should be initialized on the meta device so you can freely manipulate the skeleton213        of the model in order to replace modules in-place. Make sure to override the abstract method `_process_model_before_weight_loading`.214 215        Args:216            model (`~transformers.PreTrainedModel`):217                The model to quantize218            kwargs (`dict`, *optional*):219                The keyword arguments that are passed along `_process_model_before_weight_loading`.220        """221        model.is_quantized = True222        model.quantization_method = self.quantization_config.quant_method223        if self.pre_quantized:224            self._convert_model_for_quantization(model)225        return self._process_model_before_weight_loading(model, **kwargs)226 227    def postprocess_model(self, model: "PreTrainedModel", **kwargs):228        """229        Post-process the model post weights loading.230        Make sure to override the abstract method `_process_model_after_weight_loading`.231 232        Args:233            model (`~transformers.PreTrainedModel`):234                The model to quantize235            kwargs (`dict`, *optional*):236                The keyword arguments that are passed along `_process_model_after_weight_loading`.237        """238        return self._process_model_after_weight_loading(model, **kwargs)239 240    def remove_quantization_config(self, model):241        """242        Remove the quantization config from the model.243        """244        if hasattr(model, "hf_quantizer"):245            del model.hf_quantizer246        if hasattr(model.config, "quantization_config"):247            del model.config.quantization_config248        if hasattr(model.config, "_pre_quantization_dtype"):249            del model.config._pre_quantization_dtype250        if hasattr(model, "quantization_method"):251            del model.quantization_method252        model.is_quantized = False253 254    def dequantize(self, model):255        """256        Potentially dequantize the model to retrieve the original model, with some loss in accuracy / performance.257        Note not all quantization schemes support this.258        """259        model = self._dequantize(model)260 261        # Delete quantizer and quantization config262        del model.hf_quantizer263        del model.config.quantization_config264        del model.config._pre_quantization_dtype265        del model.quantization_method266        model.is_quantized = False267 268        return model269 270    def get_accelerator_warm_up_factor(self):271        """272        The factor to be used in `caching_allocator_warmup` to get the number of bytes to pre-allocate to warm up accelerator.273        A factor of 2 means we allocate all bytes in the empty model (since we allocate in fp16), a factor of 4 means274        we allocate half the memory of the weights residing in the empty model, etc...275        """276        # By default we return 4, i.e. half the model size (this corresponds to the case where the model is not277        # really pre-processed, i.e. we do not have the info that weights are going to be 8 bits before actual278        # weight loading)279        return 4280 281    def _dequantize(self, model):282        raise NotImplementedError(283            f"{self.quantization_config.quant_method} has no implementation of `dequantize`, please raise an issue on GitHub."284        )285 286    def get_param_name(self, param_name: str) -> str:287        """288        Override this method if you want to adjust the `param_name`.289        """290        return param_name291 292    @staticmethod293    def get_modules_to_not_convert(294        model: "PreTrainedModel",295        skip_modules: Optional[list[str]] = None,296        keep_in_fp32_modules: Optional[list[str]] = None,297        add_default_skips: bool = False,298    ):299        from ..integrations import get_keys_to_not_convert300 301        if skip_modules is None or add_default_skips:302            modules_to_not_convert = get_keys_to_not_convert(model)303        else:304            modules_to_not_convert = []305 306        if skip_modules is not None:307            modules_to_not_convert.extend(skip_modules)308 309        if keep_in_fp32_modules is not None:310            modules_to_not_convert.extend(keep_in_fp32_modules)311 312        return modules_to_not_convert313 314    @property315    def is_qat_trainable(self) -> bool:316        """Flag indicating whether the quantized model can carry out quantization aware training"""317        return False318 319    @property320    def is_compileable(self) -> bool:321        """Flag indicating whether the quantized model can be compiled"""322        return False323 324    def get_state_dict_and_metadata(self, model, safe_serialization=False):325        """Get state dict and metadata. Useful when we need to modify a bit the state dict due to quantization"""326        return None, {}327 328    def update_state_dict_with_metadata(self, state_dict, metadata):329        """Update state dict with metadata. Default behaviour returns state_dict"""330        return state_dict331 332    @abstractmethod333    def _process_model_before_weight_loading(self, model, **kwargs): ...334 335    @abstractmethod336    def _process_model_after_weight_loading(self, model, **kwargs): ...337 338    @abstractmethod339    def is_serializable(self, safe_serialization=None): ...340 341    @property342    @abstractmethod343    def is_trainable(self): ...344 345    def _convert_model_for_quantization(self, model):346        from accelerate import init_empty_weights347 348        for name, module in model.named_modules():349            module_class_name = module.__class__.__name__350            if module_class_name in MODULES_TO_PATCH_FOR_QUANTIZATION and (351                self.quantization_config.quant_method352                in MODULES_TO_PATCH_FOR_QUANTIZATION[module_class_name]["quantization_methods"]353            ):354                with init_empty_weights():355                    parent_module, name = get_module_from_name(model, name)356                    parent_module._modules[name] = MODULES_TO_PATCH_FOR_QUANTIZATION[module_class_name]["module_name"](357                        model.config.get_text_config()358                    )359 360 361class SequentialLlama4TextExperts(ModuleList):362    """363    A module that implements a compressed version of a list of expert modules.364    This is specifically designed to work with Llama4TextExperts in MoE layers.365    """366 367    def __init__(self, config):368        from transformers.models.llama4.modeling_llama4 import Llama4TextMLP369 370        super().__init__([Llama4TextMLP(config) for _ in range(config.num_local_experts)])371        self.num_experts = config.num_local_experts372 373    def forward(374        self,375        hidden_states: "torch.Tensor",376    ) -> "torch.Tensor":377        hidden_states = hidden_states.reshape(self.num_experts, -1, hidden_states.shape[-1])378        routed_out = torch.zeros_like(hidden_states)379        for expert_idx in range(self.num_experts):380            routed_out[expert_idx] = self[expert_idx](hidden_states[expert_idx])381        return routed_out382 383 384MODULES_TO_PATCH_FOR_QUANTIZATION = {385    "Llama4TextExperts": {386        "module_name": SequentialLlama4TextExperts,387        "quantization_methods": [388            QuantizationMethod.COMPRESSED_TENSORS,389            QuantizationMethod.BITS_AND_BYTES,390        ],391    }392}393 
Aluode/PerceptionLabPortable · CoolFace