Ravi5528 commited on
Commit
6af0329
·
verified ·
1 Parent(s): 2cc3103

Upload folder using huggingface_hub

Browse files
README.md ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ language:
3
+ - code
4
+ license: apache-2.0
5
+ library_name: transformers
6
+ tags:
7
+ - code
8
+ - python
9
+ - javascript
10
+ - cpp
11
+ - sql
12
+ - html
13
+ - code-generation
14
+ - codebharat
15
+ - llama
16
+ - PyTorch
17
+ - byte-level-bpe
18
+ pipeline_tag: text-generation
19
+ widget:
20
+ - text: "def quicksort(arr):"
21
+ example_title: "Python QuickSort"
22
+ - text: "function debounce(func, wait) {"
23
+ example_title: "JavaScript Debounce"
24
+ - text: "int binarySearch(const std::vector<int>& arr, int target) {"
25
+ example_title: "C++ Binary Search"
26
+ ---
27
+
28
+ # CodeBharat-100M
29
+
30
+ **CodeBharat-100M** is a 100.68M parameter decoder-only Transformer pretrained from scratch on code (Python, JavaScript, TypeScript, C++, SQL, HTML/CSS, and synthetic textbooks).
31
+
32
+ ## Model Details
33
+ - **Architecture:** Decoder-Only Transformer (Llama/Qwen-style: RMSNorm, RoPE, SwiGLU, Grouped-Query Attention)
34
+ - **Parameters:** 100,679,424 (100.68M)
35
+ - **Vocabulary:** 49,152 tokens (Byte-level BPE)
36
+ - **Context Window:** 1,024 tokens
37
+ - **Training Device:** NVIDIA GeForce RTX 5050 GPU
38
+ - **Final Validation Loss:** 1.3891
39
+
40
+ ## Quickstart Usage
41
+
42
+ ### Native PyTorch / Tokenizers Usage
43
+
44
+ ```python
45
+ import torch
46
+ from tokenizers import Tokenizer
47
+ from pathlib import Path
48
+
49
+ # Load tokenizer and model weights
50
+ tokenizer = Tokenizer.from_file("tokenizer.json")
51
+ weights = torch.load("pytorch_model.bin", map_location="cuda" if torch.cuda.is_available() else "cpu")
52
+ ```
53
+
54
+ ### Run Inference via CLI
55
+ ```bash
56
+ python 100m-codebharat/scripts/11_generate.py --prompt "def binary_search(arr, target):"
57
+ ```
58
+
59
+ ## Model Card Info
60
+ - **Developed by:** CodeBharat Team
61
+ - **Model Type:** Causal Language Model for Code
62
+ - **License:** Apache 2.0
config.json ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "architectures": [
3
+ "CodeBharatForCausalLM"
4
+ ],
5
+ "model_type": "llama",
6
+ "vocab_size": 49152,
7
+ "hidden_size": 768,
8
+ "num_hidden_layers": 10,
9
+ "num_attention_heads": 12,
10
+ "num_key_value_heads": 4,
11
+ "intermediate_size": 2048,
12
+ "max_position_embeddings": 1024,
13
+ "rms_norm_eps": 1e-06,
14
+ "rope_theta": 10000.0,
15
+ "tie_word_embeddings": true,
16
+ "torch_dtype": "bfloat16",
17
+ "transformers_version": "4.40.0"
18
+ }
model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:721236c44044aad7a9592fe7a50507e8cb236d4bda300abb1c16cbf18ed6424d
3
+ size 402727248
modeling_codebharat.py ADDED
@@ -0,0 +1,570 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """CodeBharat-100M decoder-only Transformer.
2
+
3
+ The default configuration is deliberately matched to the prepared 100M
4
+ dataset:
5
+
6
+ * 49,152-token CodeBharat byte-level BPE vocabulary
7
+ * 1,024-token training sequences
8
+ * 100,679,424 trainable parameters with tied input/output embeddings
9
+
10
+ Architecture choices follow the conservative Llama/Qwen-style decoder recipe:
11
+ pre-norm RMSNorm, RoPE, SwiGLU, and grouped-query attention (GQA). The
12
+ implementation keeps full causal attention over all 1,024 positions because
13
+ code benefits from global context within a packed sequence.
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ import argparse
19
+ import json
20
+ from dataclasses import asdict, dataclass
21
+ from pathlib import Path
22
+ from typing import Any
23
+
24
+ import torch
25
+ import torch.nn as nn
26
+ import torch.nn.functional as F
27
+
28
+
29
+ BASE_DIR = Path(__file__).resolve().parent.parent
30
+
31
+
32
+ @dataclass
33
+ class ModelConfig:
34
+ """Configuration for the default CodeBharat-100M model.
35
+
36
+ The defaults are a single intentional architecture, not loose suggestions.
37
+ They produce 100,679,424 trainable parameters when embeddings are tied.
38
+ """
39
+
40
+ vocab_size: int = 49_152
41
+ hidden_size: int = 768
42
+ num_layers: int = 10
43
+ num_attention_heads: int = 12
44
+ num_key_value_heads: int = 4
45
+ intermediate_size: int = 2_048
46
+ max_seq_len: int = 1_024
47
+ rope_theta: float = 10_000.0
48
+ rms_norm_eps: float = 1e-6
49
+ initializer_range: float = 0.02
50
+ attention_dropout: float = 0.0
51
+ tie_word_embeddings: bool = True
52
+
53
+ def __post_init__(self) -> None:
54
+ positive_fields = {
55
+ "vocab_size": self.vocab_size,
56
+ "hidden_size": self.hidden_size,
57
+ "num_layers": self.num_layers,
58
+ "num_attention_heads": self.num_attention_heads,
59
+ "num_key_value_heads": self.num_key_value_heads,
60
+ "intermediate_size": self.intermediate_size,
61
+ "max_seq_len": self.max_seq_len,
62
+ }
63
+ for name, value in positive_fields.items():
64
+ if value <= 0:
65
+ raise ValueError(f"{name} must be positive, got {value}")
66
+ if self.hidden_size % self.num_attention_heads != 0:
67
+ raise ValueError(
68
+ "hidden_size must be divisible by num_attention_heads "
69
+ f"({self.hidden_size} / {self.num_attention_heads})"
70
+ )
71
+ if self.num_attention_heads % self.num_key_value_heads != 0:
72
+ raise ValueError(
73
+ "num_attention_heads must be divisible by num_key_value_heads "
74
+ f"({self.num_attention_heads} / {self.num_key_value_heads})"
75
+ )
76
+ if self.head_dim % 2:
77
+ raise ValueError("head_dim must be even so RoPE can rotate pairs")
78
+ if self.max_seq_len <= 1:
79
+ raise ValueError("max_seq_len must be greater than one")
80
+ if self.rope_theta <= 1.0:
81
+ raise ValueError("rope_theta must be greater than one")
82
+ if self.rms_norm_eps <= 0.0:
83
+ raise ValueError("rms_norm_eps must be positive")
84
+ if self.initializer_range <= 0.0:
85
+ raise ValueError("initializer_range must be positive")
86
+ if not 0.0 <= self.attention_dropout < 1.0:
87
+ raise ValueError("attention_dropout must be in [0, 1)")
88
+
89
+ @property
90
+ def head_dim(self) -> int:
91
+ """The dimensionality of each attention head."""
92
+ return self.hidden_size // self.num_attention_heads
93
+
94
+ @property
95
+ def num_key_value_groups(self) -> int:
96
+ """Number of query heads that share each key/value head."""
97
+ return self.num_attention_heads // self.num_key_value_heads
98
+
99
+ @property
100
+ def estimated_parameter_count(self) -> int:
101
+ """Return the exact count implied by this no-bias architecture."""
102
+ embedding = self.vocab_size * self.hidden_size
103
+ key_value_dim = self.num_key_value_heads * self.head_dim
104
+ attention = self.hidden_size * (
105
+ self.hidden_size + 2 * key_value_dim + self.hidden_size
106
+ )
107
+ swiglu = 3 * self.hidden_size * self.intermediate_size
108
+ layer_norms = 2 * self.hidden_size
109
+ transformer = self.num_layers * (attention + swiglu + layer_norms)
110
+ final_norm = self.hidden_size
111
+ output_head = 0 if self.tie_word_embeddings else embedding
112
+ return embedding + transformer + final_norm + output_head
113
+
114
+ def to_dict(self) -> dict[str, Any]:
115
+ """Produce checkpoint-friendly, JSON-serializable configuration data."""
116
+ return asdict(self)
117
+
118
+
119
+ class RMSNorm(nn.Module):
120
+ """Root mean square normalization, computed safely in float32."""
121
+
122
+ def __init__(self, hidden_size: int, eps: float) -> None:
123
+ super().__init__()
124
+ self.weight = nn.Parameter(torch.ones(hidden_size))
125
+ self.eps = eps
126
+
127
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
128
+ input_dtype = hidden_states.dtype
129
+ hidden_states = hidden_states.float()
130
+ variance = hidden_states.pow(2).mean(dim=-1, keepdim=True)
131
+ normalized = hidden_states * torch.rsqrt(variance + self.eps)
132
+ return self.weight * normalized.to(input_dtype)
133
+
134
+
135
+ class RotaryEmbedding(nn.Module):
136
+ """Rotary position embeddings with interleaved real/imaginary pairs."""
137
+
138
+ def __init__(self, head_dim: int, theta: float) -> None:
139
+ super().__init__()
140
+ inv_freq = 1.0 / (
141
+ theta
142
+ ** (
143
+ torch.arange(0, head_dim, 2, dtype=torch.float32)
144
+ / head_dim
145
+ )
146
+ )
147
+ self.register_buffer("inv_freq", inv_freq, persistent=False)
148
+
149
+ def forward(
150
+ self,
151
+ position_ids: torch.Tensor,
152
+ dtype: torch.dtype,
153
+ ) -> tuple[torch.Tensor, torch.Tensor]:
154
+ """Return cos/sin values shaped as (batch, sequence, head_dim / 2)."""
155
+ positions = position_ids.to(dtype=torch.float32)
156
+ inv_freq = self.inv_freq.to(device=position_ids.device)
157
+ angles = positions.unsqueeze(-1) * inv_freq
158
+ return angles.cos().to(dtype=dtype), angles.sin().to(dtype=dtype)
159
+
160
+
161
+ def apply_rotary_embedding(
162
+ query_states: torch.Tensor,
163
+ key_states: torch.Tensor,
164
+ cos: torch.Tensor,
165
+ sin: torch.Tensor,
166
+ ) -> tuple[torch.Tensor, torch.Tensor]:
167
+ """Apply interleaved RoPE to Q and K tensors.
168
+
169
+ Query and key have shape (batch, heads, sequence, head_dim). Cosine and
170
+ sine have shape (batch, sequence, head_dim / 2).
171
+ """
172
+
173
+ cos = cos.unsqueeze(1)
174
+ sin = sin.unsqueeze(1)
175
+
176
+ def rotate(x: torch.Tensor) -> torch.Tensor:
177
+ x_even = x[..., ::2]
178
+ x_odd = x[..., 1::2]
179
+ rotated = torch.stack(
180
+ (x_even * cos - x_odd * sin, x_even * sin + x_odd * cos),
181
+ dim=-1,
182
+ )
183
+ return rotated.flatten(start_dim=-2)
184
+
185
+ return rotate(query_states), rotate(key_states)
186
+
187
+
188
+ class GroupedQueryAttention(nn.Module):
189
+ """Causal self-attention with GQA and PyTorch SDPA kernels."""
190
+
191
+ def __init__(self, config: ModelConfig) -> None:
192
+ super().__init__()
193
+ self.num_attention_heads = config.num_attention_heads
194
+ self.num_key_value_heads = config.num_key_value_heads
195
+ self.num_key_value_groups = config.num_key_value_groups
196
+ self.head_dim = config.head_dim
197
+ self.attention_dropout = config.attention_dropout
198
+
199
+ key_value_dim = self.num_key_value_heads * self.head_dim
200
+ self.q_proj = nn.Linear(
201
+ config.hidden_size,
202
+ config.hidden_size,
203
+ bias=False,
204
+ )
205
+ self.k_proj = nn.Linear(config.hidden_size, key_value_dim, bias=False)
206
+ self.v_proj = nn.Linear(config.hidden_size, key_value_dim, bias=False)
207
+ self.o_proj = nn.Linear(
208
+ config.hidden_size,
209
+ config.hidden_size,
210
+ bias=False,
211
+ )
212
+
213
+ def forward(
214
+ self,
215
+ hidden_states: torch.Tensor,
216
+ cos: torch.Tensor,
217
+ sin: torch.Tensor,
218
+ ) -> torch.Tensor:
219
+ batch_size, seq_len, _ = hidden_states.shape
220
+
221
+ query_states = self.q_proj(hidden_states).view(
222
+ batch_size,
223
+ seq_len,
224
+ self.num_attention_heads,
225
+ self.head_dim,
226
+ )
227
+ key_states = self.k_proj(hidden_states).view(
228
+ batch_size,
229
+ seq_len,
230
+ self.num_key_value_heads,
231
+ self.head_dim,
232
+ )
233
+ value_states = self.v_proj(hidden_states).view(
234
+ batch_size,
235
+ seq_len,
236
+ self.num_key_value_heads,
237
+ self.head_dim,
238
+ )
239
+
240
+ query_states = query_states.transpose(1, 2)
241
+ key_states = key_states.transpose(1, 2)
242
+ value_states = value_states.transpose(1, 2)
243
+ query_states, key_states = apply_rotary_embedding(
244
+ query_states,
245
+ key_states,
246
+ cos,
247
+ sin,
248
+ )
249
+
250
+ # Manual expansion is portable across CPU, CUDA, and Apple MPS. It
251
+ # avoids relying on device-specific SDPA GQA support.
252
+ if self.num_key_value_groups > 1:
253
+ key_states = key_states.repeat_interleave(
254
+ self.num_key_value_groups,
255
+ dim=1,
256
+ )
257
+ value_states = value_states.repeat_interleave(
258
+ self.num_key_value_groups,
259
+ dim=1,
260
+ )
261
+
262
+ dropout_p = self.attention_dropout if self.training else 0.0
263
+ attn_output = F.scaled_dot_product_attention(
264
+ query_states,
265
+ key_states,
266
+ value_states,
267
+ attn_mask=None,
268
+ dropout_p=dropout_p,
269
+ is_causal=True,
270
+ )
271
+ attn_output = attn_output.transpose(1, 2).contiguous().view(
272
+ batch_size,
273
+ seq_len,
274
+ -1,
275
+ )
276
+ return self.o_proj(attn_output)
277
+
278
+
279
+ class SwiGLUMLP(nn.Module):
280
+ """Bias-free SwiGLU feed-forward network."""
281
+
282
+ def __init__(self, config: ModelConfig) -> None:
283
+ super().__init__()
284
+ self.gate_proj = nn.Linear(
285
+ config.hidden_size,
286
+ config.intermediate_size,
287
+ bias=False,
288
+ )
289
+ self.up_proj = nn.Linear(
290
+ config.hidden_size,
291
+ config.intermediate_size,
292
+ bias=False,
293
+ )
294
+ self.down_proj = nn.Linear(
295
+ config.intermediate_size,
296
+ config.hidden_size,
297
+ bias=False,
298
+ )
299
+
300
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
301
+ gate = F.silu(self.gate_proj(hidden_states))
302
+ return self.down_proj(gate * self.up_proj(hidden_states))
303
+
304
+
305
+ class DecoderLayer(nn.Module):
306
+ """Pre-norm decoder block: attention residual, then SwiGLU residual."""
307
+
308
+ def __init__(self, config: ModelConfig) -> None:
309
+ super().__init__()
310
+ self.input_layernorm = RMSNorm(
311
+ config.hidden_size,
312
+ config.rms_norm_eps,
313
+ )
314
+ self.self_attn = GroupedQueryAttention(config)
315
+ self.post_attention_layernorm = RMSNorm(
316
+ config.hidden_size,
317
+ config.rms_norm_eps,
318
+ )
319
+ self.mlp = SwiGLUMLP(config)
320
+
321
+ def forward(
322
+ self,
323
+ hidden_states: torch.Tensor,
324
+ cos: torch.Tensor,
325
+ sin: torch.Tensor,
326
+ ) -> torch.Tensor:
327
+ residual = hidden_states
328
+ hidden_states = self.input_layernorm(hidden_states)
329
+ hidden_states = self.self_attn(hidden_states, cos, sin)
330
+ hidden_states = residual + hidden_states
331
+
332
+ residual = hidden_states
333
+ hidden_states = self.post_attention_layernorm(hidden_states)
334
+ hidden_states = self.mlp(hidden_states)
335
+ return residual + hidden_states
336
+
337
+
338
+ class CodeBharat(nn.Module):
339
+ """Dense, causal CodeBharat-100M language model.
340
+
341
+ Inputs must be integer token IDs with shape (batch, sequence). Packed corpus
342
+ shards are uint16 on disk; the data loader must convert each batch to int64
343
+ or int32 before calling this model.
344
+ """
345
+
346
+ def __init__(self, config: ModelConfig | None = None) -> None:
347
+ super().__init__()
348
+ self.config = config if config is not None else ModelConfig()
349
+
350
+ self.token_embeddings = nn.Embedding(
351
+ self.config.vocab_size,
352
+ self.config.hidden_size,
353
+ )
354
+ self.layers = nn.ModuleList(
355
+ DecoderLayer(self.config) for _ in range(self.config.num_layers)
356
+ )
357
+ self.final_norm = RMSNorm(
358
+ self.config.hidden_size,
359
+ self.config.rms_norm_eps,
360
+ )
361
+ self.rotary_emb = RotaryEmbedding(
362
+ self.config.head_dim,
363
+ self.config.rope_theta,
364
+ )
365
+ self.lm_head: nn.Linear | None
366
+ if self.config.tie_word_embeddings:
367
+ self.lm_head = None
368
+ else:
369
+ self.lm_head = nn.Linear(
370
+ self.config.hidden_size,
371
+ self.config.vocab_size,
372
+ bias=False,
373
+ )
374
+
375
+ self.apply(self._init_weights)
376
+
377
+ def _init_weights(self, module: nn.Module) -> None:
378
+ if isinstance(module, (nn.Linear, nn.Embedding)):
379
+ nn.init.normal_(
380
+ module.weight,
381
+ mean=0.0,
382
+ std=self.config.initializer_range,
383
+ )
384
+
385
+ def forward(
386
+ self,
387
+ input_ids: torch.Tensor,
388
+ position_ids: torch.Tensor | None = None,
389
+ ) -> torch.Tensor:
390
+ """Return next-token logits with shape (batch, sequence, vocab_size)."""
391
+ if input_ids.ndim != 2:
392
+ raise ValueError(
393
+ "input_ids must have shape (batch, sequence), "
394
+ f"got {tuple(input_ids.shape)}"
395
+ )
396
+ if input_ids.dtype not in (torch.int32, torch.int64):
397
+ raise TypeError(
398
+ "input_ids must be torch.int32 or torch.int64; "
399
+ f"got {input_ids.dtype}. Cast packed uint16 batches first."
400
+ )
401
+
402
+ batch_size, seq_len = input_ids.shape
403
+ if seq_len > self.config.max_seq_len:
404
+ raise ValueError(
405
+ f"sequence length {seq_len} exceeds configured maximum "
406
+ f"{self.config.max_seq_len}"
407
+ )
408
+
409
+ if position_ids is None:
410
+ position_ids = torch.arange(
411
+ seq_len,
412
+ device=input_ids.device,
413
+ dtype=torch.long,
414
+ ).unsqueeze(0).expand(batch_size, -1)
415
+ elif position_ids.shape != input_ids.shape:
416
+ raise ValueError(
417
+ "position_ids must have the same shape as input_ids, "
418
+ f"got {tuple(position_ids.shape)} and {tuple(input_ids.shape)}"
419
+ )
420
+ elif position_ids.dtype not in (torch.int32, torch.int64):
421
+ raise TypeError("position_ids must be torch.int32 or torch.int64")
422
+ elif position_ids.numel() and position_ids.max().item() >= self.config.max_seq_len:
423
+ raise ValueError(
424
+ "position_ids contains a position outside the configured "
425
+ f"maximum of {self.config.max_seq_len}"
426
+ )
427
+
428
+ hidden_states = self.token_embeddings(input_ids)
429
+ cos, sin = self.rotary_emb(position_ids, hidden_states.dtype)
430
+
431
+ for layer in self.layers:
432
+ hidden_states = layer(hidden_states, cos, sin)
433
+ hidden_states = self.final_norm(hidden_states)
434
+
435
+ if self.lm_head is None:
436
+ return F.linear(hidden_states, self.token_embeddings.weight)
437
+ return self.lm_head(hidden_states)
438
+
439
+ def count_parameters(self) -> int:
440
+ """Return trainable parameters, respecting weight tying."""
441
+ return sum(
442
+ parameter.numel()
443
+ for parameter in self.parameters()
444
+ if parameter.requires_grad
445
+ )
446
+
447
+
448
+ def _verify_packed_data_contract(config: ModelConfig) -> None:
449
+ """Fail early if model defaults drift from the packed-data contract."""
450
+ metadata_path = BASE_DIR / "data" / "tokenized" / "meta.json"
451
+ if not metadata_path.exists():
452
+ print(f"[smoke] packed-data metadata not found: {metadata_path}")
453
+ return
454
+
455
+ metadata = json.loads(metadata_path.read_text(encoding="utf-8"))
456
+ expected = {
457
+ "vocab_size": config.vocab_size,
458
+ "seq_len": config.max_seq_len,
459
+ "dtype": "uint16",
460
+ }
461
+ actual = {name: metadata.get(name) for name in expected}
462
+ if actual != expected:
463
+ raise AssertionError(
464
+ f"Packed-data contract mismatch: expected {expected}, got {actual}"
465
+ )
466
+ print("[smoke] packed-data contract OK")
467
+
468
+
469
+ def run_smoke_test() -> None:
470
+ """Check parameter budget, data contract, causality, and gradients."""
471
+ torch.manual_seed(7)
472
+
473
+ config = ModelConfig()
474
+ model = CodeBharat(config).eval()
475
+ parameter_count = model.count_parameters()
476
+ if parameter_count != config.estimated_parameter_count:
477
+ raise AssertionError(
478
+ "Parameter estimate mismatch: "
479
+ f"{parameter_count:,} actual vs {config.estimated_parameter_count:,} expected"
480
+ )
481
+ if not 100_000_000 <= parameter_count <= 101_000_000:
482
+ raise AssertionError(
483
+ f"Default model is outside the 100M target: {parameter_count:,}"
484
+ )
485
+
486
+ with torch.inference_mode():
487
+ input_ids = torch.randint(
488
+ 0,
489
+ config.vocab_size,
490
+ (1, 16),
491
+ dtype=torch.long,
492
+ )
493
+ logits = model(input_ids)
494
+ expected_shape = (1, 16, config.vocab_size)
495
+ if logits.shape != expected_shape:
496
+ raise AssertionError(
497
+ f"Unexpected default-model logits shape: {tuple(logits.shape)}"
498
+ )
499
+ if not torch.isfinite(logits).all():
500
+ raise AssertionError("Default-model logits contain non-finite values")
501
+ _verify_packed_data_contract(config)
502
+
503
+ # A small model makes causal and backward checks fast while using the same
504
+ # components as the 100M model.
505
+ tiny_config = ModelConfig(
506
+ vocab_size=128,
507
+ hidden_size=64,
508
+ num_layers=2,
509
+ num_attention_heads=4,
510
+ num_key_value_heads=2,
511
+ intermediate_size=192,
512
+ max_seq_len=32,
513
+ )
514
+ tiny_model = CodeBharat(tiny_config).eval()
515
+ tiny_input = torch.randint(0, tiny_config.vocab_size, (2, 12))
516
+ altered_input = tiny_input.clone()
517
+ altered_input[:, -1] = (altered_input[:, -1] + 1) % tiny_config.vocab_size
518
+
519
+ with torch.inference_mode():
520
+ original_logits = tiny_model(tiny_input)
521
+ altered_logits = tiny_model(altered_input)
522
+ torch.testing.assert_close(
523
+ original_logits[:, :-1],
524
+ altered_logits[:, :-1],
525
+ rtol=0.0,
526
+ atol=1e-6,
527
+ msg="A future token changed an earlier causal prediction",
528
+ )
529
+
530
+ tiny_model.train()
531
+ train_logits = tiny_model(tiny_input)
532
+ loss = F.cross_entropy(
533
+ train_logits[:, :-1].reshape(-1, tiny_config.vocab_size),
534
+ tiny_input[:, 1:].reshape(-1),
535
+ )
536
+ loss.backward()
537
+ if tiny_model.token_embeddings.weight.grad is None:
538
+ raise AssertionError("Backward pass did not produce embedding gradients")
539
+
540
+ print(f"[smoke] parameters: {parameter_count:,} ({parameter_count / 1e6:.2f}M)")
541
+ print(f"[smoke] default forward: {tuple(logits.shape)}")
542
+ print(f"[smoke] tiny causal + backward checks: OK (loss={loss.item():.4f})")
543
+ print("[smoke] CodeBharat-100M model: PASS")
544
+
545
+
546
+ def parse_args() -> argparse.Namespace:
547
+ parser = argparse.ArgumentParser(description=__doc__)
548
+ parser.add_argument(
549
+ "--smoke-test",
550
+ action="store_true",
551
+ help="run architecture and packed-data contract checks",
552
+ )
553
+ return parser.parse_args()
554
+
555
+
556
+ def main() -> None:
557
+ args = parse_args()
558
+ if args.smoke_test:
559
+ run_smoke_test()
560
+ return
561
+
562
+ config = ModelConfig()
563
+ print("CodeBharat-100M architecture")
564
+ print(json.dumps(config.to_dict(), indent=2))
565
+ print(f"Estimated parameters: {config.estimated_parameter_count:,}")
566
+ print("Run with --smoke-test to execute forward, causal, and gradient checks.")
567
+
568
+
569
+ if __name__ == "__main__":
570
+ main()
pytorch_model.bin ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:8329e7cbce4b1010479ca1c8901d7a0a1d6279c636a96194ddee01d389478205
3
+ size 402747355
tokenizer.json ADDED
The diff for this file is too large to render. See raw diff
 
tokenizer_config.json ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ {
2
+ "add_bos_token": false,
3
+ "add_eos_token": false,
4
+ "model_max_length": 1024,
5
+ "tokenizer_class": "PreTrainedTokenizerFast"
6
+ }
upload_to_hf.py ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Upload exported CodeBharat-100M model folder to Hugging Face Hub.
2
+
3
+ Usage:
4
+ python upload_to_hf.py --repo-id "YOUR_USERNAME/codebharat-100m" --token "YOUR_HF_TOKEN"
5
+ """
6
+
7
+ import argparse
8
+ from pathlib import Path
9
+ from huggingface_hub import HfApi, create_repo
10
+
11
+ def main():
12
+ parser = argparse.ArgumentParser(description="Upload CodeBharat-100M to Hugging Face Hub")
13
+ parser.add_argument("--repo-id", type=str, required=True, help="Target HF repo ID, e.g. username/codebharat-100m")
14
+ parser.add_argument("--token", type=str, default=None, help="Hugging Face User Access Token (write permission)")
15
+ parser.add_argument("--private", action="store_true", help="Create as a private repository")
16
+ args = parser.parse_args()
17
+
18
+ model_dir = Path(__file__).resolve().parent
19
+ api = HfApi()
20
+
21
+ print(f"[hf-upload] Creating repository: {args.repo_id}")
22
+ create_repo(repo_id=args.repo_id, token=args.token, private=args.private, exist_ok=True)
23
+
24
+ print(f"[hf-upload] Uploading files from {model_dir} to {args.repo_id}...")
25
+ api.upload_folder(
26
+ folder_path=str(model_dir),
27
+ repo_id=args.repo_id,
28
+ repo_type="model",
29
+ token=args.token,
30
+ )
31
+ print(f"[done] Model successfully published at: https://huggingface.co/{args.repo_id}")
32
+
33
+ if __name__ == "__main__":
34
+ main()