portable 과 leaky 의 경계선
backend 를 바꿔도 살아남는 게 뭐고 안 살아남는 게 뭔지 정확히 아는 게, 추상화를 믿는 것과 추상화에 뒤통수 맞는 것의 차이야. 다행히 Keras 코드로 짠 건 거의 다 불변이고, 차이는 backend-native 객체를 직접 만지는 *seam* 에서만 드러나.
그대로인 것
- model 정의 — Sequential·Functional·Subclassing 다 동일
- train loop —
model.compile()+model.fit() - callback — EarlyStopping·ModelCheckpoint 등
.keras저장 포맷- 모든
keras.ops연산
바뀌는 것 (어디서 leak 하나)
아래 차이는 Keras 너머 engine 으로 손 뻗을 때만 물어 — raw tensor 꺼내거나, device 에 직접 올리거나, backend-native 라이브러리 섞을 때:
| 측면 | TensorFlow | PyTorch | JAX |
|---|---|---|---|
| tensor 타입 | tf.Tensor | torch.Tensor | jax.Array |
| device 관리 | tf.device | .to(device) | jax.devices() |
| layer 정체 | Keras layer | torch.nn.Module | Keras layer |
| JIT 컴파일 | tf.function | torch.compile | jax.jit (default) |
실전 룰: Keras + keras.ops 안에만 있으면 이 칸 쇼핑은 의미 없어 — model 이 진짜로 portable. native 코드로 건너가는 순간 그 경계를 명확히 표시해 — backend 교체가 너를 깨뜨릴 수 있는 유일한 자리니까.