alainbrown commited on
Commit
b3873f1
·
verified ·
1 Parent(s): 5af5dfb

Publish Tiny GPT model

Browse files
README.md ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ language:
3
+ - en
4
+ license: mit
5
+ library_name: transformers
6
+ pipeline_tag: text-generation
7
+ datasets:
8
+ - roneneldan/TinyStories
9
+ tags:
10
+ - custom_code
11
+ - educational
12
+ ---
13
+
14
+ # Tiny GPT
15
+
16
+ Tiny GPT is an educational decoder-only Transformer trained from scratch on
17
+ the [TinyStories](https://huggingface.co/datasets/roneneldan/TinyStories)
18
+ dataset. The implementation is intentionally small and readable.
19
+
20
+ ## Model details
21
+
22
+ - Architecture: decoder-only causal language model
23
+ - Context length: 512 tokens
24
+ - Vocabulary size: 10,000
25
+ - Hidden size: 256
26
+ - Transformer layers: 6
27
+ - Attention heads: 8
28
+
29
+ Source code: https://github.com/alainbrown/tiny-gpt
30
+
31
+ ## Usage
32
+
33
+ This repository contains custom Transformers code. Review it before enabling
34
+ `trust_remote_code`.
35
+
36
+ ```python
37
+ from transformers import AutoModelForCausalLM, AutoTokenizer
38
+
39
+ repo_id = "alainbrown/tiny-gpt"
40
+ tokenizer = AutoTokenizer.from_pretrained(repo_id, trust_remote_code=True)
41
+ model = AutoModelForCausalLM.from_pretrained(repo_id, trust_remote_code=True)
42
+
43
+ inputs = tokenizer("Once upon a time", return_tensors="pt")
44
+ logits = model(**inputs).logits
45
+ ```
46
+
47
+ ## Intended use
48
+
49
+ This model is intended for education and experimentation. It is not intended
50
+ for production, factual question answering, or safety-critical applications.
51
+
52
+ ## Limitations
53
+
54
+ The model is small, trained on synthetic children's stories, and has not been
55
+ comprehensively evaluated. It may produce incoherent, repetitive, incorrect,
56
+ or inappropriate text. English is the only supported language.
57
+
58
+ ## Training
59
+
60
+ The training pipeline is available in the linked GitHub repository. This model
61
+ repository excludes optimizer and progress state and contains inference files
62
+ only.
config.json ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "architectures": [
3
+ "TinyGPTForCausalLM"
4
+ ],
5
+ "auto_map": {
6
+ "AutoConfig": "configuration_tiny_gpt.TinyGPTConfig",
7
+ "AutoModelForCausalLM": "modeling_tiny_gpt.TinyGPTForCausalLM"
8
+ },
9
+ "context_size": 512,
10
+ "d_model": 256,
11
+ "dropout": 0.1,
12
+ "dtype": "float32",
13
+ "eos_token_id": 0,
14
+ "hidden_size": 256,
15
+ "max_position_embeddings": 512,
16
+ "model_type": "tiny_gpt",
17
+ "n_heads": 8,
18
+ "n_layers": 6,
19
+ "num_attention_heads": 8,
20
+ "num_hidden_layers": 6,
21
+ "pad_token_id": 0,
22
+ "tie_word_embeddings": true,
23
+ "transformers_version": "5.12.1",
24
+ "vocab_size": 10000
25
+ }
configuration_tiny_gpt.py ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from transformers import PretrainedConfig
2
+
3
+
4
+ class TinyGPTConfig(PretrainedConfig):
5
+ model_type = "tiny_gpt"
6
+
7
+ def __init__(
8
+ self,
9
+ context_size=32,
10
+ vocab_size=1024,
11
+ d_model=64,
12
+ n_layers=4,
13
+ n_heads=4,
14
+ dropout=0.1,
15
+ tie_word_embeddings=True,
16
+ use_cache=False,
17
+ **kwargs,
18
+ ):
19
+ self.context_size = context_size
20
+ self.vocab_size = vocab_size
21
+ self.d_model = d_model
22
+ self.n_layers = n_layers
23
+ self.n_heads = n_heads
24
+ self.dropout = dropout
25
+ self.hidden_size = d_model
26
+ self.num_hidden_layers = n_layers
27
+ self.num_attention_heads = n_heads
28
+ self.max_position_embeddings = context_size
29
+ super().__init__(
30
+ tie_word_embeddings=tie_word_embeddings,
31
+ use_cache=use_cache,
32
+ **kwargs,
33
+ )
generation_config.json ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ {
2
+ "_from_model_config": true,
3
+ "transformers_version": "5.12.1"
4
+ }
model.py ADDED
@@ -0,0 +1,166 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import math
2
+ import torch
3
+ from torch import nn
4
+
5
+ """
6
+ token embeddings
7
+ learned positional embeddings
8
+ causal self-attention
9
+ feed-forward network
10
+ custom LayerNorm
11
+ residual connections
12
+ stacked transformer blocks
13
+ GELU
14
+ dropout
15
+ Pre-LayerNorm
16
+ tied token embedding/output projection weights
17
+ multi-head attention
18
+
19
+ """
20
+ class Model(nn.Module):
21
+ def __init__(self, context_size, vocab_size, d_model, n_layers, n_heads, dropout=0.1):
22
+ super().__init__()
23
+
24
+ self.context_size = context_size
25
+ self.vocab_size = vocab_size
26
+ self.d_model = d_model
27
+ self.n_layers = n_layers
28
+ self.n_heads = n_heads
29
+ self.dropout_p = dropout
30
+
31
+ self.token_embedding = nn.Embedding(vocab_size, d_model)
32
+ self.position_embedding = nn.Embedding(context_size, d_model)
33
+ self.transformer_blocks = nn.ModuleList(
34
+ [TransformerBlock(d_model, n_heads, dropout) for _ in range(n_layers)]
35
+ )
36
+ self.linear = nn.Linear(d_model, vocab_size, bias=False)
37
+ self.dropout = nn.Dropout(dropout)
38
+ self.final_layer_norm = LayerNorm(d_model)
39
+
40
+ def forward(self, x):
41
+ B, T = x.shape
42
+
43
+ assert T <= self.context_size, "Input sequence is longer than context_size"
44
+
45
+ positions = torch.arange(T, device=x.device)
46
+
47
+ position = self.position_embedding(positions)
48
+ token = self.token_embedding(x)
49
+
50
+ x = token + position
51
+ x = self.dropout(x)
52
+
53
+ for block in self.transformer_blocks:
54
+ x = block(x)
55
+
56
+ x = self.final_layer_norm(x)
57
+ logits = self.linear(x)
58
+
59
+ return logits
60
+
61
+ class FeedForward(nn.Module):
62
+ def __init__(self, d_model):
63
+ super().__init__()
64
+ self.ff1 = nn.Linear(d_model, 4 * d_model)
65
+ self.ff2 = nn.Linear(d_model * 4, d_model)
66
+
67
+ def forward(self, x):
68
+ x = self.ff1(x)
69
+ x = nn.functional.gelu(x)
70
+ x = self.ff2(x)
71
+ return x
72
+
73
+ class LayerNorm(nn.Module):
74
+ def __init__(self, d_model):
75
+ super().__init__()
76
+ self.gamma = nn.Parameter(torch.ones(d_model))
77
+ self.beta = nn.Parameter(torch.zeros(d_model))
78
+
79
+ def forward(self, x):
80
+ mean = x.mean(dim=-1, keepdim=True)
81
+ diff = (x - mean)
82
+ variance = (diff * diff).mean(dim=-1, keepdim=True)
83
+ normalized = diff / torch.sqrt(variance + 1e-6)
84
+ return self.gamma * normalized + self.beta
85
+
86
+ class MultiHeadAttention(nn.Module):
87
+ def __init__(self, d_model, n_heads, dropout=0.1):
88
+ super().__init__()
89
+
90
+ assert d_model % n_heads == 0, "d_model must be divisible by n_heads"
91
+
92
+ self.n_heads = n_heads
93
+ self.head_dim = d_model // n_heads
94
+ self.scale = math.sqrt(self.head_dim)
95
+
96
+ self.query = nn.Linear(d_model, d_model)
97
+ self.key = nn.Linear(d_model, d_model)
98
+ self.value = nn.Linear(d_model, d_model)
99
+
100
+ self.head_proj = nn.Linear(d_model, d_model)
101
+
102
+ def forward(self, x):
103
+ query = self.split_heads(self.query(x))
104
+ key = self.split_heads(self.key(x))
105
+ value = self.split_heads(self.value(x))
106
+
107
+ scores = torch.matmul(query, key.transpose(-2, -1))
108
+ scores = scores / self.scale
109
+
110
+ context_size = query.shape[2]
111
+
112
+ mask = torch.tril(
113
+ torch.ones(context_size, context_size, device=query.device)
114
+ )
115
+ mask = mask.view(1, 1, context_size, context_size)
116
+
117
+ scores = scores.masked_fill(mask == 0, float("-inf"))
118
+
119
+ weights = torch.nn.functional.softmax(scores, dim=-1)
120
+
121
+ attended = torch.matmul(weights, value)
122
+
123
+ attended = self.combine_heads(attended)
124
+
125
+ attended = self.head_proj(attended)
126
+
127
+ return attended
128
+
129
+ def split_heads(self, x):
130
+ batch_size, seq_len, d_model = x.shape
131
+
132
+ x = x.reshape(batch_size, seq_len, self.n_heads, self.head_dim)
133
+ x = x.transpose(1, 2)
134
+
135
+ return x
136
+
137
+ def combine_heads(self, x):
138
+ batch_size, n_heads, seq_len, head_dim = x.shape
139
+
140
+ x = x.transpose(1, 2)
141
+ x = x.contiguous().view(batch_size, seq_len, n_heads * head_dim)
142
+
143
+ return x
144
+
145
+ class TransformerBlock(nn.Module):
146
+ def __init__(self, d_model, n_heads, dropout=0.1):
147
+ super().__init__()
148
+
149
+ self.feed_forward = FeedForward(d_model)
150
+ self.layer_norm1 = LayerNorm(d_model)
151
+ self.layer_norm2 = LayerNorm(d_model)
152
+ self.dropout = nn.Dropout(dropout)
153
+ self.multi_head_attention = MultiHeadAttention(d_model, n_heads, dropout)
154
+
155
+ def forward(self, x):
156
+ attention = self.multi_head_attention(self.layer_norm1(x))
157
+ attention = self.dropout(attention)
158
+
159
+ x = x + attention
160
+
161
+ feed_forward = self.feed_forward(self.layer_norm2(x))
162
+ feed_forward = self.dropout(feed_forward)
163
+
164
+ x = x + feed_forward
165
+
166
+ return x
model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:9e9f8e95c7fa765cda29765de8ccd1c89d0f08545d42c72943b9c546665db657
3
+ size 39973360
modeling_tiny_gpt.py ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch.nn.functional as F
2
+ from transformers import PreTrainedModel
3
+ from transformers.generation import GenerationMixin
4
+ from transformers.modeling_outputs import CausalLMOutput
5
+
6
+ from .configuration_tiny_gpt import TinyGPTConfig
7
+ from .model import Model
8
+
9
+
10
+ class TinyGPTForCausalLM(PreTrainedModel, GenerationMixin):
11
+ config_class = TinyGPTConfig
12
+ main_input_name = "input_ids"
13
+
14
+ def __init__(self, config):
15
+ super().__init__(config)
16
+ self.core_model = Model(
17
+ context_size=config.context_size,
18
+ vocab_size=config.vocab_size,
19
+ d_model=config.d_model,
20
+ n_layers=config.n_layers,
21
+ n_heads=config.n_heads,
22
+ dropout=config.dropout,
23
+ )
24
+ self.post_init()
25
+
26
+ def get_input_embeddings(self):
27
+ return self.core_model.token_embedding
28
+
29
+ def set_input_embeddings(self, value):
30
+ self.core_model.token_embedding = value
31
+
32
+ def get_output_embeddings(self):
33
+ return self.core_model.linear
34
+
35
+ def set_output_embeddings(self, new_embeddings):
36
+ self.core_model.linear = new_embeddings
37
+
38
+ def forward(self, input_ids=None, labels=None, **kwargs):
39
+ if input_ids is None:
40
+ raise ValueError("input_ids must be provided")
41
+
42
+ logits = self.core_model(input_ids)
43
+ loss = None
44
+ if labels is not None:
45
+ shift_logits = logits[..., :-1, :].contiguous()
46
+ shift_labels = labels[..., 1:].contiguous()
47
+ loss = F.cross_entropy(
48
+ shift_logits.view(-1, shift_logits.size(-1)),
49
+ shift_labels.view(-1),
50
+ )
51
+
52
+ return CausalLMOutput(loss=loss, logits=logits)
tokenizer.json ADDED
The diff for this file is too large to render. See raw diff
 
tokenizer_config.json ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ {
2
+ "backend": "tokenizers",
3
+ "eos_token": "<EOS>",
4
+ "model_max_length": 512,
5
+ "pad_token": "<EOS>",
6
+ "tokenizer_class": "TokenizersBackend"
7
+ }