| Meta device |
| ============ |
| |
| The "meta" device is an abstract device which denotes a tensor which records |
| only metadata, but no actual data. Meta tensors have two primary use cases: |
| |
| * Models can be loaded on the meta device, allowing you to load a |
| representation of the model without actually loading the actual parameters |
| into memory. This can be helpful if you need to make transformations on |
| the model before you load the actual data. |
| |
| * Most operations can be performed on meta tensors, producing new meta |
| tensors that describe what the result would have been if you performed |
| the operation on a real tensor. You can use this to perform abstract |
| analysis without needing to spend time on compute or space to represent |
| the actual tensors. Because meta tensors do not have real data, you cannot |
| perform data-dependent operations like :func:`torch.nonzero` or |
| :meth:`~torch.Tensor.item`. In some cases, not all device types (e.g., CPU |
| and CUDA) have exactly the same output metadata for an operation; we |
| typically prefer representing the CUDA behavior faithfully in this |
| situation. |
| |
| .. warning:: |
| |
| Although in principle meta tensor computation should always be faster than |
| an equivalent CPU/CUDA computation, many meta tensor implementations are |
| implemented in Python and have not been ported to C++ for speed, so you |
| may find that you get lower absolute framework latency with small CPU tensors. |
| |
| Idioms for working with meta tensors |
| ------------------------------------ |
| |
| An object can be loaded with :func:`torch.load` onto meta device by specifying |
| ``map_location='meta'``:: |
| |
| >>> torch.save(torch.randn(2), 'foo.pt') |
| >>> torch.load('foo.pt', map_location='meta') |
| tensor(..., device='meta', size=(2,)) |
| |
| If you have some arbitrary code which performs some tensor construction without |
| explicitly specifying a device, you can override it to instead construct on meta device by using |
| the :func:`torch.device` context manager:: |
| |
| >>> with torch.device('meta'): |
| ... print(torch.randn(30, 30)) |
| ... |
| tensor(..., device='meta', size=(30, 30)) |
| |
| This is especially helpful NN module construction, where you often are not |
| able to explicitly pass in a device for initialization:: |
| |
| >>> from torch.nn.modules import Linear |
| >>> with torch.device('meta'): |
| ... print(Linear(20, 30)) |
| ... |
| Linear(in_features=20, out_features=30, bias=True) |
| |
| You cannot convert a meta tensor directly to a CPU/CUDA tensor, because the |
| meta tensor stores no data and we do not know what the correct data values for |
| your new tensor are:: |
| |
| >>> torch.ones(5, device='meta').to("cpu") |
| Traceback (most recent call last): |
| File "<stdin>", line 1, in <module> |
| NotImplementedError: Cannot copy out of meta tensor; no data! |
| |
| Use a factory function like :func:`torch.empty_like` to explicitly specify how |
| you would like the missing data to be filled in. |
| |
| NN modules have a convenience method :meth:`torch.nn.Module.to_empty` that |
| allow you to the module to another device, leaving all parameters |
| uninitialized. You are expected to explicitly reinitialize the parameters |
| manually:: |
| |
| >>> from torch.nn.modules import Linear |
| >>> with torch.device('meta'): |
| ... m = Linear(20, 30) |
| >>> m.to_empty(device="cpu") |
| Linear(in_features=20, out_features=30, bias=True) |
| |
| :mod:`torch._subclasses.meta_utils` contains undocumented utilities for taking |
| an arbitrary Tensor and constructing an equivalent meta Tensor with high |
| fidelity. These APIs are experimental and may be changed in a BC breaking way |
| at any time. |