CoolFace
Apppublic

Aluode/PerceptionLabPortable

sourceHugging Faceupdated 9mo agoView on Hugging Face
0likes
quantizer_hqq.py277 linesDownload Raw Back to quantizers
1# Copyright 2024 The HuggingFace 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.14 15from collections import defaultdict16from typing import TYPE_CHECKING17 18from ..integrations import prepare_for_hqq_linear19from ..utils import is_hqq_available, is_torch_available, logging20from .base import HfQuantizer21from .quantizers_utils import get_module_from_name22 23 24if TYPE_CHECKING:25    from ..modeling_utils import PreTrainedModel26 27 28if is_torch_available():29    import torch30 31if is_hqq_available():32    from hqq.core.quantize import HQQLinear33 34    # This is a compatibility hack. HQQ-quantized linear layers do not have a `weight` attribute,35    # but some models attempt to access `weight.dtype` during the forward pass. To prevent runtime errors,36    # we patch HQQLinear with a dummy `weight` property that returns an empty tensor with the correct dtype and device.37    @property38    def weight(self):39        return torch.empty(0, dtype=self.compute_dtype, device=self.device)40 41    HQQLinear.weight = weight42 43logger = logging.get_logger(__name__)44 45 46class HqqHfQuantizer(HfQuantizer):47    """48    HQQ quantizer base HF class.49    nn.Linear modules are first tagged with quant_config in _process_model_before_weight_loading().50    """51 52    use_keep_in_fp32_modules = False53    requires_parameters_quantization = True54    requires_calibration = False55    required_packages = ["hqq"]56 57    def __init__(self, quantization_config, **kwargs):58        if not is_hqq_available():59            raise ImportError(60                "A valid HQQ version (>=0.2.1) is not available. Please follow the instructions to install it: `https://github.com/mobiusml/hqq/`."61            )62        super().__init__(quantization_config, **kwargs)63        self.dtype = None64        self.using_multi_gpu = False65        # Keys that are serialized specifically by hqq66        self.hqq_keys = HQQLinear(None, None).state_dict_keys() - {"bias"}67 68        if kwargs.get("from_tf", False) or kwargs.get("from_flax", False):69            raise ValueError(70                "Converting weights from tf/flax weights is currently not supported, please make"71                " sure the weights are in PyTorch format."72            )73 74        if self.dtype is None:75            if "dtype" in kwargs:76                self.dtype = kwargs["dtype"]77            else:78                self.dtype = torch.float3279                logger.info("Setting dtype to torch.float32 as the default value since it was not specified.")80 81        device_map = kwargs.get("device_map")82        if isinstance(device_map, dict):83            if "cpu" in device_map.values() or "disk" in device_map.values():84                raise ValueError(85                    "You are attempting to use an HQQ model with a device_map that contains a CPU or disk device."86                    " This is not supported. Please remove the CPU or disk device from the device_map."87                )88            else:89                self.using_multi_gpu = len(set(device_map.values())) > 190 91    def update_missing_keys(92        self, model: "PreTrainedModel", missing_keys: list[str], prefix: str, **kwargs93    ) -> list[str]:94        if self.pre_quantized:95            return [key for key in missing_keys if ("weight" not in key)]96        else:97            return missing_keys98 99    # Adds missing keys for HQQLinear modules that are loaded but the model with initialized with torch.nn.Linear100    def update_expected_keys(101        self, model: "PreTrainedModel", expected_keys: list[str], loaded_keys: list[str]102    ) -> list[str]:103        if not self.pre_quantized:104            return expected_keys105 106        # Collects all quantizable (linear) layers107        def _find_hqq_quantizable_layers(model, layers):108            for name, module in model.named_children():109                if isinstance(module, (torch.nn.Linear)):110                    layers.add(module.name)111                _find_hqq_quantizable_layers(module, layers)112 113        new_keys = set(expected_keys)114 115        # Name modules116        for name, module in model.named_modules():117            module.name = name118 119        # valid modules are Linear layers that have HQQLinear state_dict. We ignore skip_modules and any layers with Linear state_dict() params120        _valid_modules = set()121        _find_hqq_quantizable_layers(model, _valid_modules)122 123        # Remove skipped modules124        _skipped_modules = set()125        for _module in _valid_modules:126            for _skip_module in model.config.quantization_config["skip_modules"]:127                if _skip_module in _module:128                    _skipped_modules.add(_module)129        _valid_modules -= _skipped_modules130 131        # Append new expected layers based on _ref_keys132        _ref_keys = HQQLinear(133            linear_layer=None,134            quant_config=None,135            compute_dtype=torch.float16,136            device="cpu",137            del_orig=False,138        ).state_dict_keys() - {"bias"}139 140        # Clean-up141        _rm_keys = set()142        for key in new_keys:143            if any(_module in key for _module in _valid_modules):144                _rm_keys.add(key)145        new_keys -= _rm_keys146        # At this point, new_keys contains all the keys of the layers that are NOT HQQLinear or torch.nn.Linear147 148        # Re-populate Linear/HQQLinear149        for _module in _valid_modules:150            if _module + ".weight" in loaded_keys:151                new_keys.add(_module + ".weight")152            else:153                new_keys.update({_module + "." + _ref_key for _ref_key in _ref_keys})154            if _module + ".bias" in loaded_keys:155                new_keys.add(_module + ".bias")156 157        return list(new_keys)158 159    def param_needs_quantization(self, model: "PreTrainedModel", param_name: str, **kwargs) -> bool:160        module, _ = get_module_from_name(model, param_name)161        # Since we do not prepare the modules in advance, we need every param of the Linear layer to go through162        # `create_quantized_param`, even when `self.is_quantized == True`163        return isinstance(module, torch.nn.Linear)164 165    def create_quantized_param(166        self,167        model: "PreTrainedModel",168        param_value: "torch.Tensor",169        param_name: str,170        target_device: "torch.device",171        **kwargs,172    ):173        module, tensor_name = get_module_from_name(model, param_name)174        module_name = param_name.rsplit(".", 1)[0]175        parent_module, node = get_module_from_name(model, module_name)176 177        quant_config = model.config.quantization_config["quant_config"]178        skip_modules = model.config.quantization_config["skip_modules"]179 180        # In this case we do not quantize this layer (it's explicitly skipped) -> simply load param181        if any(skip_module in module.name for skip_module in skip_modules):182            module.load_state_dict(183                {tensor_name: param_value.to(device=target_device, dtype=self.dtype)}, strict=False, assign=True184            )185            return186 187        # We need this hack as the model is not pre-prepared as an empty skeleton on meta device188        if self.pre_quantized:189            # Save them for later190            if not hasattr(self, "hqq_params"):191                self.hqq_params = defaultdict(dict)192            self.hqq_params[module_name].update({tensor_name: param_value})193            hqq_params = self.hqq_params[module_name]194 195            # If they are all present and saved, make it a HQQLinear layer! (we cannot do it param after param because196            # hqq does not support it...)197            if all(k in hqq_params for k in self.hqq_keys) and ("bias" in hqq_params or module.bias is None):198                hqq_layer = HQQLinear(199                    linear_layer=None,200                    quant_config=None,201                    compute_dtype=self.dtype,202                    device=target_device,203                    del_orig=False,204                )205                hqq_layer.load_state_dict(hqq_params)206 207                if hqq_layer.bias is not None and isinstance(hqq_layer.bias, torch.Tensor):208                    hqq_layer.bias = torch.nn.Parameter(hqq_layer.bias)209                if self.using_multi_gpu:210                    hqq_layer = self._patch_layer_for_multigpu(hqq_layer)211 212                setattr(parent_module, node, hqq_layer)213                del self.hqq_params[module_name], module214            return215 216        # Load param in the module (without caring about device or dtype, it will be changed later)217        module.load_state_dict({tensor_name: param_value}, strict=False, assign=True)218 219        # If both the weight and bias have already been loaded, time to quantize!220        module_is_ready = module.weight.device.type != "meta" and (221            module.bias is None or module.bias.device.type != "meta"222        )223 224        if module_is_ready:225            module_tag = ".".join(module.name.split(".")[-2:])226            if "weight_quant_params" in quant_config:227                module_quant_config = quant_config228            elif module_tag in quant_config:229                module_quant_config = quant_config[module_tag]230 231            hqq_layer = HQQLinear(232                module,233                quant_config=module_quant_config,234                compute_dtype=self.dtype,235                device=target_device,236                del_orig=True,237            )238 239            if hqq_layer.bias is not None and isinstance(hqq_layer.bias, torch.Tensor):240                hqq_layer.bias = torch.nn.Parameter(hqq_layer.bias)241 242            if self.using_multi_gpu:243                hqq_layer = self._patch_layer_for_multigpu(hqq_layer)244 245            setattr(parent_module, node, hqq_layer)246 247    def _patch_layer_for_multigpu(self, hqq_layer):248        def forward_with_device(self, x):249            out = torch.matmul(x.to(self.device), self.dequantize().t())250            if self.bias is not None:251                out += self.bias252            return out253 254        hqq_layer.forward = lambda x: forward_with_device(hqq_layer, x)255        return hqq_layer256 257    def _process_model_before_weight_loading(258        self,259        model: "PreTrainedModel",260        **kwargs,261    ):262        # Add the corresponding quant_config to each valid module. This allows us to do the actual nn.Linear -> HQQLinear conversion in create_quantized_param().263        # prepare_for_hqq_linear() also sets the right quantization config inside the model (model.config.quantization_config) and the layers (hqq_layer.quant_config)264        model = prepare_for_hqq_linear(model, quantization_config=self.quantization_config)265 266    def _process_model_after_weight_loading(self, model: "PreTrainedModel", **kwargs):267        model.is_hqq_quantized = True268        model.is_hqq_serializable = self.is_serializable()269        return model270 271    def is_serializable(self, safe_serialization=None):272        return True273 274    @property275    def is_trainable(self) -> bool:276        return True277 
Aluode/PerceptionLabPortable · CoolFace