Commit ·
65799de
1
Parent(s): b87fca5
v0.1 release
Browse files- .gitattributes +3 -0
- README.md +124 -0
- gradio_app.py +145 -0
- picotype_base.onnx +3 -0
- picotype_base.onnx.data +3 -0
- picotype_pro.onnx +3 -0
- picotype_pro.onnx.data +3 -0
- picotype_small.onnx +3 -0
- picotype_small.onnx.data +3 -0
- picotype_tiny.onnx +3 -0
- picotype_tiny.onnx.data +3 -0
- requirements.txt +3 -0
.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
|