ThingsAI commited on
Commit
e3d43ae
·
verified ·
1 Parent(s): 9bab82f

Upload Glyph multi-task model (epoch 3, avg acc 88.0%)

Browse files
Files changed (2) hide show
  1. README.md +56 -53
  2. model.py +2 -2
README.md CHANGED
@@ -12,52 +12,58 @@ tags:
12
  - lightweight
13
  pipeline_tag: text-classification
14
  ---
15
-
16
  # Glyph — Multi-Task Byte-Level Text Classifier
17
-
18
  **4M parameters** · **5 tasks** · **No tokenizer needed** · **Runs on CPU**
19
-
20
  Glyph is an ultra-compact multi-task text classification model that operates directly on raw UTF-8 bytes. A single shared backbone serves 5 classification heads simultaneously.
21
-
22
  ## Tasks & Performance
23
-
24
  | Task | Labels | Val Accuracy | Description |
25
  |------|--------|-------------|-------------|
26
- | `lang_id` | {n_lang_id} | {acc_lang_id:.1f}% | Language identification |
27
- | `prog_lang` | {n_prog_lang} | {acc_prog_lang:.1f}% | Programming language detection |
28
- | `spam` | 2 | {acc_spam:.1f}% | Spam vs ham classification |
29
- | `toxic` | 2 | {acc_toxic:.1f}% | Toxicity detection (multilingual) |
30
-
31
- **Average validation accuracy: {avg_acc:.1f}%**
32
-
33
  ## Architecture
34
-
35
  Glyph uses a byte-level hybrid architecture inspired by [CommonLingua](https://huggingface.co/PleIAs/CommonLingua):
36
-
37
  - **Input**: Raw UTF-8 bytes (no tokenizer), padded to 512 bytes
38
  - **Trigram hash embedding**: Polynomial rolling hash of byte 3-grams → 8192-bucket embedding table
39
  - **Byte unigram embedding**: Standard embedding for individual bytes
40
  - **4× Conv1D blocks**: Causal convolutions + BatchNorm + GELU + residual
41
  - **2× Bidirectional attention**: Multi-head self-attention with RoPE
42
  - **Global average pooling** → per-task classification heads
43
-
44
  Total: ~4M shared parameters + small per-task linear heads.
45
-
46
  ## Usage
47
-
48
  ```python
49
- import torch
50
  from huggingface_hub import hf_hub_download
51
-
52
- # Download and load
 
53
  ckpt_path = hf_hub_download("ThingAI/Glyph", "model.pt")
 
 
 
 
 
 
 
 
54
  ckpt = torch.load(ckpt_path, map_location="cpu", weights_only=False)
55
-
56
- # Reconstruct model (see model.py in repo)
57
- from model import MultiTaskLID
58
- model = MultiTaskLID(ckpt["task_configs"]).eval()
59
  model.load_state_dict(ckpt["model"])
60
-
61
  # Predict
62
  def predict(text, task="lang_id", top_k=3):
63
  raw = text.encode("utf-8")[:512]
@@ -66,68 +72,65 @@ def predict(text, task="lang_id", top_k=3):
66
  with torch.no_grad():
67
  logits = model(inp, task)["logits"][0]
68
  probs = torch.softmax(logits, dim=-1)
69
- idx2label = {{i: l for l, i in ckpt["label_maps"][task].items()}}
 
 
70
  topk = probs.topk(top_k)
71
  return [(idx2label[topk.indices[i].item()], topk.values[i].item()) for i in range(top_k)]
72
-
73
  # Examples
74
  print(predict("La pizza napoletana è patrimonio UNESCO", task="lang_id"))
75
- # [('ita', 0.98), ('cos', 0.003), ...]
76
-
77
  print(predict("def foo(x): return x + 1", task="prog_lang"))
78
- # [('Python', 0.95), ...]
79
-
80
  print(predict("You won a FREE iPhone!!!", task="spam"))
81
- # [('spam', 0.99), ...]
82
  ```
83
-
84
  ## Quick predict script
85
-
86
  ```bash
87
  python predict.py --text "Ciao, come stai?"
88
  # lang_id: ita (98.2%)
89
-
90
- python predict.py --task prog_lang --text "fn main() {{ println!(\"hello\"); }}"
91
  # prog_lang: Rust (97.1%)
92
-
93
  python predict.py --task spam --text "URGENT: Click here to win!"
94
  # spam: spam (99.5%)
95
  ```
96
-
97
  ## Training Data
98
-
99
  | Task | Dataset | Samples |
100
  |------|---------|---------|
101
  | Language ID | [PleIAs/CommonLingua-Train](https://huggingface.co/datasets/PleIAs/CommonLingua-Train) | 200K (capped) |
102
  | Programming Language | [cakiki/rosetta-code](https://huggingface.co/datasets/cakiki/rosetta-code) | ~26K |
103
  | Spam Detection | [ucirvine/sms_spam](https://huggingface.co/datasets/ucirvine/sms_spam) | 5.5K |
104
  | Toxicity | [textdetox/multilingual_toxicity_dataset](https://huggingface.co/datasets/textdetox/multilingual_toxicity_dataset) | ~45K |
105
-
106
  ## Key Design Decisions
107
-
108
  - **Byte-level input**: No tokenizer means it works on any language, script, or encoding without preprocessing
109
  - **Trigram hashing**: +1.2 F1 over unigram-only baseline, acts as regularization via hash collisions
110
  - **Multi-task learning**: Shared backbone learns universal text representations; per-task heads are tiny (~130K params each)
111
  - **No attention masking**: Bidirectional attention for classification (not causal)
112
  - **OneCycleLR**: Fast convergence in few epochs
113
-
114
  ## Limitations
115
-
116
  - Designed for paragraph-level classification (50+ bytes). Short texts (<20 bytes) may be unreliable
117
  - Toxicity detection accuracy is lower than specialized models (trained on limited multilingual data)
118
  - Programming language detection trained on Rosetta Code samples which have a specific style
119
-
120
  ## Citation
121
-
122
  ```bibtex
123
- @misc{{glyph2026,
124
- author = {{ThingAI}},
125
- title = {{Glyph: Multi-Task Byte-Level Text Classifier}},
126
- year = {{2026}},
127
- url = {{https://huggingface.co/ThingAI/Glyph}}
128
- }}
129
  ```
130
-
131
  ## License
132
-
133
  Apache 2.0
 
12
  - lightweight
13
  pipeline_tag: text-classification
14
  ---
15
+
16
  # Glyph — Multi-Task Byte-Level Text Classifier
17
+
18
  **4M parameters** · **5 tasks** · **No tokenizer needed** · **Runs on CPU**
19
+
20
  Glyph is an ultra-compact multi-task text classification model that operates directly on raw UTF-8 bytes. A single shared backbone serves 5 classification heads simultaneously.
21
+
22
  ## Tasks & Performance
23
+
24
  | Task | Labels | Val Accuracy | Description |
25
  |------|--------|-------------|-------------|
26
+ | `lang_id` | 351 | 94.3% | Language identification |
27
+ | `prog_lang` | 30 | 94.4% | Programming language detection |
28
+ | `spam` | 2 | 96.1% | Spam vs ham classification |
29
+ | `toxic` | 2 | 67.3% | Toxicity detection (multilingual) |
30
+
31
+ **Average validation accuracy: 88.0%**
32
+
33
  ## Architecture
34
+
35
  Glyph uses a byte-level hybrid architecture inspired by [CommonLingua](https://huggingface.co/PleIAs/CommonLingua):
36
+
37
  - **Input**: Raw UTF-8 bytes (no tokenizer), padded to 512 bytes
38
  - **Trigram hash embedding**: Polynomial rolling hash of byte 3-grams → 8192-bucket embedding table
39
  - **Byte unigram embedding**: Standard embedding for individual bytes
40
  - **4× Conv1D blocks**: Causal convolutions + BatchNorm + GELU + residual
41
  - **2× Bidirectional attention**: Multi-head self-attention with RoPE
42
  - **Global average pooling** → per-task classification heads
43
+
44
  Total: ~4M shared parameters + small per-task linear heads.
45
+
46
  ## Usage
47
+
48
  ```python
49
+ import torch, importlib, sys
50
  from huggingface_hub import hf_hub_download
51
+
52
+ # Download model + weights
53
+ model_py_path = hf_hub_download("ThingAI/Glyph", "model.py")
54
  ckpt_path = hf_hub_download("ThingAI/Glyph", "model.pt")
55
+
56
+ # Load model definition
57
+ import importlib.util
58
+ spec = importlib.util.spec_from_file_location("model", model_py_path)
59
+ mod = importlib.util.module_from_spec(spec)
60
+ spec.loader.exec_module(mod)
61
+
62
+ # Load weights
63
  ckpt = torch.load(ckpt_path, map_location="cpu", weights_only=False)
64
+ model = mod.MultiTaskLID(ckpt["task_configs"]).eval()
 
 
 
65
  model.load_state_dict(ckpt["model"])
66
+
67
  # Predict
68
  def predict(text, task="lang_id", top_k=3):
69
  raw = text.encode("utf-8")[:512]
 
72
  with torch.no_grad():
73
  logits = model(inp, task)["logits"][0]
74
  probs = torch.softmax(logits, dim=-1)
75
+ idx2label = dict(enumerate(
76
+ sorted(ckpt["label_maps"][task], key=ckpt["label_maps"][task].get)
77
+ ))
78
  topk = probs.topk(top_k)
79
  return [(idx2label[topk.indices[i].item()], topk.values[i].item()) for i in range(top_k)]
80
+
81
  # Examples
82
  print(predict("La pizza napoletana è patrimonio UNESCO", task="lang_id"))
 
 
83
  print(predict("def foo(x): return x + 1", task="prog_lang"))
 
 
84
  print(predict("You won a FREE iPhone!!!", task="spam"))
 
85
  ```
86
+
87
  ## Quick predict script
88
+
89
  ```bash
90
  python predict.py --text "Ciao, come stai?"
91
  # lang_id: ita (98.2%)
92
+
93
+ python predict.py --task prog_lang --text "fn main() { println!("hello"); }"
94
  # prog_lang: Rust (97.1%)
95
+
96
  python predict.py --task spam --text "URGENT: Click here to win!"
97
  # spam: spam (99.5%)
98
  ```
99
+
100
  ## Training Data
101
+
102
  | Task | Dataset | Samples |
103
  |------|---------|---------|
104
  | Language ID | [PleIAs/CommonLingua-Train](https://huggingface.co/datasets/PleIAs/CommonLingua-Train) | 200K (capped) |
105
  | Programming Language | [cakiki/rosetta-code](https://huggingface.co/datasets/cakiki/rosetta-code) | ~26K |
106
  | Spam Detection | [ucirvine/sms_spam](https://huggingface.co/datasets/ucirvine/sms_spam) | 5.5K |
107
  | Toxicity | [textdetox/multilingual_toxicity_dataset](https://huggingface.co/datasets/textdetox/multilingual_toxicity_dataset) | ~45K |
108
+
109
  ## Key Design Decisions
110
+
111
  - **Byte-level input**: No tokenizer means it works on any language, script, or encoding without preprocessing
112
  - **Trigram hashing**: +1.2 F1 over unigram-only baseline, acts as regularization via hash collisions
113
  - **Multi-task learning**: Shared backbone learns universal text representations; per-task heads are tiny (~130K params each)
114
  - **No attention masking**: Bidirectional attention for classification (not causal)
115
  - **OneCycleLR**: Fast convergence in few epochs
116
+
117
  ## Limitations
118
+
119
  - Designed for paragraph-level classification (50+ bytes). Short texts (<20 bytes) may be unreliable
120
  - Toxicity detection accuracy is lower than specialized models (trained on limited multilingual data)
121
  - Programming language detection trained on Rosetta Code samples which have a specific style
122
+
123
  ## Citation
124
+
125
  ```bibtex
126
+ @misc{glyph2026,
127
+ author = {ThingAI},
128
+ title = {Glyph: Multi-Task Byte-Level Text Classifier},
129
+ year = {2026},
130
+ url = {https://huggingface.co/ThingAI/Glyph}
131
+ }
132
  ```
133
+
134
  ## License
135
+
136
  Apache 2.0
model.py CHANGED
@@ -1,4 +1,4 @@
1
- """ThingsLID model definition — standalone, no dependencies beyond PyTorch."""
2
 
3
  import torch
4
  import torch.nn as nn
@@ -62,7 +62,7 @@ class ConvBlock(nn.Module):
62
 
63
  class MultiTaskLID(nn.Module):
64
  """
65
- ThingsLID: Multi-task byte-level text classifier.
66
  ~4M shared parameters + per-task classification heads.
67
  """
68
  def __init__(self, task_configs, max_len=512, d_byte=64, d_tri=64,
 
1
+ """Glyph model definition — standalone, no dependencies beyond PyTorch."""
2
 
3
  import torch
4
  import torch.nn as nn
 
62
 
63
  class MultiTaskLID(nn.Module):
64
  """
65
+ Glyph: Multi-task byte-level text classifier.
66
  ~4M shared parameters + per-task classification heads.
67
  """
68
  def __init__(self, task_configs, max_len=512, d_byte=64, d_tri=64,