AZERDSQ commited on
Commit
750c97d
·
verified ·
1 Parent(s): 65e88a1

Upload folder using huggingface_hub

Browse files
README.md ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: apache-2.0
3
+ language:
4
+ - en
5
+ tags:
6
+ - gpt
7
+ - from-scratch
8
+ - edge-training
9
+ - instruct
10
+ pipeline_tag: text-generation
11
+ ---
12
+
13
+ # G1-nano-instruct
14
+
15
+ A 60M-parameter GPT trained and instruction-tuned entirely on a single 8GB-RAM NVIDIA Jetson device — no cloud infrastructure, no multi-GPU setup, pretraining and fine-tuning both. Instruction-tuned checkpoint — see [`G1-nano-base`](https://huggingface.co/AZERDSQ/G1-nano-base) for the raw pretrained version.
16
+
17
+ `G1-nano` is not a bigger or "smarter" successor to [`G0-nano`](https://huggingface.co/AZERDSQ/G0-nano-instruct) — same parameter count and compute budget, same 8GB constraint. What changed: **2x native context (2048 vs 1024 tokens)** and, unlike `G0-nano-instruct` (single-turn, Alpaca-only SFT), **real multi-turn conversation support** — the fine-tuning mix adds UltraChat and OpenAssistant on top of Alpaca, with a packed/masked training scheme so multiple conversations per training row never leak into each other. On short single-turn benchmarks the two models are within noise of each other (see below); the point of `G1` isn't to win that scoreboard, it's to hold a longer, multi-turn conversation at the same size and cost.
18
+
19
+ ### Architecture
20
+
21
+ Llama-style decoder-only transformer:
22
+ - **60.0M parameters** (shared embeddings with LM head)
23
+ - **14 layers**, hidden size 576
24
+ - **Grouped-Query Attention**: 9 query heads / **1** key-value head, head_dim 64
25
+ - **RoPE position encoding** (θ=10000)
26
+ - **SwiGLU feed-forward** (intermediate size 1664)
27
+ - **RMSNorm** normalization
28
+ - **2048-token context**, used natively during both pretraining and fine-tuning (`G0-nano` fine-tuned at 512)
29
+ - **16,388-token vocabulary** (16,384 base + 4 chat tokens `<|user|>`/`<|assistant|>`/`<|end|>`/`<|system|>`)
30
+
31
+ Pretrained on ~1.5B tokens of English web/book text, then fine-tuned on ~39K conversations (UltraChat 200k, OpenAssistant/oasst1, cleaned Alpaca), packed to 2048 tokens/row with a block-diagonal attention mask so packed examples don't see each other.
32
+
33
+ ### Usage
34
+
35
+ ```python
36
+ from transformers import AutoModelForCausalLM, AutoTokenizer
37
+
38
+ model = AutoModelForCausalLM.from_pretrained("AZERDSQ/G1-nano-instruct", trust_remote_code=True)
39
+ tokenizer = AutoTokenizer.from_pretrained("AZERDSQ/G1-nano-instruct", trust_remote_code=True)
40
+
41
+ prompt = "<|user|>What is the capital of France?<|end|><|assistant|>"
42
+ inputs = tokenizer(prompt, return_tensors="pt")
43
+ out = model.generate(**inputs, max_new_tokens=60, do_sample=False)
44
+ print(tokenizer.decode(out[0]))
45
+ ```
46
+
47
+ Multi-turn: chain `<|user|>...<|end|><|assistant|>...<|end|>` turns before the final open `<|user|>...<|end|><|assistant|>`.
48
+
49
+ Also available on [Ollama](https://ollama.com/azerdsq/g1-nano-instruct) — the chat template is baked in, just type your question.
50
+
51
+ ### Benchmarks
52
+
53
+ Zero-shot evaluation, full test sets, `lm-evaluation-harness`, compared against `G0-nano-instruct` (same params/compute budget, half the context, single-turn-only fine-tuning):
54
+
55
+ | Benchmark | G1-nano-instruct | G0-nano-instruct |
56
+ |---|---|---|
57
+ | LAMBADA (OpenAI) | **23.09%** | 18.34% |
58
+ | PIQA | **60.34%** | 59.09% |
59
+ | WinoGrande | **52.57%** | 51.70% |
60
+ | ARC-Easy | 42.13% | **43.31%** |
61
+ | ARC-Challenge | 20.48% | 20.99% |
62
+ | SciQ | 63.80% | **66.80%** |
63
+ | **Average** | **43.74%** | 43.37% |
64
+
65
+ `G1` edges ahead on average (+0.37pt, within noise), winning LAMBADA/PIQA/WinoGrande while `G0` keeps ARC-Easy/SciQ. Not a clean sweep either way — and these are all short, single-turn tasks, so they don't measure the two things `G1-nano-instruct` actually adds: 2x context and multi-turn coherence.
66
+
67
+ ### Limitations
68
+
69
+ - **Knowledge constraints**: 60M parameters caps factual retention hard — expect confident, fluent, frequently wrong answers on anything knowledge-dense (~2 bits/parameter, [Allen-Zhu & Li, ICLR'25](https://arxiv.org/abs/2404.05405)).
70
+ - **2048-token context.** Longer than `G0-nano-instruct`, still short by modern standards.
71
+ - **English only.**
72
+ - **Single-sequence generation only** (no padded batched inference).
73
+
74
+ ### License
75
+
76
+ Apache 2.0. Weights only — training code and data pipelines are excluded.
__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": 16388,
7
+ "hidden_size": 576,
8
+ "num_hidden_layers": 14,
9
+ "num_attention_heads": 9,
10
+ "num_key_value_heads": 1,
11
+ "head_dim": 64,
12
+ "intermediate_size": 1664,
13
+ "max_position_embeddings": 2048,
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,8 @@
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "bos_token_id": 2,
3
+ "eos_token_id": [
4
+ 3,
5
+ 16386
6
+ ],
7
+ "pad_token_id": 0
8
+ }
model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e1cd1f7a9186f6e8e9594c1e5edb4ebce020d779a463c88267a28266ca544e8e
3
+ size 240146776
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,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ "<|user|>",
15
+ "<|assistant|>",
16
+ "<|end|>",
17
+ "<|system|>"
18
+ ],
19
+ "clean_up_tokenization_spaces": false
20
+ }
Free AI Image Generator No sign-up. Instant results. Open Now