NumPyなしで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 が出てきたら 「これはPyTorchでやっていたことをCPU配列版でやっている」 と理解すれば十分です。

3. それでもNumPyが頻繁に登場する理由: 「エコシステムの共通言語」



ではこの疑問が生まれます。

「PyTorchがもっと強力なのに、全部PyTorchテンソルでやればいいのでは?」

できない理由は一つあります。 Pythonのデータ/科学エコシステムのほとんどが NumPyを基準 に設計されているからです。

3-1. データ前処理段階:NumPyの世界

データを準備するときに頻繁に使うライブラリを見てみると:

  • OpenCV, Pillow → 画像を読み込むときは 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().numpy()CPUに落としてからNumPyへ変換 する必要があります。


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)                   # バッチ次元追加: (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