CoolFace
Modelpublic

nvidia/C-RADIOv4-1D-H

sourceHugging Faceotherupdated 2mo agoView on Hugging Face
8likes285downloads
enable_spectral_reparam.py278 linesDownload Raw Back to root
1# Copyright (c) 2023-2024, NVIDIA CORPORATION.  All rights reserved.2#3# NVIDIA CORPORATION and its licensors retain all intellectual property4# and proprietary rights in and to this software, related documentation5# and any modifications thereto.  Any use, reproduction, disclosure or6# distribution of this software and related documentation without an express7# license agreement from NVIDIA CORPORATION is strictly prohibited.8 9from logging import getLogger10import math11import os12from typing import Dict, List, Optional, Union, Tuple13from types import MethodType14 15import torch16from torch import nn17from torch.nn import functional as F18from torch.nn.utils import parametrize19from torch.nn.utils.parametrizations import _SpectralNorm20 21from timm.models.vision_transformer import Attention, Mlp22 23_EPS = 1e-524 25 26class _SNReweight(_SpectralNorm):27    def __init__(self, weight: torch.Tensor, *args, init_norm_to_current: bool = False, alpha: float = 0.05, version: int = 2, **kwargs):28        super().__init__(weight, *args, **kwargs)29 30        self.alpha = alpha31        self.version = version32        self.register_buffer('_sn_version', torch.tensor(version))33 34        if init_norm_to_current:35            # This will set the numerator to match the denominator, which should preserve the original values36            init_scale = self._get_sigma(weight, n_power_iterations=20).item()37        else:38            init_scale = 1.039 40        if version == 1:41            init_value = init_scale42        elif version == 2:43            t = init_scale - alpha44            if t < _EPS:45                getLogger("spectral_reparam").warn(f'The initialized spectral norm {init_scale} is too small to be represented. Setting to {_EPS} instead.')46                t = _EPS47 48            init_value = math.log(math.exp(t) - 1)49        else:50            raise ValueError(f'Unsupported version: {version}')51 52        # Make 2D so that weight decay gets applied53        self.scale = nn.Parameter(torch.tensor([[init_value]], dtype=torch.float32, device=weight.device))54 55    # Re-implementing this because we need to make division by sigma safe56    def _get_sigma(self, weight: torch.Tensor, n_power_iterations: int = None) -> torch.Tensor:57        if not n_power_iterations:58            n_power_iterations = self.n_power_iterations59        if weight.ndim == 1:60            # Faster and more exact path, no need to approximate anything61            sigma = weight.norm()62        else:63            weight_mat = self._reshape_weight_to_matrix(weight)64            if self.training:65                self._power_method(weight_mat, n_power_iterations)66            # See above on why we need to clone67            u = self._u.clone(memory_format=torch.contiguous_format)68            v = self._v.clone(memory_format=torch.contiguous_format)69            # The proper way of computing this should be through F.bilinear, but70            # it seems to have some efficiency issues:71            # https://github.com/pytorch/pytorch/issues/5809372            sigma = torch.dot(u, torch.mv(weight_mat, v))73 74        return sigma + self.eps75 76    def forward(self, weight: torch.Tensor, *args, **kwargs):77        dtype = weight.dtype78        sigma = self._get_sigma(weight, *args, **kwargs)79 80        if self.version == 1:81            scale = self.scale82        elif self.version == 2:83            scale = F.softplus(self.scale) + self.alpha84        else:85            raise ValueError(f'Unsupported version: {self.version}')86 87        scale = scale.float() / sigma.float()88 89        y = weight * scale90 91        if dtype in (torch.float16, torch.bfloat16):92            y = y.to(dtype)93        return y94 95    def _load_from_state_dict(self, state_dict, prefix, local_metadata, strict, missing_keys, unexpected_keys, error_msgs):96        version_key = f'{prefix}_sn_version'97        if version_key not in state_dict:98            self.version = 199            state_dict[version_key] = torch.tensor(1)100        return super()._load_from_state_dict(state_dict, prefix, local_metadata, strict, missing_keys, unexpected_keys, error_msgs)101 102 103class _ChunkedSNReweight(nn.Module):104    def __init__(self, weight: torch.Tensor, num_chunks: int, *args, init_norm_to_current: bool = False, **kwargs):105        super().__init__()106 107        self.num_chunks = num_chunks108        parts = weight.split(weight.shape[0] // num_chunks, dim=0)109 110        self.parts = nn.ModuleList([111            _SNReweight(p, *args, init_norm_to_current=init_norm_to_current, **kwargs)112            for p in parts113        ])114 115    def forward(self, weight: torch.Tensor, *args, **kwargs):116        parts = weight.split(weight.shape[0] // self.num_chunks, dim=0)117 118        parts = [119            fn(p)120            for fn, p in zip(self.parts, parts)121        ]122 123        return torch.cat(parts, dim=0)124 125 126class _AttnSNReweight(_ChunkedSNReweight):127    def __init__(self, weight: torch.Tensor, *args, init_norm_to_current: bool = False, renorm_values: bool = False, **kwargs):128        super().__init__(weight, 3, *args, init_norm_to_current=init_norm_to_current, **kwargs)129 130        if not renorm_values:131            self.parts[2] = nn.Identity()132 133 134def enable_spectral_reparam(model: Union[nn.Module, List[nn.Module]],135                            n_power_iterations: int = 1,136                            eps: float = 1e-6,137                            init_norm_to_current: bool = False,138                            renorm_values: bool = True,139                            renorm_mlp: bool = True,140                            state_dict_guidance: Optional[Dict[str, torch.Tensor]] = None):141    if isinstance(model, (list, tuple)):142        for i, sub in enumerate(model):143            sub_sd = state_dict_guidance[i] if isinstance(state_dict_guidance, (list, tuple)) else state_dict_guidance144            enable_spectral_reparam(sub, n_power_iterations=n_power_iterations, eps=eps,145                                    init_norm_to_current=init_norm_to_current, renorm_values=renorm_values,146                                    renorm_mlp=renorm_mlp, state_dict_guidance=sub_sd)147        return148 149    print('Enabling spectral reparametrization')150    args = dict(n_power_iterations=n_power_iterations, dim=0, eps=eps, init_norm_to_current=init_norm_to_current)151    visited_prefixes = set()152 153    def is_guidance_parametrized(name: str):154        if state_dict_guidance is None:155            return True156 157        p_name = f'{name}.parametrizations'158        is_prm = any(k for k in state_dict_guidance if k.startswith(p_name) and k.endswith('_sn_version'))159        return is_prm160 161    def parametrize_linear(linear: nn.Linear):162        parametrize.register_parametrization(163            linear,164            'weight',165            _SNReweight(linear.weight, **args)166        )167 168    for name, mod in model.named_modules():169        pref = '.'.join(name.split('.')[:-1])170        if pref in visited_prefixes:171            continue172 173        if isinstance(mod, Attention) or name.endswith('.attn'):174            if is_guidance_parametrized(f'{name}.qkv'):175                parametrize.register_parametrization(176                    mod.qkv,177                    'weight',178                    _AttnSNReweight(mod.qkv.weight, renorm_values=renorm_values, **args),179                )180            if hasattr(mod, 'proj') and is_guidance_parametrized(f'{name}.proj'):181                parametrize_linear(mod.proj)182            visited_prefixes.add(name)183        elif name.endswith('mlp') and renorm_mlp and hasattr(mod, 'w12'):184            if is_guidance_parametrized(f'{name}.w12'):185                parametrize.register_parametrization(186                    mod.w12,187                    'weight',188                    _ChunkedSNReweight(mod.w12.weight, num_chunks=2, **args),189                )190            if is_guidance_parametrized(f'{name}.w3'):191                parametrize_linear(mod.w3)192            visited_prefixes.add(name)193        elif isinstance(mod, nn.Linear) and 'patch_generator' not in name and is_guidance_parametrized(name):194            parametrize_linear(mod)195 196 197def configure_spectral_reparam_from_args(model: nn.Module, args, state_dict_guidance: Optional[Dict[str, torch.Tensor]] = None):198    spectral_reparam = getattr(args, 'spectral_reparam', False)199    if isinstance(spectral_reparam, bool) and spectral_reparam:200        enable_spectral_reparam(model, init_norm_to_current=True, state_dict_guidance=state_dict_guidance)201    elif isinstance(spectral_reparam, dict):202        enable_spectral_reparam(203            model,204            n_power_iterations=spectral_reparam.get('n_power_iterations', 1),205            eps=spectral_reparam.get('eps', 1e-12),206            init_norm_to_current=True,207            state_dict_guidance=state_dict_guidance,208        )209 210 211def disable_spectral_reparam(model: nn.Module):212    print('Disabling spectral reparametrization')213    for name, mod in model.named_modules():214        if parametrize.is_parametrized(mod):215            parametrize.remove_parametrizations(mod, 'weight')216            pass217 218 219 220if __name__ == '__main__':221    import argparse222    from . import radio_model as create_model223 224    parser = argparse.ArgumentParser(description='Remove parametrization from state dict')225    parser.add_argument('--checkpoint', type=str, required=True, help='The checkpoint to load')226    parser.add_argument('--output', type=str, default='', help='Where to store the checkpoint')227    parser.add_argument('--release', default=False, action='store_true', help='Prune extraneous checkpoint fields')228    parser.add_argument('--strict', default=False, action='store_true', help='Strictly load the state dict')229 230    args = parser.parse_args()231 232    if not args.output:233        chk_dir, chk_name = os.path.split(args.checkpoint)234        args.output = os.path.join(chk_dir, f'clean_{chk_name}')235        print(f'Set output to "{args.output}"')236 237    chk = torch.load(args.checkpoint, map_location='cpu', mmap=True)238 239    model = create_model.create_model_from_args(chk['args'])240 241    key = 'base_model.'242    mod_state = dict()243    extra_state = dict()244    for k, v in chk['state_dict'].items():245        if k.startswith(key):246            mod_state[k[len(key):]] = v247        else:248            extra_state[k] = v249 250    chk_load_info = model.load_state_dict(mod_state, strict=args.strict)251    if chk_load_info.unexpected_keys or chk_load_info.missing_keys:252        print(chk_load_info)253 254    if chk['args'].spectral_reparam:255        disable_spectral_reparam(model)256 257    if hasattr(chk['args'], 'dtype'):258        model.to(dtype=chk['args'].dtype)259 260    mod_state = model.state_dict()261    final_state = dict()262    final_state.update({f'{key}{k}': v for k, v in mod_state.items()})263    final_state.update(extra_state)264 265    chk['state_dict'] = final_state266    chk['args'].spectral_reparam = False267 268    if args.release:269        chk = {270            'arch': chk['arch'],271            'epoch': chk['epoch'],272            'state_dict': chk['state_dict'],273            'args': chk['args'],274        }275 276    torch.save(chk, args.output)277    pass278