Quantization 101

iissaacc·2022년 5월 4일
0
  • updated May.06.22: 오류수정, scratch code 추가

Prologue

edgde device에는 gpu가 없는 경우가 많다. cpu는 int8을 활용하는데 cpu에 모델을 실으려면 모델이 가진 자료형을 조작해야 한다. 이 과정을 quantization이라고 한다. 세상에 없던 기술이 뿅하고 나온 것은 아니고 이미지를 압축하는 원리를 가져와서 응용했다.

Quantization

Image compression

전통적으로 범위가 [α,β][\alpha,\beta]xxquantization하는 공식은 이렇다.

q(x;s,z)=round(xs+z)q(x;s,z)=round\left(\frac{x}{s}+z\right)

xx는 입력값, s, zs,\ z는 우리가 찾아야 하는 변수다. quantization을 하고나면 xqx_q의 범위는 [αq,βq][\alpha_q,\beta_q]로 정해진다. 이렇게 압축을 해놨으면 푸는 일도 있을 텐데 dequantization이라고 부른다. 방법은 아래와 같다.

xx^=s(q(x;s,z)z)x\approx\hat{x}=s(q(x;s,z)-z)

여기까지 왔으면 우리는 α\alphaαq\alpha_q, β\betaβq\beta_q의 관계를 구할 수 있다.

α=s(αqz)β=s(βqz)\alpha=s(\alpha_q-z)\\ \beta=s(\beta_q-z)

이제 방정식 2개가 나왔으므로 우리는 변수 sszz를 연립방정식으로 풀 수 있게 됐다.

s=βαβqαqz=βαqαβqβα\begin{array}{lc} s=\frac{\beta-\alpha}{\beta_q-\alpha_q}\\ \\ z=\frac{\beta\alpha_q-\alpha\beta_q}{\beta-\alpha} \end{array}

이 점을 기본으로 깔고 quantization을 다시 보면 좋을 것 같다.

Model compression

Konwledge Distillation은 teacher model을 통해 student model의 학습에 영향을 미치는 반면 Quantization은 모델의 weight에 직접적으로 손을 댄다. wieght를 uint8으로 바꾸는 방법은 아래와 같다.

q(x;s,z,b)=clip(round(xs+z);0,2b1)q(x;s,z,b)=clip\left(round\left(\frac{x}{s}+z\right);0,2^{b}-1\right)
clip(x;L,H)={L,x<Lx,LxHH,H<xclip(x;L,H)= \begin{cases} L,\quad x<L\\ x,\quad L\le x\le H\\ H,\quad H<x\\ \end{cases}

xx는 quantize해야 할 target, s, z, bs,\ z,\ b는 각각 step size, center, bit-width에 해당한다. 이걸 하나씩 차근차근 살펴보면 앞으로 quantization 연구를 읽을 때 수월하지 않나 싶다.

1. bit-width

이건 쉽게 말해 precision이다. 일반적으로 weight를 구성하고 있는 자료형은 float32다. int32[231, 2311][-2^{31},\ 2^{31}-1]을 범위로 하는 정수값을 가지는데 실수라면 정수 사이사이를 채워야 하는만큼 메모리 공간도 많이 차지하고 연산비용도 크다. model의 크기를 줄이고 연산 속도를 높이려고 모델의 bit-width을 16 bit나, 8 bit 좀더 공격적으로는 4 bit로 낮추려고 시도한다.

2. step size

정수는 어떤 수를 다른 수와 구분하는 간격이 1이다. 실수라고 하더라도 컴퓨터가 어떤 두 수를 구분하는 간격이 있는데 이 간격을 step size라고 부른다. quantization 연구에서 이 부분을 명확하게 설명하지 않아서 답답했다. 앞에서 살펴본 방법을 써도 되지만 cpu제조사에서 사용하는 알고리즘을 활용하는 추세인 것 같다.

3. center

uint8으로 바꾸면 범위는 [0, 281][0,\ 2^8-1]로 정해지는데 이 때는 크게 걱정안 해도 된다. int8으로 바꾸면 범위가 [27, 271][-2^{7},\ 2^{7}-1]이므로 중간값을 좀 생각해야 한다. weight의 분포를 따라갈 건지, 아니면 강제로 중간 값을 0으로 고정할 건지 정해야 하고 전자를 asymetric, 후자를 symetric이라고 부른다. weight의 분포를 따라가는게 일반적으로 더 좋은 성능을 가진다.

round & clip

weight의 범위를 조절하고 나서도 element를 보면 152.0972152.0972 이런 식으로 decimal이 남는데 round()round(\cdot)로 한 번 정리하고 bit width에 들지 않는 범위에 있는 수는 clip()clip(\cdot)으로 보정해준다. RoI pooling에서 그랬듯이 round()round(\cdot)clip()clip(\cdot)을 거치고 나면 원본 wieght와는 차이가 날 수밖에 없는데 이 차이를 최대한 줄이는 알고리즘이 좋은 성능을 낸다.

전체적인 과정은 여기를 참고하면 된다.

Epilouge

이제 기본은 뗐다. 이후에 할 수 있는 이야기는 clip()clip(\cdot)에서 더 좋은 [L,H][L, H]는 어떻게 찾을 건지, outlier는 어떻게 처리할 건지, quantization은 channel별로 따로 진행할 건지, quantization이 끝나고 시뮬레이션은 어떻게 할 건지, 학습 후에 quantization을 할 건지(Post Training Quantization), 아니면 quantization을 하고 학습을 할 건지(Quantization Aware Trainig) 같은 게 있다.

Reference

  1. https://leimao.github.io/article/Neural-Networks-Quantization/
  2. https://pytorch.org/blog/quantization-in-practice/

0개의 댓글