AI 다이어트 실패 사건 · 내보내기에서 굳는 것들 · 실습
이 파일은 무엇을 약속하고 있나 — 내보내기에서 굳는 것들
목표
onnx.helper 로 MLP 를 직접 지어 /root/onnxq-export/mlp.onnx 를 만들고, ONNX 파일에서 굳어 있는 것들을 읽어 내는 도구 /root/onnxq-export/modelmeta.py 를 만든다. 판 번호를 갈아 끼우며 onnx.checker 와 onnxruntime 에 차례로 물어, 검사기는 통과하는데 런타임이 거절하는 자리를 직접 찾아 문서로 남긴다.
왜 중요한가
ONNX 파일은 그래프만 담지 않는다. 어느 연산자 집합 판으로 썼는지, IR 버전이 몇인지, 누가 만들었는지, 무엇이 입력이고 무엇이 이미 정해진 가중치인지가 내보내는 순간 함께 굳는다. 받는 쪽은 그 결정을 바꿀 수 없고, 바꾸려면 다시 내보내야 한다.
그래서 "우리 서버에서 안 열린다" 는 신고의 원인은 대개 변환 옵션이 아니라 파일 첫머리에 있다. 판 번호를 낮춰 다시 저장해도 연산자 정의가 따라 내려가지 않기 때문에, 검사기는 통과시키고 런타임만 거절하는 파일이 만들어진다. 오류 문구가 "판이 낮다" 가 아니라 "이 연산자의 구현이 없다" 로 나와서 원인이 더 안 보인다.onnx.checker 도 한 겹이 아니다. 기본 검사는 구조만 보고, full_check=True 를 주어야 모양 추론까지 돈다. 곱셈이 성립하지 않는 MatMul 은 기본 검사를 그냥 통과한다. 그리고 두 검사 모두 등록되지 않은 도메인의 연산자를 막지 못한다.
채점기는 여러분이 적어 낸 문구를 믿지 않는다. 임시 디렉터리에 채점기가 직접 지은 ONNX 파일을 차려 놓고 여러분의 도구를 실제로 실행해, 같은 파일을 채점기가 읽어 얻은 답과 대조한다. 모양·이름·판 번호·활성 함수는 실행마다 바뀐다.
단계
1. /root/onnxq-export/build_mlp.py 를 만들어 실행해 /root/onnxq-export/mlp.onnx 를 만드세요.
2. /root/onnxq-export/modelmeta.py 에 info 를 만들어 판 번호·producer·입출력·initializer·노드를 읽어 내게 하세요.
3. info 에 input_overrides 와 runtime_inputs 를 더해 initializer 와 입력의 경계를 드러내게 하세요.
4. check 를 더해 onnx.checker 를 기본과 full_check=True 두 번 돌리고 판정을 따로 적게 하세요.
5. load 를 더해 onnxruntime 이 세션을 여는지, 못 열면 어떤 예외인지 적게 하세요.
6. stamp 를 더해 연산자를 그대로 두고 판 번호만 갈아 다시 저장하게 하세요.
7. scan 을 더해 이 런타임이 여는 판의 범위를 실제로 재게 하고, 자기 모델의 범위를 /root/onnxq-export/opset_range.json 에 적으세요.
8. /root/onnxq-export/export_report.json 과 /root/onnxq-export/export_report.md 로 인수인계 문서를 만드세요.
참고
- 파이썬은 /opt/onnx-lab/bin/python 입니다. 시스템
python3에는 onnx 도 numpy 도 없습니다. 실행 예:/opt/onnx-lab/bin/python /root/onnxq-export/modelmeta.py info /root/onnxq-export/mlp.onnx - 이 파드는 네트워크가 없습니다. 설치는 되지 않고, 이미 받아 둔 모델도 없습니다. 재료는 직접 짓습니다.
- 모델 계약: 입력
x는 FLOAT 이고 축은 2개, 0번 축은 심볼 이름(문자열), 1번 축은 8 입니다. 출력y는 FLOAT 이고 1번 축은 4 입니다. 노드에는 MatMul·Add·Relu 가 들어가고, 가중치는 initializer 2개 이상으로 굳힙니다.producer_name은 비우지 않습니다. opset 은 7 부터 26 사이, ir_version 은 13 이하로 둡니다. - 실행 계약:
modelmeta.py <명령> .... 답은 JSON 한 덩어리로 표준출력에 냅니다. 성공하면 종료 코드 0, 모르는 명령이면 2 입니다. onnxruntime 이 표준오류에 찍는 경고는 답이 아니니 표준출력만 깨끗하게 유지하세요. info <모델>응답:ir_version정수,producer_name문자열,opsets는 도메인을 키로 한 객체,inputs·outputs는{"name", "elem_type", "dims"}목록,initializers는 이름을 정렬한 목록,nodes는 op_type 목록입니다.dims의 각 축은 고정이면 정수, 심볼이면 문자열, 아무것도 없으면 null 입니다.elem_type은onnx.TensorProto.DataType.Name(...)이 주는 이름(예: FLOAT)입니다.- 3단계부터
info응답에input_overrides(graph.input 과 initializer 양쪽에 있는 이름을 정렬한 목록)와runtime_inputs(세션이 실제로 달라고 하는 입력 이름, 세션을 못 열면 null)가 더해집니다.inputs에는 initializer 로 채워지는 이름을 넣지 않습니다. check <모델>응답:{"checker": "ok"|"error", "full_check": "ok"|"error", "message": 문자열}. 실패한 경우 message 에는 잡힌 예외의 첫 줄을 그대로 싣습니다.load <모델>응답:{"load": "ok"|"error", "error_type": 예외 클래스 이름 또는 null, "message": 문자열}.stamp <모델> <opset> <ir> <출력>은 기본 도메인의 opset 과 ir_version 만 바꿔 다른 파일로 저장합니다. 노드·가중치·producer_name 은 건드리지 않습니다. 응답은{"out", "opset", "ir_version", "nodes"}입니다.scan <모델>응답:{"min_ok": 정수 또는 null, "max_ok": 정수 또는 null, "ok": 정수 목록, "failed": 정수 목록}. 판 번호 1 부터 27 까지 갈아 끼우며 세션이 열리는지만 봅니다(검사기 결과가 아니라 세션 열기로 판정합니다).opset_range.json에는 최소한min_ok와max_ok를 적습니다.export_report.json에는model·ir_version·producer_name·opset·nodes·initializers·runtime_inputs·min_ok_opset·max_ok_opset과, 검사기는 통과하는데 런타임이 거절하는 판을 담은checker_ok_runtime_error객체(opset·checker·load)를 적습니다.export_report.md는## 무엇을 내보냈나## 판이 굳는 자리## 검사기가 못 잡는 것## 다음 사람에게 넘길 것네 절로 쓰고, 열리는 판의 하한과 상한을 숫자로 적습니다.- 공식 문서: [ONNX Concepts](https://onnx.ai/onnx/intro/concepts.html) · [ONNX Versioning](https://onnx.ai/onnx/repo-docs/Versioning.html) · [ONNX IR](https://onnx.ai/onnx/repo-docs/IR.html) · [ORT Compatibility](https://onnxruntime.ai/docs/reference/compatibility.html) · [ORT Python API](https://onnxruntime.ai/docs/api/python/api_summary.html)
- 흔한 실수:
graph.input을 세어 입력 개수라고 말하기, 기본 검사만 돌리고 통과라고 보고하기, 판 번호를 낮추면 연산자도 따라 내려간다고 믿기, 런타임 경고를 표준출력에 섞어 JSON 을 깨뜨리기.
단계 8개
- 모델을 직접 짓기
- 파일에 굳어 있는 것들 읽어 내기
- 가중치는 입력이 아니다
- 검사기는 한 겹이 아니다
- 런타임에게 직접 물어보기
- 판 번호만 갈아 끼우기
- 이 런타임이 여는 판의 범위 재기
- 인수인계 한 장으로 남기기