LabHub
배우기 러닝패스 코스

The AI Diet Gone Wrong

What Does This File Promise? Reading What the Export Froze

LabHub 에서 이어서 보기

한국어 원문으로 표시합니다.

목표

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.pyinfo 를 만들어 판 번호·producer·입출력·initializer·노드를 읽어 내게 하세요.
  3. infoinput_overridesruntime_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 로 인수인계 문서를 만드세요.

참고

모델을 직접 짓기

/root/onnxq-export/build_mlp.py 를 만들어 실행해 /root/onnxq-export/mlp.onnx 를 만드세요. 입력 x 는 [심볼, 8], 출력 y 는 [심볼, 4] 이고, MatMul·Add·Relu 로 두 층을 쌓습니다. 가중치는 initializer 로 굳힙니다.

helper.make_tensor_value_info 의 모양 목록에 문자열을 넣으면 그 축은 심볼 이름이 되고, 정수를 넣으면 고정됩니다. 가중치는 numpy_helper.from_array(배열, 이름) 으로 만들어 make_graph 의 다섯 번째 인자에 넣습니다. 그 이름을 노드의 입력으로 쓰되 make_graph 의 입력 목록에는 넣지 마세요. 저장하기 전에 onnx.checker.check_model(model, full_check=True) 로 한 번 걸러 보세요.

파일에 굳어 있는 것들 읽어 내기

/root/onnxq-export/modelmeta.pyinfo <모델> 을 만들어 ir_version·producer_name·opsets·inputs·outputs·initializers·nodes 를 JSON 으로 내게 하세요.

onnx.load(path) 가 ModelProto 를 줍니다. model.opset_import 는 도메인과 판 번호의 목록이고, 기본 도메인은 빈 문자열입니다. 축은 dim_param 이 있으면 심볼, dim_value 가 있으면 정수, 둘 다 없으면 null 로 적습니다. inputs 에는 initializer 이름을 빼세요 — 그 값은 파일 안에 이미 들어 있습니다.

가중치는 입력이 아니다

info 응답에 input_overrides(graph.input 과 initializer 양쪽에 있는 이름)와 runtime_inputs(세션이 실제로 달라고 하는 입력 이름)를 더하세요. 세션을 못 열면 runtime_inputs 는 null 입니다.

IR 4 이전에는 initializer 를 반드시 graph.input 에도 선언해야 했습니다. 그래서 낡은 도구가 만든 파일에는 가중치가 입력 목록에 함께 적혀 있고, 그 이름은 '기본값이 있는 입력' 이라는 뜻입니다. 런타임에게 물어보면 그 이름을 필수 입력으로 요구하지 않는다는 것을 바로 볼 수 있습니다. 세션을 여는 코드는 예외를 삼키고 null 을 돌려주세요 — 열리지 않는 파일도 info 로는 읽을 수 있어야 합니다.

검사기는 한 겹이 아니다

check <모델> 을 더해 onnx.checker 를 기본과 full_check=True 로 각각 돌리고 {"checker", "full_check", "message"} 를 내게 하세요. 실패했으면 잡힌 예외의 첫 줄을 그대로 message 에 실으세요.

기본 검사는 구조만 봅니다. full_check=True 를 주면 모양 추론까지 돌아서, 곱셈이 성립하지 않는 MatMul 이나 선언한 출력 모양과 추론된 모양이 다른 경우를 잡습니다. 예외 문구를 지어내지 말고 str(exc) 의 첫 줄을 그대로 옮기세요 — 채점기가 자기가 심어 둔 이름이 그 안에 있는지 봅니다.

런타임에게 직접 물어보기

load <모델> 을 더해 onnxruntime 세션을 열어 보고 {"load", "error_type", "message"} 를 내게 하세요. error_type 은 잡힌 예외의 클래스 이름이고, 열렸으면 null 입니다.

검사기를 통과한 파일도 런타임은 거절할 수 있습니다. 등록되지 않은 도메인의 연산자, 판 번호가 너무 낮아 그 판에 정의가 없는 연산자, 런타임이 아직 열지 않는 높은 판이 그렇습니다. 거절의 종류가 서로 달라서 예외 클래스 이름까지 적어 두면 다음 사람이 원인을 바로 가릅니다.

판 번호만 갈아 끼우기

stamp <모델> <opset> <ir> <출력> 을 더해 기본 도메인의 opset 과 ir_version 만 바꿔 다른 파일로 저장하게 하세요. 노드·가중치·producer_name 은 그대로 두어야 합니다.

model.opset_import 를 돌며 도메인이 빈 문자열인 항목의 version 을 바꾸고, 없으면 새로 붙입니다. 이것은 변환이 아니라 도장 찍기입니다 — 연산자의 정의가 따라 내려가지도, 올라가지도 않습니다. 그 사실을 다음 단계에서 눈으로 보게 됩니다.

이 런타임이 여는 판의 범위 재기

scan <모델> 을 더해 판 번호 1 부터 27 까지 갈아 끼우며 세션이 열리는지 재고 {"min_ok", "max_ok", "ok", "failed"} 를 내게 하세요. 그리고 자기 모델의 범위를 /root/onnxq-export/opset_range.jsonmin_ok·max_ok 로 적으세요.

앞 단계의 stamp 와 load 를 그대로 이어 붙이면 됩니다. 임시 디렉터리에 찍어 내고 열어 보기만 하면 되니 원본은 건드리지 마세요. 하한은 모델이 쓰는 연산자가 어느 판부터 정의되었는지에 따라 달라지고, 상한은 런타임이 어디까지 여는지에 따라 달라집니다. 두 숫자의 출처가 다르다는 점이 이 단계의 핵심입니다.

인수인계 한 장으로 남기기

/root/onnxq-export/export_report.json 에 모델에서 읽어 낸 값과 판 범위, 그리고 검사기는 통과하는데 런타임이 거절하는 판을 담은 checker_ok_runtime_error 를 적고, /root/onnxq-export/export_report.md 를 네 절로 쓰세요.

checker_ok_runtime_error 의 opset 은 열리는 하한보다 낮은 판이면 됩니다. 그 판으로 찍은 파일을 check 와 load 에 각각 물어 실제로 나온 판정을 적으세요 — 채점기도 같은 파일을 만들어 다시 확인합니다. 보고서에는 하한과 상한을 숫자로 적어야 받는 쪽이 자기 런타임과 견줄 수 있습니다.