Skip to content

Add DINOv2 dense feature visualization sample (ViT-S/14 on CompiledModel GPU)#223

Open
john-rocky wants to merge 2 commits into
google-ai-edge:mainfrom
john-rocky:dinov2-feature-visualization-sample
Open

Add DINOv2 dense feature visualization sample (ViT-S/14 on CompiledModel GPU)#223
john-rocky wants to merge 2 commits into
google-ai-edge:mainfrom
john-rocky:dinov2-feature-visualization-sample

Conversation

@john-rocky

Copy link
Copy Markdown
Contributor

What

New feature_visualization/dinov2_kotlin_gpu sample: the self-supervised DINOv2 ViT-S/14 backbone (Meta, Apache-2.0) running fully on the LiteRT CompiledModel GPU, with a dense-feature visualization — a top-3 PCA of the patch tokens mapped to RGB. Semantically similar patches (object parts vs background) share a color, so the object pops out with no labels or segmentation.

  • Kotlin sample app (com.google.ai.edge.examples.dinov2): pick a photo → image and its DINOv2 feature-PCA side by side. The PCA (power iteration on the 384×384 covariance) is host-side.
  • conversion/: build_dinov2.py (litert-torch) + validate_dinov2.py (CompiledModel Python API parity) + README.
  • Model on HF: litert-community/DINOv2-ViT-S14-LiteRT (45 MB fp16, fetched at build time by download_model.gradle, not committed).

GPU re-authoring (proven ViT recipes)

  • 4D attention (C12): fused-qkv split to q/k/v, reshaped to [1,heads,N,d] (≤4D), manual softmax(qkᵀ/√d)·v.
  • SafeLayerNorm: deviation scaled by 1/64 before squaring so the fp16 variance doesn't overflow on DINOv2's massive activations, then rescaled (exact).
  • LayerScale (ls1/ls2) baked into the following projections.
  • tanh-GELU — near-exact; the sigmoid-GELU approximation drifts to feature corr 0.968 over 12 blocks, tanh → 0.99999.
  • pos_embed baked at a fixed 448 grid at model creation → no runtime interpolation (no GATHER_ND).

Verification (Pixel 8a, LiteRT 2.1.5, fp16)

  • 864/864 nodes on the GPU delegate, 1 partition, ~8 ms.
  • Re-authored torch vs stock timm: corr 0.999992. fp16 tflite vs fp32 PyTorch via the CompiledModel API: patch-feature corr > 0.99 (validate_dinov2.py PASS).
  • Device fp16 features vs fp32: corr 0.996. Host PCA (power iteration) matches SVD (|corr| 1.0, all 3 components).
  • :app:assembleDebug builds.

Companion LiteRT-Models entry: john-rocky/LiteRT-Models#58.

…del GPU)

Self-supervised DINOv2 ViT-S/14 (Meta, Apache-2.0) running fully on the LiteRT CompiledModel GPU; a top-3 PCA of the dense patch tokens -> RGB visualizes what the backbone sees. New feature_visualization/dinov2_kotlin_gpu sample.

Proven ViT recipes: fused-qkv -> 4D attention (C12), SafeLayerNorm (1/64 pre-square for DINOv2 massive activations), LayerScale baked into projections, tanh-GELU (sigmoid approx drifts to feature corr 0.968 over 12 blocks; tanh -> 0.99999). pos_embed baked at a fixed 448 grid at creation -> no GATHER_ND. Pixel 8a: 864/864 nodes, 1 partition, ~8 ms; device fp16 features vs fp32 corr 0.996. conversion/validate_dinov2.py = CompiledModel-API parity. Host PCA (power iteration) validated vs SVD. Model: litert-community/DINOv2-ViT-S14-LiteRT.
Rework the UI layer to the canonical Compose + MVVM sample shape while
leaving the inference path byte-identical.

- MainActivity: thin ComponentActivity Compose host (viewModels factory,
  collectAsStateWithLifecycle, PickVisualMedia gallery launcher).
- MainViewModel: owns Dinov2Features, loads the fp16 model from assets in
  a try/catch (surfacing the throwable as an inline error), and confines
  every model call to a single-thread dispatcher. The old Activity's
  sideBySide() render math is moved here verbatim; only the GPU inference
  is timed, as before.
- UiState / ImageUtils / view (Dinov2Screen, Theme, Color) and
  res/values (strings, themes, colors) added; no hardcoded UI strings.
- Build files adopt the version catalog and Compose (AGP 8.9.1 /
  Kotlin 2.2.21 / compileSdk 36); LiteRT stays pinned at 2.1.5. The
  existing assets provisioning via download_model.gradle is preserved.

Dinov2Features.kt (the CompiledModel + host PCA helper) is unchanged.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant