AI 다이어트 실패 사건 · 연산자별 지원과 대체 · 실습
float 섬을 세어 본다 — 연산자별 지원과 대체
목표
같은 모델을 다섯 가지로 내보낸 뒤, 그래프에서 int8 로 감싸인 연산자와 float 로 남은 섬을 세는 분석기 graphscan.py 를 만든다. 범위를 좁히는 두 가지 옵션의 효과를 숫자로 확인하고, 모델을 Gemm 으로 고쳐 Q/DQ 왕복을 줄인다.
왜 중요한가
양자화 도구는 자기가 아는 연산자만 int8 모양으로 바꾼다. QDQ 형식에서는 연산자 이름이 그대로 남고 텐서마다 QuantizeLinear 와 DequantizeLinear 가 끼워질 뿐이다. 그래서 "양자화했다" 와 "int8 로 돈다" 는 다른 말이고, 둘을 가르려면 그래프를 세어야 한다.
이 실습은 판정 규칙을 이렇게 고정한다. 모든 입력이 DequantizeLinear 에서 오고 모든 출력이 QuantizeLinear 로만 가는 연산자를 int8 로 감싸인 것으로 본다. 나머지는 float 로 남고, 이어진 것끼리 묶으면 float 섬이 된다.
섬은 개수보다 경계가 문제다. 섬마다 들어오는 DQ 와 나가는 Q 가 붙고, 경계마다 값을 옮겨 담는 일과 반올림이 한 번씩 더 생긴다. 섬을 줄이는 길은 둘이다. 범위를 좁혀 어긋나는 노드를 아예 빼거나, 모델을 고쳐 양자화되는 모양으로 만들거나.
채점기는 여러분이 적은 숫자를 믿지 않는다. 매번 다른 층 수·모양·제외 노드로 자기 모델을 임시 디렉터리에 만들어 여러분의 분석기를 실제로 실행하고, 채점기가 같은 규칙으로 센 값과 대조한다. 6·7단계는 여러분이 만든 모델 파일을 채점기가 직접 읽어 다시 셉니다.
단계
1. /root/ops/gen_models.py 를 만들어 실행해 /root/ops 아래 모델 다섯 개를 만드세요.
2. /root/ops/graphscan.py 에 nodes 를 만들어 연산자 census 와 초기값 자료형을 세게 하세요.
3. qdq 를 더해 QuantizeLinear·DequantizeLinear 와 Q/DQ 왕복을 세게 하세요.
4. islands 를 더해 int8 로 감싸인 연산자와 float 섬을 가르게 하세요.
5. boundary 를 더해 섬마다 드나드는 경계를 세게 하세요.
6. diff 를 더해 두 모델의 차이를 내게 하고, 범위를 좁힌 결과를 /root/ops/scope.json 에 적으세요.
7. MatMul 과 Add 를 Gemm 으로 합친 /root/ops/gemm.onnx 와 그것을 양자화한 /root/ops/gemm_full.onnx 를 만들고 /root/ops/fuse.json 에 적으세요.
8. /root/ops/ops_report.md 를 네 절로 쓰세요.
참고
- 파이썬은
/opt/onnx-lab/bin/python입니다. 시스템python3에는 onnx 가 없습니다. - 실행 계약:
/opt/onnx-lab/bin/python /root/ops/graphscan.py <명령> <모델...>. 성공하면 종료 코드 0, 파일이 없으면 3, 사용법이 틀리면 2 입니다. 답은 JSON 한 덩어리로 표준출력에 냅니다. - 1단계가 만드는 다섯 파일:
fp32.onnx(원본),full.onnx(정적 QDQ, 범위를 안 좁힌 판),matmul_only.onnx(op_types_to_quantize=["MatMul"]),excluded.onnx(nodes_to_exclude로 MatMul 하나를 뺀 판),dynamic.onnx(quantize_dynamic). 원본은 MatMul·Add·Relu 로 층을 셋 이상 쌓고 노드마다 이름을 붙입니다. 입력 이름은x입니다. nodes응답:{"model", "nodes", "op_types", "initializers"}. op_types 는 연산자 이름을 키로 한 개수이고, initializers 는 초기값의 자료형 이름(onnx.TensorProto.DataType.Name이 주는 FLOAT·INT8 같은 이름)을 키로 한 개수입니다.qdq응답:{"model", "quantize", "dequantize", "round_trips", "weight_dequantize"}. round_trips 는 출력이 DequantizeLinear 로 곧장 들어가는 QuantizeLinear 의 수이고, weight_dequantize 는 첫 입력이 초기값인 DequantizeLinear 의 수입니다.islands응답:{"model", "compute", "wrapped", "float", "island_count", "islands"}. compute 는 QuantizeLinear·DequantizeLinear 를 뺀 연산자 수입니다. islands 의 각 항목은{"nodes", "size", "op_types"}이고 nodes 와 op_types 는 정렬합니다. 섬 목록은 첫 노드 이름 순으로 정렬합니다. 이름이 빈 노드는연산자이름_자리번호로 부릅니다.- 감싸임 판정: 노드의 모든 입력이 DequantizeLinear 의 출력이고, 모든 출력이 QuantizeLinear 에만 소비되면 감싸인 것입니다. 소비자가 하나도 없는 출력이 있으면 감싸이지 않은 것입니다.
boundary응답:{"model", "island_count", "crossings", "per_island"}. per_island 의 각 항목은{"nodes", "in_dequantize", "out_quantize"}이고, 같은 노드를 여러 번 세지 않습니다. crossings 는 전부 더한 값입니다.diff <a> <b>응답:{"a", "b", "op_delta", "quantize_delta", "dequantize_delta", "round_trip_delta", "island_delta", "float_delta"}. 각 delta 는 b 에서 a 를 뺀 값이고, op_delta 는 0이 아닌 항목만 담습니다.scope.json모양:{"excluded_node": 이름, "models": {"full": {...}, "matmul_only": {...}, "excluded": {...}}, "diff_matmul_only": {...}, "diff_excluded": {...}}. models 의 각 항목은{"quantize","dequantize","round_trips","island_count","float","crossings"}이고, diff 둘은 a 를 full.onnx 로 두고 낸diff응답 그대로입니다.fuse.json모양:{"before": {...}, "after": {...}, "removed_quantize": 정수, "removed_round_trips": 정수, "max_abs_error": 실수}. before 는 full.onnx, after 는 gemm_full.onnx 에 대한{"op_types","quantize","dequantize","round_trips"}입니다. max_abs_error 는 fp32.onnx 와 gemm.onnx 를 같은 입력으로 돌렸을 때의 최대 절대 오차입니다.- Gemm 으로 합치기:
MatMul의 출력이 그Add에서만 쓰이고Add의 다른 입력이 초기값일 때만 합칩니다.Gemm(A, B, C)는 기본값 alpha=1.0, beta=1.0, transA=0, transB=0 으로A @ B + C입니다. 합친 뒤onnx.checker.check_model을 지나야 하고, 원본과 같은 답을 내야 합니다. - 동적 양자화 판은 형식이 다릅니다.
MatMulInteger와DynamicQuantizeLinear가 나오고 QuantizeLinear·DequantizeLinear 는 없습니다. 섬과 경계를 재는 잣대는 QDQ 판을 위한 것이니, 동적 판에는nodes만 쓰는 것이 정직합니다. - 공식 문서: [양자화](https://onnxruntime.ai/docs/performance/model-optimizations/quantization.html) · [그래프 최적화](https://onnxruntime.ai/docs/performance/model-optimizations/graph-optimizations.html) · [QuantizeLinear](https://onnx.ai/onnx/operators/onnx__QuantizeLinear.html) · [DequantizeLinear](https://onnx.ai/onnx/operators/onnx__DequantizeLinear.html) · [MatMulInteger](https://onnx.ai/onnx/operators/onnx__MatMulInteger.html) · [Gemm](https://onnx.ai/onnx/operators/onnx__Gemm.html)
- 흔한 실수: 파일 크기만 보고 양자화가 됐다고 하기, QDQ 판에서 MatMul 이 보인다고 양자화가 안 됐다고 하기, 같은 DequantizeLinear 를 여러 번 세기, 동적 판에 QDQ 잣대를 대기.
- 이 실습은 시간·처리량을 재지 않습니다. 세는 것만 다룹니다.
단계 8개
- 같은 모델을 다섯 가지로 내보내기
- 무엇이 들어 있는지부터 세기
- Q/DQ 왕복을 세기
- float 로 남은 섬 가려내기
- 섬마다 경계를 세기
- 범위를 좁히면 무엇이 달라지는가
- 모델을 고쳐 왕복을 줄이기
- 그래프로 답할 수 있게 적기