Runtime Operator Coverage

마지막 수정:

Draft
설명: 점검 전 위젯: 점검 전 링크: 점검 전
quantizationruntimeonnx-runtimeoperator-coverageint8profiling

Quantized artifact가 있다고 해서 실제 실행이 quantized라는 뜻은 아니다.

INT8 weight file
!=
INT8 kernel execution

모델 크기는 줄었는데 latency가 그대로이거나 더 느릴 수 있다. 저장은 낮은 precision인데 runtime이 계산할 때 dequantize해서 FP16/FP32 kernel을 쓸 수 있기 때문이다.

Looks quantized

QDQ / MatMulInteger exists

Risk
graph has INT8 nodes, but expensive ops may stay FP
Check
inspect graph nodes

Covers compute

Conv / MatMul / Gemm quantized

Risk
node coverage can lie if FLOPs are elsewhere
Check
cross-check with FLOPs

Runs fast

native kernel selected

Risk
provider may dequantize or fallback
Check
profile runtime
Coverage gap change dynamic/static mode or op_types_to_quantize
Kernel gap switch execution provider or use QDQ format
Transformer graph gap run transformer-aware optimizer
Sensitive op exclude node or keep layer in FP16/FP32
INT8 artifact does not guarantee INT8 execution. Real speedup requires important operators to be quantized and backed by native runtime kernels.

Operator coverage를 확인한다

ONNX Runtime에서 quantized graph는 보통 두 가지 방식으로 보인다.

QDQ format:
QuantizeLinear / DequantizeLinear node가 op 주변에 붙음

Operator replacement:
MatMulInteger, DynamicQuantizeLinear 같은 quantized op가 등장

그래프에 이런 node가 있는지 확인하면 “quantization이 적용됐는가”를 볼 수 있다.

하지만 node 개수만 보면 부족하다.

많은 node가 INT8
하지만 가장 비싼 MatMul이 FP32
-> speedup 거의 없음

적은 node만 INT8
하지만 FLOPs 대부분을 차지하는 MatMul이 INT8
-> speedup 가능

그래서 coverage는 op count가 아니라 compute-heavy op 기준으로 봐야 한다.

Dynamic과 static은 coverage가 다르다

Dynamic quantization은 보통 MatMul/Gemm 위주로 적용된다.

Transformer:
MatMul이 많음
-> dynamic quantization이 효과적일 수 있음

CNN:
Conv가 많음
-> MatMul만 quantize하면 대부분 FP로 남음

Static quantization은 calibration을 거쳐 Conv, Add, MatMul 같은 더 많은 op를 QDQ로 감쌀 수 있다. CNN에서는 static QDQ가 더 맞는 경우가 많다.

반대로 transformer activation은 input마다 range가 크게 흔들릴 수 있다. Generic static quantization이 activation range를 잘못 잡으면 품질이 무너질 수 있다.

Execution provider가 kernel을 결정한다

ONNX Runtime은 execution provider를 통해 실제 kernel을 고른다.

CPUExecutionProvider
CUDAExecutionProvider
TensorRTExecutionProvider
OpenVINO

그래프에 INT8 op가 있어도 provider가 그 op의 native INT8 kernel을 지원하지 않으면 fallback이 생긴다.

INT8 op exists
-> provider has no kernel
-> dequantize or fallback
-> speedup 없음

그래서 speedup 검증에는 profiler가 필요하다.

어떤 op가 시간을 쓰는가?
그 op가 INT8 kernel인가?
dequantize / quantize overhead가 큰가?

문제를 해결하는 방법

Coverage가 낮다면 quantization 설정을 바꾼다.

dynamic -> static QDQ
op_types_to_quantize에 Conv / MatMul / Gemm 포함
unsupported op 제외
sensitive node는 FP32 유지

Runtime kernel이 문제라면 provider나 format을 바꾼다.

CPU -> CUDA / TensorRT provider
ConvInteger 대신 QDQ format
target hardware가 지원하는 quantization scheme 사용

Transformer graph가 문제라면 transformer-aware optimizer가 필요할 수 있다.

attention pattern fuse
layernorm pattern 정리
shape inference 보강
runtime이 알아볼 수 있는 graph로 변환

핵심은 “INT8 파일을 만들었는가”가 아니라 “중요한 연산이 실제로 INT8로 실행되는가”다.

확인

  • INT8 artifact와 INT8 execution은 왜 다른가?
  • Operator coverage를 node 개수만으로 보면 왜 위험한가?
  • Execution provider가 speedup에 영향을 주는 이유는 무엇인가?