AI 다이어트 실패 사건 · 축 하나가 굳으면 배치가 막힌다 · 이론
축 하나가 굳으면 배치가 막힌다
한 줄 요약
ONNX 의 축은 정수로 굳거나, 심볼 이름으로 열려 있거나, 아예 없거나 셋 중 하나이고, 배치를 못 키우는 사고의 대부분은 세 번째가 아니라 첫 번째에서 온다 — 그것도 오류 없이 지나가는 자리에서 시작한다.
왜 이게 필요했나
양자화한 모델이 얼마나 빨라졌는지 재려면 배치를 키워 봐야 한다. 32개를 한 번에 넣어 보려고 했더니 런타임이 거절한다. 모델은 분명히 "동적 배치" 로 내보냈다고 들었고, 세션도 잘 열렸다. 그런데 값을 넣는 순간 멈춘다.
이 자리에서 흔한 대응은 런타임 옵션을 뒤지는 것이다. 답은 파일 안에 있다. 세션이 열린다는 것은 그래프가 성립한다는 뜻일 뿐, 우리가 넣으려는 모양을 받아 준다는 뜻이 아니다.
축은 세 가지 상태를 가진다
[ONNX Concepts](https://onnx.ai/onnx/intro/concepts.html) 가 말하는 텐서의 모양은 축마다 셋 중 하나다.
- 정수:
dim_value가 박혀 있다. 그 축은 그 값이어야 한다. - 심볼 이름:
dim_param에 문자열이 들어 있다. 실행할 때 정해지고, 같은 이름은 같은 값을 뜻한다. - 비어 있음: 둘 다 없다. 모른다는 뜻이고, 추론도 여기서 멈춘다.
두 번째의 "같은 이름은 같은 값" 이 자주 간과된다. 입력 두 개가 모두 0번 축에 batch 라고 적혀 있으면 그것은 그냥 "둘 다 동적" 이 아니라 둘의 행수가 서로 같아야 한다는 제약이다. 한쪽에 5행, 다른 쪽에 3행을 넣으면 거절당한다.
그런데 그 거절은 친절하지 않다. 런타임은 "심볼 batch 가 5 와 3 으로 갈렸다" 고 말해 주지 않는다. 그래프 최적화를 거친 뒤의 노드 이름을 대면서 그 노드의 입력 모양이 맞지 않는다고 한다. 원인과 증상 사이에 최적화 한 겹이 끼어 있어서 처음 보면 원인이 안 보인다.
선언 x [batch, 4] bias [batch, 3]넣은 것 x 5행 bias 3행런타임의 말 (융합된 노드 이름) 의 입력 모양이 맞지 않는다실제 원인 같은 심볼에 다른 값을 넣었다추론이 채우는 것과 못 채우는 것
[onnx.shape_inference](https://onnx.ai/onnx/api/shape_inference.html) 는 그래프를 훑어 중간 텐서의 모양을 채워 value_info 에 넣어 준다. 입력이 [batch, 4] 이고 가중치가 4 x 6 이면 중간 텐서는 [batch, 6] 으로 채워진다. 심볼은 심볼인 채로 전파된다.
못 채우는 경우가 둘 있다. 하나는 입력의 모양이 비어 있을 때다. 모르는 것에서 출발하면 끝까지 모른다. 다른 하나는 중간 텐서가 아예 없을 때다. 노드가 하나뿐이라 그 출력이 곧 그래프 출력이면 채울 대상이 없어서 value_info 가 빈 채로 돌아온다. 이때 "추론이 실패했다" 고 읽으면 안 된다 — 채울 것이 없었을 뿐이다.
그리고 더 위험한 경우가 있다. 추론이 잘못된 값을 확신에 차서 채우는 것. Reshape 의 목표 형이 상수로 박혀 있으면, 추론은 그 상수를 그대로 믿는다. 입력을 [batch, 4] 로 열어 두었어도 Reshape 가 [3, 6] 을 가리키고 있으면 그 뒤의 모든 텐서는 3 으로 굳는다. 오류는 나지 않는다. 배치 축은 선언에만 남고 실제로는 죽는다.
현장에서 만나는 모습
첫째, 배치 1로 내보낸 모델. 내보내기 도구에 예시 입력 하나를 주면 그 모양이 그대로 굳는다. 동적 축을 지정하지 않으면 [1, ...] 이 박히고, 그 파일은 영원히 한 번에 한 건만 처리한다. 처리량을 재는 실험이 통째로 무의미해진다.
둘째, 거절이 세션 열기가 아니라 값 넣기에서 난다. 세션은 잘 열린다. 그래서 "모델 로딩은 됐으니 모델 문제는 아니다" 라고 결론 내리고 엉뚱한 곳을 판다. 굳은 축의 오류 문구는 어느 입력의 몇 번째 축에서 무엇을 기대했고 무엇을 받았는지를 정확히 적어 주니, 그 줄만 읽으면 된다.
셋째, 상수 형이 박힌 Reshape. 이것이 가장 조용한 사고다. 모델을 열어 보면 입력이 [batch, 4] 로 멀쩡히 열려 있고, 추론 결과도 앞뒤가 맞는다. 다만 배치 축이 어느 지점부터 정수로 바뀌어 있다. 그래서 "동적 축으로 내보냈는데 왜 안 되냐" 는 말이 계속 나온다. 고치는 방법은 단순하다 — 배치 자리를 -1 로 두면 나머지 축에서 계산되어 축이 살아난다.
넷째, 양자화가 축을 굳히지는 않지만 원인을 가린다. 양자화된 그래프는 노드 이름이 바뀌고 Q/DQ 가 끼어들어 읽기 어려워진다. 그래서 축 문제는 양자화 전에 확인해 두는 것이 훨씬 싸다.
실무에서 진짜 중요한 것
- 넣어 보고 말한다. 선언만 읽지 말고 여러 행수로 실제로 넣어 본 기록을 남긴다.
- 같은 심볼은 같은 값이다. 두 입력이 같은 이름을 쓰면 그 제약을 문서에 적는다.
- 추론의 빈칸과 확신을 구분한다. 비어 있는 것과 잘못 채워진 것은 전혀 다른 문제다.
- 배치 자리는 -1 로. Reshape 의 목표 형에 배치 값을 직접 쓰지 않는다.
다음 실습에서 할 것
같은 가중치로 배치 축이 심볼인 판과 1로 굳은 판을 만들어 나란히 세우고, 도구 axes.py 를 한 단계씩 키운다. 두 파일에 같은 코드로 여러 행수를 넣어 어디서 갈라지는지 기록하고, 추론이 채운 것과 못 채운 것을 가르고, 같은 심볼을 쓰는 두 입력에 다른 행수를 넣어 본다. 마지막에는 상수 형이 박힌 Reshape 를 찾아내 배치 자리를 되돌려 축을 되살린다. 채점기는 매번 다른 축 이름과 모양, 다른 박힌 행수로 자기 파일을 지어 여러분의 도구를 실제로 돌리고 답을 대조한다.