在數據處理中,對原始數據進行重塑或重新排序并創建多個副本是很常見的行為。無論執行任何新步驟,都會創建新副本。隨著程序的增大,占用的內存也會增大,我幾乎從未考慮過這個問題,直到遇到了“內存不足”錯誤。
張量 (tensor) 的神奇之處在于多個張量可以引用同一存儲空間(即包含給定類型的數字的連續內存區塊)。此行為由 torch.storage 進行管理。
每個張量都包含 .storage 屬性,用于顯示內存中存儲的張量內容。
在下一篇的文章中,我將聊一聊張量所具有的更神奇的屬性,即跟蹤上級操作。
在本文中,我將主要介紹內存優化方面的內容。
全新 Vitis AI 1.2 發行版將首次為 PyTorch 提供支持。本文對于新增對此熱門框架的支持表示祝賀,并提供了 1 個 PyTorch 專用的 Jupyter Notebook 格式示例。
輸入 [1]:
import torch a = torch.randint(0, 9, (5,3)) a
輸出 [1]:
tensor([[4, 1, 6], [0, 8, 8], [1, 2, 1], [0, 5, 7], [0, 0, 7]])
輸出 [2]:
a.storage()
輸出 [2]:
4 1 6 0 8 8 1 2 1 0 5 7 0 0 7 [torch.LongStorage of size 15]
輸出 [3]:
a.shape
輸出 [3]:
torch.Size([5, 3])
我們可能需要對原始“a”張量進行轉置 (transpose) 和平展 (flatten) 處理。
何必為了相同數據浪費雙倍內存?哪怕數據只是形狀 (shape) 不同,也沒有必要。
輸入 [4]:
b = torch.transpose(a, 0, 1) b
輸出 [4]:
tensor([[4, 0, 1, 0, 0], [1, 8, 2, 5, 0], [6, 8, 1, 7, 7]])
a和b確實是指向相同存儲空間的張量。
兩者表現方式不同,原因在于我們使用 stride 函數指令其按不同順序讀取該存儲空間。
b的 stride 值為 (1,3),即讀取存儲空間時,每隔 1 個元素都必須跳至下一行,并且每隔 3 個元素必須跳至下一列。
輸出 [5]:
b.stride(), a.stride()
輸出 [5]:
((1, 3), (3, 1))
我們可以從a或b訪問數據,或者也可以從原始存儲空間直接訪問數據。
但如果從存儲空間訪問,則讀取的值將不再是張量。
輸入 [6]:
a[1,2], b[2,1], a.storage()[5], b.storage()[5]
輸出 [6]:
(tensor(8), tensor(8), 8, 8)
現在,令我感到疑惑不解的是,我發現這些張量的值神奇般地自行發生了改變:
更改a時,b也變了。
輸入 [7]:
a[0,0] = 10 b[0,0]
輸出 [7]:
tensor(10)
發生這種狀況的原因是因為,從內存角度來看,張量即經過排序的存儲空間表示法。
從同一存儲空間生成的 2 個張量并非獨立張量,而且我必須牢記的是,當我每次更改 1 個張量后,指向相同存儲空間的所有其它張量也都會被修改。
可見,即使高效的內存利用方式也難免有其缺點!
子集
通過原始數據的子集仍然能夠有效利用內存。
新的張量仍然指向原始存儲空間的子集。
輸入 [8]:
c = a[0:2, 0:2] c
輸出 [8]:
tensor([[10, 1], [ 0, 8]])
輸入 [9]:
c[0,0]=77 a
輸出 [9]:
tensor([[77, 1, 6], [ 0, 8, 8], [ 1, 2, 1], [ 0, 5, 7], [ 0, 0, 7]])
inplace 運算符
inplace 運算符即無需創建張量副本就可以直接對存儲空間進行操作的函數。這些運算符通常具有易于識別的名稱且以下劃線結尾。
輸入 [10]:
a.zero_() b
輸出 [10]:
tensor([[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]])
張量克隆
如果確實需要 1 個獨立的新張量,可以對其進行克隆。
這樣也會創建新的存儲空間。
輸入 [11]:
a_clone = a.clone() a_clone[0,0] = 55 a_clone
輸出 [11]:
tensor([[55, 0, 0], [ 0, 0, 0], [ 0, 0, 0], [ 0, 0, 0], [ 0, 0, 0]])
輸出 [12]:
a
輸出 [12]:
tensor([[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]])
為連續張量重組存儲空間
部分函數僅適用于連續張量。
對a進行轉置時,通過在b中分配來自存儲空間的非連續矩陣值,生成了新的張量。
輸入 [13]:
a.is_contiguous()
輸出 [13]:
True
輸出 [14]:
b.is_contiguous()
輸出 [14]:
False
我們可將b設為連續張量,但這將導致b生成經過重組的新存儲空間,從而導致a和b永遠無法成為獨立張量:
輸入 [15]:
b = b.contiguous() b[0,0] = 18 a[0,0]
輸出 [15]:
tensor(0)
輸出 [16]:
b.is_contiguous()
輸出 [16]:
True
輸出 [16]:
a.is_contiguous()
輸出 [17]:
True
編輯:hfy
-
內存
+關注
關注
8文章
3037瀏覽量
74158 -
AI
+關注
關注
87文章
31172瀏覽量
269551 -
數據處理
+關注
關注
0文章
606瀏覽量
28597 -
pytorch
+關注
關注
2文章
808瀏覽量
13263
發布評論請先 登錄
相關推薦
評論