8.6 KiB
์บ์ฑcaching
๋๊ตฐ๊ฐ์ ๋ํ๋ฅผ ๋๋๊ณ ์๋๋ฐ, ์๋๋ฐฉ์ด ์ด์ ์ ํ๋ ๋ง์ ๊ธฐ์ตํ์ง ๋ชปํ๊ณ ๋น์ ์ด ๋๋ตํ ๋๋ง๋ค ์ฒ์๋ถํฐ ๋ค์ ์์ํด์ผ ํ๋ค๊ณ ์์ํด ๋ณด์ธ์. ์ด๋ ๋๋ฆฌ๊ณ ๋นํจ์จ์ ์ด๊ฒ ์ฃ ?
์ด ๋น์ ๋ฅผ ํธ๋์คํฌ๋จธ ๋ชจ๋ธ์๋ ์ ์ฉํ ์ ์์ต๋๋ค. ์๊ธฐํ๊ท ๋ชจ๋ธ์ ์์ฑ์ ํ ๋ฒ์ ํ๋์ ํ ํฐ์ฉ ์์ธกํ๊ธฐ ๋๋ฌธ์ ๋๋ฆด ์ ์์ต๋๋ค. ๊ฐ๊ฐ์ ์๋ก์ด ์์ธก์ ์ด์ ์ ๋ชจ๋ ๋ฌธ๋งฅ์ ์์กดํฉ๋๋ค.
1000๋ฒ์งธ ํ ํฐ์ ์์ธกํ๋ ค๋ฉด, ๋ชจ๋ธ์ ์ด์ 999๊ฐ ํ ํฐ์ ์ ๋ณด๊ฐ ํ์ํฉ๋๋ค. ์ด ์ ๋ณด๋ ๊ฐ ํ ํฐ ํํ๋ค ์ฌ์ด์ ํ๋ ฌ ๊ณฑ์ ํตํด ํํ๋ฉ๋๋ค.
1001๋ฒ์งธ ํ ํฐ์ ์์ธกํ๋ ค๋ฉด, ์ด์ 999๊ฐ ํ ํฐ์ ๋์ผํ ์ ๋ณด์ ๋ํ์ฌ 1000๋ฒ์งธ ํ ํฐ์ ์ ๋ณด๋ ํ์ํฉ๋๋ค. ์ด๋ ๊ฒ ๋๋ฉด ํ ํฐ๋ง๋ค ๋ชจ๋ธ์ ๋ฐ๋ณต์ ์ผ๋ก ๋ง์ ํ๋ ฌ ์ฐ์ฐ์ ์ํํด์ผ ํฉ๋๋ค!
์ด๋ฌํ ๋นํจ์จ์ฑ์ ์ ๊ฑฐํ๊ธฐ ์ํด KV ์บ์(Key-Value Cache)๋ฅผ ์ฌ์ฉํฉ๋๋ค. ์ดํ ์ ๋ ์ด์ด์์ ์ด์ ์ ์ฒ๋ฆฌํ ํ ํฐ์ผ๋ก๋ถํฐ ์ป์ ํค์ ๊ฐ ์์ ์ ์ฅํด๋๊ณ , ์ดํ ํ ํฐ ์์ธก ์ ์ด๋ฅผ ์ฌ์ฌ์ฉํ์ฌ ์ฐ์ฐ์ ์ค์ด๋ ๋ฐฉ์์ ๋๋ค.
Warning
์บ์ฑ์ ์ถ๋ก ์๋ง ์ฌ์ฉํด์ผ ํฉ๋๋ค. ํ์ต ์ค์ ํ์ฑํ๋๋ฉด ์์์น ๋ชปํ ์ค๋ฅ๊ฐ ๋ฐ์ํ ์ ์์ต๋๋ค.
์บ์ฑ์ด ์ด๋ป๊ฒ ๊ทธ๋ฆฌ๊ณ ์ ์๋ํ๋์ง ๋ ์ ์ดํดํ๊ธฐ ์ํด, ์ดํ ์ ํ๋ ฌ์ ๊ตฌ์กฐ๋ฅผ ์์ธํ ์ดํด๋ณด๊ฒ ์ต๋๋ค.
์ดํ ์ ํ๋ ฌattention-matrices
์ค์ผ์ผ๋ ๋ท-ํ๋ก๋ํธ ์ดํ
์
์ ๋ฐฐ์น ํฌ๊ธฐ b, ์ดํ
์
ํค๋ ์ h, ํ์ฌ๊น์ง์ ์ํ์ค ๊ธธ์ด T, ์ดํ
์
ํค๋๋น ์ฐจ์ d_head์ ๋ํด ์๋์ ๊ฐ์ด ๊ณ์ฐ๋ฉ๋๋ค.
\text{Attention}(Q, K, V) = \text{softmax}\left( \frac{Q K^\top}{\sqrt{d_{\text{head}}}} \times \text{mask} \right) V
์ฟผ๋ฆฌ(Q), ํค(K), ๊ฐ(V) ํ๋ ฌ์ (b, h, T, d_head) ํํ์ ์
๋ ฅ ์๋ฒ ๋ฉ์์์ ํฌ์์
๋๋ค.
์ธ๊ณผ์ ์ดํ ์ ์ ๊ฒฝ์ฐ, ๋ง์คํฌ๋ ๋ชจ๋ธ์ด ๋ฏธ๋ ํ ํฐ์ ์ดํ ์ ํ๋ ๊ฒ์ ๋ฐฉ์งํฉ๋๋ค. ํ ํฐ์ด ํ ๋ฒ ์ฒ๋ฆฌ๋๋ฉด, ๊ทธ ํํ์ ๋ฏธ๋ ํ ํฐ๊ณผ ๊ด๋ จํ์ฌ ์ ๋ ๋ณํ์ง ์์ต๋๋ค. ์ด๋ \( K_{\text{past}} \)์ \( V_{\text{past}} \)๋ฅผ ์บ์ํ์ฌ ๋ง์ง๋ง ํ ํฐ์ ํํ์ ๊ณ์ฐํ๋ ๋ฐ ์ฌ์ฌ์ฉํ ์ ์์์ ์๋ฏธํฉ๋๋ค.
\text{Attention}(q_t, [\underbrace{k_1, k_2, \dots, k_{t-1}}_{\text{cached}}, k_{t}], [\underbrace{v_1, v_2, \dots, v_{t-1}}_{\text{cached}}, v_{t}])
์ถ๋ก ์์๋ ๋ค์ ํ ํฐ \( t+1 \)์ ์์ธกํ๋ ํํ \( x_t \)๋ฅผ ๊ณ์ฐํ๊ธฐ ์ํด ๋ง์ง๋ง ํ ํฐ์ ์ฟผ๋ฆฌ๋ง ํ์ํฉ๋๋ค. ๋จ๊ณ์์ ์๋ก์ด ํค์ ๊ฐ ๋ฒกํฐ๊ฐ ์บ์์ ์ ์ฅ๋๊ณ ๊ณผ๊ฑฐ ํค์ ๊ฐ์ ์ถ๊ฐ๋ฉ๋๋ค.
K_{\text{cache}} \leftarrow \text{concat}(K_{\text{past}}, k_t), \quad V_{\text{cache}} \leftarrow \text{concat}(V_{\text{past}}, v_t)
์ดํ ์ ์ ๋ชจ๋ธ์ ๊ฐ ๋ ์ด์ด์์ ๋ ๋ฆฝ์ ์ผ๋ก ๊ณ์ฐ๋๋ฉฐ, ์บ์ฑ์ ๋ ์ด์ด๋ณ๋ก ์ํ๋ฉ๋๋ค.
์บ์ฑ์ด ํจ์จ์ฑ์ ์ด๋ป๊ฒ ๊ฐ์ ํ๋์ง ๋น๊ตํ ์๋ ํ๋ฅผ ์ฐธ์กฐํ์ธ์.
| ์บ์ฑ ์์ | ์บ์ฑ ์ฌ์ฉ |
|---|---|
๋จ๊ณ๋ง๋ค ์ด์ ์ ๋ชจ๋ K์ V๋ฅผ ์ฌ๊ณ์ฐ |
๋จ๊ณ๋ง๋ค ํ์ฌ์ K์ V๋ง ๊ณ์ฐ |
| ๋จ๊ณ๋น ์ดํ ์ ๋น์ฉ์ด ์ํ์ค ๊ธธ์ด์ ๋ํด ์ ๊ณฑ | ๋จ๊ณ๋น ์ดํ ์ ๋น์ฉ์ด ์ํ์ค ๊ธธ์ด์ ๋ํด ์ ํ (๋ฉ๋ชจ๋ฆฌ๋ ์ ํ์ ์ผ๋ก ์ฆ๊ฐํ์ง๋ง, ํ ํฐ๋น ๊ณ์ฐ์ ๋ฎ๊ฒ ์ ์ง๋จ) |
์บ์ ํด๋์คcache-class
๊ธฐ๋ณธ KV ์บ์ ์ธํฐํ์ด์ค๋ ํ์ฌ ํ ํฐ์ ํค์ ๊ฐ ํ
์๋ฅผ ๋ฐ์์ ์
๋ฐ์ดํธ๋ K์ V ํ
์๋ฅผ ๋ฐํํฉ๋๋ค. ์ด๋ ๋ชจ๋ธ์ forward ๋ฉ์๋์ ์ํด ๋ด๋ถ์ ์ผ๋ก ๊ด๋ฆฌ๋ฉ๋๋ค.
new_K, new_V = cache.update(k_t, v_t, layer_idx)
attn_output = attn_layer_idx_fn(q_t, new_K, new_V)
Transformers์ [Cache] ํด๋์ค๋ฅผ ์ฌ์ฉํ ๋, ์
ํ ์ดํ
์
๋ชจ๋์ ๊ณผ๊ฑฐ์ ํ์ฌ ์ ๋ณด๋ฅผ ํตํฉํ๊ธฐ ์ํด ๋ช ๊ฐ์ง ์ค์ํ ๋จ๊ณ๋ฅผ ์ํํฉ๋๋ค.
-
์ดํ ์ ๋ชจ๋์ ํ์ฌ kv ์์ ์บ์์ ์ ์ฅ๋ ๊ณผ๊ฑฐ kv ์๊ณผ ์ฐ๊ฒฐํฉ๋๋ค. ์ด๋
(new_tokens_length, past_kv_length + new_tokens_length)ํํ์ ์ดํ ์ ๊ฐ์ค์น๋ฅผ ์์ฑํฉ๋๋ค. ํ์ฌ์ ๊ณผ๊ฑฐ kv ์์ด ๋ณธ์ง์ ์ผ๋ก ๊ฒฐํฉํด ์ดํ ์ ์ ์๋ฅผ ๊ณ์ฐํ๋ฉฐ, ๋ชจ๋ธ์ด ์ด์ ๋ฌธ๋งฅ๊ณผ ํ์ฌ ์ ๋ ฅ์ ์ธ์ํ๋๋ก ๋ณด์ฅํฉ๋๋ค. -
forward๋ฉ์๋๊ฐ ๋ฐ๋ณต์ ์ผ๋ก ํธ์ถ๋ ๋, ์ดํ ์ ๋ง์คํฌ ํํ๊ฐ ๊ณผ๊ฑฐ์ ํ์ฌ kv ์์ ๊ฒฐํฉ๋ ๊ธธ์ด์ ์ผ์นํ๋ ๊ฒ์ด ์ค์ํฉ๋๋ค. ์ดํ ์ ๋ง์คํฌ๋(batch_size, past_kv_length + new_tokens_length)ํํ์ฌ์ผ ํฉ๋๋ค. ์ด๋ ์ผ๋ฐ์ ์ผ๋ก [~GenerationMixin.generate]์์ ๋ด๋ถ์ ์ผ๋ก ์ฒ๋ฆฌ๋์ง๋ง, [Cache]๋ก ์์ฒด ์์ฑ ๋ฃจํ๋ฅผ ๊ตฌํํ๊ณ ์ถ๋ค๋ฉด ์ด๋ฅผ ์ผ๋์ ๋์ธ์! ์ดํ ์ ๋ง์คํฌ๋ ๊ณผ๊ฑฐ์ ํ์ฌ ํ ํฐ๊ฐ์ ๋ณด์ ํด์ผ ํฉ๋๋ค.
์บ์ ์ ์ฅ์ ๊ตฌํcache-storage-implementation
์บ์๋ ๊ฐ ๋ ์ด์ด๊ฐ key์ value ์บ์๋ฅผ ํฌํจํ๋ ๋ ์ด์ด ๋ชฉ๋ก ํํ๋ก ๊ตฌ์ฑ๋์ด ์์ต๋๋ค. key ๋ฐ value ์บ์๋ [batch_size, num_heads, seq_len, head_dim] ํํ์ ํ
์์
๋๋ค.
๋ ์ด์ด๋ ์๋ก ๋ค๋ฅธ ํ์
์ผ ์ ์์ผ๋ฉฐ(์: DynamicLayer, StaticLayer, StaticSlidingWindowLayer), ์ด๋ ์ฃผ๋ก ์ํ์ค ๊ธธ์ด๋ฅผ ์ด๋ป๊ฒ ์ฒ๋ฆฌํ๊ณ ์บ์๋ฅผ ์ด๋ป๊ฒ ๊ฐฑ์ ํ๋์ง์ ๋ฐ๋ผ ๋ฌ๋ผ์ง๋๋ค.
๊ฐ์ฅ ๋จ์ํ ํํ๋ DynamicLayer๋ก, ๋ ๋ง์ ํ ํฐ์ด ์ฒ๋ฆฌ๋จ์ ๋ฐ๋ผ ์ ์ง์ ์ผ๋ก ํ์ฅ๋ฉ๋๋ค. ์ํ์ค ๊ธธ์ด ์ฐจ์(seq_len)์ ์๋ก์ด ํ ํฐ์ด ์ถ๊ฐ๋ ๋๋ง๋ค ์ฆ๊ฐํฉ๋๋ค:
cache.layers[idx].keys = torch.cat([cache.layers[idx].keys, key_states], dim=-2)
cache.layers[idx].values = torch.cat([cache.layers[idx].values, value_states], dim=-2)
StaticLayer๋ StaticSlidingWindowLayer์ ๊ฐ์ ๋ค๋ฅธ ๋ ์ด์ด ํ์
์ ์บ์๊ฐ ์์ฑ๋ ๋ ๊ณ ์ ๋ ์ํ์ค ๊ธธ์ด๋ฅผ ๊ฐ์ง๋ฉฐ, ์ด๋ torch.compile๊ณผ ํธํ๋๋๋ก ๋ง๋ญ๋๋ค. StaticSlidingWindowLayer์ ๊ฒฝ์ฐ, ์๋ก์ด ํ ํฐ์ด ์ถ๊ฐ๋๋ฉด ๊ธฐ์กด ํ ํฐ์ ์บ์์์ ์ ๊ฑฐ๋ฉ๋๋ค.
์๋ ์์ ๋ [DynamicCache]๋ก ์์ฑ ๋ฃจํ๋ฅผ ๋ง๋๋ ๋ฐฉ๋ฒ์ ๋ณด์ฌ์ค๋๋ค. ๋
ผ์๋ ๋ฐ์ ๊ฐ์ด, ์ดํ
์
๋ง์คํฌ๋ ๊ณผ๊ฑฐ์ ํ์ฌ ํ ํฐ๊ฐ์ ์ฐ๊ฒฐ์ด๋ฉฐ ๋ค์ ํ ํฐ์ ์ํด ์บ์ ์์น์ 1์ด ์ถ๊ฐ๋ฉ๋๋ค.
import torch
from transformers import AutoTokenizer, AutoModelForCausalLM, DynamicCache, infer_device
device = f"{infer_device()}:0"
model_id = "meta-llama/Llama-2-7b-chat-hf"
model = AutoModelForCausalLM.from_pretrained(model_id, dtype=torch.bfloat16, device_map=device)
tokenizer = AutoTokenizer.from_pretrained(model_id)
past_key_values = DynamicCache(config=model.config)
messages = [{"role": "user", "content": "Hello, what's your name."}]
inputs = tokenizer.apply_chat_template(messages, add_generation_prompt=True, return_tensors="pt", return_dict=True).to(model.device)
generated_ids = inputs.input_ids
max_new_tokens = 10
for _ in range(max_new_tokens):
outputs = model(**inputs, past_key_values=past_key_values, use_cache=True)
# ํ์์ ๊ธฐ๋ฒ์ผ๋ก ๋ค์ ํ ํฐ ํ๋๋ฅผ ์ํ๋ง
next_token_ids = outputs.logits[:, -1:].argmax(-1)
generated_ids = torch.cat([generated_ids, next_token_ids], dim=-1)
# ์ฒ๋ฆฌ๋์ง ์์ ํ ํฐ์ ๋จ๊ฒจ๋์ด ๋ค์ ์์ฑ ๋จ๊ณ๋ฅผ ์ํ ์
๋ ฅ์ ์ค๋นํฉ๋๋ค. ์ฐ๋ฆฌ์ ๊ฒฝ์ฐ ์๋ก์ด ํ ํฐ ํ๋๋ง ์กด์ฌํฉ๋๋ค.
# ์์์ ์ค๋ช
ํ ๋๋ก ์๋ก์ด ํ ํฐ์ ์ํด ์ดํ
์
๋ง์คํฌ๋ฅผ ํ์ฅํฉ๋๋ค
attention_mask = inputs["attention_mask"]
attention_mask = torch.cat([attention_mask, attention_mask.new_ones((attention_mask.shape[0], 1))], dim=-1)
inputs = {"input_ids": next_token_ids, "attention_mask": attention_mask}
print(tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0])
"[INST] Hello, what's your name. [/INST] Hello! My name is LLaMA,"