CoolFace
Apppublic

breadlicker45/gpuGAN

sourceHugging Facecc-by-nc-4.0updated 8mo agoView on Hugging Face
0likes
persistence.py252 linesDownload Raw Back to torch_utils
1# Copyright (c) 2021, 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 9"""Facilities for pickling Python code alongside other data.10 11The pickled code is automatically imported into a separate Python module12during unpickling. This way, any previously exported pickles will remain13usable even if the original code is no longer available, or if the current14version of the code is not consistent with what was originally pickled."""15 16import sys17import pickle18import io19import inspect20import copy21import uuid22import types23import dnnlib24 25#----------------------------------------------------------------------------26 27_version            = 6         # internal version number28_decorators         = set()     # {decorator_class, ...}29_import_hooks       = []        # [hook_function, ...]30_module_to_src_dict = dict()    # {module: src, ...}31_src_to_module_dict = dict()    # {src: module, ...}32 33#----------------------------------------------------------------------------34 35def persistent_class(orig_class):36    r"""Class decorator that extends a given class to save its source code37    when pickled.38 39    Example:40 41        from torch_utils import persistence42 43        @persistence.persistent_class44        class MyNetwork(torch.nn.Module):45            def __init__(self, num_inputs, num_outputs):46                super().__init__()47                self.fc = MyLayer(num_inputs, num_outputs)48                ...49 50        @persistence.persistent_class51        class MyLayer(torch.nn.Module):52            ...53 54    When pickled, any instance of `MyNetwork` and `MyLayer` will save its55    source code alongside other internal state (e.g., parameters, buffers,56    and submodules). This way, any previously exported pickle will remain57    usable even if the class definitions have been modified or are no58    longer available.59 60    The decorator saves the source code of the entire Python module61    containing the decorated class. It does *not* save the source code of62    any imported modules. Thus, the imported modules must be available63    during unpickling, also including `torch_utils.persistence` itself.64 65    It is ok to call functions defined in the same module from the66    decorated class. However, if the decorated class depends on other67    classes defined in the same module, they must be decorated as well.68    This is illustrated in the above example in the case of `MyLayer`.69 70    It is also possible to employ the decorator just-in-time before71    calling the constructor. For example:72 73        cls = MyLayer74        if want_to_make_it_persistent:75            cls = persistence.persistent_class(cls)76        layer = cls(num_inputs, num_outputs)77 78    As an additional feature, the decorator also keeps track of the79    arguments that were used to construct each instance of the decorated80    class. The arguments can be queried via `obj.init_args` and81    `obj.init_kwargs`, and they are automatically pickled alongside other82    object state. A typical use case is to first unpickle a previous83    instance of a persistent class, and then upgrade it to use the latest84    version of the source code:85 86        with open('old_pickle.pkl', 'rb') as f:87            old_net = pickle.load(f)88        new_net = MyNetwork(*old_obj.init_args, **old_obj.init_kwargs)89        misc.copy_params_and_buffers(old_net, new_net, require_all=True)90    """91    assert isinstance(orig_class, type)92    if is_persistent(orig_class):93        return orig_class94 95    assert orig_class.__module__ in sys.modules96    orig_module = sys.modules[orig_class.__module__]97    orig_module_src = _module_to_src(orig_module)98 99    class Decorator(orig_class):100        _orig_module_src = orig_module_src101        _orig_class_name = orig_class.__name__102 103        def __init__(self, *args, **kwargs):104            super().__init__(*args, **kwargs)105            self._init_args = copy.deepcopy(args)106            self._init_kwargs = copy.deepcopy(kwargs)107            assert orig_class.__name__ in orig_module.__dict__108            _check_pickleable(self.__reduce__())109 110        @property111        def init_args(self):112            return copy.deepcopy(self._init_args)113 114        @property115        def init_kwargs(self):116            return dnnlib.EasyDict(copy.deepcopy(self._init_kwargs))117 118        def __reduce__(self):119            fields = list(super().__reduce__())120            fields += [None] * max(3 - len(fields), 0)121            if fields[0] is not _reconstruct_persistent_obj:122                meta = dict(type='class', version=_version, module_src=self._orig_module_src, class_name=self._orig_class_name, state=fields[2])123                fields[0] = _reconstruct_persistent_obj # reconstruct func124                fields[1] = (meta,) # reconstruct args125                fields[2] = None # state dict126            return tuple(fields)127 128    Decorator.__name__ = orig_class.__name__129    _decorators.add(Decorator)130    return Decorator131 132#----------------------------------------------------------------------------133 134def is_persistent(obj):135    r"""Test whether the given object or class is persistent, i.e.,136    whether it will save its source code when pickled.137    """138    try:139        if obj in _decorators:140            return True141    except TypeError:142        pass143    return type(obj) in _decorators # pylint: disable=unidiomatic-typecheck144 145#----------------------------------------------------------------------------146 147def import_hook(hook):148    r"""Register an import hook that is called whenever a persistent object149    is being unpickled. A typical use case is to patch the pickled source150    code to avoid errors and inconsistencies when the API of some imported151    module has changed.152 153    The hook should have the following signature:154 155        hook(meta) -> modified meta156 157    `meta` is an instance of `dnnlib.EasyDict` with the following fields:158 159        type:       Type of the persistent object, e.g. `'class'`.160        version:    Internal version number of `torch_utils.persistence`.161        module_src  Original source code of the Python module.162        class_name: Class name in the original Python module.163        state:      Internal state of the object.164 165    Example:166 167        @persistence.import_hook168        def wreck_my_network(meta):169            if meta.class_name == 'MyNetwork':170                print('MyNetwork is being imported. I will wreck it!')171                meta.module_src = meta.module_src.replace("True", "False")172            return meta173    """174    assert callable(hook)175    _import_hooks.append(hook)176 177#----------------------------------------------------------------------------178 179def _reconstruct_persistent_obj(meta):180    r"""Hook that is called internally by the `pickle` module to unpickle181    a persistent object.182    """183    meta = dnnlib.EasyDict(meta)184    meta.state = dnnlib.EasyDict(meta.state)185    for hook in _import_hooks:186        meta = hook(meta)187        assert meta is not None188 189    assert meta.version == _version190    module = _src_to_module(meta.module_src)191 192    assert meta.type == 'class'193    orig_class = module.__dict__[meta.class_name]194    decorator_class = persistent_class(orig_class)195    obj = decorator_class.__new__(decorator_class)196 197    setstate = getattr(obj, '__setstate__', None)198    if callable(setstate):199        setstate(meta.state) # pylint: disable=not-callable200    else:201        obj.__dict__.update(meta.state)202    return obj203 204#----------------------------------------------------------------------------205 206def _module_to_src(module):207    r"""Query the source code of a given Python module.208    """209    src = _module_to_src_dict.get(module, None)210    if src is None:211        src = inspect.getsource(module)212        _module_to_src_dict[module] = src213        _src_to_module_dict[src] = module214    return src215 216def _src_to_module(src):217    r"""Get or create a Python module for the given source code.218    """219    module = _src_to_module_dict.get(src, None)220    if module is None:221        module_name = "_imported_module_" + uuid.uuid4().hex222        module = types.ModuleType(module_name)223        sys.modules[module_name] = module224        _module_to_src_dict[module] = src225        _src_to_module_dict[src] = module226        exec(src, module.__dict__) # pylint: disable=exec-used227    return module228 229#----------------------------------------------------------------------------230 231def _check_pickleable(obj):232    r"""Check that the given object is pickleable, raising an exception if233    it is not. This function is expected to be considerably more efficient234    than actually pickling the object.235    """236    def recurse(obj):237        if isinstance(obj, (list, tuple, set)):238            return [recurse(x) for x in obj]239        if isinstance(obj, dict):240            return [[recurse(x), recurse(y)] for x, y in obj.items()]241        if isinstance(obj, (str, int, float, bool, bytes, bytearray)):242            return None # Python primitive types are pickleable.243        if f'{type(obj).__module__}.{type(obj).__name__}' in ['numpy.ndarray', 'torch.Tensor']:244            return None # NumPy arrays and PyTorch tensors are pickleable.245        if is_persistent(obj):246            return None # Persistent objects are pickleable, by virtue of the constructor check.247        return obj248    with io.BytesIO() as f:249        pickle.dump(recurse(obj), f)250 251#----------------------------------------------------------------------------252