AseemD commited on
Commit
99f24ee
·
1 Parent(s): b398d70

add the application and model file

Browse files
Files changed (2) hide show
  1. app.py +84 -0
  2. model.py +231 -0
app.py ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import tiktoken
3
+ import gradio as gr
4
+ from model import GPT, GPTConfig
5
+ from torch.nn import functional as F
6
+ from transformers import GPT2LMHeadModel, GPT2Tokenizer
7
+
8
+ # Set device
9
+ device = 'cuda' if torch.cuda.is_available() else 'cpu'
10
+
11
+ # Load the tokenizer
12
+ TOKENIZER = tiktoken.get_encoding('gpt2')
13
+
14
+ # Load untrained model
15
+ UNTRAINED_MODEL = GPT(GPTConfig)
16
+ UNTRAINED_MODEL.to(device)
17
+ UNTRAINED_MODEL.eval()
18
+
19
+ # Load fine-tuned model
20
+ TRAINED_MODEL = GPT(GPTConfig)
21
+ checkpoint = torch.load("log/model_19072.pt", weights_only=False)
22
+ TRAINED_MODEL.load_state_dict(checkpoint["model"])
23
+ TRAINED_MODEL.to(device)
24
+ TRAINED_MODEL.eval()
25
+
26
+
27
+ def generate_text(input, model, num_sequences, max_length):
28
+ tokens = TOKENIZER.encode(input)
29
+ tokens = torch.tensor(tokens, dtype=torch.long)
30
+ tokens = tokens.unsqueeze(0).repeat(num_sequences, 1)
31
+ x = tokens.to(device)
32
+
33
+ sentences = []
34
+ while x.size(1) < max_length:
35
+ with torch.no_grad():
36
+ logits, loss = model(x)
37
+ logits = logits[:, -1, :]
38
+ probs = F.softmax(logits, dim=-1)
39
+ topk_probs, topk_indices = torch.topk(probs, 50, dim=-1)
40
+ ix = torch.multinomial(topk_probs, 1)
41
+ xcol = torch.gather(topk_indices, -1, ix)
42
+ x = torch.cat((x, xcol), dim=1)
43
+
44
+ for i in range(num_sequences):
45
+ tokens = x[i, :max_length].tolist()
46
+ decoded = TOKENIZER.decode(tokens)
47
+ sentences.append(decoded)
48
+
49
+ return sentences
50
+
51
+
52
+ def gradio_fn(prompt, num_sequences=1, max_length=30):
53
+ """Generate text using both models."""
54
+ untrained_texts = generate_text(prompt, UNTRAINED_MODEL, num_sequences, max_length)
55
+ untrained_output = "\n\n".join(f"> {s}" for s in untrained_texts)
56
+
57
+ trained_texts = generate_text(prompt, TRAINED_MODEL, num_sequences, max_length)
58
+ trained_output = "\n\n".join(f"> {s}" for s in trained_texts)
59
+
60
+ return untrained_output, trained_output
61
+
62
+ # Gradio interface
63
+ def main():
64
+ interface = gr.Interface(
65
+ fn=gradio_fn,
66
+ inputs=[
67
+ gr.Textbox(label="Enter your prompt here:"),
68
+ gr.Slider(minimum=1, maximum=10, step=1, label="Number of Generations"),
69
+ gr.Slider(minimum=10, maximum=100, step=10, label="Max Length"),
70
+ ],
71
+ outputs=[
72
+ gr.Textbox(label="Generated Text (Untrained Model)"),
73
+ gr.Textbox(label="Generated Text (Trained Model)"),
74
+ ],
75
+ title="GPT-2 Text Generator",
76
+ description="Generate text an untrained and a trained GPT-2 model."
77
+ )
78
+
79
+ interface.launch(share=True)
80
+
81
+ if __name__ == "__main__":
82
+ main()
83
+
84
+
model.py ADDED
@@ -0,0 +1,231 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import inspect
3
+ import torch.nn as nn
4
+ from dataclasses import dataclass
5
+ from torch.nn import functional as F
6
+
7
+ # Model Architecture ================================================================================================================
8
+
9
+ @dataclass
10
+ class GPTConfig:
11
+ block_size: int = 1024 # max sequence length
12
+ vocab_size: int = 50257 # number of tokens: 50,000 BPE merges + 256 bytes tokens + 1 <|endoftext|> token
13
+ n_layer: int = 12 # number of layers
14
+ n_head: int = 12 # number of heads in the multihead attention
15
+ n_embd: int = 768 # embedding dimension
16
+ dropout: float = 0.1
17
+
18
+
19
+ # FLASH ATTENTION
20
+ # Flash attention is a kernel fusion operation of the attention operation.
21
+ # It was found out manually. It cannot be found by compilers Because it requires an algorithmic rewrite of how attention is implemented.
22
+ # Though it performs more operations, it is faster than regular attention because it is mindful of the memory hierarchy and has high AI.
23
+ # It avoids read and write operations. It never materializes the large NxN attention matrix which reduces AI.
24
+ # It relies on the online softmax trick which incrementally calculates softmax without having to materialize the inputs to the softmax.
25
+
26
+ # This is a combination of attention and multi-head attention.
27
+ # There are 1024 tokens in a sequence each emitting 3 vectors - Q, K, V.
28
+ class CausalSelfAttention(nn.Module):
29
+ def __init__(self, config):
30
+ super().__init__()
31
+ assert config.n_embd % config.n_head == 0
32
+ # Key, Query and value Projections for all heads, but in a batch
33
+ self.c_attn = nn.Linear(config.n_embd, 3 * config.n_embd)
34
+ # Output projection
35
+ self.c_proj = nn.Linear(config.n_embd, config.n_embd)
36
+ self.c_proj.NANOGPT_SCALE_INIT = 1
37
+ # Regularization
38
+ self.n_head = config.n_head
39
+ self.n_embd = config.n_embd
40
+ # not really a 'bias', more of a mask, but following the OpenAI/HF naming though
41
+ self.register_buffer("bias", torch.tril(torch.ones(config.block_size, config.block_size))
42
+ .view(1, 1, config.block_size, config.block_size))
43
+
44
+ def forward(self, x):
45
+ B, T, C = x.size() # (batch_size, sequence_length, n_embd)
46
+
47
+ # Calculate Query, Key, Values for all heads in batch and move head forward to be the batch dim.
48
+ # nh is "number of heads", hs is "head size", and C (number of channels) = nh * hs.
49
+ # e.g. in GPT-2 (124M), n_head=12, hs=64, so nh*hs=C=768 channels in the Transformer.
50
+ qkv = self.c_attn(x)
51
+ q, k, v = qkv.split(self.n_embd, dim=2)
52
+ k = k.view(B, T, self.n_head, C // self.n_head).transpose(1, 2) # (B, nh, T, hs)
53
+ q = q.view(B, T, self.n_head, C // self.n_head).transpose(1, 2) # (B, nh, T, hs)
54
+ v = v.view(B, T, self.n_head, C // self.n_head).transpose(1, 2) # (B, nh, T, hs)
55
+
56
+ # Attention (materializes the large (T,T) matrix for all the queries and keys)
57
+ # att = (q @ k.transpose(-2, -1)) * (1.0 / math.sqrt(k.size(-1)))
58
+ # att = att.masked_fill(self.bias[:,:,:T,:T] == 0, float('-inf')) # Only looks at previous tokens
59
+ # att = F.softmax(att, dim=-1)
60
+ # y = att @ v # (B, nh, T, T) x (B, nh, T, hs) -> (B, nh, T, hs)
61
+ y = F.scaled_dot_product_attention(q, k, v, is_causal=True)
62
+ y = y.transpose(1, 2).contiguous().view(B, T, C) # re-assemble all head outputs side by side
63
+ # Output projection
64
+ y = self.c_proj(y)
65
+ return y
66
+
67
+
68
+ class MLP(nn.Module):
69
+ def __init__(self, config):
70
+ super().__init__()
71
+ self.c_fc = nn.Linear(config.n_embd, 4 * config.n_embd)
72
+ self.gelu = nn.GELU()
73
+ self.c_proj = nn.Linear(4 * config.n_embd, config.n_embd)
74
+ self.c_proj.NANOGPT_SCALE_INIT = 1
75
+ self.dropout = nn.Dropout(config.dropout)
76
+
77
+ def forward(self, x):
78
+ x = self.c_fc(x)
79
+ x = self.gelu(x)
80
+ x = self.c_proj(x)
81
+ x = self.dropout(x)
82
+ return x
83
+
84
+
85
+ # In the GPT-3 paper, the LayerNorm layers are applied
86
+ # before the linear and attention layers.
87
+ class Block(nn.Module):
88
+
89
+ def __init__(self, config):
90
+ super().__init__()
91
+ self.ln_1 = nn.LayerNorm(config.n_embd)
92
+ self.attn = CausalSelfAttention(config)
93
+ self.ln_2 = nn.LayerNorm(config.n_embd)
94
+ self.mlp = MLP(config)
95
+
96
+ def forward(self, x):
97
+ x = x + self.attn(self.ln_1(x))
98
+ x = x + self.mlp(self.ln_2(x))
99
+ return x
100
+
101
+
102
+ # A final Layernorm is added before the final linear head.
103
+ class GPT(nn.Module):
104
+ def __init__(self, config):
105
+ super().__init__()
106
+ self.config = config
107
+
108
+ self.transformer = nn.ModuleDict(dict(
109
+ wte = nn.Embedding(config.vocab_size, config.n_embd), # Embedding
110
+ wpe = nn.Embedding(config.block_size, config.n_embd), # Position embeddings
111
+ h = nn.ModuleList([Block(config) for _ in range(config.n_layer)]), # Transformer blocks
112
+ ln_f = nn.LayerNorm(config.n_embd), # Final layer norm (GPT3)
113
+ ))
114
+ self.lm_head = nn.Linear(config.n_embd, config.vocab_size, bias=False)
115
+
116
+ # Weight Sharing Scheme
117
+ self.transformer.wte.weight = self.lm_head.weight
118
+ # init params
119
+ self.apply(self._init_weights)
120
+
121
+ # Weight Initialization
122
+ def _init_weights(self, module):
123
+ if isinstance(module, nn.Linear):
124
+ std = 0.02
125
+ if hasattr(module, 'NANOGPT_SCALE_INIT'):
126
+ std *= (2 * self.config.n_layer) ** -0.5
127
+ torch.nn.init.normal_(module.weight, mean=0.0, std=std)
128
+ if module.bias is not None:
129
+ torch.nn.init.zeros_(module.bias)
130
+ elif isinstance(module, nn.Embedding):
131
+ torch.nn.init.normal_(module.weight, mean=0.0, std=0.02)
132
+
133
+
134
+ def forward(self, idx, targets=None):
135
+ # idx is of shape (B, T)
136
+ B, T = idx.size()
137
+ assert T <= self.config.block_size, f"Cannot forward sequence of length {T}, block size is only {self.config.block_size}"
138
+ # Forward the token and posisition embeddings
139
+ pos = torch.arange(0, T, dtype=torch.long, device=idx.device) # shape (T)
140
+ pos_emb = self.transformer.wpe(pos) # position embeddings of shape (T, n_embd)
141
+ tok_emb = self.transformer.wte(idx) # token embeddings of shape (B, T, n_embd)
142
+ x = tok_emb + pos_emb
143
+ # Forward the blocks of the transformer
144
+ for block in self.transformer.h:
145
+ x = block(x)
146
+ # Forward the final layernorm and the classifier
147
+ x = self.transformer.ln_f(x)
148
+ logits = self.lm_head(x) # (B, T, vocab_size)
149
+ loss = None
150
+ if targets is not None:
151
+ # Flatten out multidiemntsional input for cross entropy.
152
+ loss = F.cross_entropy(logits.view(-1, logits.size(-1)), targets.view(-1))
153
+
154
+ return logits, loss
155
+
156
+ @classmethod
157
+ def from_pretrained(cls, model_type):
158
+ """Loads pretrained GPT-2 model weights from huggingface"""
159
+
160
+ assert model_type in {'gpt2', 'gpt2-medium', 'gpt2-large', 'gpt2-xl'}
161
+ from transformers import GPT2LMHeadModel
162
+ print("loading weights from pretrained gpt: %s" % model_type)
163
+
164
+ # n_layer, n_head and n_embd are determined from model_type
165
+ config_args = {
166
+ 'gpt2': dict(n_layer=12, n_head=12, n_embd=768), # 124M params
167
+ 'gpt2-medium': dict(n_layer=24, n_head=16, n_embd=1024), # 350M params
168
+ 'gpt2-large': dict(n_layer=36, n_head=20, n_embd=1280), # 774M params
169
+ 'gpt2-xl': dict(n_layer=48, n_head=25, n_embd=1600), # 1558M params
170
+ }[model_type]
171
+ config_args['vocab_size'] = 50257 # always 50257 for GPT model checkpoints
172
+ config_args['block_size'] = 1024 # always 1024 for GPT model checkpoints
173
+
174
+ # create a from-scratch initialized minGPT model
175
+ config = GPTConfig(**config_args)
176
+ model = GPT(config)
177
+ sd = model.state_dict()
178
+ sd_keys = sd.keys()
179
+ sd_keys = [k for k in sd_keys if not k.endswith('.attn.bias')] # Discard this mask / buffer, not a param
180
+
181
+ # init a huggingface/transformers model
182
+ model_hf = GPT2LMHeadModel.from_pretrained(model_type)
183
+ sd_hf = model_hf.state_dict()
184
+
185
+ # copy while ensuring all of the parameters are aligned and match in names and shapes
186
+ sd_keys_hf = sd_hf.keys()
187
+ sd_keys_hf = [k for k in sd_keys_hf if not k.endswith('.attn.masked_bias')] # ignore these, just a buffer
188
+ sd_keys_hf = [k for k in sd_keys_hf if not k.endswith('.attn.bias')] # same, just the mask (buffer)
189
+ transposed = ['attn.c_attn.weight', 'attn.c_proj.weight', 'mlp.c_fc.weight', 'mlp.c_proj.weight']
190
+ # Basically the openai checkpoints use a "Conv1D" module, but we only want to use a vanilla Linear
191
+ # This means that we have to transpose these weights when we import them
192
+ assert len(sd_keys_hf) == len(sd_keys), f"mismatched keys: {len(sd_keys_hf)} != {len(sd_keys)}"
193
+ for k in sd_keys_hf:
194
+ if any(k.endswith(w) for w in transposed):
195
+ # special treatment for the Conv1D weights we need to transpose
196
+ assert sd_hf[k].shape[::-1] == sd[k].shape
197
+ with torch.no_grad():
198
+ sd[k].copy_(sd_hf[k].t())
199
+ else:
200
+ # vanilla copy over the other parameters
201
+ assert sd_hf[k].shape == sd[k].shape
202
+ with torch.no_grad():
203
+ sd[k].copy_(sd_hf[k])
204
+ return model
205
+
206
+ # The parameters are divided into decay and nondecay params.
207
+ # It is common to not decay bias and 1 dimensional tensors.
208
+ def configure_optimizers(self, weight_decay, learning_rate, device, master_process):
209
+ # start with all of the candidate parameters (that require grad)
210
+ param_dict = {pn: p for pn, p in self.named_parameters()}
211
+ param_dict = {pn: p for pn, p in param_dict.items() if p.requires_grad}
212
+ # create optim groups. Any parameters that is 2D will be weight decayed, otherwise no.
213
+ # i.e. all weight tensors in matmuls + embeddings decay, all biases and layernorms don't.
214
+ decay_params = [p for n, p in param_dict.items() if p.dim() >= 2] # Embeddings and weights in matmul
215
+ nodecay_params = [p for n, p in param_dict.items() if p.dim() < 2] # 1D tensors like LayerNorms, biases
216
+ optim_groups = [
217
+ {'params': decay_params, 'weight_decay': weight_decay},
218
+ {'params': nodecay_params, 'weight_decay': 0.0}
219
+ ]
220
+ num_decay_params = sum(p.numel() for p in decay_params)
221
+ num_nodecay_params = sum(p.numel() for p in nodecay_params)
222
+ if master_process:
223
+ print(f"Num decayed parameter tensors: {len(decay_params)}, with {num_decay_params:,} parameters")
224
+ print(f"Num non-decayed parameter tensors: {len(nodecay_params)}, with {num_nodecay_params:,} parameters")
225
+ # Create AdamW optimizer and use the fused version if it is available
226
+ fused_available = 'fused' in inspect.signature(torch.optim.AdamW).parameters
227
+ use_fused = fused_available and 'cuda' in device # Fuses the kernels used in the updation of parameters to make it faster
228
+ if master_process:
229
+ print(f"Using fused AdamW: {use_fused} \n")
230
+ optimizer = torch.optim.AdamW(optim_groups, lr=learning_rate, betas=(0.9, 0.95), eps=1e-8, fused=use_fused)
231
+ return optimizer