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.shape→x_torch.shapenp.reshape(x, ...)→x_torch.view(...)またはx_torch.reshape(...)
そのため、PyTorchのテンソル演算を習得すると、自然にNumPyスタイルの思考と構文 も身につきます。
2. 「NumPyを先に学ぶべきか?」という現実的な答え
多くのチュートリアルはNumPyから始めるため、混乱しやすいですが、実務的な観点から答えはこうです。
NumPyを先に学ぶ必要はありません。PyTorchがテンソル演算からすぐに始められます。
理由は簡単です。
- テンソルの形状/ブロードキャスト/インデクシングなどの概念は NumPyとPyTorchがほぼ同じ に動作します。
- ディープラーニングで実際に多く使うのは 「ディープラーニングモデル用のテンソル演算 + 自動微分」で、 これは結局PyTorch側にあります。
したがって ディープラーニングが目的なら:
- PyTorchのテンソル操作を先に習得
- コード中に
np.xxxが出てきたら 「これはPyTorchでやっていたことをCPU配列版でやっている」 と理解すれば十分です。
3. それでもNumPyが頻繁に登場する理由: 「エコシステムの共通言語」
ではこの疑問が生まれます。
「PyTorchがもっと強力なのに、全部PyTorchテンソルでやればいいのでは?」
できない理由は一つあります。 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().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. 最終整理: こう理解すると楽になる
まとめると、以下の観点で十分です。
- PyTorchを学ぶことは「未来型NumPy」を学ぶことに似ている。 テンソル操作とブロードキャスト感覚を身につければ、実質的にNumPy感覚です。
- NumPyを別途深く掘り下げる必要はない。
ディープラーニングが目的ならPyTorch中心で学び、
中間に出てくる
np.xxxは「CPU配列版のテンソル操作」として理解すれば十分です。 - ただし実務では
* データ前処理 / 後処理 / 可視化 / 統計計算のために
NumPyを完全に排除できない。
* 特に PyTorch ↔ NumPy変換コード (
.numpy(),torch.from_numpy,.cpu()) は よく見かけるので慣れておく必要があります。 - システム観点では * 両者は メモリを共有(Zero-Copy) できるよう設計されているため 大規模データでも効率的に往復できます。
ディープラーニングコードを見るときは、こう考えてください。
- テンソルがGPUに上がり、微分が必要なら PyTorch領域
- 周辺ライブラリやデータをやり取りしたり、単純数値/統計/可視化なら NumPy領域
この二つの世界を自然に往復することが、現代Pythonディープラーニング開発者の基本スキルセットと言えるでしょう。

コメントはありません。