只用 PyTorch 就行嗎?
對於深度學習開發者來說,這個關係往往會讓人困惑。
在許多微調程式碼中,總會同時出現兩個名字:
import numpy as np
import torch
「不,所有張量運算都在 PyTorch 裡完成…為什麼還需要 NumPy?」 「兩者概念重疊,還是必須單獨學習 NumPy?」
我也曾好奇這個問題,並在一個清晰的回答基礎上整理了以下內容。
1. NumPy 與 PyTorch,究竟有什麼不同?
粗略來說,PyTorch 可以被視為:
「GPU 上的 NumPy + 自動微分(autograd)」
拆解角色:
- NumPy
- 在 CPU 上運行的陣列/矩陣庫
- 數值計算、線性代數、統計運算的「Python 標準」
-
以
ndarray為核心資料結構 -
PyTorch
- 與 NumPy 風格相近的 API
- 不僅能在 CPU 上運行,還能在 GPU(CUDA)上運行
- 支援張量運算的自動微分 → 可進行深度學習訓練
PyTorch 的開發者有意地模仿了 NumPy 的語法:
np.array([...])→torch.tensor([...])x_np.shape→x_torch.shapenp.reshape(x, ...)→x_torch.view(...)或x_torch.reshape(...)
因此,學習 PyTorch 的張量運算時,自然也會同時習得 NumPy 的思維與語法。
2. 「先學 NumPy 再學 PyTorch」的實際答案
許多教學會先從 NumPy 開始,容易讓人混淆。從實務角度來看,答案是:
不必先學 NumPy,直接從 PyTorch 的張量運算開始即可。
原因很簡單:
- 張量的形狀、廣播、索引等概念在 NumPy 與 PyTorch 中幾乎相同。
- 在深度學習中,我們真正需要的是「張量運算 + 自動微分」,這完全在 PyTorch 內部完成。
所以,如果你的目標是深度學習:
- 先熟悉 PyTorch 的張量操作
- 當程式碼中出現
np.xxx時,理解為「CPU 陣列版本的張量操作」即可。
3. 為什麼 NumPy 還會頻繁出現?——「生態系統的通用語言」
這裡會產生疑問:
「PyTorch 更強大,為什麼還要用 NumPy?」
原因是:Python 數據/科學生態系統大多以 NumPy 為基礎設計。
3-1. 數據預處理階段:NumPy 的世界
常用的庫:
OpenCV、Pillow→ 讀取圖像時通常返回 NumPy 陣列pandas→DataFrame.values/to_numpy()等使用 NumPy 陣列- 各種統計/數值套件 → 以 NumPy 為輸入/輸出標準
也就是說,「送進模型之前的數據操作」大多使用 NumPy。
3-2. 模型訓練/推論階段:PyTorch 的世界
import numpy as np
import torch
x_np = ... # 預處理後的 NumPy 陣列
x_tensor = torch.from_numpy(x_np) # 轉成張量
x_tensor = x_tensor.to("cuda") # 移到 GPU
out = model(x_tensor)
從這裡開始,就是 PyTorch 的張量領域。
3-3. 視覺化・儲存・後處理階段:再次回到 NumPy
模型預測結果:
- 用
matplotlib畫圖 - 再放回
pandas儲存為 CSV - 做簡單統計
這些庫大多以 NumPy 陣列 為基礎運作。
因此,往往需要將張量轉回 NumPy:
out_cpu = out.detach().cpu().numpy()
常見錯誤:
TypeError: can't convert cuda:0 device type tensor to numpy
這是因為 GPU 張量不能直接轉成 NumPy,必須先 .cpu()。
4. 系統層面的關鍵點:Zero‑Copy 共享
從更低層面來看,有一個有趣的點:
x_np = np.random.randn(3, 3)
x_tensor = torch.from_numpy(x_np) # 這裡!
實際上並未複製資料。
torch.from_numpy(...)會共享 NumPy 使用的記憶體。- 因此,即使是巨大的陣列,不會額外佔用記憶體。
相反:
x_np2 = x_tensor.numpy()
同樣(若是 CPU 張量)會得到共享同一記憶體的 NumPy 陣列。
示例:
x_np = np.zeros((2, 2))
x_tensor = torch.from_numpy(x_np)
x_tensor[0, 0] = 1
print(x_np)
# [[1. 0.]
# [0. 0.]] ← NumPy 陣列也跟著改變
兩者就像「同一扇窗」看同一個資料。
總結: * PyTorch 與 NumPy 可以在 CPU 上共享記憶體。 * 這使得「預處理(NumPy)↔訓練(PyTorch)↔後處理(NumPy)」能在不產生大量複製成本的情況下流暢切換。
5. 一個完整流程的簡易範例
假設一個極簡的圖像分類例子:
import cv2
import numpy as np
import torch
# 1. 圖像載入 & NumPy 預處理(NumPy 區域)
img = cv2.imread("cat.png") # shape: (H, W, C), BGR, NumPy array
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
img = cv2.resize(img, (224, 224))
img = img.astype(np.float32) / 255.0
img = np.transpose(img, (2, 0, 1)) # (C, H, W)
# 2. NumPy → PyTorch 張量轉換 & GPU 移動(PyTorch 區域)
x = torch.from_numpy(img) # shape: (3, 224, 224)
x = x.unsqueeze(0) # 加 batch 維度: (1, 3, 224, 224)
x = x.to("cuda")
# 3. 模型推論
with torch.no_grad():
logits = model(x)
probs = torch.softmax(logits, dim=1)
# 4. 再轉回 NumPy 進行後處理/視覺化(NumPy 區域)
probs_np = probs.cpu().numpy()
pred_class = np.argmax(probs_np, axis=1)[0]
從程式碼流程可見:
- 輸入/輸出邊界使用 NumPy
- 中間訓練/推論使用 PyTorch
6. 最終總結:這樣理解就夠了
總結如下:
- 學習 PyTorch 就是學習「未來版的 NumPy」。掌握張量操作與廣播感覺,實際上已經掌握了 NumPy 的核心概念。
- 不需要深入學習 NumPy。若目標是深度學習,先學 PyTorch,遇到
np.xxx時只需把它視為「CPU 陣列版的張量操作」。 - 但在實務中,數據預處理/後處理/視覺化仍離不開 NumPy,尤其是與
pandas、matplotlib等庫交互。 - PyTorch 與 NumPy 能共享記憶體(Zero‑Copy),使得大規模資料在兩者之間高效切換。
當你看到深度學習程式碼時,這樣思考即可:
- 張量在 GPU 上、需要微分 → PyTorch
- 與其他庫交互、簡單數值/統計/視覺化 → NumPy
這兩個世界自然流暢地往返,成為現代 Python 深度學習開發者的基本技能。

目前沒有評論。