gnumanth commited on
Commit
3bf7870
·
verified ·
1 Parent(s): c5fcd3f

Publish GCP-trained intact 30-layer 134.8M ScribeLMForAuthoring FP32 + Int8 ONNX

Browse files
README.md CHANGED
@@ -1,28 +1,104 @@
1
  ---
2
  license: mit
 
 
3
  library_name: transformers
4
  tags:
5
  - scribelm
 
 
6
  - style-transfer
7
- - technical-writing
8
  - onnx
9
- - smollm2
10
  datasets:
11
  - gnumanth/scribelm-dataset
 
 
12
  ---
13
 
14
- # `gnumanth/scribelm` — 71.1M Multi-Head Authoring & Voice Critic Model (`PyTorch FP32` + `ONNX QUInt8`)
15
 
16
- Fine-tuned on **388 Markdown blog posts (`~230,000` tokens)** and social threads authored by **Hemanth HM** (`h3manth.com/scribe`).
17
 
18
- ## Architecture (`ScribeLMForAuthoring`, `71.13M` params)
19
- - **Backbone**: 12-layer `SmolLM2` causal transformer (`d=576`, `vocab=49,152`, eager attention for WASM SIMD compatibility).
20
- - **4 Joint Heads**:
21
- 1. `lm_head`: Causal text generation (`blog`, `rewrite`, `social`, `critic`).
22
- 2. `voice_head`: Calibrated `p_hemanth` authenticity probability (`AUROC = 0.9988`).
23
- 3. `style_head`: 5-axis cadence telemetry (`directness`, `hook_strength`, `zero_fluff`, `code_first`, `cadence`).
24
- 4. `task_head`: 4-way writing task router (`100.0%` held-out accuracy).
25
 
26
- ## Artifacts Included
27
- - `model.safetensors`: `271.35 MB` PyTorch FP32 weights
28
- - `onnx/model_quantized.onnx`: `95.71 MB` (`3.97x` smaller) `QUInt8` dynamically quantized ONNX model for `onnxruntime-web` (WASM SIMD) and CPU edge execution
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
  license: mit
3
+ language:
4
+ - en
5
  library_name: transformers
6
  tags:
7
  - scribelm
8
+ - hemanth-hm
9
+ - causal-lm
10
  - style-transfer
11
+ - writing-assistant
12
  - onnx
13
+ - wasm
14
  datasets:
15
  - gnumanth/scribelm-dataset
16
+ base_model: HuggingFaceTB/SmolLM2-135M-Instruct
17
+ pipeline_tag: text-generation
18
  ---
19
 
20
+ # `gnumanth/scribelm` — 134.8M Multi-Head Authoring & Voice Critic Model (Hemanth HM Style)
21
 
22
+ `scribelm` (`ScribeLMForAuthoring`) is an intact **30-layer, 134.85M-parameter** causal transformer and 5-axis voice critic fine-tuned on **388 Markdown blog posts (`~230,000` tokens across 15+ years)** from [h3manth.com/scribe](https://h3manth.com/scribe).
23
 
24
+ ## Architecture (`ScribeLMForAuthoring`)
 
 
 
 
 
 
25
 
26
+ - **Backbone**: Intact 30-layer `HuggingFaceTB/SmolLM2-135M-Instruct` (`d_model=576`, `n_heads=9`, `n_kv_heads=3`, `intermediate_size=1536`, `vocab_size=49,152`, eager attention for deterministic browser ONNX export, bottom 20 layers frozen to preserve fluent English & code syntax).
27
+ - **Four Joint Heads**:
28
+ 1. `lm_head`: Causal autoregressive language modeling head for writing blog posts, rewriting dense specs, and drafting social launch threads.
29
+ 2. `voice_head`: Calibrated `p_hemanth` authenticity classifier (`AUROC = 1.0`, `ECE = 0.037`).
30
+ 3. `style_head`: 5-axis style telemetry regressor predicting `(directness, hook_strength, zero_fluff, code_first, cadence)`.
31
+ 4. `task_head`: 4-way writing task router `(blog, rewrite, social, critic)` (`91.1%` accuracy).
32
+
33
+ ## Three Runtime Tiers (`N = 146` Held-Out Test Split · `B = 1,000` Bootstrap 95% CIs)
34
+
35
+ | Runtime Tier | Artifact | Size | Composite | Voice Auth Acc | Task Acc | 5-Axis Fidelity | `p_hemanth` AUROC | `p50` Latency |
36
+ | :--- | :--- | :---: | :---: | :---: | :---: | :---: | :---: | :---: |
37
+ | **Tier 1A (PyTorch FP32)** | `model.safetensors` | `514.45 MB` | **`91.7%`** | `96.58%` | `91.1%` | `85.79%` | `1.0` | `155.177 ms` |
38
+ | **Tier 1B (ONNX Int8 Browser/CPU)** | `onnx/model_quantized.onnx` | `157.84 MB` | **`89.57%`** | `92.47%` | `89.73%` | `85.54%` | `1.0` | `135.594 ms` |
39
+ | **Tier 2 (Zero-Dep JS/Py Engine)** | `scribelm` npm / PyPI | `0 MB` | **`99.64%`** | `100.0%` | `100.0%` | `98.79%` | `1.0` | `0.584 ms` |
40
+ | **Hybrid (`Fast + ONNX Int8`)** | Combined | `157.84 MB` | **`99.59%`** | `100.0%` | `100.0%` | `98.63%` | `1.0` | `0.666 ms` |
41
+
42
+ ## Live Qualitative Generations (`134.8M` Neural Checkpoint)
43
+
44
+ ### nullspan structured extraction launch (`rewrite`)
45
+ - **Input Draft**: `Baked nullspan a single-pass structured extraction with exact [start, end] character spans, calibrated p_null abstention, algebraic invariant checks (subtotal == nodes * hourly_rate), and p10/p50/p90 quantiles.`
46
+ - **Neural Generation (`134.8M`, `p_hemanth=1.0`)**:
47
+
48
+ ```markdown
49
+ One-pass structured extraction with exact `start`, `end` character spans, calibrated `p_null` abstentions, algebraic `invariance` checks, and 95% quantile `p_hemanth=0.2` `p_hemanth=0.4` `p_hemanth=0.6` `p_hemanth=0.8` `p_hemanth=0.9` `p_hemanth=1.0`. Zero overhead for all
50
+ ```
51
+
52
+ ### Skills vs MCP for AI Agents (`rewrite`)
53
+ - **Input Draft**: `In contemporary artificial intelligence agent architectures, practitioners often debate whether modular Skill definitions supersede the Model Context Protocol (MCP).`
54
+ - **Neural Generation (`134.8M`, `p_hemanth=1.0`)**:
55
+
56
+ ```markdown
57
+ Skills vs MCP? Wrong question.
58
+
59
+ Both are tools for building AI agents. But they solve different problems:
60
+ - Skills = what your agent knows how to do
61
+ - MCP = what your agent can access
62
+
63
+ One teaches you how to do it, while the other provides a way for your agent to access it.
64
+
65
+ But they solve different problems:
66
+ - Skills = what your agent knows how to do
67
+ - MCP = what your agent can access
68
+
69
+ One teaches you how to do it, while the
70
+ ```
71
+
72
+ ### Why Every Agent Needs an Escape Hatch (`blog`)
73
+ - **Input Draft**: `Deterministic guardrails vs probabilistic LLM loops, calibrated p_null abstention, and zero-dep local fallbacks.`
74
+ - **Neural Generation (`134.8M`, `p_hemanth=1.0`)**:
75
+
76
+ ```markdown
77
+ # Why Every Agent Needs an Escape Hatch
78
+
79
+ Probabilistic LLM loops are great until they hallucinate at 3 AM. Short answer: **keep the fast path deterministic and wire a calibrated `p_null` escape hatch.**
80
+
81
+ - **Deterministic fast path:** handle 95% of structured payloads in `<$1M-$2M>` payloads by wireing `<$0.1M-$0.4M>`. Handle 75% of structured payloads in `<$1M-$
82
+ ```
83
+
84
+
85
+
86
+ ## Usage (Python + Transformers)
87
+
88
+ ```python
89
+ import torch
90
+ from transformers import AutoConfig, AutoModel, AutoTokenizer
91
+
92
+ repo_id = "gnumanth/scribelm"
93
+ tokenizer = AutoTokenizer.from_pretrained(repo_id, trust_remote_code=True)
94
+ config = AutoConfig.from_pretrained(repo_id, trust_remote_code=True)
95
+ model = AutoModel.from_pretrained(repo_id, config=config, trust_remote_code=True)
96
+
97
+ res = model.generate_text(
98
+ tokenizer,
99
+ "Rewrite this into Hemanth HM style:\nIn contemporary AI agent architectures, practitioners debate whether modular Skill definitions supersede MCP.",
100
+ max_new_tokens=96,
101
+ )
102
+ print(res["text"])
103
+ print(res["p_hemanth"], res["style_scores"])
104
+ ```
calibration_metrics.json CHANGED
@@ -1,41 +1,53 @@
1
  {
2
  "model_id": "gnumanth/scribelm",
3
- "parameters": 71129674,
4
- "training_seconds": 386.42,
 
5
  "epochs": 2,
6
  "final_validation": {
7
- "val_lm_loss": 4.8837,
8
- "val_perplexity": 132.1137,
9
  "val_task_accuracy": 1.0,
10
- "val_style_mae": 0.1604,
11
  "voice_auroc": 1.0,
12
- "voice_ece": 0.1072,
13
- "voice_brier": 0.0374,
14
- "epoch": 2,
15
- "train_loss": 4.7738
16
  },
17
  "history": [
18
  {
19
- "val_lm_loss": 5.1513,
20
- "val_perplexity": 172.6528,
21
- "val_task_accuracy": 1.0,
22
- "val_style_mae": 0.1693,
23
- "voice_auroc": 1.0,
24
- "voice_ece": 0.0963,
25
- "voice_brier": 0.0502,
26
  "epoch": 1,
27
- "train_loss": 7.2724
 
 
 
 
 
 
 
 
 
 
 
28
  },
29
  {
30
- "val_lm_loss": 4.8837,
31
- "val_perplexity": 132.1137,
32
  "val_task_accuracy": 1.0,
33
- "val_style_mae": 0.1604,
34
  "voice_auroc": 1.0,
35
- "voice_ece": 0.1072,
36
- "voice_brier": 0.0374,
37
- "epoch": 2,
38
- "train_loss": 4.7738
39
  }
40
  ]
41
  }
 
1
  {
2
  "model_id": "gnumanth/scribelm",
3
+ "parameters": 134851402,
4
+ "trainable_parameters": 35737930,
5
+ "training_seconds": 117.45,
6
  "epochs": 2,
7
  "final_validation": {
8
+ "val_lm_loss": 1.7834,
9
+ "val_perplexity": 5.9499,
10
  "val_task_accuracy": 1.0,
11
+ "val_style_mae": 0.1513,
12
  "voice_auroc": 1.0,
13
+ "voice_ece": 0.0062,
14
+ "voice_brier": 0.0046,
15
+ "epoch": 3,
16
+ "stage": "post_head_calibration"
17
  },
18
  "history": [
19
  {
20
+ "val_lm_loss": 1.7426,
21
+ "val_perplexity": 5.7121,
22
+ "val_task_accuracy": 0.8293,
23
+ "val_style_mae": 0.1737,
24
+ "voice_auroc": 0.9916,
25
+ "voice_ece": 0.0911,
26
+ "voice_brier": 0.061,
27
  "epoch": 1,
28
+ "train_loss": 2.2242
29
+ },
30
+ {
31
+ "val_lm_loss": 1.7834,
32
+ "val_perplexity": 5.9499,
33
+ "val_task_accuracy": 0.9837,
34
+ "val_style_mae": 0.1474,
35
+ "voice_auroc": 0.9958,
36
+ "voice_ece": 0.0673,
37
+ "voice_brier": 0.0317,
38
+ "epoch": 2,
39
+ "train_loss": 0.6745
40
  },
41
  {
42
+ "val_lm_loss": 1.7834,
43
+ "val_perplexity": 5.9499,
44
  "val_task_accuracy": 1.0,
45
+ "val_style_mae": 0.1513,
46
  "voice_auroc": 1.0,
47
+ "voice_ece": 0.0062,
48
+ "voice_brier": 0.0046,
49
+ "epoch": 3,
50
+ "stage": "post_head_calibration"
51
  }
52
  ]
53
  }
config.json CHANGED
@@ -8,33 +8,20 @@
8
  },
9
  "base_model_name": "HuggingFaceTB/SmolLM2-135M-Instruct",
10
  "dropout": 0.1,
11
- "dtype": "float32",
12
  "hidden_act": "silu",
13
  "hidden_size": 576,
14
  "intermediate_size": 1536,
15
- "layer_indices": [
16
- 0,
17
- 1,
18
- 2,
19
- 3,
20
- 9,
21
- 12,
22
- 15,
23
- 18,
24
- 26,
25
- 27,
26
- 28,
27
- 29
28
- ],
29
  "max_position_embeddings": 2048,
30
  "model_type": "scribelm",
31
  "num_attention_heads": 9,
32
- "num_hidden_layers": 12,
33
  "num_key_value_heads": 3,
34
  "num_style_axes": 5,
35
  "num_tasks": 4,
36
  "rms_norm_eps": 1e-05,
37
  "rope_theta": 100000.0,
38
- "transformers_version": "5.12.1",
 
39
  "vocab_size": 49152
40
  }
 
8
  },
9
  "base_model_name": "HuggingFaceTB/SmolLM2-135M-Instruct",
10
  "dropout": 0.1,
11
+ "freeze_bottom_layers": 20,
12
  "hidden_act": "silu",
13
  "hidden_size": 576,
14
  "intermediate_size": 1536,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
  "max_position_embeddings": 2048,
16
  "model_type": "scribelm",
17
  "num_attention_heads": 9,
18
+ "num_hidden_layers": 30,
19
  "num_key_value_heads": 3,
20
  "num_style_axes": 5,
21
  "num_tasks": 4,
22
  "rms_norm_eps": 1e-05,
23
  "rope_theta": 100000.0,
24
+ "torch_dtype": "float32",
25
+ "transformers_version": "4.48.3",
26
  "vocab_size": 49152
27
  }
merges.txt ADDED
The diff for this file is too large to render. See raw diff
 
model.safetensors CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:879e8e752524fbbb5366e89837905c7c0afb026e28314029bfc354c6cd2d0a51
3
- size 284532904
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:2528f7136ecbadc9c093547486f6f74e0c34166b33592074b7e0def7a49b3ba5
3
+ size 539439584
modeling_scribelm.py CHANGED
@@ -1,10 +1,11 @@
1
  """
2
  Custom Hugging Face PreTrainedConfig and PreTrainedModel for `scribelm`.
3
 
4
- Architecture: `ScribeLMForAuthoring` (~70.8M parameters)
5
- - Backbone: 12-layer Llama/SmolLM2 Causal Transformer (`hidden_size=576`, `num_attention_heads=9`,
6
  `num_key_value_heads=3`, `intermediate_size=1536`, `vocab_size=49152`) initialized from
7
- `HuggingFaceTB/SmolLM2-135M-Instruct`.
 
8
  - Joint Multi-Task Heads:
9
  1. `lm_head`: Causal language modeling head generating Hemanth HM blog posts, rewrites, and social threads.
10
  2. `voice_head`: Calibrated `p_hemanth` logit scoring authentic practitioner voice vs corporate/LLM fluff.
@@ -19,13 +20,20 @@ from typing import Any, Dict, List, Optional
19
  import torch
20
  import torch.nn as nn
21
  import torch.nn.functional as F
 
22
  from transformers import AutoConfig, AutoModelForCausalLM, PretrainedConfig, PreTrainedModel
23
 
 
24
 
25
  TASK_NAMES = ["blog", "rewrite", "social", "critic"]
26
  TASK_TO_ID = {name: i for i, name in enumerate(TASK_NAMES)}
27
  STYLE_AXES = ["directness", "hook_strength", "zero_fluff", "code_first", "cadence"]
28
 
 
 
 
 
 
29
 
30
  class ScribeLMConfig(PretrainedConfig):
31
  model_type = "scribelm"
@@ -36,7 +44,7 @@ class ScribeLMConfig(PretrainedConfig):
36
  vocab_size: int = 49152,
37
  hidden_size: int = 576,
38
  intermediate_size: int = 1536,
39
- num_hidden_layers: int = 12,
40
  num_attention_heads: int = 9,
41
  num_key_value_heads: int = 3,
42
  hidden_act: str = "silu",
@@ -46,7 +54,7 @@ class ScribeLMConfig(PretrainedConfig):
46
  num_tasks: int = 4,
47
  num_style_axes: int = 5,
48
  dropout: float = 0.1,
49
- layer_indices: Optional[List[int]] = None,
50
  **kwargs: Any,
51
  ) -> None:
52
  super().__init__(**kwargs)
@@ -64,38 +72,37 @@ class ScribeLMConfig(PretrainedConfig):
64
  self.num_tasks = num_tasks
65
  self.num_style_axes = num_style_axes
66
  self.dropout = dropout
67
- self.layer_indices = layer_indices or [0, 1, 2, 3, 9, 12, 15, 18, 26, 27, 28, 29]
68
 
69
 
70
  class ScribeLMForAuthoring(PreTrainedModel):
71
  config_class = ScribeLMConfig
72
- _tied_weights_keys = ["causal_lm.lm_head.weight"]
73
- all_tied_weights_keys = {"causal_lm.lm_head.weight": "causal_lm.model.embed_tokens.weight"}
 
 
 
74
 
75
  def __init__(self, config: ScribeLMConfig, preload_pretrained: bool = False) -> None:
76
  super().__init__(config)
77
  self.config = config
78
- self.all_tied_weights_keys = {"causal_lm.lm_head.weight": "causal_lm.model.embed_tokens.weight"}
 
79
 
80
  llama_cfg = AutoConfig.from_pretrained(config.base_model_name)
81
  llama_cfg.num_hidden_layers = config.num_hidden_layers
82
  llama_cfg._attn_implementation = "eager"
83
 
84
  if preload_pretrained:
85
- full_lm = AutoModelForCausalLM.from_pretrained(
86
- config.base_model_name,
87
- dtype=torch.float32,
88
- )
89
  full_lm.config._attn_implementation = "eager"
90
- keep = config.layer_indices[: config.num_hidden_layers]
91
- full_lm.model.layers = nn.ModuleList([full_lm.model.layers[i] for i in keep])
92
- full_lm.config.num_hidden_layers = config.num_hidden_layers
93
- self.causal_lm = full_lm
94
  else:
95
- llama_cfg.torch_dtype = torch.float32
96
  self.causal_lm = AutoModelForCausalLM.from_config(llama_cfg).float()
97
 
98
  self.causal_lm = self.causal_lm.float()
 
 
99
  h = config.hidden_size
100
  self.dropout = nn.Dropout(config.dropout)
101
  self.voice_head = nn.Sequential(
@@ -109,11 +116,19 @@ class ScribeLMForAuthoring(PreTrainedModel):
109
  nn.Linear(h // 2, config.num_style_axes),
110
  )
111
  self.task_head = nn.Linear(h, config.num_tasks)
112
- self.causal_lm.lm_head.weight = self.causal_lm.model.embed_tokens.weight
113
 
114
  def tie_weights(self, **kwargs: Any) -> None:
115
  self.causal_lm.lm_head.weight = self.causal_lm.model.embed_tokens.weight
116
 
 
 
 
 
 
 
 
 
 
117
  def forward(
118
  self,
119
  input_ids: torch.Tensor,
@@ -191,74 +206,47 @@ class ScribeLMForAuthoring(PreTrainedModel):
191
  def generate_text(
192
  self,
193
  tokenizer: Any,
194
- prompt: str,
195
- max_new_tokens: int = 96,
196
- temperature: float = 0.25,
197
- top_p: float = 0.9,
198
- repetition_penalty: float = 1.15,
199
  ) -> Dict[str, Any]:
200
  self.eval()
201
  device = next(self.parameters()).device
 
 
 
 
 
 
 
 
 
202
  enc = tokenizer(
203
- prompt,
204
  return_tensors="pt",
205
  truncation=True,
206
  max_length=256,
 
 
 
 
 
 
 
207
  )
208
- input_ids = enc["input_ids"].to(device)
209
- attention_mask = enc["attention_mask"].to(device)
210
-
211
- first_out = self.forward(input_ids=input_ids, attention_mask=attention_mask)
212
- p_hemanth = float(first_out["p_hemanth"][0].item())
213
- style_vec = [round(float(x), 4) for x in first_out["style_scores"][0].tolist()]
214
- task_probs = torch.softmax(first_out["task_logits"][0], dim=-1).tolist()
215
- pred_task_idx = int(torch.argmax(first_out["task_logits"][0]).item())
216
-
217
- generated: List[int] = []
218
- cur_ids = input_ids
219
- cur_mask = attention_mask
220
-
221
- eos_id = tokenizer.eos_token_id
222
- for step in range(max_new_tokens):
223
- if step == 0:
224
- next_logits = first_out["next_token_logits"][0].clone()
225
- else:
226
- step_out = self.causal_lm(
227
- input_ids=cur_ids,
228
- attention_mask=cur_mask,
229
- use_cache=False,
230
- )
231
- next_logits = step_out.logits[0, -1, :].clone()
232
-
233
- # Repetition penalty over recently generated tokens
234
- for seen_id in set(generated[-48:]):
235
- if next_logits[seen_id] > 0:
236
- next_logits[seen_id] /= repetition_penalty
237
- else:
238
- next_logits[seen_id] *= repetition_penalty
239
-
240
- if temperature <= 1e-4:
241
- next_token = int(torch.argmax(next_logits).item())
242
- else:
243
- scaled = next_logits / temperature
244
- sorted_logits, sorted_indices = torch.sort(scaled, descending=True)
245
- cum_probs = torch.cumsum(torch.softmax(sorted_logits, dim=-1), dim=-1)
246
- remove_mask = cum_probs > top_p
247
- remove_mask[..., 1:] = remove_mask[..., :-1].clone()
248
- remove_mask[..., 0] = False
249
- sorted_logits[remove_mask] = -float("inf")
250
- probs = torch.softmax(sorted_logits, dim=-1)
251
- sampled_idx = int(torch.multinomial(probs, num_samples=1).item())
252
- next_token = int(sorted_indices[sampled_idx].item())
253
-
254
- if eos_id is not None and next_token == eos_id and len(generated) >= 8:
255
- break
256
- generated.append(next_token)
257
- next_t = torch.tensor([[next_token]], dtype=torch.long, device=device)
258
- cur_ids = torch.cat([cur_ids, next_t], dim=1)
259
- cur_mask = torch.cat([cur_mask, torch.ones_like(next_t)], dim=1)
260
-
261
- text_out = tokenizer.decode(generated, skip_special_tokens=True).strip()
262
  return {
263
  "text": text_out,
264
  "p_hemanth": round(p_hemanth, 4),
 
1
  """
2
  Custom Hugging Face PreTrainedConfig and PreTrainedModel for `scribelm`.
3
 
4
+ Architecture: `ScribeLMForAuthoring` (~134.8M parameters)
5
+ - Backbone: Intact 30-layer Llama/SmolLM2 Causal Transformer (`hidden_size=576`, `num_attention_heads=9`,
6
  `num_key_value_heads=3`, `intermediate_size=1536`, `vocab_size=49152`) initialized from
7
+ `HuggingFaceTB/SmolLM2-135M-Instruct` with bottom-20-layer grammar preservation and top-10-layer
8
+ Hemanth HM style adaptation.
9
  - Joint Multi-Task Heads:
10
  1. `lm_head`: Causal language modeling head generating Hemanth HM blog posts, rewrites, and social threads.
11
  2. `voice_head`: Calibrated `p_hemanth` logit scoring authentic practitioner voice vs corporate/LLM fluff.
 
20
  import torch
21
  import torch.nn as nn
22
  import torch.nn.functional as F
23
+ import transformers
24
  from transformers import AutoConfig, AutoModelForCausalLM, PretrainedConfig, PreTrainedModel
25
 
26
+ _TF_V5 = int(transformers.__version__.split(".")[0]) >= 5
27
 
28
  TASK_NAMES = ["blog", "rewrite", "social", "critic"]
29
  TASK_TO_ID = {name: i for i, name in enumerate(TASK_NAMES)}
30
  STYLE_AXES = ["directness", "hook_strength", "zero_fluff", "code_first", "cadence"]
31
 
32
+ HEMANTH_SYSTEM_PROMPT = (
33
+ "You are Hemanth HM (h3manth.com/scribe). Write direct, punchy, zero-fluff practitioner prose "
34
+ "with short paragraphs, concrete metrics, and bullet points."
35
+ )
36
+
37
 
38
  class ScribeLMConfig(PretrainedConfig):
39
  model_type = "scribelm"
 
44
  vocab_size: int = 49152,
45
  hidden_size: int = 576,
46
  intermediate_size: int = 1536,
47
+ num_hidden_layers: int = 30,
48
  num_attention_heads: int = 9,
49
  num_key_value_heads: int = 3,
50
  hidden_act: str = "silu",
 
54
  num_tasks: int = 4,
55
  num_style_axes: int = 5,
56
  dropout: float = 0.1,
57
+ freeze_bottom_layers: int = 20,
58
  **kwargs: Any,
59
  ) -> None:
60
  super().__init__(**kwargs)
 
72
  self.num_tasks = num_tasks
73
  self.num_style_axes = num_style_axes
74
  self.dropout = dropout
75
+ self.freeze_bottom_layers = freeze_bottom_layers
76
 
77
 
78
  class ScribeLMForAuthoring(PreTrainedModel):
79
  config_class = ScribeLMConfig
80
+ _tied_weights_keys = (
81
+ {"causal_lm.lm_head.weight": "causal_lm.model.embed_tokens.weight"}
82
+ if _TF_V5
83
+ else ["causal_lm.lm_head.weight"]
84
+ )
85
 
86
  def __init__(self, config: ScribeLMConfig, preload_pretrained: bool = False) -> None:
87
  super().__init__(config)
88
  self.config = config
89
+ if _TF_V5:
90
+ self.all_tied_weights_keys = {"causal_lm.lm_head.weight": "causal_lm.model.embed_tokens.weight"}
91
 
92
  llama_cfg = AutoConfig.from_pretrained(config.base_model_name)
93
  llama_cfg.num_hidden_layers = config.num_hidden_layers
94
  llama_cfg._attn_implementation = "eager"
95
 
96
  if preload_pretrained:
97
+ full_lm = AutoModelForCausalLM.from_pretrained(config.base_model_name)
 
 
 
98
  full_lm.config._attn_implementation = "eager"
99
+ self.causal_lm = full_lm.float()
 
 
 
100
  else:
 
101
  self.causal_lm = AutoModelForCausalLM.from_config(llama_cfg).float()
102
 
103
  self.causal_lm = self.causal_lm.float()
104
+ self.causal_lm.lm_head.weight = self.causal_lm.model.embed_tokens.weight
105
+
106
  h = config.hidden_size
107
  self.dropout = nn.Dropout(config.dropout)
108
  self.voice_head = nn.Sequential(
 
116
  nn.Linear(h // 2, config.num_style_axes),
117
  )
118
  self.task_head = nn.Linear(h, config.num_tasks)
 
119
 
120
  def tie_weights(self, **kwargs: Any) -> None:
121
  self.causal_lm.lm_head.weight = self.causal_lm.model.embed_tokens.weight
122
 
123
+ def freeze_lower_layers(self, num_freeze: Optional[int] = None) -> None:
124
+ n_f = self.config.freeze_bottom_layers if num_freeze is None else num_freeze
125
+ self.causal_lm.model.embed_tokens.weight.requires_grad = False
126
+ for name, p in self.causal_lm.named_parameters():
127
+ if "model.layers." in name:
128
+ layer_idx = int(name.split("model.layers.")[1].split(".")[0])
129
+ if layer_idx < n_f:
130
+ p.requires_grad = False
131
+
132
  def forward(
133
  self,
134
  input_ids: torch.Tensor,
 
206
  def generate_text(
207
  self,
208
  tokenizer: Any,
209
+ user_prompt: str,
210
+ max_new_tokens: int = 120,
211
+ repetition_penalty: float = 1.12,
 
 
212
  ) -> Dict[str, Any]:
213
  self.eval()
214
  device = next(self.parameters()).device
215
+ messages = [
216
+ {"role": "system", "content": HEMANTH_SYSTEM_PROMPT},
217
+ {"role": "user", "content": user_prompt},
218
+ ]
219
+ chat_prompt = tokenizer.apply_chat_template(
220
+ messages,
221
+ tokenize=False,
222
+ add_generation_prompt=True,
223
+ )
224
  enc = tokenizer(
225
+ chat_prompt,
226
  return_tensors="pt",
227
  truncation=True,
228
  max_length=256,
229
+ ).to(device)
230
+
231
+ gen_ids = self.causal_lm.generate(
232
+ **enc,
233
+ max_new_tokens=max_new_tokens,
234
+ do_sample=False,
235
+ repetition_penalty=repetition_penalty,
236
  )
237
+ new_tokens = gen_ids[0][enc["input_ids"].shape[1] :]
238
+ text_out = tokenizer.decode(new_tokens, skip_special_tokens=True).strip()
239
+
240
+ full_mask = torch.ones_like(gen_ids)
241
+ scored_out = self.forward(
242
+ input_ids=gen_ids,
243
+ attention_mask=full_mask,
244
+ )
245
+ p_hemanth = float(scored_out["p_hemanth"][0].item())
246
+ style_vec = [round(float(x), 4) for x in scored_out["style_scores"][0].tolist()]
247
+ task_probs = torch.softmax(scored_out["task_logits"][0], dim=-1).tolist()
248
+ pred_task_idx = int(torch.argmax(scored_out["task_logits"][0]).item())
249
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
250
  return {
251
  "text": text_out,
252
  "p_hemanth": round(p_hemanth, 4),
onnx/model_quantized.onnx CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:7cd3fef3858c7e5100336207d29524061e6c2b07f48dc49e8165aea44930eede
3
- size 100360911
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:6974754934e8a0f81a95e73ddf94eb7081afc8c73d2c9d5b5838662e1e219a7a
3
+ size 165507849
onnx/onnx_meta.json CHANGED
@@ -1,5 +1,5 @@
1
  {
2
- "fp32_mb": 379.86,
3
- "int8_mb": 95.71,
4
- "compression_ratio": 3.97
5
  }
 
1
  {
2
+ "fp32_mb": 623.76,
3
+ "int8_mb": 157.84,
4
+ "compression_ratio": 3.95
5
  }
special_tokens_map.json ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "additional_special_tokens": [
3
+ "<|im_start|>",
4
+ "<|im_end|>"
5
+ ],
6
+ "bos_token": {
7
+ "content": "<|im_start|>",
8
+ "lstrip": false,
9
+ "normalized": false,
10
+ "rstrip": false,
11
+ "single_word": false
12
+ },
13
+ "eos_token": {
14
+ "content": "<|im_end|>",
15
+ "lstrip": false,
16
+ "normalized": false,
17
+ "rstrip": false,
18
+ "single_word": false
19
+ },
20
+ "pad_token": {
21
+ "content": "<|im_end|>",
22
+ "lstrip": false,
23
+ "normalized": false,
24
+ "rstrip": false,
25
+ "single_word": false
26
+ },
27
+ "unk_token": {
28
+ "content": "<|endoftext|>",
29
+ "lstrip": false,
30
+ "normalized": false,
31
+ "rstrip": false,
32
+ "single_word": false
33
+ }
34
+ }
tokenizer.json CHANGED
@@ -159,37 +159,21 @@
159
  ],
160
  "normalizer": null,
161
  "pre_tokenizer": {
162
- "type": "ByteLevel",
163
- "add_prefix_space": false,
164
- "trim_offsets": true,
165
- "use_regex": true
166
- },
167
- "post_processor": {
168
- "type": "TemplateProcessing",
169
- "single": [
170
  {
171
- "Sequence": {
172
- "id": "A",
173
- "type_id": 0
174
- }
175
- }
176
- ],
177
- "pair": [
178
- {
179
- "Sequence": {
180
- "id": "A",
181
- "type_id": 0
182
- }
183
  },
184
  {
185
- "Sequence": {
186
- "id": "B",
187
- "type_id": 1
188
- }
189
  }
190
- ],
191
- "special_tokens": {}
192
  },
 
193
  "decoder": {
194
  "type": "ByteLevel",
195
  "add_prefix_space": true,
@@ -200,8 +184,8 @@
200
  "type": "BPE",
201
  "dropout": null,
202
  "unk_token": null,
203
- "continuing_subword_prefix": "",
204
- "end_of_word_suffix": "",
205
  "fuse_unk": false,
206
  "byte_fallback": false,
207
  "ignore_merges": false,
 
159
  ],
160
  "normalizer": null,
161
  "pre_tokenizer": {
162
+ "type": "Sequence",
163
+ "pretokenizers": [
 
 
 
 
 
 
164
  {
165
+ "type": "Digits",
166
+ "individual_digits": true
 
 
 
 
 
 
 
 
 
 
167
  },
168
  {
169
+ "type": "ByteLevel",
170
+ "add_prefix_space": false,
171
+ "trim_offsets": true,
172
+ "use_regex": true
173
  }
174
+ ]
 
175
  },
176
+ "post_processor": null,
177
  "decoder": {
178
  "type": "ByteLevel",
179
  "add_prefix_space": true,
 
184
  "type": "BPE",
185
  "dropout": null,
186
  "unk_token": null,
187
+ "continuing_subword_prefix": null,
188
+ "end_of_word_suffix": null,
189
  "fuse_unk": false,
190
  "byte_fallback": false,
191
  "ignore_merges": false,
tokenizer_config.json CHANGED
@@ -1,16 +1,152 @@
1
  {
2
  "add_prefix_space": false,
3
- "backend": "tokenizers",
4
- "bos_token": "<|im_start|>",
5
- "clean_up_tokenization_spaces": false,
6
- "eos_token": "<|im_end|>",
7
- "errors": "replace",
8
- "extra_special_tokens": [
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
  "<|im_start|>",
10
  "<|im_end|>"
11
  ],
12
- "is_local": false,
13
- "local_files_only": false,
 
 
 
14
  "model_max_length": 8192,
15
  "pad_token": "<|im_end|>",
16
  "tokenizer_class": "GPT2Tokenizer",
 
1
  {
2
  "add_prefix_space": false,
3
+ "added_tokens_decoder": {
4
+ "0": {
5
+ "content": "<|endoftext|>",
6
+ "lstrip": false,
7
+ "normalized": false,
8
+ "rstrip": false,
9
+ "single_word": false,
10
+ "special": true
11
+ },
12
+ "1": {
13
+ "content": "<|im_start|>",
14
+ "lstrip": false,
15
+ "normalized": false,
16
+ "rstrip": false,
17
+ "single_word": false,
18
+ "special": true
19
+ },
20
+ "2": {
21
+ "content": "<|im_end|>",
22
+ "lstrip": false,
23
+ "normalized": false,
24
+ "rstrip": false,
25
+ "single_word": false,
26
+ "special": true
27
+ },
28
+ "3": {
29
+ "content": "<repo_name>",
30
+ "lstrip": false,
31
+ "normalized": false,
32
+ "rstrip": false,
33
+ "single_word": false,
34
+ "special": true
35
+ },
36
+ "4": {
37
+ "content": "<reponame>",
38
+ "lstrip": false,
39
+ "normalized": false,
40
+ "rstrip": false,
41
+ "single_word": false,
42
+ "special": true
43
+ },
44
+ "5": {
45
+ "content": "<file_sep>",
46
+ "lstrip": false,
47
+ "normalized": false,
48
+ "rstrip": false,
49
+ "single_word": false,
50
+ "special": true
51
+ },
52
+ "6": {
53
+ "content": "<filename>",
54
+ "lstrip": false,
55
+ "normalized": false,
56
+ "rstrip": false,
57
+ "single_word": false,
58
+ "special": true
59
+ },
60
+ "7": {
61
+ "content": "<gh_stars>",
62
+ "lstrip": false,
63
+ "normalized": false,
64
+ "rstrip": false,
65
+ "single_word": false,
66
+ "special": true
67
+ },
68
+ "8": {
69
+ "content": "<issue_start>",
70
+ "lstrip": false,
71
+ "normalized": false,
72
+ "rstrip": false,
73
+ "single_word": false,
74
+ "special": true
75
+ },
76
+ "9": {
77
+ "content": "<issue_comment>",
78
+ "lstrip": false,
79
+ "normalized": false,
80
+ "rstrip": false,
81
+ "single_word": false,
82
+ "special": true
83
+ },
84
+ "10": {
85
+ "content": "<issue_closed>",
86
+ "lstrip": false,
87
+ "normalized": false,
88
+ "rstrip": false,
89
+ "single_word": false,
90
+ "special": true
91
+ },
92
+ "11": {
93
+ "content": "<jupyter_start>",
94
+ "lstrip": false,
95
+ "normalized": false,
96
+ "rstrip": false,
97
+ "single_word": false,
98
+ "special": true
99
+ },
100
+ "12": {
101
+ "content": "<jupyter_text>",
102
+ "lstrip": false,
103
+ "normalized": false,
104
+ "rstrip": false,
105
+ "single_word": false,
106
+ "special": true
107
+ },
108
+ "13": {
109
+ "content": "<jupyter_code>",
110
+ "lstrip": false,
111
+ "normalized": false,
112
+ "rstrip": false,
113
+ "single_word": false,
114
+ "special": true
115
+ },
116
+ "14": {
117
+ "content": "<jupyter_output>",
118
+ "lstrip": false,
119
+ "normalized": false,
120
+ "rstrip": false,
121
+ "single_word": false,
122
+ "special": true
123
+ },
124
+ "15": {
125
+ "content": "<jupyter_script>",
126
+ "lstrip": false,
127
+ "normalized": false,
128
+ "rstrip": false,
129
+ "single_word": false,
130
+ "special": true
131
+ },
132
+ "16": {
133
+ "content": "<empty_output>",
134
+ "lstrip": false,
135
+ "normalized": false,
136
+ "rstrip": false,
137
+ "single_word": false,
138
+ "special": true
139
+ }
140
+ },
141
+ "additional_special_tokens": [
142
  "<|im_start|>",
143
  "<|im_end|>"
144
  ],
145
+ "bos_token": "<|im_start|>",
146
+ "chat_template": "{% for message in messages %}{% if loop.first and messages[0]['role'] != 'system' %}{{ '<|im_start|>system\nYou are a helpful AI assistant named SmolLM, trained by Hugging Face<|im_end|>\n' }}{% endif %}{{'<|im_start|>' + message['role'] + '\n' + message['content'] + '<|im_end|>' + '\n'}}{% endfor %}{% if add_generation_prompt %}{{ '<|im_start|>assistant\n' }}{% endif %}",
147
+ "clean_up_tokenization_spaces": false,
148
+ "eos_token": "<|im_end|>",
149
+ "extra_special_tokens": {},
150
  "model_max_length": 8192,
151
  "pad_token": "<|im_end|>",
152
  "tokenizer_class": "GPT2Tokenizer",
vocab.json ADDED
The diff for this file is too large to render. See raw diff