只用 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.shapex_torch.shape
  • np.reshape(x, ...)x_torch.view(...)x_torch.reshape(...)

因此,当你学习 PyTorch 的张量运算时,自然也会同时掌握 NumPy 的思维方式和语法


2. “先学 NumPy 吗?”的现实答案

许多教程先从 NumPy 开始,容易让人产生误解。实务角度来看,答案是:

不必先学 NumPy。PyTorch 可以直接从张量运算开始学习。

原因很简单。

  • 张量的形状/广播/索引等概念 NumPy 与 PyTorch 几乎完全相同
  • 在深度学习中,我们真正需要的是 “深度学习模型的张量运算 + 自动微分”, 这完全在 PyTorch 里完成。

所以,如果你的目标是深度学习:

  1. 先掌握 PyTorch 的张量操作
  2. 当代码中出现 np.xxx 时 只需理解为“在 CPU 上的张量操作”即可。

3. 但为什么 NumPy 仍然频繁出现?——“生态系统的通用语言”



这就产生了疑问:

“PyTorch 更强大,为什么不全部用 PyTorch 张量?”

原因是:

Python 数据/科学生态的大多数库都是以 NumPy 为基准设计的。

3-1. 数据预处理阶段:NumPy 的世界

常用的库:

  • OpenCVPillow → 读取图像时通常返回 NumPy 数组
  • pandasDataFrame.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. 最终总结:这样理解更轻松

总结如下:

  1. 学习 PyTorch 就是学习“未来版 NumPy”。 掌握张量操作和广播感知,实际上就是掌握 NumPy 的思维。
  2. 不必深入学习 NumPy。 如果目标是深度学习,先学 PyTorch,遇到 np.xxx 时只需把它视为“CPU 版张量操作”。
  3. 但在实际工作中: * 数据预处理 / 后处理 / 可视化 / 统计计算 * 需要使用 NumPy * 关键是熟悉 PyTorch ↔ NumPy 的转换代码(.numpy()torch.from_numpy.cpu())。
  4. 从系统角度看: * 两者可以共享内存(Zero‑Copy),在大数据场景下高效切换。

当你看到深度学习代码时,记住:

  • 张量在 GPU 上并且需要微分 → PyTorch 区域
  • 与周围库交互、数据处理、可视化 → NumPy 区域

这两者自然流畅地往返,就是现代 Python 深度学习开发者的基本技能。

image