it = iter(range(100))
@jax.jit
def f(x):
return x + next(it) # ❌ iterator state 가 mutate. 그것도 첫 호출 한 번.
⚠️ 오류 없이 틀릴 수 있어
이 패턴들은 JAX가 반드시 오류를 내는 문제가 아니야. 이전에 추적해 캐시한 결과를 조용히 다시 사용할 수 있어. 학습이 진행되지 않거나 결과가 이상하다면 순수성부터 의심해. 시험 삼아 일반 print를 함수 안에 넣고 같은 형태의 입력으로 두 번 호출했는데 두 번째에는 출력되지 않는다면, 추적과 실제 실행을 구분해 다시 살펴봐야 해.
방어 방법은 외부 상태를 모두 명시적인 입력과 출력으로 바꾸는 거야.
# global 대신 인자로
def f(x, scale): # ✅
return x * scale
# state 는 in/out 으로
def step(state, x):
new_state = state + 1
return new_state, x * new_state
# random 은 key 로
def f(x, key):
noise = jax.random.normal(key, x.shape)
return x + noise
# print 는 jax.debug.print
@jax.jit
def f(x):
jax.debug.print("x is {x}", x=x) # ✅ runtime print
return x ** 2
이 일곱 가지 함정을 기억하면 "왜 실행되지 않지?"라는 문제의 90%를 훨씬 빠르게 좁힐 수 있어.
Code
import jax.numpy as jnp
x = jnp.array([1, 2, 3])
# x[0] = 99 # TypeError: JAX arrays are immutable
# Fix: use .at[].set()
x_new = x.at[0].set(99) # Returns new array, x is unchanged
import jax
import jax.numpy as jnp
learning_rate = 0.01
# BAD: reads from closure
@jax.jit
def update_bad(params, grads):
return params - learning_rate * grads
# GOOD: pass as argument
@jax.jit
def update_good(params, grads, lr):
return params - lr * grads
# OR: use static_argnums for values that rarely change
@jax.jit
def update_static(params, grads, lr):
return params - lr * grads
# JAX will recompile when lr changes, but that's acceptable if it rarely does
import jax
import jax.numpy as jnp
@jax.jit
def fn_with_print(x):
print("This runs during TRACING only, not execution!")
y = x + 1
print(f"y = {y}") # Prints a tracer object, not a number
return y
result = fn_with_print(jnp.array(5.0))
# Output during first call:
# "This runs during TRACING only, not execution!"
# "y = Traced<ShapedArray(float32[])>with<DynamicJaxprTrace...>"
# Second call: no print at all — JIT reuses the cached trace
result2 = fn_with_print(jnp.array(10.0))
@jax.jit
def fn_with_debug_print(x):
y = x + 1
jax.debug.print("y = {}", y) # Prints at execution time!
return y
fn_with_debug_print(jnp.array(5.0)) # Prints "y = 6.0"
fn_with_debug_print(jnp.array(10.0)) # Prints "y = 11.0"
import jax
import jax.numpy as jnp
# NumPy uses global state — IMPURE
import numpy as np
np.random.seed(42)
a = np.random.randn(3) # Mutates global RNG state
b = np.random.randn(3) # Different result — depends on hidden state
# JAX uses explicit keys — PURE
key = jax.random.PRNGKey(42)
key1, key2 = jax.random.split(key)
a = jax.random.normal(key1, (3,)) # Deterministic given key1
b = jax.random.normal(key2, (3,)) # Deterministic given key2
# Same key always gives same result
a_again = jax.random.normal(key1, (3,))
print(jnp.allclose(a, a_again)) # True — pure!
import jax
import jax.numpy as jnp
# PROBLEMATIC under JIT: Python if depends on a traced value
@jax.jit
def bad_relu(x):
if x > 0: # ConcretizationTypeError!
return x
else:
return 0.0
# GOOD: use JAX control flow
@jax.jit
def good_relu(x):
return jnp.where(x > 0, x, 0.0)