?
目錄
Tensor
概念
數據類型
創建tensor
基本創建方式
1、?torch.tensor()
2、torch.Tensor()
3、torch.IntTensor() 等
創建線性張量和隨機張量
1、創建線性張量
2、創建隨機張量
切換設備
類型轉換
與 Numpy 數據轉換
1、張量轉 Numpy
2、Numpy 轉張量
tensor常見操作
1、獲取元素值
2、元素值運算
3、tensor相乘
4、形狀操作
view():
reshape():
5、維度轉換
transpose(dim0, dim1):兩個維度轉換
permute(dim0, dim1, ... , dimN):重排所有維度
6、升維降維
unsqueeze():升維
squeeze():降維
PyTorch是一個基于Python的深度學習框架,它提供了一種靈活、高效、易于學習的方式來實現深度學習模型,最初由Facebook開發,被廣泛應用于計算機視覺、自然語言處理、語音識別等領域。PyTorch提供了許多高級功能,如自動微分(automatic differentiation)、自動求導(automatic gradients)等,這些功能可以幫助我們更好地理解模型的訓練過程,并提高模型訓練效率。
沒錯苯人開始 pytorch的學習了,這篇來寫一下 Torch 的一些基本API。
Tensor
概念
?PyTorch會將數據全都封裝成張量(Tensor)進行計算,所謂張量就是元素為相同類型的多維矩陣,或者多維數組,通俗來說可以看作是擴展了標量、向量、矩陣的更高維度的數組。張量的維度決定了它的形狀(Shape),例如:
-
標量?是 0 維張量,如 a = torch.tensor(5)
-
向量 是 1 維張量,如?b = torch.tensor([1, 2, 3])
-
矩陣 是 2 維張量,如 c = torch.tensor([[1,2], [3,4]])
-
更高維度的張量,如3維、4維等,通常用于表示圖像、視頻數據等復雜結構。
數據類型
? PyTorch中有3種數據類型:浮點數類型、整數類型、布爾類型。其中,浮點數和整數又分為8位、16位、32位、64位,加起來共9種。為什么要分這么多種類型呢是因為場景不同,對數據的精度和速度要求不同。通常,移動或嵌入式設備追求速度,對精度要求相對低一些。精度越高,往往效果也越好,自然硬件開銷就比較高。
創建tensor
以下講的創建 tensor 的函數中有兩個有默認值的參數 dtype 和 device, 分別代表數據類型和計算設備,可以通過屬性 dtype 和 device 獲取,如 print(t1.dtype)
基本創建方式
1、?torch.tensor()
代碼示例:
def test1():# 創建張量t1 = torch.tensor(88) #創建標量t2 = torch.tensor(np.random.randn(3, 2)) #用numpy隨機數組創建1維張量t3 = torch.tensor([[1, 2, 3], [4, 5, 6]]) #創建2維張量print(t1)print(t2)print(t2.shape)print(t3)print(t3.shape)
運行結果:
注意,如果出現?UserWarning: Failed to initialize NumPy: _ARRAY_API not found 錯誤,一般是因為 numpy和pytorch版本不兼容,可以降低 numpy版本,直接卸載重裝,最好是1.x版本
2、torch.Tensor()
代碼示例:
def test2():#torch.Tenor()創建張量t1 = torch.Tensor(3) #根據形狀創建print(t1)t2 = torch.Tensor([[1,2,3],[4,5,6]]) #根據具體的值創建print(t2)print(t2.shape)
運行結果:
這里可以看到,當傳入的是一個整數時,tensor() 是直接創建標量,而 Tensor() 則是初始化一個形狀為該整數的張量。除此之外,它倆的區別還在于:
特性 | torch.Tensor() | torch.tensor() |
---|---|---|
數據類型推斷 | 強制轉為 torch.float32 | 根據輸入數據自動推斷(如整數→int64 ) |
顯式指定 dtype | 不支持 | 支持(如 dtype=torch.float64 ) |
設備指定 | 不支持 | 支持(如 device='cuda' ) |
輸入為張量時的行為 | 創建新副本(不繼承原屬性) | 默認共享數據(除非 copy=True ) |
推薦使用場景 | 需要快速創建浮點張量 | 需要精確控制數據類型或設備 |
一般是使用 torch.tensor() 更多,更安全、功能更全,可避免未初始化張量的隱患。
3、torch.IntTensor() 等
?用于創建指定類型的張量,諸如此類還有 torch.FloatTensor(32位浮點數)、 torch.DoubleTensor(64位浮點數)、torch.ShortTensor(16位整數)、?torch.LongTensor(64位整數)......等。如果數據類型不匹配,那么在創建的過程中會進行類型轉換,防止數據丟失。
代碼示例:
def test3():#創建指定類型的張量tt1 = torch.IntTensor(3,3)print(tt1)tt2 = torch.FloatTensor(3, 3)print(tt2, tt2.dtype)tt3 = torch.DoubleTensor(3, 3)print(tt3, tt3.dtype)tt4 = torch.LongTensor(3, 3)print(tt4, tt4.dtype)tt5 = torch.ShortTensor(3, 3)print(tt5, tt5.dtype)
運行結果就不貼了
創建線性張量和隨機張量
1、創建線性張量
可使用 torch.arange() 和 torch.linspace() 創建一維線性張量
torch.arange(start, end, step):生成一個等間隔的一維張量(序列),區間為 [start, end)(左閉右開),步長由 step 決定。
torch.linspace(start, end, step):生成一個在區間 [start, end](左右均閉合)內均勻分布的 steps 個元素的一維張量(相當于等差數列)
代碼示例如下:
def test4():# 創建線性張量t1 = torch.arange(0, 10, step=2)print(t1)t2 = torch.linspace(1, 2, steps=5)print(t2)
運行結果:
2、創建隨機張量
使用torch.randn 和 torch.randint 創建隨機張量。
torch.randn(size, ?dtype?, device?, requires_grad) :生成服從標準正態分布(均值為 0,標準差為 1)的隨機張量,size 可以傳元組或者直接傳數字,生成的都是二維張量;后兩個參數就不過多介紹;requires_grad 表示是否需要計算梯度,默認為False,這個參數很重要,可能下一篇會說。
torch.randint(low, high, size):在指定的[ low, high)范圍內生成離散的、均勻分布的整數,low 為最小值,high 為最大值,size 為張量的形狀
另外還要介紹一下隨機數種子:隨機數種子(Random Seed)?是控制計算機生成“隨機數”的一個關鍵參數,它的存在讓“隨機”變得可復現,設置種子后算法會固定從同一個“起點”開始計算,生成的隨機序列完全一致,至于設置什么數字并不重要,只是不同的數字生成的數字序列不同罷了,API為??torch.manual_seed()
代碼實例:
def test5():'''創建隨機張量'''torch.manual_seed(42)# 設置隨機數種子t1 = torch.randn(2,3) #生成隨機張量,標準正態分布,均值 0,標準差 1t2 = torch.rand(2,3) #生成隨機張量,均勻分布,范圍在 [0, 1)t3 = torch.randint(0, 10, (2, 3)) #在0到10內(不包括10)生成 2x3 的張量print(t1)print(t2)print(t3)
運行結果:
這里因為設置了隨機數種子所以每次輸出的結果都相同,如果將第一行注釋掉,每次生成的張量就不一樣了,同時注意一下 size 有沒有括號都是生成的二維數組,當然也可以生成三維張量,例如
torch.rand(2, 3, 4)
切換設備
tensor 相關操作默認在cpu上運行,但是可以顯式地切換到GPU,且不同設備上的數據是不能相互運算的。切換操作如下:
def test6():'''切換設備'''t1 = torch.tensor([1,2,3])print(t1.shape, t1.dtype, t1.device)#切換到GPU 方法一device = "cuda" if torch.cuda.is_available() else "cpu"print(t1.device)# 切換到GPU 方法二 to()方法t1 = t1.to(device="cuda")print(t1.device)
運行結果:
因為苯人電腦沒有顯卡所以轉不過去,這里只是一個代碼示范
或者直接用 cuda 進行轉換:
t1 = t1.cuda()
當然也可以直接在 GPU 上創建張量:
data = torch.tensor([1, 2, 3], device='cuda')
print(data.device)
類型轉換
?在寫代碼時,類型的不同有時也會導致各種錯誤,所以類型的轉換也是很重要的,有三種類型轉換的方法:
def test7():'''類型轉換'''t1 = torch.tensor([1,2,3])print(t1.dtype) #torch.int64# 1、使用 type() 進行轉換t1 = t1.type(torch.float32)print(t1.dtype) #torch.float32# 2、使用類型方法轉換t1 = t1.float()print(t1.dtype) # float32t1 = t1.half()print(t1.dtype) # float16t1 = t1.double()print(t1.dtype) # float64t1 = t1.long()print(t1.dtype) # int64t1 = t1.int()print(t1.dtype) # int32# 3、使用 dtype 屬性指定t1 = torch.tensor([1,2,3], dtype=torch.float32)print(t1.dtype) #torch.float32
與 Numpy 數據轉換
1、張量轉 Numpy
淺拷貝:內存共享,修改 Numpy數組也會改變原張量,API:numpy()
深拷貝:內存不共享,相當于建了一個獨立的副本,API:numpy().copy()
代碼示例(淺拷貝):
def test8():'''張量轉numpy'''#淺拷貝t1 = torch.tensor([[1,2,3], [4,5,6],[7,8,9]])t2 = t1.numpy() 轉 numpyprint(t1)print(f't2:{t2}')t2[0][1] = 666 #內存共享print(t1)print(f't2:{t2}')
運行結果:
可以看到,t1 轉成 t2 后,修改 t2 還是會改變 t1,如果想要避免內存共享可以用深拷貝:
#深拷貝# t2 = t1.numpy().copy()
其他同上
2、Numpy 轉張量
同樣分為內存共享和內存不共享:
淺拷貝:內存共享,修改 tensor 也會改變原數組,API:torch.from_numpy()
深拷貝:內存不共享,相當于建了一個獨立的副本,API:torch.tensor()
代碼示例(深拷貝):
'''numpy轉張量'''# 深拷貝n1 = np.array([[11,22,33],[44,55,66]])n1_tensor = torch.tensor(n1) #轉張量print(f'n1:{n1}')print(f'n1_tensor:{n1_tensor}')n1_tensor[0][1] = 88 #內存不共享print(f'n1:{n1}')print(f'n1_tensor:{n1_tensor}')
運行結果:
可以看到,修改 n1_tensor 不會改變 n1,如果想要淺拷貝的話:
#淺拷貝# n1_tensor = torch.from_numpy(n1)
tensor常見操作
?接下來介紹一些在深度學習中基本的 tensor 操作:
1、獲取元素值
我們可以把只含有單個元素的 tensor 轉換為Python數值,這和 tensor 的維度無關,但若有多個元素則會報錯,且僅適用于 CPU張量。
API為 item():
def test9():'''獲取元素值'''t1 = torch.tensor([[[18]]])t2 = torch.tensor([20,13])print(t1.item())print(t2.item())
運行結果:
可以看到佑報錯,這就是因為 t2 含有不止一個元素所以不能用 item()
2、元素值運算
?常見的加、減、乘、除、次方、取反、開方等各種操作,但是也分是否修改原始值
不修改原始值代碼示例:
'''元素值運算'''t1 = torch.randint(0, 10, (2,3))print(f't1:{t1}')print(t1.add(1))print(t1.sub(1))print(t1.mul(2))print(t1.div(2))print(f't1:{t1}')
運行結果:
可以看到,經過加減乘除后 t1 的元素并沒有被改變
修改原始值:
t2 = torch.randint(0, 10, (2, 3))print(f't2:{t2}')t2.add_(1)print(f't2:{t2}')
運行結果:
可以看到 t2 已被改變,所以帶下劃線 “_” 的運算會改變原始值
3、tensor相乘
tensor 之間的乘法有兩種,一種是矩陣乘法,還有一種是阿達瑪積。
矩陣乘法是線性代數中的一種基本運算,用于將兩個矩陣相乘,生成一個新的矩陣。
舉個例子,假設有兩個矩陣:
-
矩陣 A的形狀為 m×n(m行 n列)。
-
矩陣 B的形狀為 n×p(n行 p列)。
則矩陣?A和 B的乘積 C=A×B,是一個形狀為 m×p的矩陣,可以理解為前一個矩陣控制行數,后一個矩陣控制列數,計算公式為:
要求如果第一個矩陣的shape是 (N, M),那么第二個矩陣 shape必須是 (M, P),最后兩個矩陣點積運算的shape為 (N, P),而在 pytorch 中,使用 matmul() 或者 @ 來完成 tensor 的乘法
代碼示例如下:
import torchdef test1():'''矩陣乘法'''t1 = torch.tensor([[1,2,3],[4,5,6]])t2 = torch.tensor([[7,8],[9,10],[11,12]])# 兩種方法print(t1 @ t2)print(t1.matmul(t2))if __name__ == '__main__':test1()
運行結果:
下面介紹阿達瑪積。阿達瑪積是指兩個形狀相同的矩陣或張量對應位置的元素相乘。它與矩陣乘法不同,矩陣乘法是線性代數中的標準乘法,而阿達瑪積是逐元素操作。
假設有兩個形狀相同的矩陣 A和 B,則它們的阿達瑪積 C=A°B定義為:
在 pytorch 中,可以使用 * 或者 mul() 來實現,代碼如下:
'''阿達瑪積'''t3 = torch.tensor([[1,2,3],[4,5,6]])t4 = torch.tensor([[7,8,9],[11,22,33]])#兩種方法print(t3 * t4)print(t3.mul(t4))
運行結果:
4、形狀操作
?在 PyTorch 中,張量的形狀操作是非常重要的,因為它允許你靈活地調整張量的維度和結構,以適應不同的計算需求。 下面介紹兩種方法:
view():
快速改變張量的形狀,但不復制數據,新張量與原始張量共享內存。前提條件是原張量在內存中連續,可用?is_contiguous() 判斷,連續會輸出 True,否則需要先調用?contiguous() 轉成連續的
API為 view(size),size即新形狀,可以是元組或者 -1,單獨的 -1 表示展平向量,放在元組中表示自動計算某一維度的元素數量(必須與總元素一致)
代碼如下:
def test2():'''形狀操作'''# view()t1 = torch.arange(6) #創建一個0到5的一維張量print(t1.is_contiguous())t2 = t1.view(-1) #展平 雖然本來就是一維張量t3 = t1.view(2,3) #轉成 2x3 的張量t4 = t1.view(3,-1) #轉成3行 至于幾列由電腦自動計算 只要保證總元素相同print(f't1:{t1}')print(f't2:{t2}')print(f't3:{t3}')print(f't4:{t4}')
運行結果:
若t1 不連續,那么用 t1.contiguous().view() 進行操作
reshape():
更靈活的變形方法,自動處理連續性(如果原始張量不連續,會復制數據),連續的話內存就共享,否則相當于獨立數據。
API為 reshape(size),用法與 view() 相同,這里就不再展示代碼了,看一下兩者的區別吧:
?
特性 | view() | reshape() |
---|---|---|
共享內存 | 總是共享(需連續) | 可能共享(若連續)或復制 |
連續性要求 | 必須連續 | 自動處理連續性 |
使用場景 | 確定張量連續時的高效操作 | 不確定連續性時的通用操作 |
5、維度轉換
pytorch 中維度轉換分為兩個維度轉換和多個維度轉換,分別用不同的方法:
transpose(dim0, dim1):兩個維度轉換
transpose() 僅用于兩個維度的轉換如矩形轉置,返回的是原張量的視圖,參數 dim0 是要交換的第一個維度,dim1 是要交換的第二個,代碼如下:
t1 = torch.tensor([[1,2],[3,4]])t2 = t1.transpose(0,1) #交換維度,相當于轉置print(t1)print(t2)
運行結果:
?t2 = t1.transpose(0,1) 表示交換第0維和第1維,第0維表示張量的最外層結構,也就是行方向,第1維就表示列方向,所以交換的是行列方向,就相當于矩陣轉置了
permute(dim0, dim1, ... , dimN):重排所有維度
?permute() 方法自由重新排列所有維度,可一次性交換多個維度,適用于復雜的維度重組(如調整圖像通道順序),代碼如下:
#多維度重排 permutet3 = torch.rand(2,3,4)t4 = t3.permute(2,1,0)print(t4.shape)
運行結果:
兩者的區別在于:
特性 | transpose(dim0, dim1) | permute(dim0, dim1, ..., dimN) |
---|---|---|
交換維度數量 | 只能交換兩個維度 | 可以一次性重排所有維度 |
靈活性 | 低 | 高 |
常用場景 | 矩陣轉置、簡單維度交換 | 復雜的維度重組(如NCHW→NHWC) |
連續性 | 通常輸出不連續 | 通常輸出不連續 |
注意,使用了這兩個方法后輸出的新張量通常不連續,所以后續可能要用?contiguous() 轉換
6、升維降維
?升維和降維是常用的操作,需要掌握:
unsqueeze():升維
?升維操作是在一個指定的位置插入一個大小為 1 的新維度,API為 unsqueeze(dim) ,dim 為指定要增加維度的位置(從 0 開始索引),代碼示例如下:
def test4():'''升維 unsqueeze()'''t1 = torch.rand(2,3,4)t1 = t1.unsqueeze(2)print(t1.shape)
運行結果:
squeeze():降維
?降維是去除不必要的維度,API為 squeeze(dim),dim 是指定要移除的維度。如果指定了 dim,則只移除該維度(前提是該維度大小為 1),如果不指定,則移除所有大小為 1 的維度,代碼示例:
?
'''降維squeeze()'''t2 = torch.tensor([[[1,2,3]]]) #形狀為 (1,1,3)t3 = t2.squeeze() #不指定dimt4 = t2.squeeze(0) #指定dim為0print(t2.shape)print(t3.shape)print(t4.shape)
運行結果:
可以看到,當不指定 dim 時移除了所有大小為1的維度。指定之后只移除了第0維的1。
這篇就到這里,但其實還有很多其他的操作只是我不想寫了[暈],居然寫了8000多字。。下一篇寫啥我也沒想好,先這樣吧(??????)??
以上有問題可以指出