File size: 5,641 Bytes
53c9448
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
---
license: apache-2.0
library_name: litert
pipeline_tag: image-classification
tags: [ram, ram-plus, recognize-anything, image-tagging, multi-label, open-vocabulary, swin, litert, tflite, on-device, gpu]
base_model: xinyu1205/recognize-anything-plus-model
---

# RAM++ (Recognize Anything Plus) β€” LiteRT on-device image tagging

[RAM++](https://github.com/xinyu1205/recognize-anything) (Apache-2.0) re-authored for LiteRT:
give it a photo, get the tags it recognizes from a **4,585-tag** open vocabulary β€” per-tag sigmoid,
no fixed class head. Four graphs β€” the Swin-L encoder **stages 0-2** and the Query2Label **tag head**
run on the CompiledModel **GPU**; the **last Swin stage** and the 479 MB frozen **tag bank** run on
**CPU** (the deep Swin block fp16-miscomputes on the Mali delegate β€” see below).

Verified on a Pixel 8a: Swin 0-2 GPU (corr 0.998) + stage-3/reweight CPU (exact) + tag head GPU
(corr 0.9987, ~270 ms). Sample photo (a dog on a couch) β†’ **14 tags in ~2 s**, all correct:
`dog Β· couch Β· living room Β· sit Β· carpet Β· picture frame Β· plant Β· armchair Β· lamp Β· pillow …`.

## Files

| file | graph | in β†’ out | delegate |
|---|---|---|---|
| `ram_swin_s012_fp16.tflite` | Swin stages 0-2 | image [1,3,384,384] β†’ feat [1,144,1536] | GPU |
| `ram_stage3_tail_fp16.tflite` | Swin stage 3 + norm + proj | feat β†’ image_embeds [1,145,512] | CPU |
| `ram_reweight_fp16.tflite` | multi-grained reweight | cls [1,512] β†’ tag queries [1,4585,768] | CPU |
| `ram_taghead_fp16.tflite` | Query2Label tag head | queries + image_embeds β†’ logits [1,4585] | GPU |
| `ram_tag_list.txt`, `ram_tag_threshold.bin` | host assets (4585 tags + per-class thresholds) | β€” | β€” |

## Pipeline

```
image β†’[ImageNet norm]β†’ [GPU Swin 0-2]β†’ feat β†’[CPU Swin-3 + norm + proj]β†’ image_embeds[1,145,512]
       token0 = cls β†’[CPU reweight over the 4585Γ—51 tag bank]β†’ queries[1,4585,768]
       (queries, image_embeds) β†’[GPU Q2L tag head]β†’ logits β†’[sigmoid + per-class threshold]β†’ tags
```

## Why the GPU/CPU split β€” a Mali fp16 finding

The Swin-L encoder is fully GPU-convertible, but its **last stage miscomputes in fp16 on the Mali
delegate**. Bisecting the four stages on-device: stage 0 = 0.9999, stage 1 = 0.9999, stage 2 =
0.9983, **stage 3 = 0.709**. It is **not** head_dim (stage 2 shares head_dim 32) and **not** overflow
(every stage-3 value < 848 β‰ͺ fp16 max 65504; a round-to-fp16-between-ops simulation reproduces fp32
at corr 0.99999997) β€” it is Mali's **fp16 matmul accumulation** in the deep, high-magnitude blocks
(the residual stream grows to absmax 847; the 6144-wide fc2 and 48-head attention accumulate in fp16).
Those 2 blocks run on CPU; everything else stays on GPU. The reweight bakes the tag bank once as fp16
(229 MB, not 686 MB).

## Minimal usage (Python)

```python
import numpy as np
from PIL import Image
from ai_edge_litert.interpreter import Interpreter

def run(path, x, *ins):                                  # single-input or size-matched multi-input
    it = Interpreter(model_path=path); it.allocate_tensors()
    ind = it.get_input_details()
    if not ins:
        it.set_tensor(ind[0]["index"], x)
    else:
        for d in ind:
            n = int(np.prod(d["shape"]))
            it.set_tensor(d["index"], x if n == x.size else ins[0])
    it.invoke()
    return it.get_tensor(it.get_output_details()[0]["index"])

# preprocess (ImageNet)
img = Image.open("photo.jpg").convert("RGB").resize((384, 384))
a = np.asarray(img, np.float32) / 255.0
a = (a - [0.485, 0.456, 0.406]) / [0.229, 0.224, 0.225]
x = a.transpose(2, 0, 1)[None].astype(np.float32)        # [1,3,384,384]

feat = run("ram_swin_s012_fp16.tflite", x)               # [1,144,1536]
iemb = run("ram_stage3_tail_fp16.tflite", feat)          # [1,145,512]
cls = iemb[:, 0, :]                                       # [1,512]
queries = run("ram_reweight_fp16.tflite", cls)           # [1,4585,768]
logits = run("ram_taghead_fp16.tflite", queries, iemb)   # [1,4585]

probs = 1 / (1 + np.exp(-logits[0]))
thr = np.fromfile("ram_tag_threshold.bin", np.float32)
tags = [t for t in open("ram_tag_list.txt").read().splitlines()]
print([tags[i] for i in np.where(probs > thr)[0]])
```

## Minimal usage (Kotlin, LiteRT CompiledModel)

```kotlin
val g1 = CompiledModel.create("ram_swin_s012_fp16.tflite", CompiledModel.Options(Accelerator.GPU), null)
val c2 = CompiledModel.create("ram_stage3_tail_fp16.tflite", CompiledModel.Options(Accelerator.CPU), null)
val rw = CompiledModel.create("ram_reweight_fp16.tflite", CompiledModel.Options(Accelerator.CPU), null)
val th = CompiledModel.create("ram_taghead_fp16.tflite", CompiledModel.Options(Accelerator.GPU), null)

g1In[0].writeFloat(preprocess(bitmap)); g1.run(g1In, g1Out)           // -> feat[1,144,1536]
c2In[0].writeFloat(g1Out[0].readFloat()); c2.run(c2In, c2Out)        // -> image_embeds[1,145,512]
val iemb = c2Out[0].readFloat(); val cls = iemb.copyOfRange(0, 512)
rwIn[0].writeFloat(cls); rw.run(rwIn, rwOut)                          // -> queries[1,4585,768]
val q = rwOut[0].readFloat()
for (b in thIn) { val n = b.readFloat().size; b.writeFloat(if (n == q.size) q else iemb) }
th.run(thIn, thOut)                                                  // -> logits[1,4585]
// sigmoid(logits[i]) > threshold[i] -> tag[i]
```

A complete Android sample (image pick β†’ tags) is in **google-ai-edge/litert-samples**.

## Upstream

[xinyu1205/recognize-anything](https://github.com/xinyu1205/recognize-anything) Β·
`xinyu1205/recognize-anything-plus-model` (Apache-2.0). Paper: *Open-Set Image Tagging with
Multi-Grained Text Supervision*.