torch.Storage¶
在 PyTorch 中,一個常規的 tensor 是一個多維陣列,由以下元件定義:
Storage:tensor 的實際資料,儲存為一個連續的一維位元組陣列。
dtype:tensor 中元素的 資料型別,例如 torch.float32 或 torch.int64。shape:一個元組,指示 tensor 在每個維度上的大小。Stride:在每個維度上從一個元素移動到下一個元素所需的步長。
Offset:儲存中 tensor 資料開始的起始點。對於新建立的 tensor,這通常為 0。
這些元件共同定義了 tensor 的結構和資料,其中 storage 儲存實際資料,其餘部分作為元資料。
無型別 Storage API¶
torch.UntypedStorage 是一個連續的一維元素陣列。其長度等於 tensor 的位元組數。storage 作為 tensor 的底層資料容器。通常,在 PyTorch 中使用常規建構函式(如 zeros()、zeros_like() 或 new_zeros())建立的 tensor,其 tensor storage 與 tensor 本身之間存在一對一的對應關係。
然而,一個 storage 可以被多個 tensor 共享。例如,tensor 的任何檢視(透過 view() 或某些(但非全部)索引型別(如整數和切片)獲得)將指向與原始 tensor 相同的底層 storage。序列化和反序列化共享同一 storage 的 tensor 時,這種關係得以保留,並且這些 tensor 繼續指向同一 storage。有趣的是,反序列化指向單個 storage 的多個 tensor 比反序列化多個獨立的 tensor 更快。
可以透過 untyped_storage() 方法訪問 tensor storage。這將返回型別為 torch.UntypedStorage 的物件。幸運的是,storage 有一個唯一的識別符號,透過 torch.UntypedStorage.data_ptr() 方法訪問。在常規設定中,具有相同資料 storage 的兩個 tensor 將具有相同的 storage data_ptr。然而,tensor 本身可以指向兩個獨立的 storage,一個用於其 資料 屬性,另一個用於其 梯度 屬性。每個都需要自己的 data_ptr()。通常,不能保證 torch.Tensor.data_ptr() 和 torch.UntypedStorage.data_ptr() 匹配,也不應假定如此。
無型別 storage 與基於它們的 tensor 有些獨立。實際上,這意味著具有不同 dtype 或 shape 的 tensor 可以指向同一個 storage。這也意味著 tensor storage 可以被更改,如下例所示:
>>> t = torch.ones(3)
>>> s0 = t.untyped_storage()
>>> s0
0
0
128
63
0
0
128
63
0
0
128
63
[torch.storage.UntypedStorage(device=cpu) of size 12]
>>> s1 = s0.clone()
>>> s1.fill_(0)
0
0
0
0
0
0
0
0
0
0
0
0
[torch.storage.UntypedStorage(device=cpu) of size 12]
>>> # Fill the tensor with a zeroed storage
>>> t.set_(s1, storage_offset=t.storage_offset(), stride=t.stride(), size=t.size())
tensor([0., 0., 0.])
警告
請注意,直接修改 tensor 的 storage(如本例所示)不是推薦的做法。這種低階操作僅出於教育目的進行演示,以展示 tensor 及其底層 storage 之間的關係。通常,使用標準的 torch.Tensor 方法(例如 clone() 和 fill_())來實現相同結果更有效、更安全。
除了 data_ptr,無型別 storage 還有其他屬性,例如 filename(如果 storage 指向磁碟上的檔案)、device 或 is_cuda 用於裝置檢查。storage 還可以使用諸如 copy_、fill_ 或 pin_memory 之類的方法進行原地或非原地操作。有關更多資訊,請檢視下面的 API 參考。請記住,修改 storage 是一種低階 API,伴隨著風險!大多數這些 API 也存在於 tensor 級別:如果存在,應優先使用它們的 tensor 對應項。
特殊情況¶
我們提到,具有非 None 梯度 屬性的 tensor 實際上包含兩個資料塊。在這種情況下,untyped_storage() 將返回 資料 屬性的 storage,而梯度的 storage 可以透過 tensor.grad.untyped_storage() 獲取。
>>> t = torch.zeros(3, requires_grad=True)
>>> t.sum().backward()
>>> assert list(t.untyped_storage()) == [0] * 12 # the storage of the tensor is just 0s
>>> assert list(t.grad.untyped_storage()) != [0] * 12 # the storage of the gradient isn't
- 也存在 tensor 沒有典型 storage 或根本沒有 storage 的特殊情況:
"meta"裝置上的 Tensor:"meta"裝置上的 tensor 用於 shape 推斷,不包含實際資料。Fake Tensors:PyTorch 編譯器使用的另一個內部工具是 FakeTensor,它基於類似的想法。
Tensor 子類或類似 tensor 的物件也可能表現出異常行為。一般來說,我們不期望很多使用場景需要操作到 Storage 級別!
- class torch.UntypedStorage(*args, **kwargs)[source][source]¶
-
- copy_()¶
- cuda(device=None, non_blocking=False)[source]¶
返回此物件在 CUDA 記憶體中的副本。
如果此物件已在 CUDA 記憶體中且位於正確的裝置上,則不執行復制並返回原始物件。
- 引數
- 返回型別
Union[_StorageBase, TypedStorage]
- data_ptr()¶
- element_size()¶
- property filename: Optional[str]¶
返回與此 storage 關聯的檔名。
如果 storage 在 CPU 上並透過
from_file()設定shared為True建立,則檔名將是一個字串。否則,此屬性為None。
- fill_()¶
- static from_buffer()¶
- static from_file(filename, shared=False, size=0) Storage¶
建立一個由記憶體對映檔案支援的 CPU storage。
如果
shared為True,則所有程序之間共享記憶體。所有更改都會寫入檔案。如果shared為False,則 storage 上的更改不會影響檔案。size是 storage 中的元素數量。如果shared為False,則檔案必須包含至少size * sizeof(Type)位元組(Type是 storage 的型別,對於UnTypedStorage,檔案必須包含至少size位元組)。如果shared為True,如果需要將建立檔案。- 引數
filename (str) – 要對映的檔名
shared (bool) – 是否共享記憶體(是傳遞
MAP_SHARED還是MAP_PRIVATE給底層 mmap(2) 呼叫)size (int) – storage 中的元素數量
- hpu(device=None, non_blocking=False)[source]¶
返回此物件在 HPU 記憶體中的副本。
如果此物件已在 HPU 記憶體中且位於正確的裝置上,則不執行復制並返回原始物件。
- 引數
- 返回型別
Union[_StorageBase, TypedStorage]
- property is_cuda¶
- property is_hpu¶
- is_pinned(device='cuda')[source]¶
確定 CPU storage 是否已固定在裝置上。
- 引數
device (str 或 torch.device) – 要固定記憶體的裝置(預設值:
'cuda')。不建議使用此引數,它可能會被棄用。- 返回
一個布林變數。
- nbytes()¶
- new()¶
- pin_memory(device='cuda')[source]¶
將 CPU 儲存複製到固定記憶體,如果尚未固定。
- 引數
device (str 或 torch.device) – 要固定記憶體的裝置(預設值:
'cuda')。不建議使用此引數,它可能會被棄用。- 返回
一個固定記憶體的 CPU 儲存。
- resizable()¶
- resize_()¶
將儲存移動到共享記憶體。
對於已在共享記憶體中的儲存以及 CUDA 儲存(它們無需移動即可跨程序共享),這是一個空操作。共享記憶體中的儲存無法調整大小。
請注意,為緩解 this 等問題,在同一物件上從多個執行緒呼叫此函式是執行緒安全的。但是,在未進行適當同步的情況下,呼叫 self 上的任何其他函式都是非執行緒安全的。請參閱 Multiprocessing best practices 以獲取更多詳細資訊。
注意
當共享記憶體中儲存的所有引用都被刪除時,關聯的共享記憶體物件也將被刪除。PyTorch 有一個特殊的清理流程,以確保即使當前程序意外退出,此操作也會發生。
值得注意的是
share_memory_()與from_file()並設定shared = True之間的區別。share_memory_使用 shm_open(3) 建立一個 POSIX 共享記憶體物件,而from_file()使用 open(2) 開啟使用者傳入的檔名。兩者都使用帶有
MAP_SHARED的 mmap(2) 呼叫,將檔案/物件對映到當前的虛擬地址空間。share_memory_會在對映物件後呼叫shm_unlink(3),以確保當沒有程序開啟該物件時,共享記憶體物件會被釋放。torch.from_file(shared=True)不會取消連結該檔案。此檔案是持久的,將保留直到使用者刪除它。
- 返回
self
- type(dtype=None, non_blocking=False)[source]¶
- 返回型別
Union[_StorageBase, TypedStorage]
傳統型別儲存¶
警告
從歷史角度來看,PyTorch 以前使用型別化儲存類,這些類現已棄用,應避免使用。以下詳細介紹了此 API,以防您遇到它,但強烈不建議使用它。將來,除了 torch.UntypedStorage 之外的所有儲存類都將被移除,並且所有情況下都將使用 torch.UntypedStorage。
torch.Storage 是對應於預設資料型別 (torch.get_default_dtype()) 的儲存類的別名。例如,如果預設資料型別是 torch.float,則 torch.Storage 解析為 torch.FloatStorage。
torch.<type>Storage 和 torch.cuda.<type>Storage 類,例如 torch.FloatStorage、torch.IntStorage 等,實際上從未例項化。呼叫它們的建構函式會建立一個具有適當 torch.dtype 和 torch.device 的 torch.TypedStorage。torch.<type>Storage 類擁有 torch.TypedStorage 所擁有的所有相同的類方法。
一個 torch.TypedStorage 是一個連續的、一維的陣列,其中包含特定 torch.dtype 的元素。它可以指定任何 torch.dtype,內部資料將得到相應的解釋。torch.TypedStorage 包含一個 torch.UntypedStorage,它將資料儲存為無型別的位元組陣列。
每個跨步 torch.Tensor 都包含一個 torch.TypedStorage,它儲存 torch.Tensor 檢視的所有資料。
- class torch.TypedStorage(*args, wrap_storage=None, dtype=None, device=None, _internal=False)[source][source]¶
- cuda(device=None, non_blocking=False)[source][source]¶
返回此物件在 CUDA 記憶體中的副本。
如果此物件已在 CUDA 記憶體中且位於正確的裝置上,則不執行復制並返回原始物件。
- property device¶
- classmethod from_file(filename, shared=False, size=0) Storage[source][source]¶
建立一個由記憶體對映檔案支援的 CPU storage。
如果
shared為True,則所有程序之間共享記憶體。所有更改都會寫入檔案。如果shared為False,則 storage 上的更改不會影響檔案。size是儲存中的元素數量。如果shared為False,則檔案必須至少包含size * sizeof(Type)位元組(Type是儲存的型別)。如果shared為True,則會在需要時建立檔案。- 引數
filename (str) – 要對映的檔名
shared (bool) –
是否共享記憶體(是否將
MAP_SHARED或MAP_PRIVATE傳遞到底層 mmap(2) 呼叫)size (int) – storage 中的元素數量
- hpu(device=None, non_blocking=False)[source][source]¶
返回此物件在 HPU 記憶體中的副本。
如果此物件已在 HPU 記憶體中且位於正確的裝置上,則不執行復制並返回原始物件。
- property is_cuda¶
- property is_hpu¶
- is_pinned(device='cuda')[source][source]¶
確定 CPU TypedStorage 是否已固定在裝置上。
- 引數
device (str 或 torch.device) – 要固定記憶體的裝置(預設值:
'cuda')。不建議使用此引數,它可能會被棄用。- 返回
一個布林變數。
- pin_memory(device='cuda')[source][source]¶
將 CPU TypedStorage 複製到鎖頁記憶體(如果尚未鎖定)。
- 引數
device (str 或 torch.device) – 要固定記憶體的裝置(預設值:
'cuda')。不建議使用此引數,它可能會被棄用。- 返回
一個固定記憶體的 CPU 儲存。
- type(dtype=None, non_blocking=False)[source][source]¶
如果未提供 dtype,則返回型別,否則將此物件強制轉換為指定的型別。
如果此物件已經是正確的型別,則不執行復制,而是返回原始物件。
- 引數
- 返回型別
Union[_StorageBase, TypedStorage, str]
- untyped()[source][source]¶
返回內部的
torch.UntypedStorage。
- class torch.DoubleStorage(*args, wrap_storage=None, dtype=None, device=None, _internal=False)[source][source]¶
- dtype: torch.dtype = torch.float64[source]¶
- class torch.FloatStorage(*args, wrap_storage=None, dtype=None, device=None, _internal=False)[source][source]¶
- dtype: torch.dtype = torch.float32[source]¶
- class torch.HalfStorage(*args, wrap_storage=None, dtype=None, device=None, _internal=False)[source][source]¶
- dtype: torch.dtype = torch.float16[source]¶
- class torch.LongStorage(*args, wrap_storage=None, dtype=None, device=None, _internal=False)[source][source]¶
- dtype: torch.dtype = torch.int64[source]¶
- class torch.IntStorage(*args, wrap_storage=None, dtype=None, device=None, _internal=False)[source][source]¶
- dtype: torch.dtype = torch.int32[source]¶
- class torch.ShortStorage(*args, wrap_storage=None, dtype=None, device=None, _internal=False)[source][source]¶
- dtype: torch.dtype = torch.int16[source]¶
- class torch.CharStorage(*args, wrap_storage=None, dtype=None, device=None, _internal=False)[source][source]¶
- dtype: torch.dtype = torch.int8[source]¶
- class torch.ByteStorage(*args, wrap_storage=None, dtype=None, device=None, _internal=False)[source][source]¶
- dtype: torch.dtype = torch.uint8[source]¶
- class torch.BoolStorage(*args, wrap_storage=None, dtype=None, device=None, _internal=False)[source][source]¶
- dtype: torch.dtype = torch.bool[source]¶
- class torch.BFloat16Storage(*args, wrap_storage=None, dtype=None, device=None, _internal=False)[source][source]¶
- dtype: torch.dtype = torch.bfloat16[source]¶
- class torch.ComplexDoubleStorage(*args, wrap_storage=None, dtype=None, device=None, _internal=False)[source][source]¶
- dtype: torch.dtype = torch.complex128[source]¶
- class torch.ComplexFloatStorage(*args, wrap_storage=None, dtype=None, device=None, _internal=False)[source][source]¶
- dtype: torch.dtype = torch.complex64[source]¶
- class torch.QUInt8Storage(*args, wrap_storage=None, dtype=None, device=None, _internal=False)[source][source]¶
- dtype: torch.dtype = torch.quint8[source]¶
- class torch.QInt8Storage(*args, wrap_storage=None, dtype=None, device=None, _internal=False)[source][source]¶
- dtype: torch.dtype = torch.qint8[source]¶
- class torch.QInt32Storage(*args, wrap_storage=None, dtype=None, device=None, _internal=False)[source][source]¶
- dtype: torch.dtype = torch.qint32[source]¶