- 深度強化學習實踐(原書第2版)
- (俄)馬克西姆·拉潘
- 2334字
- 2021-08-18 17:39:19
3.1 張量
張量是所有DL工具包的基本組成部分。名字聽起來很神秘,但本質上張量就是一個多維數組。用數學知識來類比,單個數字就像點,是零維的,向量就像線段,是一維的,矩陣則是二維的對象。三維數字的集合可以用平行六面體表示,但它們不像矩陣一樣有特定的名稱。我們可以用“張量”來表示高維集合,如圖3.1所示。

圖3.1 從一個數字到n維張量
需要注意的是,在DL中所使用的張量和在張量演算或張量代數中所用的張量相比,只在部分層面上相關。在DL中,張量是任意多維的數組,但在數學中,張量是向量空間之間的映射,在某些情況下可以表示為多維數組,但背后具有更多的語義信息。數學家通常會對任何使用公認數學術語來命名不同事物的人皺眉,因此要當心!
3.1.1 創建張量
如果你熟悉NumPy庫,那你應該知道其主要目的是以通用的方式處理多維數組。在NumPy中,這樣的數組沒被稱為張量,但事實上,它們就是張量。張量在科學計算中被廣泛用作數據的通用存儲方式。例如,彩圖會被編碼成具有寬度、高度和色值的三維張量。
除了維度之外,元素類型也是張量的特征之一。PyTorch支持八種類型,包括三種浮點類型(16位、32位和64位)和五種整數類型(有符號8位、無符號8位、16位、32位和64位)。不同類型的張量用不同的類表示,其中最常用的是torch.FloatTensor
(對應32位浮點類型)、torch.ByteTensor
(無符號8位整數)、torch.LongTensor
(有符號64位整數)。其余的可以在PyTorch的文檔中查到。
有三種方法可以在PyTorch中創建張量:
- 通過調用所需類型的構造函數。
- 通過將NumPy數組或Python列表轉換為張量。在這種情況下,類型將從數組的類型中獲取。
- 通過要求PyTorch創建帶有特定數據的張量。例如,可以使用
torch.zeros()
函數創建一個全為零的張量。
下面是上述方法的實現示例:

在這里,我們導入了PyTorch和NumPy,并創建未初始化的3×2的張量。默認情況下,PyTorch會為張量分配內存,但不進行初始化。要清除張量的內容,需要使用以下操作:

張量有兩種類型的操作:inplace和functional。inplace操作需在函數名稱后附加一個下劃線,作用于張量的內容。然后,會返回對象本身。functional操作是創建一個張量的副本,對其副本進行修改,而原始張量保持不變。從性能和內存角度來看,inplace方式通常更為高效。
通過其構造函數創建張量的另一種方法是提供Python可迭代對象(例如列表或元組),它將被用作新創建張量的內容:

下面的代碼用NumPy創建了一個全零張量:

torch.tensor
方法接受NumPy數組作為參數,并創建一個適當形狀的張量。在前面的示例中,我們創建了一個由零初始化的NumPy數組,默認情況下,該數組創建一個double
(64位浮點數)數組。因此,生成的張量具有DoubleTensor
類型(在前面的示例中使用dtype
值顯示)。在DL中,通常不需要雙精度,因為使用雙精度會增加內存和性能開銷。通常的做法是使用32位浮點類型,甚至使用16位浮點類型,這已經能夠滿足需求了。要創建這樣的張量,需要明確指定NumPy數組的類型:

作為可選項,可以在dtype
參數中將所需張量的類型提供給torch.tensor
函數。但是,請小心,因為此參數期望傳入PyTorch類型規范,而不是NumPy類型規范。PyTorch類型保存在torch
包中,例如torch.float32
和torch.uint8
。

兼容性說明
0.4.0版本中添加了torch.tensor()
方法和顯式PyTorch類型規范,這是朝著簡化張量創建邁出的一步。在以前的版本中,推薦使用torch.from_numpy()
函數來轉換NumPy數組,但是在處理Python列表和NumPy數組的組合時遇到了問題。from_numpy()
函數仍可實現后向兼容性,但不推薦使用此函數,而推薦使用更靈活的torch.tensor()
方法。
3.1.2 零維張量
從0.4.0版本開始,PyTorch已經支持了與標量相對應的零維張量(在圖3.1的左側)。這種張量可能是某些操作(例如對張量中的所有值求和)的結果。這種情況以前是通過創建維度為1的張量(向量)來處理的。
該解決方案是有效的,但它并不簡單,因為需要額外的索引才能訪問值。現在,已經支持零維張量并由合適的函數返回,并且可以通過torch.tensor()
函數創建。為了訪問這種張量的實際Python值,可以使用特殊的item()
方法:

3.1.3 張量操作
你可以對張量執行很多操作,因為操作太多,無法一一列出。通常,在PyTorch的官方文檔(http://pytorch.org/docs/)中搜索就足夠使用了。需要指出的是,除了前文所討論的inplace和functional(即帶有下劃線的函數和不帶下劃線的函數,例如abs()
和abs_()
),還有兩個地方可以查找操作:torch
包和張量類。在第一種情況下,函數通常接受張量作為參數。在第二個中,函數作用于張量上。
在大多數情況下,張量操作試圖和NumPy中相應的操作等效,因此,如果NumPy中存在一些不是非常專有的函數,那么PyTorch中也可能會有,例如torch.stack()
、torch.transpose()
和torch.cat()
。
3.1.4 GPU張量
PyTorch透明地支持CUDA GPU,這意味著所有操作都有兩個版本(CPU和GPU)供自動選擇。使用哪個版本操作根據所操作的張量類型決定。
我提到的每種張量類型都是針對CPU的,并且具有與之對應的GPU類型。唯一的區別是GPU張量在torch.cuda
包中,而不是在torch
中。例如,torch.FloatTensor
是在CPU內存中的32位浮點張量,而torch.cuda.FloatTensor
是在GPU中的32位浮點張量。
為了從CPU轉換到GPU,有一個to(device)
的張量方法,可以創建張量的副本到指定設備(可以是CPU或GPU)。如果張量已經在相應的設備上,那么什么也不會發生,并且返回原始張量。可以用不同的方式指定設備類型。首先,可以僅傳遞設備的字符串名稱,對于CPU內存為“cpu”,對于GPU可用“cuda”。GPU設備可以在冒號之后指定一個可選設備的索引。例如,系統中的第二個GPU可以用“cuda:1”尋址(索引從零開始)。
在to()
方法中指定設備的另一種更為有效的方法是使用torch.device
類,該類接受設備名稱和可選索引。它有device
屬性,所以可以訪問張量當前所在的設備。

上面的代碼在CPU上創建了一個張量,然后將其復制到GPU中。兩個副本均可用于計算,并且所有GPU特有的機制對用戶都是透明的:

兼容性說明
to()
方法和torch.device
類是在0.4.0中引入的。在以前的版本中,CPU和GPU之間的相互復制是分開的,分別通過單獨的方法CPU()
和cuda()
來執行,需要添加額外的代碼行來顯式地將張量轉換成它們的CUDA版本。在最新的版本中,你可以在程序開始時創建一個想要的torch.device
對象,然后在創建的每一個張量上使用to(device)
。張量中的舊方法,cpu()
和cuda()
仍然存在,如果想確保一個張量在CPU或GPU中,而不管它的原始位置,使用舊方法可能會更方便。