[{"data":1,"prerenderedAt":420},["ShallowReactive",2],{"post-document:\u002Fdeep_learning\u002Foptimization\u002F2026\u002F08\u002F31\u002Fquantization\u002F":3},{"id":4,"title":5,"body":6,"categories":399,"date":402,"description":403,"extension":404,"image":405,"key_concepts":405,"last_modified_at":405,"legacyPath":406,"meta":407,"navigation":409,"part":405,"path":410,"published":409,"robots":405,"seo":411,"series":405,"stem":412,"strengths":405,"summary":413,"tags":414,"tradeoffs":405,"__hash__":419},"posts\u002Fposts\u002FDeep_Learning\u002FOptimization\u002F2026-08-31-quantization.md","LLM 양자화와 QLoRA",{"type":7,"value":8,"toc":395},"minimark",[9,14,18,21,24,27,30,33,36,39,42,45,48,51,54,57,60,63,66,69,72,75,86,89,93,96,99,102,105,108,110,113,116,119,122,125,128,131,135,138,141,144,147,150,153,156,159,162,165,171,174,178,181,184,187,190,193,196,199,202,205,208,211,214,220,223,229,232,236,239,245,248,254,257,260,266,269,273,276,279,285,288,294,297,300,304,307,309,312,315,318,321,327,330,334,337,340,346,349,352,358,361,367,372,378,381,384,387,390],[10,11,13],"h1",{"id":12},"_1-양자화","1. 양자화",[15,16,17],"p",{},"모델의 실수형 가중치를 더 적은 비트 수의 숫자로 근사해서 저장하는 것이다.",[15,19,20],{},"원래 LLM 가중치가 FP16이라면 가중치 하나를 16비트로 저장한다.",[15,22,23],{},"(0.1274,\\quad -1.3821,\\quad 0.7362)",[15,25,26],{},"4bit로 양자화하면 사용할 수 있는 값이 (2^4=16)개밖에 없으니까, 원래 연속적인 실수를 16개의 대표값 중 하나로 매핑하게 된다.",[15,28,29],{},"가장 기본적인 양자화 원리는 다음과 같다.",[15,31,32],{},"예를 들어 가중치 범위가",[15,34,35],{},"[\n-1.0 \\sim 1.0\n]",[15,37,38],{},"이고 4bit를 사용한다고 해보자.\n4bit면 표현 가능한 정수는 0부터 15까지다.",[15,40,41],{},"총 16개다.",[15,43,44],{},"그러면 실수 (x)를 정수 (q)로 바꾸는 가장 단순한 방식은 대략",[15,46,47],{},"[\nq = \\operatorname{round}\\left(\\frac{x-x_{\\min}}{s}\\right)\n]",[15,49,50],{},"처럼 표현할 수 있다.",[15,52,53],{},"여기서 (s)는 Scale이다.",[15,55,56],{},"[\ns=\\frac{x_{\\max}-x_{\\min}}{2^b-1}\n]",[15,58,59],{},"(b=4)라면",[15,61,62],{},"[\ns\n=\\frac{1-(-1)}{15}\n=\\frac{2}{15}\n\\approx 0.1333\n]",[15,64,65],{},"이다.",[15,67,68],{},"[\nx=0.72\n]",[15,70,71],{},"가 된다.",[15,73,74],{},"즉,",[76,77,83],"pre",{"className":78,"code":80,"language":81,"meta":82},[79],"language-text","원래 값\n  0.72\n\n  ↓ Quantization\n\n4bit Integer\n  13\n","text","",[84,85,80],"code",{"__ignoreMap":82},[15,87,88],{},"으로 저장하는 것이다.",[10,90,92],{"id":91},"그런데-계산할-때-13을-그대로-쓰는-것은-아니다","그런데 계산할 때 13을 그대로 쓰는 것은 아니다",[15,94,95],{},"필요할 때 다시 근사적인 실수로 복원한다.",[15,97,98],{},"이를 Dequantization이라고 한다.",[15,100,101],{},"[\n\\hat{x}=q \\times s+x_{\\min}\n]",[15,103,104],{},"방금 값이라면",[15,106,107],{},"[\n13\\times0.1333-1\\approx0.733\n]",[15,109,65],{},[15,111,112],{},"원래 값은 (0.72)였는데 복원하면",[15,114,115],{},"[\n0.733\n]",[15,117,118],{},"정도가 된다.\n즉,",[15,120,121],{},"[\n0.72\\rightarrow 13\\rightarrow 0.733\n]",[15,123,124],{},"이다.\n이 차이를 Quantization Error라고 한다.",[15,126,127],{},"[\n\\text{Error}=0.733-0.72=0.013\n]",[15,129,130],{},"그래서 양자화의 본질은 정밀도를 조금 포기해서 메모리와 계산 비용을 크게 줄이는 것이다.",[10,132,134],{"id":133},"왜-메모리가-줄어드나","왜 메모리가 줄어드나?",[15,136,137],{},"7B 모델이 있다고 해보자.\nFP16이라면 Parameter 하나당",[15,139,140],{},"[\n16\\text{bit}=2\\text{byte}\n]",[15,142,143],{},"이므로 대략",[15,145,146],{},"[\n7\\text{B}\\times2\\text{byte}\\approx14\\text{GB}\n]",[15,148,149],{},"가 필요하다.\n4bit라면",[15,151,152],{},"[\n4\\text{bit}=0.5\\text{byte}\n]",[15,154,155],{},"이므로 이론적으로",[15,157,158],{},"[\n7\\text{B}\\times0.5\\text{byte}\\approx3.5\\text{GB}\n]",[15,160,161],{},"정도까지 줄어든다.",[15,163,164],{},"그래서",[76,166,169],{"className":167,"code":168,"language":81,"meta":82},[79],"FP32\n  32bit\n\n  ↓\n\nFP16 \u002F BF16\n  16bit\n\n  ↓\n\nINT8\n  8bit\n\n  ↓\n\nINT4\n  4bit\n",[84,170,168],{"__ignoreMap":82},[15,172,173],{},"로 내려갈수록 메모리가 줄어든다.",[10,175,177],{"id":176},"그런데-실제-llm은-전체-weight를-한-번에-양자화하지-않는다","그런데 실제 LLM은 전체 Weight를 한 번에 양자화하지 않는다",[15,179,180],{},"여기서 중요한 개념이 Block-wise Quantization이다.",[15,182,183],{},"가중치가 엄청 큰 행렬이라고 해보자.",[15,185,186],{},"[\nW\\in\\mathbb{R}^{4096\\times4096}\n]",[15,188,189],{},"이 전체 행렬 하나에 Scale 하나만 쓰면 문제가 생긴다.",[15,191,192],{},"예를 들어 대부분의 Weight가",[15,194,195],{},"[\n-0.1\\sim0.1\n]",[15,197,198],{},"사이에 있는데 딱 하나가",[15,200,201],{},"[\n5.0\n]",[15,203,204],{},"이라면 전체 범위가",[15,206,207],{},"[\n-0.1\\sim5.0\n]",[15,209,210],{},"이 되어버린다.",[15,212,213],{},"그러면 작은 Weight들이 매우 부정확하게 표현된다.\n그래서 실제로는 Weight를 작은 Block으로 나눈다.",[76,215,218],{"className":216,"code":217,"language":81,"meta":82},[79],"거대한 Weight\n\n  [----------------------------]\n\n  ↓\n\nBlock 1\n  [----]\n\nBlock 2\n  [----]\n\nBlock 3\n  [----]\n\nBlock 4\n  [----]\n",[84,219,217],{"__ignoreMap":82},[15,221,222],{},"그리고 각 Block마다 별도의 Scale을 갖는다.",[76,224,227],{"className":225,"code":226,"language":81,"meta":82},[79],"Block 1 → Scale₁\nBlock 2 → Scale₂\nBlock 3 → Scale₃\nBlock 4 → Scale₄\n",[84,228,226],{"__ignoreMap":82},[15,230,231],{},"이렇게 하면 각 지역의 숫자 분포에 더 잘 맞출 수 있다.",[10,233,235],{"id":234},"qlora는-조금-더-특별하다","QLoRA는 조금 더 특별하다",[15,237,238],{},"QLoRA에서는 그냥 일반적인 INT4를 사용하는 것이 아니라 대표적으로 NF4, 즉 NormalFloat 4-bit를 사용한다.\nLLM Weight를 보면 대체로 값들이 정규분포와 비슷하게 몰려 있는 경우가 많다.",[76,240,243],{"className":241,"code":242,"language":81,"meta":82},[79],"빈도\n\n          ███\n        ███████\n      ███████████\n----███████████████----\n             0\n",[84,244,242],{"__ignoreMap":82},[15,246,247],{},"즉 0 근처에 Weight가 많고 큰 값은 적다.\n일반적인 균등 양자화라면 값을 일정한 간격으로 나눈다.",[76,249,252],{"className":250,"code":251,"language":81,"meta":82},[79],"-1\n│\n├── -0.75\n├── -0.50\n├── -0.25\n├──  0\n├──  0.25\n├──  0.50\n├──  0.75\n│\n1\n",[84,253,251],{"__ignoreMap":82},[15,255,256],{},"하지만 Weight가 0 근처에 몰려 있다면 0 근처에 더 많은 표현값을 배치하는 것이 효율적일 수 있다.",[15,258,259],{},"NF4는 이런 신경망 Weight의 분포 특성에 맞춰 16개의 대표값을 설계한다.",[76,261,264],{"className":262,"code":263,"language":81,"meta":82},[79],"Uniform INT4\n\n  |----|----|----|----|----|\n\n\nNF4\n\n  |-------|---|-|-|-|---|-------|\n                0\n",[84,265,263],{"__ignoreMap":82},[15,267,268],{},"처럼 0 근처를 더 세밀하게 표현한다고 이해하면 된다.\n엄밀하게는 정규분포를 고려해 Quantization Level을 설계한 데이터 타입이다.",[10,270,272],{"id":271},"qlora에서-학습은-어떻게-되는-걸까","QLoRA에서 학습은 어떻게 되는 걸까?",[15,274,275],{},"QLoRA라고 해서 모든 연산을 4bit로 하는 것은 아니다.\nBase Model의 Weight는 4bit로 저장한다.",[15,277,278],{},"하지만 실제 Matrix Multiplication을 할 때는 다음과 같다.",[76,280,283],{"className":281,"code":282,"language":81,"meta":82},[79],"4bit Weight\n  ↓\nDequantization\n  ↓\nBF16 \u002F FP16\n  ↓\nMatrix Multiplication\n",[84,284,282],{"__ignoreMap":82},[15,286,287],{},"그리고 LoRA Adapter는 보통 더 높은 정밀도로 학습한다.\n구조적으로는 이런 느낌이다.",[76,289,292],{"className":290,"code":291,"language":81,"meta":82},[79],"                 Base Model Weight\n                          W\n                        4bit\n                          │\n                    Dequantization\n                          │\n                          ↓\nx ────────────────────── Wx\n│                         │\n│                         ├──→ +\n│                         │     │\n└──→ LoRA A → LoRA B ──────────┘\n      BF16      BF16             │\n                                 ↓\n                                 y\n",[84,293,291],{"__ignoreMap":82},[15,295,296],{},"[\ny=W_{\\text{quantized}}x+\\frac{\\alpha}{r}BAx\n]",[15,298,299],{},"정확하게는 계산할 때 양자화된 (W)가 적절한 계산 dtype으로 복원되어 사용된다.",[10,301,303],{"id":302},"그런데-base-model은-4bit인데-학습이-가능한-이유는","그런데 Base Model은 4bit인데 학습이 가능한 이유는?",[15,305,306],{},"QLoRA에서는 Base Weight를 Frozen 상태로 유지한다.",[15,308,74],{},[15,310,311],{},"[\nW\n]",[15,313,314],{},"를 업데이트하지 않는다. Gradient가 필요한 것은",[15,316,317],{},"[\nA, B\n]",[15,319,320],{},"뿐이다.",[76,322,325],{"className":323,"code":324,"language":81,"meta":82},[79],"Forward\n\nQuantized W\n  +\nLoRA A, B\n  ↓\nPrediction\n  ↓\nLoss\n\nBackward\n\nW → 업데이트 ❌\nA → Gradient ✅\nB → Gradient ✅\n",[84,326,324],{"__ignoreMap":82},[15,328,329],{},"그래서 LoRA 대비 메모리 절감이 가능해진다.",[10,331,333],{"id":332},"double-quantization","Double Quantization",[15,335,336],{},"QLoRA에서 재미있는 기술 중 하나다.",[15,338,339],{},"아까 Block마다 Scale이 필요하다고 했다.\n그런데 Scale도 결국 숫자다.",[76,341,344],{"className":342,"code":343,"language":81,"meta":82},[79],"Block 1 → scale = 0.03451\nBlock 2 → scale = 0.02137\nBlock 3 → scale = 0.04282\n...\n",[84,345,343],{"__ignoreMap":82},[15,347,348],{},"Block이 많으면 Scale 저장 비용도 무시할 수 없게 된다.",[15,350,351],{},"그래서 QLoRA는",[353,354,355],"blockquote",{},[15,356,357],{},"Quantization을 위한 Scale 자체를 다시 Quantization",[15,359,360],{},"한다.",[76,362,365],{"className":363,"code":364,"language":81,"meta":82},[79],"Weight\n  ↓\n4bit Quantization\n  ↓\nScale 필요\n\n그 Scale\n  ↓\n다시 Quantization\n",[84,366,364],{"__ignoreMap":82},[368,369,371],"h2",{"id":370},"qlora","QLoRA",[76,373,376],{"className":374,"code":375,"language":81,"meta":82},[79],"Base LLM\n  FP16 \u002F BF16\n      │\n      ↓\n4bit Quantization\n      │\n      ├── NF4\n      ├── Block-wise Quantization\n      └── Double Quantization\n      │\n      ↓\nQuantized Base Model\n  Frozen\n      │\n      +\n      │\nLoRA Adapter\n  A, B\n  Trainable\n      │\n      ↓\nFine-Tuning\n",[84,377,375],{"__ignoreMap":82},[15,379,380],{},"그리고 추론할 때는 다음과 같다.",[15,382,383],{},"[\n\\boxed{y=Wx+\\frac{\\alpha}{r}BAx}\n]",[15,385,386],{},"기본 구조는 유지된다.",[15,388,389],{},"다만, (W)가 고정밀 원본 Weight가 아니라 4bit로 압축 저장된 Weight라는 것이 핵심적인 차이다.",[353,391,392],{},[15,393,394],{},"양자화는 Weight를 단순히 소수점 몇 자리에서 잘라버리는 것이 아니다. 여러 실수값을 제한된 수의 대표값에 매핑하고, Scale 등의 정보를 이용해서 필요할 때 근사 복원하는 과정이다.",{"title":82,"searchDepth":396,"depth":396,"links":397},2,[398],{"id":370,"depth":396,"text":371},[400,401],"Deep_Learning","Optimization","2026-08-31 00:00:00 +0900","LLM 양자화의 기본 원리와 Block-wise Quantization, NF4, Double Quantization, QLoRA의 학습 구조를 정리한다.","md",null,"\u002Fdeep_learning\u002Foptimization\u002F2026\u002F08\u002F31\u002Fquantization\u002F",{"layout":408},"post",true,"\u002Fposts\u002Fdeep_learning\u002Foptimization\u002F2026-08-31-quantization",{"title":5,"description":403},"posts\u002FDeep_Learning\u002FOptimization\u002F2026-08-31-quantization","가중치를 저비트로 근사하는 과정부터 QLoRA가 메모리를 절감하는 방식까지 정리한다.",[415,416,417,371,418],"Deep Learning","LLM","Quantization","NF4","KJnMmKmZySm2SwHfB2km69ybLAGN63mGEaKrsg92iYs",1788744789003]