Stage 05 — Shape는 주석이 아니다: tensor 전의 실행 계약
Vector·Matrix·embedding의 shape와 parameter·graph 비용을 가장 작은 신뢰 경계에서 강제한다.
PyTorch에서는 shape가 맞지 않으면 대개 tensor kernel이 즉시 실패한다. 순수 Python의 list[list[Value]]에는 그런 실행 계약이 없다. zip()은 짧은 쪽에 맞춰 조용히 잘리고, ragged matrix는 여러 연산을 통과한 뒤에야 실패할 수 있다. 잘못된 값이 정상 결과처럼 앞으로 진행할 수 있다는 점이 가장 큰 위험이다.
테스트: Stage 05 테스트
이번 단계가 답할 질문
tensor 구현 전에도 shape를 실행 시점의 강제 계약으로 검사해야 할까? 그렇다면 Vector, Matrix, embedding lookup, parameter budget 중 어디에서 실패시켜야 Blast Radius가 가장 작을까?
처음 세운 가설
tensor 구현 전에도 shape를 실행 시점의 강제 계약으로 검사하는 게 맞음. 오히려 순수 Python 단계에서 더 중요하다.
PyTorch에선 shape가 틀리면 대개 연산 커널이 실패하지만
list[list[Value]]기반 구현은 잘못된 shape가 한동안 살아남을 수 있다.zip()이 짧은 쪽에 맞춰 조용히 잘라버리거나 ragged matrix가 몇 단계 뒤에서야 터지거나 broadcasting 비슷한 동작이 우연히 성립하는 식으로. 이런 건 계산 결과가 틀린 채 전진할 수 있어서 Blast Radius가 크다고 볼 수 있다.
결국 유효하지 않은 객체는 생성하지 않고, 유효하지 않은 연산은 시작하지 않고, 감당할 수 없는 모델은 할당하지 않는다.
모든 내부 루프에서 shape를 반복 검사할 필요는 없음. 그러면 원리를 보여주는 코드가 경비실만 잔뜩 달린 건물처럼 될 수 있다.
신뢰 경계에서 한 번 강하게 검사하고, 내부에서는 불변식을 신뢰하게 하는 것. “입력 shape가 틀림”과 “정상 shape지만 자원 예산을 넘음”은 원인과 대응이 다르니 구분 가치가 있다.
| 계약 | 실패 위치 |
|---|---|
| 원소와 차원의 유효성 | Vector / Matrix 생성자 |
| 객체 사이 shape 관계 | vector add·matmul 진입점 |
| embedding table 구조 | Embedding 생성자 |
| token 입력 유효성 | embedding forward 진입점 |
| parameter 비용 | allocation 전 EmbeddingPlan |
| 실행별 계산 비용 | forward 또는 연산 진입점 |
예외는 두 종류만 추가한다
ShapeError: 입력이나 config 사이의 관계를 고쳐야 한다.ResourceBudgetError: shape는 합법이지만 상한이나 model 크기를 바꿔야 한다. 원소 type, 비유한 값, token 범위에는 이미 의미가 분명한TypeError,ValueError,IndexError를 사용한다. 거대한 예외 계층을 만들지 않는다.
intrinsic shape는 생성자가 확정한다
Vector는 생성될 때 길이 를 확정한다.
Matrix는 모든 row의 길이가 같은지 먼저 검사한 뒤에만 Value를 만든다.
ragged matrix는 아직 shape가 정해지지 않은 값이다. matmul까지 전달하지 않고 생성 자체를 거부한다.
relational shape는 연산 전에 검사한다
두 vector의 덧셈은 길이가 같을 때만 정의한다.
길이가 (3,)과 (2,)라면 Python zip()은 두 항만 계산한다. Stage 05는 이를 silent truncation으로 보고 실패시킨다.
matrix multiplication의 계약은 다음과 같다.
실행한 정상 예제는 다음 결과를 만들었다.
ragged matrix, (3,) + (2,), (1,2) @ (1,2)는 모두 첫 스칼라 연산 전에 ShapeError로 끝났다.

matmul graph 비용도 연산 전에 안다
길이 의 dot product는 번의 곱셈과 번의 덧셈을 만든다.
따라서 와 matrix의 matmul이 만드는 새 graph node 수는
이다. 실행한 예제는 개다. 상한 11이면 곱셈을 시작하지 않고, 상한 12이면 계산한다.
token과 position embedding shape
Stage 04의 vocabulary 크기는 이다. context 길이를 , embedding width를 라 하면
이다. 위치 의 출력은 두 row의 합이다.
실행 config는 , , , 입력 길이는 이다.
token weight shape = (257, 4)
position weight shape = (8, 4)
output shape = (3, 4)
token ID 전체와 를 확인한 뒤에만 덧셈을 시작한다. 두 번째 ID가 잘못됐는데 첫 번째 row만 계산하는 부분 실행은 허용하지 않는다.
allocation 전에 parameter 수를 계산한다
실행 config를 대입하면 다음과 같다.
EmbeddingPlan은 이 값을 정수 계산만으로 먼저 구한다. 상한 1059에서 실패한 뒤 initializer 호출 횟수가 0인지 확인했다.


이 상한은 logical parameter 개수다. Python Value에는 float 외에도 gradient, label, parent tuple과 객체 header가 있으므로 1060이 실제 memory byte 수를 뜻하지 않는다. byte 단위 memory 계획은 이후 별도로 측정해야 한다.
forward의 신뢰 경계
embedding forward가 새로 만드는 덧셈 node 수는 다음과 같다.
이번 실행은 개다. public forward는 token ID 전체, sequence shape, graph budget을 한 번 검증한다. 검증이 끝난 내부 loop는 private trusted add로 조립하며 같은 검사를 반복하지 않는다.
비용과 Blast Radius
| 실패 | 차단 위치 | 늦게 발견할 때의 문제 |
|---|---|---|
| ragged row | Matrix 생성자 | 뒤 연산마다 row 길이를 추측 |
| vector 길이 불일치 | Vector.add() | zip() silent truncation |
| matmul dimension 불일치 | Matrix.matmul() | 일부 곱셈 뒤 실패 |
| weight/config 불일치 | Embedding 생성자 | 잘못된 token 의미 전파 |
| invalid token ID | forward 전체 입력 검사 | 부분 graph 생성 |
| parameter 초과 | EmbeddingPlan | 큰 Python object allocation |
| graph node 초과 | 연산 진입점 | CPU·memory 소비 뒤 실패 |
GPU와 NPU는 아직 사용하지 않는다. 이 shape와 parameter 이름은 나중에 PyTorch port가 따라야 할 reference specification이다.
직접 실행한 결과
python .\stages\stage_05_shape_contracts.py
python -m unittest tests.test_stage_05_shape_contracts -v- token weight shape:
(257, 4) - position weight shape:
(8, 4) - output shape:
(3, 4) - embedding parameter:
1060 - budget 실패 뒤 initializer 호출:
0 - 정상 matmul output:
(2, 2) - Stage 05 테스트 13개 통과
- 전체 테스트 57개 통과
- sanity check:
PASS
Stage 05 테스트 13개 통과
이번 Stage가 증명한 것
최초 가설은 맞았다. tensor가 없어도 shape는 실행 시점에 강제해야 하는 계약이며, 순수 Python에서는 silent truncation과 ragged data를 막기 위해 더 중요하다.
가장 작은 Blast Radius를 만드는 순서는 intrinsic shape 확정, weight/config 결합, relational shape 검증, graph 비용 검증, allocation 전 parameter budget 검증이다.
Stage 06의 질문은 “큰 logit에서 확률이 NaN이 되지 않았다는 사실만으로 수치적으로 안정하다고 말할 수 있는가?”다.