매번 똑같은 3 단계
Functional API 패턴은 3 단계고, 한 번 손에 붙으면 graph 가 아무리 복잡해져도 안 바뀌어:
- 입력 정의 —
keras.Input(shape=...). 이게 *symbolic tensor* 를 만들어. shape 과 dtype 은 들고 있지만 아직 데이터는 없는 placeholder. - layer 체인 —
x = Layer()(x). 호출 한 번이 graph 에 edge 하나 기록. - model 생성 —
keras.Model(inputs, outputs). Keras 가 outputs 에서 inputs 로 거꾸로 추적하면서 DAG 를 굳혀.
괄호가 두 일을 한다
이 API 가 자연스러워지는 단 하나의 깨달음: *모든 layer 가 callable*. layers.Dense(64, activation="relu") 가 layer 객체를 *만들고*, 뒤에 붙는 (inputs) 가 그걸 tensor 에 *호출* 해. 생성 = weight 할당, 호출 = graph 에 배선하고 새 symbolic tensor 반환. 괄호 두 쌍이 각각 다른 일을 하는 거야.
이 분리가 핵심 보상이야. 생성과 호출이 따로니까 layer 인스턴스를 변수에 담아두고 두 번 이상 호출할 수 있어 — 그리고 매 호출이 *같은* weight 를 재사용해. dense = Dense(64) 만들고 dense(x1)·dense(x2) 부르면 둘이 weight 공유. 이 한 가지 사실이 shared layer (다음 lesson) 와 weight tying 을 가능하게 만드는 전부야. 그리고 여기서 실제 데이터로 forward pass 가 도는 게 아니라는 점도 봐 — graph 를 *기술* 하는 거지 *실행* 하는 게 아니야. 그래서 결과가 serializable 한 거고.