Upload folder using huggingface_hub
Browse files- MODEL_CARD.md +229 -0
- docs/PLAN.md +191 -0
- walkthrough.md +459 -0
MODEL_CARD.md
ADDED
|
@@ -0,0 +1,229 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
+
- multi-head
|
| 13 |
+
- clipboard
|
| 14 |
+
pipeline_tag: text-classification
|
| 15 |
+
library_name: pico-type
|
| 16 |
+
co2_eq_emissions: 0.02
|
| 17 |
+
model-index:
|
| 18 |
+
- name: pico-type
|
| 19 |
+
results:
|
| 20 |
+
- task:
|
| 21 |
+
type: text-classification
|
| 22 |
+
name: Multi-Head Content Classification
|
| 23 |
+
metrics:
|
| 24 |
+
- type: accuracy
|
| 25 |
+
value: 0.998
|
| 26 |
+
name: Coarse Accuracy
|
| 27 |
+
- type: accuracy
|
| 28 |
+
value: 1.0
|
| 29 |
+
name: Modality Accuracy
|
| 30 |
+
- type: accuracy
|
| 31 |
+
value: 0.947
|
| 32 |
+
name: Subtype Accuracy
|
| 33 |
+
- type: accuracy
|
| 34 |
+
value: 0.387
|
| 35 |
+
name: Code Language Accuracy
|
| 36 |
+
- type: accuracy
|
| 37 |
+
value: 0.988
|
| 38 |
+
name: Text Language Accuracy
|
| 39 |
+
- type: accuracy
|
| 40 |
+
value: 1.0
|
| 41 |
+
name: File MIME Accuracy
|
| 42 |
+
- type: average_precision
|
| 43 |
+
value: 1.0
|
| 44 |
+
name: Risk Average Precision
|
| 45 |
+
---
|
| 46 |
+
|
| 47 |
+
# pico-type
|
| 48 |
+
|
| 49 |
+
**A tiny byte-level multi-head content classifier** (~1.5M parameters) built by [**eulogik**](https://eulogik.com).
|
| 50 |
+
|
| 51 |
+
Classifies any content into **7 categories** simultaneously from raw bytes in a single forward pass β no tokenizer, no pretrained embeddings, no GPU required.
|
| 52 |
+
|
| 53 |
+
## Model Details
|
| 54 |
+
|
| 55 |
+
| Property | Value |
|
| 56 |
+
|----------|-------|
|
| 57 |
+
| **Developer** | [eulogik](https://eulogik.com) |
|
| 58 |
+
| **Release Date** | June 2026 |
|
| 59 |
+
| **License** | Apache 2.0 |
|
| 60 |
+
| **Model Type** | Byte-level neural classifier |
|
| 61 |
+
| **Input** | Raw UTF-8 bytes (0β255), up to 1024 bytes |
|
| 62 |
+
| **Output** | 7 classification heads |
|
| 63 |
+
| **Parameters** | 1.43Mβ1.56M (4 Matryoshka tiers) |
|
| 64 |
+
| **ONNX Size** | ~200 KB (FP32) |
|
| 65 |
+
| **Inference** | ~5ms on CPU (base tier) |
|
| 66 |
+
| **Training Hardware** | Apple M4 (MPS), 16GB |
|
| 67 |
+
|
| 68 |
+
## Architecture
|
| 69 |
+
|
| 70 |
+
```
|
| 71 |
+
ByteEmbed(256β96d) β 3ΓConv1D(k=3,5,7) β 2ΓBiAttention(RoPE) β Pool β 7ΓMatryoshka Heads
|
| 72 |
+
```
|
| 73 |
+
|
| 74 |
+
- **Byte-level**: No tokenizer, no subword vocabulary, no pretrained embeddings
|
| 75 |
+
- **Matryoshka**: 4 tiered variants share the same trunk; head dimensions vary (16/64/192/576)
|
| 76 |
+
- **Multi-head**: 7 independent classification heads trained jointly with per-task loss weighting
|
| 77 |
+
|
| 78 |
+
## Classification Heads
|
| 79 |
+
|
| 80 |
+
| Head | Classes | Gated | Examples |
|
| 81 |
+
|------|---------|-------|----------|
|
| 82 |
+
| **coarse** | 12 | β | text, code, link, image, file, config, markup, data, error, secret, archive, binary |
|
| 83 |
+
| **modality** | 8 | β | textual, binary_image, binary_archive, binary_executable, binary_document, binary_audio, binary_video, binary_other |
|
| 84 |
+
| **subtype** | 24 | coarseβ{config,markup,data} | json, yaml, toml, ini, csv, html, xml, markdown, sql, log, diff, dockerfile |
|
| 85 |
+
| **code_lang** | 62 | coarse=code | python, javascript, typescript, java, c, cpp, go, rust, swift, kotlin, bash, sql, ruby, php, perl, lua, r, julia, haskell, scala, dart, elixir, clojure, erlang, zig, nim |
|
| 86 |
+
| **text_lang** | 30 | coarse=text | en, es, fr, de, it, pt, ru, zh, ja, ko, ar, hi, tr, nl, pl, sv, da, fi, nb, cs, hu, ro, uk, el, he, th, vi, id, ms, ta |
|
| 87 |
+
| **file_mime** | 90 | coarseβ{image,file} | text/html, application/json, application/pdf, image/png, image/jpeg, video/mp4, audio/mpeg, application/zip |
|
| 88 |
+
| **risk** | 6 | β | api_key, jwt, password, email, phone, ssh_key (independent probabilities) |
|
| 89 |
+
|
| 90 |
+
## Performance
|
| 91 |
+
|
| 92 |
+
### Synthetic Benchmark (1000 samples, base tier)
|
| 93 |
+
|
| 94 |
+
| Head | Accuracy | Support |
|
| 95 |
+
|------|----------|---------|
|
| 96 |
+
| coarse | 99.8% | 1000 |
|
| 97 |
+
| modality | 100.0% | 1000 |
|
| 98 |
+
| subtype | 94.7% | ~250 |
|
| 99 |
+
| code_lang | 38.7% | ~90 |
|
| 100 |
+
| text_lang | 98.8% | ~80 |
|
| 101 |
+
| file_mime | 100.0% | ~250 |
|
| 102 |
+
| risk (mAP) | 100.0% | β |
|
| 103 |
+
|
| 104 |
+
**Inference**: 5.0ms on M4 CPU via ONNX Runtime (base tier, 1024 bytes)
|
| 105 |
+
|
| 106 |
+
### Real-World Evaluation (21 hand-curated inputs)
|
| 107 |
+
|
| 108 |
+
| Category | Accuracy |
|
| 109 |
+
|----------|----------|
|
| 110 |
+
| Coarse | **86%** (18/21) |
|
| 111 |
+
| Modality | **100%** (21/21) |
|
| 112 |
+
| Overall | **52%** (11/21 exact match) |
|
| 113 |
+
|
| 114 |
+
_Diverse training improved real-world accuracy from 23% to 52%._
|
| 115 |
+
|
| 116 |
+
### Tier Comparison
|
| 117 |
+
|
| 118 |
+
| Tier | Dim | Params | ONNX Size | Speed |
|
| 119 |
+
|------|-----|--------|-----------|-------|
|
| 120 |
+
| tiny | 16 | 1.43M | 207 KB | ~3ms |
|
| 121 |
+
| small | 64 | 1.45M | 207 KB | ~4ms |
|
| 122 |
+
| base | 192 | 1.48M | 209 KB | ~5ms |
|
| 123 |
+
| pro | 576 | 1.56M | 206 KB | ~12ms |
|
| 124 |
+
|
| 125 |
+
## Training
|
| 126 |
+
|
| 127 |
+
The model is trained using a multi-task loss:
|
| 128 |
+
|
| 129 |
+
```
|
| 130 |
+
L = Ξ£ w_h Β· L_h
|
| 131 |
+
```
|
| 132 |
+
|
| 133 |
+
- **Single-label heads**: Cross-entropy with ignore_index=-100 for gated samples
|
| 134 |
+
- **Risk head**: Binary cross-entropy
|
| 135 |
+
- **Head weights**: coarse=8.0, modality=2.0, code_lang=2.0, text_lang=1.5, others=1.0
|
| 136 |
+
|
| 137 |
+
### Training Data
|
| 138 |
+
|
| 139 |
+
1. **Synthetic data** β 11 content buckets with per-language templates (62 code langs, 30 text langs, 90 MIME types, 22 binary formats)
|
| 140 |
+
2. **Diverse synthetic** β Realistic error traces, markdown with code blocks, nested configs, environment files, multi-language prose
|
| 141 |
+
3. **Real GitHub code** β ~650 files across 62 languages via GitHub Search API (30% training mix)
|
| 142 |
+
|
| 143 |
+
### Training Hyperparameters
|
| 144 |
+
|
| 145 |
+
| Parameter | Value |
|
| 146 |
+
|-----------|-------|
|
| 147 |
+
| Optimizer | AdamW (Ξ²β=0.9, Ξ²β=0.999) |
|
| 148 |
+
| Learning Rate | 3e-3 (cosine decay) |
|
| 149 |
+
| Warmup Steps | 200 |
|
| 150 |
+
| Weight Decay | 0.01 (trunk), 0.0 (heads) |
|
| 151 |
+
| Batch Size | 16 |
|
| 152 |
+
| Total Steps | 4000 |
|
| 153 |
+
| Gradient Clip | 1.0 |
|
| 154 |
+
| Hardware | Apple M4 16GB (MPS) |
|
| 155 |
+
|
| 156 |
+
## Usage
|
| 157 |
+
|
| 158 |
+
### CLI
|
| 159 |
+
```bash
|
| 160 |
+
pip install picotype
|
| 161 |
+
echo "def hello(): pass" | picotype --pretty
|
| 162 |
+
picotype --file document.txt
|
| 163 |
+
picotype --clip
|
| 164 |
+
```
|
| 165 |
+
|
| 166 |
+
### Python
|
| 167 |
+
```python
|
| 168 |
+
from picotype import PicoType, PicoTypeConfig, load_checkpoint, decode_output
|
| 169 |
+
|
| 170 |
+
model = PicoType(PicoTypeConfig()).eval()
|
| 171 |
+
load_checkpoint("checkpoints/best.pt", model)
|
| 172 |
+
logits = model(encode_bytes(b"input content"))
|
| 173 |
+
result = decode_output(logits)
|
| 174 |
+
```
|
| 175 |
+
|
| 176 |
+
### ONNX Runtime
|
| 177 |
+
```python
|
| 178 |
+
import onnxruntime as ort
|
| 179 |
+
session = ort.InferenceSession("picotype_base.onnx")
|
| 180 |
+
# input: int64[1, seq_len], bool[1, seq_len]
|
| 181 |
+
```
|
| 182 |
+
|
| 183 |
+
### MCP Server
|
| 184 |
+
```bash
|
| 185 |
+
PICOTYPE_MODEL_DIR=./checkpoints python -m model.pico_type.mcp_server
|
| 186 |
+
```
|
| 187 |
+
|
| 188 |
+
## Model Tiers
|
| 189 |
+
|
| 190 |
+
All tiers share the same trunk; only the Matryoshka head linears differ. You can switch tiers at inference time with zero overhead:
|
| 191 |
+
|
| 192 |
+
```python
|
| 193 |
+
logits_tiny = model(x, mask, tier="tiny")
|
| 194 |
+
logits_base = model(x, mask, tier="base")
|
| 195 |
+
```
|
| 196 |
+
|
| 197 |
+
## Deployment
|
| 198 |
+
|
| 199 |
+
| Platform | URL |
|
| 200 |
+
|----------|-----|
|
| 201 |
+
| HuggingFace Space | [eulogik/pico-type](https://huggingface.co/spaces/eulogik/pico-type) |
|
| 202 |
+
| HuggingFace Model | [eulogik/pico-type](https://huggingface.co/eulogik/pico-type) |
|
| 203 |
+
| GitHub | [eulogik/pico-type](https://github.com/eulogik/pico-type) |
|
| 204 |
+
| PyPI | `pip install picotype` |
|
| 205 |
+
| arXiv | [XXXX.XXXXX](https://arxiv.org/abs/XXXX.XXXXX) |
|
| 206 |
+
|
| 207 |
+
## Limitations
|
| 208 |
+
|
| 209 |
+
- **code_lang accuracy**: 62-way classification with minimal per-class support. Real GitHub data helps (+10% absolute) but authentic code patterns (imports, type hints, decorators) remain challenging.
|
| 210 |
+
- **Synthetic-only training**: Model overfits to template patterns. Diverse synthetic + real data significantly improves real-world robustness.
|
| 211 |
+
- **Max input length**: 1024 bytes. Longer content is truncated.
|
| 212 |
+
- **No fine-grained text understanding**: Designed for content classification, not NLP tasks like sentiment analysis or NER.
|
| 213 |
+
|
| 214 |
+
## Citation
|
| 215 |
+
|
| 216 |
+
```bibtex
|
| 217 |
+
@software{eulogik2026picotype,
|
| 218 |
+
author = {eulogik},
|
| 219 |
+
title = {pico-type: A Tiny Byte-Level Multi-Head Content Classifier},
|
| 220 |
+
year = {2026},
|
| 221 |
+
url = {https://github.com/eulogik/pico-type}
|
| 222 |
+
}
|
| 223 |
+
```
|
| 224 |
+
|
| 225 |
+
---
|
| 226 |
+
|
| 227 |
+
<div align="center">
|
| 228 |
+
<sub>Built by <a href="https://eulogik.com">eulogik</a> β AI infrastructure for developers.</sub>
|
| 229 |
+
</div>
|
docs/PLAN.md
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Final Plan: **pico-type** β A 3.5M Multi-Head Byte Classifier for Universal Content Type Detection
|
| 2 |
+
|
| 3 |
+
## 1. Executive Summary
|
| 4 |
+
|
| 5 |
+
**pico-type** is a tiny, fully open-source, byte-level multi-head classifier that takes any blob (clipboard text, file bytes, image header, etc.) and emits a structured label set in one forward pass. It ships as:
|
| 6 |
+
|
| 7 |
+
- A HuggingFace model (Apache-2.0) with multi-tier Matryoshka exports
|
| 8 |
+
- A Rust + Python CLI (`picotype`)
|
| 9 |
+
- A Gradio Space that doubles as an **MCP server** (callable from Claude, Cursor, VSCode)
|
| 10 |
+
- A browser extension (compete with ClipGate)
|
| 11 |
+
- A Raycast/Alfred extension
|
| 12 |
+
- A companion arXiv paper
|
| 13 |
+
|
| 14 |
+
**Why now / the gap:** Existing clipboard tools are either regex-only (ClipGate, 13 types) or LLM-powered (Clipboard_ai, needs Ollama, GB-scale). Existing tiny classifiers do one job (code-language, MIME, intent, PII). No model does **all of them in one sub-5MB forward pass with a multi-head output**.
|
| 15 |
+
|
| 16 |
+
## 2. Architecture
|
| 17 |
+
|
| 18 |
+
```
|
| 19 |
+
Inputs (β€1024 UTF-8 bytes, masked/padded)
|
| 20 |
+
β
|
| 21 |
+
ββ ByteEmbed (256 β 96d, learned; optional FFT-Rotate variant from Kathleen)
|
| 22 |
+
β
|
| 23 |
+
ββ 3Γ Conv1D block (kernel 3,5,7) + GELU + residual
|
| 24 |
+
β
|
| 25 |
+
ββ 2Γ Bidirectional attention block (d=192, 4 heads, RoPE ΞΈ=500k)
|
| 26 |
+
β [ablation: also test a Kathleen-style O(L) oscillator trunk
|
| 27 |
+
β for the sub-1MB mobile tier]
|
| 28 |
+
β
|
| 29 |
+
ββ Pool = [mean β max β std] β 576d shared trunk
|
| 30 |
+
β
|
| 31 |
+
ββ Matryoshka heads (independent Linear at 16/64/192/576 dims)
|
| 32 |
+
ββ h_coarse (12-way softmax) β primary type
|
| 33 |
+
ββ h_modality (8-way softmax) β textual / binary-image / binary-archive β¦
|
| 34 |
+
ββ h_subtype (24-way softmax) β JSON, YAML, CSV, HTML, β¦ (only if coarseβ{config,markup,data})
|
| 35 |
+
ββ h_code_lang (62-way softmax + "undetected") β if coarse=code
|
| 36 |
+
ββ h_text_lang (30-way softmax + "undetected") β if coarse=text
|
| 37 |
+
ββ h_file_mime (90-way softmax + "undetected") β if modalityβ{binary-*}
|
| 38 |
+
ββ h_risk (6-way sigmoid multi-label) β api_key, jwt, ssh_key, password, email, phone
|
| 39 |
+
```
|
| 40 |
+
|
| 41 |
+
**Output schema (always returned, with confidence):**
|
| 42 |
+
```json
|
| 43 |
+
{
|
| 44 |
+
"coarse": "code",
|
| 45 |
+
"modality":"textual",
|
| 46 |
+
"subtype": null,
|
| 47 |
+
"code_language": "python", // or "undetected"
|
| 48 |
+
"text_language": null,
|
| 49 |
+
"file_mime": null,
|
| 50 |
+
"risk_flags": [],
|
| 51 |
+
"confidence": 0.94,
|
| 52 |
+
"model_tier": "standard" // tiny | small | standard | full
|
| 53 |
+
}
|
| 54 |
+
```
|
| 55 |
+
|
| 56 |
+
**Size targets (Matryoshka tiering):**
|
| 57 |
+
| Tier | Dim | Params | FP32 | INT8 | Target device |
|
| 58 |
+
|---|---|---|---|---|---|
|
| 59 |
+
| `pico-type-tiny` | 16 | ~0.5M | 2 MB | 0.5 MB | MCU / IoT |
|
| 60 |
+
| `pico-type-small` | 64 | ~1.5M | 6 MB | 1.5 MB | Browser (WASM), mobile |
|
| 61 |
+
| `pico-type-base` | 192 | ~3.5M | 14 MB | 3.5 MB | Desktop CLI, browser ext |
|
| 62 |
+
| `pico-type-pro` | 576 | ~8M | 32 MB | 8 MB | Server / accurate use |
|
| 63 |
+
|
| 64 |
+
## 3. Training
|
| 65 |
+
|
| 66 |
+
**Framework:** Pure PyTorch (~600 LoC train script), no transformers dependency. Permissive.
|
| 67 |
+
|
| 68 |
+
**Distillation teachers (Polymorph-style per-head):**
|
| 69 |
+
- `microsoft/deberta-v3-small` (140M) β coarse + modality
|
| 70 |
+
- `huggingface/CodeBERTa-language-id` (84M) β code_lang
|
| 71 |
+
- `papluca/xlm-roberta-base-language-detection` (270M) β text_lang
|
| 72 |
+
- `mjbommar/magic-bert-50m-roformer-classification` (42M) β file_mime
|
| 73 |
+
- T=2.0, Ξ±=0.7 KD loss, plus hard-label CE on 30% of batch
|
| 74 |
+
|
| 75 |
+
**Data sources (all open / free):**
|
| 76 |
+
- Code: `bigcode/the-stack-v2` (multilingual), `cakiki/rosetta-code`
|
| 77 |
+
- Natural text: Wikipedia dumps, OSCAR, mC4 (subset)
|
| 78 |
+
- Configs/markup: GitHub `*.{json,yaml,toml,ini,xml,html,md,tex}` via BigQuery GH archive
|
| 79 |
+
- URLs: CommonCrawl WAT
|
| 80 |
+
- Errors: StackOverflow / GH issue bodies (regex-filtered for traceback patterns)
|
| 81 |
+
- File bytes: `mjbommar/binary-tokenizer-001-64k` corpora, plus synthetic magic-byte headers
|
| 82 |
+
- Images: 1 KB header slices from OpenImages V7
|
| 83 |
+
- Secrets: synthetic generation (regex + entropy filter), `gptmail-secret-detection` datasets
|
| 84 |
+
|
| 85 |
+
**Synthetic data generator:** 1 Python script producing balanced mixtures of: prose paragraphs, code snippets (multi-lang, snippets of 64β1023 bytes), config files, error traces, etc. Critical for head coverage.
|
| 86 |
+
|
| 87 |
+
**Training recipe:**
|
| 88 |
+
- Sequence length 1024 bytes
|
| 89 |
+
- AdamW, peak lr 3e-3, cosine, 5% warmup, 30 epochs
|
| 90 |
+
- bf16, batch 128, grad clip 1.0
|
| 91 |
+
- Task-balanced sampling: each head gets β₯1 sample per batch
|
| 92 |
+
- Loss = Ξ£_head w_h Β· L_h with w tuned on val
|
| 93 |
+
|
| 94 |
+
**Hardware:** Single A100 80GB, ~36 h total training (3 seeds).
|
| 95 |
+
|
| 96 |
+
## 4. Data β Label Pipeline
|
| 97 |
+
|
| 98 |
+
| Bucket | Source | Auto-labels |
|
| 99 |
+
|---|---|---|
|
| 100 |
+
| Code | The Stack v2 | `language` field, repo paths |
|
| 101 |
+
| Prose | Wikipedia, mC4 | `lang` metadata |
|
| 102 |
+
| Config/markup | GH files | file ext + content sniff |
|
| 103 |
+
| URL | CommonCrawl WAT | regex for `http(s)://` |
|
| 104 |
+
| Error | SO, GH issues | regex for `Traceback`, `Error:`, `at line` |
|
| 105 |
+
| File bytes | binary-tokenizer corpora | magic-byte first-32B lookup |
|
| 106 |
+
| Image header | OpenImages | first 32B β format (PNG/JPEG/WebP/β¦) |
|
| 107 |
+
| Secret | synthetic | regex + entropy β₯ 4.5 |
|
| 108 |
+
|
| 109 |
+
## 5. Distribution & Popularity
|
| 110 |
+
|
| 111 |
+
### 5.1 HuggingFace deliverables
|
| 112 |
+
- `pico-type/pico-type-base` β main model card with eval harness, ONNX int8/fp16, tract-onnx, GGUF
|
| 113 |
+
- `pico-type/pico-type-tiny` β¦ `-pro` β Matryoshka tiers
|
| 114 |
+
- `pico-type/picotype-space` β Gradio Space (also a **registered MCP server** on HF Hub β free distribution via `huggingface.co/mcp` proxy)
|
| 115 |
+
- `pico-type/picotype-eval` β public eval suite (reproducible JSON benchmarks)
|
| 116 |
+
- Datasets: `pico-type/synth-clipboard-v1`, `pico-type/eval-suite-v1`
|
| 117 |
+
|
| 118 |
+
### 5.2 Software deliverables
|
| 119 |
+
- **CLI** (`picotype`): Rust binary, ONNX runtime, sub-5ms inference. Reads stdin/file/clipboard, prints JSON.
|
| 120 |
+
- **MCP server** (`picotype-mcp`): stdio + Streamable HTTP, exposes tools: `classify`, `classify_batch`, `watch_clipboard` (macOS/Windows), `classify_history`
|
| 121 |
+
- **Browser extension** (Chrome/Firefox): replaces ClipGate's regex with pico-type; per-type icons; on-device, 100% local
|
| 122 |
+
- **Raycast extension** + Alfred workflow
|
| 123 |
+
- **VSCode extension**: paste-with-type, status bar shows type of selection
|
| 124 |
+
- **iOS Shortcut / Android Tasker** plug-in
|
| 125 |
+
|
| 126 |
+
### 5.3 Launch timeline (4β6 weeks)
|
| 127 |
+
| Week | Milestone |
|
| 128 |
+
|---|---|
|
| 129 |
+
| 1 | Repo scaffold, byte-level pipeline working on synthetic data, all heads wired |
|
| 130 |
+
| 2 | Real data ingest, distillation teachers downloaded, base tier trained, eval harness |
|
| 131 |
+
| 3 | All four Matryoshka tiers trained, ONNX/tract/GGUF exported, ablations complete |
|
| 132 |
+
| 4 | CLI (Rust), MCP server (Python+Gradio), HF Space + MCP registration, model card |
|
| 133 |
+
| 5 | Browser extension (MV3), Raycast/VSCode extensions, docs site, demo video |
|
| 134 |
+
| 6 | arXiv preprint, Show HN, r/MachineLearning, r/LocalLLaMA, r/programming launches |
|
| 135 |
+
|
| 136 |
+
### 5.4 Growth hooks
|
| 137 |
+
- First **MCP-native content classifier** β every Claude/Cursor/VSCode user is a potential user
|
| 138 |
+
- Free-tier on HF = automatic inference endpoint exposure
|
| 139 |
+
- Trending strategy: launch on a Tuesday, cross-post 4-5 channels same day, follow-up "vs ClipGate benchmarks" post
|
| 140 |
+
- Maintain a "What's new in pico-type" weekly digest β builds audience
|
| 141 |
+
- Open evaluation suite β community contributes new types (e.g., `meme_url`, `arxiv_id`, `semantic_version`) via a small "head-add" fine-tuning recipe
|
| 142 |
+
- Roadmap V2: adding a user-contributed `custom_types` head (LoRA per type) Γ la Polymorph
|
| 143 |
+
|
| 144 |
+
## 6. Repo structure (proposed)
|
| 145 |
+
|
| 146 |
+
```
|
| 147 |
+
pico-type/
|
| 148 |
+
βββ README.md
|
| 149 |
+
βββ LICENSE # Apache-2.0
|
| 150 |
+
βββ model/
|
| 151 |
+
β βββ pico_type/
|
| 152 |
+
β β βββ arch.py # ByteHybrid trunk + heads
|
| 153 |
+
β β βββ train.py # multi-task trainer
|
| 154 |
+
β β βββ distill.py # KD from per-head teachers
|
| 155 |
+
β β βββ data.py # synthetic generator + dataset
|
| 156 |
+
β β βββ export.py # ONNX, int8, tract, gguf
|
| 157 |
+
β β βββ eval.py # public eval harness
|
| 158 |
+
β βββ configs/ # tier configs
|
| 159 |
+
βββ crates/picotype/ # Rust CLI
|
| 160 |
+
βββ crates/picotype-mcp/ # Rust MCP server (or Python)
|
| 161 |
+
βββ extensions/
|
| 162 |
+
β βββ chrome/ # MV3
|
| 163 |
+
β βββ raycast/
|
| 164 |
+
β βββ alfred/
|
| 165 |
+
β βββ vscode/
|
| 166 |
+
βββ paper/ # arXiv LaTeX
|
| 167 |
+
βββ spaces/picotype/ # Gradio + MCP
|
| 168 |
+
βββ docs/ # mintlify or mkdocs
|
| 169 |
+
```
|
| 170 |
+
|
| 171 |
+
## 7. Key risks & mitigations
|
| 172 |
+
|
| 173 |
+
| Risk | Mitigation |
|
| 174 |
+
|---|---|
|
| 175 |
+
| Multi-head gradients fight (task interference) | Gradient norm clipping per-head, gating + scheduled dropout |
|
| 176 |
+
| 30 langs underperform on low-resource | Reuse PleIAs/CommonLingua approach, weight by data |
|
| 177 |
+
| Byte-level convs slow on long inputs | Cap at 1024 bytes, document limit, Matryoshka tiers |
|
| 178 |
+
| MCP ecosystem churns | Support stdio + Streamable HTTP (latest spec), drop SSE |
|
| 179 |
+
| Competition clones us | First-mover + multi-channel launch + arXiv paper + community eval suite |
|
| 180 |
+
|
| 181 |
+
## 8. Open decisions before I start coding
|
| 182 |
+
|
| 183 |
+
1. **Name lock**: `pico-type` confirmed. Handle: `pico-type` on HF? Or `picotype` (no dash)? I suggest `pico-type` for HF and `picotype` for the CLI to match existing naming patterns.
|
| 184 |
+
2. **Tier naming**: I propose `tiny`/`small`/`base`/`pro` matching Sentence-Transformers conventions.
|
| 185 |
+
3. **OSI license**: Apache-2.0 (matches CommonLingua base, patent grant). Confirm.
|
| 186 |
+
4. **arXiv target**: `cs.CL` (primary) + `cs.LG`. Co-authors: open question.
|
| 187 |
+
5. **Tagline** (for the model card): "One tiny model, one forward pass, every clipboard."
|
| 188 |
+
|
| 189 |
+
---
|
| 190 |
+
|
| 191 |
+
**Ready to execute when you give the green light.** I'll start by scaffolding the repo (`model/pico_type/arch.py` + `data.py` + minimal `train.py`) and getting a single-head baseline running on synthetic data β proving the byte-level pipeline + multi-head design before we invest in full data ingest. Estimated time-to-first-baseline: ~2 hours.
|
walkthrough.md
ADDED
|
@@ -0,0 +1,459 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# pico-type β Walkthrough
|
| 2 |
+
|
| 3 |
+
> Living document. Every agent/harness picking up this project should start here. Also, you must keep updating as you go further.
|
| 4 |
+
|
| 5 |
+
---
|
| 6 |
+
|
| 7 |
+
## 1. What this is
|
| 8 |
+
|
| 9 |
+
**pico-type** is a tiny (~1.5M params), byte-level, multi-head content classifier. Input: up to 1024 raw bytes (clipboard text, file bytes, image header, etc.). Output: structured label set in one forward pass.
|
| 10 |
+
|
| 11 |
+
Built per the locked-in plan in [`docs/PLAN.md`](docs/PLAN.md) (recovered from the original opencode session β see Β§11).
|
| 12 |
+
|
| 13 |
+
### Why this exists
|
| 14 |
+
Existing clipboard tools are regex-only (ClipGate, 13 types) or LLM-powered (needs Ollama, GB-scale). Existing tiny classifiers do one job. **No model does all of them in one sub-5MB forward pass with a multi-head output.**
|
| 15 |
+
|
| 16 |
+
### Deliverables
|
| 17 |
+
- HuggingFace model (Apache-2.0) with 4 Matryoshka tiers β
(ONNX exported)
|
| 18 |
+
- Python CLI (`picotype`) β
|
| 19 |
+
- Gradio Space app β
(`gradio_app.py` β label lists fixed Jun 18)
|
| 20 |
+
- Python MCP server β
(`model/pico_type/mcp_server.py`)
|
| 21 |
+
- Rust MCP server β
(`crates/picotype-mcp/`)
|
| 22 |
+
- pytest smoke tests β
(`tests/test_smoke.py`)
|
| 23 |
+
- HF model card β
(`MODEL_CARD.md`)
|
| 24 |
+
- Badge'd README β
|
| 25 |
+
- Rust CLI (`crates/picotype/`) β
|
| 26 |
+
- Chrome extension scaffold + icons β
(MV3)
|
| 27 |
+
- PyPI package `pico-type` v0.1.3 β
(`pip install pico-type`)
|
| 28 |
+
- arXiv paper β in progress (draft with final numbers)
|
| 29 |
+
- Raycast/Alfred/VSCode extensions β pending
|
| 30 |
+
|
| 31 |
+
---
|
| 32 |
+
|
| 33 |
+
## 2. Locked-in architecture (from `docs/PLAN.md` Β§2)
|
| 34 |
+
|
| 35 |
+
```
|
| 36 |
+
Inputs (β€1024 UTF-8 bytes, masked/padded)
|
| 37 |
+
β
|
| 38 |
+
ββ ByteEmbed (256 β 96d, learned)
|
| 39 |
+
β
|
| 40 |
+
ββ 3Γ Conv1D block (kernel 3, 5, 7) + GELU + residual β 192d
|
| 41 |
+
β
|
| 42 |
+
ββ 2Γ BiAttention block (d=192, 4 heads, RoPE ΞΈ=500k)
|
| 43 |
+
β
|
| 44 |
+
ββ Pool = [mean β max β std] β 576d shared trunk
|
| 45 |
+
β
|
| 46 |
+
ββ 7 Matryoshka heads (Linear at 16/64/192/576 dim slices)
|
| 47 |
+
ββ h_coarse (12) β primary type
|
| 48 |
+
ββ h_modality (8) β textual / binary-image / β¦
|
| 49 |
+
ββ h_subtype (24) β JSON/YAML/CSV/HTMLβ¦ (if coarse β {config, markup, data})
|
| 50 |
+
ββ h_code_lang (62) β if coarse=code, + "undetected" fallback
|
| 51 |
+
ββ h_text_lang (30) β if coarse=text, + "undetected" fallback
|
| 52 |
+
ββ h_file_mime (90) β if coarse β {image, file, archive, binary} or modality=binary_*, + "undetected"
|
| 53 |
+
ββ h_risk (6) β sigmoid multi-label: api_key, jwt, ssh_key, password, email, phone
|
| 54 |
+
```
|
| 55 |
+
|
| 56 |
+
### Tier matrix
|
| 57 |
+
| Tier | Dim slice | Params (actual) | INT8 size |
|
| 58 |
+
|---|---|---|---|
|
| 59 |
+
| `pico-type-tiny` | 16 | 1.43M | 1.37 MB |
|
| 60 |
+
| `pico-type-small` | 64 | 1.45M | 1.38 MB |
|
| 61 |
+
| `pico-type-base` | 192 | 1.48M | 1.41 MB |
|
| 62 |
+
| `pico-type-pro` | 576 | 1.56M | 1.49 MB |
|
| 63 |
+
|
| 64 |
+
Actual sizes came in **under** the plan's targets (0.5/1.5/3.5/8 MB INT8). Trunk dominates; if we need to shrink tiny further, reduce `trunk_dim` 192β128 or `num_attn_layers` 2β1.
|
| 65 |
+
|
| 66 |
+
### Output schema (always returned, with confidence)
|
| 67 |
+
```json
|
| 68 |
+
{
|
| 69 |
+
"coarse": "code",
|
| 70 |
+
"modality": "textual",
|
| 71 |
+
"subtype": null,
|
| 72 |
+
"code_language": "python",
|
| 73 |
+
"text_language": null,
|
| 74 |
+
"file_mime": null,
|
| 75 |
+
"risk_flags": [],
|
| 76 |
+
"confidence": 0.94,
|
| 77 |
+
"modality_confidence": 0.91,
|
| 78 |
+
"model_tier": "base"
|
| 79 |
+
}
|
| 80 |
+
```
|
| 81 |
+
|
| 82 |
+
---
|
| 83 |
+
|
| 84 |
+
## 3. Repo layout
|
| 85 |
+
|
| 86 |
+
```
|
| 87 |
+
classifier-model/
|
| 88 |
+
βββ .venv/ # Python 3.11 venv (torch, numpy<2, safetensors, pyyaml)
|
| 89 |
+
βββ .git/
|
| 90 |
+
βββ checkpoints/ # best.pt, ONNX models, eval results
|
| 91 |
+
βββ docs/
|
| 92 |
+
β βββ PLAN.md # Full architecture plan
|
| 93 |
+
βββ gradio_app.py # Gradio Space app
|
| 94 |
+
βββ model/
|
| 95 |
+
β βββ pico_type/
|
| 96 |
+
β βββ __init__.py # re-exports public API
|
| 97 |
+
β βββ labels.py # vocabularies + decode_output
|
| 98 |
+
β βββ arch.py # PicoType model
|
| 99 |
+
β βββ data.py # synthetic generator
|
| 100 |
+
β βββ train.py # multi-task trainer
|
| 101 |
+
β βββ eval.py # eval harness
|
| 102 |
+
β βββ distill.py # KD pipeline
|
| 103 |
+
β βββ export.py # ONNX export
|
| 104 |
+
β βββ cli.py # Python CLI (picotype)
|
| 105 |
+
β βββ mcp_server.py # MCP server (stdio)
|
| 106 |
+
βββ tests/
|
| 107 |
+
β βββ test_smoke.py # pytest smoke tests
|
| 108 |
+
βββ spaces/
|
| 109 |
+
β βββ requirements.txt # HF Space dependencies
|
| 110 |
+
βββ MODEL_CARD.md # HF model card
|
| 111 |
+
βββ README.md # badge'd README
|
| 112 |
+
βββ LICENSE # Apache-2.0
|
| 113 |
+
βββ pyproject.toml
|
| 114 |
+
βββ walkthrough.md # this file
|
| 115 |
+
```
|
| 116 |
+
|
| 117 |
+
---
|
| 118 |
+
|
| 119 |
+
## 4. Public API (current)
|
| 120 |
+
|
| 121 |
+
```python
|
| 122 |
+
from model.pico_type import (
|
| 123 |
+
PicoType, PicoTypeConfig, TIERS, # model
|
| 124 |
+
COARSE_LABELS, MODALITY_LABELS, SUBTYPE_LABELS,
|
| 125 |
+
CODE_LANG_LABELS, TEXT_LANG_LABELS,
|
| 126 |
+
FILE_MIME_LABELS, RISK_LABELS,
|
| 127 |
+
UNDETECTED, decode_output, # labels + decoder
|
| 128 |
+
)
|
| 129 |
+
from model.pico_type.arch import encode_bytes # bytes β (tokens, mask) tensors
|
| 130 |
+
```
|
| 131 |
+
|
| 132 |
+
### Smoke test
|
| 133 |
+
```bash
|
| 134 |
+
source .venv/bin/activate
|
| 135 |
+
python -m model.pico_type.arch
|
| 136 |
+
```
|
| 137 |
+
|
| 138 |
+
---
|
| 139 |
+
|
| 140 |
+
## 5. Key invariants (DO NOT break these)
|
| 141 |
+
|
| 142 |
+
These are tied to the locked-in plan and the on-disk checkpoints. Changing them means re-training + re-publishing.
|
| 143 |
+
|
| 144 |
+
### Vocab sizes (asserted in `labels.py`)
|
| 145 |
+
| Head | Size | Plan target |
|
| 146 |
+
|---|---|---|
|
| 147 |
+
| coarse | 12 | 12 β |
|
| 148 |
+
| modality | 8 | 8 β |
|
| 149 |
+
| subtype | 24 | 24 β |
|
| 150 |
+
| code_lang | 62 | 62 β |
|
| 151 |
+
| text_lang | 30 | 30 β |
|
| 152 |
+
| file_mime | 90 | 90 β |
|
| 153 |
+
| risk | 6 | 6 β |
|
| 154 |
+
|
| 155 |
+
### Gating (enforced in `decode_output`, must also be enforced in training loss)
|
| 156 |
+
- `subtype` is only valid when `coarse β {config, markup, data}`
|
| 157 |
+
- `code_lang` is only valid when `coarse == code`
|
| 158 |
+
- `text_lang` is only valid when `coarse == text`
|
| 159 |
+
- `file_mime` is only valid when `coarse β {image, file, archive, binary}` or `modality` starts with `binary_`
|
| 160 |
+
- `risk` is **always** valid (multi-label)
|
| 161 |
+
|
| 162 |
+
### `UNDETECTED` behavior
|
| 163 |
+
- For `code_lang` / `text_lang` / `file_mime`: the model has N logits (62/30/90), no separate "undetected" class. If `max_softmax < undetected_threshold` (default 0.4), the decoder returns `"undetected"` instead of the argmax label.
|
| 164 |
+
- For `risk`: per-class sigmoid; class is flagged if `sigmoid(logit) β₯ risk_threshold` (default 0.5).
|
| 165 |
+
|
| 166 |
+
### Matryoshka slicing
|
| 167 |
+
The shared trunk emits a 576d vector. Each `MatryoshkaHead` slices `x[..., :tier_dim]` then applies its tier-specific `nn.Linear(tier_dim, num_classes)`. All 4 tier linears live in the model (so a single checkpoint contains all tiers); at inference, **only the chosen tier's linears are loaded** β `parameter_count(tier)` reflects this.
|
| 168 |
+
|
| 169 |
+
### Byte input
|
| 170 |
+
- `0` is the pad byte (matches the 0th row of the embedding)
|
| 171 |
+
- `max_bytes` default 1024
|
| 172 |
+
- Inputs longer than `max_bytes` are **truncated** (not rejected)
|
| 173 |
+
- Mask is 1 for real bytes, 0 for pad β passed to attention and pool
|
| 174 |
+
|
| 175 |
+
---
|
| 176 |
+
|
| 177 |
+
## 6. Environment
|
| 178 |
+
|
| 179 |
+
```bash
|
| 180 |
+
# venv already created at .venv
|
| 181 |
+
source .venv/bin/activate
|
| 182 |
+
python --version # 3.11
|
| 183 |
+
python -c "import torch, numpy, safetensors, yaml; print('ok')"
|
| 184 |
+
```
|
| 185 |
+
|
| 186 |
+
**Python 3.14 was tried first but has no torch wheels β we use Python 3.11.** This is documented because the failed attempt is in the opencode session history.
|
| 187 |
+
|
| 188 |
+
---
|
| 189 |
+
|
| 190 |
+
## 7. What's done β
|
| 191 |
+
|
| 192 |
+
### Training, Deployment & Publishing
|
| 193 |
+
- **Training**: 1700 synthetic steps β 5000 synthetic steps (eval_loss 1.61) β **4000 real-data fine-tuning steps** (2 rounds: 10 samples/lang, then 25 samples/lang from GitHub). Eval loss **1.9848** after final round. best.pt updated.
|
| 194 |
+
- **Eval results (1000 samples, base tier, 5.6ms)**:
|
| 195 |
+
- coarse 100%, modality 100%, subtype **98.4%**, code_lang **53.9%**, text_lang **100%**, file_mime 100%, risk mAP 100%
|
| 196 |
+
- code_lang improved **+9.9%** (43.96% β 53.85%) via real GitHub code samples
|
| 197 |
+
- **ONNX export**: All 4 tiers re-exported from best.pt (~207-209 KB on disk with external data, opset 18).
|
| 198 |
+
- **HF Model**: `huggingface.co/eulogik/pico-type` β ONNX models at root level + checkpoints/ directory, model card, paper scaffold.
|
| 199 |
+
- **HF Space**: `huggingface.co/spaces/eulogik/pico-type` β Gradio app **label lists fixed** (Jun 18 2026). Root cause: `gradio_app.py` had different label ordering than `labels.py` (text_lang had `"ar","hi"` instead of `"id","ms"`; file_mime was completely different set). Also fixed `np.bool_`β`bool` for NumPy 2.x compat (Space runs Python 3.13).
|
| 200 |
+
- **PyPI**: `pico-type` v0.1.3 published at https://pypi.org/project/pico-type/0.1.3/.
|
| 201 |
+
- **GitHub**: `github.com/eulogik/pico-type` β `main` branch, CI passes.
|
| 202 |
+
|
| 203 |
+
### Known Training Issues
|
| 204 |
+
- **MPS OOM**: batch_size 64 causes MPS OOM (19+ GiB allocated). Fixed by reducing to batch_size=16 and `train_tiers=('base',)`.
|
| 205 |
+
- **MPS graph cache**: Writes to system `/tmp`, was filling disk when free space <1GB. ~9GB now available, OK.
|
| 206 |
+
- **code_lang stuck at ~44% on synthetic-only**: Real-data fine-tuning pushed it to **53.9%** with 25 samples/lang from GitHub. Further improvement likely needs more diverse real data (error messages, SQL, HTML, binary headers).
|
| 207 |
+
|
| 208 |
+
### HF Space fix (Jun 18)
|
| 209 |
+
- **Root cause**: `gradio_app.py` had different label list ordering than `model/pico_type/labels.py`. Model outputs numeric logits at specific indices, but Space app decoded them with wrong label lists:
|
| 210 |
+
- `text_lang`: had `"ar","hi"` at positions 26-27 instead of `"id","ms"`
|
| 211 |
+
- `file_mime`: completely different set and ordering (started with `"text/html"` instead of `"application/pdf"`)
|
| 212 |
+
- **Also fixed**: `np.bool_`β`bool` (removed in NumPy 2.x, needed for Space's Python 3.13)
|
| 213 |
+
- **Also fixed**: Removed unused `pathlib` import, removed `numpy<2` pin from Space requirements
|
| 214 |
+
- New ONNX files copied to model repo root level (Space downloads from root, not `checkpoints/` directory)
|
| 215 |
+
|
| 216 |
+
### `model/pico_type/labels.py`
|
| 217 |
+
- All 7 vocabularies (sizes match plan exactly, asserted at import time)
|
| 218 |
+
- `decode_output(logits, tier, undetected_threshold, risk_threshold)` β respects all gating rules, applies UNDETECTED threshold
|
| 219 |
+
- `HEAD_NUM_CLASSES` dict for heads that need to query class counts
|
| 220 |
+
- `label_for(head, idx)` helper
|
| 221 |
+
- Constants: `UNDETECTED`, `ALL_HEADS`, `SUBTYPE_GATED_BY`, `CODE_LANG_GATED_BY`, `TEXT_LANG_GATED_BY`, `FILE_MIME_GATED_BY`
|
| 222 |
+
|
| 223 |
+
### `model/pico_type/arch.py`
|
| 224 |
+
- `PicoTypeConfig` dataclass with all hyperparams
|
| 225 |
+
- `ByteEmbed` β `nn.Embedding(256, 96)` init normal std=0.02
|
| 226 |
+
- `ConvBlock` β `Conv1d β LayerNorm β GELU β Dropout`, residual via 1Γ1 projection when dims change
|
| 227 |
+
- `RotaryPosEmb` β precomputed cos/sin cache, auto-grows if seq exceeds cache
|
| 228 |
+
- `AttnBlock` β pre-norm, fused QKV, RoPE on Q/K, `F.scaled_dot_product_attention`, MLP w/ 4Γ expansion
|
| 229 |
+
- `Pool` β `mean β max β std` over masked positions (handles padding correctly)
|
| 230 |
+
- `MatryoshkaHead` β `nn.ModuleDict` of `nn.Linear` per tier
|
| 231 |
+
- `PicoType` β top-level model
|
| 232 |
+
- `encode_bytes(data, max_len, pad)` β `bytes β (LongTensor[B, L], LongTensor[B, L])`
|
| 233 |
+
- `smoke_test()` β instantiates model, runs a forward, returns param counts
|
| 234 |
+
- `__main__` block runs the smoke test
|
| 235 |
+
- **NaN fix in AttnBlock**: `F.scaled_dot_product_attention` with a boolean mask where all entries are False (sample has no padding) produces NaN on CPU. Fixed by converting to float (`-inf` for masked positions, 0 for valid) and guarding with `mask.all().item()`. See `AttnBlock.forward` for the guard.
|
| 236 |
+
|
| 237 |
+
### `model/pico_type/data.py`
|
| 238 |
+
- `SyntheticGenerator(seed)` β generates one balanced sample at a time from 11 buckets: code, text, config, markup, data, link, error, image, file, secret, archive, binary
|
| 239 |
+
- 11 generator methods (`_gen_code`, `_gen_text`, `_gen_config`, `_gen_markup`, `_gen_data`, `_gen_link`, `_gen_error`, `_gen_image`, `_gen_file`, `_gen_secret`, `_gen_archive`, `_gen_binary`)
|
| 240 |
+
- `Sample` dataclass with `data: bytes`, label fields (int for single-label heads, `list[int]` for risk), `IGNORE_INDEX = -100` for gated heads
|
| 241 |
+
- `SyntheticDataset(generator, size)` β wraps generator for `DataLoader` compatibility
|
| 242 |
+
- Code templates for all 62 languages across 18 syntax groups (Python-like, C-like, JS-like, Lisp-like, etc.) β uses `re.sub` with `${kind}` placeholders
|
| 243 |
+
- Word lists for all 30 text languages
|
| 244 |
+
- Binary magic-byte headers for PDF, ZIP, GZIP, ELF, SQLite, Parquet, TIFF, PNG, JPEG, WASM, DEB, TTF, plus archive formats (7z, RAR, TAR, XZ, BZ2)
|
| 245 |
+
- `_detect_risk` runs on text samples (AWS key, JWT, SSH key, password detection)
|
| 246 |
+
- `label_counts()` returns class distribution for debugging
|
| 247 |
+
- `smoke_test()` generates 500 samples and prints coverage per head
|
| 248 |
+
|
| 249 |
+
### `crates/picotype-mcp/` (new)
|
| 250 |
+
- Rust MCP server crate implementing JSON-RPC 2.0 over stdio transport
|
| 251 |
+
- Same protocol as Python MCP server: `initialize` handshake + `tools/call` for `classify` and `classify_file`
|
| 252 |
+
- Uses `ort` (ONNX Runtime) for inference, respects `PICOTYPE_MODEL_DIR` and `PICOTYPE_TIER` env vars
|
| 253 |
+
- Tested and working: builds in ~30s, passes `initialize` and `tools/call` requests
|
| 254 |
+
|
| 255 |
+
### `extensions/chrome/icons/` (new)
|
| 256 |
+
- 16x16, 48x48, 128x128 PNG icons with indigo rounded-square design and "P" letter
|
| 257 |
+
- Resolves missing icons referenced in `manifest.json`
|
| 258 |
+
|
| 259 |
+
### Training update (synthetic)
|
| 260 |
+
- Continued training from scratch (no prior checkpoints available) for 5000 steps
|
| 261 |
+
- Config: lr=5e-4, warmup=200, batch_size=16 (MPS), train_size=20000, eval_size=500
|
| 262 |
+
- Eval loss improved: 31.97 β 28.52 (step 0) β 1.97 (step 2500) β **1.61 (step 5000)**
|
| 263 |
+
- Per-head results (step 5000, synthetic-only): coarse=100%, modality=100%, subtype=98%, code_lang=44%, text_lang=97.5%, file_mime=100%, risk=100%
|
| 264 |
+
- code_lang stuck at ~44% β synthetic code templates too limited; confirmed need for real data
|
| 265 |
+
- Saved best.pt, final.pt, step_{500,1000,...,4500}.pt in checkpoints/
|
| 266 |
+
|
| 267 |
+
### Real-data fine-tuning
|
| 268 |
+
- `model/pico_type/realdata.py` β fetches real source code from GitHub Search API per language
|
| 269 |
+
- `model/pico_type/train_real.py` β mixed real+synthetic training (30% real), lr=3e-4, batch=16
|
| 270 |
+
- **Round 1** (10/lang, 2000 steps): code_lang 44%β**43.96%** (marginal)
|
| 271 |
+
- **Round 2** (25/lang, ~1500 samples, 2000 steps): code_lang **43.96%β53.85%** β
|
| 272 |
+
- Final eval (1000 samples, best.pt): coarse=100%, modality=100%, subtype=98.4%, code_lang=53.9%, text_lang=100%, file_mime=100%, risk=100%, inference=5.6ms
|
| 273 |
+
- 5 languages with 0 GitHub results: fsharp, lisp, vim, fortran, vb (unsupported search names)
|
| 274 |
+
|
| 275 |
+
### Real-data fine-tuning (new)
|
| 276 |
+
- `model/pico_type/realdata.py` β fetches real source code from GitHub Search API per language, builds mixed datasets
|
| 277 |
+
- `model/pico_type/train_real.py` β fine-tuning loop mixing real code + synthetic data (30% ratio), lower LR (3e-4)
|
| 278 |
+
- **Round 1**: 10 samples/language (423 total), 2000 steps β code_lang 41.7%β**43.96%**
|
| 279 |
+
- **Round 2**: 25 samples/language (~1500 total), 2000 steps β code_lang **43.96%β53.85%**
|
| 280 |
+
- GitHub token used, 0.5s delay/request to respect rate limits
|
| 281 |
+
- 5 languages returned 0 samples: fsharp, lisp, vim, fortran, vb (unsupported GitHub search names)
|
| 282 |
+
|
| 283 |
+
### Real-world eval
|
| 284 |
+
- 14 hand-curated samples across 10 coarse categories
|
| 285 |
+
- Overall accuracy: 71.4% (10/14)
|
| 286 |
+
- Correct: python, js, JSON, text, link, password, AWS key, SSH key, ZIP, bash
|
| 287 |
+
- Wrong: SQL (βtext), HTML (βcode), error trace (βtext), PNG header (βconfig)
|
| 288 |
+
- Confirms synthetic data gap: SQL, HTML, errors, and binary headers need real training data
|
| 289 |
+
|
| 290 |
+
### PyPI publish
|
| 291 |
+
- Bumped version from 0.1.0β0.1.2 (0.1.1 already existed)
|
| 292 |
+
- Built and uploaded to PyPI: `pico-type==0.1.2` at https://pypi.org/project/pico-type/0.1.2/
|
| 293 |
+
- **v0.1.3**: Published with best.pt (53.9% code_lang), updated eval results, paper scaffold
|
| 294 |
+
|
| 295 |
+
### `model/pico_type/__init__.py`
|
| 296 |
+
- Re-exports the public API (already present in the repo when we recovered)
|
| 297 |
+
|
| 298 |
+
### `model/pico_type/train.py`
|
| 299 |
+
- `TrainConfig` dataclass β lr, warmup, total_steps, batch_size, grad_clip, per-head weights, etc.
|
| 300 |
+
- `collate_fn(batch)` β pads variable-length samples, creates `input_ids`, `attention_mask`, `labels` dict
|
| 301 |
+
- `MultiTaskLoss(weights)` β CE per head (ignore_index=-100 for gated heads) + BCE for risk. Skips any head with zero valid labels in batch (returns 0.0). Applies per-head weights (coarse=3.0, modality=2.0, code_lang=1.5, text_lang=1.5, others=1.0).
|
| 302 |
+
- `get_lr(step, config)` β linear warmup β cosine decay
|
| 303 |
+
- `train(config)` β full training loop:
|
| 304 |
+
- `SyntheticGenerator` + `SyntheticDataset` for train/eval
|
| 305 |
+
- AdamW, separate param groups (trunk w/ weight_decay, Matryoshka heads w/o)
|
| 306 |
+
- BF16 AMP (CUDA) or FP32 (CPU/MPS)
|
| 307 |
+
- Gradient clipping at 1.0
|
| 308 |
+
- Logs every `log_every` steps, eval every `eval_every`, save every `save_every`
|
| 309 |
+
- Saves `best.pt` (lowest eval loss), `final.pt`, plus periodic `step_{N}.pt`
|
| 310 |
+
- `load_checkpoint(path, model, optimizer)` β loads state dict
|
| 311 |
+
- **Known issues fixed**: NaN in SDP with all-valid mask (use float `-inf` instead of boolean mask); NaN from CE on all-ignore labels (skip head); Python 3.14 has no torch wheels (use 3.11)
|
| 312 |
+
|
| 313 |
+
### `model/pico_type/eval.py`
|
| 314 |
+
- `EvalConfig` dataclass β checkpoint, tier, eval_size, batch_size
|
| 315 |
+
- `evaluate(config)` β generates synthetic eval set, runs forward pass for all 7 heads
|
| 316 |
+
- Per-head `HeadMetrics`: accuracy, per-class precision/recall/F1, confusion matrix
|
| 317 |
+
- `RiskMetrics`: per-class average precision (sklearn-free implementation)
|
| 318 |
+
- `run_eval()` β CLI: `python -m model.pico_type.eval --eval-size 1000 --checkpoint checkpoints/best.pt`
|
| 319 |
+
- `_average_precision(y_true, y_scores)` β area under PR curve via trapezoidal rule
|
| 320 |
+
|
| 321 |
+
### Hindi support + v0.1.4 (Jun 18)
|
| 322 |
+
- Replaced `ms` (Malay, index 29) with `hi` (Hindi) in `TEXT_LANG_LABELS`. Added Hindi word list to `_TEXT_WORDS` in `data.py`.
|
| 323 |
+
- Fine-tuned 800 steps β Hindi text now correctly detected as `text_lang=hi`.
|
| 324 |
+
- code_lang improved to **61.3%** on synthetic eval after Hindi fine-tune (unexpected side benefit).
|
| 325 |
+
- Published as **v0.1.4** at https://pypi.org/project/pico-type/0.1.4/
|
| 326 |
+
|
| 327 |
+
### Diverse synthetic training + real-world eval improvement (Jun 18)
|
| 328 |
+
- Created diverse generator: patched `_gen_config`, `_gen_markup`, `_gen_text`, `_gen_error` with realistic patterns (markdown with code blocks, Python tracebacks, JS errors, nested YAML, env vars, tech prose, emails, conversations).
|
| 329 |
+
- **Real-world eval accuracy**: 22.7% β **52.4%** (2.3x better, 11/21 correct)
|
| 330 |
+
- coarse classification fixed for config, error, text, markup
|
| 331 |
+
- code_lang still "undetected" for real snippets (Python `def`, JS `function`, C `#include`, etc.)
|
| 332 |
+
- Key change: coarse head weight increased to **8.0** (was 3.0) to prevent "default to code" behavior
|
| 333 |
+
- Synthetic eval regression: coarse 100%β99.8%, code_lang 61.3%β38.7%, text_lang 100%β98.8% (expected β harder data)
|
| 334 |
+
|
| 335 |
+
### ONNX re-export + v0.1.5 (Jun 18)
|
| 336 |
+
- Re-exported all 4 ONNX tiers from best.pt (diverse-trained checkpoint)
|
| 337 |
+
- Published as **v0.1.5** at https://pypi.org/project/pico-type/0.1.5/
|
| 338 |
+
- ONNX files pushed to HF model repo root, Space redeployed
|
| 339 |
+
|
| 340 |
+
### Quantization attempts (deferred)
|
| 341 |
+
- **INT8**: ONNX shape inference fails β `[ShapeInferenceError] Inferred shape (192) vs (12)`. Multi-head architecture (shared 192-dim pooled vector β 7 linear layers with different output dims) confuses shape inference.
|
| 342 |
+
- **FP16**: `onnxconverter_common.float16` succeeds but produces type mismatch errors in ONNX Runtime. Could not resolve with `op_block_list`.
|
| 343 |
+
|
| 344 |
+
### Fresh training from scratch (in progress)
|
| 345 |
+
- Started training from scratch with diverse generator + real GitHub data (30% ratio) + high coarse weight (8.0)
|
| 346 |
+
- 4000 steps, batch_size=16, 15 samples/lang from 62 languages
|
| 347 |
+
- Located at: `checkpoints/fresh/train.log`
|
| 348 |
+
|
| 349 |
+
---
|
| 350 |
+
|
| 351 |
+
## 8. What's next (from plan Β§3βΒ§6, in order)
|
| 352 |
+
|
| 353 |
+
| # | File | What it does | Status |
|
| 354 |
+
|---|---|---|---|---|---|
|
| 355 |
+
| 1 | `data.py` | Synthetic generator + dataset for multi-head training. 11 buckets, all 12 coarse classes, code/word templates for all 62/30 langs. | β
**done** |
|
| 356 |
+
| 2 | `train.py` | Multi-task trainer. AdamW + cosine, bf16, per-head loss weighting, gradient clipping, checkpoint save/load. resume_from field for continuing training. | β
**done** |
|
| 357 |
+
| 3 | `eval.py` | Eval harness: per-head accuracy/PRF1, confusion matrix, risk AP, inference timing. CLI entry point. | β
**done** |
|
| 358 |
+
| 4 | `distill.py` | KD from per-head teachers (deberta-v3-small, CodeBERTa-lang-id, xlm-roberta-lang-detect). T=2.0, Ξ±=0.7. | β
**done** |
|
| 359 |
+
| 5 | `export.py` | ONNX export (opset 18), int8, tract, gguf. | β
**done** |
|
| 360 |
+
| 6 | `cli.py` | Python CLI (`picotype`) β stdin/file/clipboard input β ONNX inference β JSON output | β
**done** |
|
| 361 |
+
| 7 | `mcp_server.py` | MCP server (stdio transport) for Claude/Cursor/VSCode | β
**done** |
|
| 362 |
+
| 8 | `gradio_app.py` | Gradio Space app for HF Spaces | β
**done** |
|
| 363 |
+
| 9 | `tests/test_smoke.py` | pytest smoke tests (8 tests: arch, data, ONNX, CLI, labels) | β
**done** |
|
| 364 |
+
| 10 | `MODEL_CARD.md` | HuggingFace model card with eval results | β
**done** |
|
| 365 |
+
| 11 | `README.md` | Overhauled with badges, perf table, deploy links | β
**done** |
|
| 366 |
+
| 12 | `spaces/requirements.txt` | Dependencies for HF Space deployment | β
**done** |
|
| 367 |
+
| 13 | HF Model + Space | Published to huggingface.co/eulogik/pico-type (model) and /spaces/eulogik/pico-type (Space) | β
**done** |
|
| 368 |
+
| 14 | PyPI publish | pico-type v0.1.0βv0.1.2βv0.1.3 on PyPI | β
**done** |
|
| 369 |
+
| 15 | `crates/picotype/` | Rust CLI w/ ONNX runtime. | β
**done** |
|
| 370 |
+
| 16 | `crates/picotype-mcp/` | Rust MCP server (stdio). | β
**done** |
|
| 371 |
+
| 17 | `extensions/chrome/` | Chrome MV3 scaffolded + icons created. Raycast, Alfred, VSCode β pending. | partial |
|
| 372 |
+
| 18 | `paper/` | arXiv LaTeX scaffolded, final numbers updated (`paper/main.tex`). | in progress |
|
| 373 |
+
| 19 | Training | 1700 β **5000 synthetic steps** (loss 1.61) β **4000 real-data steps** (25/lang, code_lang 53.9%). | β
**done** |
|
| 374 |
+
| 20 | ONNX re-export | All 4 tiers re-exported from best.pt (post real-data fine-tune). | β
**done** |
|
| 375 |
+
| 21 | Real-data pipeline | `realdata.py` + `train_real.py` β GitHub code fetcher, mixed dataset, fine-tuning loop. | β
**done** |
|
| 376 |
+
| 22 | HF Space fix | Label lists synced to `labels.py`, `np.bool_`β`bool` for NumPy 2, requirements updated. | β
**done** |
|
| 377 |
+
| 23 | INT8 quantization | Shape inference issue with multi-head architecture β needs graph fix. | pending |
|
| 378 |
+
| 24 | HuggingFace Hub push | ONNX, best.pt, eval results, paper, README all pushed. | β
**done** |
|
| 379 |
+
|
| 380 |
+
---
|
| 381 |
+
|
| 382 |
+
## 9. Open decisions (from plan Β§8 β still open)
|
| 383 |
+
|
| 384 |
+
1. **HF handle**: `pico-type` (dash) for model card, `picotype` (no dash) for CLI binary. **Proposed, not confirmed.**
|
| 385 |
+
2. **Tier naming**: `tiny`/`small`/`base`/`pro` (matches Sentence-Transformers convention). **Proposed, not confirmed.**
|
| 386 |
+
3. **License**: Apache-2.0 (matches CommonLingua base). **Proposed, not confirmed.**
|
| 387 |
+
4. **arXiv target**: `cs.CL` (primary) + `cs.LG`. Co-authors: open question.
|
| 388 |
+
5. **Tagline**: *"One tiny model, one forward pass, every clipboard."* **Proposed, not confirmed.**
|
| 389 |
+
|
| 390 |
+
---
|
| 391 |
+
|
| 392 |
+
## 10. Quick recipes
|
| 393 |
+
|
| 394 |
+
### Instantiate the model
|
| 395 |
+
```python
|
| 396 |
+
from model.pico_type import PicoType, PicoTypeConfig
|
| 397 |
+
|
| 398 |
+
cfg = PicoTypeConfig(max_bytes=1024)
|
| 399 |
+
model = PicoType(cfg)
|
| 400 |
+
print(model.tier_sizes()) # {tiny: 1434344, small: 1445480, base: 1475176, pro: 1564264}
|
| 401 |
+
```
|
| 402 |
+
|
| 403 |
+
### Run a forward + decode
|
| 404 |
+
```python
|
| 405 |
+
from model.pico_type.arch import encode_bytes
|
| 406 |
+
from model.pico_type.labels import decode_output
|
| 407 |
+
|
| 408 |
+
model.eval()
|
| 409 |
+
x, mask = encode_bytes(b'def hi(): return 1', max_len=1024)
|
| 410 |
+
with torch.no_grad():
|
| 411 |
+
logits = model(x, mask, tier='base')
|
| 412 |
+
out = decode_output(logits, tier='base')
|
| 413 |
+
# {'coarse': ..., 'modality': ..., 'subtype': ..., 'code_language': ...,
|
| 414 |
+
# 'text_language': ..., 'file_mime': ..., 'risk_flags': [...],
|
| 415 |
+
# 'confidence': ..., 'modality_confidence': ..., 'model_tier': 'base'}
|
| 416 |
+
```
|
| 417 |
+
|
| 418 |
+
### Convert to a single-tier checkpoint (for release)
|
| 419 |
+
Use `parameter_count(tier)` to get the param count for that tier. To build a release checkpoint, you would: train full model β for each tier, save only `trunk.*` + `heads.*.linears.{tier}.*` β export.
|
| 420 |
+
|
| 421 |
+
---
|
| 422 |
+
|
| 423 |
+
## 11. Session history (why this file exists)
|
| 424 |
+
|
| 425 |
+
The user was working on this project in opencode. The session (`ses_16dd3d39fffer9xnBUQYBS3u5z` β *"Tiny model for clipboard content classification"*) **crashed mid-execution** while writing `model/pico_type/arch.py`. Opencode had to be re-installed; the user thought files might be lost.
|
| 426 |
+
|
| 427 |
+
**They weren't.** The full session data was recovered intact from:
|
| 428 |
+
- `~/.local/share/opencode/opencode.db` (262MB SQLite)
|
| 429 |
+
- `~/.local/share/opencode/storage/session_diff/ses_16dd3d39fffer9xnBUQYBS3u5z.json` (128MB JSON)
|
| 430 |
+
- The trash (`~/.Trash/opencode`) only contained **opencode Desktop app data** (different product, irrelevant to the CLI session).
|
| 431 |
+
|
| 432 |
+
From the recovery we:
|
| 433 |
+
- Extracted the full plan β `docs/PLAN.md`
|
| 434 |
+
- Re-wrote `arch.py` (the file the crashed session was aborting on) and `labels.py` (never written in original session)
|
| 435 |
+
- Wrote this `walkthrough.md` so the next agent/harness has full context
|
| 436 |
+
|
| 437 |
+
### User's 3 original prompts
|
| 438 |
+
1. *"I want to build a really tiny model which categorises/classifies content. eg if we pass clipboard copied content, it should classify that as text, image, rich text, link, code(with language name), file with file type etc. Deep research the existing models on huggingface etc. go through new research papers and find an opportunity / gap to make this model in the most efficient manner plus make it really popular. feel free to suggest anything"*
|
| 439 |
+
2. *"continue asking questions and ahead. btw, the text language should be identified too like code language. if language not detected, it should simply return text + undetected or code undetected"*
|
| 440 |
+
3. *"go"*
|
| 441 |
+
|
| 442 |
+
### Where the original session was at crash
|
| 443 |
+
- 23 messages, 95 parts
|
| 444 |
+
- Plan had been finalized (10KB markdown)
|
| 445 |
+
- Approved with "go"
|
| 446 |
+
- Switched to `build` agent
|
| 447 |
+
- Set up Python 3.11 venv, installed torch/numpy/safetensors/pyyaml
|
| 448 |
+
- Created the full directory tree (`model/pico_type/`, `model/configs/`, `crates/picotype/`, etc.)
|
| 449 |
+
- Wrote `model/pico_type/__init__.py` β
|
| 450 |
+
- **Wrote `arch.py`** (the file the user originally saw being written) β wait, the original session was *aborted* on the arch.py write. The `__init__.py` is in the repo. We re-wrote arch.py from scratch using the plan + the small preview from the original write tool input.
|
| 451 |
+
|
| 452 |
+
---
|
| 453 |
+
|
| 454 |
+
## 12. Anti-patterns to avoid (learned)
|
| 455 |
+
|
| 456 |
+
- **Python 3.14 has no torch wheels.** Always use the venv's Python 3.11.
|
| 457 |
+
- **Don't double-count Matryoshka head params** when iterating `named_modules()` β `ModuleDict` is visited separately from its children. Use `named_parameters()` and check `.linears.{tier}.` in the name.
|
| 458 |
+
- **Gating heads are not always-on.** `subtype`, `code_lang`, `text_lang`, `file_mime` must mask their loss when not applicable. The decoder handles this; the trainer must too.
|
| 459 |
+
- **`UNK`/undetected is a decoder-side decision**, not a model class. The model has N logits; the decoder thresholds.
|