NumPy 없이 PyTorch만 해도 되나요?

딥러닝 개발자라면 한 번쯤 헷갈렸던 그 관계 정리

파인튜닝 코드들을 보다 보면 항상 같이 등장하는 두 이름이 있습니다.

 import numpy as np
 import torch

“아니, 텐서 연산은 파이토치에 다 있는데… 굳이 넘파이(NumPy)가 왜 필요하지?” “둘이 컨셉이 겹치는 것 같은데, 넘파이를 따로 공부해야 하나?”

저도 정확히 이 부분이 궁금했고, 그걸 깔끔하게 설명해 준 답변을 바탕으로 내용을 정리해 봤습니다.


1. NumPy와 PyTorch, 도대체 뭐가 다른가?



아주 거칠게 말하면 파이토치는:

“GPU에 올라간 NumPy + 자동 미분(autograd)”

이라고 볼 수 있습니다.

역할을 나눠보면:

  • NumPy

  • CPU 위에서 돌아가는 배열/행렬 라이브러리

  • 수치 계산, 선형대수, 통계 연산의 사실상 “파이썬 표준”
  • ndarray라는 자료구조를 중심으로 동작

  • PyTorch

  • NumPy와 거의 같은 스타일의 API

  • 하지만 CPU뿐 아니라 GPU(CUDA) 에도 올라갈 수 있고
  • 텐서 연산에 대해 자동 미분을 지원 → 딥러닝 학습이 가능

파이토치 개발자들이 아예 의도적으로 NumPy 문법을 거의 베꼈습니다.

  • np.array([...])torch.tensor([...])
  • x_np.shapex_torch.shape
  • np.reshape(x, ...)x_torch.view(...) 또는 x_torch.reshape(...)

그래서 파이토치의 텐서 연산을 익히다 보면, 자연스럽게 NumPy 스타일의 사고방식과 문법까지 함께 익히게 됩니다.


2. “NumPy 먼저 공부해야 하나요?”에 대한 현실적인 답

많은 튜토리얼이 NumPy부터 시작해서 헷갈리기 쉬운데, 실무적인 관점에서 답을 내리면 이렇습니다.

NumPy를 각 잡고 먼저 공부할 필요까지는 없습니다. PyTorch가 텐서 연산부터 바로 시작해도 충분합니다.

이유는 간단합니다.

  • 텐서의 모양/브로드캐스팅/인덱싱 같은 개념은 NumPy와 PyTorch가 거의 똑같이 동작합니다.
  • 딥러닝을 할 때 우리가 진짜로 많이 쓰는 건 “딥러닝 모델을 위한 텐서 연산 + 자동 미분”이고, 이건 어차피 파이토치 쪽에만 있습니다.

그래서 딥러닝이 목적이라면:

  1. 파이토치 텐서 조작법을 먼저 익히고
  2. 코드 중간에 np.xxx가 보일 때 “아, 이건 파이토치에서 하던 그거를 CPU 배열 버전으로 쓰는구나” 정도로 이해해도 충분합니다.

3. 그런데도 NumPy의가 계속 보이는 이유: “생태계의 공용어”



그럼 이런 의문이 듭니다.

“파이토치가 더 강력한데, 그냥 다 파이토치 텐서로 하면 안 되나?”

안 되는 이유가 하나 있습니다. 파이썬 데이터/과학 생태계의 대부분이 넘파이를 기준으로 설계되어 있기 때문입니다.

3-1. 데이터 전처리 단계: NumPy의 세상

데이터를 준비할 때 자주 쓰는 라이브러리들을 보면:

  • OpenCV, Pillow → 이미지 읽으면 보통 NumPy 배열로 반환
  • pandasDataFrame.values / to_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)

이 구간부터는 파이토치의 텐서 월드입니다.

3-3. 시각화·저장·후처리 단계: 다시 NumPy의 세상

모델이 예측한 결과를:

  • matplotlib로 그래프 그리기
  • 다시 pandas에 넣어서 CSV로 저장
  • 간단한 통계 계산

이런 걸 하려면 이 라이브러리들도 대부분 넘파이 배열을 기준으로 동작합니다.

그래서 다시 텐서를 넘파이로 바꿔야 하죠.

out_cpu = out.detach().cpu().numpy()

여기서 자주 보는 에러가 바로 이거입니다:

TypeError: can't convert cuda:0 device type tensor to numpy

GPU에 있는 텐서를 바로 .numpy()로 바꾸려 하면 생기고, 반드시 .cpu().numpy()CPU로 내린 뒤 넘파이로 변환해야 합니다.


4. 시스템 관점에서 중요한 포인트: “Zero-Copy” 공유

조금 더 저수준 관점에서 재밌는 포인트가 하나 있습니다.

x_np = np.random.randn(3, 3)
x_tensor = torch.from_numpy(x_np)  # 여기!

여기서 실제로는 데이터가 복사되지 않습니다.

  • torch.from_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.]]  ← 넘파이 배열도 같이 바뀜

둘이 서로의 “창문”처럼 같은 데이터를 보는 셈이죠.

정리하면:

  • 파이토치와 넘파이는 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. 파이토치를 공부하는 것은 “미래형 넘파이”를 배우는 것과 비슷하다. 텐서 조작법과 브로드캐스팅 감각을 익히면, 그게 사실상 넘파이 감각이다.
  2. 넘파이를 별도로 깊게 파고들 필요는 없다. 딥러닝이 목표라면 파이토치 중심으로 공부하고, 중간에 보이는 np.xxx는 “CPU 배열 버전의 텐서 조작” 정도로 이해하면 된다.
  3. 다만, 실무에서는
  • 데이터 전처리 / 후처리 / 시각화 / 통계 계산 때문에 넘파이를 완전히 피할 수는 없다.
  • 특히 PyTorch ↔ NumPy 변환 코드 (.numpy(), torch.from_numpy, .cpu())는 자주 보게 되니 이 부분은 익숙해져야 한다. 4. 시스템 관점에서는

  • 둘이 메모리를 공유(Zero-Copy) 할 수 있게 설계되어 있어 대규모 데이터에서도 효율적으로 오갈 수 있다.

딥러닝 코드를 볼 때 이제 이렇게 생각하시면 됩니다.

  • 텐서가 GPU에 올라가 있고, 미분이 필요하다 → PyTorch의 영역
  • 주변 라이브러리와 데이터를 주고받거나, 단순 수치/통계/시각화 → NumPy의 영역

이 두 세계를 자연스럽게 오가는 게, 현대 파이썬 딥러닝 개발자의 기본 스킬셋이라고 보면 됩니다.

image