AZERDSQ commited on
Commit
5da8f28
·
verified ·
1 Parent(s): 1f436ec

Upload folder using huggingface_hub

Browse files
README.md ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: apache-2.0
3
+ language:
4
+ - en
5
+ pipeline_tag: text-generation
6
+ tags:
7
+ - pytorch
8
+ - causal-lm
9
+ - from-scratch
10
+ - custom_code
11
+ - gqa
12
+ - rope
13
+ ---
14
+
15
+ # G0-nano-base
16
+
17
+ A 62M-parameter GPT trained **completely from scratch on a single 8GB-RAM device** (an NVIDIA Jetson) — no cloud cluster, no multi-GPU node. This is the base (pretrained, non-instruct) checkpoint: it completes text, it does not follow instructions. See [`G0-nano-instruct`](https://huggingface.co/AZERDSQ/G0-nano-instruct) for the chat-tuned version.
18
+
19
+ The point of this model isn't to compete with billion-parameter LLMs — a 62M model is physically capped at roughly 15.5MB of factual knowledge (~2 bits/parameter, [Allen-Zhu & Li, ICLR'25](https://arxiv.org/abs/2404.05405)), regardless of training method. The point is that it was trained end-to-end, pretraining included, within an 8GB memory budget.
20
+
21
+ ## Architecture
22
+
23
+ Llama-style decoder-only transformer:
24
+
25
+ | | |
26
+ |---|---|
27
+ | Parameters | 62.1M (embeddings shared with LM head) |
28
+ | Layers | 12 |
29
+ | Hidden size | 640 |
30
+ | Attention | Grouped-Query Attention, 10 query heads / 2 KV heads, head_dim 64 |
31
+ | Position encoding | RoPE (θ=10000) |
32
+ | Feed-forward | SwiGLU, hidden 1728 |
33
+ | Normalization | RMSNorm |
34
+ | Context length | 1024 tokens |
35
+ | Vocabulary | 16,384 (SentencePiece, trained from scratch) |
36
+ | Precision | fp32 |
37
+
38
+ Trained on ~1.5B tokens of English web/book text (FineWeb-Edu, BookCorpus, OpenWebText, PG-19, WikiHow).
39
+
40
+ ## Usage
41
+
42
+ ```python
43
+ from transformers import AutoModelForCausalLM, AutoTokenizer
44
+
45
+ tok = AutoTokenizer.from_pretrained("AZERDSQ/G0-nano-base", trust_remote_code=True)
46
+ model = AutoModelForCausalLM.from_pretrained("AZERDSQ/G0-nano-base", trust_remote_code=True)
47
+
48
+ inputs = tok("The city of Paris is", return_tensors="pt")
49
+ out = model.generate(**inputs, max_new_tokens=50, do_sample=True, top_k=50, temperature=0.8)
50
+ print(tok.decode(out[0]))
51
+ ```
52
+
53
+ `trust_remote_code=True` is required — this is a custom architecture (GQA + RoPE + SwiGLU), not one of the built-in `transformers` model types.
54
+
55
+ ## Limitations
56
+
57
+ - 62M parameters caps factual knowledge hard — expect confident, fluent, frequently wrong completions on anything knowledge-dense.
58
+ - 1024-token context.
59
+ - English only.
60
+ - The custom tokenizer/model code does not handle padded batched inference — single-sequence generation only.
61
+ - Base model: raw next-token prediction, no chat formatting. It will continue text, not answer questions.
62
+
63
+ ## License
64
+
65
+ Apache 2.0. Weights only — this release does not include training code or data pipelines.
__init__.py ADDED
File without changes
config.json ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "model_type": "nanogpt",
3
+ "architectures": [
4
+ "NanoGPTForCausalLM"
5
+ ],
6
+ "vocab_size": 16384,
7
+ "hidden_size": 640,
8
+ "num_hidden_layers": 12,
9
+ "num_attention_heads": 10,
10
+ "num_key_value_heads": 2,
11
+ "head_dim": 64,
12
+ "intermediate_size": 1728,
13
+ "max_position_embeddings": 1024,
14
+ "rope_theta": 10000.0,
15
+ "rms_norm_eps": 1e-05,
16
+ "tie_word_embeddings": true,
17
+ "torch_dtype": "float32",
18
+ "auto_map": {
19
+ "AutoConfig": "configuration_nanogpt.NanoGPTConfig",
20
+ "AutoModelForCausalLM": "modeling_nanogpt.NanoGPTForCausalLM"
21
+ }
22
+ }
configuration_nanogpt.py ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from transformers import PretrainedConfig
2
+
3
+
4
+ class NanoGPTConfig(PretrainedConfig):
5
+ model_type = "nanogpt"
6
+
7
+ def __init__(
8
+ self,
9
+ vocab_size=16384,
10
+ hidden_size=640,
11
+ num_hidden_layers=12,
12
+ num_attention_heads=10,
13
+ num_key_value_heads=2,
14
+ head_dim=64,
15
+ intermediate_size=1728,
16
+ max_position_embeddings=1024,
17
+ rope_theta=10000.0,
18
+ rms_norm_eps=1e-5,
19
+ tie_word_embeddings=True,
20
+ **kwargs,
21
+ ):
22
+ self.vocab_size = vocab_size
23
+ self.hidden_size = hidden_size
24
+ self.num_hidden_layers = num_hidden_layers
25
+ self.num_attention_heads = num_attention_heads
26
+ self.num_key_value_heads = num_key_value_heads
27
+ self.head_dim = head_dim
28
+ self.intermediate_size = intermediate_size
29
+ self.max_position_embeddings = max_position_embeddings
30
+ self.rope_theta = rope_theta
31
+ self.rms_norm_eps = rms_norm_eps
32
+ super().__init__(tie_word_embeddings=tie_word_embeddings, **kwargs)
generation_config.json ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ {
2
+ "bos_token_id": 2,
3
+ "eos_token_id": [
4
+ 3
5
+ ],
6
+ "pad_token_id": 0
7
+ }
model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:ac6db6b91d6d0106a6e762fd1037f07c7db579502f7efdad604913fd7e139457
3
+ size 248456088
modeling_nanogpt.py ADDED
@@ -0,0 +1,204 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import torch.nn as nn
3
+ import torch.nn.functional as F
4
+ from transformers import PreTrainedModel
5
+ from transformers.cache_utils import Cache, DynamicCache
6
+ from transformers.modeling_outputs import CausalLMOutputWithPast
7
+
8
+ from .configuration_nanogpt import NanoGPTConfig
9
+
10
+
11
+ class RMSNorm(nn.Module):
12
+ def __init__(self, dim: int, eps: float):
13
+ super().__init__()
14
+ self.eps = eps
15
+ self.weight = nn.Parameter(torch.ones(dim))
16
+
17
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
18
+ norm = x * torch.rsqrt(x.pow(2).mean(-1, keepdim=True) + self.eps)
19
+ return norm * self.weight
20
+
21
+
22
+ def build_rope_cache(seq_len: int, head_dim: int, theta: float, device, dtype):
23
+ inv_freq = 1.0 / (theta ** (torch.arange(0, head_dim, 2, device=device).float() / head_dim))
24
+ t = torch.arange(seq_len, device=device).float()
25
+ freqs = torch.outer(t, inv_freq)
26
+ return freqs.cos().to(dtype), freqs.sin().to(dtype)
27
+
28
+
29
+ def apply_rope(x: torch.Tensor, cos: torch.Tensor, sin: torch.Tensor) -> torch.Tensor:
30
+ x1, x2 = x[..., ::2], x[..., 1::2]
31
+ cos = cos[None, None, :x.size(2), :]
32
+ sin = sin[None, None, :x.size(2), :]
33
+ rotated1 = x1 * cos - x2 * sin
34
+ rotated2 = x1 * sin + x2 * cos
35
+ return torch.stack([rotated1, rotated2], dim=-1).flatten(-2).to(x.dtype)
36
+
37
+
38
+ class GQAAttention(nn.Module):
39
+ def __init__(self, config: NanoGPTConfig):
40
+ super().__init__()
41
+ self.n_heads = config.num_attention_heads
42
+ self.n_kv_heads = config.num_key_value_heads
43
+ self.head_dim = config.head_dim
44
+ self.n_rep = self.n_heads // self.n_kv_heads
45
+
46
+ self.wq = nn.Linear(config.hidden_size, self.n_heads * self.head_dim, bias=False)
47
+ self.wk = nn.Linear(config.hidden_size, self.n_kv_heads * self.head_dim, bias=False)
48
+ self.wv = nn.Linear(config.hidden_size, self.n_kv_heads * self.head_dim, bias=False)
49
+ self.wo = nn.Linear(self.n_heads * self.head_dim, config.hidden_size, bias=False)
50
+
51
+ def forward(self, x, cos, sin, past_key_values=None, layer_idx=None, use_cache=False):
52
+ B, T, _ = x.shape
53
+ q = self.wq(x).view(B, T, self.n_heads, self.head_dim).transpose(1, 2)
54
+ k = self.wk(x).view(B, T, self.n_kv_heads, self.head_dim).transpose(1, 2)
55
+ v = self.wv(x).view(B, T, self.n_kv_heads, self.head_dim).transpose(1, 2)
56
+
57
+ past_len = 0 if past_key_values is None else past_key_values.get_seq_length(layer_idx)
58
+ q = apply_rope(q, cos[past_len:past_len + T], sin[past_len:past_len + T])
59
+ k = apply_rope(k, cos[past_len:past_len + T], sin[past_len:past_len + T])
60
+
61
+ if past_key_values is not None:
62
+ k, v = past_key_values.update(k, v, layer_idx)
63
+
64
+ k = k.repeat_interleave(self.n_rep, dim=1)
65
+ v = v.repeat_interleave(self.n_rep, dim=1)
66
+
67
+ if past_len == 0 and T > 1:
68
+ out = F.scaled_dot_product_attention(q, k, v, is_causal=True)
69
+ else:
70
+ key_len = k.size(2)
71
+ query_positions = torch.arange(T, device=x.device) + past_len
72
+ key_positions = torch.arange(key_len, device=x.device)
73
+ attn_mask = key_positions.unsqueeze(0) <= query_positions.unsqueeze(1)
74
+ out = F.scaled_dot_product_attention(q, k, v, attn_mask=attn_mask)
75
+
76
+ out = out.transpose(1, 2).contiguous().view(B, T, self.n_heads * self.head_dim)
77
+ return self.wo(out)
78
+
79
+
80
+ class SwiGLU(nn.Module):
81
+ def __init__(self, config: NanoGPTConfig):
82
+ super().__init__()
83
+ self.w_gate = nn.Linear(config.hidden_size, config.intermediate_size, bias=False)
84
+ self.w_up = nn.Linear(config.hidden_size, config.intermediate_size, bias=False)
85
+ self.w_down = nn.Linear(config.intermediate_size, config.hidden_size, bias=False)
86
+
87
+ def forward(self, x):
88
+ return self.w_down(F.silu(self.w_gate(x)) * self.w_up(x))
89
+
90
+
91
+ class Block(nn.Module):
92
+ def __init__(self, config: NanoGPTConfig):
93
+ super().__init__()
94
+ self.attn_norm = RMSNorm(config.hidden_size, config.rms_norm_eps)
95
+ self.attn = GQAAttention(config)
96
+ self.mlp_norm = RMSNorm(config.hidden_size, config.rms_norm_eps)
97
+ self.mlp = SwiGLU(config)
98
+
99
+ def forward(self, x, cos, sin, past_key_values=None, layer_idx=None, use_cache=False):
100
+ x = x + self.attn(self.attn_norm(x), cos, sin, past_key_values, layer_idx, use_cache)
101
+ x = x + self.mlp(self.mlp_norm(x))
102
+ return x
103
+
104
+
105
+ class NanoGPTPreTrainedModel(PreTrainedModel):
106
+ config_class = NanoGPTConfig
107
+ base_model_prefix = "nanogpt"
108
+ supports_gradient_checkpointing = False
109
+
110
+ def _init_weights(self, module):
111
+ if isinstance(module, nn.Linear):
112
+ nn.init.normal_(module.weight, mean=0.0, std=0.02)
113
+ elif isinstance(module, nn.Embedding):
114
+ nn.init.normal_(module.weight, mean=0.0, std=0.02)
115
+
116
+
117
+ class NanoGPTForCausalLM(NanoGPTPreTrainedModel):
118
+ _tied_weights_keys = {"lm_head.weight": "tok_emb.weight"}
119
+
120
+ def __init__(self, config: NanoGPTConfig):
121
+ super().__init__(config)
122
+ self.tok_emb = nn.Embedding(config.vocab_size, config.hidden_size)
123
+ self.blocks = nn.ModuleList([Block(config) for _ in range(config.num_hidden_layers)])
124
+ self.final_norm = RMSNorm(config.hidden_size, config.rms_norm_eps)
125
+ self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False)
126
+
127
+ self._rope_cache = {}
128
+ self.post_init()
129
+
130
+ def get_input_embeddings(self):
131
+ return self.tok_emb
132
+
133
+ def set_input_embeddings(self, value):
134
+ self.tok_emb = value
135
+
136
+ def get_output_embeddings(self):
137
+ return self.lm_head
138
+
139
+ def set_output_embeddings(self, value):
140
+ self.lm_head = value
141
+
142
+ def _get_rope(self, seq_len, device, dtype):
143
+ key = (seq_len, device, dtype)
144
+ if key not in self._rope_cache:
145
+ self._rope_cache[key] = build_rope_cache(
146
+ seq_len, self.config.head_dim, self.config.rope_theta, device, dtype
147
+ )
148
+ return self._rope_cache[key]
149
+
150
+ def forward(
151
+ self,
152
+ input_ids=None,
153
+ attention_mask=None,
154
+ past_key_values=None,
155
+ labels=None,
156
+ use_cache=None,
157
+ output_attentions=None,
158
+ output_hidden_states=None,
159
+ return_dict=None,
160
+ **kwargs,
161
+ ):
162
+ use_cache = bool(use_cache) if use_cache is not None else False
163
+ B, T = input_ids.shape
164
+
165
+ if use_cache and past_key_values is None:
166
+ past_key_values = DynamicCache(config=self.config)
167
+ past_len = past_key_values.get_seq_length() if past_key_values is not None else 0
168
+ assert past_len + T <= self.config.max_position_embeddings, (
169
+ f"sequence length {past_len + T} > max_position_embeddings "
170
+ f"{self.config.max_position_embeddings}"
171
+ )
172
+
173
+ x = self.tok_emb(input_ids)
174
+ cos, sin = self._get_rope(past_len + T, input_ids.device, x.dtype)
175
+
176
+ for i, block in enumerate(self.blocks):
177
+ x = block(x, cos, sin, past_key_values, i, use_cache)
178
+ x = self.final_norm(x)
179
+ logits = self.lm_head(x)
180
+
181
+ loss = None
182
+ if labels is not None:
183
+ loss = F.cross_entropy(
184
+ logits[:, :-1, :].reshape(-1, logits.size(-1)),
185
+ labels[:, 1:].reshape(-1),
186
+ ignore_index=-100,
187
+ )
188
+
189
+ return CausalLMOutputWithPast(
190
+ loss=loss,
191
+ logits=logits,
192
+ past_key_values=past_key_values if use_cache else None,
193
+ )
194
+
195
+ def prepare_inputs_for_generation(
196
+ self, input_ids, past_key_values=None, attention_mask=None, use_cache=True, **kwargs
197
+ ):
198
+ if past_key_values is not None and past_key_values.get_seq_length() > 0:
199
+ input_ids = input_ids[:, -1:]
200
+ return {
201
+ "input_ids": input_ids,
202
+ "past_key_values": past_key_values,
203
+ "use_cache": use_cache,
204
+ }
spm_16384.model ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:a76c144aef571d3db4f18b6c57fd91adee39f85fdad722fb3c893b3a827a81c5
3
+ size 513334
tokenization_nanogpt.py ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import shutil
3
+
4
+ import sentencepiece as spm
5
+ from transformers import PreTrainedTokenizer
6
+
7
+ VOCAB_FILES_NAMES = {"vocab_file": "spm_16384.model"}
8
+
9
+
10
+ class NanoGPTTokenizer(PreTrainedTokenizer):
11
+ """Wrapper SentencePiece + tokens de chat additionnels (>= 16384).
12
+
13
+ Les tokens de chat (``<|user|>``, ``<|assistant|>``, ``<|end|>``,
14
+ ``<|system|>``) sont geres par le mecanisme "added tokens" standard de
15
+ ``transformers`` (passe via ``additional_special_tokens``) plutot que par
16
+ une logique maison : HF les decoupe avant tokenization et les reinsere
17
+ correctement au decode, et leur assigne des ids sequentiels a partir de
18
+ ``len(self)`` au moment du ``__init__`` -- ce qui reproduit exactement le
19
+ mapping fige dans ``training/chat_format.py`` (16384..16387), du moment
20
+ que la liste est fournie dans le meme ordre.
21
+ """
22
+
23
+ vocab_files_names = VOCAB_FILES_NAMES
24
+ model_input_names = ["input_ids", "attention_mask"]
25
+
26
+ def __init__(
27
+ self,
28
+ vocab_file,
29
+ bos_token="<s>",
30
+ eos_token="</s>",
31
+ unk_token="<unk>",
32
+ pad_token="<pad>",
33
+ additional_special_tokens=None,
34
+ **kwargs,
35
+ ):
36
+ self.vocab_file = vocab_file
37
+ self.sp_model = spm.SentencePieceProcessor()
38
+ self.sp_model.Load(vocab_file)
39
+
40
+ super().__init__(
41
+ bos_token=bos_token,
42
+ eos_token=eos_token,
43
+ unk_token=unk_token,
44
+ pad_token=pad_token,
45
+ additional_special_tokens=additional_special_tokens or [],
46
+ **kwargs,
47
+ )
48
+
49
+ @property
50
+ def vocab_size(self):
51
+ return self.sp_model.get_piece_size()
52
+
53
+ def get_vocab(self):
54
+ vocab = {self.sp_model.id_to_piece(i): i for i in range(self.vocab_size)}
55
+ vocab.update(self.added_tokens_encoder)
56
+ return vocab
57
+
58
+ def _tokenize(self, text, **kwargs):
59
+ return self.sp_model.encode(text, out_type=str)
60
+
61
+ def _convert_token_to_id(self, token):
62
+ return self.sp_model.piece_to_id(token)
63
+
64
+ def _convert_id_to_token(self, index):
65
+ return self.sp_model.id_to_piece(index)
66
+
67
+ def convert_tokens_to_string(self, tokens):
68
+ return self.sp_model.decode(tokens) if tokens else ""
69
+
70
+ def build_inputs_with_special_tokens(self, token_ids_0, token_ids_1=None):
71
+ return [self.bos_token_id] + token_ids_0
72
+
73
+ def save_vocabulary(self, save_directory, filename_prefix=None):
74
+ out_name = (filename_prefix + "-" if filename_prefix else "") + VOCAB_FILES_NAMES["vocab_file"]
75
+ out_path = os.path.join(save_directory, out_name)
76
+ if os.path.abspath(self.vocab_file) != os.path.abspath(out_path):
77
+ shutil.copyfile(self.vocab_file, out_path)
78
+ return (out_path,)
tokenizer_config.json ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "tokenizer_class": "NanoGPTTokenizer",
3
+ "auto_map": {
4
+ "AutoTokenizer": [
5
+ "tokenization_nanogpt.NanoGPTTokenizer",
6
+ null
7
+ ]
8
+ },
9
+ "bos_token": "<s>",
10
+ "eos_token": "</s>",
11
+ "unk_token": "<unk>",
12
+ "pad_token": "<pad>",
13
+ "additional_special_tokens": [],
14
+ "clean_up_tokenization_spaces": false
15
+ }