<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Machine Learning on kenji.blog</title><link>http://kenji.blog/ko/categories/machine-learning/</link><description>Recent content in Machine Learning on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>ko</language><copyright>kenjinote</copyright><lastBuildDate>Fri, 11 Sep 2026 22:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/ko/categories/machine-learning/index.xml" rel="self" type="application/rss+xml"/><item><title>【초보자용】 Transformer 모델의 수학적 구조 파헤치기</title><link>http://kenji.blog/ko/p/transformer-mathematical-structure/</link><pubDate>Fri, 11 Sep 2026 22:00:00 +0900</pubDate><guid>http://kenji.blog/ko/p/transformer-mathematical-structure/</guid><description>&lt;img src="http://kenji.blog/p/transformer-mathematical-structure/img/eyecatch.jpg" alt="Featured image of post 【초보자용】 Transformer 모델의 수학적 구조 파헤치기" />&lt;h1 id="머리말-왜-transformer의-수학을-배우는가">머리말: 왜 Transformer의 수학을 배우는가?
&lt;/h1>&lt;p>현대 자연어 처리(NLP), 그리고 AI 전체의 역사를 새로 썼다고 해도 과언이 아닌 아키텍처가 바로 &amp;lsquo;Transformer&amp;rsquo;입니다. 2017년 Google 연구진이 발표한 논문 『Attention Is All You Need』에서 처음 제안된 이 모델은, OpenAI의 GPT 시리즈(ChatGPT의 기반 기술)나 Google의 BERT, Anthropic의 Claude 등 현재 세계를 휩쓸고 있는 대규모 언어 모델(LLM)의 심장부로 기능하고 있습니다.&lt;/p>
&lt;p>하지만 Transformer의 원리에 대해 &amp;lsquo;Attention(주의 메커니즘)을 사용하여 문맥을 이해한다&amp;rsquo;와 같은 정성적인 설명은 자주 볼 수 있지만, 그 이면에 있는 &lt;strong>수학적인 구조&lt;/strong>에 대해 초보자를 위해 깊이 파고든 해설은 의외로 적은 것이 현실입니다. AI가 어떻게 &amp;lsquo;언어&amp;rsquo;를 &amp;lsquo;수식&amp;rsquo;으로 처리하고 놀라울 정도로 자연스러운 문장을 생성해 내는지 진정으로 이해하기 위해서는, 그 수학적 메커니즘을 파헤치는 것이 필수적입니다.&lt;/p>
&lt;p>본 문서에서는 수학이나 프로그래밍에 대한 기초 지식(고등학교 수준의 행렬이나 미분 개념을 아는 분)을 가진 분들을 대상으로, Transformer의 심장부인 &amp;lsquo;Self-Attention 메커니즘&amp;rsquo;, &amp;lsquo;쿼리·키·밸류(Q/K/V) 모델&amp;rsquo;, &amp;lsquo;Softmax 함수를 통한 정규화&amp;rsquo;, 그리고 &amp;lsquo;Positional Encoding&amp;rsquo; 등의 수학적 구조를 철저하고 알기 쉽게 풀어내고자 합니다.&lt;/p>
&lt;p>수식의 나열에 압도될 수도 있지만, 하나하나의 계산에는 명확한 &amp;lsquo;의미&amp;rsquo;가 있습니다. 이 글을 다 읽을 즈음에는 Transformer가 단순한 마법의 블랙박스가 아니라, 정교하게 설계된 수학과 통계의 결정체라는 것을 이해할 수 있을 것입니다.&lt;/p>
&lt;hr>
&lt;h1 id="1-기존-기법의-한계와-transformer의-혁신성">1. 기존 기법의 한계와 Transformer의 혁신성
&lt;/h1>&lt;p>Transformer가 등장하기 전, 자연어 처리의 주류는 순환 신경망(RNN)이나 그 파생형인 LSTM(Long Short-Term Memory)이었습니다. RNN은 시계열 데이터를 처리하기 위해 설계되었으며, 문장을 단어 단위로 처음부터 순서대로 읽어 들입니다.&lt;/p>
&lt;p>하지만 RNN에는 치명적인 약점이 두 가지 있었습니다.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>장기 의존성 학습이 어려움&lt;/strong>: 문장이 길어지면 처음에 입력된 단어의 정보가 마지막에 도달할 때쯤이면 희미해집니다(기울기 소실 문제).&lt;/li>
&lt;li>&lt;strong>병렬 계산이 불가능함&lt;/strong>: 단어를 순서대로 처리해야 하므로, GPU를 이용한 대규모 병렬 계산이 어려워 학습에 막대한 시간이 걸립니다.&lt;/li>
&lt;/ol>
&lt;p>Transformer는 RNN의 구조를 완전히 버리고, 오직 &amp;lsquo;Attention&amp;rsquo;만을 사용하여 문맥을 파악한다는 패러다임 시프트를 일으켰습니다. 이를 통해 시퀀스 길이가 아무리 길어도 정보의 손실이 없으며, 연산을 병렬화하여 GPU의 성능을 극대화하는 것이 가능해진 것입니다.&lt;/p>
&lt;hr>
&lt;h1 id="2-transformer의-전체-아키텍처">2. Transformer의 전체 아키텍처
&lt;/h1>&lt;p>먼저 Transformer 전체의 아키텍처를 조감해 봅시다. Transformer는 크게 나누어 &amp;lsquo;Encoder(인코더)&amp;lsquo;와 &amp;lsquo;Decoder(디코더)&amp;lsquo;라는 두 개의 블록으로 구성되어 있습니다. 번역 작업을 예로 들면, Encoder가 입력 언어(예: 영어)를 수학적인 벡터 표현으로 변환하고, Decoder가 그 벡터 표현을 바탕으로 출력 언어(예: 한국어)를 생성합니다.&lt;/p>
&lt;p>아래 그림은 Encoder 블록의 내부 구조를 간략화한 것입니다.&lt;/p>
&lt;div class="mermaid">graph TD
A["입력 토큰(Input Tokens)"] --> B["입력 임베딩(Input Embedding)"]
B --> C["위치 인코딩(Positional Encoding)"]
C --> D["멀티 헤드 셀프 어텐션(Multi-Head Self-Attention)"]
D --> E["더하기 &amp; 층 정규화(Add &amp; Layer Normalization)"]
E --> F["피드 포워드 네트워크(Feed Forward Network)"]
F --> G["더하기 &amp; 층 정규화(Add &amp; Layer Normalization)"]
G --> H["다음 층으로 출력(Output to Next Layer)"]
C -.->|"잔차 연결(Residual Connection)"| E
E -.->|"잔차 연결(Residual Connection)"| G&lt;/div>
&lt;p>지금부터는 각 컴포넌트에서 수행되는 수학적인 조작을 순서대로 살펴보겠습니다.&lt;/p>
&lt;hr>
&lt;h1 id="3-단어의-벡터화와-위치-인코딩positional-encoding">3. 단어의 벡터화와 위치 인코딩(Positional Encoding)
&lt;/h1>&lt;p>컴퓨터는 텍스트를 있는 그대로 이해할 수 없습니다. 입력된 텍스트는 먼저 &amp;lsquo;토큰(Token)&amp;lsquo;이라는 단위로 분할되고, 각각이 고정 길이의 벡터로 변환됩니다. 이것이 &lt;strong>Input Embedding&lt;/strong>입니다.&lt;/p>
&lt;h2 id="31-input-embedding의-수학">3.1 Input Embedding의 수학
&lt;/h2>&lt;p>어휘(Vocabulary)의 크기를 $V$, 임베딩 벡터의 차원 수를 $d_{model}$(원본 논문에서는 $d_{model} = 512$)이라고 합시다. 각 단어 $w_i$ 는 임베딩 행렬 $W_E \in \mathbb{R}^{V \times d_{model}}$ 을 사용하여 벡터 $x_i \in \mathbb{R}^{d_{model}}$ 로 변환됩니다.&lt;/p>
$$ x_i = W_E \cdot \text{one\_hot}(w_i) $$
&lt;p>이에 따라 문장 전체는 행렬 $X \in \mathbb{R}^{N \times d_{model}}$ 로 표현됩니다($N$ 은 문장의 길이).&lt;/p>
&lt;h2 id="32-positional-encoding위치-인코딩의-필요성과-수식">3.2 Positional Encoding(위치 인코딩)의 필요성과 수식
&lt;/h2>&lt;p>Transformer는 RNN처럼 단어를 순서대로 처리하는 것이 아니라, 모든 단어를 동시에 병렬 처리합니다. 이는 계산 속도 측면에서는 큰 장점이지만, 동시에 &lt;strong>&amp;lsquo;단어의 순서(어순)&amp;lsquo;라는 중요한 정보가 손실되어 버린다&lt;/strong>는 문제를 야기합니다. 예를 들어 &amp;ldquo;개가 사람을 문다&amp;quot;와 &amp;ldquo;사람이 개를 문다&amp;quot;는 입력되는 단어 집합은 같지만 의미는 전혀 다릅니다.&lt;/p>
&lt;p>이 어순 정보를 모델에 제공하기 위해 고안된 것이 &lt;strong>Positional Encoding&lt;/strong>입니다.
위치 $pos$ 에 있는 단어의 $i$ 번째 차원에 대한 Positional Encoding $PE$ 는 다음 삼각 함수를 사용하여 계산됩니다.&lt;/p>
$$ PE_{(pos, 2i)} = \sin\left(\frac{pos}{10000^{2i/d_{model}}}\right) $$
$$ PE_{(pos, 2i+1)} = \cos\left(\frac{pos}{10000^{2i/d_{model}}}\right) $$
&lt;p>여기서 $pos$ 는 단어의 위치($0, 1, 2, \dots, N-1$), $i$ 는 벡터 차원의 인덱스($0, 1, \dots, d_{model}/2 - 1$)입니다.&lt;/p>
&lt;h3 id="왜-사인과-코사인을-사용할까">왜 사인과 코사인을 사용할까?
&lt;/h3>&lt;p>언뜻 보면 매우 복잡하고 기묘한 수식처럼 보이지만, 여기에는 깊은 수학적인 이유가 있습니다. 삼각 함수를 사용함으로써 모델은 &lt;strong>&amp;lsquo;절대적인 위치&amp;rsquo;뿐만 아니라 &amp;lsquo;상대적인 위치&amp;rsquo;의 차이&lt;/strong>를 쉽게 학습할 수 있게 되는 것입니다.&lt;/p>
&lt;p>고등학교 수학에서 배우는 삼각 함수의 덧셈 정리를 떠올려 보세요.
&lt;/p>
$$ \sin(\alpha + \beta) = \sin\alpha \cos\beta + \cos\alpha \sin\beta $$
$$ \cos(\alpha + \beta) = \cos\alpha \cos\beta - \sin\alpha \sin\beta $$
&lt;p>어떤 위치 $pos$ 에서 오프셋 $k$ 만큼 떨어진 위치 $pos + k$ 의 Positional Encoding은, 위치 $pos$ 의 Positional Encoding의 선형 결합으로 표현할 수 있습니다. 즉, 행렬 $M_k$ 를 사용하여 다음과 같이 쓸 수 있습니다.&lt;/p>
$$ PE_{pos+k} = M_k \cdot PE_{pos} $$
&lt;p>이를 통해 Attention 메커니즘은 단어끼리 &amp;lsquo;얼마나 떨어져 있는지&amp;rsquo;라는 상대적 거리를 내적 계산을 통해 쉽게 인식할 수 있게 됩니다. 또한, 파장이 다른 여러 사인/코사인 파동을 조합함으로써 아무리 긴 문장이라도 고유한 위치 벡터를 생성할 수 있다는 장점도 있습니다.&lt;/p>
&lt;p>최종적인 입력 행렬 $X_{input}$ 은 단어의 임베딩 벡터에 이 위치 인코딩을 더한 것이 됩니다.&lt;/p>
$$ X_{input} = X + PE $$
&lt;hr>
&lt;h1 id="4-self-attention셀프-어텐션-메커니즘의-심오한-수학">4. Self-Attention(셀프 어텐션 메커니즘)의 심오한 수학
&lt;/h1>&lt;p>드디어 Transformer의 가장 중요한 컴포넌트인 **Self-Attention(셀프 어텐션 메커니즘)**으로 들어갑니다. Self-Attention의 목적은 &amp;lsquo;문장 내의 모든 단어들 사이의 연관도를 계산하여, 각 단어의 벡터를 문맥을 고려한 더 풍부한 표현으로 갱신하는 것&amp;rsquo;입니다.&lt;/p>
&lt;p>여기서는 &amp;lsquo;검색 시스템&amp;rsquo;의 비유가 사용됩니다.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Query (Q)&lt;/strong>: 쿼리(검색어). &amp;ldquo;내가 지금 찾고 있는 정보는 무엇인가?&amp;rdquo;&lt;/li>
&lt;li>&lt;strong>Key (K)&lt;/strong>: 키(색인/제목). &amp;ldquo;내가 가지고 있는 정보는 무엇인가?&amp;rdquo;&lt;/li>
&lt;li>&lt;strong>Value (V)&lt;/strong>: 밸류(실체/내용). &amp;ldquo;내가 실제로 제공할 정보는 무엇인가?&amp;rdquo;&lt;/li>
&lt;/ul>
&lt;h2 id="41-행렬-q-k-v-의-생성">4.1 행렬 $Q, K, V$ 의 생성
&lt;/h2>&lt;p>입력 행렬 $X \in \mathbb{R}^{N \times d_{model}}$ (여기서는 설명을 간단히 하기 위해 배치 크기는 무시합니다)에 대하여 학습 가능한 가중치 행렬 $W^Q, W^K, W^V \in \mathbb{R}^{d_{model} \times d_k}$ 를 곱함으로써 쿼리 $Q$, 키 $K$, 밸류 $V$ 를 계산합니다. (통상적으로 $d_k = d_v = d_{model} / h$)&lt;/p>
$$ Q = X W^Q $$
$$ K = X W^K $$
$$ V = X W^V $$
&lt;p>여기서 $Q, K, V$ 는 모두 $\mathbb{R}^{N \times d_k}$ 의 행렬이 됩니다.&lt;/p>
&lt;h2 id="42-attention-score의-계산내적">4.2 Attention Score의 계산(내적)
&lt;/h2>&lt;p>각 단어의 Query가 다른 모든 단어의 Key와 얼마나 연관되어 있는지를 측정하기 위해 벡터의 &lt;strong>내적&lt;/strong>을 계산합니다. 행렬 연산으로 쓰면 다음과 같습니다.&lt;/p>
$$ \text{Scores} = Q K^T $$
&lt;p>이 계산을 통해 얻어지는 행렬 $\text{Scores} \in \mathbb{R}^{N \times N}$ 의 각 원소 $s_{ij}$ 는, $i$ 번째 단어의 Query와 $j$ 번째 단어의 Key의 내적, 즉 &amp;lsquo;연관도의 강도&amp;rsquo;를 나타냅니다.&lt;/p>
&lt;h2 id="43-스케일링scale">4.3 스케일링(Scale)
&lt;/h2>&lt;p>내적을 통한 점수 계산에는 한 가지 문제가 있습니다. 벡터의 차원 $d_k$ 가 커지면 내적의 값이 극단적으로 커지거나 작아져 버리는 것입니다.&lt;/p>
&lt;p>이를 수학적으로 증명해 봅시다.
쿼리의 각 원소 $q \sim \mathcal{N}(0, 1)$, 키의 각 원소 $k \sim \mathcal{N}(0, 1)$ 이 독립적인 표준 정규 분포를 따른다고 가정합니다.
내적 $q \cdot k = \sum_{i=1}^{d_k} q_i k_i$ 의 평균과 분산을 구합니다.
평균： $\mathbb{E}[q_i k_i] = \mathbb{E}[q_i] \mathbb{E}[k_i] = 0 \times 0 = 0$ 이므로, 합의 평균도 $0$.
분산： $q_i k_i$ 의 분산은 독립성에 의해 $\text{Var}(q_i k_i) = \mathbb{E}[(q_i k_i)^2] - (\mathbb{E}[q_i k_i])^2 = 1 \times 1 - 0 = 1$.
따라서 내적 전체의 분산은 차원 수 $d_k$ 와 같아집니다.&lt;/p>
$$ \text{Var}(q \cdot k) = d_k $$
&lt;p>분산이 커지면 이후에 적용할 Softmax 함수에서 최댓값 이외의 기울기가 극단적으로 작아지는 &amp;lsquo;기울기 소실(Gradient Vanishing)&amp;lsquo;이 발생하여 학습이 진행되지 않게 됩니다.
이를 방지하기 위해 점수를 $\sqrt{d_k}$ 로 나누어(스케일링하여) 분산을 항상 $1$ 로 유지하도록 합니다.&lt;/p>
$$ \text{Scaled Scores} = \frac{Q K^T}{\sqrt{d_k}} $$
&lt;h2 id="44-softmax-함수를-통한-확률화">4.4 Softmax 함수를 통한 확률화
&lt;/h2>&lt;p>얻은 점수를 합계가 $1$ 이 되는 확률 분포(가중치)로 변환하기 위해 &lt;strong>Softmax 함수&lt;/strong>를 행 단위로 적용합니다.&lt;/p>
$$ a_{ij} = \text{softmax}(s_i)_j = \frac{\exp(s_{ij} / \sqrt{d_k})}{\sum_{m=1}^N \exp(s_{im} / \sqrt{d_k})} $$
&lt;p>행렬 $A \in \mathbb{R}^{N \times N}$ 은 Attention Weight(주의 가중치) 행렬이라고 불립니다. 이 행렬의 각 행 $i$ 를 보면 &amp;ldquo;단어 $i$ 를 이해하는 데 있어, 다른 어떤 단어 $j$ 에 얼마나 주목(Attention)해야 하는가&amp;quot;가 0에서 1 사이의 값으로 표현되어 있습니다.&lt;/p>
&lt;h2 id="45-value의-가중합">4.5 Value의 가중합
&lt;/h2>&lt;p>마지막으로, 얻은 Attention Weight 행렬 $A$ 를 사용하여 Value 행렬 $V$ 의 가중합(Weighted Sum)을 계산합니다.&lt;/p>
$$ \text{Output} = A V = \text{softmax}\left(\frac{Q K^T}{\sqrt{d_k}}\right) V $$
&lt;p>이 연산을 통해 출력되는 행렬 $Z \in \mathbb{R}^{N \times d_v}$ 는 &amp;lsquo;문맥을 고려하여 갱신된 단어의 벡터 표현&amp;rsquo;들의 집합이 됩니다.
이것이 논문에서 정의하고 있는 &lt;strong>Scaled Dot-Product Attention&lt;/strong>의 전모입니다.&lt;/p>
&lt;hr>
&lt;h1 id="5-multi-head-attention멀티-헤드-어텐션">5. Multi-Head Attention(멀티 헤드 어텐션)
&lt;/h1>&lt;p>한 번의 Attention 계산(싱글 헤드)만으로는 하나의 관점(예를 들어 &amp;lsquo;문법적인 관계&amp;rsquo;)에서밖에 문맥을 포착하지 못할 가능성이 있습니다. 그래서 언어가 가진 다양한 의미적·구문적 관계(&amp;lsquo;주어와 서술어&amp;rsquo;, &amp;lsquo;대명사와 그 지시 대상&amp;rsquo; 등)를 동시에 파악하기 위해 &lt;strong>Multi-Head Attention&lt;/strong>이 도입되었습니다.&lt;/p>
&lt;p>앞서 말한 $Q, K, V$ 의 생성과 Attention 계산을 병렬로 $h$ 번(헤드의 수. 원본 논문에서는 $h=8$) 수행합니다.&lt;/p>
$$ \text{head}_i = \text{Attention}(X W_i^Q, X W_i^K, X W_i^V) $$
&lt;p>여기서 $W_i^Q, W_i^K, W_i^V \in \mathbb{R}^{d_{model} \times d_k}$ 는 $i$ 번째 헤드 전용의 학습 가능한 가중치 행렬입니다.&lt;/p>
&lt;p>각 헤드에서 출력된 결과 $\text{head}_i \in \mathbb{R}^{N \times d_v}$ 는 가로로 결합(Concatenate)됩니다.&lt;/p>
$$ \text{Concat}(\text{head}_1, \dots, \text{head}_h) \in \mathbb{R}^{N \times (h \cdot d_v)} $$
&lt;p>일반적으로 $h \cdot d_v = d_{model}$ 이 되도록 설정하기 때문에 결합 후의 차원은 다시 입력과 같은 $d_{model}$ 로 돌아옵니다. 마지막으로 이 행렬에 가중치 행렬 $W^O \in \mathbb{R}^{d_{model} \times d_{model}}$ 을 곱하여 최종적인 출력을 얻습니다.&lt;/p>
$$ \text{MultiHead}(Q, K, V) = \text{Concat}(\text{head}_1, \dots, \text{head}_h) W^O $$
&lt;div class="mermaid">graph TD
X["입력 X(Input X)"] --> Q1["Q1"]
X --> K1["K1"]
X --> V1["V1"]
Q1 &amp; K1 &amp; V1 --> H1["헤드 1(Head 1)"]
X --> Q2["Q2"]
X --> K2["K2"]
X --> V2["V2"]
Q2 &amp; K2 &amp; V2 --> H2["헤드 2(Head 2)"]
X --> QN["..."]
X --> KN["..."]
X --> VN["..."]
QN &amp; KN &amp; VN --> HN["헤드 h(Head h)"]
H1 &amp; H2 &amp; HN --> C["결합(Concatenate)"]
C --> WO["WO 곱하기(Multiply by WO)"]
WO --> OUT["멀티 헤드 출력(Multi-Head Output)"]&lt;/div>
&lt;hr>
&lt;h1 id="6-feed-forward-neural-network-ffn">6. Feed-Forward Neural Network (FFN)
&lt;/h1>&lt;p>Multi-Head Attention의 출력은 다음으로 **Position-wise Feed-Forward Network (FFN)**에 입력됩니다.
이는 시퀀스의 &amp;lsquo;각 위치(단어)마다 독립적으로&amp;rsquo; 적용되는 2층의 완전 연결 신경망(Fully Connected Neural Network)입니다.&lt;/p>
&lt;p>수식으로 나타내면 다음과 같습니다.&lt;/p>
$$ \text{FFN}(x) = \max(0, x W_1 + b_1) W_2 + b_2 $$
&lt;p>여기서 $\max(0, z)$ 는 ReLU(Rectified Linear Unit) 활성화 함수를 나타냅니다(최근의 모델에서는 GELU나 SwiGLU가 사용되는 경우도 많습니다).&lt;/p>
&lt;p>이 네트워크의 역할은 매우 중요합니다. Attention 메커니즘은 &amp;lsquo;단어 간의 관계성(공간적·순차적인 관계)&amp;lsquo;을 학습하지만, FFN은 &amp;lsquo;각 단어 벡터 자체의 비선형적인 특징 변환&amp;rsquo;을 담당합니다.
통상적으로 제1층의 가중치 $W_1$ 을 통해 차원을 일시적으로 크게 확대(예를 들어 $d_{model}=512$ 에서 $d_{ff}=2048$ 로 4배 확대)하여 특징 공간에서 복잡한 계산을 수행한 후, 제2층의 가중치 $W_2$ 로 다시 원래의 차원으로 되돌립니다. 이러한 &amp;lsquo;차원의 확대와 축소&amp;rsquo;를 통해 모델의 표현력이 비약적으로 향상됩니다.&lt;/p>
&lt;hr>
&lt;h1 id="7-잔차-연결residual-connection과-layer-normalization">7. 잔차 연결(Residual Connection)과 Layer Normalization
&lt;/h1>&lt;p>딥러닝에서 네트워크의 층을 깊게 쌓아가면 학습 시 기울기가 소실되거나 폭발해 버려 제대로 학습할 수 없게 되는 문제가 발생합니다. 이를 방지하기 위해 Transformer의 각 서브 레이어(Attention과 FFN) 주변에는 **잔차 연결(Residual Connection)**과 **Layer Normalization(층 정규화)**가 배치되어 있습니다.&lt;/p>
&lt;p>수식으로 쓰면 서브 레이어의 출력은 다음과 같이 처리됩니다.&lt;/p>
$$ \text{Output} = \text{LayerNorm}(x + \text{Sublayer}(x)) $$
&lt;h2 id="71-잔차-연결-x--textsublayerx">7.1 잔차 연결 ($x + \text{Sublayer}(x)$)
&lt;/h2>&lt;p>입력 $x$ 를 서브 레이어의 출력에 직접 더합니다. 이를 통해 역전파(Backpropagation) 시 기울기가 지름길(Shortcut)을 통해 얕은 층으로 직접 전달되므로 층이 깊어져도 학습이 안정됩니다.&lt;/p>
&lt;h2 id="72-layer-normalization의-수학">7.2 Layer Normalization의 수학
&lt;/h2>&lt;p>Layer Normalization은 특징 차원 방향의 평균과 분산을 계산하여 데이터를 정규화하는 기술입니다. 배치 크기 $B$, 시퀀스 길이 $N$, 차원 수 $d_{model}$ 의 입력에서 어떤 하나의 단어 벡터 $x \in \mathbb{R}^{d_{model}}$ 에 대해 정규화를 수행합니다.&lt;/p>
&lt;p>평균 $\mu$ 와 분산 $\sigma^2$ 을 계산합니다.
&lt;/p>
$$ \mu = \frac{1}{d_{model}} \sum_{i=1}^{d_{model}} x_i $$
$$ \sigma^2 = \frac{1}{d_{model}} \sum_{i=1}^{d_{model}} (x_i - \mu)^2 $$
&lt;p>그리고 정규화된 출력 $\hat{x}$ 를 얻습니다.
&lt;/p>
$$ \text{LN}(x) = \frac{x - \mu}{\sqrt{\sigma^2 + \epsilon}} \odot \gamma + \beta $$
&lt;p>
($\epsilon$ 은 0으로 나누는 것을 방지하기 위한 미소한 상수. $\gamma, \beta$ 는 학습 가능한 스케일 및 시프트 파라미터)&lt;/p>
&lt;p>배치 방향의 정규화(Batch Normalization)가 아닌 층 방향의 정규화(Layer Normalization)를 채택한 이유는, 문장처럼 길이가 일정하지 않은 시퀀스 데이터를 처리할 때 배치 간의 통계량이 불안정해지기 쉽기 때문입니다. Layer Normalization을 통해 Transformer는 배치 크기에 의존하지 않고 안정적인 학습이 가능해집니다.&lt;/p>
&lt;hr>
&lt;h1 id="8-디코더-특유의-구조-masked-attention과-cross-attention">8. 디코더 특유의 구조: Masked Attention과 Cross-Attention
&lt;/h1>&lt;p>지금까지 해설한 구조는 인코더의 것입니다. 문장을 생성하는 디코더 블록에서는 구조가 조금 다릅니다.&lt;/p>
&lt;h2 id="81-masked-multi-head-attention">8.1 Masked Multi-Head Attention
&lt;/h2>&lt;p>디코더의 역할은 &amp;lsquo;과거의 단어로부터 다음 단어를 예측하는 것&amp;rsquo;입니다. 따라서 훈련 시에 &amp;lsquo;미래의 단어&amp;rsquo;를 보게 되면 부정행위(커닝)가 되어 버립니다. 이를 방지하기 위한 수학적 조작이 **Masking(마스킹)**입니다.&lt;/p>
&lt;p>점수 행렬 $Q K^T$ 에 대해 상삼각 부분(미래의 정보에 해당)에 $-\infty$ 에 가까운 매우 작은 값을 설정하는 마스크 행렬 $M$ 을 더합니다.&lt;/p>
$$ M_{ij} = \begin{cases} 0 &amp; (i \le j) \\ -\infty &amp; (i > j) \end{cases} $$
$$ \text{Masked Attention}(Q, K, V) = \text{softmax}\left(\frac{Q K^T + M}{\sqrt{d_k}}\right) V $$
&lt;p>Softmax 함수를 계산할 때 $\exp(-\infty) = 0$ 이 되므로 미래 단어에 대한 Attention Weight는 완전히 $0$ 이 됩니다. 이를 통해 인과 관계(Causality)를 유지한 자기 회귀적(Autoregressive)인 생성이 가능해집니다.&lt;/p>
&lt;h2 id="82-encoder-decoder-cross-attention">8.2 Encoder-Decoder Cross-Attention
&lt;/h2>&lt;p>디코더의 두 번째 서브 레이어는 인코더의 출력을 참조하는 &lt;strong>Cross-Attention&lt;/strong>입니다.
여기서 $Q$ 는 직전 디코더 층에서 생성되지만, $K$ 와 $V$ 는 인코더의 최종 층 출력에서 생성됩니다.&lt;/p>
$$ Q_{decoder} = X_{dec} W^Q $$
$$ K_{encoder} = X_{enc} W^K $$
$$ V_{encoder} = X_{enc} W^V $$
&lt;p>이 계산을 통해 번역 작업 등에서 &amp;lsquo;현재 번역하고 있는 단어가 원래 외국어 문장의 어느 부분과 강하게 연관되어 있는지&amp;rsquo;를 모델이 학습할 수 있게 됩니다.&lt;/p>
&lt;hr>
&lt;h1 id="9-계산량과-현대의-최적화-수학">9. 계산량과 현대의 최적화 수학
&lt;/h1>&lt;p>Transformer는 훌륭한 모델이지만 그 수학적 구조로 인한 &amp;lsquo;약점&amp;rsquo;도 존재합니다.
Self-Attention의 계산량에 주목해 봅시다. 점수 행렬 $Q K^T$ 의 계산에서는 $(N \times d_k)$ 의 행렬과 $(d_k \times N)$ 의 행렬을 곱해야 하므로, 그 계산량은 **$O(N^2 \cdot d_{model})$**이 됩니다.&lt;/p>
&lt;p>즉, &lt;strong>시퀀스 길이 $N$ 에 대해 계산량과 메모리 사용량이 제곱으로 증가&lt;/strong>하는 것입니다.
문장이 짧은 경우에는 문제가 되지 않지만, 책 한 권 전체와 같은 방대한 컨텍스트를 LLM에 입력하려고 하면 $N$ 이 수만~수십만에 달해 기존의 Attention 계산에서는 GPU 메모리가 즉시 고갈되고 맙니다.&lt;/p>
&lt;p>이 $O(N^2)$ 의 저주를 끊어내기 위해 최근에는 수학적·하드웨어적 접근을 통한 다양한 최적화 기법이 제안되고 있습니다.
그 대표적인 예가 &lt;strong>FlashAttention&lt;/strong>입니다. FlashAttention은 GPU의 메모리 계층(SRAM과 HBM) 간의 데이터 전송(메모리 액세스)을 최소화하도록 Attention 계산을 타일 모양으로 분할(Tiling)하여 수행하는 알고리즘입니다. 수식상으로는 표준적인 Attention과 완전히 같은 결과를 출력(Exact Attention)함에도 불구하고, 하드웨어 수준의 최적화를 통해 극적인 속도 향상과 메모리 절감을 실현하였고 GPT-4와 같은 긴 문맥 모델의 구현을 가능하게 했습니다.&lt;/p>
&lt;p>그 밖에도 계산량을 $O(N \log N)$ 이나 $O(N)$ 으로 근사하는 Sparse Attention이나 Linear Attention 등의 연구도 활발히 진행되고 있습니다.&lt;/p>
&lt;hr>
&lt;h1 id="10-구현의-이미지pytorch-스타일의-의사-코드">10. 구현의 이미지(PyTorch 스타일의 의사 코드)
&lt;/h1>&lt;p>지금까지의 수학적 구조를 실제 프로그래밍 코드(Python / PyTorch)로 구현해 보면 놀라울 정도로 단순하게 작성할 수 있다는 것을 알 수 있습니다. Self-Attention의 핵심 부분에 대한 의사 코드(Pseudo-code)를 보여드립니다.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">torch&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">torch.nn.functional&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="nn">F&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">math&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">scaled_dot_product_attention&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">q&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">k&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">v&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">mask&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">None&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># q, k, v의 형태: [batch_size, num_heads, seq_length, d_k]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">d_k&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">q&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">size&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 1. 내적을 통한 점수 계산: Q * K^T&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 마지막 두 차원을 전치(transpose)하여 행렬곱(matmul) 계산&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">scores&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">torch&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">matmul&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">q&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">k&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">transpose&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 2. 스케일링&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">scores&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">scores&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">math&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">sqrt&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">d_k&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 3. 마스킹 (Masked Attention의 경우)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">mask&lt;/span> &lt;span class="ow">is&lt;/span> &lt;span class="ow">not&lt;/span> &lt;span class="kc">None&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">scores&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">scores&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">masked_fill&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mask&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="mf">1e9&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 4. Softmax를 통한 확률화&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">attention_weights&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">F&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">softmax&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">scores&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">dim&lt;/span>&lt;span class="o">=-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 5. Value 행렬 곱하기&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">output&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">torch&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">matmul&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">attention_weights&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">v&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">output&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">attention_weights&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>수식으로 표현된 $Q K^T / \sqrt{d_k}$ 가 &lt;code>torch.matmul(q, k.transpose(-2, -1)) / math.sqrt(d_k)&lt;/code> 로 직관적으로 구현되어 있다는 것을 알 수 있습니다. 수학의 이론이 고도화된 최적화 라이브러리의 힘을 빌려 단 몇 줄의 코드로 구현된다는 것은 딥러닝의 매우 흥미로운 측면입니다.&lt;/p>
&lt;hr>
&lt;h1 id="맺음말-수식에서-보이는-지능의-형태">맺음말: 수식에서 보이는 &amp;lsquo;지능&amp;rsquo;의 형태
&lt;/h1>&lt;p>본 문서에서는 Transformer 모델의 심오한 곳에 있는 수학적 구조를 파헤쳐 보았습니다.&lt;/p>
&lt;p>단어를 다차원 벡터 공간에 매핑하는 Embedding, 위치 정보를 삼각파의 합성으로 표현하는 Positional Encoding, 그리고 정보 검색의 비유에서 비롯된 행렬의 내적 계산인 Self-Attention 메커니즘. 이들 하나하나의 컴포넌트는 선형대수학, 미적분학, 확률과 통계라는 기초적인 수학이 쌓여 이루어진 것에 불과합니다.&lt;/p>
&lt;p>하지만 이러한 단순한 행렬 연산이 겹겹이 쌓이고, 수십억 수천억 개의 파라미터를 통해 거대한 데이터 세트로부터 패턴을 학습할 때, 그곳에는 우리의 &amp;lsquo;언어&amp;rsquo;를 이해하고 논리적인 추론을 하며 때로는 창의적인 아이디어를 만들어 내는 것 같은 &amp;lsquo;지능의 형태&amp;rsquo;가 나타납니다.&lt;/p>
&lt;p>&amp;lsquo;Attention Is All You Need&amp;rsquo;라는 도발적인 제목이 보여주듯 복잡한 순환 처리나 합성곱 처리를 버리고 순수한 &amp;lsquo;어텐션(연관도)&amp;rsquo; 계산에 특화된 이 아키텍처의 아름다움은 바로 그 수학적인 단순함에 있다고 할 수 있을 것입니다.&lt;/p>
&lt;p>앞으로 Transformer를 뛰어넘는 새로운 아키텍처(State Space Model인 Mamba 등)가 등장할 가능성도 있지만, Transformer가 구축한 &amp;lsquo;Attention을 통한 문맥 이해&amp;rsquo;의 수학적 프레임워크는 AI의 역사에 영원히 새겨질 것입니다.&lt;/p>
&lt;p>만약 여러분이 앞으로 ChatGPT나 Claude 등의 LLM을 사용할 기회가 있다면, 그 백그라운드에서 초당 수조 번의 $Q K^T$ 행렬곱이 계산되고 Softmax 함수가 확률을 산출해 내는 모습을 상상해 보세요. 기술에 대한 해상도가 높아져 AI의 세계가 더욱 흥미롭게 느껴질 것입니다.&lt;/p>
&lt;h3 id="참고-문헌">참고 문헌
&lt;/h3>&lt;ul>
&lt;li>Vaswani, A., et al. (2017). &amp;ldquo;Attention Is All You Need.&amp;rdquo; &lt;em>Advances in Neural Information Processing Systems&lt;/em>.&lt;/li>
&lt;li>Alammar, J. (2018). &amp;ldquo;The Illustrated Transformer.&amp;rdquo;&lt;/li>
&lt;/ul>
&lt;hr>
&lt;p>&lt;em>이 글은 자연어 처리와 AI의 수학적 기초를 배우려는 분들을 위한 가이드로 작성되었습니다. 질문이나 토론할 내용이 있다면 꼭 댓글로 알려주세요!&lt;/em>&lt;/p></description></item><item><title>llama.cpp의 양자화 기술(GGUF)의 원리 해설</title><link>http://kenji.blog/ko/p/llama-cpp-quantization-gguf/</link><pubDate>Fri, 11 Sep 2026 00:00:00 +0900</pubDate><guid>http://kenji.blog/ko/p/llama-cpp-quantization-gguf/</guid><description>&lt;img src="http://kenji.blog/p/llama-cpp-quantization-gguf/img/eyecatch.jpg" alt="Featured image of post llama.cpp의 양자화 기술(GGUF)의 원리 해설" />&lt;h2 id="1-들어가며-왜-llm에는-양자화가-필요한가">1. 들어가며: 왜 LLM에는 양자화가 필요한가?
&lt;/h2>&lt;p>최근 대규모 언어 모델(LLM: Large Language Models)의 발전은 눈부실 정도이지만, 그 이면에는 &amp;lsquo;컴퓨팅 자원의 고갈&amp;rsquo;과 &amp;lsquo;메모리 대역폭의 병목 현상&amp;rsquo;이라는 심각한 문제가 떠오르고 있습니다. 예를 들어, Llama 3와 같은 70B(700억) 파라미터 모델을 표준적인 16비트 부동소수점(FP16)으로 메모리에 로드할 경우, 파라미터만으로 약 140GB의 VRAM/RAM을 소비합니다. 여기에 추론 시의 컨텍스트(KV 캐시)가 더해지면, 데이터센터용 하이엔드 GPU(NVIDIA A100 80GB나 H100 80GB)를 여러 대 클러스터링하지 않고서는 작동하지 않습니다.&lt;/p>
&lt;p>개인 개발자나 엣지 디바이스(MacBook이나 일반적인 게이밍 PC)에서 LLM을 구동시키기 위한 구세주로 등장한 것이 &lt;strong>llama.cpp&lt;/strong>와 그 핵심을 이루는 &lt;strong>양자화(Quantization) 기술&lt;/strong>입니다. 특히 **GGUF (GPT-Generated Unified Format)**라는 파일 포맷과 &lt;strong>k-quants&lt;/strong>라고 불리는 고도화된 블록 단위의 양자화 알고리즘은, 모델의 정확도(Perplexity) 저하를 극한까지 억제하면서 모델 크기를 몇 분의 일로 압축하는 획기적인 기법입니다.&lt;/p>
&lt;p>본 문서에서는 이 llama.cpp에서의 양자화의 수학적 배경부터 GGML 형식과의 차이, GGUF 포맷의 상세한 구조, 그리고 k-quants의 내부 메커니즘에 이르기까지 철저하게 해설합니다.&lt;/p>
&lt;hr>
&lt;h2 id="2-양자화quantization의-수학적-기초">2. 양자화(Quantization)의 수학적 기초
&lt;/h2>&lt;p>LLM의 문맥에서 양자화란, 연속적인 값(또는 고정밀도의 부동소수점 수)을 더 적은 비트 수(INT8, INT4, INT3 등)의 이산적인 값으로 매핑하는 작업을 의미합니다.&lt;/p>
&lt;h3 id="21-선형-양자화의-기본-수식">2.1. 선형 양자화의 기본 수식
&lt;/h3>&lt;p>가장 단순한 접근법은 선형 양자화(Min-Max 양자화)입니다. 원래의 고정밀도 가중치 텐서를 $W$, 양자화된 정수 텐서를 $W_q$라고 합시다.&lt;/p>
$$ W_q = \text{round}\left( \frac{W}{S} \right) + Z $$
&lt;p>여기서,&lt;/p>
&lt;ul>
&lt;li>$S$는 **스케일 팩터(Scale Factor)**이며, 양자화의 스텝 크기(해상도)를 결정합니다.&lt;/li>
&lt;li>$Z$는 **제로 포인트(Zero-point)**이며, 실수의 $0.0$이 양자화 후의 어떤 정수값에 대응할지를 시프트하기 위한 바이어스 값입니다.&lt;/li>
&lt;li>$\text{round}(\cdot)$는 가장 가까운 정수로의 반올림 함수입니다.&lt;/li>
&lt;/ul>
&lt;p>역양자화(Dequantization)를 통해, 추론 시에는 근사적인 실수 가중치 $\tilde{W}$를 복원합니다.&lt;/p>
$$ \tilde{W} = S \times (W_q - Z) $$
&lt;h3 id="22-대칭-양자화-vs-비대칭-양자화">2.2. 대칭 양자화 vs 비대칭 양자화
&lt;/h3>&lt;p>제로 포인트 $Z$를 다루는 방식에 따라 크게 두 가지 방식으로 나뉩니다.&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>비대칭 양자화 (Asymmetric Quantization)&lt;/strong>
데이터의 최솟값 $W_{\min}$과 최댓값 $W_{\max}$를 사용하여 매핑합니다.
&lt;/p>
$$ S = \frac{W_{\max} - W_{\min}}{2^b - 1}, \quad Z = \text{round}\left(-\frac{W_{\min}}{S}\right) $$
&lt;p>
여기서 $b$는 양자화 비트 수입니다(예: 4비트라면 $2^4-1 = 15$). $Z$를 유지할 필요가 있기 때문에 계산과 메모리 오버헤드가 약간 증가합니다.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>대칭 양자화 (Symmetric Quantization)&lt;/strong>
데이터의 절댓값의 최댓값을 사용하여 0을 중심으로 매핑합니다($Z=0$).
&lt;/p>
$$ S = \frac{\max(|W_{\max}|, |W_{\min}|)}{2^{b-1} - 1}, \quad Z = 0 $$
&lt;p>
llama.cpp의 초창기 양자화(예를 들어 레거시인 Q4_0 등)는 대칭 양자화를 채택하고 있으며, $Z$ 항이 없기 때문에 SIMD 명령에서의 내적 계산이 매우 빨라진다는 장점이 있습니다.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="3-ggml에서-gguf로의-진화와-파일-구조">3. GGML에서 GGUF로의 진화와 파일 구조
&lt;/h2>&lt;p>llama.cpp를 논할 때 빼놓을 수 없는 것이, C++로 작성된 텐서 연산 라이브러리 &lt;strong>GGML&lt;/strong>과 거기서 파생된 파일 포맷 &lt;strong>GGUF&lt;/strong>입니다.&lt;/p>
&lt;h3 id="31-ggml의-과제">3.1. GGML의 과제
&lt;/h3>&lt;p>초기 llama.cpp는 &lt;code>ggml&lt;/code> 포맷(및 &lt;code>ggjt&lt;/code> 등의 변종)을 사용하고 있었습니다. 하지만 이들에는 다음과 같은 문제가 있었습니다.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>확장성 부족:&lt;/strong> 매직 넘버나 하이퍼파라미터가 고정 길이·고정 순서로 하드코딩되어 있어, 새로운 모델 아키텍처(예: Llama, Falcon, Mixtral 등)나 새로운 토크나이저를 추가할 때마다 파괴적인 변경이 발생했습니다.&lt;/li>
&lt;li>&lt;strong>하위 호환성 상실:&lt;/strong> 포맷이 빈번하게 업데이트되어, 오래된 모델 파일을 최신 llama.cpp에서 읽어 들이지 못하는 사태가 다수 발생했습니다.&lt;/li>
&lt;/ul>
&lt;h3 id="32-gguf-포맷의-탄생">3.2. GGUF 포맷의 탄생
&lt;/h3>&lt;p>2023년 8월에 도입된 &lt;strong>GGUF&lt;/strong>는 이러한 문제들을 해결하기 위해 설계된 범용성 높은 포맷입니다. 가장 큰 특징은 &lt;strong>키-값(Key-Value) 기반의 메타데이터 구조&lt;/strong>를 채택했다는 것입니다.&lt;/p>
&lt;p>아래의 Mermaid 다이어그램은 GGUF의 파일 구조를 추상화한 것입니다.&lt;/p>
&lt;div class="mermaid">graph TD
A["GGUF 파일"] --> B["헤더 (매직, 버전)"]
A --> C["메타데이터 (키-값 쌍)"]
A --> D["텐서 정보 (이름, 셰이프, 오프셋)"]
A --> E["텐서 데이터 (바이너리 페이로드)"]
C --> C1["general.architecture: llama"]
C --> C2["llama.context_length: 4096"]
C --> C3["tokenizer.ggml.tokens: [...]"]
E --> E1["레이어 0 가중치"]
E --> E2["레이어 1 가중치"]
E --> E3["..."]&lt;/div>
&lt;p>&lt;strong>GGUF의 주요 이점:&lt;/strong>&lt;/p>
&lt;ol>
&lt;li>&lt;strong>유연성:&lt;/strong> 모델의 하이퍼파라미터나 RoPE(Rotary Positional Embedding) 설정, 토크나이저의 어휘 데이터 등을 모두 이름이 지정된 Key-Value 쌍으로 저장합니다. 알 수 없는 키는 무시되므로 새로운 기능의 추가가 쉽습니다.&lt;/li>
&lt;li>&lt;strong>엔디안 독립성:&lt;/strong> GGUF는 기본적으로 리틀 엔디안을 채택하고 있지만, 명시적으로 플래그를 가지기 때문에 다른 아키텍처 간에도 안전하게 이식 가능합니다.&lt;/li>
&lt;li>&lt;strong>mmap(메모리 매핑) 최적화:&lt;/strong> 텐서 데이터는 특정 경계로 정렬(패딩)되어 있으며, OS의 &lt;code>mmap()&lt;/code> 시스템 콜을 사용하여 디스크에서 직접 메모리 공간으로 매핑 가능합니다. 이를 통해 모델 읽기의 초기화 시간이 사실상 제로가 됩니다.&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="4-k-quants의-심연-고도화된-블록-단위-양자화">4. k-quants의 심연: 고도화된 블록 단위 양자화
&lt;/h2>&lt;p>GGUF 포맷의 진면목은 모델 가중치의 압축을 담당하는 **k-quants (K-quantization)**라는 메커니즘입니다.&lt;/p>
&lt;p>일반적인 신경망의 가중치는 레이어 전체로 보면 정규분포에 가까운 형태를 띠고 있지만, 국소적으로는 이상치(Outliers)가 존재합니다. 일률적인 스케일 팩터 $S$로 레이어 전체의 가중치를 양자화하면, 이상치에 끌려가서 작은 가중치의 정보가 완전히 소실되어 버립니다.&lt;/p>
&lt;p>이를 방지하기 위해 llama.cpp에서는 **블록 단위 양자화(Block-wise Quantization)**를 수행합니다. 가중치 텐서를 작은 블록(예를 들어 32요소나 256요소)으로 분할하고, 블록마다 고유한 스케일 팩터(및 제로 포인트)를 가지게 하는 것입니다.&lt;/p>
&lt;h3 id="41-레거시-양자화q4_0-q4_1의-한계">4.1. 레거시 양자화(Q4_0, Q4_1)의 한계
&lt;/h3>&lt;p>초기의 &lt;code>Q4_0&lt;/code>은 32개의 FP16 가중치를 하나의 블록으로 하고, 1개의 FP16 스케일 팩터를 공유했습니다.&lt;/p>
&lt;ul>
&lt;li>블록 크기: 32&lt;/li>
&lt;li>메모리: 1개의 스케일(16bit) + 32개의 4bit 가중치(128bit) = 144bit&lt;/li>
&lt;li>요소당 실효 비트 수 (bpw: bits per weight): $144 / 32 = 4.5$ bpw&lt;/li>
&lt;/ul>
&lt;p>이것만으로도 충분히 우수하지만, 정확도와 압축률의 한계가 보이기 시작했습니다. 그래서 등장한 것이 더욱 복잡하고 정교한 계층 구조를 가지는 &lt;strong>k-quants&lt;/strong>입니다.&lt;/p>
&lt;h3 id="42-슈퍼-블록과-서브-블록의-계층-구조-q4_k_m의-예">4.2. 슈퍼 블록과 서브 블록의 계층 구조 (Q4_K_M의 예)
&lt;/h3>&lt;p>k-quants는 커다란 &amp;lsquo;슈퍼 블록(Super-block)&amp;lsquo;과 그 내부에 포함되는 작은 &amp;lsquo;서브 블록(Sub-block)&amp;lsquo;이라는 계층 구조를 가집니다. 이를 통해 메타데이터(스케일 값 등) 자체의 양자화도 수행하여, 극한까지 bpw를 낮추면서 정확도를 유지합니다.&lt;/p>
&lt;p>가장 인기 있는 설정인 &lt;strong>Q4_K_M&lt;/strong>의 구조를 살펴봅시다. Q4_K_M에서는 256요소의 슈퍼 블록을 사용합니다.&lt;/p>
&lt;div class="mermaid">graph TD
A["슈퍼 블록 (256 가중치)"] --> B["스케일 메타데이터 (FP16/INT8)"]
A --> C["서브 블록 0 (32 가중치, 4-bit)"]
A --> D["서브 블록 1 (32 가중치, 4-bit)"]
A --> E["..."]
A --> F["서브 블록 7 (32 가중치, 4-bit)"]
B --> B1["슈퍼 스케일 (FP16)"]
B --> B2["서브 스케일 (8 x 6-bit)"]
B --> B3["서브 최솟값 (8 x 6-bit)"]&lt;/div>
&lt;p>C++(GGML)에서 실제 구조체는 다음과 같이 정의되어 있습니다.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// llama.cpp의 block_q4_K 개념적 구조
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="cp">#define QK_K 256
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">struct&lt;/span> &lt;span class="nc">block_q4_K&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">uint8_t&lt;/span> &lt;span class="n">d&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">];&lt;/span> &lt;span class="c1">// 슈퍼 블록 전체의 슈퍼 스케일 (FP16 x 2 등)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kt">uint8_t&lt;/span> &lt;span class="n">scales&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">12&lt;/span>&lt;span class="p">];&lt;/span> &lt;span class="c1">// 8개의 서브 블록(각 32요소)의 6-bit 스케일과 6-bit 최솟값(제로 포인트)을 팩(pack)한 데이터
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kt">uint8_t&lt;/span> &lt;span class="n">qs&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">QK_K&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">];&lt;/span> &lt;span class="c1">// 4-bit로 양자화된 가중치 데이터 (256요소 / 2 = 128 bytes)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;strong>수학적인 역양자화(Dequantization) 처리:&lt;/strong>&lt;/p>
&lt;p>서브 블록 $i$($0 \le i &lt; 8$) 내의 요소 $j$($0 \le j &lt; 32$)의 근사적인 실수값 $\tilde{W}_{i, j}$는 다음과 같이 계산됩니다.&lt;/p>
$$ \tilde{W}_{i, j} = S_{\text{super}} \times s_i \times (w_{i, j} - m_i) $$
&lt;ul>
&lt;li>$S_{\text{super}}$: 슈퍼 블록 전체의 부동소수점 스케일&lt;/li>
&lt;li>$s_i$: 서브 블록 $i$용으로 양자화된 6-bit 스케일&lt;/li>
&lt;li>$m_i$: 서브 블록 $i$용으로 양자화된 6-bit 최솟값(제로 포인트)&lt;/li>
&lt;li>$w_{i, j}$: 4-bit의 양자화 가중치 ($0 \dots 15$)&lt;/li>
&lt;/ul>
&lt;p>이 계층 구조를 통해 이상치에 대한 적응력을 유지하면서, 스케일 팩터 자체가 차지하는 메모리 양을 극적으로 감소시킵니다. Q4_K_M은 전체적으로 &lt;strong>4.8 bpw&lt;/strong> 정도를 실현합니다.&lt;/p>
&lt;h3 id="43-다양한-k-quants-옵션">4.3. 다양한 k-quants 옵션
&lt;/h3>&lt;p>llama.cpp는 목적에 따라 다양한 변형을 제공하고 있습니다. &amp;ldquo;K&amp;rdquo; 뒤의 접미사(S, M, L)는 크기의 대소를 나타냅니다.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th style="text-align:left">포맷&lt;/th>
&lt;th style="text-align:center">BPW (Bits per Weight)&lt;/th>
&lt;th style="text-align:left">개요 및 특징&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td style="text-align:left">&lt;strong>Q2_K&lt;/strong>&lt;/td>
&lt;td style="text-align:center">2.5～3.3&lt;/td>
&lt;td style="text-align:left">극한까지 압축. 정확도 저하가 뚜렷하지만, VRAM이 극단적으로 적은 환경용.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">&lt;strong>Q3_K_M&lt;/strong>&lt;/td>
&lt;td style="text-align:center">3.3&lt;/td>
&lt;td style="text-align:left">3비트 양자화의 표준. Q4보다는 열화되지만 허용 범위 내에 들어오는 경우가 많음.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">&lt;strong>Q4_K_M&lt;/strong>&lt;/td>
&lt;td style="text-align:center">4.8&lt;/td>
&lt;td style="text-align:left">&lt;strong>권장되는 스위트스팟(Sweet spot)&lt;/strong>. 모델 크기의 반감과 정확도 유지를 양립.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">&lt;strong>Q5_K_M&lt;/strong>&lt;/td>
&lt;td style="text-align:center">5.5&lt;/td>
&lt;td style="text-align:left">더 높은 정확도를 원할 경우. Q4와 FP16의 중간적인 위치.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">&lt;strong>Q6_K&lt;/strong>&lt;/td>
&lt;td style="text-align:center">6.6&lt;/td>
&lt;td style="text-align:left">FP16과 거의 동등한 Perplexity를 유지하지만, 파일 크기는 큰 편.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">&lt;strong>Q8_0&lt;/strong>&lt;/td>
&lt;td style="text-align:center">8.5&lt;/td>
&lt;td style="text-align:left">INT8 상당. 주로 추론 시의 계산용 중간 텐서나, 최종 레이어에서만 사용됨.&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>※실제 BPW는 모델의 텐서(예를 들어 Attention의 Q/K/V 프로젝션인지, FFN의 가중치인지)에 따라 혼합 양자화(Mixed Quantization)가 이루어지기 때문에, 모델 전체로 평균화됩니다. 중요한 텐서는 Q6으로, 그 외는 Q4로 양자화하는 등의 최적화가 내부적으로 수행되고 있습니다.&lt;/p>
&lt;hr>
&lt;h2 id="5-추론-시의-성능-최적화-simd와-cuda-아키텍처">5. 추론 시의 성능 최적화: SIMD와 CUDA 아키텍처
&lt;/h2>&lt;p>GGUF 모델을 메모리에 로드한 것만으로는 추론이 빨라지지 않습니다. LLM 추론의 대부분은 &amp;lsquo;행렬곱(Matrix-Vector Multiplication, 줄여서 GEMV, 혹은 Matrix-Matrix, GEMM)&amp;lsquo;입니다. 양자화된 가중치와, FP16(또는 FP32)으로 유지되고 있는 활성화(입력 데이터)의 적화(Multiply-Accumulate) 연산을 어떻게 고속화할지가 관건입니다.&lt;/p>
&lt;h3 id="51-cpu-환경에서의-simd-명령-활용">5.1. CPU 환경에서의 SIMD 명령 활용
&lt;/h3>&lt;p>llama.cpp가 CPU 추론에서 경이적인 속도를 자랑하는 것은, 어셈블리 레벨에서의 &lt;strong>SIMD (Single Instruction, Multiple Data)&lt;/strong> 최적화에 있습니다.
예를 들어 Intel/AMD의 CPU에서는 &lt;strong>AVX2&lt;/strong>나 &lt;strong>AVX-512&lt;/strong>, Apple Silicon에서는 &lt;strong>ARM 대각선(NEON)&lt;/strong> 명령셋을 십분 활용합니다.&lt;/p>
&lt;p>추론 중, 굳이 $W_q$를 FP32로 되돌려서(Dequantize해서) 곱셈을 수행하는 것은 아닙니다.
활성화 측도 블록 단위로 동적 양자화(Dynamic Quantization, 보통은 INT8로 양자화)하고, &lt;strong>INT8 $\times$ INT4&lt;/strong>의 정수 연산을 SIMD의 특수한 점곱 명령(예: &lt;code>vdpaddd&lt;/code>나 &lt;code>_mm256_madd_epi16&lt;/code>)을 사용하여 단숨에 계산합니다. 최종적인 누산기(Accumulator)에서 FP32로 되돌려 스케일 팩터를 곱함으로써 경이적인 처리량을 실현하고 있습니다.&lt;/p>
&lt;h3 id="52-gpu-환경-cublas--cuda에서의-오프로드">5.2. GPU 환경 (cuBLAS / CUDA)에서의 오프로드
&lt;/h3>&lt;p>최근의 llama.cpp는 CPU뿐만 아니라, NVIDIA GPU에 대한 강력한 지원(CUBLAS / CUDA)도 가지고 있습니다.
GGUF 파일의 일부 또는 전부의 레이어를 VRAM으로 오프로드하는 것이 가능합니다(&lt;code>--n-gpu-layers&lt;/code> 옵션).&lt;/p>
&lt;div class="mermaid">sequenceDiagram
participant User
participant CPU_RAM as CPU &amp; RAM (mmap)
participant VRAM as GPU VRAM
participant Compute as Tensor Cores
User->>CPU_RAM: GGUF 로드 (mmap)
CPU_RAM->>VRAM: 레이어 오프로드 (예: 30/32 레이어)
Note over CPU_RAM, VRAM: 데이터는 VRAM에 양자화된 상태로 유지됨
User->>Compute: 포워드 패스 (입력 토큰)
Compute->>VRAM: 양자화된 가중치 페치
Compute->>Compute: SRAM에서 즉시(on-the-fly) FP16으로 역양자화
Compute->>Compute: 행렬곱 연산 (cuBLAS / 커스텀 커널)
Compute->>User: 출력 로짓 (Logits)&lt;/div>
&lt;p>GPU 상에서 계산할 경우, VRAM의 대역폭(Memory Bandwidth)이 최대의 병목이 됩니다. 가중치가 k-quants로 압축되어 있기 때문에, VRAM에서 GPU의 연산 유닛(SM: Streaming Multiprocessor나 텐서 코어)으로의 데이터 전송량이 1/3 ~ 1/4로 감소합니다. 계산 유닛에 가중치가 도달하는 순간에 실시간(on-the-fly)으로 FP16으로 역양자화(전개)되며, 텐서 코어를 사용하여 초고속으로 행렬곱이 실행됩니다.
즉, 양자화는 &lt;strong>&amp;lsquo;계산량을 줄이기&amp;rsquo; 위해서가 아니라 &amp;lsquo;메모리 전송량을 줄이기&amp;rsquo; 위해서 수행되고 있다&lt;/strong>고 할 수 있습니다.&lt;/p>
&lt;hr>
&lt;h2 id="6-메모리-사용량과-성능의-트레이드오프-구체적-예시">6. 메모리 사용량과 성능의 트레이드오프 구체적 예시
&lt;/h2>&lt;p>여기서, Llama 3 8B 모델을 예로 들어, GGUF의 양자화 레벨별 요구 스펙을 살펴보겠습니다. (수치는 대략적인 기준입니다)&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th style="text-align:left">모델/양자화&lt;/th>
&lt;th style="text-align:left">파일 크기&lt;/th>
&lt;th style="text-align:left">필요한 VRAM/RAM&lt;/th>
&lt;th style="text-align:left">추론 속도(기준)&lt;/th>
&lt;th style="text-align:left">Perplexity 열화&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td style="text-align:left">&lt;strong>Llama-3-8B (FP16)&lt;/strong>&lt;/td>
&lt;td style="text-align:left">약 16 GB&lt;/td>
&lt;td style="text-align:left">18 GB 이상&lt;/td>
&lt;td style="text-align:left">기준&lt;/td>
&lt;td style="text-align:left">없음 (Base)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">&lt;strong>Llama-3-8B (Q8_0)&lt;/strong>&lt;/td>
&lt;td style="text-align:left">약 8.5 GB&lt;/td>
&lt;td style="text-align:left">10 GB 이상&lt;/td>
&lt;td style="text-align:left">고속&lt;/td>
&lt;td style="text-align:left">거의 없음&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">&lt;strong>Llama-3-8B (Q6_K)&lt;/strong>&lt;/td>
&lt;td style="text-align:left">약 6.6 GB&lt;/td>
&lt;td style="text-align:left">8 GB 이상&lt;/td>
&lt;td style="text-align:left">매우 고속&lt;/td>
&lt;td style="text-align:left">극소&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">&lt;strong>Llama-3-8B (Q4_K_M)&lt;/strong>&lt;/td>
&lt;td style="text-align:left">약 4.9 GB&lt;/td>
&lt;td style="text-align:left">6.5 GB 이상&lt;/td>
&lt;td style="text-align:left">최속·최적&lt;/td>
&lt;td style="text-align:left">허용 범위·미미함&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">&lt;strong>Llama-3-8B (Q3_K_M)&lt;/strong>&lt;/td>
&lt;td style="text-align:left">약 3.9 GB&lt;/td>
&lt;td style="text-align:left">5.5 GB 이상&lt;/td>
&lt;td style="text-align:left">최속&lt;/td>
&lt;td style="text-align:left">약간 눈에 띔&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">&lt;strong>Llama-3-8B (Q2_K)&lt;/strong>&lt;/td>
&lt;td style="text-align:left">약 3.0 GB&lt;/td>
&lt;td style="text-align:left">4.5 GB 이상&lt;/td>
&lt;td style="text-align:left">고속&lt;/td>
&lt;td style="text-align:left">명백한 열화&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>주의점 (KV 캐시의 영향):&lt;/strong>
LLM 추론에 있어서, 컨텍스트 길이(프롬프트의 토큰 수)가 길어지면, 모델의 가중치뿐만 아니라 과거의 Attention 상태를 보존하는 &lt;strong>KV 캐시&lt;/strong>의 메모리 소비가 폭발적으로 증가합니다.
예를 들어 컨텍스트가 8192 토큰인 경우, KV 캐시만으로 수 GB를 소비합니다. 따라서 실제 운용에서는 &lt;code>모델 파일 크기 + 약 1.5GB～3GB&lt;/code>의 여유 공간(Headroom)을 확보해 둘 필요가 있습니다. Q4_K_M이 권장되는 이유는 이 KV 캐시를 확보하더라도 일반적인 8GB VRAM 탑재 GPU(RTX 3060 / 4060 등)에서 안전하게 동작하는 절묘한 라인이기 때문입니다.&lt;/p>
&lt;p>최근의 llama.cpp에서는 이 &lt;strong>KV 캐시 자체를 Q8_0이나 Q4_0으로 양자화하는 기능&lt;/strong>도 추가되어 있어, 컨텍스트 길이를 더욱 늘리기 위한 노력이 끊임없이 이루어지고 있습니다.&lt;/p>
&lt;hr>
&lt;h2 id="7-요약">7. 요약
&lt;/h2>&lt;p>본 문서에서는 llama.cpp의 심장부인 GGUF 포맷과 k-quants 양자화 기술의 내부 구조에 대해 깊이 파고들어 해설했습니다.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>GGUF의 유연성:&lt;/strong> 키-값 형태의 메타데이터 구조를 통해, LLM의 급속한 진화(새로운 모델 아키텍처의 등장)에도 파괴적인 변경 없이 따라갈 수 있는 견고한 생태계를 구축했습니다.&lt;/li>
&lt;li>&lt;strong>k-quants를 통한 극한의 압축:&lt;/strong> 슈퍼 블록과 서브 블록의 계층적인 스케일 팩터 관리를 통해, 이상치의 정보를 유지하면서 가중치 1개당 평균 4.8비트(Q4_K_M)라는 놀라운 압축을 실현했습니다.&lt;/li>
&lt;li>&lt;strong>메모리 대역폭 병목 해소:&lt;/strong> SIMD나 CUDA에서의 고도화된 커널 구현을 통해, 실시간으로 역양자화하며 계산을 수행함으로써 VRAM 전송량을 줄이고 추론 속도를 극적으로 향상시켰습니다.&lt;/li>
&lt;/ol>
&lt;p>AI의 민주화를 추진하는 llama.cpp의 기술력은 단순한 도구의 틀을 넘어 현대 소프트웨어 엔지니어링의 최고봉 중 하나라고 해도 과언이 아닙니다. 양자화 알고리즘이나 GGUF 포맷의 원리를 이해함으로써, 자신의 환경에 가장 적합한 모델을 선택하거나 성능 튜닝을 더 정확하게 수행할 수 있을 것입니다.&lt;/p>
&lt;h3 id="참고-링크">참고 링크
&lt;/h3>&lt;ul>
&lt;li>&lt;a class="link" href="https://github.com/ggerganov/llama.cpp" target="_blank" rel="noopener"
>llama.cpp GitHub Repository&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://github.com/ggerganov/ggml/blob/master/docs/gguf.md" target="_blank" rel="noopener"
>GGUF Format Specification&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://github.com/ggerganov/llama.cpp/pull/1684" target="_blank" rel="noopener"
>K-quants Implementation PR&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>(끝)&lt;/p></description></item></channel></rss>