Transformer Input & Attention

1. Overview

  • Tokenization: ์ž…๋ ฅ ํ…์ŠคํŠธ๋ฅผ Token ID๋กœ ๋ณ€ํ™˜
  • Embedding: Token ID๋ฅผ ๊ณ ์ • ์ฐจ์›์˜ ๋ฒกํ„ฐ๋กœ ๋ณ€ํ™˜
  • QKV Projection: ๊ฐ€์ค‘์น˜ ํ–‰๋ ฌ์„ ์‚ฌ์šฉํ•˜์—ฌ Query, Key, Value ์ƒ์„ฑ
  • Multi-Head Attention (MHA): ์—ฌ๋Ÿฌ Head์—์„œ ๋…๋ฆฝ์ ์œผ๋กœ Attention ๊ณ„์‚ฐ
  • Grouped-Query Attention (GQA): ์—ฌ๋Ÿฌ Query Head๊ฐ€ Key/Value Head ๊ณต์œ 
  • KV Cache: ์ด์ „ ํ† ํฐ์˜ Key/Value๋ฅผ ์ €์žฅํ•˜๊ณ  ์žฌ์‚ฌ์šฉ

Processing Flow

flowchart TD
    A["Input Text"] --> B["Tokenizer"]
    B --> C["Token IDs"]
    C --> D["Token Embedding"]
    D --> E["Input Vector"]
    E --> F["QKV Projection"]
    F --> Q["Query"]
    F --> K["Key"]
    F --> V["Value"]
    K --> CACHE["KV Cache"]
    V --> CACHE
    Q --> ATT["Multi-Head / Grouped-Query Attention"]
    CACHE --> ATT
    ATT --> O["Head Concatenation"]
    O --> P["Output Projection"]

2. Tokenization & Embedding

Tokenization

์ž…๋ ฅ ํ…์ŠคํŠธ๋ฅผ Vocabulary์— ์ •์˜๋œ Token ID๋กœ ๋ณ€ํ™˜ํ•œ๋‹ค.

Input: "The cat sleeps"
          โ†“
Tokens: ["The", "cat", "sleeps"]
          โ†“
IDs: [101, 205, 309]

์œ„ ํ† ํฐ ๋ถ„ํ• ๊ณผ ID๋Š” ์„ค๋ช…์šฉ ์˜ˆ์‹œ์ด๋‹ค. run.c์—์„œ Tokenization์„ ์ˆ˜ํ–‰ํ•˜๋Š” ์ฝ”๋“œ:

encode(t, prompt, 1, 0, &prompt_tokens, &num_prompt_tokens);

Token Embedding

Token ID๋ฅผ ๋ชจ๋ธ์˜ Embedding Table์—์„œ ์กฐํšŒํ•˜์—ฌ ๋ฒกํ„ฐ๋กœ ๋ณ€ํ™˜ํ•œ๋‹ค.

float* content_row = w->token_embedding_table + token * dim;
memcpy(x, content_row, dim * sizeof(*x));

์˜ˆ๋ฅผ ๋“ค์–ด dim = 288์ด๋ฉด ํ† ํฐ ํ•˜๋‚˜๊ฐ€ 288๊ฐœ์˜ ์‹ค์ˆ˜๋กœ ํ‘œํ˜„๋œ๋‹ค.

x\in\mathbb{R}^{288} ]$$ ```mermaid flowchart LR A["Token ID: 205"] --> B["Embedding Table"] B --> C["Vector x"] C --> D["288 Dimensions"] ``` - **Token**: ํ…์ŠคํŠธ๋ฅผ ๋ถ„ํ• ํ•œ ์ฒ˜๋ฆฌ ๋‹จ์œ„ - **Dimension**: ํ† ํฐ ํ•˜๋‚˜๋ฅผ ํ‘œํ˜„ํ•˜๋Š” ๋ฒกํ„ฐ์˜ ์ˆซ์ž ๊ฐœ์ˆ˜ - **Embedding**: ํ•™์Šต๋œ ๋ฒกํ„ฐ ํ‘œํ˜„ ## 3. QKV Projection ์ž…๋ ฅ ๋ฒกํ„ฐ๋ฅผ ํ•™์Šต๋œ ๊ฐ€์ค‘์น˜ ํ–‰๋ ฌ๋กœ ๋ณ€ํ™˜ํ•ด Q, K, V๋ฅผ ์ƒ์„ฑํ•œ๋‹ค. $$[ Q=W_Qx,\quad K=W_Kx,\quad V=W_Vx ]$$ |๋ฒกํ„ฐ|์—ญํ• | |---|---| |Query (Q)|์–ด๋–ค ์ •๋ณด๋ฅผ ์ฐธ๊ณ ํ• ์ง€ ํŒ๋‹จํ•˜๋Š” ํ‘œํ˜„| |Key (K)|Query์™€ ๋น„๊ตํ•˜์—ฌ Attention Score ๊ณ„์‚ฐ| |Value (V)|Attention ๊ฒฐ๊ณผ์— ๋ฐ˜์˜ํ•  ์ •๋ณด| ```mermaid flowchart TD X["Input Vector x"] --> WQ["Wq: Linear Projection"] X --> WK["Wk: Linear Projection"] X --> WV["Wv: Linear Projection"] WQ --> Q["Query Vector"] WK --> K["Key Vector"] WV --> V["Value Vector"] ``` `run.c`์˜ ํ•ต์‹ฌ ์—ฐ์‚ฐ์„ ๋‹จ์ˆœํ™”ํ•œ ์ฝ”๋“œ: ``` matmul(q, x, wq, dim, dim); matmul(k, x, wk, dim, kv_dim); matmul(v, x, wv, dim, kv_dim); ``` - `wq`, `wk`, `wv`: ํ•™์Šต๋œ ๊ฐ€์ค‘์น˜ - `q`, `k`, `v`: ์ž…๋ ฅ์œผ๋กœ๋ถ€ํ„ฐ ๊ณ„์‚ฐ๋œ ๋ฒกํ„ฐ - ์‹ค์ œ ๊ตฌํ˜„์—์„œ๋Š” ์ •๊ทœํ™”๋œ ์ž…๋ ฅ๊ณผ ๋ ˆ์ด์–ด๋ณ„ ๊ฐ€์ค‘์น˜ ์˜คํ”„์…‹ ์‚ฌ์šฉ ## 4. Multi-Head Attention (MHA) **Q, K, V๋ฅผ ์—ฌ๋Ÿฌ Head๋กœ ๋‚˜๋ˆ„์–ด ๊ฐ๊ฐ Attention์„ ๊ณ„์‚ฐํ•˜๋Š” ๊ตฌ์กฐ** ์˜ˆ์‹œ: ``` dim = 288; n_heads = 6; ``` Head ํ•˜๋‚˜์˜ ์ฐจ์›: $$[ head\_size=\frac{288}{6}=48 ]$$ ```mermaid flowchart TD Q["Query Vector: 288 Dimensions"] Q --> H1["Head 1: 48"] Q --> H2["Head 2: 48"] Q --> H3["Head 3: 48"] Q --> H4["Head 4: 48"] Q --> H5["Head 5: 48"] Q --> H6["Head 6: 48"] H1 --> C["Concatenation: 288"] H2 --> C H3 --> C H4 --> C H5 --> C H6 --> C ``` ์œ„ ๊ทธ๋ฆผ์€ Query์˜ ์ฐจ์› ๋ถ„ํ• ์„ ๋ณด์—ฌ์ค€๋‹ค. ์‹ค์ œ๋กœ๋Š” ๊ฐ Head์— ๋Œ€์‘ํ•˜๋Š” K/V์™€ Attention์„ ๊ณ„์‚ฐํ•œ **๊ฒฐ๊ณผ ๋ฒกํ„ฐ**๋ฅผ ๊ฒฐํ•ฉํ•œ๋‹ค. Head๋Š” ๋‹จ์–ด ํ•˜๋‚˜๋ฅผ ๋‹ด๋‹นํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, ํ† ํฐ ๊ฐ„ ๊ด€๊ณ„๋ฅผ ๊ณ„์‚ฐํ•˜๋Š” ๋…๋ฆฝ์ ์ธ Attention ์—ฐ์‚ฐ ๋‹จ์œ„์ด๋‹ค. ### Attention Calculation $$[ \operatorname{Attention}(Q,K,V)= \operatorname{softmax} \left(\frac{QK^T}{\sqrt{d_k}}\right)V ]$$ ```mermaid flowchart TD Q["Query"] --> S["Q ร— K Transpose"] K["Key"] --> S S --> SCALE["Scale by sqrt(d_k)"] SCALE --> SM["Softmax"] SM --> WS["Weighted Sum"] V["Value"] --> WS WS --> O["Attention Output"] ``` 1. Query์™€ Key์˜ ๋‚ด์ ์œผ๋กœ ์œ ์‚ฌ๋„ ๊ณ„์‚ฐ 2. Head ์ฐจ์›์˜ ์ œ๊ณฑ๊ทผ์œผ๋กœ ์ ์ˆ˜ ์กฐ์ ˆ 3. Softmax๋กœ Attention ๊ฐ€์ค‘์น˜ ๊ณ„์‚ฐ 4. Value์˜ ๊ฐ€์ค‘ํ•ฉ์œผ๋กœ ์ถœ๋ ฅ ์ƒ์„ฑ `run.c`์—์„œ๋Š” Head๋ณ„๋กœ ๋ฐ˜๋ณตํ•˜์—ฌ Attention์„ ๊ณ„์‚ฐํ•œ๋‹ค. ``` for (int h = 0; h < p->n_heads; h++) { float* q = s->q + h * head_size; } ``` ## 5. Grouped-Query Attention (GQA) **์—ฌ๋Ÿฌ Query Head๊ฐ€ ํ•˜๋‚˜์˜ Key/Value Head๋ฅผ ๊ณต์œ ํ•˜๋Š” Attention ๊ตฌ์กฐ** ์˜ˆ์‹œ: ``` dim = 288; n_heads = 6; n_kv_heads = 2; ``` ### MHA ๊ตฌ์กฐ ```mermaid flowchart LR Q1["Q1"] --> KV1["K1 / V1"] Q2["Q2"] --> KV2["K2 / V2"] Q3["Q3"] --> KV3["K3 / V3"] Q4["Q4"] --> KV4["K4 / V4"] Q5["Q5"] --> KV5["K5 / V5"] Q6["Q6"] --> KV6["K6 / V6"] ``` - Query Head: 6๊ฐœ - Key/Value Head: ๊ฐ๊ฐ 6๊ฐœ - Query Head๋งˆ๋‹ค ๋ณ„๋„์˜ K/V Head ์‚ฌ์šฉ ### GQA ๊ตฌ์กฐ ```mermaid flowchart LR Q1["Q1"] --> KV1["K1 / V1"] Q2["Q2"] --> KV1 Q3["Q3"] --> KV1 Q4["Q4"] --> KV2["K2 / V2"] Q5["Q5"] --> KV2 Q6["Q6"] --> KV2 ``` - Query Head: 6๊ฐœ - Key/Value Head: ๊ฐ๊ฐ 2๊ฐœ - Query Head 3๊ฐœ๊ฐ€ ํ•˜๋‚˜์˜ K/V Head ๊ณต์œ  ### Dimension Calculation $$[ head\_size=\frac{dim}{n_heads}=48 ]$$ $$[ kv\_dim=dim\times\frac{n_kv_heads}{n_heads}=96 ]$$ |๊ตฌ๋ถ„|MHA|GQA| |---|---|---| |Query Heads|6|6| |Key/Value Heads|6|2| |Query Dimension|288|288| |Key Dimension|288|96| |Value Dimension|288|96| |KV ๊ณต์œ |์—†์Œ|์žˆ์Œ| `run.c`์—์„œ KV Head๋ฅผ ์„ ํƒํ•˜๋Š” ๊ณ„์‚ฐ: ``` int kv_mul = p->n_heads / p->n_kv_heads; int kv_head = h / kv_mul; ``` `kv_mul = 3`์ด๋ฏ€๋กœ Query Head 3๊ฐœ๋งˆ๋‹ค ํ•˜๋‚˜์˜ KV Head๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. GQA๋Š” KV Head ์ˆ˜๋ฅผ ์ค„์—ฌ Key/Value ์ €์žฅ ๊ณต๊ฐ„ ๋ฐ ๋ฉ”๋ชจ๋ฆฌ ์ ‘๊ทผ ๋ถ€๋‹ด์„ ๊ฐ์†Œ์‹œํ‚จ๋‹ค. ## 6. KV Cache **์ด์ „ ํ† ํฐ์—์„œ ๊ณ„์‚ฐํ•œ Key/Value๋ฅผ ์ €์žฅํ•˜๊ณ  ๋‹ค์Œ ํ† ํฐ ์ฒ˜๋ฆฌ์— ์žฌ์‚ฌ์šฉํ•˜๋Š” ๋ฉ”๋ชจ๋ฆฌ** LLM์€ ํ† ํฐ์„ ์ˆœ์ฐจ์ ์œผ๋กœ ์ƒ์„ฑํ•˜๋ฏ€๋กœ ์ด์ „ ํ† ํฐ์˜ K/V๋ฅผ ๋ฐ˜๋ณต ๊ณ„์‚ฐํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค. ### KV Cache Processing ```mermaid flowchart TD A["Token 1"] --> B["Compute K1, V1"] B --> C["Store KV Cache"] C --> D["Token 2"] D --> E["Reuse K1, V1"] D --> F["Compute K2, V2"] E --> G["Attention for Token 2"] F --> G F --> H["Update KV Cache"] H --> I["Token 3"] I --> J["Reuse K1,V1 and K2,V2"] I --> K["Compute K3,V3"] J --> L["Attention for Token 3"] K --> L ``` ### Memory Allocation `RunState` ๊ตฌ์กฐ์ฒด: ``` float* key_cache; float* value_cache; ``` `malloc_run_state()`์—์„œ ๋ฉ”๋ชจ๋ฆฌ ํ• ๋‹น: ``` int kv_dim = (p->dim * p->n_kv_heads) / p->n_heads; s->key_cache = calloc( p->n_layers * p->seq_len * kv_dim, sizeof(float) ); s->value_cache = calloc( p->n_layers * p->seq_len * kv_dim, sizeof(float) ); ``` - `n_layers`: Transformer Layer ๊ฐœ์ˆ˜ - `seq_len`: ์ตœ๋Œ€ ์‹œํ€€์Šค ๊ธธ์ด - `kv_dim`: ํ† ํฐ ํ•˜๋‚˜์˜ Key ๋˜๋Š” Value ์ฐจ์› FP32 ๊ธฐ์ค€ ์ด KV Cache ๋ฉ”๋ชจ๋ฆฌ: $$[ M_{KV}=2\times n_{layers}\times seq_{len}\times kv_{dim}\times4 ]$$ ### GQA + KV Cache ```mermaid flowchart TD A["Current Token"] --> B["Generate Q Heads: 6"] A --> C["Generate K/V Heads: 2"] C --> D["Store Current K/V in Cache"] E["Previous Token KV Cache"] --> F["Available K/V"] D --> F B --> G["Grouped-Query Attention"] F --> G G --> H["6 Head Outputs"] H --> I["Concatenate: 288 Dimensions"] ``` - GQA: ์—ฌ๋Ÿฌ Query Head๊ฐ€ ๊ฐ™์€ KV Head๋ฅผ ๊ณต์œ  - KV Cache: ์ด์ „ ํ† ํฐ์˜ K/V๋ฅผ ์žฌ์‚ฌ์šฉ - ๋‘ ๊ธฐ๋ฒ•์„ ๊ฒฐํ•ฉํ•˜๋ฉด ์บ์‹œ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์ค„์ด๋ฉด์„œ ์ค‘๋ณต K/V ๊ณ„์‚ฐ์„ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค. ## 7. Summary ```mermaid flowchart TD A["Input Text"] --> B["Tokenizer"] B --> C["Token IDs"] C --> D["Token Embedding: 288"] D --> E["QKV Projection"] E --> Q["Q: 6 Heads ร— 48"] E --> K["K: 2 Heads ร— 48"] E --> V["V: 2 Heads ร— 48"] K --> CACHE["KV Cache"] V --> CACHE Q --> GQA["Grouped-Query Attention"] CACHE --> GQA GQA --> H["6 Attention Outputs"] H --> CONCAT["Concatenate: 288"] CONCAT --> O["Output Projection"] ``` | ๊ฐœ๋… | ํ•ต์‹ฌ ์—ญํ•  | | ----------------------- | ---------------------- | | Tokenizer | ํ…์ŠคํŠธ โ†’ Token ID | | Embedding | Token ID โ†’ ๋ฒกํ„ฐ | | QKV Projection | Q/K/V ๊ณ„์‚ฐ | | Multi-Head Attention | ์—ฌ๋Ÿฌ Head์—์„œ Attention ์ˆ˜ํ–‰ | | Grouped-Query Attention | ์—ฌ๋Ÿฌ Query Head๊ฐ€ K/V ๊ณต์œ  | | KV Cache | ์ด์ „ K/V ์ €์žฅ ๋ฐ ์žฌ์‚ฌ์šฉ |