np.vectorize를 사용하면서 했던 실수와 깨달음최근에 ReLU 함수를 그리기 위해 np.vectorize를 사용했는데, 예상과는 다른 동작을 보고 당황했던 적이 있다. 이번 포스팅에서는 내가 어떤 실수를 했고, np.vectorize가 어떻게 작동하는지를 정리해보려고 한다.
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-5, 5, 100)
def relu(x: float) -> float:
if x <= 0:
return 0
else:
return (x + abs(x)) / 2
y = np.vectorize(relu)(x)
plt.plot(x, y)
plt.xlabel("x")
plt.ylabel("f(x)")
plt.title("ReLU Function")
plt.show()
나는 f(x)를 직접 출력해서 f(x) 자체가 배열을 반환할 것이라 착각했다.
print(f(x)) # ❌ 예상: 배열 / 실제: 오류 혹은 단일 값
하지만 relu 함수는 스칼라 입력만 받도록 설계된 함수였기 때문에, x 전체를 직접 넣으면 에러가 나거나 예상치 못한 결과가 나왔다.
np.vectorize는 뭘까?np.vectorize는 사실 진짜 벡터 연산이 아니다. 그저 루프를 돌면서 각각의 원소에 함수를 적용해주는 일종의 포장(wrapper) 함수다.
y = np.vectorize(relu)(x)
위 코드는 내부적으로 아래와 같은 루프를 도는 것과 같다:
np.vectorize를 쓰지 않고, 파이썬 기본 문법으로도 동일한 처리가 가능하다.
y = [relu(i) for i in x]
이것도 각 요소마다 relu(i)를 적용하는 루프와 같다. 실제로는 더 명확하고 직관적일 수 있다.
NumPy의 강력한 내장 연산을 사용하면 훨씬 빠르고 간단하게 ReLU를 구현할 수 있다:
y = np.maximum(0, x)
속도도 빠르고, 가독성도 높다. 대규모 데이터에서는 이게 훨씬 효율적이다.
| 개념 | 설명 |
|---|---|
np.vectorize | 내부적으로 루프 돌며 각 원소에 함수 적용 |
f(x) 문제 | 스칼라 입력만 가능. 배열 입력 시 에러 발생 |
| 대안 1 | np.vectorize(f)(x) — 간편하지만 느릴 수 있음 |
| 대안 2 | [f(i) for i in x] — 파이썬 기본 문법 |
| 대안 3 | np.maximum(0, x) — 벡터화된 연산, 가장 빠름 |