PyTorch學習(1):張量(Tensor)核心操作詳解
一、張量(Tensor)核心操作詳解
張量是PyTorch的基礎數據結構,類似于NumPy的ndarray,但支持GPU加速和自動微分。
1. 張量創建與基礎屬性
import torch# 創建張量
a = torch.tensor([1, 2, 3]) # 從列表中創建
print(a)
b = torch.zeros(2, 3) # 2*3零矩陣
print(b)
c = torch.ones_like(b) # 與b同形的全1矩陣
print(c)
d = torch.rand(3, 4) # 3*4隨機矩陣
print(d)
e = torch.arange(0, 10, 2) # [0,2,4,6,8]
print(e)# 關鍵屬性查看
print(f"形狀: {d.shape}") # torch.Size([3, 4])
print(f"數據類型:{d.dtype}") # torch.float32
print(f"存儲設備: {d.device}") # cpu 或 cuda:0
2. 張量運算與廣播機制
x = torch.tensor([[1, 2], [3, 4]])
y = torch.tensor([[5], [6]])
print(x)
print(y)# 基本計算
add =x + y # 廣播加法:[[6,7], [9,10]]
mul =x * 2 # 標量乘法:[[2,4], [6,8]]
print(add)
print(mul)# 高級運算
sum_x =torch.sum(x,dim=0) # 沿列求和:[4,6]
max_val,max_idx=torch.max(x,dim=1) # 每行最大值和索引
exp_x =torch.exp(x) # 指數運算
print(sum_x)
print(max_val,max_idx)
print(exp_x)
3. 形狀操作與內存管理
z = torch.arange(12)
print(z)# 形狀變換(不復制數據)
z_view =z.view(3,4) # 視圖變形,3*4矩陣
print(z_view)
z_reshape = z.reshape(2,6)
print(z_reshape)# 內部復制
z_clone = z.clone() # 顯性復制數據
z_transpone =z_view.T # 轉置(共享數據)
print(z_clone)
print(z_transpone)# 維度操作
print(f"原始形狀:{z.shape}")
unsqueezed = z.unsqueeze(0) # 增加維度:形狀從(12,)變成(1,12)
print(f"增加維度后的形狀:{unsqueezed.shape}")
print(unsqueezed)squeezed = unsqueezed.squeeze(0) # 壓縮維度:shape(12)
print(f"壓縮維度后的形狀:{squeezed.shape}")
4. 與NumPy互操作
import numpy as np# Tensor → NumPy
tensor = torch.rand(2,3)
print (tensor)
numpy_array = tensor.numpy() # 共享內存(CPU張量)
print (numpy_array)# NumPy → Tensor
np_arr = np.array([[1,2],[3,4]])
print (np_arr)
new_tensor = torch.from_numpy(np_arr) # 共享內存
print (new_tensor)# 顯式內存復制
safe_tensor = torch.tensor(np_arr)
print (safe_tensor)
5、檢查設備
# 檢測GPU可用性
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
二、動態計算圖與自動微分(Autograd)
1. 計算圖基本原理
PyTorch使用動態計算圖,運算時實時構建計算圖,示例1:
# 創建需要跟蹤梯度的張量
x = torch.tensor(2.0,requires_grad=True)
print(x)# 定義一個簡單的函數 y = x^2
y = x ** 2# 計算 y 關于 x 的梯度
y.backward() # 執行反向傳播# 查看梯度值 dy/dx = 2x = 2*2 = 4
print(x.grad) # 輸出: tensor(4.)
-
torch.tensor(2.0)
創建一個值為 2.0 的標量張量(即單元素張量),數據類型默認是torch.float32
。 -
requires_grad=True
這是一個關鍵參數,它告訴 PyTorch 需要跟蹤這個張量的所有操作,以便之后進行自動求導。設置為True
后,PyTorch 會構建一個計算圖,記錄所有依賴于x
的操作,從而在需要時通過反向傳播(backpropagation)自動計算梯度。
在這個例子中,由于x
的requires_grad=True
,PyTorch 記錄了y = x2
的計算過程,并在調用y.backward()
時自動計算了梯度dy/dx
,結果存儲在x.grad
中。
為什么需要這樣做?
在深度學習中,梯度是優化模型參數的關鍵。例如,在神經網絡訓練時,我們需要計算損失函數對模型參數的梯度,以便使用梯度下降等優化算法更新參數。通過將requires_grad
設置為True
,PyTorch 會自動記錄所有對該張量的操作,從而在調用backward()
方法時計算梯度。
這種自動計算梯度的能力是深度學習框架(如 PyTorch、TensorFlow)的核心優勢之一,它讓我們無需手動推導復雜模型的導數公式,就能高效訓練神經網絡。
PyTorch使用動態計算圖,運算時實時構建計算圖,示例2:
# 創建需要跟蹤梯度的張量
x = torch.tensor(2.0, requires_grad=True)
# 定義一個函數 y = x**3 + 2*x + 1
y = x**3 + 2*x + 1
# 再進行函數
z = torch.sin(y)z.backward() # 反向傳播自動計算梯度print(x.grad) # dz/dx = dz/dy * dy/dx = cos(y)*(3x2+2)
2. 梯度計算模式
這是更靈活的顯式梯度計算方式,適用于復雜場景
# 示例:計算偏導數
u = torch.tensor(1.0, requires_grad=True)
v = torch.tensor(2.0, requires_grad=True)
f = u**2 + 3*v# 計算梯度
grads = torch.autograd.grad(f, [u, v]) # (df/du, df/dv)
print(grads) # (tensor(2.), tensor(3.))
requires_grad=True
?告訴 PyTorch 跟蹤這些張量的所有操作,以便后續計算梯度。- \(u = 1.0\)?和?\(v = 2.0\)?是初始值,用于計算梯度的具體數值。
定義了一個二元函數?\(f(u, v) = u^2 + 3v\)。在深度學習中,這類似一個損失函數,我們需要計算它對參數的梯度。
?
核心區別
操作 | 用途 | 結果存儲 | 適用場景 |
---|---|---|---|
z.backward() | 計算?z ?對所有?requires_grad=True ?的葉子節點張量的梯度,并累積到?.grad ?屬性中。 | 梯度存儲在葉子節點的?.grad ?屬性中。 | 標準的反向傳播(如訓練神經網絡)。 |
torch.autograd.grad(f, [u, v]) | 顯式計算?f ?對指定張量?[u, v] ?的梯度,返回梯度張量組成的元組。 | 梯度作為元組直接返回,不修改原有張量。 | 需要自定義梯度計算路徑(如多輸出模型) |
3. 梯度控制技巧
import torch
import torch.optim as optim# 初始化參數
x = torch.tensor(2.0, requires_grad=True)
w = torch.tensor(3.0, requires_grad=True)
b = torch.tensor(1.0, requires_grad=True)# 創建優化器
optimizer = optim.SGD([x, w, b], lr=0.01)# 1. 梯度累積清零
optimizer.zero_grad() # 訓練循環中必須的操作
print(f"初始梯度 x.grad: {x.grad}") # 輸出: None# 前向傳播
y = x * w + b
loss = (y - 10)**2 # 假設目標值為10# 反向傳播
loss.backward()
print(f"第一次反向傳播后 x.grad: {x.grad}") # 輸出: tensor(12.)# 再次反向傳播(梯度會累積)
loss.backward()
print(f"第二次反向傳播后 x.grad: {x.grad}") # 輸出: tensor(24.)# 梯度清零
optimizer.zero_grad()
print(f"optimizer.zero_grad() 后 x.grad: {x.grad}") # 輸出: tensor(0.)# 2. 凍結梯度
with torch.no_grad():z = x * 2 # 不會追蹤計算歷史
print(f"z.requires_grad: {z.requires_grad}") # 輸出: False# 3. 分離計算圖
a = x * w
detached = a.detach() # 創建無梯度關聯的副本
print(f"detached.requires_grad: {detached.requires_grad}") # 輸出: False# 4. 修改requires_grad
x.requires_grad_(False) # 關閉梯度追蹤
print(f"修改后 x.requires_grad: {x.requires_grad}") # 輸出: False# 驗證修改后的效果
try:y = x ** 2y.backward() # 會報錯,因為x.requires_grad=False
except RuntimeError as e:print(f"錯誤: {e}") # 輸出: element 0 of tensors does not require grad and does not have a grad_fn
總結對比表
optimizer.zero_grad() # 訓練循環中必須的操作
torch.no_grad() # 梯度清零
操作 | 作用范圍 | 是否影響原張量 | 是否釋放內存 | 典型場景 |
---|---|---|---|---|
optimizer.zero_grad() | 優化器管理的所有參數 | 是(梯度清零) | 否 | 每個訓練迭代前 |
torch.no_grad() | 上下文內的所有計算 | 否 | 是 | 推理階段、無需梯度的計算 |
a.detach() | 單個張量 | 否(創建新張量) | 否 | 截斷梯度流、固定部分網絡 |
x.requires_grad_(False) | 單個張量 | 是(原地修改) | 否 | 凍結預訓練模型參數 |
代碼說明:
-
梯度累積清零:
- 兩次調用?
loss.backward()
?會累積梯度 optimizer.zero_grad()
?將梯度重置為零
- 兩次調用?
-
凍結梯度:
with torch.no_grad()
?塊內的計算不會追蹤梯度- 輸出張量?
z
?的?requires_grad
?為?False
-
分離計算圖:
a.detach()
?創建與計算圖分離的新張量- 修改分離后的張量不會影響原梯度計算
-
修改 requires_grad:
x.requires_grad_(False)
?原地修改張量屬性- 后續嘗試對?
x
?進行反向傳播會報錯
注意事項:
- 實際訓練中,
optimizer.zero_grad()
?應在每個訓練迭代開始時調用 torch.no_grad()
?常用于推理階段以提高性能detach()
?適用于需要固定部分網絡參數的場景- 修改?
requires_grad
?是永久性的,需謹慎操作
4. 梯度檢查(調試技巧)
import torch
def grad_check():x = torch.tensor(3.0, requires_grad=True)y = x ** 2# 理論梯度y.backward()analytic_grad = x.grad.item()# 數值梯度(修正:使用 x.item() 而非硬編碼 3.0)eps = 1e-5y1 = (x.item() + eps) ** 2y2 = (x.item() - eps) ** 2numeric_grad = (y1 - y2) / (2 * eps)print(f"解析梯度: {analytic_grad:.6f}, 數值梯度: {numeric_grad:.6f}")assert abs(analytic_grad - numeric_grad) < 1e-6# 調用函數
grad_check()
這段代碼實現了一個簡單的 ** 梯度驗證(Gradient Check)** 功能,用于驗證 PyTorch 自動微分計算的梯度是否與數值計算的梯度一致。
1. 定義測試函數和輸入
x = torch.tensor(3.0, requires_grad=True)
y = x ** 2
2. 計算解析梯度(Analytical Gradient)
y.backward()
analytic_grad = x.grad.item()
3. 計算數值梯度(Numerical Gradient)
eps = 1e-5
y1 = (x.item() + eps) ** 2
y2 = (x.item() - eps) ** 2
numeric_grad = (y1 - y2) / (2 * eps)
4. 驗證結果
print(f"解析梯度: {analytic_grad:.6f}, 數值梯度: {numeric_grad:.6f}")
assert abs(analytic_grad - numeric_grad) < 1e-6
打印結果:解析梯度: 6.000000, 數值梯度: 6.000000
關鍵知識點
-
自動微分(Autograd): PyTorch 通過跟蹤計算圖自動計算梯度,無需手動推導公式。
-
數值微分: 數值方法是驗證梯度計算正確性的重要工具,但計算效率低,僅用于測試。
-
梯度驗證的重要性: 在實現復雜模型(如自定義層或損失函數)時,梯度驗證能幫助發現反向傳播中的錯誤。
潛在問題與改進
-
步長?\(\epsilon\)?的選擇: 太小會導致數值誤差(如浮點數舍入),太大會降低近似精度。
-
多維張量支持: 當前代碼僅支持標量輸入,擴展到多維張量需為每個元素計算數值梯度。
-
高階導數驗證: 對于二階或更高階導數,需使用更復雜的數值方法。
這個簡單的驗證函數是深度學習開發中的重要調試工具,特別是在實現自定義操作或優化算法時。