專題鏈接:https://blog.csdn.net/qq_33345365/category_12591348.html
本教程翻譯自微軟教程:https://learn.microsoft.com/en-us/training/paths/pytorch-fundamentals/
初次編輯:2024/3/1;最后編輯:2024/3/1
這是本教程的第一篇,分為:
- 介紹pytorch基礎
- 張量操作
另外本人還有pytorch CV相關的教程,見專題:
https://blog.csdn.net/qq_33345365/category_12578430.html
介紹
大多數機器學習工作流程都涉及處理數據、創建模型、使用超參數優化模型、保存和推斷已訓練的模型。本模塊介紹在 PyTorch 中實現的完整機器學習(ML)工作流程,PyTorch 是一種流行的 Python ML 框架。
本教程使用 FashionMNIST 數據集來訓練一個神經網絡模型,該模型可以識別圖像,如 T 恤/上衣、褲子、套衫、連衣裙、外套、涼鞋、襯衫、運動鞋、包包或短靴。
在構建模型之前,會展示構建神經網絡模型的關鍵概念。
學習目標:
- 學習如何在 CPU 和 GPU 上使用張量(Tensors)
- 理解如何管理、擴展和規范化數據集
- 使用神經網絡構建圖像識別模型
- 學習如何優化模型
- 學習如何提高模型推理性能
先修要求:
基本的 Python 知識
什么是張量 Tensor
張量
張量是一種專門的數據結構,非常類似于數組和矩陣。PyTorch使用張量來編碼模型的輸入和輸出,以及模型的參數。張量類似于NumPy
數組和ndarrays
,但是張量可以在GPU或其他硬件加速器上運行。事實上,張量和NumPy數組通常可以共享相同的底層內存地址,具有稱為bridge-to-np-label
的功能,這消除了復制數據的需要。張量還針對自動微分進行了優化。
設置基本環境:
import torch
import numpy as np
初始化一個張量
張量可通過多種方式初始化,見如下例子:
1. 直接通過數據初始化
張量可以直接從數據中創建。數據類型是自動推斷的。
data = [[1, 2],[3, 4]]
x_data = torch.tensor(data)
2. 從一個NumPy數組
張量可以從NumPy數組創建,反之亦然。由于numpy格式的np_array
和張量格式x_np
在這里共享相同的內存位置,改變其中一個的值將會改變另一個的值。
np_array = np.array(data)
x_np = torch.from_numpy(np_array)print(f"Numpy np_array value: \n {np_array} \n")
print(f"Tensor x_np value: \n {x_np} \n")np.multiply(np_array, 2, out=np_array)print(f"Numpy np_array after * 2 operation: \n {np_array} \n")
print(f"Tensor x_np value after modifying numpy array: \n {x_np} \n")
輸出是:
Numpy np_array value: [[1 2][3 4]] Tensor x_np value: tensor([[1, 2],[3, 4]], dtype=torch.int32) Numpy np_array after * 2 operation: [[2 4][6 8]] Tensor x_np value after modifying numpy array: tensor([[2, 4],[6, 8]], dtype=torch.int32)
3. 從其他張量
x_ones = torch.ones_like(x_data) # retains the properties of x_data
print(f"Ones Tensor: \n {x_ones} \n")x_rand = torch.rand_like(x_data, dtype=torch.float) # overrides the datatype of x_data
print(f"Random Tensor: \n {x_rand} \n")
輸出是:
Ones Tensor: tensor([[1, 1],[1, 1]]) Random Tensor: tensor([[0.7907, 0.9041],[0.4805, 0.0954]])
4. 使用隨機數或常量
shape
由張量維度的元組定義,它設置了張量的行數和列數。在下面的函數中,shape
確定了輸出張量的維度。
shape = (2,3)
rand_tensor = torch.rand(shape)
ones_tensor = torch.ones(shape)
zeros_tensor = torch.zeros(shape)print(f"Random Tensor: \n {rand_tensor} \n")
print(f"Ones Tensor: \n {ones_tensor} \n")
print(f"Zeros Tensor: \n {zeros_tensor}")
輸出:
Random Tensor: tensor([[0.5893, 0.4485, 0.6525],[0.9083, 0.2913, 0.0752]]) Ones Tensor: tensor([[1., 1., 1.],[1., 1., 1.]]) Zeros Tensor: tensor([[0., 0., 0.],[0., 0., 0.]])
張量的屬性 Attributes of a tensor
張量屬性描述了他們的形狀、數據類型和所處的設備。
tensor = torch.rand(3,4)print(f"Shape of tensor: {tensor.shape}")
print(f"Datatype of tensor: {tensor.dtype}")
print(f"Device tensor is stored on: {tensor.device}")
輸出是:
Shape of tensor: torch.Size([3, 4])
Datatype of tensor: torch.float32
Device tensor is stored on: cpu
有100多個張量操作,包括算術運算、線性代數、矩陣操作(如轉置、索引和切片)。可以在這找到全面的描述。
這些操作中的每一個都可以在GPU上運行(通常比在CPU上速度更快)。
-
CPU的核心數少于100個。核心是執行實際計算的單元。每個核心按順序處理任務(一次處理一個任務)。
-
GPU有數千乃至上萬個核心。GPU核心以并行處理的方式處理計算,任務被分割并在不同核心上處理。這就是為什么在大多數情況下GPU比CPU更快的原因。GPU在處理大數據時表現比小數據更好,因為更有利于并行加速。GPU通常用于圖形或神經網絡的高強度計算。
-
PyTorch可以使用Nvidia CUDA庫來利用其GPU卡。
GPU并不總是優于CPU,因為CPU的單核性能強,因此在處理小數據時可能更占優勢。
默認情況下,張量在CPU上創建。張量也可以移動到GPU;為此,需要使用.to
方法將它們移動(在檢查GPU可用性后)。當然,由于CPU和GPU之間的傳輸跨設備,這意味著傳輸上的巨大開銷。
# 如果GPU可用則將tensor移動到GPU上,GPU可用需要保證1. CUDA安裝;2. 安裝對應CUDA版本的pytorch,見https://pytorch.org/,選擇合適的pytorch安裝方式
if torch.cuda.is_available():tensor = tensor.to('cuda')
類似numpy的標準索引和切片 Standard numpy-like indexing and slicing
tensor = torch.ones(4, 4)
print('First row: ',tensor[0])
print('First column: ', tensor[:, 0])
print('Last column:', tensor[..., -1])
tensor[:,1] = 0
print(tensor)
輸出是:
First row: tensor([1., 1., 1., 1.])
First column: tensor([1., 1., 1., 1.])
Last column: tensor([1., 1., 1., 1.])
tensor([[1., 0., 1., 1.],[1., 0., 1., 1.],[1., 0., 1., 1.],[1., 0., 1., 1.]])
拼接張量
可以使用 torch.cat
函數沿著指定維度連接一個張量序列。torch.stack
是一個相關的張量拼接方法,它沿著新的維度將一個張量序列堆疊起來。以下例子,維度有四個可選值:
t1 = torch.cat([tensor, tensor, tensor], dim=1)
print(t1)
dim=1;輸出:
tensor([[1., 0., 1., 1., 1., 0., 1., 1., 1., 0., 1., 1.],[1., 0., 1., 1., 1., 0., 1., 1., 1., 0., 1., 1.],[1., 0., 1., 1., 1., 0., 1., 1., 1., 0., 1., 1.],[1., 0., 1., 1., 1., 0., 1., 1., 1., 0., 1., 1.]])
dim=0,輸出:
tensor([[1., 0., 1., 1.],[1., 0., 1., 1.],[1., 0., 1., 1.],[1., 0., 1., 1.],[1., 0., 1., 1.],[1., 0., 1., 1.],[1., 0., 1., 1.],[1., 0., 1., 1.],[1., 0., 1., 1.],[1., 0., 1., 1.],[1., 0., 1., 1.],[1., 0., 1., 1.]])
dim=-1,輸出:
tensor([[1., 0., 1., 1., 1., 0., 1., 1., 1., 0., 1., 1.],[1., 0., 1., 1., 1., 0., 1., 1., 1., 0., 1., 1.],[1., 0., 1., 1., 1., 0., 1., 1., 1., 0., 1., 1.],[1., 0., 1., 1., 1., 0., 1., 1., 1., 0., 1., 1.]])
dim=-2,輸出:
tensor([[1., 0., 1., 1.],[1., 0., 1., 1.],[1., 0., 1., 1.],[1., 0., 1., 1.],[1., 0., 1., 1.],[1., 0., 1., 1.],[1., 0., 1., 1.],[1., 0., 1., 1.],[1., 0., 1., 1.],[1., 0., 1., 1.],[1., 0., 1., 1.],[1., 0., 1., 1.]])
數學操作:
# 這計算了兩個張量之間的矩陣乘法。Y1 y2 y3的值是一樣的
y1 = tensor @ tensor.T
y2 = tensor.matmul(tensor.T)y3 = torch.rand_like(tensor)
torch.matmul(tensor, tensor.T, out=y3)# 它計算元素級乘積,Z1 z2 z3的值是一樣的
z1 = tensor * tensor
z2 = tensor.mul(tensor)z3 = torch.rand_like(tensor)
torch.mul(tensor, tensor, out=z3)print(y3)
print(z3)
輸出:
tensor([[3., 3., 3., 3.],[3., 3., 3., 3.],[3., 3., 3., 3.],[3., 3., 3., 3.]])
tensor([[1., 0., 1., 1.],[1., 0., 1., 1.],[1., 0., 1., 1.],[1., 0., 1., 1.]])
單元素張量
如果您有一個單元素張量,例如,通過將張量的所有值聚合為一個值,您可以使用item()將其轉換為Python數值:
agg = tensor.sum()
agg_item = agg.item()
print(agg_item, type(agg_item))
輸出:
12.0 <class 'float'>
原地操作 in-place operations
將結果存儲到操作數(operand)中的操作稱為原地操作。它們通常用下劃線_
作為后綴。例如:x.copy_(y)
,x.t_(),會改變x
的值。
注意: 就地操作可以節省一些內存,但在計算導數時可能會出現問題,因為它們會立即丟失歷史記錄。因此,不建議使用它們。
print(tensor, "\n")
tensor.add_(5)
print(tensor)
輸出:
tensor([[1., 0., 1., 1.],[1., 0., 1., 1.],[1., 0., 1., 1.],[1., 0., 1., 1.]]) tensor([[6., 5., 6., 6.],[6., 5., 6., 6.],[6., 5., 6., 6.],[6., 5., 6., 6.]])
與Numpy橋接 Bridge with Numpy
NumPy數組和CPU上的張量可以共享它們的底層內存位置,改變其中一個將改變另一個。
張量到NumPy數組的轉換
t = torch.ones(5)
print(f"t: {t}")
n = t.numpy()
print(f"n: {n}")
t.add_(1)
print(f"t: {t}")
print(f"n: {n}")
輸出:
t: tensor([1., 1., 1., 1., 1.])
n: [1. 1. 1. 1. 1.]
t: tensor([2., 2., 2., 2., 2.])
n: [2. 2. 2. 2. 2.]
NumPy數組到張量的轉換
n = np.ones(5)
t = torch.from_numpy(n)
np.add(n, 1, out=n)
print(f"t: {t}")
print(f"n: {n}")
輸出:
t: tensor([2., 2., 2., 2., 2.], dtype=torch.float64)
n: [2. 2. 2. 2. 2.]
代碼匯總
import torch
import numpy as npdata = [[1, 2], [3, 4]]
x_data = torch.tensor(data)np_array = np.array(data)
x_np = torch.from_numpy(np_array)print(f"Numpy np_array value: \n {np_array} \n")
print(f"Tensor x_np value: \n {x_np} \n")np.multiply(np_array, 2, out=np_array)print(f"Numpy np_array after * 2 operation: \n {np_array} \n")
print(f"Tensor x_np value after modifying numpy array: \n {x_np} \n")# 從其他張量
x_ones = torch.ones_like(x_data) # retains the properties of x_data
print(f"Ones Tensor: \n {x_ones} \n")x_rand = torch.rand_like(x_data, dtype=torch.float) # overrides the datatype of x_data
print(f"Random Tensor: \n {x_rand} \n")# random and constant
shape = (2, 3)
rand_tensor = torch.rand(shape)
ones_tensor = torch.ones(shape)
zeros_tensor = torch.zeros(shape)print(f"Random Tensor: \n {rand_tensor} \n")
print(f"Ones Tensor: \n {ones_tensor} \n")
print(f"Zeros Tensor: \n {zeros_tensor}")# attributes
tensor = torch.rand(3, 4)print(f"Shape of tensor: {tensor.shape}")
print(f"Datatype of tensor: {tensor.dtype}")
print(f"Device tensor is stored on: {tensor.device}")if torch.cuda.is_available():tensor = tensor.to('cuda')tensor = torch.ones(4, 4)
print('First row: ', tensor[0])
print('First column: ', tensor[:, 0])
print('Last column:', tensor[..., -1])
tensor[:, 1] = 0
print(tensor)t1 = torch.cat([tensor, tensor, tensor], dim=-1)
print(t1)# This computes the matrix multiplication between two tensors. y1, y2, y3 will have the same value
y1 = tensor @ tensor.T
y2 = tensor.matmul(tensor.T)y3 = torch.rand_like(tensor)
torch.matmul(tensor, tensor.T, out=y3)# This computes the element-wise product. z1, z2, z3 will have the same value
z1 = tensor * tensor
z2 = tensor.mul(tensor)z3 = torch.rand_like(tensor)
torch.mul(tensor, tensor, out=z3)print(y3)
print(z3)agg = tensor.sum()
agg_item = agg.item()
print(agg_item, type(agg_item))print(tensor, "\n")
tensor.add_(5)
print(tensor)t = torch.ones(5)
print(f"t: {t}")
n = t.numpy()
print(f"n: {n}")
t.add_(1)
print(f"t: {t}")
print(f"n: {n}")n = np.ones(5)
t = torch.from_numpy(n)
np.add(n, 1, out=n)
print(f"t: {t}")
print(f"n: {n}")