changh95 commited on
Commit
cdb3d2b
·
verified ·
1 Parent(s): 28300d9

Add files using upload-large-folder tool

Browse files
Files changed (2) hide show
  1. README.md +34 -144
  2. tt_kernel_manifest.json +2 -2
README.md CHANGED
@@ -1,30 +1,16 @@
1
  ---
2
  tags:
3
  - blackhole
4
- - open-vocabulary
5
  - p150
6
- - qwen2.5
7
- - tenstorrent
8
  - tt-dit-server
9
- - tt-metal
10
  - tt-model-cache
11
- - tt-model-catalog
12
  - tt-model-container
13
- - tt-nn
14
- - ttnn
15
- - visual-grounding
16
- - vlm
17
- base_model:
18
- - nvidia/LocateAnything-3B
19
- license: other
20
- license_name: nvidia-license
21
- license_link: https://huggingface.co/nvidia/LocateAnything-3B/blob/main/LICENSE
22
- pipeline_tag: object-detection
23
  ---
24
 
25
  # locate-anything-3b-p150
26
 
27
- NVIDIA LocateAnything-3B (MoonViT-SO-400M vision tower + Qwen2.5-3B LLM with a detection vocabulary) doing visual grounding / open-vocabulary detection on a single Tenstorrent Blackhole p150a via tt-nn: image + free-text query -> labelled boxes. Vision tower and LLM both run on the chip; greedy AR decode with trace replay reaches ~38 tok/s, ~53 ms vision, ~64 ms prefill, ~2.3 frames/s end-to-end at PCC 0.9928 vs the torch reference. Weights are fetched from nvidia/LocateAnything-3B (NVIDIA license, 'other' - not OSI; read it before use); the port code (Apache-2.0 SPDX headers) is redistributed under the same upstream terms. Port source: github.com/changh95/tt-locate-anything @ 7a3b7407c4f1268cfb14ce1dd0ed4cdcd877b9a1.
 
28
 
29
  Runs on **p150** (mesh `P150`).
30
 
@@ -39,153 +25,57 @@ tt-model serve changh95/locate-anything-3b-p150
39
 
40
  `pull --with-weights` downloads the Docker image and the [`nvidia/LocateAnything-3B`](https://huggingface.co/nvidia/LocateAnything-3B) weights at `c32291ca5e996f5a7a485845b4f57a233936bba0` (into your HF cache; they are not in the image). `serve` starts the model's own HTTP server on port 20000 (or the next free port, if that one is busy); the first start compiles kernels for your device, which takes several minutes, and the server is ready when it logs `Application startup complete`.
41
 
42
- ### tt-cli users
43
 
44
  ```bash
45
- tt serve changh95/locate-anything-3b-p150 # pulls image + weights, boots, prints the port
 
 
46
  tt model stop changh95/locate-anything-3b-p150
47
  ```
48
 
49
- `tt-model stop changh95/locate-anything-3b-p150` does the same without tt-cli.
 
50
 
51
- ### What this server is (and is not)
52
 
53
- The port is the one `serve` printed (20000, or the next free one). This is the model's
54
- own HTTP API, **not** an OpenAI-compatible one: `curl <endpoint>/v1/models` only returns a
55
- stub so the ready card does not 404, and `tt-model curl` does not apply. The routes are:
 
 
 
 
56
 
57
- | route | what |
58
- |---|---|
59
- | `GET /health` | `{"status": "ok" \| "starting", "model", "device"}` - `ok` only after warm-up |
60
- | `GET /info` | model, weights repo + revision loaded, source commit, input limits, license |
61
- | `POST /predict` | one image + one query -> labelled boxes (JSON) |
62
 
63
- ```bash
64
- PORT=20000 # the port serve printed
65
- curl -s localhost:$PORT/health
66
- curl -s localhost:$PORT/info | python -m json.tool
67
-
68
- # media/demo_input.png (1920x1080) with the README's query "car"; several categories are
69
- # joined with </c>, e.g. "person</c>car". The PNG is 3.6 MB (4.8 MB as base64), far above
70
- # the shell's 128 KiB per-argument limit, so an inline "$(base64 -w0 ...)" fails with
71
- # "Argument list too long": write the body to a file and post it with -d @file.
72
- python3 - <<'EOF'
73
- import base64, json
74
- img = base64.b64encode(open("media/demo_input.png", "rb").read()).decode()
75
- json.dump({"image": img, "query": "car", "max_new_tokens": 128}, open("/tmp/la_req.json", "w"))
76
- EOF
77
- curl -s localhost:$PORT/predict -H 'Content-Type: application/json' -d @/tmp/la_req.json \
78
- | python3 -m json.tool
79
- ```
80
 
81
- Request fields: `image` (base64 PNG/JPEG, exactly one image), `query` (required free text,
82
- 1-1000 chars; join categories with `</c>`), `max_new_tokens` (optional, default 128, cap 1024;
83
- each box costs ~6-8 tokens plus its label), `return_overlay` (optional bool; adds
84
- `overlay_png_b64`, the boxes drawn on your image).
85
-
86
- Response: `detections: [{label, box: [x1, y1, x2, y2], box_norm: [..0..1000..]}]` with `box`
87
- in ORIGINAL image pixels, `points` (the model's 2-coordinate outputs, same shape), `raw_text`
88
- (the generated `<ref>car</ref><box><282><414><606><794></box><|im_end|>` string for the demo image),
89
- `width`/`height`, `canonical_size` and `grid_hw` (what the model actually saw, see below),
90
- `prompt_tokens`, `num_generated_tokens`, `stopped_on_eos`, `decode_mode`
91
- (`ar_greedy_trace`), and `timing_ms` (`vision`, `prefill`, `decode`, `total`, `decode_tok_s`).
92
- Errors: 400 for a bad image / empty query / prompt too long, 503 while starting, 500 with
93
- the exception text otherwise. One request at a time is served (batch 1, stateful KV cache);
94
- concurrent calls queue.
95
-
96
- A one-line check that the hardware phase also runs:
97
- `python code/locate_anything/server/smoke_test.py --url http://127.0.0.1:$PORT` prints
98
- `PASS ...` with the box count and timings.
99
-
100
- ### Input handling
101
-
102
- Every image is preprocessed like the HF processor at `LA_IN_TOKEN_LIMIT=1024` (all README
103
- numbers use this cap; upstream's default is 25600) and then squash-resized onto ONE canonical
104
- vision grid so MoonViT is built once: by default the grid the demo image (16:9) gets at that
105
- cap, 24x44 patches = 336x616 px = 264 image tokens. Boxes are normalized over that view, so
106
- they map back to your original pixels exactly; non-16:9 images are distorted before the
107
- model sees them. The grid is fixed for the life of the server and comes from the
108
- `LA_CANON_GRID=HxW` environment variable (even numbers of 14-px patches, e.g. `32x32` for
109
- square inputs). `tt-model serve` passes the container only the manifest's `serve.env`
110
- (there is no `--env` flag), so this package always serves the 24x44 grid; to serve another
111
- one, run the app on a host with tt-metal (see `SERVING.md`) or repackage with the variable
112
- added to `serve.env` in `tt-model.yaml`.
113
-
114
- ### First boot
115
-
116
- - `tt-model serve` downloads ~7.7 GB of weights (2 safetensors shards + tokenizer/config)
117
- into your HF cache before the container starts.
118
- - Inside the container the app writes a vanilla Qwen2.5-3B checkpoint (6.8 GB) to
119
- `~/.cache/tt-model/locate-anything-3b-p150/weights/la-qwen2_5-3b` (once), converts the
120
- LLM weights to BFP8 into `.../tensors/P150/tensor_cache_bfp8` (once), JIT-compiles kernels
121
- into `.../cache` (once), uploads MoonViT and runs two warm-up passes (prefill + 4 decode
122
- steps; the decode trace is captured on the first). Measured on the validation host: a
123
- cold first boot is a few minutes (extraction ~5 s, BFP8 conversion ~25 s, JIT + warm-up
124
- ~1-2 min), later boots ~15 s; `tt-model logs -f changh95/locate-anything-3b-p150`
125
- shows `Loading weights`, `Warming up`, `Warmup complete`. Host RAM peak ~10 GB.
126
- - Disk: 7.7 GB (HF cache) + 6.8 GB (extracted LLM) + 4.3 GB (BFP8 tensor cache) + ~0.4 GB JIT cache.
127
- - The weights repo is public and ungated; no `hf auth login` is needed for it.
128
-
129
- ### Results (from the port README; single p150a, everything on device, warm, trace decode)
130
-
131
- | Input (`media/demo_input.png`) | Greedy AR decode (`media/demo_ar.png`) |
132
  |:---:|:---:|
133
  | ![](media/demo_input.png) | ![](media/demo_ar.png) |
134
 
135
- Accuracy (PCC vs the torch-CPU golden, gate >= 0.99):
136
 
137
- | Stage | PCC |
138
  |---|---:|
139
- | Vision `patch_embed` | 0.99999 |
140
- | Vision `encoder_out` (27 blocks) | 0.9809 |
141
- | Vision `vit_proj` (after `mlp1`) | 0.9911 |
142
- | LLM prefill last-token logits | 0.9922 |
143
- | **Full on-device logits (vision -> LLM)** | **0.9928** |
144
 
145
- Performance:
146
 
147
- | Metric | Value |
148
- |---|---:|
149
- | Decode throughput | ~38 tok/s |
150
- | Vision (MoonViT + projector) | ~53 ms |
151
- | Prefill | ~64 ms |
152
- | End-to-end | ~2.32 frames/s |
153
-
154
- The LLM runs BF16 attention + BFP8 MLP weights (the `accuracy` preset; BFP8 MLP is what the
155
- >= 0.99 gate needs, BFP4 reaches ~0.935). Decode is weight-bandwidth bound (~3 GB/token), so
156
- trace replay is the big win (7.5 -> 38 tok/s) and a second command queue is not. The
157
- experimental on-device Parallel Box Decoding (MTP, `media/demo_mtp.png`) is approximate,
158
- not accuracy-gated, and not served. The validated run needed one change to tt-metal's
159
- `models/tt_transformers/tt/mlp.py`, and this package ships it as an overlay
160
- (`code/models/tt_transformers/tt/mlp.py`, otherwise byte-identical to the stock file): in
161
- single-chip decode the `w1`/`w3` outputs are moved to DRAM before the next matmul, because
162
- with stock bf16 L1-sharded intermediates the `w3` weight-stream circular buffers do not fit
163
- L1 on one p150a (`Statically allocated circular buffers ... clash with L1 buffers` at the
164
- first decode step). Measured with the overlay on the validation host: `media/demo_input.png`
165
- + `car` -> the same box as `media/demo_ar.png`, vision 49 ms, prefill 58 ms, decode 41 tok/s,
166
- 346 ms end-to-end. `LA_PREC=bfp8attn` (all-BFP8 attention, PCC 0.9912) remains available.
167
-
168
- ### Layout notes
169
-
170
- `code/locate_anything/` is the port (`tt/vision.py` MoonViT, `tt/model_la.py` the embeds-fed
171
- Qwen2.5-3B `Transformer` subclass, `tt/pipeline.py` the serving recipe, `tt/mtp.py` the
172
- experimental MTP decoder, `reference/` the torch-CPU reference and the LLM-checkpoint
173
- extractor, `tests/` the PCC/benchmark/demo pytest suites, `server/` this app + smoke test).
174
- `code/models/{common,tt_transformers,demos/qwen25_vl}` are the tt-metal packages the LLM
175
- path reuses (stock Qwen2.5 `Transformer`/`Generator`/paged `Attention`), with the one
176
- overlay file `models/tt_transformers/tt/mlp.py` described above. The tests need a device
177
- and the torch-CPU goldens (`reference/run_reference.py`), which are not shipped.
178
 
179
  ### Licensing
180
 
181
- The **upstream model and weights** are licensed **`other`** (nvidia-license):
182
- https://huggingface.co/nvidia/LocateAnything-3B/blob/main/LICENSE - NVIDIA's own licence,
183
- not an OSI licence; read it before any use. The weights are **not redistributed** here; they
184
- are fetched from the upstream repo under whatever terms that repo sets. The **port code**
185
- (Apache-2.0 SPDX headers; written by Hyunggi Chang, github.com/changh95/tt-locate-anything)
186
- is published under the same upstream terms, since a port cannot grant more than its upstream
187
- does. Backbone: Qwen2.5-3B-Instruct (Apache-2.0); vision tower: MoonViT (Kimi-VL, Moonshot AI);
188
- runtime: Tenstorrent tt-metal / tt-nn (Apache-2.0).
189
 
190
  ## Provenance
191
 
@@ -195,5 +85,5 @@ The exact sources the image was built from — `code/` in this repo is byte-iden
195
  | --- | --- |
196
  | tt-metal | [`8b98410e730bb504fea43a88609756e34821d91d`](https://github.com/tenstorrent/tt-metal/commit/8b98410e730bb504fea43a88609756e34821d91d) |
197
  | `code/` digest | `eb2ce7d7357c84e3` (sha256, first 16 hex digits) |
198
- | built | 2026-09-12T12:20:44+00:00 by tt-model 0.1.0 |
199
 
 
1
  ---
2
  tags:
3
  - blackhole
 
4
  - p150
 
 
5
  - tt-dit-server
 
6
  - tt-model-cache
 
7
  - tt-model-container
 
 
 
 
 
 
 
 
 
 
8
  ---
9
 
10
  # locate-anything-3b-p150
11
 
12
+ NVIDIA LocateAnything-3B (Eagle-family visual grounding / open-vocabulary detection VLM: MoonViT-SO-400M vision tower + Qwen2.5-3B-Instruct with a detection vocabulary) running entirely on one Tenstorrent Blackhole p150a via tt-nn: image + free-text query in, labelled boxes out.
13
+ Weights: [nvidia/LocateAnything-3B](https://huggingface.co/nvidia/LocateAnything-3B) · Paper: [arXiv:2605.27365](https://arxiv.org/abs/2605.27365) · Upstream code: [NVlabs/Eagle (Embodied)](https://github.com/NVlabs/Eagle/tree/main/Embodied) · Port: [changh95/tt-locate-anything](https://github.com/changh95/tt-locate-anything)
14
 
15
  Runs on **p150** (mesh `P150`).
16
 
 
25
 
26
  `pull --with-weights` downloads the Docker image and the [`nvidia/LocateAnything-3B`](https://huggingface.co/nvidia/LocateAnything-3B) weights at `c32291ca5e996f5a7a485845b4f57a233936bba0` (into your HF cache; they are not in the image). `serve` starts the model's own HTTP server on port 20000 (or the next free port, if that one is busy); the first start compiles kernels for your device, which takes several minutes, and the server is ready when it logs `Application startup complete`.
27
 
28
+ ### Run with tt-cli
29
 
30
  ```bash
31
+ tt serve changh95/locate-anything-3b-p150
32
+ { printf '{"query":"car","image":"'; base64 -w0 media/demo_input.png; printf '"}'; } > req.json
33
+ curl -s localhost:20000/predict -H 'Content-Type: application/json' -d @req.json
34
  tt model stop changh95/locate-anything-3b-p150
35
  ```
36
 
37
+ - `POST /predict`: `image` (base64 PNG/JPEG, one image), `query` (free text, 1-1000 chars; join categories with `</c>`, e.g. `person</c>car`); optional `max_new_tokens` (128, cap 1024), `return_overlay` (false).
38
+ - `GET /health`, `GET /info`.
39
 
40
+ ### Response
41
 
42
+ ```json
43
+ {"query": "car", "width": 1920, "height": 1080, "canonical_size": [616, 336], "grid_hw": [24, 44],
44
+ "raw_text": "<ref>car</ref><box><282><414><606><794></box><|im_end|>",
45
+ "detections": [{"label": "car", "box": [541.44, 447.12, 1163.52, 857.52], "box_norm": [282, 414, 606, 794]}],
46
+ "points": [], "num_generated_tokens": 10, "stopped_on_eos": true, "decode_mode": "ar_greedy_trace",
47
+ "timing_ms": {"vision": 47.4, "prefill": 55.8, "decode": 224.0, "total": 343.4, "decode_tok_s": 40.17}}
48
+ ```
49
 
50
+ - `box` is `[x1, y1, x2, y2]` in original image pixels; `box_norm` is the model's own 0..1000 output over the squashed `canonical_size` view. `points` holds 2-coordinate outputs the same way.
51
+ - `return_overlay: true` adds `overlay_png_b64`, the boxes drawn on your image as a base64 PNG.
 
 
 
52
 
53
+ ### Demo
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
54
 
55
+ | Input (`media/demo_input.png`), query `car` | Greedy decode on p150a (`media/demo_ar.png`) |
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56
  |:---:|:---:|
57
  | ![](media/demo_input.png) | ![](media/demo_ar.png) |
58
 
59
+ ### Accuracy and speed
60
 
61
+ | Metric | Value |
62
  |---|---:|
63
+ | Full on-device logits PCC vs torch-CPU reference (vision → LLM) | 0.9928 (gate ≥ 0.99; vision projector 0.9911, LLM prefill 0.9922) |
64
+ | Decoded box string on the demo image | matches the HF reference |
65
+ | `/predict` served over HTTP (warm, batch 1, `car`, 10 tokens) | ~343 ms server-side (vision 47 · prefill 56 · decode 224 ms, ~40 tok/s) · ~0.38 s wall |
 
 
66
 
67
+ ### Caveats
68
 
69
+ - One image + one query per request, batch 1, requests are serialized. Every image is squash-resized onto a fixed 24×44-patch grid (616×336, 16:9; `LA_IN_TOKEN_LIMIT=1024`, upstream default 25600): non-16:9 images are distorted before the model sees them, boxes still map back to original pixels.
70
+ - bf16 vision, BF16 attention + BFP8 MLP LLM; the package ships an `mlp.py` overlay (`code/models/tt_transformers/tt/mlp.py`, decode `w1`/`w3` spilled to DRAM) so the LLM fits L1 on one p150a. Only greedy AR decode is served; the experimental Parallel Box Decoding (MTP) is not.
71
+ - Weights are public and ungated but under NVIDIA's own license (not OSI); ~7.7 GB download, and the first boot extracts a 6.8 GB Qwen2.5-3B checkpoint, converts it to BFP8 and JIT-compiles (~100 s cold, ~25 s afterwards).
72
+ - Not an OpenAI-compatible API; `GET /v1/models` is a stub so the tt-model ready card does not 404.
73
+ - Validated on tt-metal `v0.78.0-dev20260820` (main `8b98410e730`), single p150a only.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74
 
75
  ### Licensing
76
 
77
+ - Weights: [nvidia/LocateAnything-3B](https://huggingface.co/nvidia/LocateAnything-3B), `other` ([NVIDIA license](https://huggingface.co/nvidia/LocateAnything-3B/blob/main/LICENSE), not OSI); fetched from upstream, not redistributed here.
78
+ - Port and serving code (`code/locate_anything`, `code/scripts`): Apache-2.0 SPDX headers, from [changh95/tt-locate-anything](https://github.com/changh95/tt-locate-anything), published under the same upstream terms; the vendored [tt-metal](https://github.com/tenstorrent/tt-metal) overlay `code/models/tt_transformers/tt/mlp.py` is Apache-2.0.
 
 
 
 
 
 
79
 
80
  ## Provenance
81
 
 
85
  | --- | --- |
86
  | tt-metal | [`8b98410e730bb504fea43a88609756e34821d91d`](https://github.com/tenstorrent/tt-metal/commit/8b98410e730bb504fea43a88609756e34821d91d) |
87
  | `code/` digest | `eb2ce7d7357c84e3` (sha256, first 16 hex digits) |
88
+ | built | 2026-09-12T13:42:57+00:00 by tt-model 0.1.0 |
89
 
tt_kernel_manifest.json CHANGED
@@ -6,7 +6,7 @@
6
  "device_count": 1,
7
  "producer": {
8
  "tt_kernel_version": "0.1.0",
9
- "created_at": "2026-09-12T12:20:55.419379+00:00",
10
  "hostname": "deepgadget"
11
  },
12
  "weights": {
@@ -102,7 +102,7 @@
102
  "image": "tt-model/locate-anything-3b-p150:04871c7cfa03",
103
  "repo": "changh95/locate-anything-3b-p150",
104
  "tt_model_version": "0.1.0",
105
- "created_at": "2026-09-12T12:20:44+00:00",
106
  "tt_metal": {
107
  "sha": "8b98410e730bb504fea43a88609756e34821d91d",
108
  "describe": "v0.78.0-dev20260820-25-g8b98410e73",
 
6
  "device_count": 1,
7
  "producer": {
8
  "tt_kernel_version": "0.1.0",
9
+ "created_at": "2026-09-12T13:43:23.393943+00:00",
10
  "hostname": "deepgadget"
11
  },
12
  "weights": {
 
102
  "image": "tt-model/locate-anything-3b-p150:04871c7cfa03",
103
  "repo": "changh95/locate-anything-3b-p150",
104
  "tt_model_version": "0.1.0",
105
+ "created_at": "2026-09-12T13:42:57+00:00",
106
  "tt_metal": {
107
  "sha": "8b98410e730bb504fea43a88609756e34821d91d",
108
  "describe": "v0.78.0-dev20260820-25-g8b98410e73",