Views
No views yet
| Property | Value |
|---|---|
| Architecture | Decoder-only Transformer (GPT-2 style) |
| Parameters | ~8M |
| Context window | 256 tokens |
| Vocab size | 4096 |
| Embed size | 256 |
| Layers | 6 |
| Heads | 6 |
| Training tokens | ~17M |
| Final val loss | 3.30 |
| GPU | NVIDIA L4 |
| Framework | PyTorch |
pip install torch sentencepiece huggingface_hub1import torch
2import torch.nn as nn
3from torch.nn import functional as F
4import sentencepiece as spm
5from huggingface_hub import hf_hub_download
6import re, io
7
8# ============================================
9# CONFIG
10# ============================================
11REPO_ID = "Jyotiprakash4357/poem-llm-small"
12embed_size = 256
13n_layers = 6
14n_heads = 6
15context = 256
16BIAS = True
17dropout = 0.0
18device = "cuda" if torch.cuda.is_available() else "cpu"
19
20# ============================================
21# LOAD TOKENIZER
22# ============================================
23tokenizer_path = hf_hub_download(
24 repo_id=REPO_ID,
25 filename="poems_tokenizer.model"
26)
27sp = spm.SentencePieceProcessor(model_file=tokenizer_path)
28vocab_size = sp.get_piece_size()
29encode = lambda s: sp.Encode(s)
30decode = lambda l: sp.Decode(l)
31
32# ============================================
33# MODEL ARCHITECTURE
34# ============================================
35class Head(nn.Module):
36 def __init__(self, head_size):
37 super().__init__()
38 self.queries = nn.Linear(embed_size, head_size, bias=BIAS)
39 self.keys = nn.Linear(embed_size, head_size, bias=BIAS)
40 self.values = nn.Linear(embed_size, head_size, bias=BIAS)
41 self.register_buffer('tril', torch.tril(torch.ones(context, context)))
42 self.dropout = nn.Dropout(dropout)
43
44 def forward(self, x):
45 BS, SL, VS = x.shape
46 q = self.queries(x)
47 k = self.keys(x)
48 v = self.values(x)
49 attn_w = q @ k.transpose(-2, -1) * k.shape[-1]**-0.5
50 attn_w = attn_w.masked_fill(self.tril[:SL, :SL]==0, float('-inf'))
51 attn_w = F.softmax(attn_w, dim=-1)
52 attn_w = self.dropout(attn_w)
53 return attn_w @ v
54
55class Multihead(nn.Module):
56 def __init__(self, n_heads, head_size):
57 super().__init__()
58 self.heads = nn.ModuleList([Head(head_size) for _ in range(n_heads)])
59 self.combine = nn.Linear(head_size * n_heads, embed_size, bias=BIAS)
60 self.dropout = nn.Dropout(dropout)
61
62 def forward(self, x):
63 x = torch.cat([head(x) for head in self.heads], dim=-1)
64 x = self.combine(x)
65 return self.dropout(x)
66
67class ForwardLayer(nn.Module):
68 def __init__(self, embed_size):
69 super().__init__()
70 self.network = nn.Sequential(
71 nn.Linear(embed_size, 6*embed_size, bias=BIAS),
72 nn.GELU(),
73 nn.Linear(6*embed_size, embed_size, bias=BIAS),
74 nn.Dropout(dropout)
75 )
76 def forward(self, x):
77 return self.network(x)
78
79class Block(nn.Module):
80 def __init__(self, n_heads):
81 super().__init__()
82 head_size = embed_size // n_heads
83 self.ma = Multihead(n_heads, head_size)
84 self.feed_forward = ForwardLayer(embed_size)
85 self.ln1 = nn.LayerNorm(embed_size)
86 self.ln2 = nn.LayerNorm(embed_size)
87
88 def forward(self, x):
89 x = x + self.ma(self.ln1(x))
90 x = x + self.feed_forward(self.ln2(x))
91 return x
92
93class GPT(nn.Module):
94 def __init__(self):
95 super().__init__()
96 self.embeddings = nn.Embedding(vocab_size, embed_size)
97 self.positions = nn.Embedding(context, embed_size)
98 self.blocks = nn.Sequential(*[Block(n_heads) for _ in range(n_layers)])
99 self.ln = nn.LayerNorm(embed_size)
100 self.final_linear = nn.Linear(embed_size, vocab_size, bias=BIAS)
101 self.apply(self._init_weights)
102
103 def _init_weights(self, module):
104 if isinstance(module, nn.Linear):
105 torch.nn.init.normal_(module.weight, mean=0.0, std=0.02)
106 if module.bias is not None:
107 torch.nn.init.zeros_(module.bias)
108 elif isinstance(module, nn.Embedding):
109 torch.nn.init.normal_(module.weight, mean=0.0, std=0.02)
110
111 def forward(self, input, targets=None):
112 loss = None
113 BS, SL = input.shape
114 emb = self.embeddings(input)
115 pos = self.positions(torch.arange(SL, device=device))
116 x = emb + pos
117 x = self.blocks(x)
118 x = self.ln(x)
119 logits = self.final_linear(x)
120 if targets is not None:
121 BS, SL, VS = logits.shape
122 logits = logits.view(BS*SL, VS)
123 targets = targets.view(BS*SL)
124 loss = F.cross_entropy(logits, targets)
125 return logits, loss
126
127 def generate(self, input, max=200, temperature=0.8,
128 top_k=40, repetition_penalty=1.3):
129 for _ in range(max):
130 input_crop = input[:, -context:]
131 logits, _ = self(input_crop)
132 logits = logits[:, -1, :]
133 for token_id in set(input[0].tolist()):
134 logits[0, token_id] /= repetition_penalty
135 logits = logits / temperature
136 top_k_logits, top_k_indices = torch.topk(logits, top_k)
137 probs = F.softmax(top_k_logits, dim=-1)
138 next_token_pos = torch.multinomial(probs, num_samples=1)
139 next_token = top_k_indices.gather(-1, next_token_pos)
140 input = torch.cat((input, next_token), dim=1)
141 return input
142
143# ============================================
144# LOAD MODEL
145# ============================================
146model = GPT().to(device)
147
148model_path = hf_hub_download(
149 repo_id=REPO_ID,
150 filename="poems_latest.pt"
151)
152
153with open(model_path, 'rb') as f:
154 buffer = io.BytesIO(f.read())
155
156checkpoint = torch.load(buffer, map_location=device)
157model.load_state_dict(checkpoint['model_state_dict'])
158model.eval()
159
160# ============================================
161# GENERATE
162# ============================================
163def clean_output(text, prompt):
164 text = text[len(prompt):]
165 text = re.sub(r'©.*?\n', '', text)
166 lines = text.split('\n')
167 clean_lines = [
168 l for l in lines
169 if len(re.findall(r'[^\x00-\x7F]', l)) <= 2
170 ]
171 return '\n'.join(clean_lines).strip()
172
173@torch.no_grad()
174def generate_poem(prompt, max_tokens=200, temperature=0.8,
175 top_k=40, repetition_penalty=1.3):
176 t1 = torch.tensor(encode(prompt), dtype=torch.long, device=device)
177 t1 = t1[None, :]
178 output = model.generate(
179 t1, max=max_tokens,
180 temperature=temperature,
181 top_k=top_k,
182 repetition_penalty=repetition_penalty
183 )[0].tolist()
184 return clean_output(decode(output), prompt)
185
186# Test
187print(generate_poem("The roses bloom in darkness"))
188print(generate_poem("Death comes softly"))
189print(generate_poem("Love is a shadow"))Death comes softly,
From the dark and the dim cloudless sea,
There is one thing in love, which has a soul
That once came into the world...