Skip to content

Files

Latest commit

 

History

History
136 lines (82 loc) Β· 9.53 KB

quantization.rst

File metadata and controls

136 lines (82 loc) Β· 9.53 KB

μ–‘μžν™” λ ˆμ‹œν”Ό

이 λ ˆμ‹œν”ΌλŠ” Pytorch λͺ¨λΈμ„ μ–‘μžν™”ν•˜λŠ” 방법을 μ„€λͺ…ν•©λ‹ˆλ‹€. μ–‘μžν™”λœ λͺ¨λΈμ€ 원본 λͺ¨λΈκ³Ό 거의 같은 정확도λ₯Ό λ‚΄λ©΄μ„œ, μ‚¬μ΄μ¦ˆκ°€ 쀄어듀고 μΆ”λ‘  속도가 λΉ¨λΌμ§‘λ‹ˆλ‹€. μ–‘μžν™” μž‘μ—…μ€ μ„œλ²„ λͺ¨λΈκ³Ό λͺ¨λ°”일 λͺ¨λΈ 배포에 λͺ¨λ‘ 적용될 수 μžˆμ§€λ§Œ, λͺ¨λ°”일 ν™˜κ²½μ—μ„œ 특히 μ€‘μš”ν•˜κ³  맀우 ν•„μš”ν•©λ‹ˆλ‹€. κ·Έ μ΄μœ λŠ” μ–‘μžν™”λ₯Ό μ μš©ν•˜μ§€ μ•Šμ€ λͺ¨λΈμ˜ 크기가 iOSλ‚˜ Android 앱이 ν—ˆμš©ν•˜λŠ” 크기 ν•œλ„λ₯Ό μ΄ˆκ³Όν•˜κ³ , 그둜 인해 λͺ¨λΈμ˜ λ°°ν¬λ‚˜ OTA μ—…λ°μ΄νŠΈκ°€ λ„ˆλ¬΄ 였래 걸리며, λ˜ν•œ μΆ”λ‘  속도가 λ„ˆλ¬΄ λŠλ €μ„œ μ‚¬μš©μžμ˜ μΎŒμ ν•¨μ„ λ°©ν•΄ν•˜κΈ° λ•Œλ¬Έμž…λ‹ˆλ‹€.

μ†Œκ°œ

μ–‘μžν™”λŠ” λͺ¨λΈ λ§€κ°œλ³€μˆ˜λ₯Ό κ΅¬μ„±ν•˜λŠ” 32λΉ„νŠΈ 크기의 μ‹€μˆ˜ μžλ£Œν˜•μ˜ 숫자λ₯Ό 8λΉ„νŠΈ 크기의 μ •μˆ˜ μžλ£Œν˜•μ˜ 숫자둜 μ „ν™˜ν•˜λŠ” κΈ°λ²•μž…λ‹ˆλ‹€. μ–‘μžν™” 기법을 μ μš©ν•˜λ©΄, μ •ν™•λ„λŠ” 거의 κ°™κ²Œ μœ μ§€ν•˜λ©΄μ„œ, λͺ¨λΈμ˜ 크기와 λ©”λͺ¨λ¦¬ 전체 μ‚¬μš©λŸ‰μ„ 원본 λͺ¨λΈμ˜ 4λΆ„μ˜ 1κΉŒμ§€ κ°μ†Œμ‹œν‚¬ 수 있고, 좔둠은 2~4λ°° 정도 λΉ λ₯΄κ²Œ λ§Œλ“€ 수 μžˆμŠ΅λ‹ˆλ‹€.

λͺ¨λΈμ„ μ–‘μžν™”ν•˜λŠ” λ°λŠ” μ „λΆ€ μ„Έ κ°€μ§€μ˜ 접근법 및 μž‘μ—…λ°©μ‹μ΄ μžˆμŠ΅λ‹ˆλ‹€. ν•™μŠ΅ ν›„ 동적 μ–‘μžν™”(post training dynamic quantization), ν•™μŠ΅ ν›„ 정적 μ–‘μžν™”(post training static quantization), 그리고 μ–‘μžν™”λ₯Ό κ³ λ €ν•œ ν•™μŠ΅(quantization aware training)이 μžˆμŠ΅λ‹ˆλ‹€. ν•˜μ§€λ§Œ μ‚¬μš©ν•˜λ €λŠ” λͺ¨λΈμ΄ 이미 μ–‘μžν™”λœ 버전이 μžˆλ‹€λ©΄, μœ„μ˜ μ„Έ 가지 방식을 κ±°μΉ˜μ§€ μ•Šκ³  κ·Έ 버전을 λ°”λ‘œ μ‚¬μš©ν•˜λ©΄ λ©λ‹ˆλ‹€. 예λ₯Ό λ“€μ–΄, torchvision λΌμ΄λΈŒλŸ¬λ¦¬μ—λŠ” 이미 MobileNet v2, ResNet 18, ResNet 50, Inception v3, GoogleNet을 ν¬ν•¨ν•œ λͺ¨λΈμ˜ μ–‘μžν™”λœ 버전이 μ‘΄μž¬ν•©λ‹ˆλ‹€. λ”°λΌμ„œ 비둝 λ‹¨μˆœν•œ μž‘μ—…μ΄κ² μ§€λ§Œ, 사전 ν•™μŠ΅ 및 μ–‘μžν™”λœ λͺ¨λΈ μ‚¬μš©(use pretrained quantized model)을 또 λ‹€λ₯Έ μž‘μ—… 방식 쀑 ν•˜λ‚˜λ‘œ ν¬ν•¨ν•˜λ € ν•©λ‹ˆλ‹€.

Note

μ–‘μžν™”λŠ” 일뢀 μ œν•œλœ λ²”μœ„μ˜ μ—°μ‚°μžμ—λ§Œ μ§€μ›λ©λ‹ˆλ‹€. 더 λ§Žμ€ μ •λ³΄λŠ” μ—¬κΈ° λ₯Ό μ°Έκ³ ν•˜μ„Έμš”.

μš”κ΅¬ 사항

PyTorch 1.6.0 or 1.7.0

torchvision 0.6.0 or 0.7.0

μž‘μ—… 흐름

λͺ¨λΈμ„ μ–‘μžν™”ν•˜λ €λ©΄ λ‹€μŒ 4가지 방식 쀑 ν•˜λ‚˜λ₯Ό μ‚¬μš©ν•˜μ„Έμš”.

1. 사전 ν•™μŠ΅ 및 μ–‘μžν™”λœ MobileNet v2 μ‚¬μš©ν•˜κΈ° (Use Pretrained Quantized MobileNet v2)

사전 ν•™μŠ΅λœ MobileNet v2 λͺ¨λΈμ„ 뢈러였렀면, λ‹€μŒμ„ μž…λ ₯ν•˜μ„Έμš”.

import torchvision
model_quantized = torchvision.models.quantization.mobilenet_v2(pretrained=True, quantize=True)

μ–‘μžν™” μ „μ˜ MobileNet v2 λͺ¨λΈκ³Ό μ–‘μžν™”λœ λ²„μ „μ˜ λͺ¨λΈμ˜ 크기λ₯Ό λΉ„κ΅ν•©λ‹ˆλ‹€.

model = torchvision.models.mobilenet_v2(pretrained=True)

import os
import torch

def print_model_size(mdl):
    torch.save(mdl.state_dict(), "tmp.pt")
    print("%.2f MB" %(os.path.getsize("tmp.pt")/1e6))
    os.remove('tmp.pt')

print_model_size(model)
print_model_size(model_quantized)

좜λ ₯은 λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

14.27 MB
3.63 MB

2. ν•™μŠ΅ ν›„ 동적 μ–‘μžν™” (Post Training Dynamic Quantization)

동적 μ–‘μžν™”λ₯Ό μ μš©ν•˜λ©΄, λͺ¨λΈμ˜ λͺ¨λ“  κ°€μ€‘μΉ˜λŠ” 32λΉ„νŠΈ 크기의 μ‹€μˆ˜ μžλ£Œν˜•μ—μ„œ 8λΉ„νŠΈ 크기의 μ •μˆ˜ μžλ£Œν˜•μœΌλ‘œ μ „ν™˜λ˜μ§€λ§Œ, ν™œμ„±ν™”μ— λŒ€ν•œ 계산을 μ§„ν–‰ν•˜κΈ° μ§μ „κΉŒμ§€λŠ” ν™œμ„± ν•¨μˆ˜λŠ” 8λΉ„νŠΈ μ •μˆ˜ν˜•μœΌλ‘œ μ „ν™˜ν•˜μ§€ μ•Šκ²Œ λ©λ‹ˆλ‹€. 동적 μ–‘μžν™”λ₯Ό μ μš©ν•˜λ €λ©΄, torch.quantization.quantize_dynamic 을 μ‚¬μš©ν•˜λ©΄ λ©λ‹ˆλ‹€.

model_dynamic_quantized = torch.quantization.quantize_dynamic(
    model, qconfig_spec={torch.nn.Linear}, dtype=torch.qint8
)

μ—¬κΈ°μ„œ qconfig_spec 으둜 model λ‚΄μ—μ„œ μ–‘μžν™” 적용 λŒ€μƒμΈ λ‚΄λΆ€ λͺ¨λ“ˆ(submodules)을 μ§€μ •ν•©λ‹ˆλ‹€.

Warning

동적 μ–‘μžν™”λŠ” 사전 ν•™μŠ΅λœ μ–‘μžν™” 적용 λͺ¨λΈμ΄ μ€€λΉ„λ˜μ§€ μ•Šμ•˜μ„ λ•Œ μ‚¬μš©ν•˜κΈ° κ°€μž₯ μ‰¬μš΄ λ°©μ‹μ΄μ§€λ§Œ, 이 λ°©μ‹μ˜ μ£Όμš” ν•œκ³„λŠ” qconfig_spec μ˜΅μ…˜μ΄ ν˜„μž¬λŠ” nn.Linear κ³Ό nn.LSTM 만 μ§€μ›ν•œλ‹€λŠ” κ²ƒμž…λ‹ˆλ‹€. μ΄λŠ” nn.Conv2d 같은 λ‹€λ₯Έ λͺ¨λ“ˆμ„ μ–‘μžν™”ν•  λ•Œ, λ‚˜μ€‘μ— λ…Όμ˜λ  정적 μ–‘μžν™”λ‚˜ μ–‘μžν™”λ₯Ό κ³ λ €ν•œ ν•™μŠ΅μ„ μ‚¬μš©ν•΄μ•Ό ν•œλ‹€λŠ” κ±Έ μ˜λ―Έν•©λ‹ˆλ‹€.

quantize_dynamic API call κ΄€λ ¨ 전체 λ¬Έμ„œλŠ” μ—¬κΈ° λ₯Ό μ°Έκ³ ν•˜μ„Έμš”. ν•™μŠ΅ ν›„ 동적 μ–‘μžν™”λ₯Ό μ‚¬μš©ν•˜λŠ” μ„Έ 가지 μ˜ˆμ œμ—λŠ” the Bert example, an LSTM model example, demo LSTM example 이 μžˆμŠ΅λ‹ˆλ‹€.

3. ν•™μŠ΅ ν›„ 정적 μ–‘μžν™” (Post Training Static Quantization)

이 방식은 λͺ¨λΈμ˜ κ°€μ€‘μΉ˜μ™€ ν™œμ„± ν•¨μˆ˜ λͺ¨λ‘λ₯Ό 8λΉ„νŠΈ 크기의 μ •μˆ˜ μžλ£Œν˜•μœΌλ‘œ 미리 λ³€ν™˜ν•˜λ―€λ‘œ, 동적 μ–‘μžν™”μ²˜λŸΌ μΆ”λ‘  κ³Όμ • 쀑에 ν™œμ„±ν™”μ— λŒ€ν•œ 즉각적인 μ–‘μžν™”λ₯Ό μ§„ν–‰ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. ν•™μŠ΅ ν›„ 정적 μ–‘μžν™”λŠ” μΆ”λ‘  속도λ₯Ό 크게 ν–₯μƒμ‹œν‚€κ³  λͺ¨λΈμ˜ 크기λ₯Ό 쀄일 수 μžˆμ§€λ§Œ, 이 방법은 동적 μ–‘μžν™”μ— λΉ„ν•΄ 원본 λͺ¨λΈ λŒ€λΉ„ 정확도가 더 λ–¨μ–΄μ§ˆ 수 μžˆμŠ΅λ‹ˆλ‹€.

정적 μ–‘μžν™”λ₯Ό λͺ¨λΈμ— μ μš©ν•˜λŠ” μ½”λ“œλŠ” λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

backend = "qnnpack"
model.qconfig = torch.quantization.get_default_qconfig(backend)
torch.backends.quantized.engine = backend
model_static_quantized = torch.quantization.prepare(model, inplace=False)
model_static_quantized = torch.quantization.convert(model_static_quantized, inplace=False)

μ΄λ‹€μŒμ— print_model_size(model_static_quantized) λ₯Ό μ‹€ν–‰ν•˜λ©΄ 정적 μ–‘μžν™”κ°€ 적용된 λͺ¨λΈμ΄ 3.98MB 라 ν‘œμ‹œλ©λ‹ˆλ‹€.

λͺ¨λΈμ˜ 전체 μ •μ˜μ™€ 정적 μ–‘μžν™”μ˜ μ˜ˆμ œλŠ” μ—¬κΈ° μ—μ„œ ν™•μΈν•˜μ„Έμš”. νŠΉμˆ˜ν•œ 정적 μ–‘μžν™” νŠœν† λ¦¬μ–Όμ€ μ—¬κΈ° μ—μ„œ ν™•μΈν•˜μ„Έμš”.

Note

λͺ¨λ°”일 μž₯λΉ„λŠ” 일반적으둜 ARM μ•„ν‚€ν…μ²˜λ₯Ό νƒ‘μž¬ν•˜λŠ”λ° μ—¬κΈ°μ„œ λͺ¨λΈμ΄ μž‘λ™ν•˜κ²Œ ν•˜λ €λ©΄, qnnpack 을 backend 둜 μ‚¬μš©ν•΄μ•Ό ν•©λ‹ˆλ‹€. 이와 달리 x86 μ•„ν‚€ν…μ²˜λ₯Ό νƒ‘μž¬ν•œ μ»΄ν“¨ν„°μ—μ„œ λͺ¨λΈμ΄ μž‘λ™ν•˜κ²Œ ν•˜λ €λ©΄, x86 을 backend 둜 μ‚¬μš©ν•˜μ„Έμš”. (μ΄μ „μ˜ 'fbgemm' λ˜ν•œ μ—¬μ „νžˆ μ‚¬μš© κ°€λŠ₯ν•˜μ§€λ§Œ, 'x86'을 기본으둜 μ‚¬μš©ν•˜λŠ” 것을 ꢌμž₯ν•©λ‹ˆλ‹€.)

4. μ–‘μžν™”λ₯Ό κ³ λ €ν•œ ν•™μŠ΅ (Quantization Aware Training)

μ–‘μžν™”λ₯Ό κ³ λ €ν•œ ν•™μŠ΅μ€ λͺ¨λΈ ν•™μŠ΅ κ³Όμ •μ—μ„œ λͺ¨λ“  κ°€μ€‘μΉ˜μ™€ ν™œμ„± ν•¨μˆ˜μ— κ°€μ§œ μ–‘μžν™”λ₯Ό μ‚½μž…ν•˜κ²Œ 되고, ν•™μŠ΅ ν›„ μ–‘μžν™”ν•˜λŠ” 방법보닀 높은 μΆ”λ‘  정확도λ₯Ό κ°€μ§‘λ‹ˆλ‹€. μ΄λŠ” 주둜 CNN λͺ¨λΈμ— μ‚¬μš©λ©λ‹ˆλ‹€.

λͺ¨λΈμ„ μ–‘μžν™”λ₯Ό κ³ λ €ν•œ ν•™μŠ΅μ„ κ°€λŠ₯ν•˜κ²Œ ν•˜λ €λ©΄, λͺ¨λΈ μ •μ˜ λΆ€λΆ„μ˜ __init__ λ©”μ†Œλ“œμ—μ„œ QuantStub κ³Ό DeQuantStub 을 μ •μ˜ν•΄μ•Ό ν•©λ‹ˆλ‹€. 이듀은 각각 tensorλ₯Ό μ‹€μˆ˜ν˜•μ—μ„œ μ–‘μžν™”λœ μžλ£Œν˜•μœΌλ‘œ μ „ν™˜ν•˜κ±°λ‚˜ λ°˜λŒ€λ‘œ μ „ν™˜ν•˜λŠ” μ—­ν• μž…λ‹ˆλ‹€.

self.quant = torch.quantization.QuantStub()
self.dequant = torch.quantization.DeQuantStub()

κ·Έλ‹€μŒ, λͺ¨λΈ μ •μ˜ λΆ€λΆ„μ˜ forward λ©”μ†Œλ“œμ˜ μ‹œμž‘ λΆ€λΆ„κ³Ό λλΆ€λΆ„μ—μ„œ, x = self.quant(x) 와 x = self.dequant(x) λ₯Ό ν˜ΈμΆœν•˜μ„Έμš”.

μ–‘μžν™”λ₯Ό κ³ λ €ν•œ ν•™μŠ΅μ„ μ§„ν–‰ν•˜λ €λ©΄, λ‹€μŒμ˜ μ½”λ“œ 쑰각을 μ‚¬μš©ν•˜μ‹­μ‹œμ˜€.

model.qconfig = torch.quantization.get_default_qat_qconfig(backend)
model_qat = torch.quantization.prepare_qat(model, inplace=False)
# μ–‘μžν™”λ₯Ό κ³ λ €ν•œ ν•™μŠ΅μ΄ μ—¬κΈ°μ„œ μ§„ν–‰λ©λ‹ˆλ‹€.
model_qat = torch.quantization.convert(model_qat.eval(), inplace=False)

μ–‘μžν™”λ₯Ό κ³ λ €ν•œ ν•™μŠ΅μ˜ 더 μžμ„Έν•œ μ˜ˆμ‹œλŠ” μ—¬κΈ° 와 μ—¬κΈ° λ₯Ό μ°Έκ³ ν•˜μ„Έμš”.

사전 ν•™μŠ΅λœ μ–‘μžν™” 적용 λͺ¨λΈλ„ μ–‘μžν™”λ₯Ό κ³ λ €ν•œ 전이 ν•™μŠ΅μ— μ‚¬μš©λ  수 μžˆμŠ΅λ‹ˆλ‹€. μ΄λ•Œλ„ μœ„μ—μ„œ μ‚¬μš©ν•œ quant 와 dequant λ₯Ό λ˜‘κ°™μ΄ μ‚¬μš©ν•©λ‹ˆλ‹€. 전체 μ˜ˆμ œλŠ” μ—¬κΈ° λ₯Ό ν™•μΈν•˜μ„Έμš”.

μœ„μ˜ 단계 쀑 ν•˜λ‚˜λ₯Ό μ΄μš©ν•΄ μ–‘μžν™”λœ λͺ¨λΈμ΄ μƒμ„±λœ 후에, λͺ¨λ°”일 μž₯μΉ˜μ—μ„œ μž‘λ™λ˜κ²Œ ν•˜λ €λ©΄ μΆ”κ°€λ‘œ TorchScript ν˜•μ‹μœΌλ‘œ μ „ν™˜ν•˜κ³  λͺ¨λ°”일 app에 μ΅œμ ν™”λ₯Ό 진행해야 ν•©λ‹ˆλ‹€. μžμ„Έν•œ λ‚΄μš©μ€ Script and Optimize for Mobile recipe λ₯Ό ν™•μΈν•˜μ„Έμš”.

더 μ•Œμ•„λ³΄κΈ°

λ‹€λ₯Έ μ–‘μžν™” μ μš©λ²•μ— λŒ€ν•œ μΆ”κ°€ μ •λ³΄λŠ” μ—¬κΈ° 와 μ—¬κΈ° λ₯Ό μ°Έκ³ ν•˜μ„Έμš”.