Download export/depth-anything-v2-small.py from executorch-community/Depth-Anything-V2-Small-ExecuTorch-WebGPU: direct link, hf CLI and curl.
- Browser
- Download file 4.39 kB
-
https://huggingface.co/executorch-community/Depth-Anything-V2-Small-ExecuTorch-WebGPU/resolve/main/export/depth-anything-v2-small.py
- Command line
-
hf download hf://executorch-community/Depth-Anything-V2-Small-ExecuTorch-WebGPU/export/depth-anything-v2-small.py
-
curl -L -o depth-anything-v2-small.py https://huggingface.co/executorch-community/Depth-Anything-V2-Small-ExecuTorch-WebGPU/resolve/main/export/depth-anything-v2-small.py
4.39 kB
| #!/usr/bin/env python3 | |
| """Depth Anything V2 Small (Hugging Face transformers port) -> ExecuTorch WebGPU. | |
| Upstream: depth-anything/Depth-Anything-V2-Small-hf (Apache-2.0; the Base and Large | |
| variants are CC BY-NC 4.0 and must not be used here). | |
| Input: [1, 3, 224, 224] float32 RGB, ImageNet-normalized outside the graph | |
| ((x / 255 - mean) / std, mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225)). | |
| Output: [1, 224, 224] relative inverse depth (`predicted_depth`). | |
| """ | |
| import argparse | |
| import sys | |
| from pathlib import Path | |
| import torch | |
| sys.path.insert(0, str(Path(__file__).resolve().parent)) | |
| import _common # noqa: E402 | |
| UPSTREAM = { | |
| "repo": "depth-anything/Depth-Anything-V2-Small-hf", | |
| "revision": "5426e4f0f36572d16453bbda7a8389317b1bef99", | |
| } | |
| class PredictedDepth(torch.nn.Module): | |
| def __init__(self, m): | |
| super().__init__() | |
| self.m = m | |
| def forward(self, pixel_values): | |
| return self.m(pixel_values=pixel_values).predicted_depth | |
| def freeze_position_embeddings(model, height, width): | |
| """Bake the bicubic-resized DINOv2 position embeddings for one input size into a buffer. | |
| Traced live, the resize leaves lifted tensor constants and aten.arange nodes whose fake | |
| tensors belong to a different FakeTensorMode, and the Vulkan preprocess passes fail on | |
| them. The values are what the eager module computes for this size. | |
| """ | |
| import types | |
| emb = model.m.backbone.embeddings | |
| with torch.no_grad(): | |
| dummy = torch.zeros(1, (height // 14) * (width // 14) + 1, emb.position_embeddings.shape[-1]) | |
| frozen = emb.interpolate_pos_encoding(dummy, height, width).detach().clone() | |
| emb.register_buffer("frozen_position_embeddings", frozen, persistent=False) | |
| emb.interpolate_pos_encoding = types.MethodType(lambda self, e, h, w: self.frozen_position_embeddings, emb) | |
| return model | |
| def build_model(attn): | |
| from transformers import AutoModelForDepthEstimation | |
| from transformers.models.dinov2 import modeling_dinov2 | |
| # transformers 5.x materializes an all-zero additive mask for eager attention even | |
| # without padding (arange/ge/where plus a CPU scalar_tensor in the graph). Images never | |
| # pad, so drop it; outputs are unchanged. | |
| create_mask = modeling_dinov2.create_bidirectional_mask | |
| modeling_dinov2.create_bidirectional_mask = lambda **kw: ( | |
| None if kw.get("attention_mask") is None else create_mask(**kw) | |
| ) | |
| model = AutoModelForDepthEstimation.from_pretrained( | |
| UPSTREAM["repo"], | |
| revision=UPSTREAM["revision"], | |
| attn_implementation=attn, | |
| torch_dtype=torch.float32, | |
| ) | |
| return PredictedDepth(model.eval()).eval() | |
| def main(): | |
| ap = argparse.ArgumentParser(description=__doc__.splitlines()[0]) | |
| ap.add_argument("--out", required=True, help="output directory") | |
| ap.add_argument( | |
| "--size", | |
| type=int, | |
| nargs=2, | |
| default=[224, 224], | |
| metavar=("H", "W"), | |
| help="multiples of 14", | |
| ) | |
| ap.add_argument( | |
| "--attn", | |
| default="eager", | |
| choices=["eager", "sdpa"], | |
| help="attention implementation traced into the graph", | |
| ) | |
| ap.add_argument( | |
| "--golden", | |
| help="optional goldens.manifest.json to compare against (key: depth)", | |
| ) | |
| args = ap.parse_args() | |
| golden = _common.load_golden(args.golden, "depth") if args.golden else None | |
| _common.export_vision( | |
| name="depth_v2s", | |
| model=freeze_position_embeddings(build_model(args.attn), *args.size), | |
| example_input=torch.randn(1, 3, *args.size), | |
| out_dir=args.out, | |
| upstream={ | |
| "model": UPSTREAM["repo"], | |
| "weights": {**UPSTREAM, "official": True}, | |
| "license": "Apache-2.0", | |
| }, | |
| extra_recipe={ | |
| "attn_implementation": args.attn, | |
| "graph_rewrites": ["transformers 5.x all-zero bidirectional attention mask skipped (attention_mask=None; no padding in images)", "DINOv2 position embeddings resized (bicubic, as upstream) for the fixed input size in eager and stored as a constant"], | |
| "preprocessing_outside_graph": "RGB / 255, ImageNet mean/std normalization, resize to input size", | |
| "postprocessing_outside_graph": "min-max normalize predicted_depth for display", | |
| }, | |
| golden=golden, | |
| ) | |
| if __name__ == "__main__": | |
| main() | |