File size: 4,357 Bytes
515688a | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | """๋ฐฐํฌ์ฉ ๋
๋ฆฝ ๋ชจ๋ธ ์ ์.
์ด ํ์ผ ํ๋์ `config.json`, `model.safetensors`, `tokenizer.json` ๋ง ์์ผ๋ฉด
๊ฐ์ค์น๋ฅผ ์ด ์ ์๋ค. ํ์ต ํ๋ค์ค(deeptool)๋ ํ์ ์๋ค -- ๊ฐ์ค์น ํ๋ ์ด์๊ณ
ํ์ต์ฉ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ค์นํ๊ฒ ๋ง๋ค ์ด์ ๊ฐ ์๋ค.
`src/tinyllm/model.py` ์ TinyLM ๊ณผ ๋ชจ๋ ์ด๋ฆ์ด ๊ฐ์์ผ state_dict ํค๊ฐ ๋ง๋๋ค.
๊ทธ ๋์ผ์ฑ์ tests/test_modeling.py ๊ฐ ์ค์ ์ฒดํฌํฌ์ธํธ๋ก ๊ฒ์ฆํ๋ค.
from modeling_tinyllm import TinyLM
model = TinyLM.from_pretrained(".")
"""
import json
from pathlib import Path
import torch
from torch import nn
class TinyLM(nn.Module):
"""GPT-2 ํํ์ decoder-only LM, torch ๋ด์ฅ ๋ ์ด์ด๋ก ์กฐ๋ฆฝ.
decoder-only ๋ฅผ `TransformerEncoderLayer` ๋ก ๋ง๋๋ ์ด์ ๋
`TransformerDecoderLayer` ๊ฐ cross-attention ์ฉ `memory` ๋ฅผ ํ์๋ก ์๊ตฌํ๊ธฐ
๋๋ฌธ์ด๋ค. causal mask ๋ฅผ ๋๊ธฐ๋ฉด encoder layer ๊ฐ ๊ณง GPT ๋ธ๋ก์ด๋ค.
"""
def __init__(self, vocab_size=8000, d_model=512, n_head=8, n_layer=8,
d_ff=2048, block_size=512, dropout=0.0):
super().__init__()
self.vocab_size = vocab_size
self.block_size = block_size
self.tok = nn.Embedding(vocab_size, d_model)
self.pos = nn.Embedding(block_size, d_model)
self.drop = nn.Dropout(dropout)
layer = nn.TransformerEncoderLayer(
d_model, n_head, d_ff, dropout,
activation="gelu", norm_first=True, batch_first=True,
)
self.blocks = nn.TransformerEncoder(
layer, n_layer, norm=nn.RMSNorm(d_model), enable_nested_tensor=False,
)
self.head = nn.Linear(d_model, vocab_size, bias=False)
self.head.weight = self.tok.weight # weight tying
def forward(self, ids):
"""ids: (B, T) int64 -> logits (B, T, vocab_size)"""
T = ids.size(1)
pos = torch.arange(T, device=ids.device)
h = self.drop(self.tok(ids) + self.pos(pos))
mask = nn.Transformer.generate_square_subsequent_mask(T, device=ids.device)
return self.head(self.blocks(h, mask=mask, is_causal=True))
@torch.no_grad()
def generate(self, ids, max_new_tokens, temperature=0.8, top_k=40,
stop_id=None):
"""ids: (B, T) ํ๋กฌํํธ -> (B, T + n). stop_id ๊ฐ ์ ๋ฐฐ์น์ ๋์ค๋ฉด ์กฐ๊ธฐ ์ข
๋ฃ.
KV ์บ์๋ ์ฐ์ง ์๋๋ค. 29M ยท 512 ํ ํฐ์ด๋ฉด forward ๊ฐ ์ ms ๋ผ ์บ์๊ฐ ์์ด๋
100 ํ ํฐ ์์ฑ์ด 1 ์ด ๋ฏธ๋ง์ด๋ค.
"""
was_training = self.training
self.eval()
for _ in range(max_new_tokens):
window = ids[:, -self.block_size:]
logits = self(window)[:, -1] / temperature
if top_k:
kth = logits.topk(min(top_k, logits.size(-1)), dim=-1).values[:, -1:]
logits = logits.masked_fill(logits < kth, float("-inf"))
nxt = torch.multinomial(logits.softmax(-1), num_samples=1)
ids = torch.cat([ids, nxt], dim=1)
if stop_id is not None and bool((nxt == stop_id).all()):
break
self.train(was_training)
return ids
@classmethod
def from_pretrained(cls, path, device="cpu"):
"""`config.json` ๊ณผ `model.safetensors` ๊ฐ ์๋ ํด๋์์ ๋ชจ๋ธ์ ์ธ์ด๋ค.
์ ์ฅ๋ณธ์๋ `head.weight` ๊ฐ ์๋ค. tying ๋๋ฌธ์ `tok.weight` ์ ์ ์ฅ์๋ฅผ
๊ณต์ ํ๋๋ฐ safetensors ๋ ๊ณต์ ์ ์ฅ์๋ฅผ ๊ฑฐ๋ถํ๊ธฐ ๋๋ฌธ์ด๋ค. ์์ฑ์๊ฐ ๋ค์
๋ฌถ์ผ๋ฏ๋ก `strict=False` ๋ก ๋ฃ์ด๋ head ๊ฐ ๋น์ง ์๋๋ค.
"""
from safetensors.torch import load_file
path = Path(path)
config = json.loads((path / "config.json").read_text())
model = cls(**{k: config[k] for k in
("vocab_size", "d_model", "n_head", "n_layer",
"d_ff", "block_size")})
state = load_file(path / "model.safetensors")
missing, unexpected = model.load_state_dict(state, strict=False)
if unexpected:
raise ValueError(f"์ ์ฅ๋ณธ์ ๋ชจ๋ฅด๋ ํ
์๊ฐ ์๋ค: {unexpected}")
if missing != ["head.weight"]:
raise ValueError(f"๋น ์ง ํ
์๊ฐ head.weight ๋ง์ด ์๋๋ค: {missing}")
return model.to(device).eval()
|