GautamKishore commited on
Commit
65799de
·
1 Parent(s): b87fca5

v0.1 release

Browse files
.gitattributes CHANGED
@@ -33,3 +33,6 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ *.onnx.data filter=lfs diff=lfs merge=lfs -text
37
+ *.onnx filter=lfs diff=lfs merge=lfs -text
38
+ *.onnx.data filter=lfs diff=lfs merge=lfs -text
README.md ADDED
@@ -0,0 +1,124 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: apache-2.0
3
+ language:
4
+ - multilingual
5
+ tags:
6
+ - content-classification
7
+ - byte-level
8
+ - onnx
9
+ - matryoshka
10
+ - lightweight
11
+ - classifier
12
+ pipeline_tag: text-classification
13
+ library_name: pico-type
14
+ ---
15
+
16
+ # pico-type
17
+
18
+ A tiny **byte-level multi-head content classifier** (~1.5M parameters) that classifies any content into **7 categories** simultaneously from raw bytes — no tokenizer, no pretrained embeddings.
19
+
20
+ ## Architecture
21
+
22
+ ```
23
+ ByteEmbed → Conv1D×3 → BiAttention×2 → Pool → Matryoshka Heads
24
+ ```
25
+
26
+ - **Byte-level**: operates directly on UTF-8 bytes, supports any language
27
+ - **Matryoshka heads**: 7 independent classification heads with 4 tiers (tiny/small/base/pro)
28
+ - **1.5M params**: fits in ~200KB ONNX, runs in <12ms on CPU
29
+ - **No tokenizer**: zero vocabulary dependencies
30
+
31
+ ## Classification Heads
32
+
33
+ | Head | Classes | Description |
34
+ |------|---------|-------------|
35
+ | **coarse** | 12 | text, code, link, image, file, config, markup, data, error, secret, archive, binary |
36
+ | **modality** | 8 | textual, binary_image, binary_archive, binary_executable, binary_document, binary_audio, binary_video, binary_other |
37
+ | **subtype** | 24 | json, yaml, toml, ini, csv, html, xml, markdown, sql, log, diff, dockerfile, etc. |
38
+ | **code_lang** | 62 | python, javascript, typescript, java, c, cpp, go, rust, swift, bash, sql, etc. |
39
+ | **text_lang** | 30 | en, es, fr, de, it, pt, ru, zh, ja, ko, ar, hi, etc. |
40
+ | **file_mime** | 90 | text/html, application/json, application/pdf, image/png, video/mp4, etc. |
41
+ | **risk** | 6 | api_key, jwt, password, email, phone, ssh_key (probabilities) |
42
+
43
+ ## Performance
44
+
45
+ Benchmarked on synthetic data (1000 samples, 1024 bytes max, base tier):
46
+
47
+ | Head | Accuracy |
48
+ |------|----------|
49
+ | coarse | 99.4% |
50
+ | modality | 100.0% |
51
+ | subtype | 95.4% |
52
+ | text_lang | 81.3% |
53
+ | file_mime | 100.0% |
54
+ | risk (mAP) | 90.5% |
55
+
56
+ - **Inference**: ~11ms per sample on CPU (ONNX)
57
+ - **Model size**: ~200KB (FP32 ONNX)
58
+
59
+ > **code_lang** shows 47.1% accuracy due to broad language coverage (62 classes). Performance improves with longer code sequences (>256 bytes).
60
+
61
+ ## Usage
62
+
63
+ ### CLI
64
+ ```bash
65
+ # Pipe content
66
+ echo "def hello(): pass" | picotype --pretty
67
+
68
+ # File
69
+ picotype --file document.txt
70
+
71
+ # Clipboard (macOS)
72
+ picotype --clip
73
+ ```
74
+
75
+ ### Python
76
+ ```python
77
+ from model.pico_type.labels import decode_output
78
+
79
+ # Run with ONNX session
80
+ result = {"coarse": "code", "modality": "textual", ...}
81
+ decoded = decode_output(result, tier="base")
82
+ ```
83
+
84
+ ### MCP Server
85
+ ```bash
86
+ PICOTYPE_MODEL_DIR=./checkpoints python -m model.pico_type.mcp_server
87
+ ```
88
+
89
+ ## Model Tiers
90
+
91
+ | Tier | Head Dim | Params | ONNX Size |
92
+ |------|----------|--------|-----------|
93
+ | tiny | 16 | 1.43M | 203 KB |
94
+ | small | 64 | 1.45M | 203 KB |
95
+ | base | 192 | 1.48M | 206 KB |
96
+ | pro | 576 | 1.56M | 202 KB |
97
+
98
+ All tiers share the same trunk; only the final linear layer differs per tier.
99
+
100
+ ## Deployment
101
+
102
+ ### HuggingFace Space
103
+ The [Gradio Space](https://huggingface.co/spaces/eulogik/pico-type) provides:
104
+ - Text input and file upload
105
+ - Real-time 7-head classification
106
+ - Tier selection (tiny/small/base/pro)
107
+
108
+ ### ONNX Runtime
109
+ ```python
110
+ import onnxruntime
111
+ session = ort.InferenceSession("picotype_base.onnx")
112
+ ```
113
+
114
+ ## Training
115
+
116
+ Trained on synthetic data (11 content buckets, 62 code languages, 30 text languages, 90 MIME types) using multi-task loss with 500 optimization steps.
117
+
118
+ - **Loss**: weighted cross-entropy (coarse) + binary cross-entropy (risk)
119
+ - **Optimizer**: AdamW (lr=1e-3, weight_decay=0.01)
120
+ - **GPU**: ~100ms/step on MPS, ~3.5s/step on CPU
121
+
122
+ ## License
123
+
124
+ Apache 2.0
gradio_app.py ADDED
@@ -0,0 +1,145 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """pico-type Gradio Space: classify content type, language, and risk."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+
7
+ import gradio as gr
8
+ import numpy as np
9
+
10
+ from model.pico_type.labels import ALL_HEADS, COARSE_LABELS, MODALITY_LABELS, SUBTYPE_LABELS, CODE_LANG_LABELS, TEXT_LANG_LABELS, FILE_MIME_LABELS, RISK_LABELS
11
+
12
+ LABEL_TABLES = {
13
+ "coarse": COARSE_LABELS,
14
+ "modality": MODALITY_LABELS,
15
+ "subtype": SUBTYPE_LABELS,
16
+ "code_lang": CODE_LANG_LABELS,
17
+ "text_lang": TEXT_LANG_LABELS,
18
+ "file_mime": FILE_MIME_LABELS,
19
+ "risk": RISK_LABELS,
20
+ }
21
+
22
+ MODEL_DIR = os.environ.get("PICOTYPE_MODEL_DIR", "checkpoints")
23
+
24
+
25
+ def _load_session(tier: str):
26
+ import onnxruntime as ort
27
+ path = os.path.join(MODEL_DIR, f"picotype_{tier}.onnx")
28
+ return ort.InferenceSession(path)
29
+
30
+
31
+ SESSIONS = {}
32
+
33
+
34
+ def _get_session(tier: str):
35
+ if tier not in SESSIONS:
36
+ SESSIONS[tier] = _load_session(tier)
37
+ return SESSIONS[tier]
38
+
39
+
40
+ def _softmax(x):
41
+ e = np.exp(x - np.max(x))
42
+ return e / e.sum()
43
+
44
+
45
+ def classify(text: str, tier: str) -> dict:
46
+ if not text.strip():
47
+ return {}
48
+ session = _get_session(tier)
49
+ text_bytes = text.encode("utf-8")[:1024]
50
+ ids = np.frombuffer(text_bytes, dtype=np.uint8).astype(np.int64)
51
+ seq_len = len(ids)
52
+ padded = np.zeros(1024, dtype=np.int64)
53
+ padded[:seq_len] = ids
54
+ mask = np.zeros(1024, dtype=np.bool_)
55
+ mask[:seq_len] = True
56
+ outs = session.run(None, {"input_ids": padded[None, :], "attention_mask": mask[None, :]})
57
+ result = {}
58
+ for name, logits in zip(ALL_HEADS, outs):
59
+ probs = _softmax(logits[0])
60
+ if name == "risk":
61
+ result[name] = {LABEL_TABLES[name][i]: float(probs[i]) for i in range(len(probs))}
62
+ else:
63
+ idx = int(np.argmax(probs))
64
+ result[name] = {"label": LABEL_TABLES[name][idx], "confidence": float(probs[idx])}
65
+ return result
66
+
67
+
68
+ def build_ui():
69
+ with gr.Blocks(title="pico-type", theme=gr.themes.Soft()) as demo:
70
+ gr.Markdown(
71
+ """
72
+ # pico-type 🔍
73
+ A tiny byte-level multi-head content classifier (~1.5M params).
74
+ Classifies content into **7 categories**: coarse type, modality, subtype, code language, text language, file MIME, and risk flags.
75
+ """
76
+ )
77
+
78
+ with gr.Row():
79
+ with gr.Column(scale=2):
80
+ text_input = gr.Textbox(
81
+ label="Input Content",
82
+ placeholder="Paste or type content to classify...",
83
+ lines=10,
84
+ )
85
+ with gr.Row():
86
+ tier_selector = gr.Radio(
87
+ choices=["tiny", "small", "base", "pro"],
88
+ value="base",
89
+ label="Model Tier",
90
+ )
91
+ submit_btn = gr.Button("Classify", variant="primary", scale=2)
92
+ clear_btn = gr.Button("Clear")
93
+
94
+ gr.Examples(
95
+ examples=[
96
+ ["def hello():\n print('Hello, world!')"],
97
+ ["The quick brown fox jumps over the lazy dog."],
98
+ ["<html><body><h1>Welcome</h1></body></html>"],
99
+ ["#!/usr/bin/env python3\nimport os\nprint('hello')"],
100
+ ["{\n \"name\": \"pico-type\",\n \"version\": \"0.1.0\"\n}"],
101
+ ["BEGIN:VCALENDAR\nVERSION:2.0\nEND:VCALENDAR"],
102
+ ],
103
+ inputs=[text_input],
104
+ label="Try these examples",
105
+ )
106
+
107
+ with gr.Column(scale=2):
108
+ output_labels = []
109
+ with gr.Tabs():
110
+ for head_name in ALL_HEADS:
111
+ with gr.Tab(head_name.replace("_", " ").title()):
112
+ lbl = gr.Label(
113
+ value={},
114
+ label=head_name.replace("_", " ").title(),
115
+ )
116
+ output_labels.append(lbl)
117
+
118
+ def handle_classify(text, tier):
119
+ result = classify(text, tier)
120
+ outputs = {}
121
+ for head in ALL_HEADS:
122
+ if head == "risk":
123
+ outputs[head] = result.get(head, {})
124
+ else:
125
+ outputs[head] = {result.get(head, {}).get("label", "unknown"): result.get(head, {}).get("confidence", 0)}
126
+ return [outputs[h] for h in ALL_HEADS]
127
+
128
+ submit_btn.click(
129
+ fn=handle_classify,
130
+ inputs=[text_input, tier_selector],
131
+ outputs=output_labels,
132
+ )
133
+
134
+ clear_btn.click(
135
+ fn=lambda: (""),
136
+ inputs=[],
137
+ outputs=[text_input],
138
+ )
139
+
140
+ return demo
141
+
142
+
143
+ if __name__ == "__main__":
144
+ demo = build_ui()
145
+ demo.launch()
picotype_base.onnx ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:3838b1a4a1e68d81c19f993898d77df735ad271e475a0476c0507a62c13d38fc
3
+ size 210599
picotype_base.onnx.data ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:eb904c90948fe7f881df48667315f98f13f3e48435dfa163a69062718ddd7f70
3
+ size 9045864
picotype_pro.onnx ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:4a9af6620c397ad257624491b99a8b93e5e38baaf6ee0f8972d1fa5bec8f3652
3
+ size 207216
picotype_pro.onnx.data ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:7d7c08dab03d86553c9ded98761dadda330eeace8ea603607531070c1721046f
3
+ size 9401856
picotype_small.onnx ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:9232c1f65799b31b88d0d4f02ffc4733103fee7c70b9a51ee1a2960b37ddd982
3
+ size 208138
picotype_small.onnx.data ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:14318aac319371512579ea4d49c966e0b7039dc44a21e74afaed65ecdcb871c6
3
+ size 8926720
picotype_tiny.onnx ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:0cc27c2fd9e255c387761a0db35d3d4155da947a7cf928731c7fa2684752c133
3
+ size 208088
picotype_tiny.onnx.data ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e86493b1c4b5a3045eb07c0c9a892d96151c32b02a2639a382186fd4579783e2
3
+ size 8882176
requirements.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ gradio>=4.0
2
+ onnxruntime>=1.15
3
+ numpy<2