~14 min · numpy, operations, reshape, broadcasting
Level 0호기심
0 XP0/51 lessons0/15 achievements
0/100 XP to next level100 XP to go0% complete
익숙한 것의 90%는 그대로 써
NumPy를 손에 익혔다면 대부분은 의식하지도 못한 채 MLX로 옮겨와. 정수 색인, 콜론 슬라이싱, 모양이 다른 배열의 브로드캐스팅, reshape, sum·mean·argmax 같은 축약 연산이 모두 예상대로 동작해. NumPy에서 쓰던 식을 그대로 입력해도 놀랄 만큼 많은 MLX 코드가 맞아.
이 레슨에서는 그대로 이어지는 부분을 확인하고, 미리 모르면 발을 걸 딱 두 가지 차이만 이름 붙일 거야.
그대로 이어지는 것들
색인과 슬라이싱, 불리언 마스크는 부분 배열을 돌려줘. 브로드캐스팅과 축약 연산은 축을 지정하든 안 하든 동작하고, reshape도 같아. 항목 대입으로 제자리 수정도 가능해. 오래된 MLX 문서에는 다르게 적힌 경우가 있지만 0.31.x에서는 a[i, j] = value가 잘 동작해. 나도 이건 조금 놀랐어.
외워둘 차이 두 가지
1. 기본이 지연 실행이야. NumPy에서 y = x * 2 + 1을 실행하면 곧바로 값을 계산해 저장해. MLX에서는 계산 방법을 담은 그래프 노드를 만들 뿐이야. 배열을 출력하거나 스칼라에 .item()을 호출하거나 Python 목록으로 바꾸는 등 실제 값을 요구할 때 계산이 시작돼. 다음 레슨에서 자세히 다룰 거야. 지금은 배열의 생김새는 NumPy와 비슷해도 계산 시점은 다르다는 것만 잡아.
2. 난수 API의 인자 모양이 달라. NumPy의 np.random.randn(1024, 1024)는 각 크기를 위치 인자로 넘겨. MLX의 mx.random.normal((1024, 1024))은 모양을 튜플 하나로 넘겨야 해. uniform, randint도 같아. 튜플을 빼먹으면 "모양은 튜플이어야 한다"는 친절한 설명 대신 인자 수가 틀렸다는 애매한 오류를 만나.
표시해둘 건 정말 이 둘뿐이야
나머지는 대부분 NumPy 습관을 믿어도 돼. NumPy 코드처럼 느껴지는 식을 쓰고 있다면 대개 올바른 MLX 코드야. 막히는 지점은 거의 언제나 지연 실행의 시점이나 난수 API의 모양 튜플이야. 둘 중 하나가 물면 이 레슨으로 돌아와.
Code
그대로 이어지는 것들 확인하기·python
import mlx.core as mx
a = mx.array([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])
print('a :', a)
print('a[0] :', a[0])
print('a[:, 1] :', a[:, 1])
print('a.sum() :', a.sum())
print('a.sum(axis=0):', a.sum(axis=0))
print('a.mean():', a.mean())
print('a.argmax(axis=1):', a.argmax(axis=1))
# Broadcasting (shape (3,) broadcasts against shape (2, 3))
b = mx.array([10.0, 20.0, 30.0])
print('a + b :', a + b)
# Reshape
print('a.reshape(3,2):', a.reshape(3, 2))
# In-place via item assignment — works fine in mlx 0.31.x
a[0, 0] = 99.0
print('after a[0,0]=99 :', a)
차이 1 — 기본은 지연 실행, 자세한 내용은 레슨 4·python
import mlx.core as mx
x = mx.array([1.0, 2.0, 3.0])
y = x * 2 + 1 # NumPy would compute now; MLX records a graph
# At this point y is a graph node, not concrete numbers.
print('y :', y) # ← print() triggers implicit eval; you see [3, 5, 7]
# To force computation explicitly, without a print:
big = mx.random.normal((1024, 1024))
big_squared = big @ big.T # lazy — no work done yet
mx.eval(big_squared) # NOW the kernel runs
print('big_squared.sum():', float(big_squared.sum()))
차이 2 — 난수 API는 모양 튜플을 받아·python
import mlx.core as mx
# NumPy: np.random.randn(3, 4)
# MLX: mx.random.normal((3, 4)) — shape is a single tuple argument
x = mx.random.normal((3, 4))
print('normal(3,4) shape:', x.shape, 'dtype:', x.dtype)
u = mx.random.uniform(low=0.0, high=1.0, shape=(2, 3))
print('uniform(2,3) shape:', u.shape)
# Reproducibility: seed once at the top of your script
mx.random.seed(42)
print('seeded sample:', mx.random.normal((3,)))