Orangerl commited on
Commit
20b4034
·
verified ·
1 Parent(s): d009e89

Add files using upload-large-folder tool

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. code/umm/.gitignore +3 -0
  2. code/umm/README.md +95 -0
  3. code/umm/__init__.py +1 -0
  4. code/umm/inference/README.md +108 -0
  5. code/umm/inference/__init__.py +1 -0
  6. code/umm/inference/infer.py +994 -0
  7. code/umm/inference/run_infer.sh +72 -0
  8. code/umm/runtime/README.md +50 -0
  9. code/umm/runtime/check_imports.py +31 -0
  10. code/umm/runtime/deepspeed_configs/zero2.json +42 -0
  11. code/umm/runtime/models/__init__.py +0 -0
  12. code/umm/runtime/models/blip3o/constants.py +82 -0
  13. code/umm/runtime/models/blip3o/conversation.py +479 -0
  14. code/umm/runtime/models/blip3o/data/covt_data.py +827 -0
  15. code/umm/runtime/models/blip3o/data/covt_data_van.py +1056 -0
  16. code/umm/runtime/models/blip3o/data/covt_data_var.py +1039 -0
  17. code/umm/runtime/models/blip3o/data/mix_und_t2i_data.py +274 -0
  18. code/umm/runtime/models/blip3o/data/moe_data.py +0 -0
  19. code/umm/runtime/models/blip3o/data/t2I_covt_gen_data.py +894 -0
  20. code/umm/runtime/models/blip3o/data/t2i_covt_data.py +742 -0
  21. code/umm/runtime/models/blip3o/model/__init__.py +42 -0
  22. code/umm/runtime/models/blip3o/model/anchors/DepthAnything/DA-2K.md +51 -0
  23. code/umm/runtime/models/blip3o/model/anchors/DepthAnything/README.md +201 -0
  24. code/umm/runtime/models/blip3o/model/anchors/DepthAnything/app.py +88 -0
  25. code/umm/runtime/models/blip3o/model/anchors/DepthAnything/depth_anything_v2/dinov2.py +415 -0
  26. code/umm/runtime/models/blip3o/model/anchors/DepthAnything/depth_anything_v2/dinov2_layers/__init__.py +11 -0
  27. code/umm/runtime/models/blip3o/model/anchors/DepthAnything/depth_anything_v2/dinov2_layers/attention.py +83 -0
  28. code/umm/runtime/models/blip3o/model/anchors/DepthAnything/depth_anything_v2/dinov2_layers/block.py +252 -0
  29. code/umm/runtime/models/blip3o/model/anchors/DepthAnything/depth_anything_v2/dinov2_layers/drop_path.py +35 -0
  30. code/umm/runtime/models/blip3o/model/anchors/DepthAnything/depth_anything_v2/dinov2_layers/layer_scale.py +28 -0
  31. code/umm/runtime/models/blip3o/model/anchors/DepthAnything/depth_anything_v2/dinov2_layers/mlp.py +41 -0
  32. code/umm/runtime/models/blip3o/model/anchors/DepthAnything/depth_anything_v2/dinov2_layers/patch_embed.py +89 -0
  33. code/umm/runtime/models/blip3o/model/anchors/DepthAnything/depth_anything_v2/dinov2_layers/swiglu_ffn.py +63 -0
  34. code/umm/runtime/models/blip3o/model/anchors/DepthAnything/depth_anything_v2/dpt.py +221 -0
  35. code/umm/runtime/models/blip3o/model/anchors/DepthAnything/depth_anything_v2/util/blocks.py +148 -0
  36. code/umm/runtime/models/blip3o/model/anchors/DepthAnything/depth_anything_v2/util/transform.py +158 -0
  37. code/umm/runtime/models/blip3o/model/anchors/DepthAnything/metric_depth/README.md +114 -0
  38. code/umm/runtime/models/blip3o/model/anchors/DepthAnything/metric_depth/dataset/hypersim.py +74 -0
  39. code/umm/runtime/models/blip3o/model/anchors/DepthAnything/metric_depth/dataset/kitti.py +57 -0
  40. code/umm/runtime/models/blip3o/model/anchors/DepthAnything/metric_depth/dataset/transform.py +277 -0
  41. code/umm/runtime/models/blip3o/model/anchors/DepthAnything/metric_depth/dataset/vkitti2.py +54 -0
  42. code/umm/runtime/models/blip3o/model/anchors/DepthAnything/metric_depth/depth_anything_v2/dinov2.py +415 -0
  43. code/umm/runtime/models/blip3o/model/anchors/DepthAnything/metric_depth/depth_anything_v2/dinov2_layers/__init__.py +11 -0
  44. code/umm/runtime/models/blip3o/model/anchors/DepthAnything/metric_depth/depth_anything_v2/dinov2_layers/attention.py +83 -0
  45. code/umm/runtime/models/blip3o/model/anchors/DepthAnything/metric_depth/depth_anything_v2/dinov2_layers/block.py +252 -0
  46. code/umm/runtime/models/blip3o/model/anchors/DepthAnything/metric_depth/depth_anything_v2/dinov2_layers/drop_path.py +35 -0
  47. code/umm/runtime/models/blip3o/model/anchors/DepthAnything/metric_depth/depth_anything_v2/dinov2_layers/layer_scale.py +28 -0
  48. code/umm/runtime/models/blip3o/model/anchors/DepthAnything/metric_depth/depth_anything_v2/dinov2_layers/mlp.py +41 -0
  49. code/umm/runtime/models/blip3o/model/anchors/DepthAnything/metric_depth/depth_anything_v2/dinov2_layers/patch_embed.py +89 -0
  50. code/umm/runtime/models/blip3o/model/anchors/DepthAnything/metric_depth/depth_anything_v2/dinov2_layers/swiglu_ffn.py +63 -0
code/umm/.gitignore ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ __pycache__/
2
+ *.py[cod]
3
+ outputs/
code/umm/README.md ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # UMM 分阶段代码目录
2
+
3
+ 这里按训练阶段独立组织当前已经闭环的 FLUX.1 unified model 主线。每个阶段都有自己的:
4
+
5
+ ```text
6
+ train.py / infer.py Python 入口
7
+ run_train.sh 分布式启动脚本
8
+ config.yaml 本阶段配置
9
+ README.md 数据、模型、loss、冻结参数、checkpoint 说明
10
+ ```
11
+
12
+ 没有统一总启动脚本,也不会自动串行运行所有阶段。上一个阶段验收完成后,手工把 checkpoint 路径写入下一个阶段配置,避免程序误选“最新目录”。
13
+
14
+ ## 目录
15
+
16
+ ```text
17
+ umm/
18
+ ├── runtime/ 本地共享 models/blip3o 源码与 DeepSpeed 配置
19
+ ├── stage1_meta_query/ 256 个 meta query 对齐 FLUX.1
20
+ ├── stage2_covt/ SAM/DINO/VGGT/Edge/SigLIP CoVT 课程与 LoRA merge
21
+ ├── stage3_generation/ CoVT/Query 与 DiT generation path
22
+ ├── stage4_tag_moe/ 多图编辑 + TAG-MoE
23
+ └── inference/ 独立推理入口
24
+ ```
25
+
26
+ ## 阶段关系
27
+
28
+ ```text
29
+ Stage 1
30
+ Qwen3-VL + FLUX.1
31
+ -> embeddings_connector/checkpoint-N.bin
32
+
33
+ Stage 2
34
+ Qwen3-VL + Stage-1 connector + SAM/DINOv2/VGGT/PiDiNet/SigLIP teachers
35
+ -> CoVT LoRA/non-LoRA -> merge dense CoVT model
36
+
37
+ Stage 3
38
+ dense CoVT model + FLUX.1
39
+ -> dense generation model + generation projector
40
+
41
+ Stage 4
42
+ dense generation model + projector + quality500k multi-image data
43
+ -> TAG-MoE trainable_weights.bin
44
+
45
+ Inference
46
+ dense generation model + projector + FLUX.1 + TAG sidecar
47
+ ```
48
+
49
+ ## 启动方式
50
+
51
+ 每个 `train.py` 都是当前真实训练脚本的本地副本,不再调用仓库原位置的训练脚本。共享的 model/data/trainer 代码位于 `runtime/`,shell 会把它放在 `PYTHONPATH` 最前面,因此修改 `umm/runtime/models/blip3o` 会直接影响这些阶段。
52
+
53
+ 每个阶段独立启动:
54
+
55
+ ```bash
56
+ bash hc_test/umm/stage1_meta_query/run_train.sh
57
+ bash hc_test/umm/stage2_covt/run_train.sh
58
+ bash hc_test/umm/stage3_generation/run_train.sh
59
+ bash hc_test/umm/stage4_tag_moe/run_train.sh
60
+ ```
61
+
62
+ 先做命令检查、不启动训练:
63
+
64
+ ```bash
65
+ DRY_RUN=1 bash hc_test/umm/stage1_meta_query/run_train.sh
66
+ DRY_RUN=1 bash hc_test/umm/stage2_covt/run_train.sh
67
+ DRY_RUN=1 bash hc_test/umm/stage3_generation/run_train.sh
68
+ DRY_RUN=1 bash hc_test/umm/stage4_tag_moe/run_train.sh
69
+ ```
70
+
71
+ 配置项仍可通过命令行覆盖:
72
+
73
+ ```bash
74
+ DRY_RUN=1 bash hc_test/umm/stage4_tag_moe/run_train.sh \
75
+ --learning_rate 2e-5 \
76
+ --output_dir /path/to/new_output
77
+ ```
78
+
79
+ 具体数据字段、loss 和 checkpoint 见各子目录 README。`runtime/` 只复制源码和小型配置,没有复制数据、预训练权重或 `.pth/.bin/.safetensors` checkpoint。
80
+
81
+ ## 运行环境
82
+
83
+ 当前主环境从原 `unvideo` 环境派生并持久化在:
84
+
85
+ ```text
86
+ /rczhang/rczhang/conda_packs/unvideo
87
+ ```
88
+
89
+ 各阶段的 train/inference shell 都显式使用这里的 Python,避免机器重启后系统路径环境丢失;分布式入口通过 `python -m deepspeed.launcher.runner` 或 `python -m torch.distributed.run` 启动,不依赖可能带旧 shebang 的复制脚本。Stage 2 的 `setup_umm_env.sh` 负责克隆/校准依赖。所有入口在启动前都会清除 proxy 环境变量,只使用直连。环境与 Stage-B 代码验证方式见 `stage2_covt/README.md`。
90
+
91
+ ## 工作边界
92
+
93
+ 这个目录整理的是与你最初描述一致的 **FLUX.1 主线**:meta query -> CoVT -> generation -> TAG-MoE。仓库里的 Target-CoVT 与 FLUX.2 Klein 是独立试验分支,不是这条主线后面可以直接续训的一两个阶段,因此没有混入这里。
94
+
95
+ 当前 Stage 3 的 `train.py` 是较新的 `train_stage3_flux.py` 组装入口;Stage 4 的默认 dense checkpoint 则来自已经跑过的 UND/GEN 历史主线。两者的模型类和 artifact 约定还没有被项目统一。为了避免制造一个表面能串联、实际错误加载的假 pipeline,这里保留真实默认 checkpoint,并在 Stage 3、Stage 4 README 中明确 hand-off。后续要打通新 Stage 3 -> Stage 4,应先统一 Stage 4 的 `OmniGenUndForConditionalGeneration` 加载类与 condition layout。
code/umm/__init__.py ADDED
@@ -0,0 +1 @@
 
 
1
+ """Stage-by-stage launch layout for the latest complete FLUX.1 UMM pipeline."""
code/umm/inference/README.md ADDED
@@ -0,0 +1,108 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Inference:Stage-4 TAG-MoE 图像编辑
2
+
3
+ ## 需要的组件
4
+
5
+ 推理不是只加载一个 checkpoint,而是组合:
6
+
7
+ ```text
8
+ 1. merged Stage-3 dense Qwen/CoVT model
9
+ 2. FLUX.1 base(VAE + transformer + scheduler)
10
+ 3. Stage-3 generation projector
11
+ 4. Stage-4 trainable_weights.bin
12
+ 5. 与训练一致的 4 experts / top-1 / last 10 layers / shared expert
13
+ ```
14
+
15
+ 默认路径写在 `run_infer.sh`,不复制任何权重。
16
+
17
+ ## 输入
18
+
19
+ 支持:
20
+
21
+ - source-only edit;
22
+ - reference-only generation;
23
+ - source + reference edit;
24
+ - 多个 reference:用逗号分隔路径(由真实 infer 实现解析);
25
+ - optional target:只用于保存 comparison,不进入生成条件。
26
+
27
+ ## 启动
28
+
29
+ Source-only:
30
+
31
+ ```bash
32
+ SOURCE_IMAGE=/path/source.png \
33
+ PROMPT='move the object to the left' \
34
+ TASK_TYPE=object_movement \
35
+ bash hc_test/umm/inference/run_infer.sh
36
+ ```
37
+
38
+ Source + reference:
39
+
40
+ ```bash
41
+ SOURCE_IMAGE=/path/source.png \
42
+ REFERENCE_IMAGE=/path/ref.png \
43
+ PROMPT='replace the subject using the reference identity' \
44
+ TASK_TYPE=subject_replacement \
45
+ bash hc_test/umm/inference/run_infer.sh
46
+ ```
47
+
48
+ 只打印命令:
49
+
50
+ ```bash
51
+ DRY_RUN=1 SOURCE_IMAGE=/path/source.png \
52
+ bash hc_test/umm/inference/run_infer.sh
53
+ ```
54
+
55
+ 覆盖 checkpoint:
56
+
57
+ ```bash
58
+ CHECKPOINT=/path/checkpoint-N/trainable_weights.bin \
59
+ SOURCE_IMAGE=/path/source.png \
60
+ bash hc_test/umm/inference/run_infer.sh
61
+ ```
62
+
63
+ ## CoVT mode
64
+
65
+ ```text
66
+ COVT_MODE=full 全部 anchor response
67
+ COVT_MODE=random 随机 anchor 子集
68
+ COVT_MODE=none 不构造 CoVT response
69
+ ```
70
+
71
+ 这控制 Qwen assistant 序列中的 CoVT;不代表 infer 一定把 anchor hidden 直接拼进 DiT。
72
+
73
+ ## 当前一致性风险
74
+
75
+ 当前 Stage-4 full training 中:
76
+
77
+ ```text
78
+ DiT condition = anchor hidden + query hidden
79
+ ```
80
+
81
+ 当前 experiment-local inference 实现中:
82
+
83
+ ```text
84
+ DiT condition = query hidden
85
+ ```
86
+
87
+ 所以它实质更接近“CoVT 间接影响 query、但不直接拼 anchor”的推理。正式使用 full-CoVT checkpoint 前应修正/统一该条件路径,再比较生成结果。
88
+
89
+ ## 输出
90
+
91
+ ```text
92
+ outputs/
93
+ └── single/
94
+ ├── generated.png
95
+ └── comparison.png
96
+ ```
97
+
98
+ multi-case 会按 case 名分别建目录。
99
+
100
+ ## 建议验收
101
+
102
+ 固定 seed,对每个 checkpoint 至少跑:
103
+
104
+ - instruction 原始 / shuffle;
105
+ - source 原始 / remove;
106
+ - reference 原始 / shuffle;
107
+ - `COVT_MODE=full/none`;
108
+ - train/infer 都 query-only 或都 full-anchor 的严格对照。
code/umm/inference/__init__.py ADDED
@@ -0,0 +1 @@
 
 
1
+ """Local UMM inference implementation."""
code/umm/inference/infer.py ADDED
@@ -0,0 +1,994 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ TAG-MoE (ffn_tag) inference for the quality500k-v1 experiment.
4
+
5
+ That run uses (see ``configs/moe_ffn_tag.yaml`` + ``01_train.log``):
6
+ - ``OmniGenUndForConditionalGeneration`` (qwen_covt_gen -> qwen_omni_gen_und remap)
7
+ - local data.py Qwen tokenization helpers (user images + assistant CoVT/gen placeholder)
8
+ - ``build_omni_dit_latent_input`` + multi-bucket VAE conditioning
9
+ - ``inject_flux_moe_ffn_tag`` (4 experts, shared, last 10 layers)
10
+ - gen_down_projector from checkpoint-30000.bin (+ trainable delta in bin)
11
+ """
12
+ from __future__ import annotations
13
+
14
+ import argparse
15
+ import ast
16
+ import os
17
+ import random
18
+ import sys
19
+ import types
20
+ from pathlib import Path
21
+ from typing import Any, Dict, List, Optional, Sequence
22
+
23
+ import numpy as np
24
+ import torch
25
+ import torchvision.transforms as T
26
+ from PIL import Image
27
+ from torchvision.transforms import v2
28
+ from tqdm import tqdm
29
+ from transformers import AutoConfig, AutoProcessor
30
+
31
+ _REPO_ROOT = Path(__file__).resolve().parents[3]
32
+ _RUNTIME_ROOT = Path(__file__).resolve().parents[1] / "runtime"
33
+ sys.path.insert(0, str(_RUNTIME_ROOT))
34
+ sys.path.insert(1, str(_REPO_ROOT))
35
+
36
+
37
+ def _stub_model_package() -> None:
38
+ model_pkg_dir = _RUNTIME_ROOT / "models" / "blip3o" / "model"
39
+ if "models.blip3o.model" in sys.modules:
40
+ return
41
+ for name, path in (
42
+ ("models", _RUNTIME_ROOT / "models"),
43
+ ("models.blip3o", _RUNTIME_ROOT / "models" / "blip3o"),
44
+ ):
45
+ if name not in sys.modules:
46
+ pkg = types.ModuleType(name)
47
+ pkg.__path__ = [str(path)]
48
+ sys.modules[name] = pkg
49
+ stub = types.ModuleType("models.blip3o.model")
50
+ stub.__path__ = [str(model_pkg_dir)]
51
+ sys.modules["models.blip3o.model"] = stub
52
+
53
+
54
+ _stub_model_package()
55
+
56
+ import models.blip3o.model.language_model.covt_gen_qwen_stage3 # noqa: F401
57
+ from models.blip3o.constants import DEFAULT_IM_END_TOKEN, IGNORE_INDEX, SYSTEM_MESSAGE
58
+ from models.blip3o.data.covt_data_var import (
59
+ get_anchor_task_name,
60
+ get_anchor_token,
61
+ get_comt_data_in_response,
62
+ get_token_num,
63
+ )
64
+ from models.blip3o.model.language_model.omni_und_gen import (
65
+ OmniGenUndForConditionalGeneration,
66
+ QwenOmniGenUndConfig,
67
+ )
68
+ from models.blip3o.model.flux_moe_tag import inject_flux_moe_ffn_tag
69
+ from models.blip3o.model.language_model.und_gen_stage3_infer import calculate_shift, retrieve_timesteps
70
+ from hc_test.umm.stage4_tag_moe.data import (
71
+ TASK_TYPE_TO_ID,
72
+ TASK_TYPES,
73
+ bucket_images,
74
+ get_image_info,
75
+ get_rope_index_3,
76
+ )
77
+ from models.blip3o.moe_train_tag.omni_dit_latent_pack import build_omni_dit_latent_input
78
+
79
+ CONDITION_NAMES = {0: "ref-only", 1: "source-only", 2: "source+ref"}
80
+ _MIN_FREE_VRAM_GIB = 45.0
81
+ _GEN_TRANSFORM = T.Compose([v2.ToTensor(), v2.Normalize([0.5], [0.5])])
82
+
83
+
84
+ def set_seed(seed: int) -> None:
85
+ random.seed(seed)
86
+ np.random.seed(seed)
87
+ torch.manual_seed(seed)
88
+ torch.cuda.manual_seed_all(seed)
89
+
90
+
91
+ def _resolve_device(name: str) -> torch.device:
92
+ if name == "auto":
93
+ if not torch.cuda.is_available():
94
+ return torch.device("cpu")
95
+ best_i, best_free = 0, -1
96
+ for i in range(torch.cuda.device_count()):
97
+ free, _total = torch.cuda.mem_get_info(i)
98
+ if free > best_free:
99
+ best_free, best_i = free, i
100
+ free_gib = best_free / 1024**3
101
+ print(f"[device] auto-selected cuda:{best_i} ({free_gib:.1f} GiB free)")
102
+ if free_gib < _MIN_FREE_VRAM_GIB:
103
+ raise RuntimeError(
104
+ f"GPU cuda:{best_i} 仅 {free_gib:.1f} GiB 空闲,推理约需 {_MIN_FREE_VRAM_GIB:.0f} GiB+。"
105
+ )
106
+ return torch.device(f"cuda:{best_i}")
107
+ return torch.device(name)
108
+
109
+
110
+ def _move_model_to_device(model, device: torch.device, dtype: torch.dtype):
111
+ if device.type == "cpu":
112
+ return model.to(device=device, dtype=dtype)
113
+ model = model.to(device="cpu", dtype=dtype)
114
+ torch.cuda.empty_cache()
115
+ for attr in ("dit", "gen_vision_tower", "gen_down_projector"):
116
+ sub = getattr(model, attr, None)
117
+ if sub is not None:
118
+ setattr(model, attr, sub.to(device=device, dtype=dtype))
119
+ torch.cuda.empty_cache()
120
+ return model.to(device=device, dtype=dtype)
121
+
122
+
123
+ def _split_tag_checkpoint(raw: Dict[str, torch.Tensor]) -> tuple:
124
+ dit_w: Dict[str, torch.Tensor] = {}
125
+ proj_w: Dict[str, torch.Tensor] = {}
126
+ emb_w: Dict[str, torch.Tensor] = {}
127
+ for k, v in raw.items():
128
+ if k.startswith("stage1_model.dit."):
129
+ dit_w[k[len("stage1_model.dit.") :]] = v
130
+ elif k.startswith("dit."):
131
+ dit_w[k[len("dit.") :]] = v
132
+ elif k.startswith("stage1_model.gen_down_projector."):
133
+ proj_w[k[len("stage1_model.gen_down_projector.") :]] = v
134
+ elif k.startswith("gen_down_projector."):
135
+ proj_w[k[len("gen_down_projector.") :]] = v
136
+ elif k.startswith("tag_semantic.") or k.startswith("tag_par_head."):
137
+ continue
138
+ elif k.startswith("stage1_model."):
139
+ emb_w[k[len("stage1_model.") :]] = v
140
+ elif k.endswith("embed_tokens.weight") or k.endswith("lm_head.weight"):
141
+ emb_w[k] = v
142
+ return dit_w, proj_w, emb_w
143
+
144
+
145
+ def _gen_placeholder(num_metaqueries: int) -> str:
146
+ return (
147
+ "\n<begin_of_img>"
148
+ + "".join(f"<img{i}>" for i in range(num_metaqueries))
149
+ + "<end_of_img>"
150
+ )
151
+
152
+
153
+ def _build_gpt_response(
154
+ gen_content: str,
155
+ *,
156
+ inject_covt: bool,
157
+ covt_mode: str,
158
+ anchor_token_nums: Sequence[int],
159
+ anchor_tokens: Sequence[str],
160
+ anchor_task_names: Sequence[str],
161
+ seed: int,
162
+ ) -> str:
163
+ gpt_response = gen_content
164
+ if inject_covt and covt_mode != "none":
165
+ rng = random.Random(seed)
166
+ if covt_mode == "full":
167
+ gpt_response = get_comt_data_in_response(
168
+ gpt_response, list(anchor_token_nums), list(anchor_tokens), list(anchor_task_names)
169
+ )
170
+ elif covt_mode == "random":
171
+ if rng.random() < 0.5:
172
+ gpt_response = get_comt_data_in_response(
173
+ gpt_response, list(anchor_token_nums), list(anchor_tokens), list(anchor_task_names)
174
+ )
175
+ elif rng.randint(0, 5) != 0:
176
+ total = len(anchor_tokens)
177
+ if total > 0:
178
+ x = rng.randint(1, total)
179
+ idxs = sorted(rng.sample(range(total), x))
180
+ sel_nums = [anchor_token_nums[i] for i in idxs]
181
+ sel_toks = [anchor_tokens[i] for i in idxs]
182
+ sel_names = [anchor_task_names[i] for i in idxs]
183
+ gpt_response = get_comt_data_in_response(gpt_response, sel_nums, sel_toks, sel_names)
184
+ return f"{gpt_response}\n{DEFAULT_IM_END_TOKEN}\n"
185
+
186
+
187
+ def build_covt_encoded_input(
188
+ processor,
189
+ *,
190
+ prompt: str,
191
+ source_pil: Optional[Image.Image],
192
+ ref_pils: List[Image.Image],
193
+ num_metaqueries: int,
194
+ image_min_pixels: int,
195
+ image_max_pixels: int,
196
+ image_resized_width: int,
197
+ image_resized_height: int,
198
+ anchor_model_id: List[str],
199
+ covt_mode: str,
200
+ seed: int,
201
+ device: torch.device,
202
+ ) -> Dict[str, Any]:
203
+ """Mirror the experiment-local UMMMoEArrowDataset tokenization for one sample."""
204
+ merge_size = getattr(processor.image_processor, "merge_size", 2)
205
+ anchor_token_nums = get_token_num(anchor_model_id)
206
+ anchor_tokens = get_anchor_token(anchor_model_id)
207
+ anchor_task_names = get_anchor_task_name(anchor_model_id)
208
+
209
+ content: List[dict] = []
210
+ llm_vision_images: List[Any] = []
211
+ if source_pil is not None:
212
+ content.append({"type": "image", "image": source_pil})
213
+ llm_vision_images.append(
214
+ get_image_info(source_pil, image_min_pixels, image_max_pixels, image_resized_width, image_resized_height)
215
+ )
216
+ for ref_pil in ref_pils:
217
+ content.append({"type": "image", "image": ref_pil})
218
+ llm_vision_images.append(
219
+ get_image_info(ref_pil, image_min_pixels, image_max_pixels, image_resized_width, image_resized_height)
220
+ )
221
+ content.append({"type": "text", "text": prompt})
222
+
223
+ messages: List[dict] = []
224
+ if SYSTEM_MESSAGE:
225
+ messages.append({"role": "system", "content": SYSTEM_MESSAGE})
226
+ messages.append({"role": "user", "content": content})
227
+
228
+ user_str = processor.apply_chat_template(messages, add_generation_prompt=True, tokenize=False)
229
+ gpt_response = _build_gpt_response(
230
+ _gen_placeholder(num_metaqueries),
231
+ inject_covt=bool(llm_vision_images),
232
+ covt_mode=covt_mode,
233
+ anchor_token_nums=anchor_token_nums,
234
+ anchor_tokens=anchor_tokens,
235
+ anchor_task_names=anchor_task_names,
236
+ seed=seed,
237
+ )
238
+
239
+ inputs = processor(
240
+ text=[user_str],
241
+ images=llm_vision_images if llm_vision_images else None,
242
+ padding=False,
243
+ return_tensors="pt",
244
+ )
245
+ prompt_input_ids = inputs["input_ids"]
246
+ response_input_ids = processor.tokenizer(
247
+ gpt_response, add_special_tokens=False, padding=False, return_tensors="pt"
248
+ )["input_ids"]
249
+ input_ids = torch.cat([prompt_input_ids, response_input_ids], dim=1).squeeze(0)
250
+ attention_mask = (input_ids > -1000000).to(torch.long)
251
+ input_ids_2d = input_ids.unsqueeze(0)
252
+
253
+ encoded: Dict[str, Any] = {
254
+ "input_ids": input_ids.unsqueeze(0),
255
+ "attention_mask": attention_mask.unsqueeze(0),
256
+ }
257
+ if "pixel_values" in inputs and inputs["pixel_values"] is not None:
258
+ encoded["pixel_values"] = inputs["pixel_values"]
259
+ encoded["image_grid_thw"] = inputs["image_grid_thw"]
260
+
261
+ position_ids, _ = get_rope_index_3(
262
+ merge_size,
263
+ input_ids_2d,
264
+ image_grid_thw=encoded.get("image_grid_thw"),
265
+ video_grid_thw=None,
266
+ second_per_grid_ts=None,
267
+ )
268
+ encoded["position_ids"] = position_ids
269
+ return {k: v.to(device) for k, v in encoded.items() if hasattr(v, "to")}
270
+
271
+
272
+ def _zero_gen_tensor(image_size: int) -> torch.Tensor:
273
+ return torch.zeros(3, image_size, image_size, dtype=torch.float32)
274
+
275
+
276
+ def prepare_dit_gen_batch(
277
+ *,
278
+ source_pil: Optional[Image.Image],
279
+ ref_pils: List[Image.Image],
280
+ image_size: int,
281
+ max_reference_images: int,
282
+ device: torch.device,
283
+ dtype: torch.dtype,
284
+ ) -> tuple:
285
+ """Build source/reference pixel values like the experiment-local collator."""
286
+ has_source = source_pil is not None
287
+ src = _GEN_TRANSFORM(source_pil.convert("RGB")) if has_source else _zero_gen_tensor(image_size)
288
+ source_pv = bucket_images([src], resolution=image_size).to(device=device, dtype=dtype)
289
+
290
+ ref_tensors: List[torch.Tensor] = []
291
+ ref_mask: List[bool] = []
292
+ for ref_pil in ref_pils[:max_reference_images]:
293
+ ref_tensors.append(_GEN_TRANSFORM(ref_pil.convert("RGB")))
294
+ ref_mask.append(True)
295
+ while len(ref_tensors) < max_reference_images:
296
+ ref_tensors.append(_zero_gen_tensor(image_size))
297
+ ref_mask.append(False)
298
+
299
+ ref_bucketed = [bucket_images([t], resolution=image_size)[0] for t in ref_tensors]
300
+ ref_pv = torch.stack(ref_bucketed, dim=0).unsqueeze(0).to(device=device, dtype=dtype)
301
+
302
+ height = int(source_pv.shape[-2]) if has_source else image_size
303
+ width = int(source_pv.shape[-1]) if has_source else image_size
304
+ batch = {
305
+ "source_pixel_values": source_pv,
306
+ "has_source_image": torch.tensor([has_source], device=device),
307
+ "reference_pixel_values": ref_pv,
308
+ "reference_image_mask": torch.tensor([ref_mask], device=device),
309
+ }
310
+ return batch, height, width
311
+
312
+
313
+ def _extract_meta_hidden(model, hidden_states: torch.Tensor, input_ids: torch.Tensor) -> torch.Tensor:
314
+ boi_id = model.gen_begin_token_idx
315
+ eoi_id = model.gen_end_token_idx
316
+ ids = input_ids[0]
317
+ boi_pos = int((ids == boi_id).nonzero(as_tuple=True)[0][0].item())
318
+ eoi_pos = int((ids == eoi_id).nonzero(as_tuple=True)[0][0].item())
319
+ return hidden_states[:, boi_pos + 1 : eoi_pos, :]
320
+
321
+
322
+ @torch.no_grad()
323
+ def _encode_meta_hidden_omni(model, encoded: dict) -> torch.Tensor:
324
+ """Omni training path: embed prep + ``model.model`` forward (see ``stage1_flux_wrapper``)."""
325
+ input_ids = encoded["input_ids"]
326
+ attention_mask = encoded["attention_mask"]
327
+ pixel_values = encoded.get("pixel_values")
328
+ image_grid_thw = encoded.get("image_grid_thw")
329
+ position_ids = encoded.get("position_ids")
330
+
331
+ inputs_embeds, visual_pos_masks, deepstack_visual_embeds = (
332
+ model._prepare_multimodal_inputs_embeds_for_forward(
333
+ input_ids=input_ids,
334
+ pixel_values=pixel_values,
335
+ image_grid_thw=image_grid_thw,
336
+ )
337
+ )
338
+ qwen_kwargs: Dict[str, Any] = {
339
+ "input_ids": None,
340
+ "inputs_embeds": inputs_embeds,
341
+ "attention_mask": attention_mask,
342
+ "output_hidden_states": True,
343
+ }
344
+ if position_ids is not None:
345
+ qwen_kwargs["position_ids"] = position_ids
346
+ if visual_pos_masks is not None:
347
+ qwen_kwargs["visual_pos_masks"] = visual_pos_masks
348
+ if deepstack_visual_embeds is not None:
349
+ qwen_kwargs["deepstack_visual_embeds"] = deepstack_visual_embeds
350
+
351
+ qwen_out = model.model(**qwen_kwargs)
352
+ hidden = qwen_out.last_hidden_state if hasattr(qwen_out, "last_hidden_state") else qwen_out[0]
353
+ return _extract_meta_hidden(model, hidden, input_ids)
354
+
355
+
356
+ def load_omni_tag_model(args):
357
+ print(f"[load] OmniGenUnd from {args.model_path}")
358
+ compute_dtype = torch.bfloat16
359
+ attn_impl = "flash_attention_2" if not args.disable_flash_attn2 else "sdpa"
360
+
361
+ cfg = AutoConfig.from_pretrained(args.model_path)
362
+ src_model_type = getattr(cfg, "model_type", None)
363
+ if src_model_type != QwenOmniGenUndConfig.model_type:
364
+ cfg_dict = cfg.to_dict()
365
+ cfg_dict["model_type"] = QwenOmniGenUndConfig.model_type
366
+ cfg_dict["architectures"] = [OmniGenUndForConditionalGeneration.__name__]
367
+ cfg = QwenOmniGenUndConfig(**cfg_dict)
368
+ print(f"[load] remapped config {src_model_type} -> {QwenOmniGenUndConfig.model_type}")
369
+
370
+ model = OmniGenUndForConditionalGeneration.from_pretrained(
371
+ args.model_path,
372
+ config=cfg,
373
+ torch_dtype=compute_dtype,
374
+ attn_implementation=attn_impl,
375
+ ignore_mismatched_sizes=True,
376
+ )
377
+ model.config.use_cache = False
378
+ model.config.dit_pe_mode = str(args.dit_pe_mode)
379
+
380
+ processor = AutoProcessor.from_pretrained(args.model_path, padding_side="left")
381
+ tokenizer = processor.tokenizer
382
+ tokenizer.num_metaqueries = args.num_metaqueries
383
+ tokenizer.system_prompt = args.system_prompt
384
+
385
+ boi_id = int(tokenizer.convert_tokens_to_ids("<begin_of_img>"))
386
+ eoi_id = int(tokenizer.convert_tokens_to_ids("<end_of_img>"))
387
+ model.gen_begin_token_idx = boi_id
388
+ model.gen_end_token_idx = eoi_id
389
+ model.num_metaqueries = args.num_metaqueries
390
+ model.get_anchor_token_idx(args.num_metaqueries, boi_id, eoi_id)
391
+ print(f"[load] boi={boi_id} eoi={eoi_id} metaqueries={args.num_metaqueries}")
392
+
393
+ print(f"[load] initialize_gen_vision_modules from {args.flux_path}")
394
+ model.initialize_gen_vision_modules(args.flux_path, from_config=False)
395
+
396
+ if args.gen_down_projector_path:
397
+ print(f"[load] gen_down_projector from {args.gen_down_projector_path}")
398
+ model.load_gen_down_projector(args.gen_down_projector_path)
399
+
400
+ print(
401
+ f"[load] inject MoE-FFN-TAG experts={args.dit_moe_num_routed_experts} "
402
+ f"topk={args.dit_moe_num_activated_experts} shared={args.dit_moe_ffn_shared_expert} "
403
+ f"last_n={args.dit_moe_ffn_last_n_double_layers}"
404
+ )
405
+ inject_flux_moe_ffn_tag(
406
+ model.dit,
407
+ num_routed_experts=args.dit_moe_num_routed_experts,
408
+ num_activated_experts=args.dit_moe_num_activated_experts,
409
+ gate_aux_alpha=args.dit_moe_gate_aux_alpha,
410
+ use_shared_expert=args.dit_moe_ffn_shared_expert,
411
+ moe_last_n_double_layers=args.dit_moe_ffn_last_n_double_layers,
412
+ )
413
+
414
+ if not args.skip_checkpoint:
415
+ ckpt_path = args.checkpoint
416
+ if os.path.isdir(ckpt_path):
417
+ ckpt_path = os.path.join(ckpt_path, "trainable_weights.bin")
418
+ print(f"[load] trainable weights from {ckpt_path}")
419
+ raw = torch.load(ckpt_path, map_location="cpu", weights_only=False)
420
+ dit_w, proj_w, emb_w = _split_tag_checkpoint(raw)
421
+
422
+ missing, unexpected = model.dit.load_state_dict(dit_w, strict=False)
423
+ print(
424
+ f"[load] DiT+TAG: dit_keys={len(dit_w)} missing={len(missing)} unexpected={len(unexpected)}"
425
+ )
426
+ if proj_w:
427
+ missing_p, unexpected_p = model.gen_down_projector.load_state_dict(proj_w, strict=False)
428
+ print(
429
+ f"[load] gen_down_projector: keys={len(proj_w)} missing={len(missing_p)} "
430
+ f"unexpected={len(unexpected_p)}"
431
+ )
432
+ if emb_w:
433
+ missing_e, unexpected_e = model.load_state_dict(emb_w, strict=False)
434
+ print(f"[load] stage1 embed: keys={len(emb_w)} missing={len(missing_e)} unexpected={len(unexpected_e)}")
435
+ else:
436
+ print("[load] skip_checkpoint")
437
+
438
+ model.tokenizer = tokenizer
439
+ device = _resolve_device(args.device)
440
+ model = _move_model_to_device(model, device, compute_dtype)
441
+ model.eval()
442
+ print(f"[load] ready on {device} dtype={compute_dtype}")
443
+ return model, processor, device
444
+
445
+
446
+ @torch.no_grad()
447
+ def tag_covt_sample_images(
448
+ model,
449
+ encoded: dict,
450
+ dit_batch: dict,
451
+ *,
452
+ height: int,
453
+ width: int,
454
+ guidance_scale: float,
455
+ num_inference_steps: int,
456
+ generator: Optional[torch.Generator],
457
+ device: torch.device,
458
+ ) -> List[Image.Image]:
459
+ dtype = next(model.parameters()).dtype
460
+ vae_scale_factor = 2 ** (len(model.gen_vision_tower.vae.config.block_out_channels) - 1)
461
+ multiple_of = vae_scale_factor * 2
462
+ height = height // multiple_of * multiple_of
463
+ width = width // multiple_of * multiple_of
464
+
465
+ meta_hidden = _encode_meta_hidden_omni(model, encoded)
466
+
467
+ num_channels_latents = model.dit.config.in_channels // 4
468
+ noise = torch.randn(
469
+ 1,
470
+ num_channels_latents,
471
+ height // vae_scale_factor,
472
+ width // vae_scale_factor,
473
+ device=device,
474
+ dtype=dtype,
475
+ generator=generator,
476
+ )
477
+
478
+ latents, img_ids, l_noise = build_omni_dit_latent_input(
479
+ model,
480
+ dit_batch,
481
+ noisy_latents=noise,
482
+ B=1,
483
+ device=device,
484
+ dtype=dtype,
485
+ )
486
+ print(f" DiT tokens: total={latents.shape[1]} target_noise={l_noise} gen={height}x{width}")
487
+
488
+ scheduler = model.noise_scheduler
489
+ sigmas = np.linspace(1.0, 1 / num_inference_steps, num_inference_steps)
490
+ if getattr(scheduler.config, "use_flow_sigmas", False):
491
+ sigmas = None
492
+ mu = calculate_shift(
493
+ l_noise,
494
+ scheduler.config.get("base_image_seq_len", 256),
495
+ scheduler.config.get("max_image_seq_len", 4096),
496
+ scheduler.config.get("base_shift", 0.5),
497
+ scheduler.config.get("max_shift", 1.15),
498
+ )
499
+ timesteps, num_inference_steps = retrieve_timesteps(
500
+ scheduler, num_inference_steps, device, sigmas=sigmas, mu=mu
501
+ )
502
+ text_ids = torch.zeros(meta_hidden.shape[1], 3, device=device, dtype=dtype)
503
+ guidance = (
504
+ torch.full([1], guidance_scale, device=device, dtype=dtype)
505
+ if model.dit.config.guidance_embeds
506
+ else None
507
+ )
508
+
509
+ last_routing: List[torch.Tensor] = []
510
+ for t in tqdm(timesteps, desc="denoising"):
511
+ timestep = t.expand(latents.shape[0]).to(dtype)
512
+ joint_attention_kwargs = {
513
+ "num_target_tokens": l_noise,
514
+ "moe_aux_list": [],
515
+ "moe_routing_probs_list": last_routing,
516
+ }
517
+ prompt_embeds, pooled = model.gen_down_projector(meta_hidden, timestep / 1000)
518
+ noise_pred = model.dit(
519
+ hidden_states=latents,
520
+ timestep=timestep / 1000,
521
+ guidance=guidance,
522
+ pooled_projections=pooled,
523
+ encoder_hidden_states=prompt_embeds,
524
+ txt_ids=text_ids,
525
+ img_ids=img_ids,
526
+ joint_attention_kwargs=joint_attention_kwargs,
527
+ return_dict=False,
528
+ )[0]
529
+ noise_pred_tgt = noise_pred[:, :l_noise]
530
+ latents_tgt = latents[:, :l_noise]
531
+ latents_tgt = scheduler.step(noise_pred_tgt, t, latents_tgt, return_dict=False)[0]
532
+ if latents.shape[1] > l_noise:
533
+ latents = torch.cat([latents_tgt, latents[:, l_noise:]], dim=1)
534
+ else:
535
+ latents = latents_tgt
536
+
537
+ if last_routing:
538
+ g = torch.stack(last_routing, dim=0).mean(0)[0]
539
+ print(" routing: " + " ".join(f"e{i}={p:.3f}" for i, p in enumerate(g.tolist())))
540
+
541
+ latents_out = model._unpack_latents(latents[:, :l_noise], height, width, vae_scale_factor)
542
+ return model.decode_latents(latents_out.to(model.gen_vision_tower.vae.dtype))
543
+
544
+
545
+ def save_comparison(images: dict, out_path: str) -> None:
546
+ from PIL import ImageDraw, ImageFont
547
+
548
+ pils, labels = [], []
549
+ w_target = 512
550
+ for label, pil in images.items():
551
+ if pil is None:
552
+ continue
553
+ ratio = w_target / pil.width
554
+ pil = pil.resize((w_target, int(pil.height * ratio)), Image.LANCZOS)
555
+ pils.append(pil)
556
+ labels.append(label)
557
+ if not pils:
558
+ return
559
+ max_h = max(p.height for p in pils)
560
+ canvas = Image.new("RGB", (w_target * len(pils), max_h + 30), (240, 240, 240))
561
+ draw = ImageDraw.Draw(canvas)
562
+ font = ImageFont.load_default()
563
+ for i, (pil, label) in enumerate(zip(pils, labels)):
564
+ canvas.paste(pil, (i * w_target, 30))
565
+ draw.text((i * w_target + 5, 5), label, fill=(50, 50, 50), font=font)
566
+ Path(out_path).parent.mkdir(parents=True, exist_ok=True)
567
+ canvas.save(out_path)
568
+ print(f" saved → {out_path}")
569
+
570
+
571
+ # TUNABLE: x2edit / multi_ref 默认多 sample 评测路径
572
+ _X2EDIT_SRC = "/rczhang/rczhang/zhangrch/video_gen/gen_dataset/processed/x2edit/images/source"
573
+ _X2EDIT_TGT = "/rczhang/rczhang/zhangrch/video_gen/gen_dataset/processed/x2edit/images/target"
574
+ _MULTI_REF_ROOT = (
575
+ "/rczhang/rczhang/zhangrch/video_gen/gen_dataset/processed/"
576
+ "multi_ref_edit_2_extracted/data/samples/20260421_165536_8way/shard_00_of_08"
577
+ )
578
+ # TUNABLE: 与 configs/moe_ffn_tag.yaml data 一致
579
+ _DEFAULT_ARROW_DIR = (
580
+ "/rczhang/rczhang/zhangrch/video_gen/gen_dataset/processed/"
581
+ "instruction_only_quality_500k/x2edit/arrow"
582
+ )
583
+ _DEFAULT_IMAGE_ROOT = None
584
+
585
+
586
+ def _x2edit_case(idx: int, task_type: str, prompt: str) -> Dict[str, Any]:
587
+ sid = f"{idx:08d}"
588
+ return {
589
+ "name": f"{sid}_{task_type}",
590
+ "task_type": task_type,
591
+ "prompt": prompt,
592
+ "source": f"{_X2EDIT_SRC}/x2edit_train_{sid}.jpg",
593
+ "target": f"{_X2EDIT_TGT}/x2edit_train_{sid}.jpg",
594
+ }
595
+
596
+
597
+ # TUNABLE: 默认固定 case(5 类 task + 更多 index + source+ref)
598
+ DEFAULT_X2EDIT_MULTI_CASES = [
599
+ _x2edit_case(0, "camera_movement", "Rotate the bowl 45 degrees counterclockwise."),
600
+ _x2edit_case(1, "action_change", "a judge's gavel on a wooden table"),
601
+ _x2edit_case(2, "reasoning_edit", "Can you show me an image with more details about the ring's band?"),
602
+ _x2edit_case(5, "portrait_editing", "an elegant ring with a round diamond center"),
603
+ _x2edit_case(16, "text_change", "Can you remove the text 'Fashionthings' from the bracelet?"),
604
+ _x2edit_case(50, "camera_movement", "a man is sitting in a car on a stage"),
605
+ _x2edit_case(100, "action_change", "two stuffed eggplant halves on a plate with vegetables"),
606
+ _x2edit_case(200, "portrait_editing", "a pair of french style sofas with white upholstered cushions"),
607
+ _x2edit_case(500, "text_change", "Remove the text 'BOSCH' from the stove hood."),
608
+ _x2edit_case(1000, "action_change", "a man wearing a t - shirt with a monkey on it"),
609
+ ]
610
+
611
+ DEFAULT_MULTI_REF_CASES = [
612
+ {
613
+ "name": "multi_ref_subject_deletion",
614
+ "task_type": "subject_deletion",
615
+ "prompt": "Remove the Person in Image 1.",
616
+ "source": f"{_MULTI_REF_ROOT}/s00_267878_0d7bad31_original.jpg",
617
+ "reference": f"{_MULTI_REF_ROOT}/s00_267878_0d7bad31_reference_Person.jpg",
618
+ "target": f"{_MULTI_REF_ROOT}/s00_267878_0d7bad31_inpainted.jpg",
619
+ },
620
+ {
621
+ "name": "multi_ref_subject_addition",
622
+ "task_type": "subject_addition",
623
+ "prompt": "Add the Person in Image 1.",
624
+ "source": f"{_MULTI_REF_ROOT}/s00_267878_0d7bad31_inpainted.jpg",
625
+ "reference": f"{_MULTI_REF_ROOT}/s00_267878_0d7bad31_reference_Person.jpg",
626
+ "target": f"{_MULTI_REF_ROOT}/s00_267878_0d7bad31_original.jpg",
627
+ },
628
+ ]
629
+
630
+ DEFAULT_ALL_MULTI_CASES = DEFAULT_X2EDIT_MULTI_CASES + DEFAULT_MULTI_REF_CASES
631
+
632
+
633
+ def _resolve_image_path(path: str, image_root: Optional[str]) -> str:
634
+ path = str(path or "").strip()
635
+ if not path:
636
+ return ""
637
+ if os.path.isabs(path) or not image_root:
638
+ return path
639
+ return os.path.join(image_root, path)
640
+
641
+
642
+ def _row_to_case(row: Dict[str, Any], idx: int, image_root: Optional[str]) -> Optional[Dict[str, Any]]:
643
+ target = _resolve_image_path(str(row.get("target_image") or ""), image_root)
644
+ source = _resolve_image_path(str(row.get("source_image") or ""), image_root)
645
+ if not target or not os.path.isfile(target):
646
+ return None
647
+
648
+ refs_raw = row.get("reference_images") or []
649
+ if isinstance(refs_raw, str):
650
+ refs_raw = [refs_raw] if refs_raw else []
651
+ ref_paths = [
652
+ p for p in (_resolve_image_path(str(r), image_root) for r in refs_raw) if p and os.path.isfile(p)
653
+ ]
654
+
655
+ has_source = bool(source) and os.path.isfile(source)
656
+ has_ref = bool(ref_paths)
657
+ if not has_source and not has_ref:
658
+ return None
659
+
660
+ task_type = str(row.get("task_type") or "other").strip() or "other"
661
+ if task_type not in TASK_TYPE_TO_ID:
662
+ task_type = "other"
663
+ instruction = str(row.get("instruction") or "").strip()
664
+ if not instruction:
665
+ return None
666
+
667
+ sample_id = str(row.get("sample_id") or row.get("id") or f"idx{idx:08d}")
668
+ case: Dict[str, Any] = {
669
+ "name": f"arrow_{idx:08d}_{task_type}",
670
+ "task_type": task_type,
671
+ "prompt": instruction,
672
+ "target": target,
673
+ }
674
+ if has_source:
675
+ case["source"] = source
676
+ if has_ref:
677
+ case["reference"] = ref_paths[0] if len(ref_paths) == 1 else ",".join(ref_paths)
678
+ return case
679
+
680
+
681
+ def load_cases_from_arrow_indices(
682
+ arrow_dir: str,
683
+ image_root: Optional[str],
684
+ indices: Sequence[int],
685
+ ) -> List[Dict[str, Any]]:
686
+ from datasets import load_from_disk
687
+
688
+ ds = load_from_disk(arrow_dir)
689
+ cases: List[Dict[str, Any]] = []
690
+ for idx in indices:
691
+ if idx < 0 or idx >= len(ds):
692
+ print(f"[warn] arrow index {idx} out of range (len={len(ds)}), skip")
693
+ continue
694
+ case = _row_to_case(ds[int(idx)], int(idx), image_root)
695
+ if case is None:
696
+ print(f"[warn] arrow index {idx} invalid (missing image/instruction), skip")
697
+ continue
698
+ cases.append(case)
699
+ return cases
700
+
701
+
702
+ def sample_cases_from_arrow(
703
+ arrow_dir: str,
704
+ image_root: Optional[str],
705
+ num_samples: int,
706
+ seed: int,
707
+ *,
708
+ source_only: bool = True,
709
+ ) -> List[Dict[str, Any]]:
710
+ from datasets import load_from_disk
711
+
712
+ ds = load_from_disk(arrow_dir)
713
+ rng = np.random.default_rng(seed)
714
+ order = rng.permutation(len(ds))
715
+ cases: List[Dict[str, Any]] = []
716
+
717
+ for idx in order:
718
+ row = ds[int(idx)]
719
+ source = _resolve_image_path(str(row.get("source_image") or ""), image_root)
720
+ has_source = bool(source) and os.path.isfile(source)
721
+ refs_raw = row.get("reference_images") or []
722
+ if isinstance(refs_raw, str):
723
+ refs_raw = [refs_raw] if refs_raw else []
724
+ has_ref = bool(refs_raw)
725
+ if source_only and not has_source:
726
+ continue
727
+ case = _row_to_case(row, int(idx), image_root)
728
+ if case is None:
729
+ continue
730
+ cases.append(case)
731
+ if len(cases) >= num_samples:
732
+ break
733
+
734
+ if len(cases) < num_samples:
735
+ raise RuntimeError(
736
+ f"Only found {len(cases)} valid arrow samples (requested {num_samples}). "
737
+ "Check arrow_dir / image_root or relax source_only."
738
+ )
739
+ return cases
740
+
741
+
742
+ def resolve_multi_cases(args) -> List[Dict[str, Any]]:
743
+ cases = list(DEFAULT_ALL_MULTI_CASES)
744
+ if args.arrow_indices:
745
+ indices = [int(x.strip()) for x in args.arrow_indices.split(",") if x.strip()]
746
+ cases.extend(load_cases_from_arrow_indices(args.arrow_dir, args.image_root, indices))
747
+ if args.multi_from_arrow > 0:
748
+ cases.extend(
749
+ sample_cases_from_arrow(
750
+ args.arrow_dir,
751
+ args.image_root,
752
+ args.multi_from_arrow,
753
+ args.seed,
754
+ source_only=not args.multi_include_ref,
755
+ )
756
+ )
757
+ if args.multi_limit > 0:
758
+ cases = cases[: args.multi_limit]
759
+ return cases
760
+
761
+
762
+ def run_single_case(
763
+ model,
764
+ processor,
765
+ device,
766
+ dtype,
767
+ args,
768
+ *,
769
+ case_name: str,
770
+ task_type: str,
771
+ prompt: str,
772
+ source_image: Optional[str],
773
+ target_image: Optional[str],
774
+ reference_image: Optional[str] = None,
775
+ case_output_dir: Path,
776
+ seed: int,
777
+ ) -> None:
778
+ anchor_model_id = ast.literal_eval(args.anchor_model_id)
779
+ case_output_dir.mkdir(parents=True, exist_ok=True)
780
+
781
+ source_pil = Image.open(source_image).convert("RGB") if source_image else None
782
+ ref_paths = [p.strip() for p in reference_image.split(",") if p.strip()] if reference_image else []
783
+ if len(ref_paths) > args.max_reference_images:
784
+ print(
785
+ f"[warn] truncating references from {len(ref_paths)} "
786
+ f"to {args.max_reference_images}"
787
+ )
788
+ ref_paths = ref_paths[: args.max_reference_images]
789
+ ref_pils = [Image.open(p).convert("RGB") for p in ref_paths]
790
+ gt_pil = Image.open(target_image).convert("RGB") if target_image else None
791
+
792
+ has_source = source_pil is not None
793
+ has_ref = len(ref_pils) > 0
794
+ cond_id = 2 if has_source and has_ref else 1 if has_source else 0 if has_ref else -1
795
+ task_type_id = TASK_TYPE_TO_ID.get(task_type, TASK_TYPE_TO_ID["other"])
796
+ print(
797
+ f"\n=== case={case_name} ===\n"
798
+ f"task_type={task_type}({task_type_id}) "
799
+ f"condition={CONDITION_NAMES.get(cond_id, 't2i')}({cond_id}) covt_mode={args.covt_mode}\n"
800
+ f"prompt: {prompt}"
801
+ )
802
+
803
+ encoded = build_covt_encoded_input(
804
+ processor,
805
+ prompt=prompt,
806
+ source_pil=source_pil,
807
+ ref_pils=ref_pils,
808
+ num_metaqueries=args.num_metaqueries,
809
+ image_min_pixels=args.image_min_pixels,
810
+ image_max_pixels=args.image_max_pixels,
811
+ image_resized_width=args.image_resized_width,
812
+ image_resized_height=args.image_resized_height,
813
+ anchor_model_id=anchor_model_id,
814
+ covt_mode=args.covt_mode,
815
+ seed=seed,
816
+ device=device,
817
+ )
818
+
819
+ dit_batch, gen_h, gen_w = prepare_dit_gen_batch(
820
+ source_pil=source_pil,
821
+ ref_pils=ref_pils,
822
+ image_size=args.image_size,
823
+ max_reference_images=args.max_reference_images,
824
+ device=device,
825
+ dtype=dtype,
826
+ )
827
+ if not has_source:
828
+ gen_h, gen_w = args.height, args.width
829
+
830
+ gen = torch.Generator(device=device).manual_seed(seed)
831
+ images = tag_covt_sample_images(
832
+ model,
833
+ encoded,
834
+ dit_batch,
835
+ height=gen_h,
836
+ width=gen_w,
837
+ guidance_scale=args.guidance_scale,
838
+ num_inference_steps=args.steps,
839
+ generator=gen,
840
+ device=device,
841
+ )
842
+
843
+ out_pil = images[0] if images else None
844
+ if out_pil is not None:
845
+ out_path = case_output_dir / "generated.png"
846
+ out_pil.save(out_path)
847
+ print(f" saved → {out_path}")
848
+
849
+ save_comparison(
850
+ {"source": source_pil, "generated": out_pil, "target": gt_pil},
851
+ str(case_output_dir / "comparison.png"),
852
+ )
853
+
854
+
855
+ def main():
856
+ parser = argparse.ArgumentParser(description="TAG-MoE quality500k-v1 inference")
857
+ # TUNABLE: 与 configs/moe_ffn_tag.yaml model 段一致
858
+ parser.add_argument(
859
+ "--model_path",
860
+ default="/rczhang/rczhang/zhangrch/video_gen/gen_checkpoint/stage3_dino_depth_siglip_pidinet_lora16_zero2_trainable_embed_lm_head_downpro_web18m_gen_llmlora_v2/lora_merged/lora_stage3_und_gen_merged_30k",
861
+ )
862
+ parser.add_argument(
863
+ "--flux_path",
864
+ default="/rczhang/rczhang/zhangrch/video_gen/pretrined_models/FLUX.1-dev",
865
+ )
866
+ parser.add_argument(
867
+ "--gen_down_projector_path",
868
+ default="/rczhang/rczhang/zhangrch/video_gen/gen_checkpoint/stage3_dino_depth_siglip_pidinet_lora16_zero2_trainable_embed_lm_head_downpro_web18m_gen_llmlora_v2/lora_vision_test/lora_stage3_und_gen/embeddings_connector/checkpoint-30000.bin",
869
+ )
870
+ parser.add_argument(
871
+ "--checkpoint",
872
+ default="/rczhang/rczhang/zhangrch/video_gen/gen_checkpoint/moe_ffn_tag_covt_quality500k_v1_moe_fast/checkpoint-1000/trainable_weights.bin",
873
+ )
874
+ parser.add_argument(
875
+ "--anchor_model_id",
876
+ default="['dino', 'depth', 'siglip', 'pidinet']",
877
+ help="CoVT anchor 列表,与实验本地 data.py 一致",
878
+ )
879
+ parser.add_argument("--prompt", default="Rotate the bowl 45 degrees counterclockwise.")
880
+ parser.add_argument("--source_image", default=None)
881
+ parser.add_argument("--reference_image", default=None)
882
+ parser.add_argument("--target_image", default=None)
883
+ parser.add_argument("--task_type", default="camera_movement", choices=TASK_TYPES)
884
+ parser.add_argument("--output_dir", default="./tag_covt_infer_out")
885
+ parser.add_argument("--num_metaqueries", type=int, default=256)
886
+ parser.add_argument("--system_prompt", default="You are a helpful assistant.")
887
+ parser.add_argument("--disable_flash_attn2", action="store_true")
888
+ parser.add_argument("--device", default="auto")
889
+ parser.add_argument("--dit_moe_num_routed_experts", type=int, default=4)
890
+ parser.add_argument("--dit_moe_num_activated_experts", type=int, default=1)
891
+ parser.add_argument("--dit_moe_gate_aux_alpha", type=float, default=0.01)
892
+ parser.add_argument("--dit_moe_ffn_shared_expert", action=argparse.BooleanOptionalAction, default=True)
893
+ parser.add_argument("--dit_moe_ffn_last_n_double_layers", type=int, default=10)
894
+ parser.add_argument("--dit_pe_mode", default="d")
895
+ parser.add_argument("--image_size", type=int, default=1024)
896
+ parser.add_argument("--image_min_pixels", type=int, default=200704)
897
+ parser.add_argument("--image_max_pixels", type=int, default=802816)
898
+ parser.add_argument("--image_resized_width", type=int, default=448)
899
+ parser.add_argument("--image_resized_height", type=int, default=448)
900
+ parser.add_argument("--max_reference_images", type=int, default=4)
901
+ parser.add_argument("--height", type=int, default=1024)
902
+ parser.add_argument("--width", type=int, default=1024)
903
+ parser.add_argument("--steps", type=int, default=30)
904
+ parser.add_argument("--guidance_scale", type=float, default=1.0)
905
+ parser.add_argument("--seed", type=int, default=42)
906
+ parser.add_argument(
907
+ "--covt_mode",
908
+ choices=("full", "random", "none"),
909
+ default="full",
910
+ help="assistant CoVT 注入:full=全 anchor(I2I 推荐);random=复现训练随机;none=仅 gen placeholder",
911
+ )
912
+ parser.add_argument("--skip_checkpoint", action="store_true")
913
+ parser.add_argument(
914
+ "--multi_x2edit",
915
+ action="store_true",
916
+ help="一次加载模型,跑默认多样本(10 x2edit + 2 source+ref,可用 --multi_limit 截断)",
917
+ )
918
+ # TUNABLE: 多样本数量与 arrow 采样
919
+ parser.add_argument(
920
+ "--multi_limit",
921
+ type=int,
922
+ default=0,
923
+ help=">0 时只跑前 N 条 multi case(默认 0=全部)",
924
+ )
925
+ parser.add_argument(
926
+ "--multi_from_arrow",
927
+ type=int,
928
+ default=0,
929
+ help=">0 时从 arrow 随机再抽 N 条(需 --arrow_dir)",
930
+ )
931
+ parser.add_argument(
932
+ "--multi_include_ref",
933
+ action="store_true",
934
+ help="--multi_from_arrow 时允许 source+ref 样本(默认仅 source-only)",
935
+ )
936
+ parser.add_argument("--arrow_dir", default=_DEFAULT_ARROW_DIR)
937
+ parser.add_argument("--image_root", default=_DEFAULT_IMAGE_ROOT)
938
+ parser.add_argument(
939
+ "--arrow_indices",
940
+ default="",
941
+ help="逗号分隔 arrow index,追加到默认 case 列表(如 300,800,1200)",
942
+ )
943
+ args = parser.parse_args()
944
+
945
+ set_seed(args.seed)
946
+ Path(args.output_dir).mkdir(parents=True, exist_ok=True)
947
+
948
+ model, processor, device = load_omni_tag_model(args)
949
+ dtype = next(model.parameters()).dtype
950
+
951
+ if args.multi_x2edit:
952
+ multi_cases = resolve_multi_cases(args)
953
+ print(f"[multi] running {len(multi_cases)} cases → {args.output_dir}")
954
+ for i, case in enumerate(multi_cases):
955
+ run_single_case(
956
+ model,
957
+ processor,
958
+ device,
959
+ dtype,
960
+ args,
961
+ case_name=case["name"],
962
+ task_type=case["task_type"],
963
+ prompt=case["prompt"],
964
+ source_image=case.get("source"),
965
+ target_image=case.get("target"),
966
+ reference_image=case.get("reference"),
967
+ case_output_dir=Path(args.output_dir) / case["name"],
968
+ seed=args.seed + i,
969
+ )
970
+ print(f"\n[multi] all done → {args.output_dir}")
971
+ return
972
+
973
+ if not args.source_image and not args.reference_image:
974
+ raise SystemExit("请指定 --source_image/--reference_image,或使用 --multi_x2edit")
975
+
976
+ run_single_case(
977
+ model,
978
+ processor,
979
+ device,
980
+ dtype,
981
+ args,
982
+ case_name="single",
983
+ task_type=args.task_type,
984
+ prompt=args.prompt,
985
+ source_image=args.source_image,
986
+ target_image=args.target_image,
987
+ reference_image=args.reference_image,
988
+ case_output_dir=Path(args.output_dir),
989
+ seed=args.seed,
990
+ )
991
+
992
+
993
+ if __name__ == "__main__":
994
+ main()
code/umm/inference/run_infer.sh ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ STAGE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5
+ REPO_ROOT="$(cd "${STAGE_DIR}/../../.." && pwd)"
6
+ cd "${REPO_ROOT}"
7
+
8
+ export PYTHONPATH="${REPO_ROOT}/hc_test/umm/runtime:${REPO_ROOT}:${PYTHONPATH:-}"
9
+ export CUDA_VISIBLE_DEVICES="${CUDA_VISIBLE_DEVICES:-0}"
10
+ export HF_HOME="${HF_HOME:-/rczhang/rczhang/zhangrch/video_gen/omnigen/cache/huggingface}"
11
+
12
+ unset http_proxy https_proxy HTTP_PROXY HTTPS_PROXY all_proxy ALL_PROXY
13
+
14
+ PYTHON_BIN="${PYTHON_BIN:-/rczhang/rczhang/conda_packs/unvideo/bin/python}"
15
+ MODEL_PATH="${MODEL_PATH:-/rczhang/rczhang/zhangrch/video_gen/gen_checkpoint/stage3_dino_depth_siglip_pidinet_lora16_zero2_trainable_embed_lm_head_downpro_web18m_gen_llmlora_v2/lora_merged/lora_stage3_und_gen_merged_30k}"
16
+ FLUX_PATH="${FLUX_PATH:-/rczhang/rczhang/zhangrch/video_gen/pretrined_models/FLUX.1-dev}"
17
+ PROJECTOR_PATH="${PROJECTOR_PATH:-/rczhang/rczhang/zhangrch/video_gen/gen_checkpoint/stage3_dino_depth_siglip_pidinet_lora16_zero2_trainable_embed_lm_head_downpro_web18m_gen_llmlora_v2/lora_vision_test/lora_stage3_und_gen/embeddings_connector/checkpoint-30000.bin}"
18
+ CHECKPOINT="${CHECKPOINT:-/rczhang/rczhang/zhangrch/video_gen/gen_checkpoint/moe_ffn_tag_covt_quality500k_v1_moe_fast/checkpoint-1000/trainable_weights.bin}"
19
+ SOURCE_IMAGE="${SOURCE_IMAGE:-}"
20
+ REFERENCE_IMAGE="${REFERENCE_IMAGE:-}"
21
+ TARGET_IMAGE="${TARGET_IMAGE:-}"
22
+ PROMPT="${PROMPT:-Rotate the bowl 45 degrees counterclockwise.}"
23
+ TASK_TYPE="${TASK_TYPE:-camera_movement}"
24
+ OUTPUT_DIR="${OUTPUT_DIR:-${STAGE_DIR}/outputs}"
25
+ IMAGE_SIZE="${IMAGE_SIZE:-512}"
26
+ STEPS="${STEPS:-30}"
27
+ SEED="${SEED:-42}"
28
+ COVT_MODE="${COVT_MODE:-full}"
29
+
30
+ CMD=(
31
+ "${PYTHON_BIN}" "${STAGE_DIR}/infer.py"
32
+ --model_path "${MODEL_PATH}"
33
+ --flux_path "${FLUX_PATH}"
34
+ --gen_down_projector_path "${PROJECTOR_PATH}"
35
+ --checkpoint "${CHECKPOINT}"
36
+ --prompt "${PROMPT}"
37
+ --task_type "${TASK_TYPE}"
38
+ --output_dir "${OUTPUT_DIR}"
39
+ --covt_mode "${COVT_MODE}"
40
+ --image_size "${IMAGE_SIZE}"
41
+ --height "${IMAGE_SIZE}"
42
+ --width "${IMAGE_SIZE}"
43
+ --steps "${STEPS}"
44
+ --seed "${SEED}"
45
+ )
46
+
47
+ if [[ -n "${SOURCE_IMAGE}" ]]; then
48
+ CMD+=(--source_image "${SOURCE_IMAGE}")
49
+ fi
50
+ if [[ -n "${REFERENCE_IMAGE}" ]]; then
51
+ CMD+=(--reference_image "${REFERENCE_IMAGE}")
52
+ fi
53
+ if [[ -n "${TARGET_IMAGE}" ]]; then
54
+ CMD+=(--target_image "${TARGET_IMAGE}")
55
+ fi
56
+ if [[ "${MULTI_X2EDIT:-0}" == "1" ]]; then
57
+ CMD+=(--multi_x2edit)
58
+ fi
59
+ CMD+=("$@")
60
+
61
+ if [[ "${DRY_RUN:-0}" == "1" ]]; then
62
+ printf '%q ' "${CMD[@]}"
63
+ printf '\n'
64
+ exit 0
65
+ fi
66
+
67
+ if [[ -z "${SOURCE_IMAGE}" && -z "${REFERENCE_IMAGE}" && "${MULTI_X2EDIT:-0}" != "1" ]]; then
68
+ echo "请通过 SOURCE_IMAGE=/path 或 REFERENCE_IMAGE=/path 指定输入;也可设置 MULTI_X2EDIT=1。" >&2
69
+ exit 2
70
+ fi
71
+
72
+ exec "${CMD[@]}"
code/umm/runtime/README.md ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Runtime:UMM 本地共享源码
2
+
3
+ 这里是各阶段真正导入的共享源码副本,不是跳转层。
4
+
5
+ ```text
6
+ runtime/
7
+ ├── models/blip3o/ Qwen/CoVT/FLUX、dataset、trainer、MoE 实现
8
+ └── deepspeed_configs/zero2.json
9
+ ```
10
+
11
+ 所有 `run_*.sh` 都把本目录放在 `PYTHONPATH` 第一位:
12
+
13
+ ```bash
14
+ export PYTHONPATH=".../hc_test/umm/runtime:.../unigen_ar_v1:${PYTHONPATH:-}"
15
+ ```
16
+
17
+ 因此 `import models.blip3o...` 实际命中本目录。可以用下面的命令确认:
18
+
19
+ ```bash
20
+ /rczhang/rczhang/conda_packs/unvideo/bin/python \
21
+ hc_test/umm/runtime/check_imports.py
22
+ ```
23
+
24
+ 输出的三个模型路径都应位于 `hc_test/umm/runtime/models/blip3o/`。
25
+
26
+ ## 哪些内容仍在目录外
27
+
28
+ 只保留大文件的路径引用,不重复复制:
29
+
30
+ - Qwen、FLUX、DINO、Depth、PiDiNet、SigLIP 等预训练权重;
31
+ - JSON/JSONL、Arrow 与图片数据;
32
+ - 已有 dense、LoRA、projector、MoE checkpoint;
33
+ - Hugging Face、datasets、W&B 与临时缓存。
34
+
35
+ 本目录中没有 `.pth`、`.bin`、`.safetensors` 或 `.ckpt` 权重文件。修改模型、dataset 或 trainer 时,应修改这里的副本;阶段入口的专属逻辑则修改相应 `stage*/train.py`。
36
+
37
+ ## 本地修正
38
+
39
+ 复制后做了让本工作区自洽并保证 Stage-B 训练正确性的修正:
40
+
41
+ - Stage 1/2/3 使用显式模型类导入,避免包级通配导入加载无关旧模块;
42
+ - Stage 3 FLUX 模型显式导入 Qwen3-VL 的两个输出类型;
43
+ - 各阶段增加本地 YAML 配置解析;
44
+ - Stage 4 与 inference 的 experiment-local import 改为 `hc_test.umm` 路径。
45
+ - Stage 2 加入 frozen SAM ViT-H 自动伪 mask、8-query decoder prompt,以及论文定义的 Hungarian Dice+Focal segmentation loss;
46
+ - CoVT curriculum 统一使用 Trainer optimizer `global_step`,避免 worker/梯度累积改变阶段边界;
47
+ - 修正真实 anchor token 注册、Stage-1 query 行加载、专家头确定性初始化、非有限 loss 硬检查;
48
+ - checkpoint 单 rank 写入并显式恢复 non-LoRA trainables 与专家 sidecar,保证断点续训不重置视觉能力头。
49
+
50
+ 这些修改都只发生在 `hc_test/umm` 内,没有改写原模型源码。
code/umm/runtime/check_imports.py ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Confirm that UMM resolves shared model code from this local runtime copy."""
3
+
4
+ from __future__ import annotations
5
+
6
+ from pathlib import Path
7
+
8
+ from models.blip3o.model.language_model import (
9
+ blip3o_qwen_stage1,
10
+ covt_qwen_stage2_van,
11
+ moe_qwen_stage3_flux,
12
+ )
13
+
14
+
15
+ def main() -> None:
16
+ runtime_root = Path(__file__).resolve().parent
17
+ modules = {
18
+ "stage1_model": blip3o_qwen_stage1,
19
+ "stage2_model": covt_qwen_stage2_van,
20
+ "stage3_model": moe_qwen_stage3_flux,
21
+ }
22
+ for name, module in modules.items():
23
+ path = Path(module.__file__).resolve()
24
+ print(f"{name}={path}")
25
+ if runtime_root not in path.parents:
26
+ raise RuntimeError(f"{name} did not resolve from UMM runtime: {path}")
27
+ print("status=ok")
28
+
29
+
30
+ if __name__ == "__main__":
31
+ main()
code/umm/runtime/deepspeed_configs/zero2.json ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "fp16": {
3
+ "enabled": "auto",
4
+ "loss_scale": 0,
5
+ "loss_scale_window": 1000,
6
+ "initial_scale_power": 16,
7
+ "hysteresis": 2,
8
+ "min_loss_scale": 1
9
+ },
10
+ "bf16": {
11
+ "enabled": "auto"
12
+ },
13
+ "optimizer": {
14
+ "type": "AdamW",
15
+ "params": {
16
+ "lr": "auto",
17
+ "betas": "auto",
18
+ "eps": "auto",
19
+ "weight_decay": "auto"
20
+ }
21
+ },
22
+ "zero_optimization": {
23
+ "stage": 2,
24
+ "offload_optimizer": {
25
+ "device": "none",
26
+ "pin_memory": true
27
+ },
28
+ "allgather_partitions": true,
29
+ "allgather_bucket_size": 5e8,
30
+ "overlap_comm": true,
31
+ "reduce_scatter": true,
32
+ "reduce_bucket_size": 5e8,
33
+ "contiguous_gradients": true,
34
+ "ignore_unused_parameters": true
35
+ },
36
+ "gradient_accumulation_steps": "auto",
37
+ "gradient_clipping": "auto",
38
+ "steps_per_print": 100,
39
+ "train_batch_size": "auto",
40
+ "train_micro_batch_size_per_gpu": "auto",
41
+ "wall_clock_breakdown": false
42
+ }
code/umm/runtime/models/__init__.py ADDED
File without changes
code/umm/runtime/models/blip3o/constants.py ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ CONTROLLER_HEART_BEAT_EXPIRATION = 30
2
+ WORKER_HEART_BEAT_INTERVAL = 15
3
+
4
+ LOGDIR = "."
5
+
6
+ IGNORE_INDEX = -100
7
+
8
+ # DEFAULT_IMAGE_TOKEN = "<image>"
9
+ # DEFAULT_IM_START_TOKEN = "[IMG]"
10
+ # DEFAULT_IM_END_TOKEN = "[/IMG]"
11
+
12
+
13
+ # IMAGE_TOKEN_IDX = 151667
14
+ T2I_TOKEN_IDX = 151666
15
+ I2I_TOKEN_IDX = 151667
16
+
17
+
18
+ UND_IMAGE_TOKEN = "<|image_pad|>"
19
+ UND_IMAGE_TOKEN_IDX = 151655
20
+
21
+
22
+ DEFAULT_IM_START_TOKEN = "<|im_start|>"
23
+ DEFAULT_IM_END_TOKEN = "<|im_end|>"
24
+ DEFAULT_IMAGE_TOKEN = "<|image_pad|>"
25
+ DEFAULT_VIDEO_TOKEN = "<|video_pad|>"
26
+ LLAVA_IMAGE_TOKEN = "<image>"
27
+ LLAVA_VIDEO_TOKEN = "<video>"
28
+ VISION_START_TOKEN = "<|vision_start|>"
29
+ VISION_END_TOKEN = "<|vision_end|>"
30
+
31
+ ANCHOR_START_TOKEN = "<|anchor_start|>"
32
+ ANCHOR_END_TOKEN = "<|anchor_end|>"
33
+ SAM_PAD_TOKEN = "<|sam_pad|>"
34
+ DINO_PAD_TOKEN = "<|dino_pad|>"
35
+ DEPTH_PAD_TOKEN = "<|depth_pad|>"
36
+ SD_PAD_TOKEN = "<|sd_pad|>"
37
+ INTERN_PAD_TOKEN = "<|intern_pad|>"
38
+ PIDINET_PAD_TOKEN = "<|pidinet_pad|>"
39
+ SIGLIP_PAD_TOKEN = "<|siglip_pad|>"
40
+ METACLIP_PAD_TOKEN = "<|metaclip_pad|>"
41
+
42
+ # Stage-B CoVT semantic-token counts. Keep these counts in one place because
43
+ # the dataset, Stage-2 geometry projector, and Stage-3 I2I suffix must agree.
44
+ COVT_SAM_TOKEN_COUNT = 8
45
+ COVT_DINO_TOKEN_COUNT = 4
46
+ COVT_DEPTH_TOKEN_COUNT = 4
47
+ COVT_PIDINET_TOKEN_COUNT = 4
48
+ COVT_SIGLIP_TOKEN_COUNT = 4
49
+
50
+
51
+ # --- TUNABLE BLOCK: Stage-3 gen anchor strings (one tokenizer id each after add_tokens; keep in sync with train_stage3_covt_gen / covt_data_var) ---
52
+ VIS_DINO_ANCHOR_TOKENS = [f"<|vis_dino_pad_{i}|>" for i in range(4)]
53
+ VIS_DEPTH_ANCHOR_TOKENS = [f"<|vis_depth_pad_{i}|>" for i in range(4)]
54
+ VIS_PIDINET_ANCHOR_TOKENS = [f"<|vis_pidinet_pad_{i}|>" for i in range(4)]
55
+ VIS_SIGLIP_ANCHOR_TOKENS = [f"<|vis_siglip_pad_{i}|>" for i in range(4)]
56
+
57
+ # Optional I2I: append CoVT anchor pads after the generation placeholder — **same layout as stage-2** covt_data:
58
+ # ``<|anchor_start|><|sam_pad|>*8<|anchor_end|>`` + ``<|anchor_start|><|dino_pad|>*4<|anchor_end|>`` + ``<|anchor_start|><|depth_pad|>*4<|anchor_end|>``
59
+ # (counts from ``covt_data.get_token_num`` for sam / dino / depth). Do **not** spell ``<|*_pad|>`` in the human
60
+ # turn, or those strings tokenize to duplicate ids and break parity with stage 2.
61
+ def _i2i_covt_anchor_blocks_stage2_sam_dino_depth() -> str:
62
+ return (
63
+ ANCHOR_START_TOKEN
64
+ + SAM_PAD_TOKEN * COVT_SAM_TOKEN_COUNT
65
+ + ANCHOR_END_TOKEN
66
+ + ANCHOR_START_TOKEN
67
+ + DINO_PAD_TOKEN * COVT_DINO_TOKEN_COUNT
68
+ + ANCHOR_END_TOKEN
69
+ + ANCHOR_START_TOKEN
70
+ + DEPTH_PAD_TOKEN * COVT_DEPTH_TOKEN_COUNT
71
+ + ANCHOR_END_TOKEN
72
+ )
73
+
74
+
75
+ I2I_COVT_ANCHOR_HUMAN_EXTRA = (
76
+ "\n\n[Format requirement] After the generation block (<begin_of_img>…<end_of_img>), the assistant reply must "
77
+ "include the same CoVT anchor pad blocks as stage-2 training (SAM / DINO / depth counts between "
78
+ "anchor start and end markers). Do not omit or reorder them."
79
+ )
80
+ I2I_COVT_ANCHOR_GPT_SUFFIX = "\n" + _i2i_covt_anchor_blocks_stage2_sam_dino_depth() + "\n"
81
+
82
+ SYSTEM_MESSAGE = "You are a helpful assistant."
code/umm/runtime/models/blip3o/conversation.py ADDED
@@ -0,0 +1,479 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import dataclasses
2
+ from enum import auto, Enum
3
+ from typing import List, Tuple
4
+ import base64
5
+ from io import BytesIO
6
+ from PIL import Image
7
+
8
+
9
+ class SeparatorStyle(Enum):
10
+ """Different separator style."""
11
+ SINGLE = auto()
12
+ TWO = auto()
13
+ MPT = auto()
14
+ PLAIN = auto()
15
+ LLAMA_2 = auto()
16
+ CHATML = auto()
17
+ QWEN = auto()
18
+
19
+
20
+ @dataclasses.dataclass
21
+ class Conversation:
22
+ """A class that keeps all conversation history."""
23
+ system: str
24
+ roles: List[str]
25
+ messages: List[List[str]]
26
+ offset: int
27
+ sep_style: SeparatorStyle = SeparatorStyle.SINGLE
28
+ sep: str = "###"
29
+ sep2: str = None
30
+ version: str = "Unknown"
31
+
32
+ skip_next: bool = False
33
+
34
+ def get_prompt(self):
35
+ messages = self.messages
36
+ if len(messages) > 0 and type(messages[0][1]) is tuple:
37
+ messages = self.messages.copy()
38
+ init_role, init_msg = messages[0].copy()
39
+ init_msg = init_msg[0]
40
+ if "mmtag" in self.version:
41
+ init_msg = init_msg.replace("<image>", "").strip()
42
+ messages[0] = (init_role, init_msg)
43
+ messages.insert(0, (self.roles[0], "<Image><image></Image>"))
44
+ messages.insert(1, (self.roles[1], "Received."))
45
+ elif not init_msg.startswith("<image>"):
46
+ init_msg = init_msg.replace("<image>", "").strip()
47
+ messages[0] = (init_role, "<image>\n" + init_msg)
48
+ else:
49
+ messages[0] = (init_role, init_msg)
50
+
51
+ if self.sep_style == SeparatorStyle.SINGLE:
52
+ ret = self.system + self.sep
53
+ for role, message in messages:
54
+ if message:
55
+ if type(message) is tuple:
56
+ message, _, _ = message
57
+ ret += role + ": " + message + self.sep
58
+ else:
59
+ ret += role + ":"
60
+
61
+ elif self.sep_style == SeparatorStyle.TWO:
62
+ seps = [self.sep, self.sep2]
63
+ ret = self.system + seps[0]
64
+ for i, (role, message) in enumerate(messages):
65
+ if message:
66
+ if type(message) is tuple:
67
+ message, _, _ = message
68
+ ret += role + ": " + message + seps[i % 2]
69
+ else:
70
+ ret += role + ":"
71
+
72
+ elif self.sep_style == SeparatorStyle.CHATML:
73
+ ret = "" if self.system == "" else self.system + self.sep + "\n"
74
+ for role, message in messages:
75
+ if message:
76
+ if type(message) is tuple:
77
+ message, images, _ = message
78
+ message = "<image>" * len(images) + message
79
+ ret += role + "\n" + message + self.sep + "\n"
80
+ else:
81
+ ret += role + "\n"
82
+ return ret
83
+
84
+ elif self.sep_style == SeparatorStyle.LLAMA_3:
85
+ if self.tokenizer is None:
86
+ raise ValueError("Llama 3 tokenizer is not available. Make sure you have the necessary permissions.")
87
+ chat_template_messages = [{"role": "system", "content": self.system}]
88
+ for role, message in messages:
89
+ if message:
90
+ if type(message) is tuple:
91
+ message, images = message
92
+ message = "<image>" * len(images) + message
93
+ chat_template_messages.append({"role": role, "content": message})
94
+
95
+ # print(chat_template_messages)
96
+ return self.tokenizer.apply_chat_template(chat_template_messages, tokenize=False, add_generation_prompt=True)
97
+ # ret = "" if self.system == "" else self.system + self.sep + "\n"
98
+ # for role, message in messages:
99
+ # if message:
100
+ # if type(message) is tuple:
101
+ # message, images = message
102
+ # message = "<image>" * len(images) + message
103
+ # ret += role + "\n" + message + self.sep + "\n"
104
+ # else:
105
+ # ret += role + "\n"
106
+ # return ret
107
+
108
+ elif self.sep_style == SeparatorStyle.MPT:
109
+ ret = self.system + self.sep
110
+ for role, message in messages:
111
+ if message:
112
+ if type(message) is tuple:
113
+ message, _, _ = message
114
+ ret += role + message + self.sep
115
+ else:
116
+ ret += role
117
+
118
+ elif self.sep_style == SeparatorStyle.GEMMA:
119
+ ret = ""
120
+ for i, (role, message) in enumerate(messages):
121
+ assert role == self.roles[i % 2], "Conversation should alternate user/assistant/user/assistant/..."
122
+ if message:
123
+ if type(message) is tuple:
124
+ message, _, _ = message
125
+ ret += role + message + self.sep
126
+ else:
127
+ ret += role
128
+
129
+ elif self.sep_style == SeparatorStyle.LLAMA_2:
130
+ wrap_sys = lambda msg: f"<<SYS>>\n{msg}\n<</SYS>>\n\n" if len(msg) > 0 else msg
131
+ wrap_inst = lambda msg: f"[INST] {msg} [/INST]"
132
+ ret = ""
133
+
134
+ for i, (role, message) in enumerate(messages):
135
+ if i == 0:
136
+ assert message, "first message should not be none"
137
+ assert role == self.roles[0], "first message should come from user"
138
+ if message:
139
+ if type(message) is tuple:
140
+ message, _, _ = message
141
+ if i == 0:
142
+ message = wrap_sys(self.system) + message
143
+ if i % 2 == 0:
144
+ message = wrap_inst(message)
145
+ ret += self.sep + message
146
+ else:
147
+ ret += " " + message + " " + self.sep2
148
+ else:
149
+ ret += ""
150
+ ret = ret.lstrip(self.sep)
151
+
152
+ elif self.sep_style == SeparatorStyle.PLAIN:
153
+ seps = [self.sep, self.sep2]
154
+ ret = self.system
155
+ for i, (role, message) in enumerate(messages):
156
+ if message:
157
+ if type(message) is tuple:
158
+ message, _, _ = message
159
+ ret += message + seps[i % 2]
160
+ else:
161
+ ret += ""
162
+ else:
163
+ raise ValueError(f"Invalid style: {self.sep_style}")
164
+
165
+ return ret
166
+
167
+ def append_message(self, role, message):
168
+ self.messages.append([role, message])
169
+
170
+ def process_image(self, image, image_process_mode, return_pil=False, image_format='PNG', max_len=1344, min_len=672):
171
+ if image_process_mode == "Pad":
172
+ def expand2square(pil_img, background_color=(122, 116, 104)):
173
+ width, height = pil_img.size
174
+ if width == height:
175
+ return pil_img
176
+ elif width > height:
177
+ result = Image.new(pil_img.mode, (width, width), background_color)
178
+ result.paste(pil_img, (0, (width - height) // 2))
179
+ return result
180
+ else:
181
+ result = Image.new(pil_img.mode, (height, height), background_color)
182
+ result.paste(pil_img, ((height - width) // 2, 0))
183
+ return result
184
+
185
+ image = expand2square(image)
186
+ elif image_process_mode in ["Default", "Crop"]:
187
+ pass
188
+ elif image_process_mode == "Resize":
189
+ image = image.resize((336, 336))
190
+ else:
191
+ raise ValueError(f"Invalid image_process_mode: {image_process_mode}")
192
+ if max(image.size) > max_len:
193
+ max_hw, min_hw = max(image.size), min(image.size)
194
+ aspect_ratio = max_hw / min_hw
195
+ shortest_edge = int(min(max_len / aspect_ratio, min_len, min_hw))
196
+ longest_edge = int(shortest_edge * aspect_ratio)
197
+ W, H = image.size
198
+ if H > W:
199
+ H, W = longest_edge, shortest_edge
200
+ else:
201
+ H, W = shortest_edge, longest_edge
202
+ image = image.resize((W, H))
203
+ if return_pil:
204
+ return image
205
+ else:
206
+ buffered = BytesIO()
207
+ image.save(buffered, format=image_format)
208
+ img_b64_str = base64.b64encode(buffered.getvalue()).decode()
209
+ return img_b64_str
210
+
211
+ def get_images(self, return_pil=False):
212
+ images = []
213
+ for i, (role, msg) in enumerate(self.messages[self.offset:]):
214
+ if i % 2 == 0:
215
+ if type(msg) is tuple:
216
+ msg, image, image_process_mode = msg
217
+ image = self.process_image(image, image_process_mode, return_pil=return_pil)
218
+ images.append(image)
219
+ return images
220
+
221
+ def to_gradio_chatbot(self):
222
+ ret = []
223
+ for i, (role, msg) in enumerate(self.messages[self.offset:]):
224
+ if i % 2 == 0:
225
+ if type(msg) is tuple:
226
+ msg, image, image_process_mode = msg
227
+ img_b64_str = self.process_image(
228
+ image, "Default", return_pil=False,
229
+ image_format='JPEG')
230
+ img_str = f'<img src="data:image/jpeg;base64,{img_b64_str}" alt="user upload image" />'
231
+ msg = img_str + msg.replace('<image>', '').strip()
232
+ ret.append([msg, None])
233
+ else:
234
+ ret.append([msg, None])
235
+ else:
236
+ ret[-1][-1] = msg
237
+ return ret
238
+
239
+ def copy(self):
240
+ return Conversation(
241
+ system=self.system,
242
+ roles=self.roles,
243
+ messages=[[x, y] for x, y in self.messages],
244
+ offset=self.offset,
245
+ sep_style=self.sep_style,
246
+ sep=self.sep,
247
+ sep2=self.sep2,
248
+ version=self.version)
249
+
250
+ def dict(self):
251
+ if len(self.get_images()) > 0:
252
+ return {
253
+ "system": self.system,
254
+ "roles": self.roles,
255
+ "messages": [[x, y[0] if type(y) is tuple else y] for x, y in self.messages],
256
+ "offset": self.offset,
257
+ "sep": self.sep,
258
+ "sep2": self.sep2,
259
+ }
260
+ return {
261
+ "system": self.system,
262
+ "roles": self.roles,
263
+ "messages": self.messages,
264
+ "offset": self.offset,
265
+ "sep": self.sep,
266
+ "sep2": self.sep2,
267
+ }
268
+
269
+
270
+ conv_vicuna_v0 = Conversation(
271
+ system="A chat between a curious human and an artificial intelligence assistant. "
272
+ "The assistant gives helpful, detailed, and polite answers to the human's questions.",
273
+ roles=("Human", "Assistant"),
274
+ messages=(
275
+ ("Human", "What are the key differences between renewable and non-renewable energy sources?"),
276
+ ("Assistant",
277
+ "Renewable energy sources are those that can be replenished naturally in a relatively "
278
+ "short amount of time, such as solar, wind, hydro, geothermal, and biomass. "
279
+ "Non-renewable energy sources, on the other hand, are finite and will eventually be "
280
+ "depleted, such as coal, oil, and natural gas. Here are some key differences between "
281
+ "renewable and non-renewable energy sources:\n"
282
+ "1. Availability: Renewable energy sources are virtually inexhaustible, while non-renewable "
283
+ "energy sources are finite and will eventually run out.\n"
284
+ "2. Environmental impact: Renewable energy sources have a much lower environmental impact "
285
+ "than non-renewable sources, which can lead to air and water pollution, greenhouse gas emissions, "
286
+ "and other negative effects.\n"
287
+ "3. Cost: Renewable energy sources can be more expensive to initially set up, but they typically "
288
+ "have lower operational costs than non-renewable sources.\n"
289
+ "4. Reliability: Renewable energy sources are often more reliable and can be used in more remote "
290
+ "locations than non-renewable sources.\n"
291
+ "5. Flexibility: Renewable energy sources are often more flexible and can be adapted to different "
292
+ "situations and needs, while non-renewable sources are more rigid and inflexible.\n"
293
+ "6. Sustainability: Renewable energy sources are more sustainable over the long term, while "
294
+ "non-renewable sources are not, and their depletion can lead to economic and social instability.\n")
295
+ ),
296
+ offset=2,
297
+ sep_style=SeparatorStyle.SINGLE,
298
+ sep="###",
299
+ )
300
+
301
+ conv_vicuna_v1 = Conversation(
302
+ system="A chat between a curious user and an artificial intelligence assistant. "
303
+ "The assistant gives helpful, detailed, and polite answers to the user's questions.",
304
+ roles=("USER", "ASSISTANT"),
305
+ version="v1",
306
+ messages=(),
307
+ offset=0,
308
+ sep_style=SeparatorStyle.TWO,
309
+ sep=" ",
310
+ sep2="</s>",
311
+ )
312
+
313
+ conv_llama_2 = Conversation(
314
+ system="""You are a helpful, respectful and honest assistant. Always answer as helpfully as possible, while being safe. Your answers should not include any harmful, unethical, racist, sexist, toxic, dangerous, or illegal content. Please ensure that your responses are socially unbiased and positive in nature.
315
+
316
+ If a question does not make any sense, or is not factually coherent, explain why instead of answering something not correct. If you don't know the answer to a question, please don't share false information.""",
317
+ roles=("USER", "ASSISTANT"),
318
+ version="llama_v2",
319
+ messages=(),
320
+ offset=0,
321
+ sep_style=SeparatorStyle.LLAMA_2,
322
+ sep="<s>",
323
+ sep2="</s>",
324
+ )
325
+
326
+
327
+ conv_blip3o_llama_2 = Conversation(
328
+ system="You are a helpful language and vision assistant. "
329
+ "You are able to understand the visual content that the user provides, "
330
+ "and assist the user with a variety of tasks using natural language.",
331
+ roles=("USER", "ASSISTANT"),
332
+ version="llama_v2",
333
+ messages=(),
334
+ offset=0,
335
+ sep_style=SeparatorStyle.LLAMA_2,
336
+ sep="<s>",
337
+ sep2="</s>",
338
+ )
339
+
340
+ conv_mpt = Conversation(
341
+ system="""<|im_start|>system
342
+ A conversation between a user and an LLM-based AI assistant. The assistant gives helpful and honest answers.""",
343
+ roles=("<|im_start|>user\n", "<|im_start|>assistant\n"),
344
+ version="mpt",
345
+ messages=(),
346
+ offset=0,
347
+ sep_style=SeparatorStyle.MPT,
348
+ sep="<|im_end|>",
349
+ )
350
+
351
+ conv_blip3o_plain = Conversation(
352
+ system="",
353
+ roles=("", ""),
354
+ messages=(
355
+ ),
356
+ offset=0,
357
+ sep_style=SeparatorStyle.PLAIN,
358
+ sep="\n",
359
+ )
360
+
361
+ conv_blip3o_v0 = Conversation(
362
+ system="A chat between a curious human and an artificial intelligence assistant. "
363
+ "The assistant gives helpful, detailed, and polite answers to the human's questions.",
364
+ roles=("Human", "Assistant"),
365
+ messages=(
366
+ ),
367
+ offset=0,
368
+ sep_style=SeparatorStyle.SINGLE,
369
+ sep="###",
370
+ )
371
+
372
+ conv_blip3o_v0_mmtag = Conversation(
373
+ system="A chat between a curious user and an artificial intelligence assistant. "
374
+ "The assistant is able to understand the visual content that the user provides, and assist the user with a variety of tasks using natural language."
375
+ "The visual content will be provided with the following format: <Image>visual content</Image>.",
376
+ roles=("Human", "Assistant"),
377
+ messages=(
378
+ ),
379
+ offset=0,
380
+ sep_style=SeparatorStyle.SINGLE,
381
+ sep="###",
382
+ version="v0_mmtag",
383
+ )
384
+
385
+ conv_blip3o_v1 = Conversation(
386
+ system="A chat between a curious human and an artificial intelligence assistant. "
387
+ "The assistant gives helpful, detailed, and polite answers to the human's questions.",
388
+ roles=("USER", "ASSISTANT"),
389
+ version="v1",
390
+ messages=(),
391
+ offset=0,
392
+ sep_style=SeparatorStyle.TWO,
393
+ sep=" ",
394
+ sep2="</s>",
395
+ )
396
+
397
+ conv_blip3o_v1_mmtag = Conversation(
398
+ system="A chat between a curious user and an artificial intelligence assistant. "
399
+ "The assistant is able to understand the visual content that the user provides, and assist the user with a variety of tasks using natural language."
400
+ "The visual content will be provided with the following format: <Image>visual content</Image>.",
401
+ roles=("USER", "ASSISTANT"),
402
+ messages=(),
403
+ offset=0,
404
+ sep_style=SeparatorStyle.TWO,
405
+ sep=" ",
406
+ sep2="</s>",
407
+ version="v1_mmtag",
408
+ )
409
+
410
+ conv_mistral_instruct = Conversation(
411
+ system="",
412
+ roles=("USER", "ASSISTANT"),
413
+ version="llama_v2",
414
+ messages=(),
415
+ offset=0,
416
+ sep_style=SeparatorStyle.LLAMA_2,
417
+ sep="",
418
+ sep2="</s>",
419
+ )
420
+
421
+ conv_chatml_direct = Conversation(
422
+ system="""<|im_start|>system
423
+ Answer the questions.""",
424
+ roles=("<|im_start|>user\n", "<|im_start|>assistant\n"),
425
+ version="mpt",
426
+ messages=(),
427
+ offset=0,
428
+ sep_style=SeparatorStyle.MPT,
429
+ sep="<|im_end|>",
430
+ )
431
+
432
+ conv_llama3 = Conversation(
433
+ system="""<|begin_of_text|><|start_header_id|>system<|end_header_id|>\n\nA chat between a curious user and an artificial intelligence assistant. The assistant gives helpful, detailed, and polite answers to the user's questions.""",
434
+ roles=("<|start_header_id|>user<|end_header_id|>\n\n", "<|start_header_id|>assistant<|end_header_id|>\n\n"),
435
+ version="llama3",
436
+ messages=(),
437
+ offset=0,
438
+ sep_style=SeparatorStyle.MPT,
439
+ sep="<|eot_id|>",
440
+ )
441
+
442
+ conv_qwen = Conversation(
443
+ system="""<|im_start|>system
444
+ You are a helpful assistant.""",
445
+ roles=("<|im_start|>user", "<|im_start|>assistant"),
446
+ version="qwen",
447
+ messages=[],
448
+ offset=0,
449
+ sep_style=SeparatorStyle.CHATML,
450
+ sep="<|im_end|>",
451
+ )
452
+
453
+
454
+ default_conversation = conv_llama3
455
+ conv_templates = {
456
+ "default": conv_vicuna_v0,
457
+ "v0": conv_vicuna_v0,
458
+ "v1": conv_vicuna_v1,
459
+ "vicuna_v1": conv_vicuna_v1,
460
+ "llama_2": conv_llama_2,
461
+ "mistral_instruct": conv_mistral_instruct,
462
+ "chatml_direct": conv_chatml_direct,
463
+ "mistral_direct": conv_chatml_direct,
464
+
465
+ "plain": conv_blip3o_plain,
466
+ "v0_plain": conv_blip3o_plain,
467
+ "blip3o_v0": conv_blip3o_v0,
468
+ "v0_mmtag": conv_blip3o_v0_mmtag,
469
+ "blip3o_v1": conv_blip3o_v1,
470
+ "v1_mmtag": conv_blip3o_v1_mmtag,
471
+ "blip3o_llama_2": conv_blip3o_llama_2,
472
+ "llama3": conv_llama3,
473
+ "qwen": conv_qwen,
474
+
475
+ "mpt": conv_mpt,
476
+ }
477
+
478
+ if __name__ == "__main__":
479
+ print(default_conversation.get_prompt())
code/umm/runtime/models/blip3o/data/covt_data.py ADDED
@@ -0,0 +1,827 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import copy
2
+ import os
3
+ from dataclasses import dataclass, field
4
+ from typing import Dict
5
+ import torch
6
+ import transformers
7
+ import ujson as json
8
+ from torch.utils.data import Dataset
9
+ from qwen_vl_utils import process_vision_info
10
+ from PIL import Image
11
+ from transformers import AutoImageProcessor
12
+ import re
13
+ import numpy as np
14
+ import cv2
15
+ from torchvision import transforms
16
+ import random
17
+
18
+ from qwen_vl_utils import process_vision_info
19
+ from models.blip3o.model.anchors.segment_anything import build_sam_vit_h, sam_model_registry, SamPredictor
20
+ from models.blip3o.model.anchors.DepthAnything.depth_anything_v2.dpt import DepthAnythingV2
21
+ from diffusers import AutoencoderKL
22
+ from transformers import AutoModel, CLIPImageProcessor
23
+
24
+ from models.blip3o.params import DataArguments
25
+ from models.blip3o.constants import *
26
+
27
+
28
+ def rank0_print(*args):
29
+ if os.environ.get("LOCAL_RANK", "0") in ("0", "-1"):
30
+ print(*args)
31
+
32
+
33
+ def truncate_sequence(input_ids, labels, max_length, eos_token_id):
34
+ if input_ids.size(0) > max_length:
35
+ input_ids = input_ids[:max_length-1]
36
+ labels = labels[:max_length-1]
37
+
38
+ if eos_token_id is not None:
39
+ input_ids = torch.cat([input_ids, torch.tensor([eos_token_id])])
40
+ labels = torch.cat([labels, torch.tensor([eos_token_id])])
41
+
42
+ return input_ids, labels
43
+
44
+ def _mm_type_ids_for_processor_batch(processor, inputs):
45
+ """Qwen3-VL M-RoPE expects mm_token_type_ids aligned with input_ids (0=text, 1=image, 2=video)."""
46
+ if "mm_token_type_ids" in inputs:
47
+ mm = inputs["mm_token_type_ids"]
48
+ if mm.dim() == 2:
49
+ mm = mm.squeeze(0)
50
+ return mm.to(torch.long)
51
+ if hasattr(processor, "create_mm_token_type_ids"):
52
+ row = inputs["input_ids"].tolist()
53
+ mm_list = processor.create_mm_token_type_ids(row)
54
+ return torch.tensor(mm_list[0], dtype=torch.long)
55
+ return torch.zeros(inputs["input_ids"].shape[-1], dtype=torch.long)
56
+
57
+
58
+ def pad_sequence(sequences, padding_side='right', padding_value=0):
59
+ """
60
+ Pad a list of sequences to the same length.
61
+ sequences: list of tensors in [seq_len, *] shape
62
+ """
63
+ assert padding_side in ['right', 'left']
64
+ max_size = sequences[0].size()
65
+ trailing_dims = max_size[1:]
66
+ max_len = max(len(seq) for seq in sequences)
67
+ batch_size = len(sequences)
68
+ output = sequences[0].new_full((batch_size, max_len) + trailing_dims, padding_value)
69
+ for i, seq in enumerate(sequences):
70
+ length = seq.size(0)
71
+ if padding_side == 'right':
72
+ output.data[i, :length] = seq
73
+ else:
74
+ output.data[i, -length:] = seq
75
+ return output
76
+
77
+ def get_image_info(image_path, min_pixel, max_pixel, width, height):
78
+ # Using this because of process_vision_info function
79
+ # Need to fix this in the future
80
+
81
+
82
+ content = {
83
+ "type": "image",
84
+ "image": image_path,
85
+ "min_pixel": min_pixel,
86
+ "max_pixel": max_pixel
87
+ }
88
+
89
+ if width is not None and height is not None:
90
+ content["resized_width"] = width
91
+ content["resized_height"] = height
92
+
93
+ messages = [
94
+ {"role": "user",
95
+ "content": [content]
96
+ }
97
+ ]
98
+
99
+ image_input, _ = process_vision_info(messages)
100
+
101
+ return image_input[0]
102
+
103
+ def get_video_info(video_path, min_pixels, max_pixels, fps):
104
+ # Using this because of process_vision_info function
105
+ # Need to fix this in the future
106
+
107
+ messages = [
108
+ {"role": "user",
109
+ "content": [
110
+ {
111
+ "type": "video",
112
+ "video": video_path,
113
+ "min_pixels": min_pixels,
114
+ "max_pixels": max_pixels,
115
+ "fps": fps
116
+ }
117
+ ]
118
+ }
119
+ ]
120
+
121
+ _, video_input, video_kwargs = process_vision_info(messages, return_video_kwargs=True)
122
+
123
+ return video_input[0], video_kwargs
124
+
125
+ def add_anchor_pad(user_input, anchor_nums, anchor_tokens):
126
+ # add anchor pad after VISION_END_TOKEN or ANCHOR_END_TOKEN
127
+ anchor_pads = []
128
+ for anchor_num, anchor_token in zip(anchor_nums, anchor_tokens):
129
+ anchor_pad = ANCHOR_START_TOKEN + anchor_token * anchor_num + ANCHOR_END_TOKEN
130
+ anchor_pads.append(anchor_pad)
131
+ anchor_pads = "".join(anchor_pads)
132
+ if VISION_END_TOKEN in user_input:
133
+ user_input = user_input.replace(VISION_END_TOKEN, VISION_END_TOKEN + anchor_pads)
134
+ return user_input
135
+
136
+ def add_cot_anchor_pad_in_user_input(user_input, anchor_nums, anchor_tokens,anchor_names):
137
+ if len(anchor_nums) == 0:
138
+ return user_input
139
+
140
+ anchor_pads = []
141
+ for anchor_num, anchor_token, _ in zip(anchor_nums, anchor_tokens, anchor_names):
142
+ anchor_pad = ANCHOR_START_TOKEN + anchor_token * anchor_num + ANCHOR_END_TOKEN
143
+ anchor_pads.append(anchor_pad)
144
+ CoT_pad = ""
145
+ if len(anchor_pads) == 1:
146
+ CoT_pad = f"The {anchor_names[0]} of the image is {anchor_pads[0]}. "
147
+ else:
148
+ for i, (anchor_name, anchor_pad) in enumerate(zip(anchor_names, anchor_pads)):
149
+ if i == 0:
150
+ CoT_pad += f"The {anchor_name} of the image is {anchor_pad}, "
151
+ elif i == len(anchor_names) - 1:
152
+ CoT_pad += f"and the {anchor_name} of the image is {anchor_pad}. "
153
+ else:
154
+ CoT_pad += f"the {anchor_name} of the image is {anchor_pad}, "
155
+ user_input = CoT_pad + user_input
156
+ return user_input
157
+
158
+ def get_cot_data_in_response(response, anchor_nums, anchor_tokens, anchor_names):
159
+ if len(anchor_nums) == 0:
160
+ return response
161
+
162
+ anchor_pads = []
163
+ for anchor_num, anchor_token in zip(anchor_nums, anchor_tokens):
164
+ anchor_pad = ANCHOR_START_TOKEN + anchor_token * anchor_num + ANCHOR_END_TOKEN
165
+ anchor_pads.append(anchor_pad)
166
+ CoT_start = "Because "
167
+ if len(anchor_names) == 1:
168
+ CoT_start += f"the {anchor_names[0]} of the image is {anchor_pads[0]}. "
169
+ else:
170
+ for anchor_name, anchor_pad in zip(anchor_names, anchor_pads):
171
+ CoT_start += f"the {anchor_name} of the image is {anchor_pad}"
172
+ if anchor_name == anchor_names[-2]:
173
+ CoT_start += ", and "
174
+ elif anchor_name == anchor_names[-1]:
175
+ CoT_start += ". "
176
+ else:
177
+ CoT_start += ", "
178
+ response = CoT_start + response
179
+ return response
180
+
181
+
182
+ COT_TEMPLATES = [
183
+ {
184
+ "name": "basic_causal",
185
+ "single": "Because the {anchor_name} of the image is {anchor_pad}. ",
186
+ "multiple": "Because the {anchor_name} of the image is {anchor_pad}{connector}",
187
+ "connectors": {
188
+ "middle": ", ",
189
+ "second_last": ", and ",
190
+ "last": ". "
191
+ }
192
+ },
193
+
194
+ {
195
+ "name": "observational",
196
+ "single": "I can observe that the {anchor_name} of the image is {anchor_pad}. ",
197
+ "multiple": "I can observe that the {anchor_name} of the image is {anchor_pad}{connector}",
198
+ "connectors": {
199
+ "middle": ", ",
200
+ "second_last": ", and ",
201
+ "last": ". "
202
+ }
203
+ },
204
+
205
+ {
206
+ "name": "analytical",
207
+ "single": "After analyzing the image, the {anchor_name} is {anchor_pad}. ",
208
+ "multiple": "After analyzing the image, the {anchor_name} is {anchor_pad}{connector}",
209
+ "connectors": {
210
+ "middle": ", ",
211
+ "second_last": ", and ",
212
+ "last": ". "
213
+ }
214
+ },
215
+
216
+ {
217
+ "name": "descriptive",
218
+ "single": "The image shows that the {anchor_name} is {anchor_pad}. ",
219
+ "multiple": "The image shows that the {anchor_name} is {anchor_pad}{connector}",
220
+ "connectors": {
221
+ "middle": ", ",
222
+ "second_last": ", and ",
223
+ "last": ". "
224
+ }
225
+ },
226
+
227
+ {
228
+ "name": "conditional",
229
+ "single": "Given that the {anchor_name} of the image is {anchor_pad}. ",
230
+ "multiple": "Given that the {anchor_name} of the image is {anchor_pad}{connector}",
231
+ "connectors": {
232
+ "middle": ", ",
233
+ "second_last": ", and ",
234
+ "last": ". "
235
+ }
236
+ },
237
+
238
+ {
239
+ "name": "evidence_based",
240
+ "single": "Based on the visual evidence, the {anchor_name} of the image is {anchor_pad}. ",
241
+ "multiple": "Based on the visual evidence, the {anchor_name} of the image is {anchor_pad}{connector}",
242
+ "connectors": {
243
+ "middle": ", ",
244
+ "second_last": ", and ",
245
+ "last": ". "
246
+ }
247
+ }
248
+ ]
249
+
250
+ def get_random_cot_template():
251
+ return random.choice(COT_TEMPLATES)
252
+
253
+ def apply_cot_template(template, anchor_names, anchor_pads):
254
+ if len(anchor_names) == 1:
255
+ return template["single"].format(
256
+ anchor_name=anchor_names[0],
257
+ anchor_pad=anchor_pads[0]
258
+ )
259
+ else:
260
+ result = ""
261
+ for i, (anchor_name, anchor_pad) in enumerate(zip(anchor_names, anchor_pads)):
262
+ if i == len(anchor_names) - 1:
263
+ connector = template["connectors"]["last"]
264
+ elif i == len(anchor_names) - 2:
265
+ connector = template["connectors"]["second_last"]
266
+ else:
267
+ connector = template["connectors"]["middle"]
268
+
269
+ result += template["multiple"].format(
270
+ anchor_name=anchor_name,
271
+ anchor_pad=anchor_pad,
272
+ connector=connector
273
+ )
274
+ return result
275
+
276
+ def get_templates_comt_data_in_response(response, anchor_nums, anchor_tokens, anchor_names):
277
+ if len(anchor_nums) == 0:
278
+ return response
279
+
280
+ anchor_pads = []
281
+ for anchor_num, anchor_token in zip(anchor_nums, anchor_tokens):
282
+ anchor_pad = ANCHOR_START_TOKEN + anchor_token * anchor_num + ANCHOR_END_TOKEN
283
+ anchor_pads.append(anchor_pad)
284
+
285
+ template = get_random_cot_template()
286
+
287
+ cot_text = apply_cot_template(template, anchor_names, anchor_pads)
288
+
289
+ response = "<think>" + cot_text + "</think>" + "<answer>" + response + "</answer>"
290
+ return response
291
+
292
+ def get_comt_data_in_response(response, anchor_nums, anchor_tokens, anchor_names):
293
+ if len(anchor_nums) == 0:
294
+ return response
295
+
296
+ anchor_pads = []
297
+ for anchor_num, anchor_token in zip(anchor_nums, anchor_tokens):
298
+ anchor_pad = ANCHOR_START_TOKEN + anchor_token * anchor_num + ANCHOR_END_TOKEN
299
+ anchor_pads.append(anchor_pad)
300
+ CoT_start = "<think> Because "
301
+ if len(anchor_names) == 1:
302
+ CoT_start += f"the {anchor_names[0]} of the image is {anchor_pads[0]}. "
303
+ else:
304
+ for anchor_name, anchor_pad in zip(anchor_names, anchor_pads):
305
+ CoT_start += f"the {anchor_name} of the image is {anchor_pad}"
306
+ if anchor_name == anchor_names[-2]:
307
+ CoT_start += ", and "
308
+ elif anchor_name == anchor_names[-1]:
309
+ CoT_start += ". "
310
+ else:
311
+ CoT_start += ", "
312
+ response = CoT_start + " </think>\n" + "<answer> " + response + " </answer>"
313
+ return response
314
+
315
+ def get_feature_data(user_input, gpt_response, anchor_nums, anchor_tokens, anchor_names):
316
+ anchor_pads = []
317
+ for anchor_num, anchor_token, anchor_name in zip(anchor_nums, anchor_tokens, anchor_names):
318
+ anchor_pad = ANCHOR_START_TOKEN + anchor_token * anchor_num + ANCHOR_END_TOKEN
319
+ anchor_pads.append(anchor_pad)
320
+ anchor_name = ", ".join(anchor_names)
321
+ anchor_pads = "".join(anchor_pads)
322
+ user_input = f"{DEFAULT_IM_START_TOKEN}{user_input['role']}\n{VISION_START_TOKEN + DEFAULT_IMAGE_TOKEN + VISION_END_TOKEN}What is the {anchor_name} of the image?\n{DEFAULT_IM_END_TOKEN}\n{DEFAULT_IM_START_TOKEN}{gpt_response['role']}\n"
323
+ gpt_response = f"{anchor_pads}\n{DEFAULT_IM_END_TOKEN}\n"
324
+ return user_input, gpt_response
325
+
326
+ def replace_pad_with_anchor_tokens(gpt_response):
327
+ token_dict = {
328
+ "<segmentation>": SAM_PAD_TOKEN * 8,
329
+ "<depth>": DEPTH_PAD_TOKEN * 4,
330
+ "<dino>": DINO_PAD_TOKEN * 4,
331
+ "<pidinet>": PIDINET_PAD_TOKEN * 4,
332
+ "<siglip>": SIGLIP_PAD_TOKEN * 4,
333
+ "<metaclip>": METACLIP_PAD_TOKEN * 4,
334
+ }
335
+ for token, anchor_token in token_dict.items():
336
+ gpt_response = gpt_response.replace(token, anchor_token)
337
+ return gpt_response
338
+
339
+ def get_token_num(anchor_model_id):
340
+ token_nums = []
341
+ for anchor_model in anchor_model_id:
342
+ if anchor_model == "sam":
343
+ token_nums.append(8)
344
+ elif anchor_model == "dino":
345
+ token_nums.append(4)
346
+ elif anchor_model == "depth":
347
+ token_nums.append(4)
348
+ elif anchor_model == "InternViT":
349
+ token_nums.append(4)
350
+ elif anchor_model == "pidinet":
351
+ token_nums.append(4)
352
+ elif anchor_model == "siglip":
353
+ token_nums.append(4)
354
+ elif anchor_model == "metaclip":
355
+ token_nums.append(4)
356
+ return token_nums
357
+
358
+ def get_anchor_token(anchor_model_id):
359
+ anchor_tokens = []
360
+ for anchor_model in anchor_model_id:
361
+ if anchor_model == "sam":
362
+ anchor_tokens.append(SAM_PAD_TOKEN)
363
+ elif anchor_model == "dino":
364
+ anchor_tokens.append(DINO_PAD_TOKEN)
365
+ elif anchor_model == "depth":
366
+ anchor_tokens.append(DEPTH_PAD_TOKEN)
367
+ elif anchor_model == "InternViT":
368
+ anchor_tokens.append(INTERN_PAD_TOKEN)
369
+ elif anchor_model == "pidinet":
370
+ anchor_tokens.append(PIDINET_PAD_TOKEN)
371
+ elif anchor_model == "siglip":
372
+ anchor_tokens.append(SIGLIP_PAD_TOKEN)
373
+ elif anchor_model == "metaclip":
374
+ anchor_tokens.append(METACLIP_PAD_TOKEN)
375
+ return anchor_tokens
376
+
377
+ def get_anchor_task_name(anchor_model_id):
378
+ anchor_task_names = []
379
+ for anchor_model in anchor_model_id:
380
+ if anchor_model == "sam":
381
+ anchor_task_names.append("segmentation")
382
+ elif anchor_model == "dino":
383
+ anchor_task_names.append("perception feature")
384
+ elif anchor_model == "depth":
385
+ anchor_task_names.append("depth map")
386
+ elif anchor_model == "InternViT":
387
+ anchor_task_names.append("caption")
388
+ elif anchor_model == "pidinet":
389
+ anchor_task_names.append("edge map")
390
+ elif anchor_model == "siglip":
391
+ anchor_task_names.append("clip feature")
392
+ elif anchor_model == "metaclip":
393
+ anchor_task_names.append("metaclip feature")
394
+ return anchor_task_names
395
+
396
+
397
+ class SupervisedDataset(Dataset):
398
+ """Dataset for supervised fine-tuning."""
399
+
400
+ def __init__(
401
+ self,
402
+ data_path: str | list,
403
+ processor: transformers.ProcessorMixin,
404
+ data_args: DataArguments,
405
+ model_id,
406
+ padding=True,
407
+ shuffle=True,
408
+ random_seed=42,
409
+ anchor_model_id=None,
410
+ ):
411
+ super(SupervisedDataset, self).__init__()
412
+ if isinstance(data_path, str):
413
+ list_data_dict = json.load(open(data_path, "r"))
414
+ else:
415
+ list_data_dict = data_path
416
+
417
+ self.model_id = model_id
418
+ self.processor = processor
419
+ self.list_data_dict = list_data_dict
420
+ self.data_args = data_args
421
+ self.padding = padding
422
+ self.image_min_pixel = data_args.image_min_pixels
423
+ self.image_max_pixel = data_args.image_max_pixels
424
+ self.image_resized_w = data_args.image_resized_width
425
+ self.image_resized_h = data_args.image_resized_height
426
+ self.video_min_pixel = data_args.video_min_pixels
427
+ self.video_max_pixel = data_args.video_max_pixels
428
+ self.fps = data_args.fps
429
+ self.anchor_model_id = anchor_model_id
430
+ self.anchor_token_nums = get_token_num(anchor_model_id)
431
+ self.anchor_tokens = get_anchor_token(anchor_model_id)
432
+ self.anchor_task_names = get_anchor_task_name(anchor_model_id)
433
+
434
+ self.cur_step = 0
435
+ self.stage_0_step = data_args.stage_0_step
436
+ self.stage_1_step = data_args.stage_1_step
437
+ self.stage_2_step = data_args.stage_2_step
438
+
439
+ # for shuffle
440
+ self.rng = np.random.default_rng(seed=random_seed)
441
+
442
+ if shuffle:
443
+ self.rng.shuffle(self.list_data_dict)
444
+
445
+ def set_cur_step(self, step: int):
446
+ self.cur_step = step
447
+ print(f"[Dataset] cur_step has been set to {step}")
448
+
449
+ def __len__(self):
450
+ return len(self.list_data_dict)
451
+
452
+ def __getitem__(self, i) -> Dict[str, torch.Tensor]:
453
+
454
+ # import ipdb; ipdb.set_trace()
455
+
456
+ sources = self.list_data_dict[i]
457
+
458
+ is_video = False
459
+
460
+ processor = self.processor
461
+ if "image" in sources:
462
+ videos = None
463
+ grid_key = "image_grid_thw"
464
+ pixel_key = "pixel_values"
465
+
466
+ image_files = sources["image"]
467
+ image_folder = self.data_args.image_folder
468
+
469
+ if isinstance(image_files, str):
470
+ image_files = Image.open(image_files).convert("RGB")
471
+ image_files = [image_files]
472
+ else:
473
+ image_files = [Image.open(image_file).convert("RGB") for image_file in image_files]
474
+ images = []
475
+
476
+ for image_file in image_files:
477
+ # if not os.path.exists(image_file):
478
+ # if not image_file.startswith("http"):
479
+ # image_file = os.path.join(image_folder, image_file)
480
+ # images.append(get_image_info(image_file, self.image_min_pixel, self.image_max_pixel, self.image_resized_w, self.image_resized_h))
481
+ # else:
482
+ images.append(get_image_info(image_file, self.image_min_pixel, self.image_max_pixel, self.image_resized_w, self.image_resized_h))
483
+
484
+ elif "video" in sources:
485
+ is_video = True
486
+ images=None
487
+ grid_key = "video_grid_thw"
488
+ pixel_key = "pixel_values_videos"
489
+
490
+ video_files = sources["video"]
491
+ video_folder = self.data_args.image_folder
492
+
493
+ if isinstance(video_files, str):
494
+ video_files = [video_files]
495
+
496
+ videos = []
497
+ for video_file in video_files:
498
+ if not os.path.exists(video_file):
499
+ if not video_file.startswith("http"):
500
+ video_file = os.path.join(video_folder, video_file)
501
+ video_input, video_kwargs = get_video_info(video_file, self.video_min_pixel, self.video_max_pixel, self.data_args.fps)
502
+ videos.append(video_input)
503
+ else:
504
+ grid_key = None
505
+ pixel_key = None
506
+ images=None
507
+ videos=None
508
+
509
+ if images is None:
510
+ print("No image or video found in the data.")
511
+ images = []
512
+ # Create a black image as a placeholder
513
+ black_image = Image.new("RGB", (self.image_resized_w, self.image_resized_h), (0, 0, 0))
514
+ images.append(get_image_info(black_image, self.image_min_pixel, self.image_max_pixel, self.image_resized_w, self.image_resized_h))
515
+
516
+ elif len(images) == 0:
517
+ print("No image or video found in the data.")
518
+ # Create a black image as a placeholder
519
+ black_image = Image.new("RGB", (self.image_resized_w, self.image_resized_h), (0, 0, 0))
520
+ images.append(get_image_info(black_image, self.image_min_pixel, self.image_max_pixel, self.image_resized_w, self.image_resized_h))
521
+
522
+ if videos is not None:
523
+
524
+ # import ipdb; ipdb.set_trace()
525
+ pass
526
+
527
+ sources = copy.deepcopy(llava_to_openai(sources['conversations'], is_video=is_video))
528
+
529
+ all_input_ids = []
530
+ all_mm_token_type_ids = []
531
+ all_labels = []
532
+ all_pixel_values = []
533
+ all_image_grid_thw = []
534
+ all_second_gird = []
535
+
536
+ # all_dino_encoded_values = []
537
+
538
+ # Qwen2-VL uses a default system message so I've added this.
539
+ if len(SYSTEM_MESSAGE) > 0:
540
+ system_message = f"{DEFAULT_IM_START_TOKEN}system\n{SYSTEM_MESSAGE}\n{DEFAULT_IM_END_TOKEN}\n"
541
+ system_message_input_ids = processor.tokenizer(system_message, add_special_tokens=False, return_tensors='pt')['input_ids']
542
+ system_labels = torch.full_like(system_message_input_ids, IGNORE_INDEX)
543
+
544
+ all_input_ids.append(system_message_input_ids.squeeze(0))
545
+ all_mm_token_type_ids.append(
546
+ torch.zeros(system_message_input_ids.shape[1], dtype=torch.long)
547
+ )
548
+ all_labels.append(system_labels.squeeze(0))
549
+
550
+ # import ipdb; ipdb.set_trace()
551
+ for _, j in enumerate(range(0, len(sources), 2)):
552
+
553
+ if j >= 2:
554
+ break
555
+
556
+ user_input = sources[j]
557
+ gpt_response = sources[j + 1]
558
+
559
+ if (DEFAULT_IMAGE_TOKEN not in user_input['content']) and (DEFAULT_VIDEO_TOKEN not in user_input['content']) and (LLAVA_IMAGE_TOKEN in user_input['content']):
560
+ user_input = f"{DEFAULT_IM_START_TOKEN}{VISION_START_TOKEN + DEFAULT_IMAGE_TOKEN + VISION_END_TOKEN}{user_input['role']}\n{user_input['content']}\n{DEFAULT_IM_END_TOKEN}\n{DEFAULT_IM_START_TOKEN}{gpt_response['role']}\n"
561
+ user_input = add_cot_anchor_pad_in_user_input(user_input, self.anchor_token_nums, self.anchor_tokens,self.anchor_task_names)
562
+ gpt_response = f"{gpt_response['content']}\n{DEFAULT_IM_END_TOKEN}\n"
563
+ raise ValueError('Every man is a poet when he is in love')
564
+ else:
565
+ if self.cur_step < self.stage_0_step:
566
+ user_input = f"{DEFAULT_IM_START_TOKEN}{user_input['role']}\n{user_input['content']}\n{DEFAULT_IM_END_TOKEN}\n{DEFAULT_IM_START_TOKEN}{gpt_response['role']}\n"
567
+ user_input = add_cot_anchor_pad_in_user_input(user_input, self.anchor_token_nums, self.anchor_tokens,self.anchor_task_names)
568
+ gpt_response = f"{gpt_response['content']}\n{DEFAULT_IM_END_TOKEN}\n"
569
+ elif self.cur_step < self.stage_1_step:
570
+ user_input, gpt_response = get_feature_data(user_input, gpt_response, self.anchor_token_nums, self.anchor_tokens, self.anchor_task_names)
571
+ elif self.cur_step < self.stage_2_step:
572
+ user_input = f"{DEFAULT_IM_START_TOKEN}{user_input['role']}\n{user_input['content']}\n{DEFAULT_IM_END_TOKEN}\n{DEFAULT_IM_START_TOKEN}{gpt_response['role']}\n"
573
+ gpt_response = f"{gpt_response['content']}"
574
+ if DEFAULT_IMAGE_TOKEN in user_input:
575
+ gpt_response = get_comt_data_in_response(gpt_response, self.anchor_token_nums, self.anchor_tokens, self.anchor_task_names)
576
+ gpt_response = f"{gpt_response}\n{DEFAULT_IM_END_TOKEN}\n"
577
+ # print(f"\033[92m gpt_response: {gpt_response}\033[0m")
578
+ else:
579
+ # user_input = f"{DEFAULT_IM_START_TOKEN}{user_input['role']}\n{user_input['content']}\n{DEFAULT_IM_END_TOKEN}\n{DEFAULT_IM_START_TOKEN}{gpt_response['role']}\n"
580
+ # gpt_response = f"{gpt_response['content']}\n{DEFAULT_IM_END_TOKEN}\n"
581
+ # gpt_response = replace_pad_with_anchor_tokens(gpt_response)
582
+ import random
583
+ xxx = random.randint(0, 5)
584
+ if xxx == 0:
585
+ user_input = f"{DEFAULT_IM_START_TOKEN}{user_input['role']}\n{user_input['content']}\n{DEFAULT_IM_END_TOKEN}\n{DEFAULT_IM_START_TOKEN}{gpt_response['role']}\n"
586
+ gpt_response = f"{gpt_response['content']}\n{DEFAULT_IM_END_TOKEN}\n"
587
+ else:
588
+ user_input = f"{DEFAULT_IM_START_TOKEN}{user_input['role']}\n{user_input['content']}\n{DEFAULT_IM_END_TOKEN}\n{DEFAULT_IM_START_TOKEN}{gpt_response['role']}\n"
589
+ gpt_response = f"{gpt_response['content']}"
590
+ if DEFAULT_IMAGE_TOKEN in user_input:
591
+ # INSERT_YOUR_CODE
592
+ total = len(self.anchor_tokens)
593
+ if total == 0:
594
+ selected_anchor_token_nums = []
595
+ selected_anchor_tokens = []
596
+ selected_anchor_task_names = []
597
+ else:
598
+ x = random.randint(1, total)
599
+ idxs = sorted(random.sample(range(total), x)) if x > 0 else []
600
+ selected_anchor_token_nums = [self.anchor_token_nums[i] for i in idxs]
601
+ selected_anchor_tokens = [self.anchor_tokens[i] for i in idxs]
602
+ selected_anchor_task_names = [self.anchor_task_names[i] for i in idxs]
603
+ gpt_response = get_comt_data_in_response(gpt_response, selected_anchor_token_nums, selected_anchor_tokens, selected_anchor_task_names)
604
+ gpt_response = f"{gpt_response}\n{DEFAULT_IM_END_TOKEN}\n"
605
+
606
+ # print(f'the user_input is {user_input}')
607
+ # print(f'the gpt_response is {gpt_response}')
608
+
609
+
610
+ # print("-----------------")
611
+ # print(user_input, gpt_response)
612
+ # print("-----------------")
613
+
614
+ # import ipdb; ipdb.set_trace()
615
+ if DEFAULT_IMAGE_TOKEN in user_input:
616
+ inputs = processor(text=[user_input], images=images, videos=videos, padding=False, return_tensors='pt')
617
+ prompt_input_ids = inputs['input_ids']
618
+ all_mm_token_type_ids.append(_mm_type_ids_for_processor_batch(processor, inputs))
619
+ # raise ValueError('Every man is a poet when he is in love')
620
+ all_pixel_values.append(inputs[pixel_key])
621
+ all_image_grid_thw.append(inputs[grid_key])
622
+
623
+ # del dino_val
624
+ torch.cuda.empty_cache()
625
+
626
+ elif DEFAULT_VIDEO_TOKEN in user_input:
627
+ if "Qwen2.5" in self.model_id:
628
+ inputs = processor(text=[user_input], images=images, videos=videos, padding=False, return_tensors='pt', **video_kwargs)
629
+ all_second_gird.extend(inputs["second_per_grid_ts"])
630
+ else:
631
+ inputs = processor(text=[user_input], images=images, videos=videos, padding=False, return_tensors='pt')
632
+ prompt_input_ids = inputs['input_ids']
633
+ all_mm_token_type_ids.append(_mm_type_ids_for_processor_batch(processor, inputs))
634
+ all_pixel_values.append(inputs[pixel_key])
635
+ all_image_grid_thw.append(inputs[grid_key])
636
+
637
+ else:
638
+ prompt_input_ids = processor.tokenizer(user_input, add_special_tokens=False, padding=False, return_tensors='pt')['input_ids']
639
+ all_mm_token_type_ids.append(
640
+ torch.zeros(prompt_input_ids.shape[1], dtype=torch.long)
641
+ )
642
+
643
+ response_input_ids = processor.tokenizer(gpt_response, add_special_tokens=False, padding=False, return_tensors='pt')['input_ids']
644
+ all_mm_token_type_ids.append(
645
+ torch.zeros(response_input_ids.shape[1], dtype=torch.long)
646
+ )
647
+
648
+ input_ids = torch.cat([prompt_input_ids, response_input_ids], dim=1).squeeze(0)
649
+ labels = torch.cat(
650
+ [
651
+ torch.tensor([IGNORE_INDEX] * len(prompt_input_ids[0])),
652
+ response_input_ids.squeeze(0),
653
+ ],
654
+ dim=0,
655
+ )
656
+
657
+ all_input_ids.append(input_ids)
658
+ all_labels.append(labels)
659
+
660
+ # There is no need for eos or bos tokens in the input_ids
661
+ # Qwen2-VL does not use them
662
+ input_ids = torch.cat(all_input_ids, dim=0).to(torch.long)
663
+ labels = torch.cat(all_labels, dim=0).to(torch.long)
664
+ mm_token_type_ids = torch.cat(all_mm_token_type_ids, dim=0).to(torch.long)
665
+ if mm_token_type_ids.shape[0] != input_ids.shape[0]:
666
+ raise ValueError(
667
+ f"mm_token_type_ids length {mm_token_type_ids.shape[0]} != input_ids {input_ids.shape[0]}"
668
+ )
669
+
670
+ # eos_token_id = processor.tokenizer.convert_tokens_to_ids(DEFAULT_IM_END_TOKEN)
671
+ # input_ids, labels = truncate_sequence(input_ids, labels, self.max_length, eos_token_id)
672
+
673
+ attention_mask = (input_ids > -1000000).to(torch.long)
674
+ task_type = "und_image"
675
+ data_dict = dict(
676
+ input_ids=input_ids,
677
+ attention_mask=attention_mask,
678
+ labels=labels,
679
+ mm_token_type_ids=mm_token_type_ids,
680
+ task_types=task_type
681
+ )
682
+
683
+ if pixel_key and grid_key:
684
+ pixel_values = torch.cat(all_pixel_values, dim=0)
685
+ image_thw = torch.cat(all_image_grid_thw, dim=0)
686
+
687
+ data_dict[pixel_key] = pixel_values
688
+ data_dict[grid_key] = image_thw
689
+ data_dict["image_files"] = image_files
690
+
691
+ if len(all_second_gird) > 0:
692
+ second_gird = all_second_gird
693
+ data_dict["second_per_grid_ts"] = second_gird
694
+
695
+ self.cur_step += 1
696
+ return data_dict
697
+
698
+
699
+ @dataclass
700
+ class DataCollatorForSupervisedDataset(object):
701
+ """Collate examples for supervised fine-tuning; truncates to tokenizer.model_max_length (same as train_stage1_algin)."""
702
+
703
+ tokenizer: transformers.PreTrainedTokenizer
704
+
705
+ def __post_init__(self):
706
+ self.pad_token_id = self.tokenizer.pad_token_id
707
+
708
+ def __call__(self, examples):
709
+ max_len = self.tokenizer.model_max_length
710
+ batch_input_ids = []
711
+ batch_label_ids = []
712
+ batch_pixel_values = []
713
+ batch_pixel_video_values = []
714
+ batch_video_thw = []
715
+ batch_image_thw = []
716
+ batch_second_per_grid_ts = []
717
+ batch_mm_token_type_ids = []
718
+
719
+ batch_image_files = []
720
+
721
+ for example in examples:
722
+ keys = example.keys()
723
+ if "pixel_values_videos" in keys:
724
+ batch_pixel_video_values.append(example["pixel_values_videos"])
725
+ batch_video_thw.append(example["video_grid_thw"])
726
+ elif "pixel_values" in keys:
727
+ batch_pixel_values.append(example["pixel_values"])
728
+ batch_image_thw.append(example["image_grid_thw"])
729
+
730
+ if "image_files" in keys:
731
+ batch_image_files.append(example["image_files"])
732
+
733
+ iid = example["input_ids"]
734
+ lab = example["labels"]
735
+ if iid.shape[0] > max_len:
736
+ iid = iid[:max_len]
737
+ lab = lab[:max_len]
738
+ batch_input_ids.append(iid)
739
+ batch_label_ids.append(lab)
740
+ if "mm_token_type_ids" in keys:
741
+ mm = example["mm_token_type_ids"]
742
+ if mm.shape[0] > max_len:
743
+ mm = mm[:max_len]
744
+ batch_mm_token_type_ids.append(mm)
745
+
746
+ if "second_per_grid_ts" in keys:
747
+ batch_second_per_grid_ts.extend(example["second_per_grid_ts"])
748
+
749
+ input_ids = pad_sequence(
750
+ batch_input_ids, padding_side='right', padding_value=self.pad_token_id
751
+ )
752
+
753
+ labels = pad_sequence(batch_label_ids, padding_side='right', padding_value=IGNORE_INDEX)
754
+
755
+ if input_ids.shape[1] > max_len:
756
+ rank0_print(
757
+ f"Warning: input with length {input_ids.shape[1]} is longer than max length {max_len}. Truncating."
758
+ )
759
+ input_ids = input_ids[:, :max_len]
760
+ labels = labels[:, :max_len]
761
+ attention_mask = input_ids != self.pad_token_id
762
+
763
+ data_dict = {
764
+ 'input_ids': input_ids,
765
+ 'labels': labels,
766
+ 'attention_mask': attention_mask,
767
+ }
768
+
769
+ if len(batch_mm_token_type_ids) > 0:
770
+ mm_pad = pad_sequence(
771
+ batch_mm_token_type_ids, padding_side='right', padding_value=0
772
+ )
773
+ data_dict["mm_token_type_ids"] = mm_pad[:, :max_len]
774
+
775
+ if len(batch_pixel_values) > 0:
776
+ pixel_values = torch.cat(batch_pixel_values, dim=0)
777
+ image_thw = torch.cat(batch_image_thw, dim=0)
778
+ data_dict["pixel_values"] = pixel_values
779
+ data_dict["image_grid_thw"] = image_thw
780
+
781
+ if len(batch_pixel_video_values) > 0:
782
+ pixel_video_values = torch.cat(batch_pixel_video_values, dim=0)
783
+ video_thw = torch.cat(batch_video_thw, dim=0)
784
+ data_dict["pixel_values_videos"] = pixel_video_values
785
+ data_dict["video_grid_thw"] = video_thw
786
+
787
+ if len(batch_second_per_grid_ts) > 0:
788
+ data_dict["second_per_grid_ts"] = batch_second_per_grid_ts
789
+
790
+ if len(batch_image_files) > 0:
791
+ data_dict["image_files"] = batch_image_files
792
+ return data_dict
793
+
794
+ def replace_image_tokens(input_string, is_video=False):
795
+ if is_video:
796
+ pattern = r'\n?' + re.escape(LLAVA_VIDEO_TOKEN) + r'\n?'
797
+ replacement = VISION_START_TOKEN + DEFAULT_VIDEO_TOKEN + VISION_END_TOKEN
798
+ else:
799
+ pattern = r'\n?' + re.escape(LLAVA_IMAGE_TOKEN) + r'\n?'
800
+ replacement = VISION_START_TOKEN + DEFAULT_IMAGE_TOKEN + VISION_END_TOKEN
801
+
802
+ return re.sub(pattern, replacement, input_string)
803
+
804
+ def llava_to_openai(conversations, is_video=False):
805
+ role_mapping = {"human": "user", "gpt": "assistant"}
806
+
807
+ transformed_data = []
808
+ for conversation in conversations:
809
+ transformed_content = replace_image_tokens(conversation["value"], is_video=is_video)
810
+ transformed_entry = {
811
+ "role": role_mapping.get(conversation["from"], conversation["from"]),
812
+ "content": transformed_content,
813
+ }
814
+ transformed_data.append(transformed_entry)
815
+
816
+ return transformed_data
817
+
818
+ def make_supervised_data_module(model_id, processor, data_args, anchor_model_id):
819
+ """Make dataset and collator for supervised fine-tuning."""
820
+ sft_dataset = SupervisedDataset(
821
+ data_path=data_args.data_path, processor=processor, data_args=data_args, model_id=model_id, anchor_model_id=anchor_model_id
822
+ )
823
+ data_collator = DataCollatorForSupervisedDataset(tokenizer=processor.tokenizer)
824
+
825
+ return dict(train_dataset=sft_dataset,
826
+ eval_dataset=None,
827
+ data_collator=data_collator)
code/umm/runtime/models/blip3o/data/covt_data_van.py ADDED
@@ -0,0 +1,1056 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import copy
2
+ import os
3
+ from dataclasses import dataclass, field
4
+ from typing import Dict, Optional, Sequence, List, Tuple, Any
5
+ from collections.abc import Sequence
6
+ import torch
7
+ import transformers
8
+ import ujson as json
9
+ from torch.utils.data import Dataset
10
+ from qwen_vl_utils import process_vision_info
11
+ from PIL import Image
12
+ from transformers import AutoImageProcessor
13
+ import re
14
+ import numpy as np
15
+ import cv2
16
+ from torchvision import transforms
17
+ import random
18
+
19
+ from models.blip3o.model.anchors.segment_anything import build_sam_vit_h, sam_model_registry, SamPredictor
20
+ from diffusers import AutoencoderKL
21
+ from transformers import AutoModel, CLIPImageProcessor
22
+
23
+ from models.blip3o.params import DataArguments
24
+ from models.blip3o.constants import *
25
+
26
+
27
+ def rank0_print(*args):
28
+ if os.environ.get("LOCAL_RANK", "0") in ("0", "-1"):
29
+ print(*args)
30
+
31
+ def get_rope_index_3(
32
+ spatial_merge_size: Optional[int] = 2,
33
+ input_ids: Optional[torch.LongTensor] = None,
34
+ image_grid_thw: Optional[torch.LongTensor] = None,
35
+ video_grid_thw: Optional[torch.LongTensor] = None,
36
+ second_per_grid_ts: Optional[torch.Tensor] = None,
37
+ attention_mask: Optional[torch.Tensor] = None,
38
+ ) -> Tuple[torch.Tensor, torch.Tensor]:
39
+
40
+ """Different from the original implementation, Qwen3VL use timestamps rather than absolute time position ids."""
41
+ if input_ids is not None:
42
+ while input_ids.dim() < 2:
43
+ input_ids = input_ids.unsqueeze(0)
44
+ if attention_mask is not None:
45
+ while attention_mask.dim() < 2:
46
+ attention_mask = attention_mask.unsqueeze(0)
47
+ # Since we use timestamps to seperate videos, like <t1> <vision_start> <frame1> <vision_end> <t2> <vision_start> <frame2> <vision_end>, the video_grid_thw should also be split
48
+ if video_grid_thw is not None:
49
+ video_grid_thw = torch.repeat_interleave(video_grid_thw, video_grid_thw[:, 0], dim=0)
50
+ video_grid_thw[:, 0] = 1
51
+
52
+ image_token_id = 151655
53
+ video_token_id = 151656
54
+ vision_start_token_id = 151652
55
+ mrope_position_deltas = []
56
+ if input_ids is not None and (image_grid_thw is not None or video_grid_thw is not None):
57
+ total_input_ids = input_ids
58
+ if attention_mask is None:
59
+ attention_mask = torch.ones_like(total_input_ids)
60
+ position_ids = torch.ones(
61
+ 3,
62
+ input_ids.shape[0],
63
+ input_ids.shape[1],
64
+ dtype=input_ids.dtype,
65
+ device=input_ids.device,
66
+ )
67
+ image_index, video_index = 0, 0
68
+ attention_mask = attention_mask.to(total_input_ids.device)
69
+ for i, input_ids in enumerate(total_input_ids):
70
+ input_ids = input_ids[attention_mask[i] == 1]
71
+ image_nums, video_nums = 0, 0
72
+ vision_start_indices = torch.argwhere(input_ids == vision_start_token_id).squeeze(1)
73
+ vision_tokens = input_ids[vision_start_indices + 1]
74
+ image_nums = (vision_tokens == image_token_id).sum()
75
+ video_nums = (vision_tokens == video_token_id).sum()
76
+ input_tokens = input_ids.tolist()
77
+ llm_pos_ids_list: list = []
78
+ st = 0
79
+ remain_images, remain_videos = image_nums, video_nums
80
+ for _ in range(image_nums + video_nums):
81
+ if image_token_id in input_tokens and remain_images > 0:
82
+ ed_image = input_tokens.index(image_token_id, st)
83
+ else:
84
+ ed_image = len(input_tokens) + 1
85
+ if video_token_id in input_tokens and remain_videos > 0:
86
+ ed_video = input_tokens.index(video_token_id, st)
87
+ else:
88
+ ed_video = len(input_tokens) + 1
89
+ if ed_image < ed_video:
90
+ t, h, w = (
91
+ image_grid_thw[image_index][0],
92
+ image_grid_thw[image_index][1],
93
+ image_grid_thw[image_index][2],
94
+ )
95
+ image_index += 1
96
+ remain_images -= 1
97
+ ed = ed_image
98
+
99
+ else:
100
+ t, h, w = (
101
+ video_grid_thw[video_index][0],
102
+ video_grid_thw[video_index][1],
103
+ video_grid_thw[video_index][2],
104
+ )
105
+ video_index += 1
106
+ remain_videos -= 1
107
+ ed = ed_video
108
+ llm_grid_t, llm_grid_h, llm_grid_w = (
109
+ t.item(),
110
+ h.item() // spatial_merge_size,
111
+ w.item() // spatial_merge_size,
112
+ )
113
+ text_len = ed - st
114
+
115
+ st_idx = llm_pos_ids_list[-1].max() + 1 if len(llm_pos_ids_list) > 0 else 0
116
+ llm_pos_ids_list.append(torch.arange(text_len).view(1, -1).expand(3, -1) + st_idx)
117
+
118
+ # t_index is always 0 because llm_grid_t is always 1 (we use timestamps to encode the temporal information for videos)
119
+ t_index = torch.arange(llm_grid_t).view(-1, 1).expand(-1, llm_grid_h * llm_grid_w).flatten()
120
+ h_index = torch.arange(llm_grid_h).view(1, -1, 1).expand(llm_grid_t, -1, llm_grid_w).flatten()
121
+ w_index = torch.arange(llm_grid_w).view(1, 1, -1).expand(llm_grid_t, llm_grid_h, -1).flatten()
122
+ llm_pos_ids_list.append(torch.stack([t_index, h_index, w_index]) + text_len + st_idx)
123
+ st = ed + llm_grid_t * llm_grid_h * llm_grid_w
124
+
125
+ if st < len(input_tokens):
126
+ st_idx = llm_pos_ids_list[-1].max() + 1 if len(llm_pos_ids_list) > 0 else 0
127
+ text_len = len(input_tokens) - st
128
+ llm_pos_ids_list.append(torch.arange(text_len).view(1, -1).expand(3, -1) + st_idx)
129
+
130
+ llm_positions = torch.cat(llm_pos_ids_list, dim=1).reshape(3, -1)
131
+ position_ids[..., i, attention_mask[i] == 1] = llm_positions.to(position_ids.device)
132
+ mrope_position_deltas.append(llm_positions.max() + 1 - len(total_input_ids[i]))
133
+ mrope_position_deltas = torch.tensor(mrope_position_deltas, device=input_ids.device).unsqueeze(1)
134
+ return position_ids, mrope_position_deltas
135
+ else:
136
+ if attention_mask is not None:
137
+ position_ids = attention_mask.long().cumsum(-1) - 1
138
+ position_ids.masked_fill_(attention_mask == 0, 1)
139
+ position_ids = position_ids.unsqueeze(0).expand(3, -1, -1).to(attention_mask.device)
140
+ max_position_ids = position_ids.max(0, keepdim=False)[0].max(-1, keepdim=True)[0]
141
+ mrope_position_deltas = max_position_ids + 1 - attention_mask.shape[-1]
142
+ else:
143
+ position_ids = (
144
+ torch.arange(input_ids.shape[1], device=input_ids.device)
145
+ .view(1, 1, -1)
146
+ .expand(3, input_ids.shape[0], -1)
147
+ )
148
+ mrope_position_deltas = torch.zeros(
149
+ [input_ids.shape[0], 1],
150
+ device=input_ids.device,
151
+ dtype=input_ids.dtype,
152
+ )
153
+
154
+ return position_ids, mrope_position_deltas
155
+
156
+
157
+ def truncate_sequence(input_ids, labels, max_length, eos_token_id):
158
+ if input_ids.size(0) > max_length:
159
+ input_ids = input_ids[:max_length-1]
160
+ labels = labels[:max_length-1]
161
+
162
+ if eos_token_id is not None:
163
+ input_ids = torch.cat([input_ids, torch.tensor([eos_token_id])])
164
+ labels = torch.cat([labels, torch.tensor([eos_token_id])])
165
+
166
+ return input_ids, labels
167
+
168
+ def pad_sequence(sequences, padding_side='right', padding_value=0):
169
+ """
170
+ Pad a list of sequences to the same length.
171
+ sequences: list of tensors in [seq_len, *] shape
172
+ """
173
+ assert padding_side in ['right', 'left']
174
+ max_size = sequences[0].size()
175
+ trailing_dims = max_size[1:]
176
+ max_len = max(len(seq) for seq in sequences)
177
+ batch_size = len(sequences)
178
+ output = sequences[0].new_full((batch_size, max_len) + trailing_dims, padding_value)
179
+ for i, seq in enumerate(sequences):
180
+ length = seq.size(0)
181
+ if padding_side == 'right':
182
+ output.data[i, :length] = seq
183
+ else:
184
+ output.data[i, -length:] = seq
185
+ return output
186
+
187
+ def get_image_info(image_path, min_pixel, max_pixel, width, height):
188
+ # Using this because of process_vision_info function
189
+ # Need to fix this in the future
190
+
191
+
192
+ content = {
193
+ "type": "image",
194
+ "image": image_path,
195
+ "min_pixel": min_pixel,
196
+ "max_pixel": max_pixel
197
+ }
198
+
199
+ if width is not None and height is not None:
200
+ content["resized_width"] = width
201
+ content["resized_height"] = height
202
+
203
+ messages = [
204
+ {"role": "user",
205
+ "content": [content]
206
+ }
207
+ ]
208
+
209
+ image_input, _ = process_vision_info(messages)
210
+
211
+ return image_input[0]
212
+
213
+ def get_video_info(video_path, min_pixels, max_pixels, fps):
214
+ # Using this because of process_vision_info function
215
+ # Need to fix this in the future
216
+
217
+ messages = [
218
+ {"role": "user",
219
+ "content": [
220
+ {
221
+ "type": "video",
222
+ "video": video_path,
223
+ "min_pixels": min_pixels,
224
+ "max_pixels": max_pixels,
225
+ "fps": fps
226
+ }
227
+ ]
228
+ }
229
+ ]
230
+
231
+ _, video_input, video_kwargs = process_vision_info(messages, return_video_kwargs=True)
232
+
233
+ return video_input[0], video_kwargs
234
+
235
+ def add_anchor_pad(user_input, anchor_nums, anchor_tokens):
236
+ # add anchor pad after VISION_END_TOKEN or ANCHOR_END_TOKEN
237
+ anchor_pads = []
238
+ for anchor_num, anchor_token in zip(anchor_nums, anchor_tokens):
239
+ anchor_pad = ANCHOR_START_TOKEN + anchor_token * anchor_num + ANCHOR_END_TOKEN
240
+ anchor_pads.append(anchor_pad)
241
+ anchor_pads = "".join(anchor_pads)
242
+ if VISION_END_TOKEN in user_input:
243
+ user_input = user_input.replace(VISION_END_TOKEN, VISION_END_TOKEN + anchor_pads)
244
+ return user_input
245
+
246
+ def add_cot_anchor_pad_in_user_input(user_input, anchor_nums, anchor_tokens,anchor_names):
247
+ if len(anchor_nums) == 0:
248
+ return user_input
249
+
250
+ anchor_pads = []
251
+ for anchor_num, anchor_token, _ in zip(anchor_nums, anchor_tokens, anchor_names):
252
+ anchor_pad = ANCHOR_START_TOKEN + anchor_token * anchor_num + ANCHOR_END_TOKEN
253
+ anchor_pads.append(anchor_pad)
254
+ CoT_pad = ""
255
+ if len(anchor_pads) == 1:
256
+ CoT_pad = f"The {anchor_names[0]} of the image is {anchor_pads[0]}. "
257
+ else:
258
+ for i, (anchor_name, anchor_pad) in enumerate(zip(anchor_names, anchor_pads)):
259
+ if i == 0:
260
+ CoT_pad += f"The {anchor_name} of the image is {anchor_pad}, "
261
+ elif i == len(anchor_names) - 1:
262
+ CoT_pad += f"and the {anchor_name} of the image is {anchor_pad}. "
263
+ else:
264
+ CoT_pad += f"the {anchor_name} of the image is {anchor_pad}, "
265
+ user_input = CoT_pad + user_input
266
+ return user_input
267
+
268
+ def get_cot_data_in_response(response, anchor_nums, anchor_tokens, anchor_names):
269
+ if len(anchor_nums) == 0:
270
+ return response
271
+
272
+ anchor_pads = []
273
+ for anchor_num, anchor_token in zip(anchor_nums, anchor_tokens):
274
+ anchor_pad = ANCHOR_START_TOKEN + anchor_token * anchor_num + ANCHOR_END_TOKEN
275
+ anchor_pads.append(anchor_pad)
276
+ CoT_start = "Because "
277
+ if len(anchor_names) == 1:
278
+ CoT_start += f"the {anchor_names[0]} of the image is {anchor_pads[0]}. "
279
+ else:
280
+ for anchor_name, anchor_pad in zip(anchor_names, anchor_pads):
281
+ CoT_start += f"the {anchor_name} of the image is {anchor_pad}"
282
+ if anchor_name == anchor_names[-2]:
283
+ CoT_start += ", and "
284
+ elif anchor_name == anchor_names[-1]:
285
+ CoT_start += ". "
286
+ else:
287
+ CoT_start += ", "
288
+ response = CoT_start + response
289
+ return response
290
+
291
+
292
+ COT_TEMPLATES = [
293
+ {
294
+ "name": "basic_causal",
295
+ "single": "Because the {anchor_name} of the image is {anchor_pad}. ",
296
+ "multiple": "Because the {anchor_name} of the image is {anchor_pad}{connector}",
297
+ "connectors": {
298
+ "middle": ", ",
299
+ "second_last": ", and ",
300
+ "last": ". "
301
+ }
302
+ },
303
+
304
+ {
305
+ "name": "observational",
306
+ "single": "I can observe that the {anchor_name} of the image is {anchor_pad}. ",
307
+ "multiple": "I can observe that the {anchor_name} of the image is {anchor_pad}{connector}",
308
+ "connectors": {
309
+ "middle": ", ",
310
+ "second_last": ", and ",
311
+ "last": ". "
312
+ }
313
+ },
314
+
315
+ {
316
+ "name": "analytical",
317
+ "single": "After analyzing the image, the {anchor_name} is {anchor_pad}. ",
318
+ "multiple": "After analyzing the image, the {anchor_name} is {anchor_pad}{connector}",
319
+ "connectors": {
320
+ "middle": ", ",
321
+ "second_last": ", and ",
322
+ "last": ". "
323
+ }
324
+ },
325
+
326
+ {
327
+ "name": "descriptive",
328
+ "single": "The image shows that the {anchor_name} is {anchor_pad}. ",
329
+ "multiple": "The image shows that the {anchor_name} is {anchor_pad}{connector}",
330
+ "connectors": {
331
+ "middle": ", ",
332
+ "second_last": ", and ",
333
+ "last": ". "
334
+ }
335
+ },
336
+
337
+ {
338
+ "name": "conditional",
339
+ "single": "Given that the {anchor_name} of the image is {anchor_pad}. ",
340
+ "multiple": "Given that the {anchor_name} of the image is {anchor_pad}{connector}",
341
+ "connectors": {
342
+ "middle": ", ",
343
+ "second_last": ", and ",
344
+ "last": ". "
345
+ }
346
+ },
347
+
348
+ {
349
+ "name": "evidence_based",
350
+ "single": "Based on the visual evidence, the {anchor_name} of the image is {anchor_pad}. ",
351
+ "multiple": "Based on the visual evidence, the {anchor_name} of the image is {anchor_pad}{connector}",
352
+ "connectors": {
353
+ "middle": ", ",
354
+ "second_last": ", and ",
355
+ "last": ". "
356
+ }
357
+ }
358
+ ]
359
+
360
+ def get_random_cot_template():
361
+ return random.choice(COT_TEMPLATES)
362
+
363
+ def apply_cot_template(template, anchor_names, anchor_pads):
364
+ if len(anchor_names) == 1:
365
+ return template["single"].format(
366
+ anchor_name=anchor_names[0],
367
+ anchor_pad=anchor_pads[0]
368
+ )
369
+ else:
370
+ result = ""
371
+ for i, (anchor_name, anchor_pad) in enumerate(zip(anchor_names, anchor_pads)):
372
+ if i == len(anchor_names) - 1:
373
+ connector = template["connectors"]["last"]
374
+ elif i == len(anchor_names) - 2:
375
+ connector = template["connectors"]["second_last"]
376
+ else:
377
+ connector = template["connectors"]["middle"]
378
+
379
+ result += template["multiple"].format(
380
+ anchor_name=anchor_name,
381
+ anchor_pad=anchor_pad,
382
+ connector=connector
383
+ )
384
+ return result
385
+
386
+ def get_templates_comt_data_in_response(response, anchor_nums, anchor_tokens, anchor_names):
387
+ if len(anchor_nums) == 0:
388
+ return response
389
+
390
+ anchor_pads = []
391
+ for anchor_num, anchor_token in zip(anchor_nums, anchor_tokens):
392
+ anchor_pad = ANCHOR_START_TOKEN + anchor_token * anchor_num + ANCHOR_END_TOKEN
393
+ anchor_pads.append(anchor_pad)
394
+
395
+ template = get_random_cot_template()
396
+
397
+ cot_text = apply_cot_template(template, anchor_names, anchor_pads)
398
+
399
+ response = "<think>" + cot_text + "</think>" + "<answer>" + response + "</answer>"
400
+ return response
401
+
402
+ def get_comt_data_in_response(response, anchor_nums, anchor_tokens, anchor_names):
403
+ if len(anchor_nums) == 0:
404
+ return response
405
+
406
+ anchor_pads = []
407
+ for anchor_num, anchor_token in zip(anchor_nums, anchor_tokens):
408
+ anchor_pad = ANCHOR_START_TOKEN + anchor_token * anchor_num + ANCHOR_END_TOKEN
409
+ anchor_pads.append(anchor_pad)
410
+ # 3DThinker's placement ablation favors geometry latents at the response
411
+ # boundary. Keep the user's CoVT requirement (inside <think>) while moving
412
+ # only the VGGT block to the very beginning of that region, before prose.
413
+ geometry_items = [
414
+ (name, pad)
415
+ for name, pad in zip(anchor_names, anchor_pads)
416
+ if name == "3D geometry"
417
+ ]
418
+ other_items = [
419
+ (name, pad)
420
+ for name, pad in zip(anchor_names, anchor_pads)
421
+ if name != "3D geometry"
422
+ ]
423
+ geometry_prefix = "".join(pad for _, pad in geometry_items)
424
+ CoT_start = "<think> " + geometry_prefix
425
+ if geometry_items:
426
+ CoT_start += " Using the 3D geometry above, "
427
+ else:
428
+ CoT_start += " Because "
429
+
430
+ if len(other_items) == 0:
431
+ CoT_start += "the answer is grounded in the image geometry. "
432
+ elif len(other_items) == 1:
433
+ CoT_start += f"the {other_items[0][0]} of the image is {other_items[0][1]}. "
434
+ else:
435
+ other_names = [name for name, _ in other_items]
436
+ for anchor_name, anchor_pad in other_items:
437
+ CoT_start += f"the {anchor_name} of the image is {anchor_pad}"
438
+ if anchor_name == other_names[-2]:
439
+ CoT_start += ", and "
440
+ elif anchor_name == other_names[-1]:
441
+ CoT_start += ". "
442
+ else:
443
+ CoT_start += ", "
444
+ response = CoT_start + " </think>\n" + "<answer> " + response + " </answer>"
445
+ return response
446
+
447
+ def get_feature_data(user_input, gpt_response, anchor_nums, anchor_tokens, anchor_names):
448
+ anchor_pads = []
449
+ for anchor_num, anchor_token, anchor_name in zip(anchor_nums, anchor_tokens, anchor_names):
450
+ anchor_pad = ANCHOR_START_TOKEN + anchor_token * anchor_num + ANCHOR_END_TOKEN
451
+ anchor_pads.append(anchor_pad)
452
+ anchor_name = ", ".join(anchor_names)
453
+ anchor_pads = "".join(anchor_pads)
454
+ user_input = f"{DEFAULT_IM_START_TOKEN}{user_input['role']}\n{VISION_START_TOKEN + DEFAULT_IMAGE_TOKEN + VISION_END_TOKEN}What is the {anchor_name} of the image?\n{DEFAULT_IM_END_TOKEN}\n{DEFAULT_IM_START_TOKEN}{gpt_response['role']}\n"
455
+ gpt_response = f"{anchor_pads}\n{DEFAULT_IM_END_TOKEN}\n"
456
+ return user_input, gpt_response
457
+
458
+ def replace_pad_with_anchor_tokens(gpt_response):
459
+ token_dict = {
460
+ "<segmentation>": SAM_PAD_TOKEN * COVT_SAM_TOKEN_COUNT,
461
+ "<depth>": DEPTH_PAD_TOKEN * COVT_DEPTH_TOKEN_COUNT,
462
+ "<dino>": DINO_PAD_TOKEN * COVT_DINO_TOKEN_COUNT,
463
+ "<pidinet>": PIDINET_PAD_TOKEN * COVT_PIDINET_TOKEN_COUNT,
464
+ "<siglip>": SIGLIP_PAD_TOKEN * COVT_SIGLIP_TOKEN_COUNT,
465
+ "<metaclip>": METACLIP_PAD_TOKEN * 4,
466
+ }
467
+ for token, anchor_token in token_dict.items():
468
+ gpt_response = gpt_response.replace(token, anchor_token)
469
+ return gpt_response
470
+
471
+ def get_token_num(anchor_model_id):
472
+ token_nums = []
473
+ for anchor_model in anchor_model_id:
474
+ if anchor_model == "sam":
475
+ token_nums.append(COVT_SAM_TOKEN_COUNT)
476
+ elif anchor_model == "dino":
477
+ token_nums.append(COVT_DINO_TOKEN_COUNT)
478
+ elif anchor_model == "depth":
479
+ token_nums.append(COVT_DEPTH_TOKEN_COUNT)
480
+ elif anchor_model == "InternViT":
481
+ token_nums.append(4)
482
+ elif anchor_model == "pidinet":
483
+ token_nums.append(COVT_PIDINET_TOKEN_COUNT)
484
+ elif anchor_model == "siglip":
485
+ token_nums.append(COVT_SIGLIP_TOKEN_COUNT)
486
+ elif anchor_model == "metaclip":
487
+ token_nums.append(4)
488
+ return token_nums
489
+
490
+ def get_anchor_token(anchor_model_id):
491
+ anchor_tokens = []
492
+ for anchor_model in anchor_model_id:
493
+ if anchor_model == "sam":
494
+ anchor_tokens.append(SAM_PAD_TOKEN)
495
+ elif anchor_model == "dino":
496
+ anchor_tokens.append(DINO_PAD_TOKEN)
497
+ elif anchor_model == "depth":
498
+ anchor_tokens.append(DEPTH_PAD_TOKEN)
499
+ elif anchor_model == "InternViT":
500
+ anchor_tokens.append(INTERN_PAD_TOKEN)
501
+ elif anchor_model == "pidinet":
502
+ anchor_tokens.append(PIDINET_PAD_TOKEN)
503
+ elif anchor_model == "siglip":
504
+ anchor_tokens.append(SIGLIP_PAD_TOKEN)
505
+ elif anchor_model == "metaclip":
506
+ anchor_tokens.append(METACLIP_PAD_TOKEN)
507
+ return anchor_tokens
508
+
509
+ def get_anchor_task_name(anchor_model_id):
510
+ anchor_task_names = []
511
+ for anchor_model in anchor_model_id:
512
+ if anchor_model == "sam":
513
+ anchor_task_names.append("segmentation")
514
+ elif anchor_model == "dino":
515
+ anchor_task_names.append("perception feature")
516
+ elif anchor_model == "depth":
517
+ anchor_task_names.append("3D geometry")
518
+ elif anchor_model == "InternViT":
519
+ anchor_task_names.append("caption")
520
+ elif anchor_model == "pidinet":
521
+ anchor_task_names.append("edge map")
522
+ elif anchor_model == "siglip":
523
+ anchor_task_names.append("clip feature")
524
+ elif anchor_model == "metaclip":
525
+ anchor_task_names.append("metaclip feature")
526
+ return anchor_task_names
527
+
528
+
529
+ class SupervisedDataset(Dataset):
530
+ """Dataset for supervised fine-tuning."""
531
+
532
+ def __init__(
533
+ self,
534
+ data_path: str | list,
535
+ processor: transformers.ProcessorMixin,
536
+ data_args: DataArguments,
537
+ model_id,
538
+ padding=True,
539
+ shuffle=True,
540
+ random_seed=42,
541
+ anchor_model_id=None,
542
+ ):
543
+ super(SupervisedDataset, self).__init__()
544
+ if isinstance(data_path, str):
545
+ list_data_dict = json.load(open(data_path, "r"))
546
+ else:
547
+ list_data_dict = data_path
548
+
549
+ self.model_id = model_id
550
+ self.processor = processor
551
+ self.list_data_dict = list_data_dict
552
+ self.data_args = data_args
553
+ self.padding = padding
554
+ self.image_min_pixel = data_args.image_min_pixels
555
+ self.image_max_pixel = data_args.image_max_pixels
556
+ self.image_resized_w = data_args.image_resized_width
557
+ self.image_resized_h = data_args.image_resized_height
558
+ self.video_min_pixel = data_args.video_min_pixels
559
+ self.video_max_pixel = data_args.video_max_pixels
560
+ self.fps = data_args.fps
561
+ self.merge_size = getattr(processor.image_processor, "merge_size", 2)
562
+ self.get_rope_index = get_rope_index_3
563
+ self.anchor_model_id = anchor_model_id
564
+ self.anchor_token_nums = get_token_num(anchor_model_id)
565
+ self.anchor_tokens = get_anchor_token(anchor_model_id)
566
+ self.anchor_task_names = get_anchor_task_name(anchor_model_id)
567
+
568
+ self.cur_step = 0
569
+ self.stage_0_step = data_args.stage_0_step
570
+ self.stage_1_step = data_args.stage_1_step
571
+ self.stage_2_step = data_args.stage_2_step
572
+
573
+ # for shuffle
574
+ self.rng = np.random.default_rng(seed=random_seed)
575
+
576
+ if shuffle:
577
+ self.rng.shuffle(self.list_data_dict)
578
+
579
+ def set_cur_step(self, step: int):
580
+ self.cur_step = step
581
+ print(f"[Dataset] cur_step has been set to {step}")
582
+
583
+ def __len__(self):
584
+ return len(self.list_data_dict)
585
+
586
+ def __getitem__(self, i) -> Dict[str, torch.Tensor]:
587
+
588
+ # import ipdb; ipdb.set_trace()
589
+
590
+ sources = self.list_data_dict[i]
591
+
592
+ is_video = False
593
+ image_files = None
594
+
595
+ processor = self.processor
596
+ if "image" in sources:
597
+ videos = None
598
+ grid_key = "image_grid_thw"
599
+ pixel_key = "pixel_values"
600
+
601
+ image_files = sources["image"]
602
+ image_folder = self.data_args.image_folder
603
+
604
+ if isinstance(image_files, str):
605
+ image_path = image_files
606
+ if not os.path.isabs(image_path):
607
+ image_path = os.path.join(image_folder, image_path)
608
+ image_files = Image.open(image_path).convert("RGB")
609
+ image_files = [image_files]
610
+ else:
611
+ resolved_image_files = [
612
+ image_file
613
+ if os.path.isabs(image_file)
614
+ else os.path.join(image_folder, image_file)
615
+ for image_file in image_files
616
+ ]
617
+ image_files = [
618
+ Image.open(image_file).convert("RGB")
619
+ for image_file in resolved_image_files
620
+ ]
621
+ images = []
622
+
623
+ for image_file in image_files:
624
+ # if not os.path.exists(image_file):
625
+ # if not image_file.startswith("http"):
626
+ # image_file = os.path.join(image_folder, image_file)
627
+ # images.append(get_image_info(image_file, self.image_min_pixel, self.image_max_pixel, self.image_resized_w, self.image_resized_h))
628
+ # else:
629
+ images.append(get_image_info(image_file, self.image_min_pixel, self.image_max_pixel, self.image_resized_w, self.image_resized_h))
630
+
631
+ elif "video" in sources:
632
+ is_video = True
633
+ images=None
634
+ grid_key = "video_grid_thw"
635
+ pixel_key = "pixel_values_videos"
636
+
637
+ video_files = sources["video"]
638
+ video_folder = self.data_args.image_folder
639
+
640
+ if isinstance(video_files, str):
641
+ video_files = [video_files]
642
+
643
+ videos = []
644
+ for video_file in video_files:
645
+ if not os.path.exists(video_file):
646
+ if not video_file.startswith("http"):
647
+ video_file = os.path.join(video_folder, video_file)
648
+ video_input, video_kwargs = get_video_info(video_file, self.video_min_pixel, self.video_max_pixel, self.data_args.fps)
649
+ videos.append(video_input)
650
+ else:
651
+ grid_key = None
652
+ pixel_key = None
653
+ images=None
654
+ videos=None
655
+
656
+ if images is None:
657
+ print("No image or video found in the data.")
658
+ images = []
659
+ # Create a black image as a placeholder
660
+ black_image = Image.new("RGB", (self.image_resized_w, self.image_resized_h), (0, 0, 0))
661
+ images.append(get_image_info(black_image, self.image_min_pixel, self.image_max_pixel, self.image_resized_w, self.image_resized_h))
662
+
663
+ elif len(images) == 0:
664
+ print("No image or video found in the data.")
665
+ # Create a black image as a placeholder
666
+ black_image = Image.new("RGB", (self.image_resized_w, self.image_resized_h), (0, 0, 0))
667
+ images.append(get_image_info(black_image, self.image_min_pixel, self.image_max_pixel, self.image_resized_w, self.image_resized_h))
668
+
669
+ if videos is not None:
670
+
671
+ # import ipdb; ipdb.set_trace()
672
+ pass
673
+
674
+ sources = copy.deepcopy(llava_to_openai(sources['conversations'], is_video=is_video))
675
+
676
+ all_input_ids = []
677
+ all_labels = []
678
+ all_pixel_values = []
679
+ all_image_grid_thw = []
680
+ all_second_gird = []
681
+
682
+ # all_dino_encoded_values = []
683
+
684
+ # Qwen2-VL uses a default system message so I've added this.
685
+ if len(SYSTEM_MESSAGE) > 0:
686
+ system_message = f"{DEFAULT_IM_START_TOKEN}system\n{SYSTEM_MESSAGE}\n{DEFAULT_IM_END_TOKEN}\n"
687
+ system_message_input_ids = processor.tokenizer(system_message, add_special_tokens=False, return_tensors='pt')['input_ids']
688
+ system_labels = torch.full_like(system_message_input_ids, IGNORE_INDEX)
689
+
690
+ all_input_ids.append(system_message_input_ids.squeeze(0))
691
+ all_labels.append(system_labels.squeeze(0))
692
+
693
+ # import ipdb; ipdb.set_trace()
694
+ for _, j in enumerate(range(0, len(sources), 2)):
695
+
696
+ if j >= 2:
697
+ break
698
+
699
+ user_input = sources[j]
700
+ gpt_response = sources[j + 1]
701
+
702
+ if (DEFAULT_IMAGE_TOKEN not in user_input['content']) and (DEFAULT_VIDEO_TOKEN not in user_input['content']) and (LLAVA_IMAGE_TOKEN in user_input['content']):
703
+ user_input = f"{DEFAULT_IM_START_TOKEN}{VISION_START_TOKEN + DEFAULT_IMAGE_TOKEN + VISION_END_TOKEN}{user_input['role']}\n{user_input['content']}\n{DEFAULT_IM_END_TOKEN}\n{DEFAULT_IM_START_TOKEN}{gpt_response['role']}\n"
704
+ # user_input = add_cot_anchor_pad_in_user_input(user_input, self.anchor_token_nums, self.anchor_tokens,self.anchor_task_names)
705
+ user_input = add_anchor_pad(user_input, self.anchor_token_nums, self.anchor_tokens)
706
+ gpt_response = f"{gpt_response['content']}\n{DEFAULT_IM_END_TOKEN}\n"
707
+ raise ValueError('Every man is a poet when he is in love')
708
+ else:
709
+ if self.cur_step < self.stage_0_step:
710
+ user_input = f"{DEFAULT_IM_START_TOKEN}{user_input['role']}\n{user_input['content']}\n{DEFAULT_IM_END_TOKEN}\n{DEFAULT_IM_START_TOKEN}{gpt_response['role']}\n"
711
+ # user_input = add_cot_anchor_pad_in_user_input(user_input, self.anchor_token_nums, self.anchor_tokens,self.anchor_task_names)
712
+ user_input = add_anchor_pad(user_input, self.anchor_token_nums, self.anchor_tokens)
713
+ gpt_response = f"{gpt_response['content']}\n{DEFAULT_IM_END_TOKEN}\n"
714
+ elif self.cur_step < self.stage_1_step:
715
+ user_input, gpt_response = get_feature_data(user_input, gpt_response, self.anchor_token_nums, self.anchor_tokens, self.anchor_task_names)
716
+ elif self.cur_step < self.stage_2_step:
717
+ user_input = f"{DEFAULT_IM_START_TOKEN}{user_input['role']}\n{user_input['content']}\n{DEFAULT_IM_END_TOKEN}\n{DEFAULT_IM_START_TOKEN}{gpt_response['role']}\n"
718
+ gpt_response = f"{gpt_response['content']}"
719
+ if DEFAULT_IMAGE_TOKEN in user_input:
720
+ gpt_response = get_comt_data_in_response(gpt_response, self.anchor_token_nums, self.anchor_tokens, self.anchor_task_names)
721
+ gpt_response = f"{gpt_response}\n{DEFAULT_IM_END_TOKEN}\n"
722
+ # print(f"\033[92m gpt_response: {gpt_response}\033[0m")
723
+ else:
724
+ # user_input = f"{DEFAULT_IM_START_TOKEN}{user_input['role']}\n{user_input['content']}\n{DEFAULT_IM_END_TOKEN}\n{DEFAULT_IM_START_TOKEN}{gpt_response['role']}\n"
725
+ # gpt_response = f"{gpt_response['content']}\n{DEFAULT_IM_END_TOKEN}\n"
726
+ # gpt_response = replace_pad_with_anchor_tokens(gpt_response)
727
+ import random
728
+ xxx = random.randint(0, 5)
729
+ if xxx == 0:
730
+ user_input = f"{DEFAULT_IM_START_TOKEN}{user_input['role']}\n{user_input['content']}\n{DEFAULT_IM_END_TOKEN}\n{DEFAULT_IM_START_TOKEN}{gpt_response['role']}\n"
731
+ gpt_response = f"{gpt_response['content']}\n{DEFAULT_IM_END_TOKEN}\n"
732
+ else:
733
+ user_input = f"{DEFAULT_IM_START_TOKEN}{user_input['role']}\n{user_input['content']}\n{DEFAULT_IM_END_TOKEN}\n{DEFAULT_IM_START_TOKEN}{gpt_response['role']}\n"
734
+ gpt_response = f"{gpt_response['content']}"
735
+ if DEFAULT_IMAGE_TOKEN in user_input:
736
+ # INSERT_YOUR_CODE
737
+ total = len(self.anchor_tokens)
738
+ if total == 0:
739
+ selected_anchor_token_nums = []
740
+ selected_anchor_tokens = []
741
+ selected_anchor_task_names = []
742
+ else:
743
+ x = random.randint(1, total)
744
+ idxs = sorted(random.sample(range(total), x)) if x > 0 else []
745
+ selected_anchor_token_nums = [self.anchor_token_nums[i] for i in idxs]
746
+ selected_anchor_tokens = [self.anchor_tokens[i] for i in idxs]
747
+ selected_anchor_task_names = [self.anchor_task_names[i] for i in idxs]
748
+ gpt_response = get_comt_data_in_response(gpt_response, selected_anchor_token_nums, selected_anchor_tokens, selected_anchor_task_names)
749
+ gpt_response = f"{gpt_response}\n{DEFAULT_IM_END_TOKEN}\n"
750
+
751
+ # print(f'the user_input is {user_input}')
752
+ # print(f'the gpt_response is {gpt_response}')
753
+
754
+
755
+ # print("-----------------")
756
+ # print(user_input, gpt_response)
757
+ # print("-----------------")
758
+
759
+ # import ipdb; ipdb.set_trace()
760
+ if DEFAULT_IMAGE_TOKEN in user_input:
761
+ inputs = processor(text=[user_input], images=images, videos=videos, padding=False, return_tensors='pt')
762
+ prompt_input_ids = inputs['input_ids']
763
+ all_pixel_values.append(inputs[pixel_key])
764
+ all_image_grid_thw.append(inputs[grid_key])
765
+
766
+ elif DEFAULT_VIDEO_TOKEN in user_input:
767
+ if "Qwen3" in self.model_id:
768
+ inputs = processor(text=[user_input], images=images, videos=videos, padding=False, return_tensors='pt', **video_kwargs)
769
+ all_second_gird.extend(inputs["second_per_grid_ts"])
770
+ else:
771
+ inputs = processor(text=[user_input], images=images, videos=videos, padding=False, return_tensors='pt')
772
+ prompt_input_ids = inputs['input_ids']
773
+ all_pixel_values.append(inputs[pixel_key])
774
+ all_image_grid_thw.append(inputs[grid_key])
775
+
776
+ else:
777
+ prompt_input_ids = processor.tokenizer(user_input, add_special_tokens=False, padding=False, return_tensors='pt')['input_ids']
778
+
779
+
780
+ response_input_ids = processor.tokenizer(gpt_response, add_special_tokens=False, padding=False, return_tensors='pt')['input_ids']
781
+
782
+ input_ids = torch.cat([prompt_input_ids, response_input_ids], dim=1).squeeze(0)
783
+ labels = torch.cat(
784
+ [
785
+ torch.tensor([IGNORE_INDEX] * len(prompt_input_ids[0])),
786
+ response_input_ids.squeeze(0),
787
+ ],
788
+ dim=0,
789
+ )
790
+ all_input_ids.append(input_ids)
791
+ all_labels.append(labels)
792
+
793
+ # There is no need for eos or bos tokens in the input_ids
794
+ # Qwen2-VL does not use them
795
+ input_ids = torch.cat(all_input_ids, dim=0).to(torch.long)
796
+ labels = torch.cat(all_labels, dim=0).to(torch.long)
797
+
798
+ # eos_token_id = processor.tokenizer.convert_tokens_to_ids(DEFAULT_IM_END_TOKEN)
799
+ # input_ids, labels = truncate_sequence(input_ids, labels, self.max_length, eos_token_id)
800
+
801
+ attention_mask = (input_ids > -1000000).to(torch.long)
802
+ task_type = "und_image"
803
+
804
+ x_ids, x_mask = input_ids, attention_mask
805
+ while x_ids.dim() < 2:
806
+ x_ids = x_ids.unsqueeze(0)
807
+
808
+ input_ids_2d= x_ids
809
+ data_dict = dict(
810
+ input_ids=input_ids,
811
+ attention_mask=attention_mask,
812
+ labels=labels,
813
+ task_types=task_type,
814
+ )
815
+
816
+ if pixel_key and grid_key:
817
+ pixel_values = torch.cat(all_pixel_values, dim=0)
818
+ image_thw = torch.cat(all_image_grid_thw, dim=0)
819
+
820
+ data_dict[pixel_key] = pixel_values
821
+ data_dict[grid_key] = image_thw
822
+ data_dict["image_files"] = image_files
823
+
824
+ if len(all_second_gird) > 0:
825
+ second_gird = all_second_gird
826
+ data_dict["second_per_grid_ts"] = second_gird
827
+
828
+ position_ids, _ = self.get_rope_index(
829
+ self.merge_size,
830
+ input_ids_2d,
831
+ image_grid_thw=torch.cat(all_image_grid_thw, dim=0) if images else None,
832
+ video_grid_thw=(
833
+ torch.cat(all_image_grid_thw, dim=0) if videos else None
834
+ ),
835
+ second_per_grid_ts= all_second_gird if len(all_second_gird) > 0 else None,
836
+
837
+ )
838
+ data_dict["position_ids"] = position_ids
839
+
840
+ return data_dict
841
+
842
+
843
+ def pad_and_cat(tensor_list):
844
+ max_length = max(tensor.shape[2] for tensor in tensor_list)
845
+
846
+ padded_tensors = []
847
+ for tensor in tensor_list:
848
+ pad_length = max_length - tensor.shape[2]
849
+ padded_tensor = torch.nn.functional.pad(tensor, (0, pad_length), "constant", 1)
850
+ padded_tensors.append(padded_tensor)
851
+
852
+ stacked_tensor = torch.cat(padded_tensors, dim=1)
853
+
854
+ return stacked_tensor
855
+
856
+
857
+ @dataclass
858
+ class DataCollatorForSupervisedDataset(object):
859
+ """Collate examples for supervised fine-tuning."""
860
+
861
+ tokenizer: transformers.PreTrainedTokenizer
862
+
863
+ def __call__(self, instances: Sequence[Dict]) -> Dict[str, torch.Tensor]:
864
+ input_ids, labels, position_ids = tuple(
865
+ [instance[key] for instance in instances]
866
+ for key in ("input_ids", "labels", "position_ids")
867
+ )
868
+ input_ids = [ids.squeeze(0) for ids in input_ids]
869
+ labels = [ids.squeeze(0) for ids in labels]
870
+ input_ids = torch.nn.utils.rnn.pad_sequence(
871
+ input_ids, batch_first=True, padding_value=self.tokenizer.pad_token_id
872
+ )
873
+ labels = torch.nn.utils.rnn.pad_sequence(
874
+ labels, batch_first=True, padding_value=IGNORE_INDEX
875
+ )
876
+ position_ids = pad_and_cat(position_ids)
877
+ input_ids = input_ids[:, : self.tokenizer.model_max_length]
878
+ labels = labels[:, : self.tokenizer.model_max_length]
879
+ position_ids = position_ids[:, :, : self.tokenizer.model_max_length]
880
+ batch = dict(
881
+ input_ids=input_ids,
882
+ labels=labels,
883
+ attention_mask=input_ids.ne(self.tokenizer.pad_token_id),
884
+ )
885
+ images = list(
886
+ instance["pixel_values"]
887
+ for instance in instances
888
+ if "pixel_values" in instance
889
+ )
890
+ videos = list(
891
+ instance["pixel_values_videos"]
892
+ for instance in instances
893
+ if "pixel_values_videos" in instance
894
+ )
895
+
896
+ # Align with batch per sample; use None for missing keys or placeholder samples to avoid image_files[i] being misaligned as None in forward if the length is shorter than batch.
897
+ images_files = [
898
+ instance["image_files"] if "image_files" in instance else None
899
+ for instance in instances
900
+ ]
901
+
902
+ if len(images) != 0:
903
+ concat_images = torch.cat([image for image in images], dim=0)
904
+ grid_thw = [
905
+ instance["image_grid_thw"]
906
+ for instance in instances
907
+ if "image_grid_thw" in instance
908
+ ]
909
+ grid_thw = torch.cat(grid_thw, dim=0)
910
+ else:
911
+ concat_images = None
912
+ grid_thw = None
913
+
914
+ if len(videos) != 0:
915
+ concat_videos = torch.cat([video for video in videos], dim=0)
916
+ video_grid_thw = [
917
+ instance["video_grid_thw"]
918
+ for instance in instances
919
+ if "video_grid_thw" in instance
920
+ ]
921
+ video_grid_thw = torch.cat(video_grid_thw, dim=0)
922
+ else:
923
+ concat_videos = None
924
+ video_grid_thw = None
925
+
926
+ batch["pixel_values"] = concat_images
927
+ batch["image_grid_thw"] = grid_thw
928
+ batch["pixel_values_videos"] = concat_videos
929
+ batch["video_grid_thw"] = video_grid_thw
930
+ batch["position_ids"] = position_ids
931
+ batch["image_files"] = images_files
932
+ return batch
933
+
934
+ # @dataclass
935
+ # class DataCollatorForSupervisedDataset(object):
936
+ # """Collate examples for supervised fine-tuning; truncates to tokenizer.model_max_length (same as train_stage1_algin)."""
937
+
938
+ # tokenizer: transformers.PreTrainedTokenizer
939
+
940
+ # def __post_init__(self):
941
+ # self.pad_token_id = self.tokenizer.pad_token_id
942
+
943
+ # def __call__(self, examples):
944
+ # max_len = self.tokenizer.model_max_length
945
+ # batch_input_ids = []
946
+ # batch_label_ids = []
947
+ # batch_pixel_values = []
948
+ # batch_pixel_video_values = []
949
+ # batch_video_thw = []
950
+ # batch_image_thw = []
951
+ # batch_second_per_grid_ts = []
952
+ # batch_mm_token_type_ids = []
953
+
954
+ # batch_image_files = []
955
+
956
+ # for example in examples:
957
+ # keys = example.keys()
958
+ # if "pixel_values_videos" in keys:
959
+ # batch_pixel_video_values.append(example["pixel_values_videos"])
960
+ # batch_video_thw.append(example["video_grid_thw"])
961
+ # elif "pixel_values" in keys:
962
+ # batch_pixel_values.append(example["pixel_values"])
963
+ # batch_image_thw.append(example["image_grid_thw"])
964
+
965
+ # if "image_files" in keys:
966
+ # batch_image_files.append(example["image_files"])
967
+
968
+ # iid = example["input_ids"]
969
+ # lab = example["labels"]
970
+ # if iid.shape[0] > max_len:
971
+ # iid = iid[:max_len]
972
+ # lab = lab[:max_len]
973
+ # batch_input_ids.append(iid)
974
+ # batch_label_ids.append(lab)
975
+ # if "mm_token_type_ids" in keys:
976
+ # mm = example["mm_token_type_ids"]
977
+ # if mm.shape[0] > max_len:
978
+ # mm = mm[:max_len]
979
+ # batch_mm_token_type_ids.append(mm)
980
+
981
+ # if "second_per_grid_ts" in keys:
982
+ # batch_second_per_grid_ts.extend(example["second_per_grid_ts"])
983
+
984
+ # input_ids = pad_sequence(
985
+ # batch_input_ids, padding_side='right', padding_value=self.pad_token_id
986
+ # )
987
+
988
+ # labels = pad_sequence(batch_label_ids, padding_side='right', padding_value=IGNORE_INDEX)
989
+
990
+ # if input_ids.shape[1] > max_len:
991
+ # rank0_print(
992
+ # f"Warning: input with length {input_ids.shape[1]} is longer than max length {max_len}. Truncating."
993
+ # )
994
+ # input_ids = input_ids[:, :max_len]
995
+ # labels = labels[:, :max_len]
996
+ # attention_mask = input_ids != self.pad_token_id
997
+
998
+ # data_dict = {
999
+ # 'input_ids': input_ids,
1000
+ # 'labels': labels,
1001
+ # 'attention_mask': attention_mask,
1002
+ # }
1003
+
1004
+ # if len(batch_pixel_values) > 0:
1005
+ # pixel_values = torch.cat(batch_pixel_values, dim=0)
1006
+ # image_thw = torch.cat(batch_image_thw, dim=0)
1007
+ # data_dict["pixel_values"] = pixel_values
1008
+ # data_dict["image_grid_thw"] = image_thw
1009
+
1010
+ # if len(batch_pixel_video_values) > 0:
1011
+ # pixel_video_values = torch.cat(batch_pixel_video_values, dim=0)
1012
+ # video_thw = torch.cat(batch_video_thw, dim=0)
1013
+ # data_dict["pixel_values_videos"] = pixel_video_values
1014
+ # data_dict["video_grid_thw"] = video_thw
1015
+
1016
+ # if len(batch_second_per_grid_ts) > 0:
1017
+ # data_dict["second_per_grid_ts"] = batch_second_per_grid_ts
1018
+
1019
+ # if len(batch_image_files) > 0:
1020
+ # data_dict["image_files"] = batch_image_files
1021
+ # return data_dict
1022
+
1023
+ def replace_image_tokens(input_string, is_video=False):
1024
+ if is_video:
1025
+ pattern = r'\n?' + re.escape(LLAVA_VIDEO_TOKEN) + r'\n?'
1026
+ replacement = VISION_START_TOKEN + DEFAULT_VIDEO_TOKEN + VISION_END_TOKEN
1027
+ else:
1028
+ pattern = r'\n?' + re.escape(LLAVA_IMAGE_TOKEN) + r'\n?'
1029
+ replacement = VISION_START_TOKEN + DEFAULT_IMAGE_TOKEN + VISION_END_TOKEN
1030
+
1031
+ return re.sub(pattern, replacement, input_string)
1032
+
1033
+ def llava_to_openai(conversations, is_video=False):
1034
+ role_mapping = {"human": "user", "gpt": "assistant"}
1035
+
1036
+ transformed_data = []
1037
+ for conversation in conversations:
1038
+ transformed_content = replace_image_tokens(conversation["value"], is_video=is_video)
1039
+ transformed_entry = {
1040
+ "role": role_mapping.get(conversation["from"], conversation["from"]),
1041
+ "content": transformed_content,
1042
+ }
1043
+ transformed_data.append(transformed_entry)
1044
+
1045
+ return transformed_data
1046
+
1047
+ def make_supervised_data_module(model_id, processor, data_args, anchor_model_id):
1048
+ """Make dataset and collator for supervised fine-tuning."""
1049
+ sft_dataset = SupervisedDataset(
1050
+ data_path=data_args.data_path, processor=processor, data_args=data_args, model_id=model_id, anchor_model_id=anchor_model_id
1051
+ )
1052
+ data_collator = DataCollatorForSupervisedDataset(tokenizer=processor.tokenizer)
1053
+
1054
+ return dict(train_dataset=sft_dataset,
1055
+ eval_dataset=None,
1056
+ data_collator=data_collator)
code/umm/runtime/models/blip3o/data/covt_data_var.py ADDED
@@ -0,0 +1,1039 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import copy
2
+ import os
3
+ from dataclasses import dataclass, field
4
+ from typing import Dict, Optional, Sequence, List, Tuple, Any
5
+ from collections.abc import Sequence
6
+ import torch
7
+ import transformers
8
+ import ujson as json
9
+ from torch.utils.data import Dataset
10
+ from qwen_vl_utils import process_vision_info
11
+ from PIL import Image
12
+ from transformers import AutoImageProcessor
13
+ import re
14
+ import numpy as np
15
+ import cv2
16
+ from torchvision import transforms
17
+ import random
18
+
19
+ from models.blip3o.model.anchors.segment_anything import build_sam_vit_h, sam_model_registry, SamPredictor
20
+ from models.blip3o.model.anchors.DepthAnything.depth_anything_v2.dpt import DepthAnythingV2
21
+ from diffusers import AutoencoderKL
22
+ from transformers import AutoModel, CLIPImageProcessor
23
+
24
+ from models.blip3o.params import DataArguments
25
+ from models.blip3o.constants import *
26
+
27
+
28
+ def rank0_print(*args):
29
+ if os.environ.get("LOCAL_RANK", "0") in ("0", "-1"):
30
+ print(*args)
31
+
32
+
33
+ def _anchor_placeholder_body(anchor_num: int, anchor_token) -> str:
34
+ """Body between <|anchor_start|> and <|anchor_end|> (single repeated token or tuple of distinct VIS_* strings)."""
35
+ if isinstance(anchor_token, (list, tuple)):
36
+ seq = list(anchor_token)
37
+ if len(seq) != anchor_num:
38
+ raise ValueError(
39
+ f"anchor_token sequence length {len(seq)} != anchor_num {anchor_num}"
40
+ )
41
+ return "".join(seq)
42
+ return anchor_token * anchor_num
43
+
44
+
45
+ def get_rope_index_3(
46
+ spatial_merge_size: Optional[int] = 2,
47
+ input_ids: Optional[torch.LongTensor] = None,
48
+ image_grid_thw: Optional[torch.LongTensor] = None,
49
+ video_grid_thw: Optional[torch.LongTensor] = None,
50
+ second_per_grid_ts: Optional[torch.Tensor] = None,
51
+ attention_mask: Optional[torch.Tensor] = None,
52
+ ) -> Tuple[torch.Tensor, torch.Tensor]:
53
+
54
+ """Different from the original implementation, Qwen3VL use timestamps rather than absolute time position ids."""
55
+ if input_ids is not None:
56
+ while input_ids.dim() < 2:
57
+ input_ids = input_ids.unsqueeze(0)
58
+ if attention_mask is not None:
59
+ while attention_mask.dim() < 2:
60
+ attention_mask = attention_mask.unsqueeze(0)
61
+ # Since we use timestamps to seperate videos, like <t1> <vision_start> <frame1> <vision_end> <t2> <vision_start> <frame2> <vision_end>, the video_grid_thw should also be split
62
+ if video_grid_thw is not None:
63
+ video_grid_thw = torch.repeat_interleave(video_grid_thw, video_grid_thw[:, 0], dim=0)
64
+ video_grid_thw[:, 0] = 1
65
+
66
+ image_token_id = 151655
67
+ video_token_id = 151656
68
+ vision_start_token_id = 151652
69
+ mrope_position_deltas = []
70
+ if input_ids is not None and (image_grid_thw is not None or video_grid_thw is not None):
71
+ total_input_ids = input_ids
72
+ if attention_mask is None:
73
+ attention_mask = torch.ones_like(total_input_ids)
74
+ position_ids = torch.ones(
75
+ 3,
76
+ input_ids.shape[0],
77
+ input_ids.shape[1],
78
+ dtype=input_ids.dtype,
79
+ device=input_ids.device,
80
+ )
81
+ image_index, video_index = 0, 0
82
+ attention_mask = attention_mask.to(total_input_ids.device)
83
+ for i, input_ids in enumerate(total_input_ids):
84
+ input_ids = input_ids[attention_mask[i] == 1]
85
+ image_nums, video_nums = 0, 0
86
+ vision_start_indices = torch.argwhere(input_ids == vision_start_token_id).squeeze(1)
87
+ vision_tokens = input_ids[vision_start_indices + 1]
88
+ image_nums = (vision_tokens == image_token_id).sum()
89
+ video_nums = (vision_tokens == video_token_id).sum()
90
+ input_tokens = input_ids.tolist()
91
+ llm_pos_ids_list: list = []
92
+ st = 0
93
+ remain_images, remain_videos = image_nums, video_nums
94
+ for _ in range(image_nums + video_nums):
95
+ if image_token_id in input_tokens and remain_images > 0:
96
+ ed_image = input_tokens.index(image_token_id, st)
97
+ else:
98
+ ed_image = len(input_tokens) + 1
99
+ if video_token_id in input_tokens and remain_videos > 0:
100
+ ed_video = input_tokens.index(video_token_id, st)
101
+ else:
102
+ ed_video = len(input_tokens) + 1
103
+ if ed_image < ed_video:
104
+ t, h, w = (
105
+ image_grid_thw[image_index][0],
106
+ image_grid_thw[image_index][1],
107
+ image_grid_thw[image_index][2],
108
+ )
109
+ image_index += 1
110
+ remain_images -= 1
111
+ ed = ed_image
112
+
113
+ else:
114
+ t, h, w = (
115
+ video_grid_thw[video_index][0],
116
+ video_grid_thw[video_index][1],
117
+ video_grid_thw[video_index][2],
118
+ )
119
+ video_index += 1
120
+ remain_videos -= 1
121
+ ed = ed_video
122
+ llm_grid_t, llm_grid_h, llm_grid_w = (
123
+ t.item(),
124
+ h.item() // spatial_merge_size,
125
+ w.item() // spatial_merge_size,
126
+ )
127
+ text_len = ed - st
128
+
129
+ st_idx = llm_pos_ids_list[-1].max() + 1 if len(llm_pos_ids_list) > 0 else 0
130
+ llm_pos_ids_list.append(torch.arange(text_len).view(1, -1).expand(3, -1) + st_idx)
131
+
132
+ # t_index is always 0 because llm_grid_t is always 1 (we use timestamps to encode the temporal information for videos)
133
+ t_index = torch.arange(llm_grid_t).view(-1, 1).expand(-1, llm_grid_h * llm_grid_w).flatten()
134
+ h_index = torch.arange(llm_grid_h).view(1, -1, 1).expand(llm_grid_t, -1, llm_grid_w).flatten()
135
+ w_index = torch.arange(llm_grid_w).view(1, 1, -1).expand(llm_grid_t, llm_grid_h, -1).flatten()
136
+ llm_pos_ids_list.append(torch.stack([t_index, h_index, w_index]) + text_len + st_idx)
137
+ st = ed + llm_grid_t * llm_grid_h * llm_grid_w
138
+
139
+ if st < len(input_tokens):
140
+ st_idx = llm_pos_ids_list[-1].max() + 1 if len(llm_pos_ids_list) > 0 else 0
141
+ text_len = len(input_tokens) - st
142
+ llm_pos_ids_list.append(torch.arange(text_len).view(1, -1).expand(3, -1) + st_idx)
143
+
144
+ llm_positions = torch.cat(llm_pos_ids_list, dim=1).reshape(3, -1)
145
+ position_ids[..., i, attention_mask[i] == 1] = llm_positions.to(position_ids.device)
146
+ mrope_position_deltas.append(llm_positions.max() + 1 - len(total_input_ids[i]))
147
+ mrope_position_deltas = torch.tensor(mrope_position_deltas, device=input_ids.device).unsqueeze(1)
148
+ return position_ids, mrope_position_deltas
149
+ else:
150
+ if attention_mask is not None:
151
+ position_ids = attention_mask.long().cumsum(-1) - 1
152
+ position_ids.masked_fill_(attention_mask == 0, 1)
153
+ position_ids = position_ids.unsqueeze(0).expand(3, -1, -1).to(attention_mask.device)
154
+ max_position_ids = position_ids.max(0, keepdim=False)[0].max(-1, keepdim=True)[0]
155
+ mrope_position_deltas = max_position_ids + 1 - attention_mask.shape[-1]
156
+ else:
157
+ position_ids = (
158
+ torch.arange(input_ids.shape[1], device=input_ids.device)
159
+ .view(1, 1, -1)
160
+ .expand(3, input_ids.shape[0], -1)
161
+ )
162
+ mrope_position_deltas = torch.zeros(
163
+ [input_ids.shape[0], 1],
164
+ device=input_ids.device,
165
+ dtype=input_ids.dtype,
166
+ )
167
+
168
+ return position_ids, mrope_position_deltas
169
+
170
+
171
+ def truncate_sequence(input_ids, labels, max_length, eos_token_id):
172
+ if input_ids.size(0) > max_length:
173
+ input_ids = input_ids[:max_length-1]
174
+ labels = labels[:max_length-1]
175
+
176
+ if eos_token_id is not None:
177
+ input_ids = torch.cat([input_ids, torch.tensor([eos_token_id])])
178
+ labels = torch.cat([labels, torch.tensor([eos_token_id])])
179
+
180
+ return input_ids, labels
181
+
182
+ def pad_sequence(sequences, padding_side='right', padding_value=0):
183
+ """
184
+ Pad a list of sequences to the same length.
185
+ sequences: list of tensors in [seq_len, *] shape
186
+ """
187
+ assert padding_side in ['right', 'left']
188
+ max_size = sequences[0].size()
189
+ trailing_dims = max_size[1:]
190
+ max_len = max(len(seq) for seq in sequences)
191
+ batch_size = len(sequences)
192
+ output = sequences[0].new_full((batch_size, max_len) + trailing_dims, padding_value)
193
+ for i, seq in enumerate(sequences):
194
+ length = seq.size(0)
195
+ if padding_side == 'right':
196
+ output.data[i, :length] = seq
197
+ else:
198
+ output.data[i, -length:] = seq
199
+ return output
200
+
201
+ def get_image_info(image_path, min_pixel, max_pixel, width, height):
202
+ # Using this because of process_vision_info function
203
+ # Need to fix this in the future
204
+
205
+ content = {
206
+ "type": "image",
207
+ "image": image_path,
208
+ "min_pixel": min_pixel,
209
+ "max_pixel": max_pixel
210
+ }
211
+
212
+ if width is not None and height is not None:
213
+ content["resized_width"] = width
214
+ content["resized_height"] = height
215
+
216
+ messages = [
217
+ {"role": "user",
218
+ "content": [content]
219
+ }
220
+ ]
221
+
222
+ image_input, _ = process_vision_info(messages)
223
+
224
+ return image_input[0]
225
+
226
+ def get_video_info(video_path, min_pixels, max_pixels, fps):
227
+ # Using this because of process_vision_info function
228
+ # Need to fix this in the future
229
+
230
+ messages = [
231
+ {"role": "user",
232
+ "content": [
233
+ {
234
+ "type": "video",
235
+ "video": video_path,
236
+ "min_pixels": min_pixels,
237
+ "max_pixels": max_pixels,
238
+ "fps": fps
239
+ }
240
+ ]
241
+ }
242
+ ]
243
+
244
+ _, video_input, video_kwargs = process_vision_info(messages, return_video_kwargs=True)
245
+
246
+ return video_input[0], video_kwargs
247
+
248
+ def add_anchor_pad(user_input, anchor_nums, anchor_tokens):
249
+ # add anchor pad after VISION_END_TOKEN or ANCHOR_END_TOKEN
250
+ anchor_pads = []
251
+ for anchor_num, anchor_token in zip(anchor_nums, anchor_tokens):
252
+ anchor_pad = ANCHOR_START_TOKEN + _anchor_placeholder_body(anchor_num, anchor_token) + ANCHOR_END_TOKEN
253
+ anchor_pads.append(anchor_pad)
254
+ anchor_pads = "".join(anchor_pads)
255
+ if VISION_END_TOKEN in user_input:
256
+ user_input = user_input.replace(VISION_END_TOKEN, VISION_END_TOKEN + anchor_pads)
257
+ return user_input
258
+
259
+ def add_cot_anchor_pad_in_user_input(user_input, anchor_nums, anchor_tokens,anchor_names):
260
+ if len(anchor_nums) == 0:
261
+ return user_input
262
+
263
+ anchor_pads = []
264
+ for anchor_num, anchor_token, _ in zip(anchor_nums, anchor_tokens, anchor_names):
265
+ anchor_pad = ANCHOR_START_TOKEN + _anchor_placeholder_body(anchor_num, anchor_token) + ANCHOR_END_TOKEN
266
+ anchor_pads.append(anchor_pad)
267
+ CoT_pad = ""
268
+ if len(anchor_pads) == 1:
269
+ CoT_pad = f"The {anchor_names[0]} of the image is {anchor_pads[0]}. "
270
+ else:
271
+ for i, (anchor_name, anchor_pad) in enumerate(zip(anchor_names, anchor_pads)):
272
+ if i == 0:
273
+ CoT_pad += f"The {anchor_name} of the image is {anchor_pad}, "
274
+ elif i == len(anchor_names) - 1:
275
+ CoT_pad += f"and the {anchor_name} of the image is {anchor_pad}. "
276
+ else:
277
+ CoT_pad += f"the {anchor_name} of the image is {anchor_pad}, "
278
+ user_input = CoT_pad + user_input
279
+ return user_input
280
+
281
+ def get_cot_data_in_response(response, anchor_nums, anchor_tokens, anchor_names):
282
+ if len(anchor_nums) == 0:
283
+ return response
284
+
285
+ anchor_pads = []
286
+ for anchor_num, anchor_token in zip(anchor_nums, anchor_tokens):
287
+ anchor_pad = ANCHOR_START_TOKEN + _anchor_placeholder_body(anchor_num, anchor_token) + ANCHOR_END_TOKEN
288
+ anchor_pads.append(anchor_pad)
289
+ CoT_start = "Because "
290
+ if len(anchor_names) == 1:
291
+ CoT_start += f"the {anchor_names[0]} of the image is {anchor_pads[0]}. "
292
+ else:
293
+ for anchor_name, anchor_pad in zip(anchor_names, anchor_pads):
294
+ CoT_start += f"the {anchor_name} of the image is {anchor_pad}"
295
+ if anchor_name == anchor_names[-2]:
296
+ CoT_start += ", and "
297
+ elif anchor_name == anchor_names[-1]:
298
+ CoT_start += ". "
299
+ else:
300
+ CoT_start += ", "
301
+ response = CoT_start + response
302
+ return response
303
+
304
+
305
+ COT_TEMPLATES = [
306
+ {
307
+ "name": "basic_causal",
308
+ "single": "Because the {anchor_name} of the image is {anchor_pad}. ",
309
+ "multiple": "Because the {anchor_name} of the image is {anchor_pad}{connector}",
310
+ "connectors": {
311
+ "middle": ", ",
312
+ "second_last": ", and ",
313
+ "last": ". "
314
+ }
315
+ },
316
+
317
+ {
318
+ "name": "observational",
319
+ "single": "I can observe that the {anchor_name} of the image is {anchor_pad}. ",
320
+ "multiple": "I can observe that the {anchor_name} of the image is {anchor_pad}{connector}",
321
+ "connectors": {
322
+ "middle": ", ",
323
+ "second_last": ", and ",
324
+ "last": ". "
325
+ }
326
+ },
327
+
328
+ {
329
+ "name": "analytical",
330
+ "single": "After analyzing the image, the {anchor_name} is {anchor_pad}. ",
331
+ "multiple": "After analyzing the image, the {anchor_name} is {anchor_pad}{connector}",
332
+ "connectors": {
333
+ "middle": ", ",
334
+ "second_last": ", and ",
335
+ "last": ". "
336
+ }
337
+ },
338
+
339
+ {
340
+ "name": "descriptive",
341
+ "single": "The image shows that the {anchor_name} is {anchor_pad}. ",
342
+ "multiple": "The image shows that the {anchor_name} is {anchor_pad}{connector}",
343
+ "connectors": {
344
+ "middle": ", ",
345
+ "second_last": ", and ",
346
+ "last": ". "
347
+ }
348
+ },
349
+
350
+ {
351
+ "name": "conditional",
352
+ "single": "Given that the {anchor_name} of the image is {anchor_pad}. ",
353
+ "multiple": "Given that the {anchor_name} of the image is {anchor_pad}{connector}",
354
+ "connectors": {
355
+ "middle": ", ",
356
+ "second_last": ", and ",
357
+ "last": ". "
358
+ }
359
+ },
360
+
361
+ {
362
+ "name": "evidence_based",
363
+ "single": "Based on the visual evidence, the {anchor_name} of the image is {anchor_pad}. ",
364
+ "multiple": "Based on the visual evidence, the {anchor_name} of the image is {anchor_pad}{connector}",
365
+ "connectors": {
366
+ "middle": ", ",
367
+ "second_last": ", and ",
368
+ "last": ". "
369
+ }
370
+ }
371
+ ]
372
+
373
+ def get_random_cot_template():
374
+ return random.choice(COT_TEMPLATES)
375
+
376
+ def apply_cot_template(template, anchor_names, anchor_pads):
377
+ if len(anchor_names) == 1:
378
+ return template["single"].format(
379
+ anchor_name=anchor_names[0],
380
+ anchor_pad=anchor_pads[0]
381
+ )
382
+ else:
383
+ result = ""
384
+ for i, (anchor_name, anchor_pad) in enumerate(zip(anchor_names, anchor_pads)):
385
+ if i == len(anchor_names) - 1:
386
+ connector = template["connectors"]["last"]
387
+ elif i == len(anchor_names) - 2:
388
+ connector = template["connectors"]["second_last"]
389
+ else:
390
+ connector = template["connectors"]["middle"]
391
+
392
+ result += template["multiple"].format(
393
+ anchor_name=anchor_name,
394
+ anchor_pad=anchor_pad,
395
+ connector=connector
396
+ )
397
+ return result
398
+
399
+ def get_templates_comt_data_in_response(response, anchor_nums, anchor_tokens, anchor_names):
400
+ if len(anchor_nums) == 0:
401
+ return response
402
+
403
+ anchor_pads = []
404
+ for anchor_num, anchor_token in zip(anchor_nums, anchor_tokens):
405
+ anchor_pad = ANCHOR_START_TOKEN + _anchor_placeholder_body(anchor_num, anchor_token) + ANCHOR_END_TOKEN
406
+ anchor_pads.append(anchor_pad)
407
+
408
+ template = get_random_cot_template()
409
+
410
+ cot_text = apply_cot_template(template, anchor_names, anchor_pads)
411
+
412
+ response = "<think>" + cot_text + "</think>" + "<answer>" + response + "</answer>"
413
+ return response
414
+
415
+ def get_comt_data_in_response(response, anchor_nums, anchor_tokens, anchor_names):
416
+ if len(anchor_nums) == 0:
417
+ return response
418
+
419
+ anchor_pads = []
420
+ for anchor_num, anchor_token in zip(anchor_nums, anchor_tokens):
421
+ anchor_pad = ANCHOR_START_TOKEN + _anchor_placeholder_body(anchor_num, anchor_token) + ANCHOR_END_TOKEN
422
+ anchor_pads.append(anchor_pad)
423
+ CoT_start = "<think> Because "
424
+ if len(anchor_names) == 1:
425
+ CoT_start += f"the {anchor_names[0]} of the image is {anchor_pads[0]}. "
426
+ else:
427
+ for anchor_name, anchor_pad in zip(anchor_names, anchor_pads):
428
+ CoT_start += f"the {anchor_name} of the image is {anchor_pad}"
429
+ if anchor_name == anchor_names[-2]:
430
+ CoT_start += ", and "
431
+ elif anchor_name == anchor_names[-1]:
432
+ CoT_start += ". "
433
+ else:
434
+ CoT_start += ", "
435
+ response = CoT_start + " </think>\n" + "<answer> " + response + " </answer>"
436
+ return response
437
+
438
+ def get_feature_data(user_input, gpt_response, anchor_nums, anchor_tokens, anchor_names):
439
+ anchor_pads = []
440
+ for anchor_num, anchor_token, anchor_name in zip(anchor_nums, anchor_tokens, anchor_names):
441
+ anchor_pad = ANCHOR_START_TOKEN + _anchor_placeholder_body(anchor_num, anchor_token) + ANCHOR_END_TOKEN
442
+ anchor_pads.append(anchor_pad)
443
+ anchor_name = ", ".join(anchor_names)
444
+ anchor_pads = "".join(anchor_pads)
445
+ user_input = f"{DEFAULT_IM_START_TOKEN}{user_input['role']}\n{VISION_START_TOKEN + DEFAULT_IMAGE_TOKEN + VISION_END_TOKEN}What is the {anchor_name} of the image?\n{DEFAULT_IM_END_TOKEN}\n{DEFAULT_IM_START_TOKEN}{gpt_response['role']}\n"
446
+ gpt_response = f"{anchor_pads}\n{DEFAULT_IM_END_TOKEN}\n"
447
+ return user_input, gpt_response
448
+
449
+ def replace_pad_with_anchor_tokens(gpt_response):
450
+ token_dict = {
451
+ "<segmentation>": SAM_PAD_TOKEN * 8,
452
+ "<depth>": "".join(VIS_DEPTH_ANCHOR_TOKENS),
453
+ "<dino>": "".join(VIS_DINO_ANCHOR_TOKENS),
454
+ "<pidinet>": "".join(VIS_PIDINET_ANCHOR_TOKENS),
455
+ "<siglip>": "".join(VIS_SIGLIP_ANCHOR_TOKENS),
456
+ "<metaclip>": METACLIP_PAD_TOKEN * 4,
457
+ }
458
+ for token, anchor_token in token_dict.items():
459
+ gpt_response = gpt_response.replace(token, anchor_token)
460
+ return gpt_response
461
+
462
+ def get_token_num(anchor_model_id):
463
+ token_nums = []
464
+ for anchor_model in anchor_model_id:
465
+ if anchor_model == "sam":
466
+ token_nums.append(8)
467
+ elif anchor_model == "dino":
468
+ token_nums.append(4)
469
+ elif anchor_model == "depth":
470
+ token_nums.append(4)
471
+ elif anchor_model == "InternViT":
472
+ token_nums.append(4)
473
+ elif anchor_model == "pidinet":
474
+ token_nums.append(4)
475
+ elif anchor_model == "siglip":
476
+ token_nums.append(4)
477
+ elif anchor_model == "metaclip":
478
+ token_nums.append(4)
479
+ return token_nums
480
+
481
+ def get_anchor_token(anchor_model_id):
482
+ anchor_tokens = []
483
+ for anchor_model in anchor_model_id:
484
+ # if anchor_model == "sam":
485
+ # anchor_tokens.append((SAM_PAD_TOKEN,) * 8)
486
+ if anchor_model == "dino":
487
+ anchor_tokens.append(tuple(VIS_DINO_ANCHOR_TOKENS))
488
+ elif anchor_model == "depth":
489
+ anchor_tokens.append(tuple(VIS_DEPTH_ANCHOR_TOKENS))
490
+ # elif anchor_model == "InternViT":
491
+ # anchor_tokens.append((INTERN_PAD_TOKEN,) * 4)
492
+ elif anchor_model == "pidinet":
493
+ anchor_tokens.append(tuple(VIS_PIDINET_ANCHOR_TOKENS))
494
+ elif anchor_model == "siglip":
495
+ anchor_tokens.append(tuple(VIS_SIGLIP_ANCHOR_TOKENS))
496
+ # elif anchor_model == "metaclip":
497
+ # anchor_tokens.append((METACLIP_PAD_TOKEN,) * 4)
498
+ return anchor_tokens
499
+
500
+ def get_anchor_task_name(anchor_model_id):
501
+ anchor_task_names = []
502
+ for anchor_model in anchor_model_id:
503
+ if anchor_model == "sam":
504
+ anchor_task_names.append("segmentation")
505
+ elif anchor_model == "dino":
506
+ anchor_task_names.append("perception feature")
507
+ elif anchor_model == "depth":
508
+ anchor_task_names.append("depth map")
509
+ elif anchor_model == "InternViT":
510
+ anchor_task_names.append("caption")
511
+ elif anchor_model == "pidinet":
512
+ anchor_task_names.append("edge map")
513
+ elif anchor_model == "siglip":
514
+ anchor_task_names.append("semantic feature")
515
+ elif anchor_model == "metaclip":
516
+ anchor_task_names.append("metaclip feature")
517
+ return anchor_task_names
518
+
519
+
520
+ class SupervisedDataset(Dataset):
521
+ """Dataset for supervised fine-tuning."""
522
+
523
+ def __init__(
524
+ self,
525
+ data_path: str | list,
526
+ processor: transformers.ProcessorMixin,
527
+ data_args: DataArguments,
528
+ model_id,
529
+ padding=True,
530
+ shuffle=True,
531
+ random_seed=42,
532
+ anchor_model_id=None,
533
+ ):
534
+ super(SupervisedDataset, self).__init__()
535
+ if isinstance(data_path, str):
536
+ list_data_dict = json.load(open(data_path, "r"))
537
+ else:
538
+ list_data_dict = data_path
539
+
540
+ self.model_id = model_id
541
+ self.processor = processor
542
+ self.list_data_dict = list_data_dict
543
+ self.data_args = data_args
544
+ self.padding = padding
545
+ self.image_min_pixel = data_args.image_min_pixels
546
+ self.image_max_pixel = data_args.image_max_pixels
547
+ self.image_resized_w = data_args.image_resized_width
548
+ self.image_resized_h = data_args.image_resized_height
549
+ self.video_min_pixel = data_args.video_min_pixels
550
+ self.video_max_pixel = data_args.video_max_pixels
551
+ self.fps = data_args.fps
552
+ self.merge_size = getattr(processor.image_processor, "merge_size", 2)
553
+ self.get_rope_index = get_rope_index_3
554
+ self.anchor_model_id = anchor_model_id
555
+ self.anchor_token_nums = get_token_num(anchor_model_id)
556
+ self.anchor_tokens = get_anchor_token(anchor_model_id)
557
+ self.anchor_task_names = get_anchor_task_name(anchor_model_id)
558
+
559
+ self.cur_step = 0
560
+ self.stage_0_step = data_args.stage_0_step
561
+ self.stage_1_step = data_args.stage_1_step
562
+ self.stage_2_step = data_args.stage_2_step
563
+
564
+ # for shuffle
565
+ self.rng = np.random.default_rng(seed=random_seed)
566
+
567
+ if shuffle:
568
+ self.rng.shuffle(self.list_data_dict)
569
+
570
+ def set_cur_step(self, step: int):
571
+ self.cur_step = step
572
+ print(f"[Dataset] cur_step has been set to {step}")
573
+
574
+ def __len__(self):
575
+ return len(self.list_data_dict)
576
+
577
+ def __getitem__(self, i) -> Dict[str, torch.Tensor]:
578
+
579
+ # import ipdb; ipdb.set_trace()
580
+
581
+ sources = self.list_data_dict[i]
582
+
583
+ is_video = False
584
+ image_files = None
585
+
586
+ processor = self.processor
587
+ if "image" in sources:
588
+ videos = None
589
+ grid_key = "image_grid_thw"
590
+ pixel_key = "pixel_values"
591
+
592
+ image_files = sources["image"]
593
+ image_folder = self.data_args.image_folder
594
+
595
+ if isinstance(image_files, str):
596
+ image_files = Image.open(image_files).convert("RGB")
597
+ image_files = [image_files]
598
+ else:
599
+ image_files = [Image.open(image_file).convert("RGB") for image_file in image_files]
600
+ images = []
601
+
602
+ for image_file in image_files:
603
+ # if not os.path.exists(image_file):
604
+ # if not image_file.startswith("http"):
605
+ # image_file = os.path.join(image_folder, image_file)
606
+ # images.append(get_image_info(image_file, self.image_min_pixel, self.image_max_pixel, self.image_resized_w, self.image_resized_h))
607
+ # else:
608
+ images.append(get_image_info(image_file, self.image_min_pixel, self.image_max_pixel, self.image_resized_w, self.image_resized_h))
609
+
610
+ elif "video" in sources:
611
+ is_video = True
612
+ images=None
613
+ grid_key = "video_grid_thw"
614
+ pixel_key = "pixel_values_videos"
615
+
616
+ video_files = sources["video"]
617
+ video_folder = self.data_args.image_folder
618
+
619
+ if isinstance(video_files, str):
620
+ video_files = [video_files]
621
+
622
+ videos = []
623
+ for video_file in video_files:
624
+ if not os.path.exists(video_file):
625
+ if not video_file.startswith("http"):
626
+ video_file = os.path.join(video_folder, video_file)
627
+ video_input, video_kwargs = get_video_info(video_file, self.video_min_pixel, self.video_max_pixel, self.data_args.fps)
628
+ videos.append(video_input)
629
+ else:
630
+ grid_key = None
631
+ pixel_key = None
632
+ images=None
633
+ videos=None
634
+
635
+ if images is None:
636
+ print("No image or video found in the data.")
637
+ images = []
638
+ # Create a black image as a placeholder
639
+ black_image = Image.new("RGB", (self.image_resized_w, self.image_resized_h), (0, 0, 0))
640
+ images.append(get_image_info(black_image, self.image_min_pixel, self.image_max_pixel, self.image_resized_w, self.image_resized_h))
641
+
642
+ elif len(images) == 0:
643
+ print("No image or video found in the data.")
644
+ # Create a black image as a placeholder
645
+ black_image = Image.new("RGB", (self.image_resized_w, self.image_resized_h), (0, 0, 0))
646
+ images.append(get_image_info(black_image, self.image_min_pixel, self.image_max_pixel, self.image_resized_w, self.image_resized_h))
647
+
648
+ if videos is not None:
649
+
650
+ # import ipdb; ipdb.set_trace()
651
+ pass
652
+
653
+ sources = copy.deepcopy(llava_to_openai(sources['conversations'], is_video=is_video))
654
+
655
+ all_input_ids = []
656
+ all_labels = []
657
+ all_pixel_values = []
658
+ all_image_grid_thw = []
659
+ all_second_gird = []
660
+
661
+ # all_dino_encoded_values = []
662
+
663
+ # Qwen2-VL uses a default system message so I've added this.
664
+ if len(SYSTEM_MESSAGE) > 0:
665
+ system_message = f"{DEFAULT_IM_START_TOKEN}system\n{SYSTEM_MESSAGE}\n{DEFAULT_IM_END_TOKEN}\n"
666
+ system_message_input_ids = processor.tokenizer(system_message, add_special_tokens=False, return_tensors='pt')['input_ids']
667
+ system_labels = torch.full_like(system_message_input_ids, IGNORE_INDEX)
668
+
669
+ all_input_ids.append(system_message_input_ids.squeeze(0))
670
+ all_labels.append(system_labels.squeeze(0))
671
+
672
+ # import ipdb; ipdb.set_trace()
673
+ for _, j in enumerate(range(0, len(sources), 2)):
674
+
675
+ if j >= 2:
676
+ break
677
+
678
+ user_input = sources[j]
679
+ gpt_response = sources[j + 1]
680
+
681
+ if (DEFAULT_IMAGE_TOKEN not in user_input['content']) and (DEFAULT_VIDEO_TOKEN not in user_input['content']) and (LLAVA_IMAGE_TOKEN in user_input['content']):
682
+ user_input = f"{DEFAULT_IM_START_TOKEN}{VISION_START_TOKEN + DEFAULT_IMAGE_TOKEN + VISION_END_TOKEN}{user_input['role']}\n{user_input['content']}\n{DEFAULT_IM_END_TOKEN}\n{DEFAULT_IM_START_TOKEN}{gpt_response['role']}\n"
683
+ # user_input = add_cot_anchor_pad_in_user_input(user_input, self.anchor_token_nums, self.anchor_tokens,self.anchor_task_names)
684
+ user_input = add_anchor_pad(user_input, self.anchor_token_nums, self.anchor_tokens)
685
+ gpt_response = f"{gpt_response['content']}\n{DEFAULT_IM_END_TOKEN}\n"
686
+ raise ValueError('Every man is a poet when he is in love')
687
+ else:
688
+ if self.cur_step < self.stage_0_step:
689
+ user_input = f"{DEFAULT_IM_START_TOKEN}{user_input['role']}\n{user_input['content']}\n{DEFAULT_IM_END_TOKEN}\n{DEFAULT_IM_START_TOKEN}{gpt_response['role']}\n"
690
+ # user_input = add_cot_anchor_pad_in_user_input(user_input, self.anchor_token_nums, self.anchor_tokens,self.anchor_task_names)
691
+ user_input = add_anchor_pad(user_input, self.anchor_token_nums, self.anchor_tokens)
692
+ gpt_response = f"{gpt_response['content']}\n{DEFAULT_IM_END_TOKEN}\n"
693
+ elif self.cur_step < self.stage_1_step:
694
+ user_input, gpt_response = get_feature_data(user_input, gpt_response, self.anchor_token_nums, self.anchor_tokens, self.anchor_task_names)
695
+ elif self.cur_step < self.stage_2_step:
696
+ user_input = f"{DEFAULT_IM_START_TOKEN}{user_input['role']}\n{user_input['content']}\n{DEFAULT_IM_END_TOKEN}\n{DEFAULT_IM_START_TOKEN}{gpt_response['role']}\n"
697
+ gpt_response = f"{gpt_response['content']}"
698
+ if DEFAULT_IMAGE_TOKEN in user_input:
699
+ gpt_response = get_comt_data_in_response(gpt_response, self.anchor_token_nums, self.anchor_tokens, self.anchor_task_names)
700
+ gpt_response = f"{gpt_response}\n{DEFAULT_IM_END_TOKEN}\n"
701
+ # print(f"\033[92m gpt_response: {gpt_response}\033[0m")
702
+ else:
703
+ # user_input = f"{DEFAULT_IM_START_TOKEN}{user_input['role']}\n{user_input['content']}\n{DEFAULT_IM_END_TOKEN}\n{DEFAULT_IM_START_TOKEN}{gpt_response['role']}\n"
704
+ # gpt_response = f"{gpt_response['content']}\n{DEFAULT_IM_END_TOKEN}\n"
705
+ # gpt_response = replace_pad_with_anchor_tokens(gpt_response)
706
+ import random
707
+ xxx = random.randint(0, 5)
708
+ if xxx == 0:
709
+ user_input = f"{DEFAULT_IM_START_TOKEN}{user_input['role']}\n{user_input['content']}\n{DEFAULT_IM_END_TOKEN}\n{DEFAULT_IM_START_TOKEN}{gpt_response['role']}\n"
710
+ gpt_response = f"{gpt_response['content']}\n{DEFAULT_IM_END_TOKEN}\n"
711
+ else:
712
+ user_input = f"{DEFAULT_IM_START_TOKEN}{user_input['role']}\n{user_input['content']}\n{DEFAULT_IM_END_TOKEN}\n{DEFAULT_IM_START_TOKEN}{gpt_response['role']}\n"
713
+ gpt_response = f"{gpt_response['content']}"
714
+ if DEFAULT_IMAGE_TOKEN in user_input:
715
+ # INSERT_YOUR_CODE
716
+ total = len(self.anchor_tokens)
717
+ if total == 0:
718
+ selected_anchor_token_nums = []
719
+ selected_anchor_tokens = []
720
+ selected_anchor_task_names = []
721
+ else:
722
+ x = random.randint(1, total)
723
+ idxs = sorted(random.sample(range(total), x)) if x > 0 else []
724
+ selected_anchor_token_nums = [self.anchor_token_nums[i] for i in idxs]
725
+ selected_anchor_tokens = [self.anchor_tokens[i] for i in idxs]
726
+ selected_anchor_task_names = [self.anchor_task_names[i] for i in idxs]
727
+ gpt_response = get_comt_data_in_response(gpt_response, selected_anchor_token_nums, selected_anchor_tokens, selected_anchor_task_names)
728
+ gpt_response = f"{gpt_response}\n{DEFAULT_IM_END_TOKEN}\n"
729
+
730
+ # print(f'the user_input is {user_input}')
731
+ # print(f'the gpt_response is {gpt_response}')
732
+
733
+
734
+ # print("-----------------")
735
+ # print(user_input, gpt_response)
736
+ # print("-----------------")
737
+
738
+ # import ipdb; ipdb.set_trace()
739
+ if DEFAULT_IMAGE_TOKEN in user_input:
740
+ inputs = processor(text=[user_input], images=images, videos=videos, padding=False, return_tensors='pt')
741
+ prompt_input_ids = inputs['input_ids']
742
+ all_pixel_values.append(inputs[pixel_key])
743
+ all_image_grid_thw.append(inputs[grid_key])
744
+
745
+ # del dino_val
746
+ torch.cuda.empty_cache()
747
+
748
+ elif DEFAULT_VIDEO_TOKEN in user_input:
749
+ if "Qwen3" in self.model_id:
750
+ inputs = processor(text=[user_input], images=images, videos=videos, padding=False, return_tensors='pt', **video_kwargs)
751
+ all_second_gird.extend(inputs["second_per_grid_ts"])
752
+ else:
753
+ inputs = processor(text=[user_input], images=images, videos=videos, padding=False, return_tensors='pt')
754
+ prompt_input_ids = inputs['input_ids']
755
+ all_pixel_values.append(inputs[pixel_key])
756
+ all_image_grid_thw.append(inputs[grid_key])
757
+
758
+ else:
759
+ prompt_input_ids = processor.tokenizer(user_input, add_special_tokens=False, padding=False, return_tensors='pt')['input_ids']
760
+
761
+
762
+ response_input_ids = processor.tokenizer(gpt_response, add_special_tokens=False, padding=False, return_tensors='pt')['input_ids']
763
+
764
+ input_ids = torch.cat([prompt_input_ids, response_input_ids], dim=1).squeeze(0)
765
+ labels = torch.cat(
766
+ [
767
+ torch.tensor([IGNORE_INDEX] * len(prompt_input_ids[0])),
768
+ response_input_ids.squeeze(0),
769
+ ],
770
+ dim=0,
771
+ )
772
+ all_input_ids.append(input_ids)
773
+ all_labels.append(labels)
774
+
775
+ # There is no need for eos or bos tokens in the input_ids
776
+ # Qwen2-VL does not use them
777
+ input_ids = torch.cat(all_input_ids, dim=0).to(torch.long)
778
+ labels = torch.cat(all_labels, dim=0).to(torch.long)
779
+
780
+ # eos_token_id = processor.tokenizer.convert_tokens_to_ids(DEFAULT_IM_END_TOKEN)
781
+ # input_ids, labels = truncate_sequence(input_ids, labels, self.max_length, eos_token_id)
782
+
783
+ attention_mask = (input_ids > -1000000).to(torch.long)
784
+ task_type = "und_image"
785
+
786
+ x_ids, x_mask = input_ids, attention_mask
787
+ while x_ids.dim() < 2:
788
+ x_ids = x_ids.unsqueeze(0)
789
+
790
+ input_ids_2d= x_ids
791
+ data_dict = dict(
792
+ input_ids=input_ids,
793
+ attention_mask=attention_mask,
794
+ labels=labels,
795
+ task_types=task_type,
796
+ )
797
+
798
+ if pixel_key and grid_key:
799
+ pixel_values = torch.cat(all_pixel_values, dim=0)
800
+ image_thw = torch.cat(all_image_grid_thw, dim=0)
801
+
802
+ data_dict[pixel_key] = pixel_values
803
+ data_dict[grid_key] = image_thw
804
+ data_dict["image_files"] = image_files
805
+
806
+ if len(all_second_gird) > 0:
807
+ second_gird = all_second_gird
808
+ data_dict["second_per_grid_ts"] = second_gird
809
+
810
+ position_ids, _ = self.get_rope_index(
811
+ self.merge_size,
812
+ input_ids_2d,
813
+ image_grid_thw=torch.cat(all_image_grid_thw, dim=0) if images else None,
814
+ video_grid_thw=(
815
+ torch.cat(all_image_grid_thw, dim=0) if videos else None
816
+ ),
817
+ second_per_grid_ts= all_second_gird if len(all_second_gird) > 0 else None,
818
+
819
+ )
820
+ data_dict["position_ids"] = position_ids
821
+
822
+ self.cur_step += 1
823
+ return data_dict
824
+
825
+
826
+ def pad_and_cat(tensor_list):
827
+ max_length = max(tensor.shape[2] for tensor in tensor_list)
828
+
829
+ padded_tensors = []
830
+ for tensor in tensor_list:
831
+ pad_length = max_length - tensor.shape[2]
832
+ padded_tensor = torch.nn.functional.pad(tensor, (0, pad_length), "constant", 1)
833
+ padded_tensors.append(padded_tensor)
834
+
835
+ stacked_tensor = torch.cat(padded_tensors, dim=1)
836
+
837
+ return stacked_tensor
838
+
839
+
840
+ @dataclass
841
+ class DataCollatorForSupervisedDataset(object):
842
+ """Collate examples for supervised fine-tuning."""
843
+
844
+ tokenizer: transformers.PreTrainedTokenizer
845
+
846
+ def __call__(self, instances: Sequence[Dict]) -> Dict[str, torch.Tensor]:
847
+ input_ids, labels, position_ids = tuple(
848
+ [instance[key] for instance in instances]
849
+ for key in ("input_ids", "labels", "position_ids")
850
+ )
851
+ input_ids = [ids.squeeze(0) for ids in input_ids]
852
+ labels = [ids.squeeze(0) for ids in labels]
853
+ input_ids = torch.nn.utils.rnn.pad_sequence(
854
+ input_ids, batch_first=True, padding_value=self.tokenizer.pad_token_id
855
+ )
856
+ labels = torch.nn.utils.rnn.pad_sequence(
857
+ labels, batch_first=True, padding_value=IGNORE_INDEX
858
+ )
859
+ position_ids = pad_and_cat(position_ids)
860
+ input_ids = input_ids[:, : self.tokenizer.model_max_length]
861
+ labels = labels[:, : self.tokenizer.model_max_length]
862
+ position_ids = position_ids[:, :, : self.tokenizer.model_max_length]
863
+ batch = dict(
864
+ input_ids=input_ids,
865
+ labels=labels,
866
+ attention_mask=input_ids.ne(self.tokenizer.pad_token_id),
867
+ )
868
+ images = list(
869
+ instance["pixel_values"]
870
+ for instance in instances
871
+ if "pixel_values" in instance
872
+ )
873
+ videos = list(
874
+ instance["pixel_values_videos"]
875
+ for instance in instances
876
+ if "pixel_values_videos" in instance
877
+ )
878
+
879
+ # Align with batch per sample; use None for missing keys or placeholder samples to avoid image_files[i] being misaligned as None in forward if the length is shorter than batch.
880
+ images_files = [
881
+ instance["image_files"] if "image_files" in instance else None
882
+ for instance in instances
883
+ ]
884
+
885
+ if len(images) != 0:
886
+ concat_images = torch.cat([image for image in images], dim=0)
887
+ grid_thw = [
888
+ instance["image_grid_thw"]
889
+ for instance in instances
890
+ if "image_grid_thw" in instance
891
+ ]
892
+ grid_thw = torch.cat(grid_thw, dim=0)
893
+ else:
894
+ concat_images = None
895
+ grid_thw = None
896
+
897
+ if len(videos) != 0:
898
+ concat_videos = torch.cat([video for video in videos], dim=0)
899
+ video_grid_thw = [
900
+ instance["video_grid_thw"]
901
+ for instance in instances
902
+ if "video_grid_thw" in instance
903
+ ]
904
+ video_grid_thw = torch.cat(video_grid_thw, dim=0)
905
+ else:
906
+ concat_videos = None
907
+ video_grid_thw = None
908
+
909
+ batch["pixel_values"] = concat_images
910
+ batch["image_grid_thw"] = grid_thw
911
+ batch["pixel_values_videos"] = concat_videos
912
+ batch["video_grid_thw"] = video_grid_thw
913
+ batch["position_ids"] = position_ids
914
+ batch["image_files"] = images_files
915
+ return batch
916
+
917
+ # @dataclass
918
+ # class DataCollatorForSupervisedDataset(object):
919
+ # """Collate examples for supervised fine-tuning; truncates to tokenizer.model_max_length (same as train_stage1_algin)."""
920
+
921
+ # tokenizer: transformers.PreTrainedTokenizer
922
+
923
+ # def __post_init__(self):
924
+ # self.pad_token_id = self.tokenizer.pad_token_id
925
+
926
+ # def __call__(self, examples):
927
+ # max_len = self.tokenizer.model_max_length
928
+ # batch_input_ids = []
929
+ # batch_label_ids = []
930
+ # batch_pixel_values = []
931
+ # batch_pixel_video_values = []
932
+ # batch_video_thw = []
933
+ # batch_image_thw = []
934
+ # batch_second_per_grid_ts = []
935
+ # batch_mm_token_type_ids = []
936
+
937
+ # batch_image_files = []
938
+
939
+ # for example in examples:
940
+ # keys = example.keys()
941
+ # if "pixel_values_videos" in keys:
942
+ # batch_pixel_video_values.append(example["pixel_values_videos"])
943
+ # batch_video_thw.append(example["video_grid_thw"])
944
+ # elif "pixel_values" in keys:
945
+ # batch_pixel_values.append(example["pixel_values"])
946
+ # batch_image_thw.append(example["image_grid_thw"])
947
+
948
+ # if "image_files" in keys:
949
+ # batch_image_files.append(example["image_files"])
950
+
951
+ # iid = example["input_ids"]
952
+ # lab = example["labels"]
953
+ # if iid.shape[0] > max_len:
954
+ # iid = iid[:max_len]
955
+ # lab = lab[:max_len]
956
+ # batch_input_ids.append(iid)
957
+ # batch_label_ids.append(lab)
958
+ # if "mm_token_type_ids" in keys:
959
+ # mm = example["mm_token_type_ids"]
960
+ # if mm.shape[0] > max_len:
961
+ # mm = mm[:max_len]
962
+ # batch_mm_token_type_ids.append(mm)
963
+
964
+ # if "second_per_grid_ts" in keys:
965
+ # batch_second_per_grid_ts.extend(example["second_per_grid_ts"])
966
+
967
+ # input_ids = pad_sequence(
968
+ # batch_input_ids, padding_side='right', padding_value=self.pad_token_id
969
+ # )
970
+
971
+ # labels = pad_sequence(batch_label_ids, padding_side='right', padding_value=IGNORE_INDEX)
972
+
973
+ # if input_ids.shape[1] > max_len:
974
+ # rank0_print(
975
+ # f"Warning: input with length {input_ids.shape[1]} is longer than max length {max_len}. Truncating."
976
+ # )
977
+ # input_ids = input_ids[:, :max_len]
978
+ # labels = labels[:, :max_len]
979
+ # attention_mask = input_ids != self.pad_token_id
980
+
981
+ # data_dict = {
982
+ # 'input_ids': input_ids,
983
+ # 'labels': labels,
984
+ # 'attention_mask': attention_mask,
985
+ # }
986
+
987
+ # if len(batch_pixel_values) > 0:
988
+ # pixel_values = torch.cat(batch_pixel_values, dim=0)
989
+ # image_thw = torch.cat(batch_image_thw, dim=0)
990
+ # data_dict["pixel_values"] = pixel_values
991
+ # data_dict["image_grid_thw"] = image_thw
992
+
993
+ # if len(batch_pixel_video_values) > 0:
994
+ # pixel_video_values = torch.cat(batch_pixel_video_values, dim=0)
995
+ # video_thw = torch.cat(batch_video_thw, dim=0)
996
+ # data_dict["pixel_values_videos"] = pixel_video_values
997
+ # data_dict["video_grid_thw"] = video_thw
998
+
999
+ # if len(batch_second_per_grid_ts) > 0:
1000
+ # data_dict["second_per_grid_ts"] = batch_second_per_grid_ts
1001
+
1002
+ # if len(batch_image_files) > 0:
1003
+ # data_dict["image_files"] = batch_image_files
1004
+ # return data_dict
1005
+
1006
+ def replace_image_tokens(input_string, is_video=False):
1007
+ if is_video:
1008
+ pattern = r'\n?' + re.escape(LLAVA_VIDEO_TOKEN) + r'\n?'
1009
+ replacement = VISION_START_TOKEN + DEFAULT_VIDEO_TOKEN + VISION_END_TOKEN
1010
+ else:
1011
+ pattern = r'\n?' + re.escape(LLAVA_IMAGE_TOKEN) + r'\n?'
1012
+ replacement = VISION_START_TOKEN + DEFAULT_IMAGE_TOKEN + VISION_END_TOKEN
1013
+
1014
+ return re.sub(pattern, replacement, input_string)
1015
+
1016
+ def llava_to_openai(conversations, is_video=False):
1017
+ role_mapping = {"human": "user", "gpt": "assistant"}
1018
+
1019
+ transformed_data = []
1020
+ for conversation in conversations:
1021
+ transformed_content = replace_image_tokens(conversation["value"], is_video=is_video)
1022
+ transformed_entry = {
1023
+ "role": role_mapping.get(conversation["from"], conversation["from"]),
1024
+ "content": transformed_content,
1025
+ }
1026
+ transformed_data.append(transformed_entry)
1027
+
1028
+ return transformed_data
1029
+
1030
+ def make_supervised_data_module(model_id, processor, data_args, anchor_model_id):
1031
+ """Make dataset and collator for supervised fine-tuning."""
1032
+ sft_dataset = SupervisedDataset(
1033
+ data_path=data_args.data_path, processor=processor, data_args=data_args, model_id=model_id, anchor_model_id=anchor_model_id
1034
+ )
1035
+ data_collator = DataCollatorForSupervisedDataset(tokenizer=processor.tokenizer)
1036
+
1037
+ return dict(train_dataset=sft_dataset,
1038
+ eval_dataset=None,
1039
+ data_collator=data_collator)
code/umm/runtime/models/blip3o/data/mix_und_t2i_data.py ADDED
@@ -0,0 +1,274 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Mixed per-batch sampling: understanding (SupervisedDataset / covt_data) + T2I (LazySupervisedMixDataset).
3
+
4
+ Each batch contains a fixed number of UND samples and T2I samples so both tasks train together.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import math
10
+ from typing import Any, Dict, Iterator, List, Optional, Sequence
11
+
12
+ import torch
13
+ from torch.utils.data import ConcatDataset, Sampler
14
+
15
+ import transformers
16
+
17
+ from models.blip3o.constants import IGNORE_INDEX
18
+ from models.blip3o.data.covt_data import SupervisedDataset
19
+ from models.blip3o.data.t2i_covt_data import LazySupervisedMixDataset
20
+
21
+
22
+ class MixedUndT2IConcatDataset(ConcatDataset):
23
+ """ConcatDataset with `set_cur_step` forwarded to child datasets that support it."""
24
+
25
+ def set_cur_step(self, step: int) -> None:
26
+ for d in self.datasets:
27
+ if hasattr(d, "set_cur_step"):
28
+ d.set_cur_step(step)
29
+
30
+
31
+ class MixedUndT2IBatchSampler(Sampler[List[int]]):
32
+ """
33
+ Yields batches of indices into ConcatDataset: first part = UND, second part = T2I.
34
+ Each batch has exactly `num_und` indices from [0, und_len) and `num_t2i` from [und_len, und_len+t2i_len).
35
+
36
+ If ``mix_start_global_step > 0``, batches before that HF trainer ``global_step`` use only UND
37
+ indices (batch size ``num_und + num_t2i`` from the UND pool); from ``global_step >= mix_start_global_step``
38
+ on, batches alternate UND + T2I as usual. Requires ``global_step_ref`` updated each step (see
39
+ ``MixedUndT2IStepSyncCallback``); safe with ``dataloader_num_workers=0``.
40
+ """
41
+
42
+ def __init__(
43
+ self,
44
+ und_len: int,
45
+ t2i_len: int,
46
+ num_und: int,
47
+ num_t2i: int,
48
+ seed: int = 42,
49
+ rank: int = 0,
50
+ world_size: int = 1,
51
+ mix_start_global_step: int = 0,
52
+ global_step_ref: Optional[List[int]] = None,
53
+ ):
54
+ if und_len <= 0 or t2i_len <= 0:
55
+ raise ValueError(f"und_len and t2i_len must be positive, got {und_len}, {t2i_len}")
56
+ if num_und <= 0 or num_t2i <= 0:
57
+ raise ValueError(f"num_und and num_t2i must be positive, got {num_und}, {num_t2i}")
58
+ if num_und > und_len or num_t2i > t2i_len:
59
+ raise ValueError(
60
+ f"Batch larger than dataset: num_und={num_und} > und_len={und_len} or "
61
+ f"num_t2i={num_t2i} > t2i_len={t2i_len}"
62
+ )
63
+ if mix_start_global_step < 0:
64
+ raise ValueError(f"mix_start_global_step must be >= 0, got {mix_start_global_step}")
65
+
66
+ self.und_len = und_len
67
+ self.t2i_len = t2i_len
68
+ self.t2i_offset = und_len
69
+ self.num_und = num_und
70
+ self.num_t2i = num_t2i
71
+ self.seed = seed
72
+ self.rank = rank
73
+ self.world_size = world_size
74
+ self.epoch = 0
75
+ self.mix_start_global_step = mix_start_global_step
76
+ self.global_step_ref = global_step_ref if global_step_ref is not None else [0]
77
+
78
+ n_mixed = min(und_len // num_und, t2i_len // num_t2i)
79
+ und_bs = num_und + num_t2i
80
+ n_und_only = und_len // und_bs if und_bs > 0 else 0
81
+ self._num_batches = max(n_mixed, n_und_only)
82
+
83
+ def set_epoch(self, epoch: int) -> None:
84
+ self.epoch = int(epoch)
85
+
86
+ def __iter__(self) -> Iterator[List[int]]:
87
+ g = torch.Generator()
88
+ g.manual_seed(self.seed + self.epoch * 100_003 + self.rank * 17)
89
+ und_perm = torch.randperm(self.und_len, generator=g).tolist()
90
+ t2i_perm = torch.randperm(self.t2i_len, generator=g).tolist()
91
+
92
+ iu = 0
93
+ it = 0
94
+ batch_idx = 0
95
+
96
+ while True:
97
+ gs = self.global_step_ref[0]
98
+ if self.mix_start_global_step == 0 or gs >= self.mix_start_global_step:
99
+ if iu + self.num_und > self.und_len or it + self.num_t2i > self.t2i_len:
100
+ break
101
+ batch = und_perm[iu : iu + self.num_und] + [
102
+ self.t2i_offset + t2i_perm[it + k] for k in range(self.num_t2i)
103
+ ]
104
+ iu += self.num_und
105
+ it += self.num_t2i
106
+ else:
107
+ und_bs = self.num_und + self.num_t2i
108
+ if iu + und_bs > self.und_len:
109
+ break
110
+ batch = und_perm[iu : iu + und_bs]
111
+ iu += und_bs
112
+
113
+ if batch_idx % self.world_size != self.rank:
114
+ batch_idx += 1
115
+ continue
116
+ yield list(batch)
117
+ batch_idx += 1
118
+
119
+ def __len__(self) -> int:
120
+ return math.ceil(self._num_batches / self.world_size)
121
+
122
+
123
+ class DataCollatorForUndT2IMixed:
124
+ """Pad text/mm fields; concat vision patches for UND rows only; stack T2I gen_image + row indices."""
125
+
126
+ def __init__(self, pad_token_id: int):
127
+ self.pad_token_id = pad_token_id
128
+
129
+ def __call__(self, instances: Sequence[Dict[str, Any]]) -> Dict[str, torch.Tensor]:
130
+ from models.blip3o.data.covt_data import pad_sequence as covt_pad_sequence
131
+
132
+ batch_input_ids: List[torch.Tensor] = []
133
+ batch_labels: List[torch.Tensor] = []
134
+ batch_mm: List[torch.Tensor] = []
135
+ batch_pixel: List[torch.Tensor] = []
136
+ batch_image_thw: List[torch.Tensor] = []
137
+ batch_pixel_video: List[torch.Tensor] = []
138
+ batch_video_thw: List[torch.Tensor] = []
139
+ batch_second: List = []
140
+ batch_image_files: List[Any] = []
141
+ batch_gen_image: List[torch.Tensor] = []
142
+ t2i_row_indices: List[int] = []
143
+
144
+ for i, ex in enumerate(instances):
145
+ batch_input_ids.append(ex["input_ids"])
146
+ batch_labels.append(ex["labels"])
147
+ if "mm_token_type_ids" in ex:
148
+ batch_mm.append(ex["mm_token_type_ids"])
149
+
150
+ keys = ex.keys()
151
+ if "pixel_values_videos" in keys:
152
+ batch_pixel_video.append(ex["pixel_values_videos"])
153
+ batch_video_thw.append(ex["video_grid_thw"])
154
+ elif "pixel_values" in keys:
155
+ batch_pixel.append(ex["pixel_values"])
156
+ batch_image_thw.append(ex["image_grid_thw"])
157
+
158
+ if "image_files" in ex:
159
+ batch_image_files.append(ex["image_files"])
160
+ else:
161
+ batch_image_files.append(None)
162
+
163
+ if "second_per_grid_ts" in ex:
164
+ batch_second.extend(ex["second_per_grid_ts"])
165
+
166
+ tt = ex.get("task_types", "und_image")
167
+ if tt == "T2I" and "gen_image" in ex:
168
+ gi = ex["gen_image"]
169
+ if gi.dim() == 3:
170
+ gi = gi.unsqueeze(0)
171
+ batch_gen_image.append(gi)
172
+ t2i_row_indices.append(i)
173
+
174
+ input_ids = covt_pad_sequence(batch_input_ids, padding_side="right", padding_value=self.pad_token_id)
175
+ attention_mask = input_ids != self.pad_token_id
176
+ labels = covt_pad_sequence(batch_labels, padding_side="right", padding_value=IGNORE_INDEX)
177
+
178
+ out: Dict[str, Any] = {
179
+ "input_ids": input_ids,
180
+ "labels": labels,
181
+ "attention_mask": attention_mask,
182
+ }
183
+
184
+ if batch_mm:
185
+ out["mm_token_type_ids"] = covt_pad_sequence(batch_mm, padding_side="right", padding_value=0)
186
+
187
+ if batch_pixel:
188
+ out["pixel_values"] = torch.cat(batch_pixel, dim=0)
189
+ out["image_grid_thw"] = torch.cat(batch_image_thw, dim=0)
190
+
191
+ if batch_pixel_video:
192
+ out["pixel_values_videos"] = torch.cat(batch_pixel_video, dim=0)
193
+ out["video_grid_thw"] = torch.cat(batch_video_thw, dim=0)
194
+
195
+ if batch_second:
196
+ out["second_per_grid_ts"] = batch_second
197
+
198
+ out["image_files"] = batch_image_files
199
+
200
+ if batch_gen_image:
201
+ out["gen_image"] = torch.cat(batch_gen_image, dim=0)
202
+ out["t2i_row_indices"] = torch.tensor(t2i_row_indices, dtype=torch.long)
203
+ else:
204
+ out["gen_image"] = None
205
+ out["t2i_row_indices"] = None
206
+
207
+ return out
208
+
209
+
210
+ def make_mixed_und_t2i_data_module(
211
+ *,
212
+ model_id: str,
213
+ processor: transformers.ProcessorMixin,
214
+ data_args: Any,
215
+ anchor_model_id: Optional[List[str]],
216
+ und_data_path: str,
217
+ t2i_data_path: str,
218
+ num_und_per_batch: int,
219
+ num_t2i_per_batch: int,
220
+ mix_start_global_step: int = 0,
221
+ seed: int = 42,
222
+ rank: int = 0,
223
+ world_size: int = 1,
224
+ ) -> Dict[str, Any]:
225
+ """
226
+ Build ConcatDataset( SupervisedDataset(und), LazySupervisedMixDataset(t2i) ) with a mixed BatchSampler.
227
+ """
228
+ und_ds = SupervisedDataset(
229
+ data_path=und_data_path,
230
+ processor=processor,
231
+ data_args=data_args,
232
+ model_id=model_id,
233
+ anchor_model_id=anchor_model_id,
234
+ )
235
+ t2i_ds = LazySupervisedMixDataset(
236
+ data_path=t2i_data_path,
237
+ processor=processor,
238
+ data_args=data_args,
239
+ model_id=model_id,
240
+ anchor_model_id=anchor_model_id,
241
+ random_seed=seed,
242
+ )
243
+
244
+ train_dataset = MixedUndT2IConcatDataset([und_ds, t2i_ds])
245
+ und_len = len(und_ds)
246
+ t2i_len = len(t2i_ds)
247
+
248
+ global_step_ref: List[int] = [0]
249
+ batch_sampler = MixedUndT2IBatchSampler(
250
+ und_len=und_len,
251
+ t2i_len=t2i_len,
252
+ num_und=num_und_per_batch,
253
+ num_t2i=num_t2i_per_batch,
254
+ seed=seed,
255
+ rank=rank,
256
+ world_size=world_size,
257
+ mix_start_global_step=mix_start_global_step,
258
+ global_step_ref=global_step_ref,
259
+ )
260
+
261
+ train_dataset._mixed_batch_sampler = batch_sampler
262
+ train_dataset._mixed_global_step_ref = global_step_ref
263
+ train_dataset._mixed_num_und = num_und_per_batch
264
+ train_dataset._mixed_num_t2i = num_t2i_per_batch
265
+
266
+ data_collator = DataCollatorForUndT2IMixed(pad_token_id=processor.tokenizer.pad_token_id)
267
+
268
+ return {
269
+ "train_dataset": train_dataset,
270
+ "eval_dataset": None,
271
+ "data_collator": data_collator,
272
+ "mixed_train_batch_sampler": batch_sampler,
273
+ "mixed_global_step_ref": global_step_ref,
274
+ }
code/umm/runtime/models/blip3o/data/moe_data.py ADDED
File without changes
code/umm/runtime/models/blip3o/data/t2I_covt_gen_data.py ADDED
@@ -0,0 +1,894 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ T2I / I2I generation dataset for CoVT + Qwen3-VL.
3
+
4
+ - Text/mm fields and ``position_ids`` follow ``covt_data_var`` (``get_rope_index_3``).
5
+ - CoVT anchor pads / staged CoT match ``covt_data_var`` helpers.
6
+ - Keeps ``gen_image`` (and optional I2I ref fields) for the generation head.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import copy
12
+ import io
13
+ import os
14
+ import random
15
+ import time
16
+ from dataclasses import dataclass
17
+ from typing import Any, Dict, List, Optional, Sequence, Tuple
18
+
19
+ import numpy as np
20
+ import torch
21
+ import transformers
22
+ import ujson as json
23
+ from datasets import Image, concatenate_datasets, load_dataset
24
+ from PIL import Image as PILImage
25
+ from PIL import ImageFile
26
+ from torch.utils.data import Dataset
27
+ from torchvision.transforms import v2
28
+
29
+ from models.blip3o.constants import *
30
+ from models.blip3o.data.covt_data_var import (
31
+ add_anchor_pad,
32
+ get_anchor_task_name,
33
+ get_anchor_token,
34
+ get_comt_data_in_response,
35
+ get_image_info,
36
+ get_rope_index_3,
37
+ get_token_num,
38
+ get_video_info,
39
+ llava_to_openai,
40
+ pad_and_cat,
41
+ )
42
+ from models.blip3o.params import DataArguments
43
+
44
+ ImageFile.LOAD_TRUNCATED_IMAGES = True
45
+
46
+ # TUNABLE: VAE / diffusion target resolution for gen_image
47
+ target_transform = v2.Compose(
48
+ [
49
+ v2.Resize(512),
50
+ v2.CenterCrop(512),
51
+ v2.ToImage(),
52
+ v2.ToDtype(torch.float32, scale=True),
53
+ v2.Normalize([0.5], [0.5]),
54
+ ]
55
+ )
56
+
57
+
58
+ def rank0_print(*args):
59
+ if os.environ.get("LOCAL_RANK", "0") in ("0", "-1"):
60
+ print(*args)
61
+
62
+
63
+ def preprocess_multimodal(sources, data_args: DataArguments):
64
+ """Replace LLAVA <image> in assistant turn with <begin_of_img>…<end_of_img> metaquery block."""
65
+ gen_placeholder = (
66
+ f"\n<begin_of_img>"
67
+ + "".join([f"<img{i}>" for i in range(data_args.num_metaqueries)])
68
+ + "<end_of_img>"
69
+ )
70
+ inst_type = None
71
+ for source in sources:
72
+ for sentence in source:
73
+ if sentence["from"] == "gpt" and LLAVA_IMAGE_TOKEN in sentence["value"]:
74
+ sentence["value"] = sentence["value"].replace(LLAVA_IMAGE_TOKEN, gen_placeholder).strip()
75
+ inst_type = "gen"
76
+ return sources, inst_type
77
+
78
+ def _pil_from_sample(img) -> PILImage.Image:
79
+ if isinstance(img, bytes):
80
+ return PILImage.open(io.BytesIO(img)).convert("RGB")
81
+ if isinstance(img, PILImage.Image):
82
+ return img.convert("RGB")
83
+ if isinstance(img, dict) and "bytes" in img:
84
+ return PILImage.open(io.BytesIO(img["bytes"])).convert("RGB")
85
+ raise TypeError(f"Unsupported image type: {type(img)}")
86
+
87
+ def _covt_targets_gpt(user_input: str, inst_type: Optional[str], task_type: str) -> bool:
88
+ """Whether to inject CoVT CoT / anchor pads into the assistant turn."""
89
+ if DEFAULT_IMAGE_TOKEN in user_input or DEFAULT_VIDEO_TOKEN in user_input:
90
+ return True
91
+ return inst_type == "gen" or task_type in ("T2I", "I2I")
92
+
93
+
94
+ def get_t21_data_files(data_path: str):
95
+ rank = int(os.environ.get("RANK", os.environ.get("LOCAL_RANK", 0)))
96
+ max_retries = 5
97
+ retry_delay = 2
98
+ for attempt in range(max_retries):
99
+ try:
100
+ if rank == 0 or attempt == max_retries - 1:
101
+ print(f"[Rank {rank}] Loading T2I json (attempt {attempt + 1}/{max_retries})...")
102
+ train_dataset = load_dataset(
103
+ "json",
104
+ data_files=data_path,
105
+ split="train",
106
+ download_mode="reuse_dataset_if_exists",
107
+ )
108
+ if rank == 0:
109
+ print(f"[Rank {rank}] T2I dataset loaded.")
110
+ break
111
+ except PermissionError as e:
112
+ if attempt < max_retries - 1:
113
+ time.sleep(retry_delay)
114
+ retry_delay *= 2
115
+ else:
116
+ raise e
117
+ train_dataset = train_dataset.rename_column("image_path", "image")
118
+ train_dataset = train_dataset.rename_column("prompt", "txt")
119
+ train_dataset = train_dataset.remove_columns(
120
+ [col for col in train_dataset.column_names if col not in ("image", "txt")]
121
+ )
122
+ return train_dataset
123
+
124
+
125
+ def force_align_image_schema(ds):
126
+ std_image = Image(decode=True)
127
+ if "image" in ds.features:
128
+ ds = ds.cast_column("image", std_image)
129
+ if "ref_image" in ds.features:
130
+ ds = ds.cast_column("ref_image", std_image)
131
+ else:
132
+ ds = ds.map(lambda x: {**x, "ref_image": None})
133
+ ds = ds.cast_column("ref_image", std_image)
134
+ return ds
135
+
136
+
137
+ class SupervisedDataset(Dataset):
138
+ """Dataset for supervised fine-tuning (UND / VQA), aligned with ``covt_data_var.SupervisedDataset``."""
139
+
140
+ def __init__(
141
+ self,
142
+ data_path: str | list,
143
+ processor: transformers.ProcessorMixin,
144
+ data_args: DataArguments,
145
+ model_id,
146
+ padding=True,
147
+ shuffle=True,
148
+ random_seed=42,
149
+ anchor_model_id=None,
150
+ ):
151
+ super(SupervisedDataset, self).__init__()
152
+ if isinstance(data_path, str):
153
+ list_data_dict = json.load(open(data_path, "r"))
154
+ else:
155
+ list_data_dict = data_path
156
+
157
+ self.model_id = model_id
158
+ self.processor = processor
159
+ self.list_data_dict = list_data_dict
160
+ self.data_args = data_args
161
+ self.padding = padding
162
+ self.image_min_pixel = data_args.image_min_pixels
163
+ self.image_max_pixel = data_args.image_max_pixels
164
+ self.image_resized_w = data_args.image_resized_width
165
+ self.image_resized_h = data_args.image_resized_height
166
+ self.video_min_pixel = data_args.video_min_pixels
167
+ self.video_max_pixel = data_args.video_max_pixels
168
+ self.fps = data_args.fps
169
+ self.merge_size = getattr(processor.image_processor, "merge_size", 2)
170
+ self.get_rope_index = get_rope_index_3
171
+ self.anchor_model_id = anchor_model_id
172
+ self.anchor_token_nums = get_token_num(anchor_model_id)
173
+ self.anchor_tokens = get_anchor_token(anchor_model_id)
174
+ self.anchor_task_names = get_anchor_task_name(anchor_model_id)
175
+
176
+ # for shuffle
177
+ self.rng = np.random.default_rng(seed=random_seed)
178
+
179
+ if shuffle:
180
+ self.rng.shuffle(self.list_data_dict)
181
+
182
+ def set_cur_step(self, step: int) -> None:
183
+ """ResumeDatasetCallback hook; UND dataset has no step-dependent sampling."""
184
+ del step # unused
185
+
186
+ def __len__(self):
187
+ return len(self.list_data_dict)
188
+
189
+ def __getitem__(self, i) -> Dict[str, torch.Tensor]:
190
+ sources = self.list_data_dict[i]
191
+
192
+ is_video = False
193
+ image_files = None
194
+
195
+ processor = self.processor
196
+ if "image" in sources:
197
+ videos = None
198
+ grid_key = "image_grid_thw"
199
+ pixel_key = "pixel_values"
200
+
201
+ image_files = sources["image"]
202
+ image_folder = self.data_args.image_folder
203
+
204
+ if isinstance(image_files, str):
205
+ image_files = PILImage.open(image_files).convert("RGB")
206
+ image_files = [image_files]
207
+ else:
208
+ image_files = [PILImage.open(image_file).convert("RGB") for image_file in image_files]
209
+ images = []
210
+
211
+ for image_file in image_files:
212
+ images.append(
213
+ get_image_info(
214
+ image_file,
215
+ self.image_min_pixel,
216
+ self.image_max_pixel,
217
+ self.image_resized_w,
218
+ self.image_resized_h,
219
+ )
220
+ )
221
+
222
+ elif "video" in sources:
223
+ is_video = True
224
+ images = None
225
+ grid_key = "video_grid_thw"
226
+ pixel_key = "pixel_values_videos"
227
+
228
+ video_files = sources["video"]
229
+ video_folder = self.data_args.image_folder
230
+
231
+ if isinstance(video_files, str):
232
+ video_files = [video_files]
233
+
234
+ videos = []
235
+ for video_file in video_files:
236
+ if not os.path.exists(video_file):
237
+ if not video_file.startswith("http"):
238
+ video_file = os.path.join(video_folder, video_file)
239
+ video_input, video_kwargs = get_video_info(
240
+ video_file, self.video_min_pixel, self.video_max_pixel, self.data_args.fps
241
+ )
242
+ videos.append(video_input)
243
+ else:
244
+ grid_key = None
245
+ pixel_key = None
246
+ images = None
247
+ videos = None
248
+
249
+ if images is None:
250
+ rank0_print("No image or video found in the data.")
251
+ images = []
252
+ black_image = PILImage.new("RGB", (self.image_resized_w, self.image_resized_h), (0, 0, 0))
253
+ images.append(
254
+ get_image_info(
255
+ black_image,
256
+ self.image_min_pixel,
257
+ self.image_max_pixel,
258
+ self.image_resized_w,
259
+ self.image_resized_h,
260
+ )
261
+ )
262
+
263
+ elif len(images) == 0:
264
+ rank0_print("No image or video found in the data.")
265
+ black_image = PILImage.new("RGB", (self.image_resized_w, self.image_resized_h), (0, 0, 0))
266
+ images.append(
267
+ get_image_info(
268
+ black_image,
269
+ self.image_min_pixel,
270
+ self.image_max_pixel,
271
+ self.image_resized_w,
272
+ self.image_resized_h,
273
+ )
274
+ )
275
+ sources = copy.deepcopy(llava_to_openai(sources["conversations"], is_video=is_video))
276
+
277
+ all_input_ids = []
278
+ all_labels = []
279
+ all_pixel_values = []
280
+ all_image_grid_thw = []
281
+ all_second_gird = []
282
+
283
+ if len(SYSTEM_MESSAGE) > 0:
284
+ system_message = f"{DEFAULT_IM_START_TOKEN}system\n{SYSTEM_MESSAGE}\n{DEFAULT_IM_END_TOKEN}\n"
285
+ system_message_input_ids = processor.tokenizer(system_message, add_special_tokens=False, return_tensors="pt")[
286
+ "input_ids"
287
+ ]
288
+
289
+ system_labels = torch.full_like(system_message_input_ids, IGNORE_INDEX)
290
+ all_input_ids.append(system_message_input_ids.squeeze(0))
291
+ all_labels.append(system_labels.squeeze(0))
292
+
293
+ for _, j in enumerate(range(0, len(sources), 2)):
294
+ if j >= 2:
295
+ break
296
+
297
+ user_input = sources[j]
298
+ gpt_response = sources[j + 1]
299
+ if (DEFAULT_IMAGE_TOKEN not in user_input["content"]) and (DEFAULT_VIDEO_TOKEN not in user_input["content"]) and (
300
+ LLAVA_IMAGE_TOKEN in user_input["content"]
301
+ ):
302
+ user_input = (
303
+ f"{DEFAULT_IM_START_TOKEN}{VISION_START_TOKEN + DEFAULT_IMAGE_TOKEN + VISION_END_TOKEN}"
304
+ f"{user_input['role']}\n{user_input['content']}\n{DEFAULT_IM_END_TOKEN}\n"
305
+ f"{DEFAULT_IM_START_TOKEN}{gpt_response['role']}\n"
306
+ )
307
+ user_input = add_anchor_pad(user_input, self.anchor_token_nums, self.anchor_tokens)
308
+ gpt_response = f"{gpt_response['content']}\n{DEFAULT_IM_END_TOKEN}\n"
309
+ raise ValueError("Every man is a poet when he is in love")
310
+ else:
311
+ user_input = (
312
+ f"{DEFAULT_IM_START_TOKEN}{user_input['role']}\n{user_input['content']}\n"
313
+ f"{DEFAULT_IM_END_TOKEN}\n{DEFAULT_IM_START_TOKEN}{gpt_response['role']}\n"
314
+ )
315
+ # TUNABLE: The probability boundary between the full set and the random subset anchor CoT.
316
+ if random.random() < 0.5:
317
+ gpt_response = f"{gpt_response['content']}"
318
+ if DEFAULT_IMAGE_TOKEN in user_input:
319
+ gpt_response = get_comt_data_in_response(
320
+ gpt_response, self.anchor_token_nums, self.anchor_tokens, self.anchor_task_names
321
+ )
322
+ gpt_response = f"{gpt_response}\n{DEFAULT_IM_END_TOKEN}\n"
323
+ else:
324
+ xxx = random.randint(0, 5)
325
+ if xxx == 0:
326
+ gpt_response = f"{gpt_response['content']}\n{DEFAULT_IM_END_TOKEN}\n"
327
+ else:
328
+ gpt_response = f"{gpt_response['content']}"
329
+ if DEFAULT_IMAGE_TOKEN in user_input:
330
+ total = len(self.anchor_tokens)
331
+ if total == 0:
332
+ selected_anchor_token_nums = []
333
+ selected_anchor_tokens = []
334
+ selected_anchor_task_names = []
335
+ else:
336
+ x = random.randint(1, total)
337
+ idxs = sorted(random.sample(range(total), x)) if x > 0 else []
338
+ selected_anchor_token_nums = [self.anchor_token_nums[i] for i in idxs]
339
+ selected_anchor_tokens = [self.anchor_tokens[i] for i in idxs]
340
+ selected_anchor_task_names = [self.anchor_task_names[i] for i in idxs]
341
+ gpt_response = get_comt_data_in_response(
342
+ gpt_response,
343
+ selected_anchor_token_nums,
344
+ selected_anchor_tokens,
345
+ selected_anchor_task_names,
346
+ )
347
+ gpt_response = f"{gpt_response}\n{DEFAULT_IM_END_TOKEN}\n"
348
+
349
+ if DEFAULT_IMAGE_TOKEN in user_input:
350
+ inputs = processor(text=[user_input], images=images, videos=videos, padding=False, return_tensors="pt")
351
+ prompt_input_ids = inputs["input_ids"]
352
+ all_pixel_values.append(inputs[pixel_key])
353
+ all_image_grid_thw.append(inputs[grid_key])
354
+ torch.cuda.empty_cache()
355
+
356
+
357
+ elif DEFAULT_VIDEO_TOKEN in user_input:
358
+ if "Qwen3" in self.model_id:
359
+ inputs = processor(
360
+ text=[user_input],
361
+ images=images,
362
+ videos=videos,
363
+ padding=False,
364
+ return_tensors="pt",
365
+ **video_kwargs,
366
+ )
367
+ all_second_gird.extend(inputs["second_per_grid_ts"])
368
+ else:
369
+ inputs = processor(
370
+ text=[user_input], images=images, videos=videos, padding=False, return_tensors="pt"
371
+ )
372
+ prompt_input_ids = inputs["input_ids"]
373
+ all_pixel_values.append(inputs[pixel_key])
374
+ all_image_grid_thw.append(inputs[grid_key])
375
+
376
+ else:
377
+ prompt_input_ids = processor.tokenizer(
378
+ user_input, add_special_tokens=False, padding=False, return_tensors="pt"
379
+ )["input_ids"]
380
+
381
+ response_input_ids = processor.tokenizer(
382
+ gpt_response, add_special_tokens=False, padding=False, return_tensors="pt"
383
+ )["input_ids"]
384
+
385
+ input_ids = torch.cat([prompt_input_ids, response_input_ids], dim=1).squeeze(0)
386
+ labels = torch.cat(
387
+ [
388
+ torch.tensor([IGNORE_INDEX] * len(prompt_input_ids[0])),
389
+ response_input_ids.squeeze(0),
390
+ ],
391
+ dim=0,
392
+ )
393
+ all_input_ids.append(input_ids)
394
+ all_labels.append(labels)
395
+
396
+ input_ids = torch.cat(all_input_ids, dim=0).to(torch.long)
397
+ labels = torch.cat(all_labels, dim=0).to(torch.long)
398
+
399
+ attention_mask = (input_ids > -1000000).to(torch.long)
400
+ task_type = "und_image"
401
+
402
+ x_ids, x_mask = input_ids, attention_mask
403
+ while x_ids.dim() < 2:
404
+ x_ids = x_ids.unsqueeze(0)
405
+
406
+ input_ids_2d = x_ids
407
+ data_dict = dict(
408
+ input_ids=input_ids,
409
+ attention_mask=attention_mask,
410
+ labels=labels,
411
+ task_types=task_type,
412
+ )
413
+
414
+ if pixel_key and grid_key:
415
+ pixel_values = torch.cat(all_pixel_values, dim=0)
416
+ image_thw = torch.cat(all_image_grid_thw, dim=0)
417
+
418
+ data_dict[pixel_key] = pixel_values
419
+ data_dict[grid_key] = image_thw
420
+ data_dict["image_files"] = image_files
421
+
422
+ if len(all_second_gird) > 0:
423
+ second_gird = all_second_gird
424
+ data_dict["second_per_grid_ts"] = second_gird
425
+
426
+ position_ids, _ = self.get_rope_index(
427
+ self.merge_size,
428
+ input_ids_2d,
429
+ image_grid_thw=torch.cat(all_image_grid_thw, dim=0) if images else None,
430
+ video_grid_thw=(torch.cat(all_image_grid_thw, dim=0) if videos else None),
431
+ second_per_grid_ts=all_second_gird if len(all_second_gird) > 0 else None,
432
+ )
433
+ data_dict["position_ids"] = position_ids
434
+
435
+ # SmartEdit LLaVAMLLMSD: understanding samples mask diffusion branch
436
+ data_dict["is_gen_task"] = torch.zeros(1)
437
+
438
+ return data_dict
439
+
440
+
441
+ class SupervisedT2IDataset(Dataset):
442
+ """T2I / I2I mix dataset: Qwen3-VL processor + CoVT anchors + gen_image."""
443
+
444
+ def __init__(
445
+ self,
446
+ data_path: str,
447
+ processor: transformers.ProcessorMixin,
448
+ data_args: DataArguments,
449
+ model_id: str,
450
+ padding: bool = True,
451
+ shuffle: bool = False,
452
+ random_seed: int = 42,
453
+ anchor_model_id: Optional[List[str]] = None,
454
+ ):
455
+ super().__init__()
456
+ self.model_id = model_id
457
+ self.processor = processor
458
+ self.data_args = data_args
459
+ self.padding = padding
460
+ self.image_min_pixel = data_args.image_min_pixels
461
+ self.image_max_pixel = data_args.image_max_pixels
462
+ self.image_resized_w = data_args.image_resized_width
463
+ self.image_resized_h = data_args.image_resized_height
464
+ self.video_min_pixel = data_args.video_min_pixels
465
+ self.video_max_pixel = data_args.video_max_pixels
466
+ self.fps = data_args.fps
467
+ self.merge_size = getattr(processor.image_processor, "merge_size", 2)
468
+ self.get_rope_index = get_rope_index_3
469
+ self.anchor_model_id = anchor_model_id or []
470
+ self.anchor_token_nums = get_token_num(self.anchor_model_id)
471
+ self.anchor_tokens = get_anchor_token(self.anchor_model_id)
472
+ self.anchor_task_names = get_anchor_task_name(self.anchor_model_id)
473
+
474
+ t2i_dataset = get_t21_data_files(data_path)
475
+ t2i_dataset = (
476
+ t2i_dataset.add_column("type", ["T2I"] * len(t2i_dataset)).select_columns(["image", "txt", "type"])
477
+ )
478
+ t2i_dataset = force_align_image_schema(t2i_dataset)
479
+ rank0_print(f"Loaded T2I dataset: {len(t2i_dataset)} samples")
480
+ self.list_data_dict = t2i_dataset.shuffle(seed=random_seed) if shuffle else t2i_dataset
481
+ rank0_print(f"Total training instances: {len(self.list_data_dict)}")
482
+
483
+ def set_cur_step(self, step: int) -> None:
484
+ """ResumeDatasetCallback hook; T2I dataset has no step-dependent sampling."""
485
+ del step # unused
486
+
487
+ def __len__(self):
488
+ return len(self.list_data_dict)
489
+
490
+ def _build_user_gpt_strings(
491
+ self,
492
+ user_turn: Dict,
493
+ gpt_turn: Dict,
494
+ inst_type: Optional[str],
495
+ task_type: str,
496
+ ) -> Tuple[str, str]:
497
+ user_input = (
498
+ f"{DEFAULT_IM_START_TOKEN}{user_turn['role']}\n{user_turn['content']}\n"
499
+ f"{DEFAULT_IM_END_TOKEN}\n{DEFAULT_IM_START_TOKEN}{gpt_turn['role']}\n"
500
+ )
501
+
502
+ if random.random() < 0.5:
503
+ gpt_response = f"{gpt_turn['content']}"
504
+ if _covt_targets_gpt(user_input, inst_type, task_type):
505
+ gpt_response = get_comt_data_in_response(
506
+ gpt_response, self.anchor_token_nums, self.anchor_tokens, self.anchor_task_names
507
+ )
508
+ gpt_response = f"{gpt_response}\n{DEFAULT_IM_END_TOKEN}\n"
509
+ else:
510
+ if random.randint(0, 5) == 0:
511
+ gpt_response = f"{gpt_turn['content']}\n{DEFAULT_IM_END_TOKEN}\n"
512
+ else:
513
+ gpt_response = f"{gpt_turn['content']}"
514
+ if _covt_targets_gpt(user_input, inst_type, task_type):
515
+ total = len(self.anchor_tokens)
516
+ if total == 0:
517
+ sel_nums, sel_toks, sel_names = [], [], []
518
+ else:
519
+ x = random.randint(1, total)
520
+ idxs = sorted(random.sample(range(total), x))
521
+ sel_nums = [self.anchor_token_nums[i] for i in idxs]
522
+ sel_toks = [self.anchor_tokens[i] for i in idxs]
523
+ sel_names = [self.anchor_task_names[i] for i in idxs]
524
+ gpt_response = get_comt_data_in_response(gpt_response, sel_nums, sel_toks, sel_names)
525
+ gpt_response = f"{gpt_response}\n{DEFAULT_IM_END_TOKEN}\n"
526
+ return user_input, gpt_response
527
+
528
+ def __getitem__(self, i) -> Dict[str, torch.Tensor]:
529
+ processor = self.processor
530
+ while True:
531
+ row = self.list_data_dict[i]
532
+ task_type = row["type"]
533
+ if task_type == "T2I":
534
+ conversations = [
535
+ {
536
+ "from": "human",
537
+ "value": (
538
+ f"Please generate an image that matches the following description: {row['txt']}"
539
+ ),
540
+ },
541
+ {"from": "gpt", "value": LLAVA_IMAGE_TOKEN},
542
+ ]
543
+ target_pils = [_pil_from_sample(row["image"])]
544
+ ref_pils = None
545
+ elif task_type == "I2I":
546
+ conversations = [
547
+ {
548
+ "from": "human",
549
+ "value": (
550
+ f"{LLAVA_IMAGE_TOKEN}\nPlease edit the given image based on the "
551
+ f"following instruction: {row['txt']}."
552
+ ),
553
+ },
554
+ {"from": "gpt", "value": LLAVA_IMAGE_TOKEN},
555
+ ]
556
+ try:
557
+ target_pils = [_pil_from_sample(row["image"])]
558
+ ref_pils = [_pil_from_sample(row["ref_image"])]
559
+ except Exception as e:
560
+ rank0_print(f"I2I image load error: {e}; resample.")
561
+ i = random.randint(0, len(self.list_data_dict) - 1)
562
+ continue
563
+ else:
564
+ raise ValueError(f"Unknown task type: {task_type}")
565
+
566
+ sources, inst_type = preprocess_multimodal(copy.deepcopy([conversations]), self.data_args)
567
+ if inst_type != "gen":
568
+ i = random.randint(0, len(self.list_data_dict) - 1)
569
+ continue
570
+
571
+ openai_turns = copy.deepcopy(llava_to_openai(sources[0], is_video=False))
572
+
573
+ # Vision tensors for Qwen3-VL (I2I: ref image in user turn)
574
+ videos = None
575
+ grid_key = "image_grid_thw"
576
+ pixel_key = "pixel_values"
577
+ vision_images: List[Any] = []
578
+ if task_type == "I2I" and ref_pils is not None:
579
+ for ref in ref_pils:
580
+ vision_images.append(
581
+ get_image_info(
582
+ ref,
583
+ self.image_min_pixel,
584
+ self.image_max_pixel,
585
+ self.image_resized_w,
586
+ self.image_resized_h,
587
+ )
588
+ )
589
+ image_files = target_pils
590
+
591
+ all_input_ids: List[torch.Tensor] = []
592
+ all_labels: List[torch.Tensor] = []
593
+ all_pixel_values: List[torch.Tensor] = []
594
+ all_image_grid_thw: List[torch.Tensor] = []
595
+ all_second_grid: List = []
596
+
597
+ if len(SYSTEM_MESSAGE) > 0:
598
+ system_message = (
599
+ f"{DEFAULT_IM_START_TOKEN}system\n{SYSTEM_MESSAGE}\n{DEFAULT_IM_END_TOKEN}\n"
600
+ )
601
+ system_ids = processor.tokenizer(system_message, add_special_tokens=False, return_tensors="pt")[
602
+ "input_ids"
603
+ ].squeeze(0)
604
+ all_input_ids.append(system_ids)
605
+ all_labels.append(torch.full_like(system_ids, IGNORE_INDEX))
606
+
607
+ for j in range(0, len(openai_turns), 2):
608
+ if j >= 2:
609
+ break
610
+ user_turn = openai_turns[j]
611
+ gpt_turn = openai_turns[j + 1]
612
+ user_str, gpt_str = self._build_user_gpt_strings(user_turn, gpt_turn, inst_type, task_type)
613
+
614
+ if DEFAULT_IMAGE_TOKEN in user_str or DEFAULT_VIDEO_TOKEN in user_str:
615
+ if "Qwen3" in self.model_id and DEFAULT_VIDEO_TOKEN in user_str:
616
+ inputs = processor(
617
+ text=[user_str],
618
+ images=vision_images if vision_images else None,
619
+ videos=videos,
620
+ padding=False,
621
+ return_tensors="pt",
622
+ )
623
+ if "second_per_grid_ts" in inputs:
624
+ all_second_grid.extend(inputs["second_per_grid_ts"])
625
+ else:
626
+ inputs = processor(
627
+ text=[user_str],
628
+ images=vision_images if vision_images else None,
629
+ videos=videos,
630
+ padding=False,
631
+ return_tensors="pt",
632
+ )
633
+ prompt_input_ids = inputs["input_ids"]
634
+ if pixel_key in inputs and inputs[pixel_key] is not None:
635
+ all_pixel_values.append(inputs[pixel_key])
636
+ all_image_grid_thw.append(inputs[grid_key])
637
+ else:
638
+ prompt_input_ids = processor.tokenizer(
639
+ user_str, add_special_tokens=False, padding=False, return_tensors="pt"
640
+ )["input_ids"]
641
+
642
+ response_input_ids = processor.tokenizer(
643
+ gpt_str, add_special_tokens=False, padding=False, return_tensors="pt"
644
+ )["input_ids"]
645
+
646
+ input_ids = torch.cat([prompt_input_ids, response_input_ids], dim=1).squeeze(0)
647
+ labels = torch.cat(
648
+ [
649
+ torch.full((prompt_input_ids.shape[1],), IGNORE_INDEX, dtype=torch.long),
650
+ response_input_ids.squeeze(0),
651
+ ],
652
+ dim=0,
653
+ )
654
+ all_input_ids.append(input_ids)
655
+ all_labels.append(labels)
656
+
657
+ input_ids = torch.cat(all_input_ids, dim=0).to(torch.long)
658
+ labels = torch.cat(all_labels, dim=0).to(torch.long)
659
+ attention_mask = (input_ids > -1000000).to(torch.long)
660
+
661
+ input_ids_2d = input_ids.unsqueeze(0) if input_ids.dim() == 1 else input_ids
662
+
663
+ data_dict: Dict[str, Any] = {
664
+ "input_ids": input_ids,
665
+ "attention_mask": attention_mask,
666
+ "labels": labels,
667
+ "task_types": task_type,
668
+ "image_files": image_files,
669
+ }
670
+
671
+ if all_pixel_values:
672
+ data_dict[pixel_key] = torch.cat(all_pixel_values, dim=0)
673
+ data_dict[grid_key] = torch.cat(all_image_grid_thw, dim=0)
674
+
675
+ if all_second_grid:
676
+ data_dict["second_per_grid_ts"] = all_second_grid
677
+
678
+ position_ids, _ = self.get_rope_index(
679
+ self.merge_size,
680
+ input_ids_2d,
681
+ image_grid_thw=data_dict.get(grid_key),
682
+ video_grid_thw=None,
683
+ second_per_grid_ts=data_dict.get("second_per_grid_ts"),
684
+ )
685
+ data_dict["position_ids"] = position_ids
686
+
687
+ # TUNABLE: gen_image spatial size via data_args.image_size or target_transform (512)
688
+ if inst_type == "gen":
689
+ if getattr(self.data_args, "gen_image_processor", None) is not None:
690
+ sz = self.data_args.gen_image_size
691
+ resized = [
692
+ img.resize((sz, sz), resample=PILImage.Resampling.BICUBIC) for img in target_pils
693
+ ]
694
+ data_dict["gen_image"] = self.data_args.gen_image_processor.preprocess(
695
+ resized, return_tensors="pt"
696
+ )["pixel_values"]
697
+ else:
698
+ data_dict["gen_image"] = torch.stack(
699
+ [target_transform(img) for img in target_pils], dim=0
700
+ )
701
+
702
+ if task_type == "I2I" and ref_pils is not None:
703
+ if getattr(self.data_args, "gen_image_processor", None) is not None:
704
+ sz = self.data_args.gen_image_size
705
+ ref_resized = [
706
+ img.resize((sz, sz), resample=PILImage.Resampling.BICUBIC) for img in ref_pils
707
+ ]
708
+ data_dict["gen_ref_image"] = self.data_args.gen_image_processor.preprocess(
709
+ ref_resized, return_tensors="pt"
710
+ )["pixel_values"]
711
+ else:
712
+ data_dict["gen_ref_image"] = torch.stack(
713
+ [target_transform(img) for img in ref_pils], dim=0
714
+ )
715
+ data_dict["ref_mask"] = torch.ones(
716
+ (1, 1, 1, 1), dtype=data_dict["gen_image"].dtype, device=data_dict["gen_image"].device
717
+ )
718
+
719
+ # SmartEdit ReasonEditMLLMSD: generation / editing samples enable diffusion branch
720
+ data_dict["is_gen_task"] = torch.ones(1)
721
+
722
+ return data_dict
723
+
724
+
725
+ @dataclass
726
+ class DataCollatorForSupervisedDataset:
727
+ """Pad text/mm tensors (Qwen3-VL position_ids) and stack gen_image for T2I rows."""
728
+
729
+ tokenizer: transformers.PreTrainedTokenizer
730
+
731
+ def __call__(self, instances: Sequence[Dict]) -> Dict[str, torch.Tensor]:
732
+ input_ids, labels, position_ids = tuple(
733
+ [instance[key] for instance in instances] for key in ("input_ids", "labels", "position_ids")
734
+ )
735
+ input_ids = [ids.squeeze(0) if ids.dim() > 1 else ids for ids in input_ids]
736
+ labels = [ids.squeeze(0) if ids.dim() > 1 else ids for ids in labels]
737
+
738
+ input_ids = torch.nn.utils.rnn.pad_sequence(
739
+ input_ids, batch_first=True, padding_value=self.tokenizer.pad_token_id
740
+ )
741
+ labels = torch.nn.utils.rnn.pad_sequence(labels, batch_first=True, padding_value=IGNORE_INDEX)
742
+ position_ids = pad_and_cat(position_ids)
743
+
744
+ input_ids = input_ids[:, : self.tokenizer.model_max_length]
745
+ labels = labels[:, : self.tokenizer.model_max_length]
746
+ position_ids = position_ids[:, :, : self.tokenizer.model_max_length]
747
+
748
+ batch: Dict[str, Any] = {
749
+ "input_ids": input_ids,
750
+ "labels": labels,
751
+ "attention_mask": input_ids.ne(self.tokenizer.pad_token_id),
752
+ "position_ids": position_ids,
753
+ }
754
+
755
+ pixel_list = [ex["pixel_values"] for ex in instances if "pixel_values" in ex]
756
+ if pixel_list:
757
+ batch["pixel_values"] = torch.cat(pixel_list, dim=0)
758
+ batch["image_grid_thw"] = torch.cat(
759
+ [ex["image_grid_thw"] for ex in instances if "image_grid_thw" in ex], dim=0
760
+ )
761
+ else:
762
+ batch["pixel_values"] = None
763
+ batch["image_grid_thw"] = None
764
+
765
+ batch["image_files"] = [ex.get("image_files") for ex in instances]
766
+
767
+ gen_images: List[torch.Tensor] = []
768
+ t2i_row_indices: List[int] = []
769
+ gen_ref_images: List[torch.Tensor] = []
770
+ ref_masks: List[torch.Tensor] = []
771
+
772
+ for idx, ex in enumerate(instances):
773
+ if ex.get("task_types") == "T2I" and "gen_image" in ex:
774
+ gi = ex["gen_image"]
775
+ if gi.dim() == 3:
776
+ gi = gi.unsqueeze(0)
777
+ gen_images.append(gi)
778
+ t2i_row_indices.append(idx)
779
+ if "gen_ref_image" in ex:
780
+ gen_ref_images.append(ex["gen_ref_image"])
781
+ if "ref_mask" in ex:
782
+ ref_masks.append(ex["ref_mask"])
783
+
784
+ if gen_images:
785
+ batch["gen_image"] = torch.cat(gen_images, dim=0)
786
+ batch["t2i_row_indices"] = torch.tensor(t2i_row_indices, dtype=torch.long)
787
+ else:
788
+ batch["gen_image"] = None
789
+ batch["t2i_row_indices"] = None
790
+
791
+ if gen_ref_images:
792
+ batch["gen_ref_image"] = torch.cat(gen_ref_images, dim=0)
793
+ if ref_masks:
794
+ batch["ref_mask"] = torch.cat(ref_masks, dim=0)
795
+
796
+ if all("is_gen_task" in ex for ex in instances):
797
+ batch["is_gen_task"] = torch.stack([ex["is_gen_task"] for ex in instances])
798
+ return batch
799
+
800
+ class Merge_UndT2IDataset(Dataset):
801
+ """SmartEdit-style merge: each ``__getitem__`` randomly draws UND or T2I by ``und_weight:t2i_weight``."""
802
+
803
+ def __init__(
804
+ self,
805
+ und_dataset: SupervisedDataset,
806
+ t2i_dataset: SupervisedT2IDataset,
807
+ und_weight: int = 1,
808
+ t2i_weight: int = 3,
809
+ ):
810
+ if und_weight <= 0 or t2i_weight <= 0:
811
+ raise ValueError(f"und_weight and t2i_weight must be positive, got {und_weight}, {t2i_weight}")
812
+ self.und_dataset = und_dataset
813
+ self.t2i_dataset = t2i_dataset
814
+ self.und_len = len(und_dataset)
815
+ self.t2i_len = len(t2i_dataset)
816
+ self.total_len = self.und_len + self.t2i_len
817
+ self.und_weight = int(und_weight)
818
+ self.t2i_weight = int(t2i_weight)
819
+ self.und_prob = self.und_weight / (self.und_weight + self.t2i_weight)
820
+
821
+ def set_cur_step(self, step: int) -> None:
822
+ """Forward to child datasets that support resume step sync."""
823
+ if hasattr(self.und_dataset, "set_cur_step"):
824
+ self.und_dataset.set_cur_step(step)
825
+ if hasattr(self.t2i_dataset, "set_cur_step"):
826
+ self.t2i_dataset.set_cur_step(step)
827
+
828
+ def __getitem__(self, index):
829
+ if self.total_len <= 0:
830
+ raise IndexError("Merge_UndT2IDataset has zero total length")
831
+ if random.random() < self.und_prob:
832
+ return self.und_dataset[random.randint(0, self.und_len - 1)]
833
+ return self.t2i_dataset[random.randint(0, self.t2i_len - 1)]
834
+
835
+ def __len__(self):
836
+ return self.total_len
837
+
838
+ def __repr__(self) -> str:
839
+ return (
840
+ f"Merge_UndT2IDataset(und_len={self.und_len}, t2i_len={self.t2i_len}, total_len={self.total_len}, "
841
+ f"und_weight={self.und_weight}, t2i_weight={self.t2i_weight}, und_prob={self.und_prob:.4f})"
842
+ )
843
+
844
+
845
+ def make_merged_und_t2i_gen_data_module(
846
+ model_id: str,
847
+ processor: transformers.ProcessorMixin,
848
+ data_args: DataArguments,
849
+ anchor_model_id: Optional[List[str]],
850
+ und_data_path: str,
851
+ t2i_data_path: str,
852
+ ) -> Dict[str, Any]:
853
+ """Build separate UND + T2I datasets and merge them (DS_SmartEdit_train Merge_Dataset pattern)."""
854
+ und_train_dataset = SupervisedDataset(
855
+ data_path=und_data_path,
856
+ processor=processor,
857
+ data_args=data_args,
858
+ model_id=model_id,
859
+ anchor_model_id=anchor_model_id,
860
+ )
861
+ t2i_train_dataset = SupervisedT2IDataset(
862
+ data_path=t2i_data_path,
863
+ processor=processor,
864
+ data_args=data_args,
865
+ model_id=model_id,
866
+ anchor_model_id=anchor_model_id,
867
+ )
868
+ und_w = int(getattr(data_args, "mix_num_und_per_device", 1) or 1)
869
+ t2i_w = int(getattr(data_args, "mix_num_t2i_per_device", 3) or 3)
870
+ merged_train_dataset = Merge_UndT2IDataset(
871
+ und_train_dataset, t2i_train_dataset, und_weight=und_w, t2i_weight=t2i_w
872
+ )
873
+ rank0_print(merged_train_dataset)
874
+ data_collator = DataCollatorForSupervisedDataset(tokenizer=processor.tokenizer)
875
+ return dict(train_dataset=merged_train_dataset, eval_dataset=None, data_collator=data_collator)
876
+
877
+
878
+ def make_supervised_data_module(
879
+ model_id: str,
880
+ processor: transformers.ProcessorMixin,
881
+ data_args: DataArguments,
882
+ anchor_model_id: Optional[List[str]],
883
+ ) -> Dict[str, Any]:
884
+ # TUNABLE: T2I-only 时用 t2i_data_path,否则回退 data_path
885
+ t2i_path = getattr(data_args, "t2i_data_path", None) or data_args.data_path
886
+ train_dataset = SupervisedT2IDataset(
887
+ data_path=t2i_path,
888
+ processor=processor,
889
+ data_args=data_args,
890
+ model_id=model_id,
891
+ anchor_model_id=anchor_model_id,
892
+ )
893
+ data_collator = DataCollatorForSupervisedDataset(tokenizer=processor.tokenizer)
894
+ return dict(train_dataset=train_dataset, eval_dataset=None, data_collator=data_collator)
code/umm/runtime/models/blip3o/data/t2i_covt_data.py ADDED
@@ -0,0 +1,742 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import os
3
+ import io
4
+ import copy
5
+ from dataclasses import dataclass, field
6
+ import json
7
+ import logging
8
+ import pathlib
9
+ from typing import Dict, Optional, Sequence, List, Tuple
10
+
11
+ import time
12
+ import torch, gc
13
+ import glob
14
+ import torch.nn as nn
15
+ import transformers
16
+ import tokenizers
17
+ import random
18
+ from torch.utils.data import Dataset
19
+
20
+ import PIL
21
+ from PIL import ImageFile
22
+ from datasets import load_dataset, concatenate_datasets,load_from_disk
23
+
24
+ from pathlib import Path
25
+ from datasets.utils.logging import set_verbosity_info
26
+ from transformers import logging as tf_logging
27
+ import torchvision.transforms as T
28
+ from torchvision.transforms import v2
29
+ from torchvision.transforms.functional import InterpolationMode
30
+ from transformers import AutoProcessor
31
+ from packaging import version
32
+ from datasets import Image
33
+ from models.blip3o.constants import *
34
+ from models.blip3o.params import DataArguments
35
+
36
+ from .covt_data import SupervisedDataset
37
+ # Set global configurations
38
+ ImageFile.LOAD_TRUNCATED_IMAGES = True
39
+ # Image preprocessing for underspecified (contextual) images
40
+ transform_und_images = T.Compose([
41
+ T.Resize(672, interpolation=InterpolationMode.BICUBIC, antialias=True),
42
+ T.CenterCrop(672) # Target size for a fixed number of tokens
43
+ ])
44
+
45
+
46
+ ## target transform for sana
47
+ target_transform = T.Compose(
48
+ [
49
+ v2.Resize(512),
50
+ v2.CenterCrop(512),
51
+ v2.ToImage(),
52
+ v2.ToDtype(torch.float32, scale=True),
53
+ v2.Normalize([0.5], [0.5]),
54
+ ]
55
+ )
56
+ set_verbosity_info()
57
+ tf_logging.set_verbosity_info()
58
+ local_rank = None
59
+
60
+
61
+ # Check tokenizer version for compatibility
62
+ IS_TOKENIZER_GREATER_THAN_0_14 = version.parse(tokenizers.__version__) >= version.parse("0.14")
63
+ def rank0_print(*args):
64
+ """Prints only on the main process (rank 0)."""
65
+ if local_rank == 0:
66
+ print(*args)
67
+
68
+
69
+
70
+ def get_comt_data_in_response(response, anchor_nums, anchor_tokens, anchor_names):
71
+ if len(anchor_nums) == 0:
72
+ return response
73
+
74
+ anchor_pads = []
75
+ for anchor_num, anchor_token in zip(anchor_nums, anchor_tokens):
76
+ anchor_pad = ANCHOR_START_TOKEN + anchor_token * anchor_num + ANCHOR_END_TOKEN
77
+ anchor_pads.append(anchor_pad)
78
+ CoT_start = "<think> Because "
79
+ if len(anchor_names) == 1:
80
+ CoT_start += f"the {anchor_names[0]} of the image is {anchor_pads[0]}. "
81
+ else:
82
+ for anchor_name, anchor_pad in zip(anchor_names, anchor_pads):
83
+ CoT_start += f"the {anchor_name} of the image is {anchor_pad}"
84
+ if anchor_name == anchor_names[-2]:
85
+ CoT_start += ", and "
86
+ elif anchor_name == anchor_names[-1]:
87
+ CoT_start += ". "
88
+ else:
89
+ CoT_start += ", "
90
+ response = CoT_start + " </think>\n" + "<answer> " + response + " </answer>"
91
+ return response
92
+
93
+ def apply_template(tokenizer, conv):
94
+ out = tokenizer.apply_chat_template(
95
+ conv,
96
+ tokenize=True,
97
+ return_tensors=None
98
+ )
99
+
100
+ return out["input_ids"]
101
+
102
+
103
+ def llava_to_openai(conversations, is_video=False):
104
+ role_mapping = {"human": "user", "gpt": "assistant"}
105
+
106
+ # import ipdb; ipdb.set_trace()
107
+ transformed_data = []
108
+ conversations = conversations[0]
109
+ for conversation in conversations:
110
+ transformed_content = conversation["value"]
111
+ transformed_entry = {
112
+ "role": role_mapping.get(conversation["from"], conversation["from"]),
113
+ "content": transformed_content,
114
+ }
115
+ transformed_data.append(transformed_entry)
116
+
117
+ return transformed_data
118
+
119
+ def preprocess_multimodal(sources, data_args):
120
+ """
121
+ Replaces generic image placeholder tokens with model-specific query tokens
122
+ (T2I, I2I, or UND_IMAGE) in the conversation structure.
123
+
124
+ Args:
125
+ sources (Sequence[List[Dict]]): A sequence of conversations, where each conversation
126
+ is a list of message dictionaries.
127
+ data_args (DataArguments): Data arguments containing token length (n_und_query).
128
+ Returns:
129
+ Tuple[Sequence[List[Dict]], Optional[str]]:
130
+ - sources: The modified conversation structure.
131
+ - inst_type: The type of image task ('und' for context, 'gen' for generation),
132
+ or None if no image token is found.
133
+ """
134
+
135
+ # Placeholder for the underspecified (contextual) image
136
+ # und_placeholder = "<|vision_start|>" + UND_IMAGE_TOKEN * data_args.n_und_query + "<|vision_end|>"
137
+ gen_placeholder = f"\n<begin_of_img>" + "".join([f"<img{i}>" for i in range(data_args.num_metaqueries)]) + "<end_of_img>"
138
+ inst_type = None
139
+ for source in sources: # Iterate through batch/instance
140
+ for sentence in source: # Iterate through conversation turns
141
+ if sentence["from"] == "gpt" and LLAVA_IMAGE_TOKEN in sentence["value"]:
142
+ # The generative token will be replaced by T2I/I2I query tokens later in data collator
143
+ sentence["value"] = sentence["value"].replace(LLAVA_IMAGE_TOKEN, gen_placeholder).strip()
144
+ inst_type = "gen"
145
+ return sources, inst_type
146
+
147
+ def preprocess_qwen(sources: Sequence[List[Dict]], tokenizer: transformers.PreTrainedTokenizer, system_message: str = "You are a helpful assistant.") -> Dict:
148
+ """
149
+ Tokenizes the conversation data according to the Qwen chat template,
150
+ creating input_ids and attention mask/labels.
151
+ Args:
152
+ sources (Sequence[List[Dict]]): A sequence of preprocessed conversations.
153
+ tokenizer (transformers.PreTrainedTokenizer): The tokenizer instance.
154
+ has_image (bool): Flag indicating if the instance contains an image.
155
+ system_message (str): The initial system instruction.
156
+ Returns:
157
+ Dict: Dictionary containing 'input_ids' and 'labels' tensors.
158
+ """
159
+ roles = {"human": "user", "gpt": "assistant"}
160
+
161
+ # Use a deep copy to avoid modifying the global tokenizer chat template if parallel processing is used
162
+ tokenizer = copy.deepcopy(tokenizer)
163
+ chat_template = "{% for message in messages %}{{'<|im_start|>' + message['role'] + '\n' + message['content'] + '<|im_end|>' + '\n'}}{% endfor %}{% if add_generation_prompt %}{{ '<|im_start|>assistant\n' }}{% endif %}"
164
+ tokenizer.chat_template = chat_template
165
+
166
+ input_ids, targets = [], []
167
+ for i, source in enumerate(sources):
168
+ if roles[source[0]["from"]] != roles["human"]:
169
+ source = source[1:]
170
+
171
+ input_id, target = [], []
172
+
173
+ # 1. Build system message
174
+ system_conv = [{"role" : "system", "content" : system_message}]
175
+ input_id += apply_template(tokenizer,system_conv)
176
+ target += [IGNORE_INDEX] * len(input_id)
177
+
178
+ # 2. Process conversation turns
179
+ for conv in source:
180
+ try:
181
+ role = conv["role"]
182
+ content = conv["content"]
183
+ except: # Handle older format
184
+ role = conv["from"]
185
+ content = conv["value"]
186
+ role = roles.get(role, role)
187
+
188
+ conv_turn = [{"role" : role, "content" : content}]
189
+ encode_id = apply_template(tokenizer,conv_turn)
190
+ input_id += encode_id
191
+
192
+ # Mask out user/system input for loss calculation
193
+ if role in ["user", "system"]:
194
+ target += [IGNORE_INDEX] * len(encode_id)
195
+ else:
196
+ target += encode_id # Label the GPT responses
197
+
198
+ assert len(input_id) == len(target), f"Input length mismatch: {len(input_id)} != {len(target)}"
199
+
200
+ input_ids.append(input_id)
201
+ targets.append(target)
202
+
203
+ # import pdb
204
+ # pdb.set_trace()
205
+ input_ids = torch.tensor(input_ids, dtype=torch.long)
206
+ targets = torch.tensor(targets, dtype=torch.long)
207
+
208
+ # valid = (targets != -100).sum()
209
+ # print("valid tokens:", valid)
210
+
211
+ return dict(
212
+ input_ids=input_ids, # tensor(bs x seq_len)
213
+ labels=targets, # tensor(bs x seq_len)
214
+ )
215
+
216
+ def parse_txt(example: Dict) -> Dict:
217
+ """Extracts text prompt from the 'edited_prompt_list' field (for I2I data)."""
218
+ example["txt"] = example["edited_prompt_list"][-1]
219
+ return example
220
+
221
+ def parse_json(example: Dict) -> Dict:
222
+ """Extracts text prompt from the 'json' field (for T2I data)."""
223
+ if isinstance(example["json"], str):
224
+ try:
225
+ parsed = json.loads(example["json"])
226
+ example["txt"] = parsed.get("prompt", "")
227
+ except json.JSONDecodeError:
228
+ example["txt"] = ""
229
+ else:
230
+ example["txt"] = example["json"].get("prompt", "")
231
+ return example
232
+
233
+
234
+ def get_tar_files(directory):
235
+ return [
236
+ os.path.join(directory, f)
237
+ for f in os.listdir(directory)
238
+ if f.endswith(".tar")
239
+ ]
240
+
241
+ def get_t2i_data_files(data_path,num_files: int = 10) -> List[str]:
242
+ """Generate list of T2I dataset URLs."""
243
+ import random
244
+ data_files = get_tar_files(data_path)
245
+ num_files = min(46, num_files)
246
+ selected_files = random.sample(data_files, num_files)
247
+ return selected_files
248
+
249
+ # def get_edit_data_files(num_files: int = 10) -> List[str]:
250
+ # """Generate list of image editing dataset URLs."""
251
+ # num_files = min(571, num_files)
252
+ # base_url = "https://huggingface.co/datasets/TIGER-Lab/OmniEdit-Filtered-1.2M/resolve/main/data/train-{:05d}-of-00571.parquet"
253
+ # return [base_url.format(i) for i in range(num_files)] # 00000 to 00570
254
+
255
+ # --------------------------------------------------------------------------
256
+ # --- Dataset and Collation ---
257
+ # --------------------------------------------------------------------------
258
+
259
+ def get_magicbrush_data_files(data_path):
260
+ train_dataset = load_from_disk(data_path)
261
+ train_dataset = train_dataset.rename_column("source_img", "ref_image")
262
+ train_dataset = train_dataset.rename_column("target_img","image")
263
+ train_dataset = train_dataset.rename_column("instruction","txt")
264
+ train_dataset = train_dataset.remove_columns(
265
+ [
266
+ col
267
+ for col in train_dataset.column_names
268
+ if not col in (["ref_image", "image","txt"])
269
+ ]
270
+ )
271
+ return train_dataset
272
+
273
+ def get_omni_data_files(data_path):
274
+ train_dataset = load_dataset(
275
+ "json",
276
+ data_files=data_path,
277
+ split="train"
278
+ )
279
+ train_dataset = train_dataset.rename_column("source_image", "ref_image")
280
+ train_dataset = train_dataset.rename_column("target_image","image")
281
+ train_dataset = train_dataset.rename_column("caption","txt")
282
+ train_dataset = train_dataset.remove_columns(
283
+ [
284
+ col
285
+ for col in train_dataset.column_names
286
+ if not col in (["ref_image", "image","txt"])
287
+ ]
288
+ )
289
+
290
+ return train_dataset
291
+
292
+ def get_t21_data_files(data_path):
293
+ import time
294
+ import fcntl
295
+ import errno
296
+
297
+ rank = int(os.environ.get("RANK", os.environ.get("LOCAL_RANK", 0)))
298
+
299
+ # Use reuse_dataset_if_exists to avoid re-downloading and lock conflicts
300
+ # Add retry mechanism
301
+ max_retries = 5
302
+ retry_delay = 2
303
+
304
+ for attempt in range(max_retries):
305
+ try:
306
+ if rank == 0 or attempt == max_retries - 1:
307
+ print(f"[Rank {rank}] Try loading the dataset (trying {attempt + 1}/{max_retries})...")
308
+ train_dataset = load_dataset(
309
+ "json",
310
+ data_files=data_path,
311
+ split="train",
312
+ download_mode="reuse_dataset_if_exists", # Force the use of local caching.
313
+ )
314
+ if rank == 0:
315
+ print(f"[Rank {rank}] Dataset loaded successfully!")
316
+ break
317
+ except PermissionError as e:
318
+ if attempt < max_retries - 1:
319
+ print(f"[Rank {rank}] File conflict, will retry in {retry_delay} seconds... (trying {attempt + 1}/{max_retries})")
320
+ time.sleep(retry_delay)
321
+ retry_delay *= 2 # Exponential Backoff
322
+ else:
323
+ raise e
324
+ except Exception as e:
325
+ print(f"[Rank {rank}] An error occurred while loading the dataset.: {e}")
326
+ raise e
327
+
328
+ train_dataset = train_dataset.rename_column("image_path","image")
329
+ train_dataset = train_dataset.rename_column("prompt","txt")
330
+ train_dataset = train_dataset.remove_columns(
331
+ [
332
+ col
333
+ for col in train_dataset.column_names
334
+ if not col in (["image","txt"])
335
+ ]
336
+ )
337
+
338
+ return train_dataset
339
+
340
+ def force_align_image_schema(ds):
341
+ STD_IMAGE = Image(decode=True)
342
+ # image
343
+ if "image" in ds.features:
344
+ ds = ds.cast_column("image", STD_IMAGE)
345
+
346
+ # ref_image
347
+ if "ref_image" in ds.features:
348
+ ds = ds.cast_column("ref_image", STD_IMAGE)
349
+ else:
350
+ ds = ds.map(lambda x: {**x, "ref_image": None})
351
+ ds = ds.cast_column("ref_image", STD_IMAGE)
352
+ return ds
353
+
354
+
355
+ class LazySupervisedMixDataset(SupervisedDataset):
356
+ """
357
+ Dataset for supervised fine-tuning, loading and mixing I2I and T2I datasets
358
+ from Parquet and WebDataset formats.
359
+ """
360
+
361
+ def __init__(
362
+ self,
363
+ data_path: str, # Unused but kept for signature consistency
364
+ processor: transformers.ProcessorMixin,
365
+ data_args: DataArguments,
366
+ model_id,
367
+ padding=True,
368
+ shuffle=True,
369
+ random_seed=42,
370
+ anchor_model_id=None,
371
+ ):
372
+ super(LazySupervisedMixDataset, self).__init__(
373
+ data_path=[],
374
+ processor=processor,
375
+ data_args=data_args,
376
+ model_id=model_id,
377
+ padding=padding,
378
+ shuffle=False,
379
+ random_seed=random_seed,
380
+ anchor_model_id=anchor_model_id,
381
+ )
382
+
383
+ list_data_dict = []
384
+
385
+ t2i_dataset_sa = get_t21_data_files(data_path)
386
+ t2i_dataset_sa = (t2i_dataset_sa.add_column("type",['T2I']*len(t2i_dataset_sa))
387
+ .select_columns(["image", "txt", "type"]))
388
+ rank0_print(f"Finished loading I2I dataset with {len(t2i_dataset_sa)} samples")
389
+
390
+ t2i_dataset_sa = force_align_image_schema(t2i_dataset_sa)
391
+
392
+ list_data_dict.append(t2i_dataset_sa)
393
+ # list_data_dict.append(t2i_dataset_web1)
394
+
395
+ # 3. Concatenate and Shuffle
396
+ if len(list_data_dict) > 1:
397
+ list_data_dict = concatenate_datasets(list_data_dict)
398
+ else:
399
+ list_data_dict = list_data_dict[0]
400
+ list_data_dict = list_data_dict.shuffle(seed=42)
401
+
402
+ rank0_print(f"Totoal number of training instance: {len(list_data_dict)}")
403
+ self.list_data_dict = list_data_dict
404
+
405
+ def __len__(self):
406
+ return len(self.list_data_dict)
407
+
408
+ def __getitem__(self, i) -> Dict[str, torch.Tensor]:
409
+ """
410
+ Retrieves a single data instance, constructs the conversation,
411
+ and applies preprocessing.
412
+ """
413
+ is_video = False
414
+ processor = self.processor
415
+
416
+ while True: # Loop to handle data loading errors
417
+ sources = self.list_data_dict[i]
418
+ task_type = sources["type"]
419
+
420
+ if task_type == "T2I":
421
+ #T2I: Human asks for generation, GPT replies with <image>
422
+ sources["conversations"] = [
423
+ {"from": "human", "value": f"Please generate image based on the following caption: {sources['txt']}"},
424
+ {"from": "gpt", "value": LLAVA_IMAGE_TOKEN}, # DEFAULT_IMAGE_TOKEN will be replaced by empty string in preprocess_multimodal
425
+ ]
426
+ image_files = sources["image"]
427
+ ref_image_files = None
428
+
429
+ elif task_type == "I2I":
430
+ # I2I: Human provides image (<image>) and instruction, GPT replies with generation (<image>)
431
+ sources["conversations"] = [
432
+ {
433
+ "from": "human",
434
+ "value": f"{LLAVA_IMAGE_TOKEN}\nPlease edit the given image based on the folllowing instruction: {sources['txt']}.",
435
+ },
436
+ {"from": "gpt", "value": LLAVA_IMAGE_TOKEN},
437
+ ]
438
+ image_files = sources["image"]
439
+ ref_image_files = sources["ref_image"]
440
+ else:
441
+ raise ValueError("Unknown source type. Please check the 'type' in 'sources'.")
442
+
443
+ # 2. Load images (handles bytes/PIL images from dataset)
444
+ if not isinstance(image_files, list):
445
+ image_files = [image_files]
446
+ if not isinstance(ref_image_files, list) and ref_image_files is not None:
447
+ ref_image_files = [ref_image_files]
448
+ images, ref_images = [], []
449
+
450
+ # Load target image(s)
451
+ if task_type == "T2I" or task_type == "I2I":
452
+ for img in image_files:
453
+ try:
454
+ # Ensure image is in RGB format
455
+ if isinstance(img, bytes): img = Image.open(io.BytesIO(img))
456
+ img = img.convert("RGB")
457
+ images.append(img)
458
+ except Exception as e:
459
+ print(f"Error opening image: {e}. Skipping sample.")
460
+ images = None; break
461
+
462
+ # Load reference image(s) for I2I
463
+ if task_type == "I2I" and images is not None:
464
+ for img in ref_image_files:
465
+ try:
466
+ if isinstance(img, bytes): img = Image.open(io.BytesIO(img))
467
+ img = img.convert("RGB")
468
+ ref_images.append(img)
469
+ except Exception as e:
470
+ print(f"Error opening ref image: {e}. Skipping sample.")
471
+ ref_images = None; break
472
+
473
+ if images is None or (task_type == "I2I" and ref_images is None):
474
+ i = random.randint(0, len(self.list_data_dict) - 1)
475
+ continue # Skip to the next random index if image loading failed
476
+
477
+ # 3. Apply preprocessing to conversation and tokenize
478
+ sources = copy.deepcopy([sources["conversations"]])
479
+ sources, inst_type = preprocess_multimodal(sources, self.data_args)
480
+ sources = copy.deepcopy(llava_to_openai(sources))
481
+
482
+ all_input_ids = []
483
+ all_mm_token_type_ids = []
484
+ all_labels = []
485
+ all_pixel_values = []
486
+ all_image_grid_thw = []
487
+ all_second_gird = []
488
+
489
+ roles = {"human": "user", "gpt": "assistant"}
490
+ # all_dino_encoded_values = []
491
+
492
+ # Qwen2-VL uses a default system message so I've added this.
493
+ if len(SYSTEM_MESSAGE) > 0:
494
+ system_message = f"{DEFAULT_IM_START_TOKEN}system\n{SYSTEM_MESSAGE}\n{DEFAULT_IM_END_TOKEN}\n"
495
+ system_message_input_ids = processor.tokenizer(system_message, add_special_tokens=False, return_tensors='pt')['input_ids']
496
+ system_labels = torch.full_like(system_message_input_ids, IGNORE_INDEX)
497
+
498
+ all_input_ids.append(system_message_input_ids.squeeze(0))
499
+ all_mm_token_type_ids.append(
500
+ torch.zeros(system_message_input_ids.shape[1], dtype=torch.long)
501
+ )
502
+ all_labels.append(system_labels.squeeze(0))
503
+
504
+ # import ipdb; ipdb.set_trace()
505
+ for _, j in enumerate(range(0, len(sources), 2)):
506
+
507
+ if j >= 2:
508
+ break
509
+
510
+ user_input = sources[j]
511
+ gpt_response = sources[j + 1]
512
+
513
+ if self.stage_1_step <= self.cur_step < self.stage_2_step:
514
+ user_input = f"{DEFAULT_IM_START_TOKEN}{user_input['role']}\n{user_input['content']}\n{DEFAULT_IM_END_TOKEN}\n{DEFAULT_IM_START_TOKEN}{gpt_response['role']}\n"
515
+ gpt_response = f"{gpt_response['content']}"
516
+ if DEFAULT_IMAGE_TOKEN in user_input:
517
+ gpt_response = get_comt_data_in_response(gpt_response, self.anchor_token_nums, self.anchor_tokens, self.anchor_task_names)
518
+ gpt_response = f"{gpt_response}\n{DEFAULT_IM_END_TOKEN}\n"
519
+ # print(f"\033[92m gpt_response: {gpt_response}\033[0m")
520
+ else:
521
+ # user_input = f"{DEFAULT_IM_START_TOKEN}{user_input['role']}\n{user_input['content']}\n{DEFAULT_IM_END_TOKEN}\n{DEFAULT_IM_START_TOKEN}{gpt_response['role']}\n"
522
+ # gpt_response = f"{gpt_response['content']}\n{DEFAULT_IM_END_TOKEN}\n"
523
+ # gpt_response = replace_pad_with_anchor_tokens(gpt_response)
524
+ import random
525
+ xxx = random.randint(0, 5)
526
+ if xxx == 0:
527
+ user_input = f"{DEFAULT_IM_START_TOKEN}{user_input['role']}\n{user_input['content']}\n{DEFAULT_IM_END_TOKEN}\n{DEFAULT_IM_START_TOKEN}{gpt_response['role']}\n"
528
+ gpt_response = f"{gpt_response['content']}\n{DEFAULT_IM_END_TOKEN}\n"
529
+ else:
530
+ user_input = f"{DEFAULT_IM_START_TOKEN}{user_input['role']}\n{user_input['content']}\n{DEFAULT_IM_END_TOKEN}\n{DEFAULT_IM_START_TOKEN}{gpt_response['role']}\n"
531
+ gpt_response = f"{gpt_response['content']}"
532
+ if DEFAULT_IMAGE_TOKEN in user_input:
533
+ # INSERT_YOUR_CODE
534
+ total = len(self.anchor_tokens)
535
+ if total == 0:
536
+ selected_anchor_token_nums = []
537
+ selected_anchor_tokens = []
538
+ selected_anchor_task_names = []
539
+ else:
540
+ x = random.randint(1, total)
541
+ idxs = sorted(random.sample(range(total), x)) if x > 0 else []
542
+ selected_anchor_token_nums = [self.anchor_token_nums[i] for i in idxs]
543
+ selected_anchor_tokens = [self.anchor_tokens[i] for i in idxs]
544
+ selected_anchor_task_names = [self.anchor_task_names[i] for i in idxs]
545
+ gpt_response = get_comt_data_in_response(gpt_response, selected_anchor_token_nums, selected_anchor_tokens, selected_anchor_task_names)
546
+ gpt_response = f"{gpt_response}\n{DEFAULT_IM_END_TOKEN}\n"
547
+
548
+ prompt_input_ids = processor.tokenizer(user_input, add_special_tokens=False, padding=False, return_tensors='pt')['input_ids']
549
+ all_mm_token_type_ids.append(
550
+ torch.zeros(prompt_input_ids.shape[1], dtype=torch.long)
551
+ )
552
+
553
+ response_input_ids = processor.tokenizer(gpt_response, add_special_tokens=False, padding=False, return_tensors='pt')['input_ids']
554
+ all_mm_token_type_ids.append(
555
+ torch.zeros(response_input_ids.shape[1], dtype=torch.long)
556
+ )
557
+
558
+ input_ids = torch.cat([prompt_input_ids, response_input_ids], dim=1).squeeze(0)
559
+ labels = torch.cat(
560
+ [
561
+ torch.tensor([IGNORE_INDEX] * len(prompt_input_ids[0])),
562
+ response_input_ids.squeeze(0),
563
+ ],
564
+ dim=0,
565
+ )
566
+
567
+ all_input_ids.append(input_ids)
568
+ all_labels.append(labels)
569
+
570
+ input_ids = torch.cat(all_input_ids, dim=0).to(torch.long)
571
+ labels = torch.cat(all_labels, dim=0).to(torch.long)
572
+ mm_token_type_ids = torch.cat(all_mm_token_type_ids, dim=0).to(torch.long)
573
+
574
+ if mm_token_type_ids.shape[0] != input_ids.shape[0]:
575
+ raise ValueError(
576
+ f"mm_token_type_ids length {mm_token_type_ids.shape[0]} != input_ids {input_ids.shape[0]}"
577
+ )
578
+
579
+ # eos_token_id = processor.tokenizer.convert_tokens_to_ids(DEFAULT_IM_END_TOKEN)
580
+ # input_ids, labels = truncate_sequence(input_ids, labels, self.max_length, eos_token_id)
581
+
582
+ attention_mask = (input_ids > -1000000).to(torch.long)
583
+
584
+ data_dict = dict(
585
+ input_ids=input_ids,
586
+ attention_mask=attention_mask,
587
+ labels=labels,
588
+ mm_token_type_ids=mm_token_type_ids,
589
+ image_files=image_files,
590
+ task_types=task_type
591
+ )
592
+ # 4. Process images using VAE/Vision Tower processors
593
+ if inst_type == "gen": # T2I or I2I (where the LLM output involves generation)
594
+ # Target image for VAE (latents for diffusion)
595
+ images = [img.resize((self.data_args.image_size, self.data_args.image_size),
596
+ resample=PIL.Image.Resampling.BICUBIC) for img in images]
597
+ data_dict["gen_image"] = self.data_args.gen_image_processor.preprocess(images, return_tensors="pt")["pixel_values"]
598
+
599
+ # I2I reference image (for diffusion conditioning)
600
+ if task_type == "I2I":
601
+ data_dict["gen_ref_image"] = self.data_args.gen_image_processor.preprocess(ref_images, return_tensors="pt")["pixel_values"]
602
+ # ref_mask is 1 for I2I
603
+ b = data_dict["gen_ref_image"].shape[0]
604
+ data_dict["ref_mask"] = torch.ones((b, 1, 1, 1), dtype=data_dict["gen_image"].dtype, device=data_dict["gen_image"].device)
605
+ # else: # T2I uses a masked/zeroed reference
606
+ # data_dict["gen_ref_image"] = torch.zeros_like(data_dict["gen_image"])
607
+ # b = data_dict["gen_ref_image"].shape[0]
608
+ # # ref_mask is 0 for T2I (no reference image conditioning)
609
+ # data_dict["ref_mask"] = torch.zeros((b, 1, 1, 1), dtype=data_dict["gen_image"].dtype, device=data_dict["gen_image"].device)
610
+
611
+ if task_type == "I2I": # I2I (where the LLM input involves image grounding)
612
+ # Underspecified image (resized for LLM context tokens)
613
+ resized_images = [transform_und_images(img) for img in ref_images]
614
+ image_inputs = self.data_args.image_processor(resized_images, return_tensors="pt")
615
+ data_dict["und_image"] = image_inputs.pixel_values
616
+ data_dict["grid_thw"] = image_inputs.image_grid_thw # Grid info for the visual encoder
617
+
618
+ self.cur_step += 1
619
+ return data_dict
620
+
621
+ @dataclass
622
+ class DataCollatorForSupervisedDataset(object):
623
+ """
624
+ Collate examples for supervised fine-tuning, primarily handling padding
625
+ and appending the T2I/I2I query tokens to the sequence.
626
+ """
627
+ tokenizer: transformers.PreTrainedTokenizer
628
+
629
+ def __call__(self, instances: Sequence[Dict]) -> Dict[str, torch.Tensor]:
630
+ input_ids, labels, ids, task_types = tuple([instance[key] for instance in instances] for key in ("input_ids", "labels", "ids", "task_types"))
631
+ multi_input_ids = []
632
+ multi_labels = []
633
+ i_s_pos = []
634
+
635
+ # 1. Truncate and Append Image Query Tokens
636
+ for input_id, label, task_type in zip(input_ids, labels, task_types):
637
+ # Truncate to make space for the image query tokens (n_query)
638
+ # The final length will be truncated to model_max_length in step 2.
639
+ input_id = input_id[: self.tokenizer.model_max_length]
640
+ label = label[: self.tokenizer.model_max_length]
641
+
642
+ # i_s_pos records the starting index of the appended image query tokens
643
+ i_s_pos.append(input_id.shape[0])
644
+
645
+ # # Create the sequence of image query tokens
646
+ # img_id = torch.full((self.n_query,), T2I_TOKEN_IDX, dtype=input_id.dtype, device=input_id.device) if task_type == "T2I" else torch.full((self.n_query,), I2I_TOKEN_IDX, dtype=input_id.dtype, device=input_id.device)
647
+
648
+ # # Append tokens to input_id and label
649
+ # input_id = torch.cat([input_id, img_id])
650
+ # img_label = torch.full((self.n_query,), T2I_TOKEN_IDX, dtype=label.dtype, device=label.device) if task_type == "T2I" else torch.full((self.n_query,), I2I_TOKEN_IDX, dtype=label.dtype, device=label.device)
651
+ # label = torch.cat([label, img_label])
652
+
653
+ multi_input_ids.append(input_id)
654
+ multi_labels.append(label)
655
+
656
+ input_ids = multi_input_ids
657
+ labels = multi_labels
658
+
659
+ # 2. Pad Sequences
660
+ input_ids = torch.nn.utils.rnn.pad_sequence(input_ids, batch_first=True, padding_value=self.tokenizer.pad_token_id)
661
+ labels = torch.nn.utils.rnn.pad_sequence(labels, batch_first=True, padding_value=IGNORE_INDEX)
662
+
663
+ # Final truncation to max length
664
+ if input_ids.shape[1] > self.tokenizer.model_max_length:
665
+ rank0_print(f"Warning: input with length {input_ids.shape[1]} is longer than max length {self.tokenizer.model_max_length}. Truncating.")
666
+ input_ids = input_ids[:, : self.tokenizer.model_max_length]
667
+ labels = labels[:, : self.tokenizer.model_max_length]
668
+
669
+ batch = dict(
670
+ input_ids=input_ids,
671
+ labels=labels,
672
+ # Attention mask ignores the padding tokens
673
+ attention_mask=input_ids.ne(self.tokenizer.pad_token_id),
674
+ )
675
+ # 3. Collate Image Data (VAE latents and contextual images)
676
+ batch_gen_images = []
677
+ batch_ref_gen_images = []
678
+ batch_und_images = []
679
+ batch_grid_thw = []
680
+ batch_ref_mask = []
681
+ # Collate gen_image (target VAE latents)
682
+ for instance in instances:
683
+ if "gen_image" in instance:
684
+ batch_gen_images.append(instance["gen_image"])
685
+
686
+ if len(batch_gen_images) > 0:
687
+ # Check for consistent shape and concatenate
688
+ if all(x is not None and y.shape == batch_gen_images[0][0].shape for x in batch_gen_images for y in x):
689
+ batch["gen_image"] = torch.cat([images for images in batch_gen_images], dim=0)
690
+ else:
691
+ batch["gen_image"] = batch_gen_images # Keep as list if shapes vary (unlikely for VAE)
692
+
693
+ # Collate gen_ref_image (reference VAE latents) and ref_mask
694
+ for instance in instances:
695
+ if "gen_ref_image" in instance:
696
+ batch_ref_gen_images.append(instance["gen_ref_image"])
697
+ if "ref_mask" in instance:
698
+ batch_ref_mask.append(instance["ref_mask"])
699
+
700
+ if len(batch_ref_gen_images) > 0:
701
+ if all(x is not None and y.shape == batch_ref_gen_images[0][0].shape for x in batch_ref_gen_images for y in x):
702
+ batch["gen_ref_image"] = torch.cat([images for images in batch_ref_gen_images], dim=0)
703
+ else:
704
+ batch["gen_ref_image"] = batch_ref_gen_images
705
+ if len(batch_ref_mask) > 0:
706
+ batch["ref_mask"] = torch.cat([images for images in batch_ref_mask], dim=0)
707
+
708
+ # Collate und_image (contextual image pixels) and grid_thw
709
+ for instance in instances:
710
+ if "und_image" in instance:
711
+ batch_und_images.append(instance["und_image"].unsqueeze(0))
712
+ batch_grid_thw.append(instance["grid_thw"])
713
+
714
+ if len(batch_und_images) > 0:
715
+ batch["und_image"] = torch.cat([images for images in batch_und_images], dim=0)
716
+ batch["grid_thw"] = torch.cat([images for images in batch_grid_thw], dim=0)
717
+ else:
718
+ batch["und_image"] = None
719
+ batch["grid_thw"] = None
720
+
721
+ # Final metadata
722
+ batch["ids"] = ids
723
+ batch["i_s_pos"] = i_s_pos # Start positions of the image query tokens
724
+ return batch
725
+
726
+ def make_supervised_data_module(tokenizer: transformers.PreTrainedTokenizer, data_args: DataArguments) -> Dict:
727
+ """
728
+ Creates the training dataset and data collator based on data arguments.
729
+ Args:
730
+ tokenizer (transformers.PreTrainedTokenizer): The model tokenizer.
731
+ data_args (DataArguments): Configuration for data loading.
732
+ Returns:
733
+ Dict: Dictionary containing 'train_dataset', 'eval_dataset', and 'data_collator'.
734
+ """
735
+
736
+ if data_args.data_type == "mix":
737
+ train_dataset = LazySupervisedMixDataset(tokenizer=tokenizer, data_path=data_args.data_path, data_args=data_args)
738
+ else:
739
+ raise ValueError(f"Unknown data type: {data_args.data_type}.")
740
+
741
+ data_collator = DataCollatorForSupervisedDataset(tokenizer=tokenizer)
742
+ return dict(train_dataset=train_dataset, eval_dataset=None, data_collator=data_collator)
code/umm/runtime/models/blip3o/model/__init__.py ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from .language_model.blip3o_qwen import blip3oQwenForCausalLM, blip3oQwenConfig
2
+ from .language_model.blip3o_qwen_inference import blip3oQwenForInferenceLM, blip3oQwenConfig
3
+ from .language_model.fm_step_prob import sde_step_with_logprob
4
+ from .language_model.blip3o_qwen_kontext import blip3oQwenKontextForCausalLM,blip3oQwenConfig
5
+ from .language_model.blip3o_qwen_kontext_inference import blip3oQwenKontextForInferenceLM,blip3oQwenConfig
6
+ from .language_model.blip3o_qwen_stage1 import blip3oAlignQwenForCausalLM,blip3oAlignQwenConfig
7
+ from .language_model.blip3o_qwen_align_infer import blip3oAlignQwenForInferenceLM,blip3oAlignQwenConfig
8
+ from .language_model.covt_qwen_stage2_inference import CoVTForConditionalGenerationInference,blip3oCOVTQwenConfig
9
+ from .language_model.omni_und_gen import OmniGenUndForConditionalGeneration, QwenOmniGenUndConfig
10
+ from .language_model.omni_und_gen_infer import OmniForConditionalInference,QwenOmniConfig
11
+
12
+ # Stage-2 CoVT: lazy so `import models.blip3o.model.language_model.moe_qwen_stage3_flux` does not
13
+ # execute covt_qwen_stage2.py (Stage-3 Flux uses moe_qwen_stage3_flux instead).
14
+ def __getattr__(name: str):
15
+ if name == "CoVTForConditionalGeneration":
16
+ from .language_model.covt_qwen_stage2 import CoVTForConditionalGeneration
17
+
18
+ return CoVTForConditionalGeneration
19
+ if name == "blip3oCOVTQwenConfig":
20
+ from .language_model.covt_qwen_stage2 import blip3oCOVTQwenConfig
21
+
22
+ return blip3oCOVTQwenConfig
23
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
24
+
25
+
26
+ __all__ = [
27
+ "blip3oQwenForCausalLM",
28
+ "blip3oQwenConfig",
29
+ "blip3oQwenForInferenceLM",
30
+ "sde_step_with_logprob",
31
+ "blip3oQwenKontextForCausalLM",
32
+ "blip3oQwenKontextForInferenceLM",
33
+ "blip3oAlignQwenForCausalLM",
34
+ "blip3oAlignQwenConfig",
35
+ "blip3oAlignQwenForInferenceLM",
36
+ "CoVTForConditionalGeneration",
37
+ "blip3oCOVTQwenConfig",
38
+ "OmniGenUndForConditionalGeneration",
39
+ "QwenOmniGenUndConfig",
40
+ "OmniForConditionalInference",
41
+ "QwenOmniConfig",
42
+ ]
code/umm/runtime/models/blip3o/model/anchors/DepthAnything/DA-2K.md ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # DA-2K Evaluation Benchmark
2
+
3
+ ## Introduction
4
+
5
+ ![DA-2K](assets/DA-2K.png)
6
+
7
+ DA-2K is proposed in [Depth Anything V2](https://depth-anything-v2.github.io) to evaluate the relative depth estimation capability. It encompasses eight representative scenarios of `indoor`, `outdoor`, `non_real`, `transparent_reflective`, `adverse_style`, `aerial`, `underwater`, and `object`. It consists of 1K diverse high-quality images and 2K precise pair-wise relative depth annotations.
8
+
9
+ Please refer to our [paper](https://arxiv.org/abs/2406.09414) for details in constructing this benchmark.
10
+
11
+
12
+ ## Usage
13
+
14
+ Please first [download the benchmark](https://huggingface.co/datasets/depth-anything/DA-2K/tree/main).
15
+
16
+ All annotations are stored in `annotations.json`. The annotation file is a JSON object where each key is the path to an image file, and the value is a list of annotations associated with that image. Each annotation describes two points and identifies which point is closer to the camera. The structure is detailed below:
17
+
18
+ ```
19
+ {
20
+ "image_path": [
21
+ {
22
+ "point1": [h1, w1], # (vertical position, horizontal position)
23
+ "point2": [h2, w2], # (vertical position, horizontal position)
24
+ "closer_point": "point1" # we always set "point1" as the closer one
25
+ },
26
+ ...
27
+ ],
28
+ ...
29
+ }
30
+ ```
31
+
32
+ To visualize the annotations:
33
+ ```bash
34
+ python visualize.py [--scene-type <type>]
35
+ ```
36
+
37
+ **Options**
38
+ - `--scene-type <type>` (optional): Specify the scene type (`indoor`, `outdoor`, `non_real`, `transparent_reflective`, `adverse_style`, `aerial`, `underwater`, and `object`). Skip this argument or set <type> as `""` to include all scene types.
39
+
40
+ ## Citation
41
+
42
+ If you find this benchmark useful, please consider citing:
43
+
44
+ ```bibtex
45
+ @article{depth_anything_v2,
46
+ title={Depth Anything V2},
47
+ author={Yang, Lihe and Kang, Bingyi and Huang, Zilong and Zhao, Zhen and Xu, Xiaogang and Feng, Jiashi and Zhao, Hengshuang},
48
+ journal={arXiv:2406.09414},
49
+ year={2024}
50
+ }
51
+ ```
code/umm/runtime/models/blip3o/model/anchors/DepthAnything/README.md ADDED
@@ -0,0 +1,201 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div align="center">
2
+ <h1>Depth Anything V2</h1>
3
+
4
+ [**Lihe Yang**](https://liheyoung.github.io/)<sup>1</sup> · [**Bingyi Kang**](https://bingykang.github.io/)<sup>2&dagger;</sup> · [**Zilong Huang**](http://speedinghzl.github.io/)<sup>2</sup>
5
+ <br>
6
+ [**Zhen Zhao**](http://zhaozhen.me/) · [**Xiaogang Xu**](https://xiaogang00.github.io/) · [**Jiashi Feng**](https://sites.google.com/site/jshfeng/)<sup>2</sup> · [**Hengshuang Zhao**](https://hszhao.github.io/)<sup>1*</sup>
7
+
8
+ <sup>1</sup>HKU&emsp;&emsp;&emsp;<sup>2</sup>TikTok
9
+ <br>
10
+ &dagger;project lead&emsp;*corresponding author
11
+
12
+ <a href="https://arxiv.org/abs/2406.09414"><img src='https://img.shields.io/badge/arXiv-Depth Anything V2-red' alt='Paper PDF'></a>
13
+ <a href='https://depth-anything-v2.github.io'><img src='https://img.shields.io/badge/Project_Page-Depth Anything V2-green' alt='Project Page'></a>
14
+ <a href='https://huggingface.co/spaces/depth-anything/Depth-Anything-V2'><img src='https://img.shields.io/badge/%F0%9F%A4%97%20Hugging%20Face-Demo-blue'></a>
15
+ <a href='https://huggingface.co/datasets/depth-anything/DA-2K'><img src='https://img.shields.io/badge/Benchmark-DA--2K-yellow' alt='Benchmark'></a>
16
+ </div>
17
+
18
+ This work presents Depth Anything V2. It significantly outperforms [V1](https://github.com/LiheYoung/Depth-Anything) in fine-grained details and robustness. Compared with SD-based models, it enjoys faster inference speed, fewer parameters, and higher depth accuracy.
19
+
20
+ ![teaser](assets/teaser.png)
21
+
22
+
23
+ ## News
24
+ - **2025-01-22:** [Video Depth Anything](https://videodepthanything.github.io) has been released. It generates consistent depth maps for super-long videos (e.g., over 5 minutes).
25
+ - **2024-12-22:** [Prompt Depth Anything](https://promptda.github.io/) has been released. It supports 4K resolution metric depth estimation when low-res LiDAR is used to prompt the DA models.
26
+ - **2024-07-06:** Depth Anything V2 is supported in [Transformers](https://github.com/huggingface/transformers/). See the [instructions](https://huggingface.co/docs/transformers/main/en/model_doc/depth_anything_v2) for convenient usage.
27
+ - **2024-06-25:** Depth Anything is integrated into [Apple Core ML Models](https://developer.apple.com/machine-learning/models/). See the instructions ([V1](https://huggingface.co/apple/coreml-depth-anything-small), [V2](https://huggingface.co/apple/coreml-depth-anything-v2-small)) for usage.
28
+ - **2024-06-22:** We release [smaller metric depth models](https://github.com/DepthAnything/Depth-Anything-V2/tree/main/metric_depth#pre-trained-models) based on Depth-Anything-V2-Small and Base.
29
+ - **2024-06-20:** Our repository and project page are flagged by GitHub and removed from the public for 6 days. Sorry for the inconvenience.
30
+ - **2024-06-14:** Paper, project page, code, models, demo, and benchmark are all released.
31
+
32
+
33
+ ## Pre-trained Models
34
+
35
+ We provide **four models** of varying scales for robust relative depth estimation:
36
+
37
+ | Model | Params | Checkpoint |
38
+ |:-|-:|:-:|
39
+ | Depth-Anything-V2-Small | 24.8M | [Download](https://huggingface.co/depth-anything/Depth-Anything-V2-Small/resolve/main/depth_anything_v2_vits.pth?download=true) |
40
+ | Depth-Anything-V2-Base | 97.5M | [Download](https://huggingface.co/depth-anything/Depth-Anything-V2-Base/resolve/main/depth_anything_v2_vitb.pth?download=true) |
41
+ | Depth-Anything-V2-Large | 335.3M | [Download](https://huggingface.co/depth-anything/Depth-Anything-V2-Large/resolve/main/depth_anything_v2_vitl.pth?download=true) |
42
+ | Depth-Anything-V2-Giant | 1.3B | Coming soon |
43
+
44
+
45
+ ## Usage
46
+
47
+ ### Prepraration
48
+
49
+ ```bash
50
+ git clone https://github.com/DepthAnything/Depth-Anything-V2
51
+ cd Depth-Anything-V2
52
+ pip install -r requirements.txt
53
+ ```
54
+
55
+ Download the checkpoints listed [here](#pre-trained-models) and put them under the `checkpoints` directory.
56
+
57
+ ### Use our models
58
+ ```python
59
+ import cv2
60
+ import torch
61
+
62
+ from depth_anything_v2.dpt import DepthAnythingV2
63
+
64
+ DEVICE = 'cuda' if torch.cuda.is_available() else 'mps' if torch.backends.mps.is_available() else 'cpu'
65
+
66
+ model_configs = {
67
+ 'vits': {'encoder': 'vits', 'features': 64, 'out_channels': [48, 96, 192, 384]},
68
+ 'vitb': {'encoder': 'vitb', 'features': 128, 'out_channels': [96, 192, 384, 768]},
69
+ 'vitl': {'encoder': 'vitl', 'features': 256, 'out_channels': [256, 512, 1024, 1024]},
70
+ 'vitg': {'encoder': 'vitg', 'features': 384, 'out_channels': [1536, 1536, 1536, 1536]}
71
+ }
72
+
73
+ encoder = 'vitl' # or 'vits', 'vitb', 'vitg'
74
+
75
+ model = DepthAnythingV2(**model_configs[encoder])
76
+ model.load_state_dict(torch.load(f'checkpoints/depth_anything_v2_{encoder}.pth', map_location='cpu'))
77
+ model = model.to(DEVICE).eval()
78
+
79
+ raw_img = cv2.imread('your/image/path')
80
+ depth = model.infer_image(raw_img) # HxW raw depth map in numpy
81
+ ```
82
+
83
+ If you do not want to clone this repository, you can also load our models through [Transformers](https://github.com/huggingface/transformers/). Below is a simple code snippet. Please refer to the [official page](https://huggingface.co/docs/transformers/main/en/model_doc/depth_anything_v2) for more details.
84
+
85
+ - Note 1: Make sure you can connect to Hugging Face and have installed the latest Transformers.
86
+ - Note 2: Due to the [upsampling difference](https://github.com/huggingface/transformers/pull/31522#issuecomment-2184123463) between OpenCV (we used) and Pillow (HF used), predictions may differ slightly. So you are more recommended to use our models through the way introduced above.
87
+ ```python
88
+ from transformers import pipeline
89
+ from PIL import Image
90
+
91
+ pipe = pipeline(task="depth-estimation", model="depth-anything/Depth-Anything-V2-Small-hf")
92
+ image = Image.open('your/image/path')
93
+ depth = pipe(image)["depth"]
94
+ ```
95
+
96
+ ### Running script on *images*
97
+
98
+ ```bash
99
+ python run.py \
100
+ --encoder <vits | vitb | vitl | vitg> \
101
+ --img-path <path> --outdir <outdir> \
102
+ [--input-size <size>] [--pred-only] [--grayscale]
103
+ ```
104
+ Options:
105
+ - `--img-path`: You can either 1) point it to an image directory storing all interested images, 2) point it to a single image, or 3) point it to a text file storing all image paths.
106
+ - `--input-size` (optional): By default, we use input size `518` for model inference. ***You can increase the size for even more fine-grained results.***
107
+ - `--pred-only` (optional): Only save the predicted depth map, without raw image.
108
+ - `--grayscale` (optional): Save the grayscale depth map, without applying color palette.
109
+
110
+ For example:
111
+ ```bash
112
+ python run.py --encoder vitl --img-path assets/examples --outdir depth_vis
113
+ ```
114
+
115
+ ### Running script on *videos*
116
+
117
+ ```bash
118
+ python run_video.py \
119
+ --encoder <vits | vitb | vitl | vitg> \
120
+ --video-path assets/examples_video --outdir video_depth_vis \
121
+ [--input-size <size>] [--pred-only] [--grayscale]
122
+ ```
123
+
124
+ ***Our larger model has better temporal consistency on videos.***
125
+
126
+ ### Gradio demo
127
+
128
+ To use our gradio demo locally:
129
+
130
+ ```bash
131
+ python app.py
132
+ ```
133
+
134
+ You can also try our [online demo](https://huggingface.co/spaces/Depth-Anything/Depth-Anything-V2).
135
+
136
+ ***Note: Compared to V1, we have made a minor modification to the DINOv2-DPT architecture (originating from this [issue](https://github.com/LiheYoung/Depth-Anything/issues/81)).*** In V1, we *unintentionally* used features from the last four layers of DINOv2 for decoding. In V2, we use [intermediate features](https://github.com/DepthAnything/Depth-Anything-V2/blob/2cbc36a8ce2cec41d38ee51153f112e87c8e42d8/depth_anything_v2/dpt.py#L164-L169) instead. Although this modification did not improve details or accuracy, we decided to follow this common practice.
137
+
138
+
139
+ ## Fine-tuned to Metric Depth Estimation
140
+
141
+ Please refer to [metric depth estimation](./metric_depth).
142
+
143
+
144
+ ## DA-2K Evaluation Benchmark
145
+
146
+ Please refer to [DA-2K benchmark](./DA-2K.md).
147
+
148
+
149
+ ## Community Support
150
+
151
+ **We sincerely appreciate all the community support for our Depth Anything series. Thank you a lot!**
152
+
153
+ - Apple Core ML:
154
+ - https://developer.apple.com/machine-learning/models
155
+ - https://huggingface.co/apple/coreml-depth-anything-v2-small
156
+ - https://huggingface.co/apple/coreml-depth-anything-small
157
+ - Transformers:
158
+ - https://huggingface.co/docs/transformers/main/en/model_doc/depth_anything_v2
159
+ - https://huggingface.co/docs/transformers/main/en/model_doc/depth_anything
160
+ - TensorRT:
161
+ - https://github.com/spacewalk01/depth-anything-tensorrt
162
+ - https://github.com/zhujiajian98/Depth-Anythingv2-TensorRT-python
163
+ - ONNX: https://github.com/fabio-sim/Depth-Anything-ONNX
164
+ - ComfyUI: https://github.com/kijai/ComfyUI-DepthAnythingV2
165
+ - Transformers.js (real-time depth in web): https://huggingface.co/spaces/Xenova/webgpu-realtime-depth-estimation
166
+ - Android:
167
+ - https://github.com/shubham0204/Depth-Anything-Android
168
+ - https://github.com/FeiGeChuanShu/ncnn-android-depth_anything
169
+
170
+
171
+ ## Acknowledgement
172
+
173
+ We are sincerely grateful to the awesome Hugging Face team ([@Pedro Cuenca](https://huggingface.co/pcuenq), [@Niels Rogge](https://huggingface.co/nielsr), [@Merve Noyan](https://huggingface.co/merve), [@Amy Roberts](https://huggingface.co/amyeroberts), et al.) for their huge efforts in supporting our models in Transformers and Apple Core ML.
174
+
175
+ We also thank the [DINOv2](https://github.com/facebookresearch/dinov2) team for contributing such impressive models to our community.
176
+
177
+
178
+ ## LICENSE
179
+
180
+ Depth-Anything-V2-Small model is under the Apache-2.0 license. Depth-Anything-V2-Base/Large/Giant models are under the CC-BY-NC-4.0 license.
181
+
182
+
183
+ ## Citation
184
+
185
+ If you find this project useful, please consider citing:
186
+
187
+ ```bibtex
188
+ @article{depth_anything_v2,
189
+ title={Depth Anything V2},
190
+ author={Yang, Lihe and Kang, Bingyi and Huang, Zilong and Zhao, Zhen and Xu, Xiaogang and Feng, Jiashi and Zhao, Hengshuang},
191
+ journal={arXiv:2406.09414},
192
+ year={2024}
193
+ }
194
+
195
+ @inproceedings{depth_anything_v1,
196
+ title={Depth Anything: Unleashing the Power of Large-Scale Unlabeled Data},
197
+ author={Yang, Lihe and Kang, Bingyi and Huang, Zilong and Xu, Xiaogang and Feng, Jiashi and Zhao, Hengshuang},
198
+ booktitle={CVPR},
199
+ year={2024}
200
+ }
201
+ ```
code/umm/runtime/models/blip3o/model/anchors/DepthAnything/app.py ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import glob
2
+ import gradio as gr
3
+ import matplotlib
4
+ import numpy as np
5
+ from PIL import Image
6
+ import torch
7
+ import tempfile
8
+ from gradio_imageslider import ImageSlider
9
+
10
+ from depth_anything_v2.dpt import DepthAnythingV2
11
+
12
+ css = """
13
+ #img-display-container {
14
+ max-height: 100vh;
15
+ }
16
+ #img-display-input {
17
+ max-height: 80vh;
18
+ }
19
+ #img-display-output {
20
+ max-height: 80vh;
21
+ }
22
+ #download {
23
+ height: 62px;
24
+ }
25
+ """
26
+ DEVICE = 'cuda' if torch.cuda.is_available() else 'mps' if torch.backends.mps.is_available() else 'cpu'
27
+ model_configs = {
28
+ 'vits': {'encoder': 'vits', 'features': 64, 'out_channels': [48, 96, 192, 384]},
29
+ 'vitb': {'encoder': 'vitb', 'features': 128, 'out_channels': [96, 192, 384, 768]},
30
+ 'vitl': {'encoder': 'vitl', 'features': 256, 'out_channels': [256, 512, 1024, 1024]},
31
+ 'vitg': {'encoder': 'vitg', 'features': 384, 'out_channels': [1536, 1536, 1536, 1536]}
32
+ }
33
+ encoder = 'vitl'
34
+ model = DepthAnythingV2(**model_configs[encoder])
35
+ state_dict = torch.load(f'checkpoints/depth_anything_v2_{encoder}.pth', map_location="cpu")
36
+ model.load_state_dict(state_dict)
37
+ model = model.to(DEVICE).eval()
38
+
39
+ title = "# Depth Anything V2"
40
+ description = """Official demo for **Depth Anything V2**.
41
+ Please refer to our [paper](https://arxiv.org/abs/2406.09414), [project page](https://depth-anything-v2.github.io), or [github](https://github.com/DepthAnything/Depth-Anything-V2) for more details."""
42
+
43
+ def predict_depth(image):
44
+ return model.infer_image(image)
45
+
46
+ with gr.Blocks(css=css) as demo:
47
+ gr.Markdown(title)
48
+ gr.Markdown(description)
49
+ gr.Markdown("### Depth Prediction demo")
50
+
51
+ with gr.Row():
52
+ input_image = gr.Image(label="Input Image", type='numpy', elem_id='img-display-input')
53
+ depth_image_slider = ImageSlider(label="Depth Map with Slider View", elem_id='img-display-output', position=0.5)
54
+ submit = gr.Button(value="Compute Depth")
55
+ gray_depth_file = gr.File(label="Grayscale depth map", elem_id="download",)
56
+ raw_file = gr.File(label="16-bit raw output (can be considered as disparity)", elem_id="download",)
57
+
58
+ cmap = matplotlib.colormaps.get_cmap('Spectral_r')
59
+
60
+ def on_submit(image):
61
+ original_image = image.copy()
62
+
63
+ h, w = image.shape[:2]
64
+
65
+ depth = predict_depth(image[:, :, ::-1])
66
+
67
+ raw_depth = Image.fromarray(depth.astype('uint16'))
68
+ tmp_raw_depth = tempfile.NamedTemporaryFile(suffix='.png', delete=False)
69
+ raw_depth.save(tmp_raw_depth.name)
70
+
71
+ depth = (depth - depth.min()) / (depth.max() - depth.min()) * 255.0
72
+ depth = depth.astype(np.uint8)
73
+ colored_depth = (cmap(depth)[:, :, :3] * 255).astype(np.uint8)
74
+
75
+ gray_depth = Image.fromarray(depth)
76
+ tmp_gray_depth = tempfile.NamedTemporaryFile(suffix='.png', delete=False)
77
+ gray_depth.save(tmp_gray_depth.name)
78
+
79
+ return [(original_image, colored_depth), tmp_gray_depth.name, tmp_raw_depth.name]
80
+
81
+ submit.click(on_submit, inputs=[input_image], outputs=[depth_image_slider, gray_depth_file, raw_file])
82
+
83
+ example_files = glob.glob('assets/examples/*')
84
+ examples = gr.Examples(examples=example_files, inputs=[input_image], outputs=[depth_image_slider, gray_depth_file, raw_file], fn=on_submit)
85
+
86
+
87
+ if __name__ == '__main__':
88
+ demo.queue().launch()
code/umm/runtime/models/blip3o/model/anchors/DepthAnything/depth_anything_v2/dinov2.py ADDED
@@ -0,0 +1,415 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Meta Platforms, Inc. and affiliates.
2
+ #
3
+ # This source code is licensed under the Apache License, Version 2.0
4
+ # found in the LICENSE file in the root directory of this source tree.
5
+
6
+ # References:
7
+ # https://github.com/facebookresearch/dino/blob/main/vision_transformer.py
8
+ # https://github.com/rwightman/pytorch-image-models/tree/master/timm/models/vision_transformer.py
9
+
10
+ from functools import partial
11
+ import math
12
+ import logging
13
+ from typing import Sequence, Tuple, Union, Callable
14
+
15
+ import torch
16
+ import torch.nn as nn
17
+ import torch.utils.checkpoint
18
+ from torch.nn.init import trunc_normal_
19
+
20
+ from .dinov2_layers import Mlp, PatchEmbed, SwiGLUFFNFused, MemEffAttention, NestedTensorBlock as Block
21
+
22
+
23
+ logger = logging.getLogger("dinov2")
24
+
25
+
26
+ def named_apply(fn: Callable, module: nn.Module, name="", depth_first=True, include_root=False) -> nn.Module:
27
+ if not depth_first and include_root:
28
+ fn(module=module, name=name)
29
+ for child_name, child_module in module.named_children():
30
+ child_name = ".".join((name, child_name)) if name else child_name
31
+ named_apply(fn=fn, module=child_module, name=child_name, depth_first=depth_first, include_root=True)
32
+ if depth_first and include_root:
33
+ fn(module=module, name=name)
34
+ return module
35
+
36
+
37
+ class BlockChunk(nn.ModuleList):
38
+ def forward(self, x):
39
+ for b in self:
40
+ x = b(x)
41
+ return x
42
+
43
+
44
+ class DinoVisionTransformer(nn.Module):
45
+ def __init__(
46
+ self,
47
+ img_size=224,
48
+ patch_size=16,
49
+ in_chans=3,
50
+ embed_dim=768,
51
+ depth=12,
52
+ num_heads=12,
53
+ mlp_ratio=4.0,
54
+ qkv_bias=True,
55
+ ffn_bias=True,
56
+ proj_bias=True,
57
+ drop_path_rate=0.0,
58
+ drop_path_uniform=False,
59
+ init_values=None, # for layerscale: None or 0 => no layerscale
60
+ embed_layer=PatchEmbed,
61
+ act_layer=nn.GELU,
62
+ block_fn=Block,
63
+ ffn_layer="mlp",
64
+ block_chunks=1,
65
+ num_register_tokens=0,
66
+ interpolate_antialias=False,
67
+ interpolate_offset=0.1,
68
+ ):
69
+ """
70
+ Args:
71
+ img_size (int, tuple): input image size
72
+ patch_size (int, tuple): patch size
73
+ in_chans (int): number of input channels
74
+ embed_dim (int): embedding dimension
75
+ depth (int): depth of transformer
76
+ num_heads (int): number of attention heads
77
+ mlp_ratio (int): ratio of mlp hidden dim to embedding dim
78
+ qkv_bias (bool): enable bias for qkv if True
79
+ proj_bias (bool): enable bias for proj in attn if True
80
+ ffn_bias (bool): enable bias for ffn if True
81
+ drop_path_rate (float): stochastic depth rate
82
+ drop_path_uniform (bool): apply uniform drop rate across blocks
83
+ weight_init (str): weight init scheme
84
+ init_values (float): layer-scale init values
85
+ embed_layer (nn.Module): patch embedding layer
86
+ act_layer (nn.Module): MLP activation layer
87
+ block_fn (nn.Module): transformer block class
88
+ ffn_layer (str): "mlp", "swiglu", "swiglufused" or "identity"
89
+ block_chunks: (int) split block sequence into block_chunks units for FSDP wrap
90
+ num_register_tokens: (int) number of extra cls tokens (so-called "registers")
91
+ interpolate_antialias: (str) flag to apply anti-aliasing when interpolating positional embeddings
92
+ interpolate_offset: (float) work-around offset to apply when interpolating positional embeddings
93
+ """
94
+ super().__init__()
95
+ norm_layer = partial(nn.LayerNorm, eps=1e-6)
96
+
97
+ self.num_features = self.embed_dim = embed_dim # num_features for consistency with other models
98
+ self.num_tokens = 1
99
+ self.n_blocks = depth
100
+ self.num_heads = num_heads
101
+ self.patch_size = patch_size
102
+ self.num_register_tokens = num_register_tokens
103
+ self.interpolate_antialias = interpolate_antialias
104
+ self.interpolate_offset = interpolate_offset
105
+
106
+ self.patch_embed = embed_layer(img_size=img_size, patch_size=patch_size, in_chans=in_chans, embed_dim=embed_dim)
107
+ num_patches = self.patch_embed.num_patches
108
+
109
+ self.cls_token = nn.Parameter(torch.zeros(1, 1, embed_dim))
110
+ self.pos_embed = nn.Parameter(torch.zeros(1, num_patches + self.num_tokens, embed_dim))
111
+ assert num_register_tokens >= 0
112
+ self.register_tokens = (
113
+ nn.Parameter(torch.zeros(1, num_register_tokens, embed_dim)) if num_register_tokens else None
114
+ )
115
+
116
+ if drop_path_uniform is True:
117
+ dpr = [drop_path_rate] * depth
118
+ else:
119
+ dpr = [x.item() for x in torch.linspace(0, drop_path_rate, depth)] # stochastic depth decay rule
120
+
121
+ if ffn_layer == "mlp":
122
+ logger.info("using MLP layer as FFN")
123
+ ffn_layer = Mlp
124
+ elif ffn_layer == "swiglufused" or ffn_layer == "swiglu":
125
+ logger.info("using SwiGLU layer as FFN")
126
+ ffn_layer = SwiGLUFFNFused
127
+ elif ffn_layer == "identity":
128
+ logger.info("using Identity layer as FFN")
129
+
130
+ def f(*args, **kwargs):
131
+ return nn.Identity()
132
+
133
+ ffn_layer = f
134
+ else:
135
+ raise NotImplementedError
136
+
137
+ blocks_list = [
138
+ block_fn(
139
+ dim=embed_dim,
140
+ num_heads=num_heads,
141
+ mlp_ratio=mlp_ratio,
142
+ qkv_bias=qkv_bias,
143
+ proj_bias=proj_bias,
144
+ ffn_bias=ffn_bias,
145
+ drop_path=dpr[i],
146
+ norm_layer=norm_layer,
147
+ act_layer=act_layer,
148
+ ffn_layer=ffn_layer,
149
+ init_values=init_values,
150
+ )
151
+ for i in range(depth)
152
+ ]
153
+ if block_chunks > 0:
154
+ self.chunked_blocks = True
155
+ chunked_blocks = []
156
+ chunksize = depth // block_chunks
157
+ for i in range(0, depth, chunksize):
158
+ # this is to keep the block index consistent if we chunk the block list
159
+ chunked_blocks.append([nn.Identity()] * i + blocks_list[i : i + chunksize])
160
+ self.blocks = nn.ModuleList([BlockChunk(p) for p in chunked_blocks])
161
+ else:
162
+ self.chunked_blocks = False
163
+ self.blocks = nn.ModuleList(blocks_list)
164
+
165
+ self.norm = norm_layer(embed_dim)
166
+ self.head = nn.Identity()
167
+
168
+ self.mask_token = nn.Parameter(torch.zeros(1, embed_dim))
169
+
170
+ self.init_weights()
171
+
172
+ def init_weights(self):
173
+ trunc_normal_(self.pos_embed, std=0.02)
174
+ nn.init.normal_(self.cls_token, std=1e-6)
175
+ if self.register_tokens is not None:
176
+ nn.init.normal_(self.register_tokens, std=1e-6)
177
+ named_apply(init_weights_vit_timm, self)
178
+
179
+ def interpolate_pos_encoding(self, x, w, h):
180
+ previous_dtype = x.dtype
181
+ npatch = x.shape[1] - 1
182
+ N = self.pos_embed.shape[1] - 1
183
+ if npatch == N and w == h:
184
+ return self.pos_embed
185
+ pos_embed = self.pos_embed.float()
186
+ class_pos_embed = pos_embed[:, 0]
187
+ patch_pos_embed = pos_embed[:, 1:]
188
+ dim = x.shape[-1]
189
+ w0 = w // self.patch_size
190
+ h0 = h // self.patch_size
191
+ # we add a small number to avoid floating point error in the interpolation
192
+ # see discussion at https://github.com/facebookresearch/dino/issues/8
193
+ # DINOv2 with register modify the interpolate_offset from 0.1 to 0.0
194
+ w0, h0 = w0 + self.interpolate_offset, h0 + self.interpolate_offset
195
+ # w0, h0 = w0 + 0.1, h0 + 0.1
196
+
197
+ sqrt_N = math.sqrt(N)
198
+ sx, sy = float(w0) / sqrt_N, float(h0) / sqrt_N
199
+ patch_pos_embed = nn.functional.interpolate(
200
+ patch_pos_embed.reshape(1, int(sqrt_N), int(sqrt_N), dim).permute(0, 3, 1, 2),
201
+ scale_factor=(sx, sy),
202
+ # (int(w0), int(h0)), # to solve the upsampling shape issue
203
+ mode="bicubic",
204
+ antialias=self.interpolate_antialias
205
+ )
206
+
207
+ assert int(w0) == patch_pos_embed.shape[-2]
208
+ assert int(h0) == patch_pos_embed.shape[-1]
209
+ patch_pos_embed = patch_pos_embed.permute(0, 2, 3, 1).view(1, -1, dim)
210
+ return torch.cat((class_pos_embed.unsqueeze(0), patch_pos_embed), dim=1).to(previous_dtype)
211
+
212
+ def prepare_tokens_with_masks(self, x, masks=None):
213
+ B, nc, w, h = x.shape
214
+ x = self.patch_embed(x)
215
+ if masks is not None:
216
+ x = torch.where(masks.unsqueeze(-1), self.mask_token.to(x.dtype).unsqueeze(0), x)
217
+
218
+ x = torch.cat((self.cls_token.expand(x.shape[0], -1, -1), x), dim=1)
219
+ x = x + self.interpolate_pos_encoding(x, w, h)
220
+
221
+ if self.register_tokens is not None:
222
+ x = torch.cat(
223
+ (
224
+ x[:, :1],
225
+ self.register_tokens.expand(x.shape[0], -1, -1),
226
+ x[:, 1:],
227
+ ),
228
+ dim=1,
229
+ )
230
+
231
+ return x
232
+
233
+ def forward_features_list(self, x_list, masks_list):
234
+ x = [self.prepare_tokens_with_masks(x, masks) for x, masks in zip(x_list, masks_list)]
235
+ for blk in self.blocks:
236
+ x = blk(x)
237
+
238
+ all_x = x
239
+ output = []
240
+ for x, masks in zip(all_x, masks_list):
241
+ x_norm = self.norm(x)
242
+ output.append(
243
+ {
244
+ "x_norm_clstoken": x_norm[:, 0],
245
+ "x_norm_regtokens": x_norm[:, 1 : self.num_register_tokens + 1],
246
+ "x_norm_patchtokens": x_norm[:, self.num_register_tokens + 1 :],
247
+ "x_prenorm": x,
248
+ "masks": masks,
249
+ }
250
+ )
251
+ return output
252
+
253
+ def forward_features(self, x, masks=None):
254
+ if isinstance(x, list):
255
+ return self.forward_features_list(x, masks)
256
+
257
+ x = self.prepare_tokens_with_masks(x, masks)
258
+
259
+ for blk in self.blocks:
260
+ x = blk(x)
261
+
262
+ x_norm = self.norm(x)
263
+ return {
264
+ "x_norm_clstoken": x_norm[:, 0],
265
+ "x_norm_regtokens": x_norm[:, 1 : self.num_register_tokens + 1],
266
+ "x_norm_patchtokens": x_norm[:, self.num_register_tokens + 1 :],
267
+ "x_prenorm": x,
268
+ "masks": masks,
269
+ }
270
+
271
+ def _get_intermediate_layers_not_chunked(self, x, n=1):
272
+ x = self.prepare_tokens_with_masks(x)
273
+ # If n is an int, take the n last blocks. If it's a list, take them
274
+ output, total_block_len = [], len(self.blocks)
275
+ blocks_to_take = range(total_block_len - n, total_block_len) if isinstance(n, int) else n
276
+ for i, blk in enumerate(self.blocks):
277
+ x = blk(x)
278
+ if i in blocks_to_take:
279
+ output.append(x)
280
+ assert len(output) == len(blocks_to_take), f"only {len(output)} / {len(blocks_to_take)} blocks found"
281
+ return output
282
+
283
+ def _get_intermediate_layers_chunked(self, x, n=1):
284
+ x = self.prepare_tokens_with_masks(x)
285
+ output, i, total_block_len = [], 0, len(self.blocks[-1])
286
+ # If n is an int, take the n last blocks. If it's a list, take them
287
+ blocks_to_take = range(total_block_len - n, total_block_len) if isinstance(n, int) else n
288
+ for block_chunk in self.blocks:
289
+ for blk in block_chunk[i:]: # Passing the nn.Identity()
290
+ x = blk(x)
291
+ if i in blocks_to_take:
292
+ output.append(x)
293
+ i += 1
294
+ assert len(output) == len(blocks_to_take), f"only {len(output)} / {len(blocks_to_take)} blocks found"
295
+ return output
296
+
297
+ def get_intermediate_layers(
298
+ self,
299
+ x: torch.Tensor,
300
+ n: Union[int, Sequence] = 1, # Layers or n last layers to take
301
+ reshape: bool = False,
302
+ return_class_token: bool = False,
303
+ norm=True
304
+ ) -> Tuple[Union[torch.Tensor, Tuple[torch.Tensor]]]:
305
+ if self.chunked_blocks:
306
+ outputs = self._get_intermediate_layers_chunked(x, n)
307
+ else:
308
+ outputs = self._get_intermediate_layers_not_chunked(x, n)
309
+ if norm:
310
+ outputs = [self.norm(out) for out in outputs]
311
+ class_tokens = [out[:, 0] for out in outputs]
312
+ outputs = [out[:, 1 + self.num_register_tokens:] for out in outputs]
313
+ if reshape:
314
+ B, _, w, h = x.shape
315
+ outputs = [
316
+ out.reshape(B, w // self.patch_size, h // self.patch_size, -1).permute(0, 3, 1, 2).contiguous()
317
+ for out in outputs
318
+ ]
319
+ if return_class_token:
320
+ return tuple(zip(outputs, class_tokens))
321
+ return tuple(outputs)
322
+
323
+ def forward(self, *args, is_training=False, **kwargs):
324
+ ret = self.forward_features(*args, **kwargs)
325
+ if is_training:
326
+ return ret
327
+ else:
328
+ return self.head(ret["x_norm_clstoken"])
329
+
330
+
331
+ def init_weights_vit_timm(module: nn.Module, name: str = ""):
332
+ """ViT weight initialization, original timm impl (for reproducibility)"""
333
+ if isinstance(module, nn.Linear):
334
+ trunc_normal_(module.weight, std=0.02)
335
+ if module.bias is not None:
336
+ nn.init.zeros_(module.bias)
337
+
338
+
339
+ def vit_small(patch_size=16, num_register_tokens=0, **kwargs):
340
+ model = DinoVisionTransformer(
341
+ patch_size=patch_size,
342
+ embed_dim=384,
343
+ depth=12,
344
+ num_heads=6,
345
+ mlp_ratio=4,
346
+ block_fn=partial(Block, attn_class=MemEffAttention),
347
+ num_register_tokens=num_register_tokens,
348
+ **kwargs,
349
+ )
350
+ return model
351
+
352
+
353
+ def vit_base(patch_size=16, num_register_tokens=0, **kwargs):
354
+ model = DinoVisionTransformer(
355
+ patch_size=patch_size,
356
+ embed_dim=768,
357
+ depth=12,
358
+ num_heads=12,
359
+ mlp_ratio=4,
360
+ block_fn=partial(Block, attn_class=MemEffAttention),
361
+ num_register_tokens=num_register_tokens,
362
+ **kwargs,
363
+ )
364
+ return model
365
+
366
+
367
+ def vit_large(patch_size=16, num_register_tokens=0, **kwargs):
368
+ model = DinoVisionTransformer(
369
+ patch_size=patch_size,
370
+ embed_dim=1024,
371
+ depth=24,
372
+ num_heads=16,
373
+ mlp_ratio=4,
374
+ block_fn=partial(Block, attn_class=MemEffAttention),
375
+ num_register_tokens=num_register_tokens,
376
+ **kwargs,
377
+ )
378
+ return model
379
+
380
+
381
+ def vit_giant2(patch_size=16, num_register_tokens=0, **kwargs):
382
+ """
383
+ Close to ViT-giant, with embed-dim 1536 and 24 heads => embed-dim per head 64
384
+ """
385
+ model = DinoVisionTransformer(
386
+ patch_size=patch_size,
387
+ embed_dim=1536,
388
+ depth=40,
389
+ num_heads=24,
390
+ mlp_ratio=4,
391
+ block_fn=partial(Block, attn_class=MemEffAttention),
392
+ num_register_tokens=num_register_tokens,
393
+ **kwargs,
394
+ )
395
+ return model
396
+
397
+
398
+ def DINOv2(model_name):
399
+ model_zoo = {
400
+ "vits": vit_small,
401
+ "vitb": vit_base,
402
+ "vitl": vit_large,
403
+ "vitg": vit_giant2
404
+ }
405
+
406
+ return model_zoo[model_name](
407
+ img_size=518,
408
+ patch_size=14,
409
+ init_values=1.0,
410
+ ffn_layer="mlp" if model_name != "vitg" else "swiglufused",
411
+ block_chunks=0,
412
+ num_register_tokens=0,
413
+ interpolate_antialias=False,
414
+ interpolate_offset=0.1
415
+ )
code/umm/runtime/models/blip3o/model/anchors/DepthAnything/depth_anything_v2/dinov2_layers/__init__.py ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Meta Platforms, Inc. and affiliates.
2
+ # All rights reserved.
3
+ #
4
+ # This source code is licensed under the license found in the
5
+ # LICENSE file in the root directory of this source tree.
6
+
7
+ from .mlp import Mlp
8
+ from .patch_embed import PatchEmbed
9
+ from .swiglu_ffn import SwiGLUFFN, SwiGLUFFNFused
10
+ from .block import NestedTensorBlock
11
+ from .attention import MemEffAttention
code/umm/runtime/models/blip3o/model/anchors/DepthAnything/depth_anything_v2/dinov2_layers/attention.py ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Meta Platforms, Inc. and affiliates.
2
+ # All rights reserved.
3
+ #
4
+ # This source code is licensed under the license found in the
5
+ # LICENSE file in the root directory of this source tree.
6
+
7
+ # References:
8
+ # https://github.com/facebookresearch/dino/blob/master/vision_transformer.py
9
+ # https://github.com/rwightman/pytorch-image-models/tree/master/timm/models/vision_transformer.py
10
+
11
+ import logging
12
+
13
+ from torch import Tensor
14
+ from torch import nn
15
+
16
+
17
+ logger = logging.getLogger("dinov2")
18
+
19
+
20
+ try:
21
+ from xformers.ops import memory_efficient_attention, unbind, fmha
22
+
23
+ XFORMERS_AVAILABLE = True
24
+ except ImportError:
25
+ logger.debug("xFormers not available (optional; DINOv2 falls back without it).")
26
+ XFORMERS_AVAILABLE = False
27
+
28
+
29
+ class Attention(nn.Module):
30
+ def __init__(
31
+ self,
32
+ dim: int,
33
+ num_heads: int = 8,
34
+ qkv_bias: bool = False,
35
+ proj_bias: bool = True,
36
+ attn_drop: float = 0.0,
37
+ proj_drop: float = 0.0,
38
+ ) -> None:
39
+ super().__init__()
40
+ self.num_heads = num_heads
41
+ head_dim = dim // num_heads
42
+ self.scale = head_dim**-0.5
43
+
44
+ self.qkv = nn.Linear(dim, dim * 3, bias=qkv_bias)
45
+ self.attn_drop = nn.Dropout(attn_drop)
46
+ self.proj = nn.Linear(dim, dim, bias=proj_bias)
47
+ self.proj_drop = nn.Dropout(proj_drop)
48
+
49
+ def forward(self, x: Tensor) -> Tensor:
50
+ B, N, C = x.shape
51
+ qkv = self.qkv(x).reshape(B, N, 3, self.num_heads, C // self.num_heads).permute(2, 0, 3, 1, 4)
52
+
53
+ q, k, v = qkv[0] * self.scale, qkv[1], qkv[2]
54
+ attn = q @ k.transpose(-2, -1)
55
+
56
+ attn = attn.softmax(dim=-1)
57
+ attn = self.attn_drop(attn)
58
+
59
+ x = (attn @ v).transpose(1, 2).reshape(B, N, C)
60
+ x = self.proj(x)
61
+ x = self.proj_drop(x)
62
+ return x
63
+
64
+
65
+ class MemEffAttention(Attention):
66
+ def forward(self, x: Tensor, attn_bias=None) -> Tensor:
67
+ if not XFORMERS_AVAILABLE:
68
+ assert attn_bias is None, "xFormers is required for nested tensors usage"
69
+ return super().forward(x)
70
+
71
+ B, N, C = x.shape
72
+ qkv = self.qkv(x).reshape(B, N, 3, self.num_heads, C // self.num_heads)
73
+
74
+ q, k, v = unbind(qkv, 2)
75
+
76
+ x = memory_efficient_attention(q, k, v, attn_bias=attn_bias)
77
+ x = x.reshape([B, N, C])
78
+
79
+ x = self.proj(x)
80
+ x = self.proj_drop(x)
81
+ return x
82
+
83
+
code/umm/runtime/models/blip3o/model/anchors/DepthAnything/depth_anything_v2/dinov2_layers/block.py ADDED
@@ -0,0 +1,252 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Meta Platforms, Inc. and affiliates.
2
+ # All rights reserved.
3
+ #
4
+ # This source code is licensed under the license found in the
5
+ # LICENSE file in the root directory of this source tree.
6
+
7
+ # References:
8
+ # https://github.com/facebookresearch/dino/blob/master/vision_transformer.py
9
+ # https://github.com/rwightman/pytorch-image-models/tree/master/timm/layers/patch_embed.py
10
+
11
+ import logging
12
+ from typing import Callable, List, Any, Tuple, Dict
13
+
14
+ import torch
15
+ from torch import nn, Tensor
16
+
17
+ from .attention import Attention, MemEffAttention
18
+ from .drop_path import DropPath
19
+ from .layer_scale import LayerScale
20
+ from .mlp import Mlp
21
+
22
+
23
+ logger = logging.getLogger("dinov2")
24
+
25
+
26
+ try:
27
+ from xformers.ops import fmha
28
+ from xformers.ops import scaled_index_add, index_select_cat
29
+
30
+ XFORMERS_AVAILABLE = True
31
+ except ImportError:
32
+ logger.debug("xFormers not available (optional; DINOv2 falls back without it).")
33
+ XFORMERS_AVAILABLE = False
34
+
35
+
36
+ class Block(nn.Module):
37
+ def __init__(
38
+ self,
39
+ dim: int,
40
+ num_heads: int,
41
+ mlp_ratio: float = 4.0,
42
+ qkv_bias: bool = False,
43
+ proj_bias: bool = True,
44
+ ffn_bias: bool = True,
45
+ drop: float = 0.0,
46
+ attn_drop: float = 0.0,
47
+ init_values=None,
48
+ drop_path: float = 0.0,
49
+ act_layer: Callable[..., nn.Module] = nn.GELU,
50
+ norm_layer: Callable[..., nn.Module] = nn.LayerNorm,
51
+ attn_class: Callable[..., nn.Module] = Attention,
52
+ ffn_layer: Callable[..., nn.Module] = Mlp,
53
+ ) -> None:
54
+ super().__init__()
55
+ # print(f"biases: qkv: {qkv_bias}, proj: {proj_bias}, ffn: {ffn_bias}")
56
+ self.norm1 = norm_layer(dim)
57
+ self.attn = attn_class(
58
+ dim,
59
+ num_heads=num_heads,
60
+ qkv_bias=qkv_bias,
61
+ proj_bias=proj_bias,
62
+ attn_drop=attn_drop,
63
+ proj_drop=drop,
64
+ )
65
+ self.ls1 = LayerScale(dim, init_values=init_values) if init_values else nn.Identity()
66
+ self.drop_path1 = DropPath(drop_path) if drop_path > 0.0 else nn.Identity()
67
+
68
+ self.norm2 = norm_layer(dim)
69
+ mlp_hidden_dim = int(dim * mlp_ratio)
70
+ self.mlp = ffn_layer(
71
+ in_features=dim,
72
+ hidden_features=mlp_hidden_dim,
73
+ act_layer=act_layer,
74
+ drop=drop,
75
+ bias=ffn_bias,
76
+ )
77
+ self.ls2 = LayerScale(dim, init_values=init_values) if init_values else nn.Identity()
78
+ self.drop_path2 = DropPath(drop_path) if drop_path > 0.0 else nn.Identity()
79
+
80
+ self.sample_drop_ratio = drop_path
81
+
82
+ def forward(self, x: Tensor) -> Tensor:
83
+ def attn_residual_func(x: Tensor) -> Tensor:
84
+ return self.ls1(self.attn(self.norm1(x)))
85
+
86
+ def ffn_residual_func(x: Tensor) -> Tensor:
87
+ return self.ls2(self.mlp(self.norm2(x)))
88
+
89
+ if self.training and self.sample_drop_ratio > 0.1:
90
+ # the overhead is compensated only for a drop path rate larger than 0.1
91
+ x = drop_add_residual_stochastic_depth(
92
+ x,
93
+ residual_func=attn_residual_func,
94
+ sample_drop_ratio=self.sample_drop_ratio,
95
+ )
96
+ x = drop_add_residual_stochastic_depth(
97
+ x,
98
+ residual_func=ffn_residual_func,
99
+ sample_drop_ratio=self.sample_drop_ratio,
100
+ )
101
+ elif self.training and self.sample_drop_ratio > 0.0:
102
+ x = x + self.drop_path1(attn_residual_func(x))
103
+ x = x + self.drop_path1(ffn_residual_func(x)) # FIXME: drop_path2
104
+ else:
105
+ x = x + attn_residual_func(x)
106
+ x = x + ffn_residual_func(x)
107
+ return x
108
+
109
+
110
+ def drop_add_residual_stochastic_depth(
111
+ x: Tensor,
112
+ residual_func: Callable[[Tensor], Tensor],
113
+ sample_drop_ratio: float = 0.0,
114
+ ) -> Tensor:
115
+ # 1) extract subset using permutation
116
+ b, n, d = x.shape
117
+ sample_subset_size = max(int(b * (1 - sample_drop_ratio)), 1)
118
+ brange = (torch.randperm(b, device=x.device))[:sample_subset_size]
119
+ x_subset = x[brange]
120
+
121
+ # 2) apply residual_func to get residual
122
+ residual = residual_func(x_subset)
123
+
124
+ x_flat = x.flatten(1)
125
+ residual = residual.flatten(1)
126
+
127
+ residual_scale_factor = b / sample_subset_size
128
+
129
+ # 3) add the residual
130
+ x_plus_residual = torch.index_add(x_flat, 0, brange, residual.to(dtype=x.dtype), alpha=residual_scale_factor)
131
+ return x_plus_residual.view_as(x)
132
+
133
+
134
+ def get_branges_scales(x, sample_drop_ratio=0.0):
135
+ b, n, d = x.shape
136
+ sample_subset_size = max(int(b * (1 - sample_drop_ratio)), 1)
137
+ brange = (torch.randperm(b, device=x.device))[:sample_subset_size]
138
+ residual_scale_factor = b / sample_subset_size
139
+ return brange, residual_scale_factor
140
+
141
+
142
+ def add_residual(x, brange, residual, residual_scale_factor, scaling_vector=None):
143
+ if scaling_vector is None:
144
+ x_flat = x.flatten(1)
145
+ residual = residual.flatten(1)
146
+ x_plus_residual = torch.index_add(x_flat, 0, brange, residual.to(dtype=x.dtype), alpha=residual_scale_factor)
147
+ else:
148
+ x_plus_residual = scaled_index_add(
149
+ x, brange, residual.to(dtype=x.dtype), scaling=scaling_vector, alpha=residual_scale_factor
150
+ )
151
+ return x_plus_residual
152
+
153
+
154
+ attn_bias_cache: Dict[Tuple, Any] = {}
155
+
156
+
157
+ def get_attn_bias_and_cat(x_list, branges=None):
158
+ """
159
+ this will perform the index select, cat the tensors, and provide the attn_bias from cache
160
+ """
161
+ batch_sizes = [b.shape[0] for b in branges] if branges is not None else [x.shape[0] for x in x_list]
162
+ all_shapes = tuple((b, x.shape[1]) for b, x in zip(batch_sizes, x_list))
163
+ if all_shapes not in attn_bias_cache.keys():
164
+ seqlens = []
165
+ for b, x in zip(batch_sizes, x_list):
166
+ for _ in range(b):
167
+ seqlens.append(x.shape[1])
168
+ attn_bias = fmha.BlockDiagonalMask.from_seqlens(seqlens)
169
+ attn_bias._batch_sizes = batch_sizes
170
+ attn_bias_cache[all_shapes] = attn_bias
171
+
172
+ if branges is not None:
173
+ cat_tensors = index_select_cat([x.flatten(1) for x in x_list], branges).view(1, -1, x_list[0].shape[-1])
174
+ else:
175
+ tensors_bs1 = tuple(x.reshape([1, -1, *x.shape[2:]]) for x in x_list)
176
+ cat_tensors = torch.cat(tensors_bs1, dim=1)
177
+
178
+ return attn_bias_cache[all_shapes], cat_tensors
179
+
180
+
181
+ def drop_add_residual_stochastic_depth_list(
182
+ x_list: List[Tensor],
183
+ residual_func: Callable[[Tensor, Any], Tensor],
184
+ sample_drop_ratio: float = 0.0,
185
+ scaling_vector=None,
186
+ ) -> Tensor:
187
+ # 1) generate random set of indices for dropping samples in the batch
188
+ branges_scales = [get_branges_scales(x, sample_drop_ratio=sample_drop_ratio) for x in x_list]
189
+ branges = [s[0] for s in branges_scales]
190
+ residual_scale_factors = [s[1] for s in branges_scales]
191
+
192
+ # 2) get attention bias and index+concat the tensors
193
+ attn_bias, x_cat = get_attn_bias_and_cat(x_list, branges)
194
+
195
+ # 3) apply residual_func to get residual, and split the result
196
+ residual_list = attn_bias.split(residual_func(x_cat, attn_bias=attn_bias)) # type: ignore
197
+
198
+ outputs = []
199
+ for x, brange, residual, residual_scale_factor in zip(x_list, branges, residual_list, residual_scale_factors):
200
+ outputs.append(add_residual(x, brange, residual, residual_scale_factor, scaling_vector).view_as(x))
201
+ return outputs
202
+
203
+
204
+ class NestedTensorBlock(Block):
205
+ def forward_nested(self, x_list: List[Tensor]) -> List[Tensor]:
206
+ """
207
+ x_list contains a list of tensors to nest together and run
208
+ """
209
+ assert isinstance(self.attn, MemEffAttention)
210
+
211
+ if self.training and self.sample_drop_ratio > 0.0:
212
+
213
+ def attn_residual_func(x: Tensor, attn_bias=None) -> Tensor:
214
+ return self.attn(self.norm1(x), attn_bias=attn_bias)
215
+
216
+ def ffn_residual_func(x: Tensor, attn_bias=None) -> Tensor:
217
+ return self.mlp(self.norm2(x))
218
+
219
+ x_list = drop_add_residual_stochastic_depth_list(
220
+ x_list,
221
+ residual_func=attn_residual_func,
222
+ sample_drop_ratio=self.sample_drop_ratio,
223
+ scaling_vector=self.ls1.gamma if isinstance(self.ls1, LayerScale) else None,
224
+ )
225
+ x_list = drop_add_residual_stochastic_depth_list(
226
+ x_list,
227
+ residual_func=ffn_residual_func,
228
+ sample_drop_ratio=self.sample_drop_ratio,
229
+ scaling_vector=self.ls2.gamma if isinstance(self.ls1, LayerScale) else None,
230
+ )
231
+ return x_list
232
+ else:
233
+
234
+ def attn_residual_func(x: Tensor, attn_bias=None) -> Tensor:
235
+ return self.ls1(self.attn(self.norm1(x), attn_bias=attn_bias))
236
+
237
+ def ffn_residual_func(x: Tensor, attn_bias=None) -> Tensor:
238
+ return self.ls2(self.mlp(self.norm2(x)))
239
+
240
+ attn_bias, x = get_attn_bias_and_cat(x_list)
241
+ x = x + attn_residual_func(x, attn_bias=attn_bias)
242
+ x = x + ffn_residual_func(x)
243
+ return attn_bias.split(x)
244
+
245
+ def forward(self, x_or_x_list):
246
+ if isinstance(x_or_x_list, Tensor):
247
+ return super().forward(x_or_x_list)
248
+ elif isinstance(x_or_x_list, list):
249
+ assert XFORMERS_AVAILABLE, "Please install xFormers for nested tensors usage"
250
+ return self.forward_nested(x_or_x_list)
251
+ else:
252
+ raise AssertionError
code/umm/runtime/models/blip3o/model/anchors/DepthAnything/depth_anything_v2/dinov2_layers/drop_path.py ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Meta Platforms, Inc. and affiliates.
2
+ # All rights reserved.
3
+ #
4
+ # This source code is licensed under the license found in the
5
+ # LICENSE file in the root directory of this source tree.
6
+
7
+ # References:
8
+ # https://github.com/facebookresearch/dino/blob/master/vision_transformer.py
9
+ # https://github.com/rwightman/pytorch-image-models/tree/master/timm/layers/drop.py
10
+
11
+
12
+ from torch import nn
13
+
14
+
15
+ def drop_path(x, drop_prob: float = 0.0, training: bool = False):
16
+ if drop_prob == 0.0 or not training:
17
+ return x
18
+ keep_prob = 1 - drop_prob
19
+ shape = (x.shape[0],) + (1,) * (x.ndim - 1) # work with diff dim tensors, not just 2D ConvNets
20
+ random_tensor = x.new_empty(shape).bernoulli_(keep_prob)
21
+ if keep_prob > 0.0:
22
+ random_tensor.div_(keep_prob)
23
+ output = x * random_tensor
24
+ return output
25
+
26
+
27
+ class DropPath(nn.Module):
28
+ """Drop paths (Stochastic Depth) per sample (when applied in main path of residual blocks)."""
29
+
30
+ def __init__(self, drop_prob=None):
31
+ super(DropPath, self).__init__()
32
+ self.drop_prob = drop_prob
33
+
34
+ def forward(self, x):
35
+ return drop_path(x, self.drop_prob, self.training)
code/umm/runtime/models/blip3o/model/anchors/DepthAnything/depth_anything_v2/dinov2_layers/layer_scale.py ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Meta Platforms, Inc. and affiliates.
2
+ # All rights reserved.
3
+ #
4
+ # This source code is licensed under the license found in the
5
+ # LICENSE file in the root directory of this source tree.
6
+
7
+ # Modified from: https://github.com/huggingface/pytorch-image-models/blob/main/timm/models/vision_transformer.py#L103-L110
8
+
9
+ from typing import Union
10
+
11
+ import torch
12
+ from torch import Tensor
13
+ from torch import nn
14
+
15
+
16
+ class LayerScale(nn.Module):
17
+ def __init__(
18
+ self,
19
+ dim: int,
20
+ init_values: Union[float, Tensor] = 1e-5,
21
+ inplace: bool = False,
22
+ ) -> None:
23
+ super().__init__()
24
+ self.inplace = inplace
25
+ self.gamma = nn.Parameter(init_values * torch.ones(dim))
26
+
27
+ def forward(self, x: Tensor) -> Tensor:
28
+ return x.mul_(self.gamma) if self.inplace else x * self.gamma
code/umm/runtime/models/blip3o/model/anchors/DepthAnything/depth_anything_v2/dinov2_layers/mlp.py ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Meta Platforms, Inc. and affiliates.
2
+ # All rights reserved.
3
+ #
4
+ # This source code is licensed under the license found in the
5
+ # LICENSE file in the root directory of this source tree.
6
+
7
+ # References:
8
+ # https://github.com/facebookresearch/dino/blob/master/vision_transformer.py
9
+ # https://github.com/rwightman/pytorch-image-models/tree/master/timm/layers/mlp.py
10
+
11
+
12
+ from typing import Callable, Optional
13
+
14
+ from torch import Tensor, nn
15
+
16
+
17
+ class Mlp(nn.Module):
18
+ def __init__(
19
+ self,
20
+ in_features: int,
21
+ hidden_features: Optional[int] = None,
22
+ out_features: Optional[int] = None,
23
+ act_layer: Callable[..., nn.Module] = nn.GELU,
24
+ drop: float = 0.0,
25
+ bias: bool = True,
26
+ ) -> None:
27
+ super().__init__()
28
+ out_features = out_features or in_features
29
+ hidden_features = hidden_features or in_features
30
+ self.fc1 = nn.Linear(in_features, hidden_features, bias=bias)
31
+ self.act = act_layer()
32
+ self.fc2 = nn.Linear(hidden_features, out_features, bias=bias)
33
+ self.drop = nn.Dropout(drop)
34
+
35
+ def forward(self, x: Tensor) -> Tensor:
36
+ x = self.fc1(x)
37
+ x = self.act(x)
38
+ x = self.drop(x)
39
+ x = self.fc2(x)
40
+ x = self.drop(x)
41
+ return x
code/umm/runtime/models/blip3o/model/anchors/DepthAnything/depth_anything_v2/dinov2_layers/patch_embed.py ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Meta Platforms, Inc. and affiliates.
2
+ # All rights reserved.
3
+ #
4
+ # This source code is licensed under the license found in the
5
+ # LICENSE file in the root directory of this source tree.
6
+
7
+ # References:
8
+ # https://github.com/facebookresearch/dino/blob/master/vision_transformer.py
9
+ # https://github.com/rwightman/pytorch-image-models/tree/master/timm/layers/patch_embed.py
10
+
11
+ from typing import Callable, Optional, Tuple, Union
12
+
13
+ from torch import Tensor
14
+ import torch.nn as nn
15
+
16
+
17
+ def make_2tuple(x):
18
+ if isinstance(x, tuple):
19
+ assert len(x) == 2
20
+ return x
21
+
22
+ assert isinstance(x, int)
23
+ return (x, x)
24
+
25
+
26
+ class PatchEmbed(nn.Module):
27
+ """
28
+ 2D image to patch embedding: (B,C,H,W) -> (B,N,D)
29
+
30
+ Args:
31
+ img_size: Image size.
32
+ patch_size: Patch token size.
33
+ in_chans: Number of input image channels.
34
+ embed_dim: Number of linear projection output channels.
35
+ norm_layer: Normalization layer.
36
+ """
37
+
38
+ def __init__(
39
+ self,
40
+ img_size: Union[int, Tuple[int, int]] = 224,
41
+ patch_size: Union[int, Tuple[int, int]] = 16,
42
+ in_chans: int = 3,
43
+ embed_dim: int = 768,
44
+ norm_layer: Optional[Callable] = None,
45
+ flatten_embedding: bool = True,
46
+ ) -> None:
47
+ super().__init__()
48
+
49
+ image_HW = make_2tuple(img_size)
50
+ patch_HW = make_2tuple(patch_size)
51
+ patch_grid_size = (
52
+ image_HW[0] // patch_HW[0],
53
+ image_HW[1] // patch_HW[1],
54
+ )
55
+
56
+ self.img_size = image_HW
57
+ self.patch_size = patch_HW
58
+ self.patches_resolution = patch_grid_size
59
+ self.num_patches = patch_grid_size[0] * patch_grid_size[1]
60
+
61
+ self.in_chans = in_chans
62
+ self.embed_dim = embed_dim
63
+
64
+ self.flatten_embedding = flatten_embedding
65
+
66
+ self.proj = nn.Conv2d(in_chans, embed_dim, kernel_size=patch_HW, stride=patch_HW)
67
+ self.norm = norm_layer(embed_dim) if norm_layer else nn.Identity()
68
+
69
+ def forward(self, x: Tensor) -> Tensor:
70
+ _, _, H, W = x.shape
71
+ patch_H, patch_W = self.patch_size
72
+
73
+ assert H % patch_H == 0, f"Input image height {H} is not a multiple of patch height {patch_H}"
74
+ assert W % patch_W == 0, f"Input image width {W} is not a multiple of patch width: {patch_W}"
75
+
76
+ x = self.proj(x) # B C H W
77
+ H, W = x.size(2), x.size(3)
78
+ x = x.flatten(2).transpose(1, 2) # B HW C
79
+ x = self.norm(x)
80
+ if not self.flatten_embedding:
81
+ x = x.reshape(-1, H, W, self.embed_dim) # B H W C
82
+ return x
83
+
84
+ def flops(self) -> float:
85
+ Ho, Wo = self.patches_resolution
86
+ flops = Ho * Wo * self.embed_dim * self.in_chans * (self.patch_size[0] * self.patch_size[1])
87
+ if self.norm is not None:
88
+ flops += Ho * Wo * self.embed_dim
89
+ return flops
code/umm/runtime/models/blip3o/model/anchors/DepthAnything/depth_anything_v2/dinov2_layers/swiglu_ffn.py ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Meta Platforms, Inc. and affiliates.
2
+ # All rights reserved.
3
+ #
4
+ # This source code is licensed under the license found in the
5
+ # LICENSE file in the root directory of this source tree.
6
+
7
+ from typing import Callable, Optional
8
+
9
+ from torch import Tensor, nn
10
+ import torch.nn.functional as F
11
+
12
+
13
+ class SwiGLUFFN(nn.Module):
14
+ def __init__(
15
+ self,
16
+ in_features: int,
17
+ hidden_features: Optional[int] = None,
18
+ out_features: Optional[int] = None,
19
+ act_layer: Callable[..., nn.Module] = None,
20
+ drop: float = 0.0,
21
+ bias: bool = True,
22
+ ) -> None:
23
+ super().__init__()
24
+ out_features = out_features or in_features
25
+ hidden_features = hidden_features or in_features
26
+ self.w12 = nn.Linear(in_features, 2 * hidden_features, bias=bias)
27
+ self.w3 = nn.Linear(hidden_features, out_features, bias=bias)
28
+
29
+ def forward(self, x: Tensor) -> Tensor:
30
+ x12 = self.w12(x)
31
+ x1, x2 = x12.chunk(2, dim=-1)
32
+ hidden = F.silu(x1) * x2
33
+ return self.w3(hidden)
34
+
35
+
36
+ try:
37
+ from xformers.ops import SwiGLU
38
+
39
+ XFORMERS_AVAILABLE = True
40
+ except ImportError:
41
+ SwiGLU = SwiGLUFFN
42
+ XFORMERS_AVAILABLE = False
43
+
44
+
45
+ class SwiGLUFFNFused(SwiGLU):
46
+ def __init__(
47
+ self,
48
+ in_features: int,
49
+ hidden_features: Optional[int] = None,
50
+ out_features: Optional[int] = None,
51
+ act_layer: Callable[..., nn.Module] = None,
52
+ drop: float = 0.0,
53
+ bias: bool = True,
54
+ ) -> None:
55
+ out_features = out_features or in_features
56
+ hidden_features = hidden_features or in_features
57
+ hidden_features = (int(hidden_features * 2 / 3) + 7) // 8 * 8
58
+ super().__init__(
59
+ in_features=in_features,
60
+ hidden_features=hidden_features,
61
+ out_features=out_features,
62
+ bias=bias,
63
+ )
code/umm/runtime/models/blip3o/model/anchors/DepthAnything/depth_anything_v2/dpt.py ADDED
@@ -0,0 +1,221 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import cv2
2
+ import torch
3
+ import torch.nn as nn
4
+ import torch.nn.functional as F
5
+ from torchvision.transforms import Compose
6
+
7
+ from .dinov2 import DINOv2
8
+ from .util.blocks import FeatureFusionBlock, _make_scratch
9
+ from .util.transform import Resize, NormalizeImage, PrepareForNet
10
+
11
+
12
+ def _make_fusion_block(features, use_bn, size=None):
13
+ return FeatureFusionBlock(
14
+ features,
15
+ nn.ReLU(False),
16
+ deconv=False,
17
+ bn=use_bn,
18
+ expand=False,
19
+ align_corners=True,
20
+ size=size,
21
+ )
22
+
23
+
24
+ class ConvBlock(nn.Module):
25
+ def __init__(self, in_feature, out_feature):
26
+ super().__init__()
27
+
28
+ self.conv_block = nn.Sequential(
29
+ nn.Conv2d(in_feature, out_feature, kernel_size=3, stride=1, padding=1),
30
+ nn.BatchNorm2d(out_feature),
31
+ nn.ReLU(True)
32
+ )
33
+
34
+ def forward(self, x):
35
+ return self.conv_block(x)
36
+
37
+
38
+ class DPTHead(nn.Module):
39
+ def __init__(
40
+ self,
41
+ in_channels,
42
+ features=256,
43
+ use_bn=False,
44
+ out_channels=[256, 512, 1024, 1024],
45
+ use_clstoken=False
46
+ ):
47
+ super(DPTHead, self).__init__()
48
+
49
+ self.use_clstoken = use_clstoken
50
+
51
+ self.projects = nn.ModuleList([
52
+ nn.Conv2d(
53
+ in_channels=in_channels,
54
+ out_channels=out_channel,
55
+ kernel_size=1,
56
+ stride=1,
57
+ padding=0,
58
+ ) for out_channel in out_channels
59
+ ])
60
+
61
+ self.resize_layers = nn.ModuleList([
62
+ nn.ConvTranspose2d(
63
+ in_channels=out_channels[0],
64
+ out_channels=out_channels[0],
65
+ kernel_size=4,
66
+ stride=4,
67
+ padding=0),
68
+ nn.ConvTranspose2d(
69
+ in_channels=out_channels[1],
70
+ out_channels=out_channels[1],
71
+ kernel_size=2,
72
+ stride=2,
73
+ padding=0),
74
+ nn.Identity(),
75
+ nn.Conv2d(
76
+ in_channels=out_channels[3],
77
+ out_channels=out_channels[3],
78
+ kernel_size=3,
79
+ stride=2,
80
+ padding=1)
81
+ ])
82
+
83
+ if use_clstoken:
84
+ self.readout_projects = nn.ModuleList()
85
+ for _ in range(len(self.projects)):
86
+ self.readout_projects.append(
87
+ nn.Sequential(
88
+ nn.Linear(2 * in_channels, in_channels),
89
+ nn.GELU()))
90
+
91
+ self.scratch = _make_scratch(
92
+ out_channels,
93
+ features,
94
+ groups=1,
95
+ expand=False,
96
+ )
97
+
98
+ self.scratch.stem_transpose = None
99
+
100
+ self.scratch.refinenet1 = _make_fusion_block(features, use_bn)
101
+ self.scratch.refinenet2 = _make_fusion_block(features, use_bn)
102
+ self.scratch.refinenet3 = _make_fusion_block(features, use_bn)
103
+ self.scratch.refinenet4 = _make_fusion_block(features, use_bn)
104
+
105
+ head_features_1 = features
106
+ head_features_2 = 32
107
+
108
+ self.scratch.output_conv1 = nn.Conv2d(head_features_1, head_features_1 // 2, kernel_size=3, stride=1, padding=1)
109
+ self.scratch.output_conv2 = nn.Sequential(
110
+ nn.Conv2d(head_features_1 // 2, head_features_2, kernel_size=3, stride=1, padding=1),
111
+ nn.ReLU(True),
112
+ nn.Conv2d(head_features_2, 1, kernel_size=1, stride=1, padding=0),
113
+ nn.ReLU(True),
114
+ nn.Identity(),
115
+ )
116
+
117
+ def forward(self, out_features, patch_h, patch_w):
118
+ out = []
119
+ for i, x in enumerate(out_features):
120
+ if self.use_clstoken:
121
+ x, cls_token = x[0], x[1]
122
+ readout = cls_token.unsqueeze(1).expand_as(x)
123
+ x = self.readout_projects[i](torch.cat((x, readout), -1))
124
+ else:
125
+ x = x[0]
126
+
127
+ x = x.permute(0, 2, 1).reshape((x.shape[0], x.shape[-1], patch_h, patch_w))
128
+
129
+ x = self.projects[i](x)
130
+ x = self.resize_layers[i](x)
131
+
132
+ out.append(x)
133
+
134
+ layer_1, layer_2, layer_3, layer_4 = out
135
+
136
+ layer_1_rn = self.scratch.layer1_rn(layer_1)
137
+ layer_2_rn = self.scratch.layer2_rn(layer_2)
138
+ layer_3_rn = self.scratch.layer3_rn(layer_3)
139
+ layer_4_rn = self.scratch.layer4_rn(layer_4)
140
+
141
+ path_4 = self.scratch.refinenet4(layer_4_rn, size=layer_3_rn.shape[2:])
142
+ path_3 = self.scratch.refinenet3(path_4, layer_3_rn, size=layer_2_rn.shape[2:])
143
+ path_2 = self.scratch.refinenet2(path_3, layer_2_rn, size=layer_1_rn.shape[2:])
144
+ path_1 = self.scratch.refinenet1(path_2, layer_1_rn)
145
+
146
+ out = self.scratch.output_conv1(path_1)
147
+ out = F.interpolate(out, (int(patch_h * 14), int(patch_w * 14)), mode="bilinear", align_corners=True)
148
+ out = self.scratch.output_conv2(out)
149
+
150
+ return out
151
+
152
+
153
+ class DepthAnythingV2(nn.Module):
154
+ def __init__(
155
+ self,
156
+ encoder='vitl',
157
+ features=256,
158
+ out_channels=[256, 512, 1024, 1024],
159
+ use_bn=False,
160
+ use_clstoken=False
161
+ ):
162
+ super(DepthAnythingV2, self).__init__()
163
+
164
+ self.intermediate_layer_idx = {
165
+ 'vits': [2, 5, 8, 11],
166
+ 'vitb': [2, 5, 8, 11],
167
+ 'vitl': [4, 11, 17, 23],
168
+ 'vitg': [9, 19, 29, 39]
169
+ }
170
+
171
+ self.encoder = encoder
172
+ self.pretrained = DINOv2(model_name=encoder)
173
+
174
+ self.depth_head = DPTHead(self.pretrained.embed_dim, features, use_bn, out_channels=out_channels, use_clstoken=use_clstoken)
175
+
176
+ def forward(self, x):
177
+ patch_h, patch_w = x.shape[-2] // 14, x.shape[-1] // 14
178
+
179
+ features = self.pretrained.get_intermediate_layers(x, self.intermediate_layer_idx[self.encoder], return_class_token=True)
180
+
181
+ depth = self.depth_head(features, patch_h, patch_w)
182
+ depth = F.relu(depth)
183
+
184
+ return depth.squeeze(1)
185
+
186
+ @torch.no_grad()
187
+ def infer_image(self, raw_image, input_size=518):
188
+ image, (h, w) = self.image2tensor(raw_image, input_size)
189
+
190
+ depth = self.forward(image)
191
+
192
+ depth = F.interpolate(depth[:, None], (h, w), mode="bilinear", align_corners=True)[0, 0]
193
+
194
+ return depth.cpu().numpy()
195
+
196
+ def image2tensor(self, raw_image, input_size=518):
197
+ transform = Compose([
198
+ Resize(
199
+ width=input_size,
200
+ height=input_size,
201
+ resize_target=False,
202
+ keep_aspect_ratio=True,
203
+ ensure_multiple_of=14,
204
+ resize_method='lower_bound',
205
+ image_interpolation_method=cv2.INTER_CUBIC,
206
+ ),
207
+ NormalizeImage(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
208
+ PrepareForNet(),
209
+ ])
210
+
211
+ h, w = raw_image.shape[:2]
212
+
213
+ image = cv2.cvtColor(raw_image, cv2.COLOR_BGR2RGB) / 255.0
214
+
215
+ image = transform({'image': image})['image']
216
+ image = torch.from_numpy(image).unsqueeze(0)
217
+
218
+ DEVICE = 'cuda' if torch.cuda.is_available() else 'mps' if torch.backends.mps.is_available() else 'cpu'
219
+ image = image.to(DEVICE)
220
+
221
+ return image, (h, w)
code/umm/runtime/models/blip3o/model/anchors/DepthAnything/depth_anything_v2/util/blocks.py ADDED
@@ -0,0 +1,148 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch.nn as nn
2
+
3
+
4
+ def _make_scratch(in_shape, out_shape, groups=1, expand=False):
5
+ scratch = nn.Module()
6
+
7
+ out_shape1 = out_shape
8
+ out_shape2 = out_shape
9
+ out_shape3 = out_shape
10
+ if len(in_shape) >= 4:
11
+ out_shape4 = out_shape
12
+
13
+ if expand:
14
+ out_shape1 = out_shape
15
+ out_shape2 = out_shape * 2
16
+ out_shape3 = out_shape * 4
17
+ if len(in_shape) >= 4:
18
+ out_shape4 = out_shape * 8
19
+
20
+ scratch.layer1_rn = nn.Conv2d(in_shape[0], out_shape1, kernel_size=3, stride=1, padding=1, bias=False, groups=groups)
21
+ scratch.layer2_rn = nn.Conv2d(in_shape[1], out_shape2, kernel_size=3, stride=1, padding=1, bias=False, groups=groups)
22
+ scratch.layer3_rn = nn.Conv2d(in_shape[2], out_shape3, kernel_size=3, stride=1, padding=1, bias=False, groups=groups)
23
+ if len(in_shape) >= 4:
24
+ scratch.layer4_rn = nn.Conv2d(in_shape[3], out_shape4, kernel_size=3, stride=1, padding=1, bias=False, groups=groups)
25
+
26
+ return scratch
27
+
28
+
29
+ class ResidualConvUnit(nn.Module):
30
+ """Residual convolution module.
31
+ """
32
+
33
+ def __init__(self, features, activation, bn):
34
+ """Init.
35
+
36
+ Args:
37
+ features (int): number of features
38
+ """
39
+ super().__init__()
40
+
41
+ self.bn = bn
42
+
43
+ self.groups=1
44
+
45
+ self.conv1 = nn.Conv2d(features, features, kernel_size=3, stride=1, padding=1, bias=True, groups=self.groups)
46
+
47
+ self.conv2 = nn.Conv2d(features, features, kernel_size=3, stride=1, padding=1, bias=True, groups=self.groups)
48
+
49
+ if self.bn == True:
50
+ self.bn1 = nn.BatchNorm2d(features)
51
+ self.bn2 = nn.BatchNorm2d(features)
52
+
53
+ self.activation = activation
54
+
55
+ self.skip_add = nn.quantized.FloatFunctional()
56
+
57
+ def forward(self, x):
58
+ """Forward pass.
59
+
60
+ Args:
61
+ x (tensor): input
62
+
63
+ Returns:
64
+ tensor: output
65
+ """
66
+
67
+ out = self.activation(x)
68
+ out = self.conv1(out)
69
+ if self.bn == True:
70
+ out = self.bn1(out)
71
+
72
+ out = self.activation(out)
73
+ out = self.conv2(out)
74
+ if self.bn == True:
75
+ out = self.bn2(out)
76
+
77
+ if self.groups > 1:
78
+ out = self.conv_merge(out)
79
+
80
+ return self.skip_add.add(out, x)
81
+
82
+
83
+ class FeatureFusionBlock(nn.Module):
84
+ """Feature fusion block.
85
+ """
86
+
87
+ def __init__(
88
+ self,
89
+ features,
90
+ activation,
91
+ deconv=False,
92
+ bn=False,
93
+ expand=False,
94
+ align_corners=True,
95
+ size=None
96
+ ):
97
+ """Init.
98
+
99
+ Args:
100
+ features (int): number of features
101
+ """
102
+ super(FeatureFusionBlock, self).__init__()
103
+
104
+ self.deconv = deconv
105
+ self.align_corners = align_corners
106
+
107
+ self.groups=1
108
+
109
+ self.expand = expand
110
+ out_features = features
111
+ if self.expand == True:
112
+ out_features = features // 2
113
+
114
+ self.out_conv = nn.Conv2d(features, out_features, kernel_size=1, stride=1, padding=0, bias=True, groups=1)
115
+
116
+ self.resConfUnit1 = ResidualConvUnit(features, activation, bn)
117
+ self.resConfUnit2 = ResidualConvUnit(features, activation, bn)
118
+
119
+ self.skip_add = nn.quantized.FloatFunctional()
120
+
121
+ self.size=size
122
+
123
+ def forward(self, *xs, size=None):
124
+ """Forward pass.
125
+
126
+ Returns:
127
+ tensor: output
128
+ """
129
+ output = xs[0]
130
+
131
+ if len(xs) == 2:
132
+ res = self.resConfUnit1(xs[1])
133
+ output = self.skip_add.add(output, res)
134
+
135
+ output = self.resConfUnit2(output)
136
+
137
+ if (size is None) and (self.size is None):
138
+ modifier = {"scale_factor": 2}
139
+ elif size is None:
140
+ modifier = {"size": self.size}
141
+ else:
142
+ modifier = {"size": size}
143
+
144
+ output = nn.functional.interpolate(output, **modifier, mode="bilinear", align_corners=self.align_corners)
145
+
146
+ output = self.out_conv(output)
147
+
148
+ return output
code/umm/runtime/models/blip3o/model/anchors/DepthAnything/depth_anything_v2/util/transform.py ADDED
@@ -0,0 +1,158 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ import cv2
3
+
4
+
5
+ class Resize(object):
6
+ """Resize sample to given size (width, height).
7
+ """
8
+
9
+ def __init__(
10
+ self,
11
+ width,
12
+ height,
13
+ resize_target=True,
14
+ keep_aspect_ratio=False,
15
+ ensure_multiple_of=1,
16
+ resize_method="lower_bound",
17
+ image_interpolation_method=cv2.INTER_AREA,
18
+ ):
19
+ """Init.
20
+
21
+ Args:
22
+ width (int): desired output width
23
+ height (int): desired output height
24
+ resize_target (bool, optional):
25
+ True: Resize the full sample (image, mask, target).
26
+ False: Resize image only.
27
+ Defaults to True.
28
+ keep_aspect_ratio (bool, optional):
29
+ True: Keep the aspect ratio of the input sample.
30
+ Output sample might not have the given width and height, and
31
+ resize behaviour depends on the parameter 'resize_method'.
32
+ Defaults to False.
33
+ ensure_multiple_of (int, optional):
34
+ Output width and height is constrained to be multiple of this parameter.
35
+ Defaults to 1.
36
+ resize_method (str, optional):
37
+ "lower_bound": Output will be at least as large as the given size.
38
+ "upper_bound": Output will be at max as large as the given size. (Output size might be smaller than given size.)
39
+ "minimal": Scale as least as possible. (Output size might be smaller than given size.)
40
+ Defaults to "lower_bound".
41
+ """
42
+ self.__width = width
43
+ self.__height = height
44
+
45
+ self.__resize_target = resize_target
46
+ self.__keep_aspect_ratio = keep_aspect_ratio
47
+ self.__multiple_of = ensure_multiple_of
48
+ self.__resize_method = resize_method
49
+ self.__image_interpolation_method = image_interpolation_method
50
+
51
+ def constrain_to_multiple_of(self, x, min_val=0, max_val=None):
52
+ y = (np.round(x / self.__multiple_of) * self.__multiple_of).astype(int)
53
+
54
+ if max_val is not None and y > max_val:
55
+ y = (np.floor(x / self.__multiple_of) * self.__multiple_of).astype(int)
56
+
57
+ if y < min_val:
58
+ y = (np.ceil(x / self.__multiple_of) * self.__multiple_of).astype(int)
59
+
60
+ return y
61
+
62
+ def get_size(self, width, height):
63
+ # determine new height and width
64
+ scale_height = self.__height / height
65
+ scale_width = self.__width / width
66
+
67
+ if self.__keep_aspect_ratio:
68
+ if self.__resize_method == "lower_bound":
69
+ # scale such that output size is lower bound
70
+ if scale_width > scale_height:
71
+ # fit width
72
+ scale_height = scale_width
73
+ else:
74
+ # fit height
75
+ scale_width = scale_height
76
+ elif self.__resize_method == "upper_bound":
77
+ # scale such that output size is upper bound
78
+ if scale_width < scale_height:
79
+ # fit width
80
+ scale_height = scale_width
81
+ else:
82
+ # fit height
83
+ scale_width = scale_height
84
+ elif self.__resize_method == "minimal":
85
+ # scale as least as possbile
86
+ if abs(1 - scale_width) < abs(1 - scale_height):
87
+ # fit width
88
+ scale_height = scale_width
89
+ else:
90
+ # fit height
91
+ scale_width = scale_height
92
+ else:
93
+ raise ValueError(f"resize_method {self.__resize_method} not implemented")
94
+
95
+ if self.__resize_method == "lower_bound":
96
+ new_height = self.constrain_to_multiple_of(scale_height * height, min_val=self.__height)
97
+ new_width = self.constrain_to_multiple_of(scale_width * width, min_val=self.__width)
98
+ elif self.__resize_method == "upper_bound":
99
+ new_height = self.constrain_to_multiple_of(scale_height * height, max_val=self.__height)
100
+ new_width = self.constrain_to_multiple_of(scale_width * width, max_val=self.__width)
101
+ elif self.__resize_method == "minimal":
102
+ new_height = self.constrain_to_multiple_of(scale_height * height)
103
+ new_width = self.constrain_to_multiple_of(scale_width * width)
104
+ else:
105
+ raise ValueError(f"resize_method {self.__resize_method} not implemented")
106
+
107
+ return (new_width, new_height)
108
+
109
+ def __call__(self, sample):
110
+ width, height = self.get_size(sample["image"].shape[1], sample["image"].shape[0])
111
+
112
+ # resize sample
113
+ sample["image"] = cv2.resize(sample["image"], (width, height), interpolation=self.__image_interpolation_method)
114
+
115
+ if self.__resize_target:
116
+ if "depth" in sample:
117
+ sample["depth"] = cv2.resize(sample["depth"], (width, height), interpolation=cv2.INTER_NEAREST)
118
+
119
+ if "mask" in sample:
120
+ sample["mask"] = cv2.resize(sample["mask"].astype(np.float32), (width, height), interpolation=cv2.INTER_NEAREST)
121
+
122
+ return sample
123
+
124
+
125
+ class NormalizeImage(object):
126
+ """Normlize image by given mean and std.
127
+ """
128
+
129
+ def __init__(self, mean, std):
130
+ self.__mean = mean
131
+ self.__std = std
132
+
133
+ def __call__(self, sample):
134
+ sample["image"] = (sample["image"] - self.__mean) / self.__std
135
+
136
+ return sample
137
+
138
+
139
+ class PrepareForNet(object):
140
+ """Prepare sample for usage as network input.
141
+ """
142
+
143
+ def __init__(self):
144
+ pass
145
+
146
+ def __call__(self, sample):
147
+ image = np.transpose(sample["image"], (2, 0, 1))
148
+ sample["image"] = np.ascontiguousarray(image).astype(np.float32)
149
+
150
+ if "depth" in sample:
151
+ depth = sample["depth"].astype(np.float32)
152
+ sample["depth"] = np.ascontiguousarray(depth)
153
+
154
+ if "mask" in sample:
155
+ sample["mask"] = sample["mask"].astype(np.float32)
156
+ sample["mask"] = np.ascontiguousarray(sample["mask"])
157
+
158
+ return sample
code/umm/runtime/models/blip3o/model/anchors/DepthAnything/metric_depth/README.md ADDED
@@ -0,0 +1,114 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Depth Anything V2 for Metric Depth Estimation
2
+
3
+ ![teaser](./assets/compare_zoedepth.png)
4
+
5
+ We here provide a simple codebase to fine-tune our Depth Anything V2 pre-trained encoder for metric depth estimation. Built on our powerful encoder, we use a simple DPT head to regress the depth. We fine-tune our pre-trained encoder on synthetic Hypersim / Virtual KITTI datasets for indoor / outdoor metric depth estimation, respectively.
6
+
7
+
8
+ # Pre-trained Models
9
+
10
+ We provide **six metric depth models** of three scales for indoor and outdoor scenes, respectively.
11
+
12
+ | Base Model | Params | Indoor (Hypersim) | Outdoor (Virtual KITTI 2) |
13
+ |:-|-:|:-:|:-:|
14
+ | Depth-Anything-V2-Small | 24.8M | [Download](https://huggingface.co/depth-anything/Depth-Anything-V2-Metric-Hypersim-Small/resolve/main/depth_anything_v2_metric_hypersim_vits.pth?download=true) | [Download](https://huggingface.co/depth-anything/Depth-Anything-V2-Metric-VKITTI-Small/resolve/main/depth_anything_v2_metric_vkitti_vits.pth?download=true) |
15
+ | Depth-Anything-V2-Base | 97.5M | [Download](https://huggingface.co/depth-anything/Depth-Anything-V2-Metric-Hypersim-Base/resolve/main/depth_anything_v2_metric_hypersim_vitb.pth?download=true) | [Download](https://huggingface.co/depth-anything/Depth-Anything-V2-Metric-VKITTI-Base/resolve/main/depth_anything_v2_metric_vkitti_vitb.pth?download=true) |
16
+ | Depth-Anything-V2-Large | 335.3M | [Download](https://huggingface.co/depth-anything/Depth-Anything-V2-Metric-Hypersim-Large/resolve/main/depth_anything_v2_metric_hypersim_vitl.pth?download=true) | [Download](https://huggingface.co/depth-anything/Depth-Anything-V2-Metric-VKITTI-Large/resolve/main/depth_anything_v2_metric_vkitti_vitl.pth?download=true) |
17
+
18
+ *We recommend to first try our larger models (if computational cost is affordable) and the indoor version.*
19
+
20
+ ## Usage
21
+
22
+ ### Prepraration
23
+
24
+ ```bash
25
+ git clone https://github.com/DepthAnything/Depth-Anything-V2
26
+ cd Depth-Anything-V2/metric_depth
27
+ pip install -r requirements.txt
28
+ ```
29
+
30
+ Download the checkpoints listed [here](#pre-trained-models) and put them under the `checkpoints` directory.
31
+
32
+ ### Use our models
33
+ ```python
34
+ import cv2
35
+ import torch
36
+
37
+ from depth_anything_v2.dpt import DepthAnythingV2
38
+
39
+ model_configs = {
40
+ 'vits': {'encoder': 'vits', 'features': 64, 'out_channels': [48, 96, 192, 384]},
41
+ 'vitb': {'encoder': 'vitb', 'features': 128, 'out_channels': [96, 192, 384, 768]},
42
+ 'vitl': {'encoder': 'vitl', 'features': 256, 'out_channels': [256, 512, 1024, 1024]}
43
+ }
44
+
45
+ encoder = 'vitl' # or 'vits', 'vitb'
46
+ dataset = 'hypersim' # 'hypersim' for indoor model, 'vkitti' for outdoor model
47
+ max_depth = 20 # 20 for indoor model, 80 for outdoor model
48
+
49
+ model = DepthAnythingV2(**{**model_configs[encoder], 'max_depth': max_depth})
50
+ model.load_state_dict(torch.load(f'checkpoints/depth_anything_v2_metric_{dataset}_{encoder}.pth', map_location='cpu'))
51
+ model.eval()
52
+
53
+ raw_img = cv2.imread('your/image/path')
54
+ depth = model.infer_image(raw_img) # HxW depth map in meters in numpy
55
+ ```
56
+
57
+ ### Running script on images
58
+
59
+ Here, we take the `vitl` encoder as an example. You can also use `vitb` or `vits` encoders.
60
+
61
+ ```bash
62
+ # indoor scenes
63
+ python run.py \
64
+ --encoder vitl \
65
+ --load-from checkpoints/depth_anything_v2_metric_hypersim_vitl.pth \
66
+ --max-depth 20 \
67
+ --img-path <path> --outdir <outdir> [--input-size <size>] [--save-numpy]
68
+
69
+ # outdoor scenes
70
+ python run.py \
71
+ --encoder vitl \
72
+ --load-from checkpoints/depth_anything_v2_metric_vkitti_vitl.pth \
73
+ --max-depth 80 \
74
+ --img-path <path> --outdir <outdir> [--input-size <size>] [--save-numpy]
75
+ ```
76
+
77
+ ### Project 2D images to point clouds:
78
+
79
+ ```bash
80
+ python depth_to_pointcloud.py \
81
+ --encoder vitl \
82
+ --load-from checkpoints/depth_anything_v2_metric_hypersim_vitl.pth \
83
+ --max-depth 20 \
84
+ --img-path <path> --outdir <outdir>
85
+ ```
86
+
87
+ ### Reproduce training
88
+
89
+ Please first prepare the [Hypersim](https://github.com/apple/ml-hypersim) and [Virtual KITTI 2](https://europe.naverlabs.com/research/computer-vision/proxy-virtual-worlds-vkitti-2/) datasets. Then:
90
+
91
+ ```bash
92
+ bash dist_train.sh
93
+ ```
94
+
95
+
96
+ ## Citation
97
+
98
+ If you find this project useful, please consider citing:
99
+
100
+ ```bibtex
101
+ @article{depth_anything_v2,
102
+ title={Depth Anything V2},
103
+ author={Yang, Lihe and Kang, Bingyi and Huang, Zilong and Zhao, Zhen and Xu, Xiaogang and Feng, Jiashi and Zhao, Hengshuang},
104
+ journal={arXiv:2406.09414},
105
+ year={2024}
106
+ }
107
+
108
+ @inproceedings{depth_anything_v1,
109
+ title={Depth Anything: Unleashing the Power of Large-Scale Unlabeled Data},
110
+ author={Yang, Lihe and Kang, Bingyi and Huang, Zilong and Xu, Xiaogang and Feng, Jiashi and Zhao, Hengshuang},
111
+ booktitle={CVPR},
112
+ year={2024}
113
+ }
114
+ ```
code/umm/runtime/models/blip3o/model/anchors/DepthAnything/metric_depth/dataset/hypersim.py ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import cv2
2
+ import h5py
3
+ import numpy as np
4
+ import torch
5
+ from torch.utils.data import Dataset
6
+ from torchvision.transforms import Compose
7
+
8
+ from dataset.transform import Resize, NormalizeImage, PrepareForNet, Crop
9
+
10
+
11
+ def hypersim_distance_to_depth(npyDistance):
12
+ intWidth, intHeight, fltFocal = 1024, 768, 886.81
13
+
14
+ npyImageplaneX = np.linspace((-0.5 * intWidth) + 0.5, (0.5 * intWidth) - 0.5, intWidth).reshape(
15
+ 1, intWidth).repeat(intHeight, 0).astype(np.float32)[:, :, None]
16
+ npyImageplaneY = np.linspace((-0.5 * intHeight) + 0.5, (0.5 * intHeight) - 0.5,
17
+ intHeight).reshape(intHeight, 1).repeat(intWidth, 1).astype(np.float32)[:, :, None]
18
+ npyImageplaneZ = np.full([intHeight, intWidth, 1], fltFocal, np.float32)
19
+ npyImageplane = np.concatenate(
20
+ [npyImageplaneX, npyImageplaneY, npyImageplaneZ], 2)
21
+
22
+ npyDepth = npyDistance / np.linalg.norm(npyImageplane, 2, 2) * fltFocal
23
+ return npyDepth
24
+
25
+
26
+ class Hypersim(Dataset):
27
+ def __init__(self, filelist_path, mode, size=(518, 518)):
28
+
29
+ self.mode = mode
30
+ self.size = size
31
+
32
+ with open(filelist_path, 'r') as f:
33
+ self.filelist = f.read().splitlines()
34
+
35
+ net_w, net_h = size
36
+ self.transform = Compose([
37
+ Resize(
38
+ width=net_w,
39
+ height=net_h,
40
+ resize_target=True if mode == 'train' else False,
41
+ keep_aspect_ratio=True,
42
+ ensure_multiple_of=14,
43
+ resize_method='lower_bound',
44
+ image_interpolation_method=cv2.INTER_CUBIC,
45
+ ),
46
+ NormalizeImage(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
47
+ PrepareForNet(),
48
+ ] + ([Crop(size[0])] if self.mode == 'train' else []))
49
+
50
+ def __getitem__(self, item):
51
+ img_path = self.filelist[item].split(' ')[0]
52
+ depth_path = self.filelist[item].split(' ')[1]
53
+
54
+ image = cv2.imread(img_path)
55
+ image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) / 255.0
56
+
57
+ depth_fd = h5py.File(depth_path, "r")
58
+ distance_meters = np.array(depth_fd['dataset'])
59
+ depth = hypersim_distance_to_depth(distance_meters)
60
+
61
+ sample = self.transform({'image': image, 'depth': depth})
62
+
63
+ sample['image'] = torch.from_numpy(sample['image'])
64
+ sample['depth'] = torch.from_numpy(sample['depth'])
65
+
66
+ sample['valid_mask'] = (torch.isnan(sample['depth']) == 0)
67
+ sample['depth'][sample['valid_mask'] == 0] = 0
68
+
69
+ sample['image_path'] = self.filelist[item].split(' ')[0]
70
+
71
+ return sample
72
+
73
+ def __len__(self):
74
+ return len(self.filelist)
code/umm/runtime/models/blip3o/model/anchors/DepthAnything/metric_depth/dataset/kitti.py ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import cv2
2
+ import torch
3
+ from torch.utils.data import Dataset
4
+ from torchvision.transforms import Compose
5
+
6
+ from dataset.transform import Resize, NormalizeImage, PrepareForNet
7
+
8
+
9
+ class KITTI(Dataset):
10
+ def __init__(self, filelist_path, mode, size=(518, 518)):
11
+ if mode != 'val':
12
+ raise NotImplementedError
13
+
14
+ self.mode = mode
15
+ self.size = size
16
+
17
+ with open(filelist_path, 'r') as f:
18
+ self.filelist = f.read().splitlines()
19
+
20
+ net_w, net_h = size
21
+ self.transform = Compose([
22
+ Resize(
23
+ width=net_w,
24
+ height=net_h,
25
+ resize_target=True if mode == 'train' else False,
26
+ keep_aspect_ratio=True,
27
+ ensure_multiple_of=14,
28
+ resize_method='lower_bound',
29
+ image_interpolation_method=cv2.INTER_CUBIC,
30
+ ),
31
+ NormalizeImage(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
32
+ PrepareForNet(),
33
+ ])
34
+
35
+ def __getitem__(self, item):
36
+ img_path = self.filelist[item].split(' ')[0]
37
+ depth_path = self.filelist[item].split(' ')[1]
38
+
39
+ image = cv2.imread(img_path)
40
+ image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) / 255.0
41
+
42
+ depth = cv2.imread(depth_path, cv2.IMREAD_UNCHANGED).astype('float32')
43
+
44
+ sample = self.transform({'image': image, 'depth': depth})
45
+
46
+ sample['image'] = torch.from_numpy(sample['image'])
47
+ sample['depth'] = torch.from_numpy(sample['depth'])
48
+ sample['depth'] = sample['depth'] / 256.0 # convert in meters
49
+
50
+ sample['valid_mask'] = sample['depth'] > 0
51
+
52
+ sample['image_path'] = self.filelist[item].split(' ')[0]
53
+
54
+ return sample
55
+
56
+ def __len__(self):
57
+ return len(self.filelist)
code/umm/runtime/models/blip3o/model/anchors/DepthAnything/metric_depth/dataset/transform.py ADDED
@@ -0,0 +1,277 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import cv2
2
+ import math
3
+ import numpy as np
4
+ import torch
5
+ import torch.nn.functional as F
6
+
7
+
8
+ def apply_min_size(sample, size, image_interpolation_method=cv2.INTER_AREA):
9
+ """Rezise the sample to ensure the given size. Keeps aspect ratio.
10
+
11
+ Args:
12
+ sample (dict): sample
13
+ size (tuple): image size
14
+
15
+ Returns:
16
+ tuple: new size
17
+ """
18
+ shape = list(sample["disparity"].shape)
19
+
20
+ if shape[0] >= size[0] and shape[1] >= size[1]:
21
+ return sample
22
+
23
+ scale = [0, 0]
24
+ scale[0] = size[0] / shape[0]
25
+ scale[1] = size[1] / shape[1]
26
+
27
+ scale = max(scale)
28
+
29
+ shape[0] = math.ceil(scale * shape[0])
30
+ shape[1] = math.ceil(scale * shape[1])
31
+
32
+ # resize
33
+ sample["image"] = cv2.resize(
34
+ sample["image"], tuple(shape[::-1]), interpolation=image_interpolation_method
35
+ )
36
+
37
+ sample["disparity"] = cv2.resize(
38
+ sample["disparity"], tuple(shape[::-1]), interpolation=cv2.INTER_NEAREST
39
+ )
40
+ sample["mask"] = cv2.resize(
41
+ sample["mask"].astype(np.float32),
42
+ tuple(shape[::-1]),
43
+ interpolation=cv2.INTER_NEAREST,
44
+ )
45
+ sample["mask"] = sample["mask"].astype(bool)
46
+
47
+ return tuple(shape)
48
+
49
+
50
+ class Resize(object):
51
+ """Resize sample to given size (width, height).
52
+ """
53
+
54
+ def __init__(
55
+ self,
56
+ width,
57
+ height,
58
+ resize_target=True,
59
+ keep_aspect_ratio=False,
60
+ ensure_multiple_of=1,
61
+ resize_method="lower_bound",
62
+ image_interpolation_method=cv2.INTER_AREA,
63
+ ):
64
+ """Init.
65
+
66
+ Args:
67
+ width (int): desired output width
68
+ height (int): desired output height
69
+ resize_target (bool, optional):
70
+ True: Resize the full sample (image, mask, target).
71
+ False: Resize image only.
72
+ Defaults to True.
73
+ keep_aspect_ratio (bool, optional):
74
+ True: Keep the aspect ratio of the input sample.
75
+ Output sample might not have the given width and height, and
76
+ resize behaviour depends on the parameter 'resize_method'.
77
+ Defaults to False.
78
+ ensure_multiple_of (int, optional):
79
+ Output width and height is constrained to be multiple of this parameter.
80
+ Defaults to 1.
81
+ resize_method (str, optional):
82
+ "lower_bound": Output will be at least as large as the given size.
83
+ "upper_bound": Output will be at max as large as the given size. (Output size might be smaller than given size.)
84
+ "minimal": Scale as least as possible. (Output size might be smaller than given size.)
85
+ Defaults to "lower_bound".
86
+ """
87
+ self.__width = width
88
+ self.__height = height
89
+
90
+ self.__resize_target = resize_target
91
+ self.__keep_aspect_ratio = keep_aspect_ratio
92
+ self.__multiple_of = ensure_multiple_of
93
+ self.__resize_method = resize_method
94
+ self.__image_interpolation_method = image_interpolation_method
95
+
96
+ def constrain_to_multiple_of(self, x, min_val=0, max_val=None):
97
+ y = (np.round(x / self.__multiple_of) * self.__multiple_of).astype(int)
98
+
99
+ if max_val is not None and y > max_val:
100
+ y = (np.floor(x / self.__multiple_of) * self.__multiple_of).astype(int)
101
+
102
+ if y < min_val:
103
+ y = (np.ceil(x / self.__multiple_of) * self.__multiple_of).astype(int)
104
+
105
+ return y
106
+
107
+ def get_size(self, width, height):
108
+ # determine new height and width
109
+ scale_height = self.__height / height
110
+ scale_width = self.__width / width
111
+
112
+ if self.__keep_aspect_ratio:
113
+ if self.__resize_method == "lower_bound":
114
+ # scale such that output size is lower bound
115
+ if scale_width > scale_height:
116
+ # fit width
117
+ scale_height = scale_width
118
+ else:
119
+ # fit height
120
+ scale_width = scale_height
121
+ elif self.__resize_method == "upper_bound":
122
+ # scale such that output size is upper bound
123
+ if scale_width < scale_height:
124
+ # fit width
125
+ scale_height = scale_width
126
+ else:
127
+ # fit height
128
+ scale_width = scale_height
129
+ elif self.__resize_method == "minimal":
130
+ # scale as least as possbile
131
+ if abs(1 - scale_width) < abs(1 - scale_height):
132
+ # fit width
133
+ scale_height = scale_width
134
+ else:
135
+ # fit height
136
+ scale_width = scale_height
137
+ else:
138
+ raise ValueError(
139
+ f"resize_method {self.__resize_method} not implemented"
140
+ )
141
+
142
+ if self.__resize_method == "lower_bound":
143
+ new_height = self.constrain_to_multiple_of(
144
+ scale_height * height, min_val=self.__height
145
+ )
146
+ new_width = self.constrain_to_multiple_of(
147
+ scale_width * width, min_val=self.__width
148
+ )
149
+ elif self.__resize_method == "upper_bound":
150
+ new_height = self.constrain_to_multiple_of(
151
+ scale_height * height, max_val=self.__height
152
+ )
153
+ new_width = self.constrain_to_multiple_of(
154
+ scale_width * width, max_val=self.__width
155
+ )
156
+ elif self.__resize_method == "minimal":
157
+ new_height = self.constrain_to_multiple_of(scale_height * height)
158
+ new_width = self.constrain_to_multiple_of(scale_width * width)
159
+ else:
160
+ raise ValueError(f"resize_method {self.__resize_method} not implemented")
161
+
162
+ return (new_width, new_height)
163
+
164
+ def __call__(self, sample):
165
+ width, height = self.get_size(
166
+ sample["image"].shape[1], sample["image"].shape[0]
167
+ )
168
+
169
+ # resize sample
170
+ sample["image"] = cv2.resize(
171
+ sample["image"],
172
+ (width, height),
173
+ interpolation=self.__image_interpolation_method,
174
+ )
175
+
176
+ if self.__resize_target:
177
+ if "disparity" in sample:
178
+ sample["disparity"] = cv2.resize(
179
+ sample["disparity"],
180
+ (width, height),
181
+ interpolation=cv2.INTER_NEAREST,
182
+ )
183
+
184
+ if "depth" in sample:
185
+ sample["depth"] = cv2.resize(
186
+ sample["depth"], (width, height), interpolation=cv2.INTER_NEAREST
187
+ )
188
+
189
+ if "semseg_mask" in sample:
190
+ # sample["semseg_mask"] = cv2.resize(
191
+ # sample["semseg_mask"], (width, height), interpolation=cv2.INTER_NEAREST
192
+ # )
193
+ sample["semseg_mask"] = F.interpolate(torch.from_numpy(sample["semseg_mask"]).float()[None, None, ...], (height, width), mode='nearest').numpy()[0, 0]
194
+
195
+ if "mask" in sample:
196
+ sample["mask"] = cv2.resize(
197
+ sample["mask"].astype(np.float32),
198
+ (width, height),
199
+ interpolation=cv2.INTER_NEAREST,
200
+ )
201
+ # sample["mask"] = sample["mask"].astype(bool)
202
+
203
+ # print(sample['image'].shape, sample['depth'].shape)
204
+ return sample
205
+
206
+
207
+ class NormalizeImage(object):
208
+ """Normlize image by given mean and std.
209
+ """
210
+
211
+ def __init__(self, mean, std):
212
+ self.__mean = mean
213
+ self.__std = std
214
+
215
+ def __call__(self, sample):
216
+ sample["image"] = (sample["image"] - self.__mean) / self.__std
217
+
218
+ return sample
219
+
220
+
221
+ class PrepareForNet(object):
222
+ """Prepare sample for usage as network input.
223
+ """
224
+
225
+ def __init__(self):
226
+ pass
227
+
228
+ def __call__(self, sample):
229
+ image = np.transpose(sample["image"], (2, 0, 1))
230
+ sample["image"] = np.ascontiguousarray(image).astype(np.float32)
231
+
232
+ if "mask" in sample:
233
+ sample["mask"] = sample["mask"].astype(np.float32)
234
+ sample["mask"] = np.ascontiguousarray(sample["mask"])
235
+
236
+ if "depth" in sample:
237
+ depth = sample["depth"].astype(np.float32)
238
+ sample["depth"] = np.ascontiguousarray(depth)
239
+
240
+ if "semseg_mask" in sample:
241
+ sample["semseg_mask"] = sample["semseg_mask"].astype(np.float32)
242
+ sample["semseg_mask"] = np.ascontiguousarray(sample["semseg_mask"])
243
+
244
+ return sample
245
+
246
+
247
+ class Crop(object):
248
+ """Crop sample for batch-wise training. Image is of shape CxHxW
249
+ """
250
+
251
+ def __init__(self, size):
252
+ if isinstance(size, int):
253
+ self.size = (size, size)
254
+ else:
255
+ self.size = size
256
+
257
+ def __call__(self, sample):
258
+ h, w = sample['image'].shape[-2:]
259
+ assert h >= self.size[0] and w >= self.size[1], 'Wrong size'
260
+
261
+ h_start = np.random.randint(0, h - self.size[0] + 1)
262
+ w_start = np.random.randint(0, w - self.size[1] + 1)
263
+ h_end = h_start + self.size[0]
264
+ w_end = w_start + self.size[1]
265
+
266
+ sample['image'] = sample['image'][:, h_start: h_end, w_start: w_end]
267
+
268
+ if "depth" in sample:
269
+ sample["depth"] = sample["depth"][h_start: h_end, w_start: w_end]
270
+
271
+ if "mask" in sample:
272
+ sample["mask"] = sample["mask"][h_start: h_end, w_start: w_end]
273
+
274
+ if "semseg_mask" in sample:
275
+ sample["semseg_mask"] = sample["semseg_mask"][h_start: h_end, w_start: w_end]
276
+
277
+ return sample
code/umm/runtime/models/blip3o/model/anchors/DepthAnything/metric_depth/dataset/vkitti2.py ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import cv2
2
+ import torch
3
+ from torch.utils.data import Dataset
4
+ from torchvision.transforms import Compose
5
+
6
+ from dataset.transform import Resize, NormalizeImage, PrepareForNet, Crop
7
+
8
+
9
+ class VKITTI2(Dataset):
10
+ def __init__(self, filelist_path, mode, size=(518, 518)):
11
+
12
+ self.mode = mode
13
+ self.size = size
14
+
15
+ with open(filelist_path, 'r') as f:
16
+ self.filelist = f.read().splitlines()
17
+
18
+ net_w, net_h = size
19
+ self.transform = Compose([
20
+ Resize(
21
+ width=net_w,
22
+ height=net_h,
23
+ resize_target=True if mode == 'train' else False,
24
+ keep_aspect_ratio=True,
25
+ ensure_multiple_of=14,
26
+ resize_method='lower_bound',
27
+ image_interpolation_method=cv2.INTER_CUBIC,
28
+ ),
29
+ NormalizeImage(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
30
+ PrepareForNet(),
31
+ ] + ([Crop(size[0])] if self.mode == 'train' else []))
32
+
33
+ def __getitem__(self, item):
34
+ img_path = self.filelist[item].split(' ')[0]
35
+ depth_path = self.filelist[item].split(' ')[1]
36
+
37
+ image = cv2.imread(img_path)
38
+ image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) / 255.0
39
+
40
+ depth = cv2.imread(depth_path, cv2.IMREAD_ANYCOLOR | cv2.IMREAD_ANYDEPTH) / 100.0 # cm to m
41
+
42
+ sample = self.transform({'image': image, 'depth': depth})
43
+
44
+ sample['image'] = torch.from_numpy(sample['image'])
45
+ sample['depth'] = torch.from_numpy(sample['depth'])
46
+
47
+ sample['valid_mask'] = (sample['depth'] <= 80)
48
+
49
+ sample['image_path'] = self.filelist[item].split(' ')[0]
50
+
51
+ return sample
52
+
53
+ def __len__(self):
54
+ return len(self.filelist)
code/umm/runtime/models/blip3o/model/anchors/DepthAnything/metric_depth/depth_anything_v2/dinov2.py ADDED
@@ -0,0 +1,415 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Meta Platforms, Inc. and affiliates.
2
+ #
3
+ # This source code is licensed under the Apache License, Version 2.0
4
+ # found in the LICENSE file in the root directory of this source tree.
5
+
6
+ # References:
7
+ # https://github.com/facebookresearch/dino/blob/main/vision_transformer.py
8
+ # https://github.com/rwightman/pytorch-image-models/tree/master/timm/models/vision_transformer.py
9
+
10
+ from functools import partial
11
+ import math
12
+ import logging
13
+ from typing import Sequence, Tuple, Union, Callable
14
+
15
+ import torch
16
+ import torch.nn as nn
17
+ import torch.utils.checkpoint
18
+ from torch.nn.init import trunc_normal_
19
+
20
+ from .dinov2_layers import Mlp, PatchEmbed, SwiGLUFFNFused, MemEffAttention, NestedTensorBlock as Block
21
+
22
+
23
+ logger = logging.getLogger("dinov2")
24
+
25
+
26
+ def named_apply(fn: Callable, module: nn.Module, name="", depth_first=True, include_root=False) -> nn.Module:
27
+ if not depth_first and include_root:
28
+ fn(module=module, name=name)
29
+ for child_name, child_module in module.named_children():
30
+ child_name = ".".join((name, child_name)) if name else child_name
31
+ named_apply(fn=fn, module=child_module, name=child_name, depth_first=depth_first, include_root=True)
32
+ if depth_first and include_root:
33
+ fn(module=module, name=name)
34
+ return module
35
+
36
+
37
+ class BlockChunk(nn.ModuleList):
38
+ def forward(self, x):
39
+ for b in self:
40
+ x = b(x)
41
+ return x
42
+
43
+
44
+ class DinoVisionTransformer(nn.Module):
45
+ def __init__(
46
+ self,
47
+ img_size=224,
48
+ patch_size=16,
49
+ in_chans=3,
50
+ embed_dim=768,
51
+ depth=12,
52
+ num_heads=12,
53
+ mlp_ratio=4.0,
54
+ qkv_bias=True,
55
+ ffn_bias=True,
56
+ proj_bias=True,
57
+ drop_path_rate=0.0,
58
+ drop_path_uniform=False,
59
+ init_values=None, # for layerscale: None or 0 => no layerscale
60
+ embed_layer=PatchEmbed,
61
+ act_layer=nn.GELU,
62
+ block_fn=Block,
63
+ ffn_layer="mlp",
64
+ block_chunks=1,
65
+ num_register_tokens=0,
66
+ interpolate_antialias=False,
67
+ interpolate_offset=0.1,
68
+ ):
69
+ """
70
+ Args:
71
+ img_size (int, tuple): input image size
72
+ patch_size (int, tuple): patch size
73
+ in_chans (int): number of input channels
74
+ embed_dim (int): embedding dimension
75
+ depth (int): depth of transformer
76
+ num_heads (int): number of attention heads
77
+ mlp_ratio (int): ratio of mlp hidden dim to embedding dim
78
+ qkv_bias (bool): enable bias for qkv if True
79
+ proj_bias (bool): enable bias for proj in attn if True
80
+ ffn_bias (bool): enable bias for ffn if True
81
+ drop_path_rate (float): stochastic depth rate
82
+ drop_path_uniform (bool): apply uniform drop rate across blocks
83
+ weight_init (str): weight init scheme
84
+ init_values (float): layer-scale init values
85
+ embed_layer (nn.Module): patch embedding layer
86
+ act_layer (nn.Module): MLP activation layer
87
+ block_fn (nn.Module): transformer block class
88
+ ffn_layer (str): "mlp", "swiglu", "swiglufused" or "identity"
89
+ block_chunks: (int) split block sequence into block_chunks units for FSDP wrap
90
+ num_register_tokens: (int) number of extra cls tokens (so-called "registers")
91
+ interpolate_antialias: (str) flag to apply anti-aliasing when interpolating positional embeddings
92
+ interpolate_offset: (float) work-around offset to apply when interpolating positional embeddings
93
+ """
94
+ super().__init__()
95
+ norm_layer = partial(nn.LayerNorm, eps=1e-6)
96
+
97
+ self.num_features = self.embed_dim = embed_dim # num_features for consistency with other models
98
+ self.num_tokens = 1
99
+ self.n_blocks = depth
100
+ self.num_heads = num_heads
101
+ self.patch_size = patch_size
102
+ self.num_register_tokens = num_register_tokens
103
+ self.interpolate_antialias = interpolate_antialias
104
+ self.interpolate_offset = interpolate_offset
105
+
106
+ self.patch_embed = embed_layer(img_size=img_size, patch_size=patch_size, in_chans=in_chans, embed_dim=embed_dim)
107
+ num_patches = self.patch_embed.num_patches
108
+
109
+ self.cls_token = nn.Parameter(torch.zeros(1, 1, embed_dim))
110
+ self.pos_embed = nn.Parameter(torch.zeros(1, num_patches + self.num_tokens, embed_dim))
111
+ assert num_register_tokens >= 0
112
+ self.register_tokens = (
113
+ nn.Parameter(torch.zeros(1, num_register_tokens, embed_dim)) if num_register_tokens else None
114
+ )
115
+
116
+ if drop_path_uniform is True:
117
+ dpr = [drop_path_rate] * depth
118
+ else:
119
+ dpr = [x.item() for x in torch.linspace(0, drop_path_rate, depth)] # stochastic depth decay rule
120
+
121
+ if ffn_layer == "mlp":
122
+ logger.info("using MLP layer as FFN")
123
+ ffn_layer = Mlp
124
+ elif ffn_layer == "swiglufused" or ffn_layer == "swiglu":
125
+ logger.info("using SwiGLU layer as FFN")
126
+ ffn_layer = SwiGLUFFNFused
127
+ elif ffn_layer == "identity":
128
+ logger.info("using Identity layer as FFN")
129
+
130
+ def f(*args, **kwargs):
131
+ return nn.Identity()
132
+
133
+ ffn_layer = f
134
+ else:
135
+ raise NotImplementedError
136
+
137
+ blocks_list = [
138
+ block_fn(
139
+ dim=embed_dim,
140
+ num_heads=num_heads,
141
+ mlp_ratio=mlp_ratio,
142
+ qkv_bias=qkv_bias,
143
+ proj_bias=proj_bias,
144
+ ffn_bias=ffn_bias,
145
+ drop_path=dpr[i],
146
+ norm_layer=norm_layer,
147
+ act_layer=act_layer,
148
+ ffn_layer=ffn_layer,
149
+ init_values=init_values,
150
+ )
151
+ for i in range(depth)
152
+ ]
153
+ if block_chunks > 0:
154
+ self.chunked_blocks = True
155
+ chunked_blocks = []
156
+ chunksize = depth // block_chunks
157
+ for i in range(0, depth, chunksize):
158
+ # this is to keep the block index consistent if we chunk the block list
159
+ chunked_blocks.append([nn.Identity()] * i + blocks_list[i : i + chunksize])
160
+ self.blocks = nn.ModuleList([BlockChunk(p) for p in chunked_blocks])
161
+ else:
162
+ self.chunked_blocks = False
163
+ self.blocks = nn.ModuleList(blocks_list)
164
+
165
+ self.norm = norm_layer(embed_dim)
166
+ self.head = nn.Identity()
167
+
168
+ self.mask_token = nn.Parameter(torch.zeros(1, embed_dim))
169
+
170
+ self.init_weights()
171
+
172
+ def init_weights(self):
173
+ trunc_normal_(self.pos_embed, std=0.02)
174
+ nn.init.normal_(self.cls_token, std=1e-6)
175
+ if self.register_tokens is not None:
176
+ nn.init.normal_(self.register_tokens, std=1e-6)
177
+ named_apply(init_weights_vit_timm, self)
178
+
179
+ def interpolate_pos_encoding(self, x, w, h):
180
+ previous_dtype = x.dtype
181
+ npatch = x.shape[1] - 1
182
+ N = self.pos_embed.shape[1] - 1
183
+ if npatch == N and w == h:
184
+ return self.pos_embed
185
+ pos_embed = self.pos_embed.float()
186
+ class_pos_embed = pos_embed[:, 0]
187
+ patch_pos_embed = pos_embed[:, 1:]
188
+ dim = x.shape[-1]
189
+ w0 = w // self.patch_size
190
+ h0 = h // self.patch_size
191
+ # we add a small number to avoid floating point error in the interpolation
192
+ # see discussion at https://github.com/facebookresearch/dino/issues/8
193
+ # DINOv2 with register modify the interpolate_offset from 0.1 to 0.0
194
+ w0, h0 = w0 + self.interpolate_offset, h0 + self.interpolate_offset
195
+ # w0, h0 = w0 + 0.1, h0 + 0.1
196
+
197
+ sqrt_N = math.sqrt(N)
198
+ sx, sy = float(w0) / sqrt_N, float(h0) / sqrt_N
199
+ patch_pos_embed = nn.functional.interpolate(
200
+ patch_pos_embed.reshape(1, int(sqrt_N), int(sqrt_N), dim).permute(0, 3, 1, 2),
201
+ scale_factor=(sx, sy),
202
+ # (int(w0), int(h0)), # to solve the upsampling shape issue
203
+ mode="bicubic",
204
+ antialias=self.interpolate_antialias
205
+ )
206
+
207
+ assert int(w0) == patch_pos_embed.shape[-2]
208
+ assert int(h0) == patch_pos_embed.shape[-1]
209
+ patch_pos_embed = patch_pos_embed.permute(0, 2, 3, 1).view(1, -1, dim)
210
+ return torch.cat((class_pos_embed.unsqueeze(0), patch_pos_embed), dim=1).to(previous_dtype)
211
+
212
+ def prepare_tokens_with_masks(self, x, masks=None):
213
+ B, nc, w, h = x.shape
214
+ x = self.patch_embed(x)
215
+ if masks is not None:
216
+ x = torch.where(masks.unsqueeze(-1), self.mask_token.to(x.dtype).unsqueeze(0), x)
217
+
218
+ x = torch.cat((self.cls_token.expand(x.shape[0], -1, -1), x), dim=1)
219
+ x = x + self.interpolate_pos_encoding(x, w, h)
220
+
221
+ if self.register_tokens is not None:
222
+ x = torch.cat(
223
+ (
224
+ x[:, :1],
225
+ self.register_tokens.expand(x.shape[0], -1, -1),
226
+ x[:, 1:],
227
+ ),
228
+ dim=1,
229
+ )
230
+
231
+ return x
232
+
233
+ def forward_features_list(self, x_list, masks_list):
234
+ x = [self.prepare_tokens_with_masks(x, masks) for x, masks in zip(x_list, masks_list)]
235
+ for blk in self.blocks:
236
+ x = blk(x)
237
+
238
+ all_x = x
239
+ output = []
240
+ for x, masks in zip(all_x, masks_list):
241
+ x_norm = self.norm(x)
242
+ output.append(
243
+ {
244
+ "x_norm_clstoken": x_norm[:, 0],
245
+ "x_norm_regtokens": x_norm[:, 1 : self.num_register_tokens + 1],
246
+ "x_norm_patchtokens": x_norm[:, self.num_register_tokens + 1 :],
247
+ "x_prenorm": x,
248
+ "masks": masks,
249
+ }
250
+ )
251
+ return output
252
+
253
+ def forward_features(self, x, masks=None):
254
+ if isinstance(x, list):
255
+ return self.forward_features_list(x, masks)
256
+
257
+ x = self.prepare_tokens_with_masks(x, masks)
258
+
259
+ for blk in self.blocks:
260
+ x = blk(x)
261
+
262
+ x_norm = self.norm(x)
263
+ return {
264
+ "x_norm_clstoken": x_norm[:, 0],
265
+ "x_norm_regtokens": x_norm[:, 1 : self.num_register_tokens + 1],
266
+ "x_norm_patchtokens": x_norm[:, self.num_register_tokens + 1 :],
267
+ "x_prenorm": x,
268
+ "masks": masks,
269
+ }
270
+
271
+ def _get_intermediate_layers_not_chunked(self, x, n=1):
272
+ x = self.prepare_tokens_with_masks(x)
273
+ # If n is an int, take the n last blocks. If it's a list, take them
274
+ output, total_block_len = [], len(self.blocks)
275
+ blocks_to_take = range(total_block_len - n, total_block_len) if isinstance(n, int) else n
276
+ for i, blk in enumerate(self.blocks):
277
+ x = blk(x)
278
+ if i in blocks_to_take:
279
+ output.append(x)
280
+ assert len(output) == len(blocks_to_take), f"only {len(output)} / {len(blocks_to_take)} blocks found"
281
+ return output
282
+
283
+ def _get_intermediate_layers_chunked(self, x, n=1):
284
+ x = self.prepare_tokens_with_masks(x)
285
+ output, i, total_block_len = [], 0, len(self.blocks[-1])
286
+ # If n is an int, take the n last blocks. If it's a list, take them
287
+ blocks_to_take = range(total_block_len - n, total_block_len) if isinstance(n, int) else n
288
+ for block_chunk in self.blocks:
289
+ for blk in block_chunk[i:]: # Passing the nn.Identity()
290
+ x = blk(x)
291
+ if i in blocks_to_take:
292
+ output.append(x)
293
+ i += 1
294
+ assert len(output) == len(blocks_to_take), f"only {len(output)} / {len(blocks_to_take)} blocks found"
295
+ return output
296
+
297
+ def get_intermediate_layers(
298
+ self,
299
+ x: torch.Tensor,
300
+ n: Union[int, Sequence] = 1, # Layers or n last layers to take
301
+ reshape: bool = False,
302
+ return_class_token: bool = False,
303
+ norm=True
304
+ ) -> Tuple[Union[torch.Tensor, Tuple[torch.Tensor]]]:
305
+ if self.chunked_blocks:
306
+ outputs = self._get_intermediate_layers_chunked(x, n)
307
+ else:
308
+ outputs = self._get_intermediate_layers_not_chunked(x, n)
309
+ if norm:
310
+ outputs = [self.norm(out) for out in outputs]
311
+ class_tokens = [out[:, 0] for out in outputs]
312
+ outputs = [out[:, 1 + self.num_register_tokens:] for out in outputs]
313
+ if reshape:
314
+ B, _, w, h = x.shape
315
+ outputs = [
316
+ out.reshape(B, w // self.patch_size, h // self.patch_size, -1).permute(0, 3, 1, 2).contiguous()
317
+ for out in outputs
318
+ ]
319
+ if return_class_token:
320
+ return tuple(zip(outputs, class_tokens))
321
+ return tuple(outputs)
322
+
323
+ def forward(self, *args, is_training=False, **kwargs):
324
+ ret = self.forward_features(*args, **kwargs)
325
+ if is_training:
326
+ return ret
327
+ else:
328
+ return self.head(ret["x_norm_clstoken"])
329
+
330
+
331
+ def init_weights_vit_timm(module: nn.Module, name: str = ""):
332
+ """ViT weight initialization, original timm impl (for reproducibility)"""
333
+ if isinstance(module, nn.Linear):
334
+ trunc_normal_(module.weight, std=0.02)
335
+ if module.bias is not None:
336
+ nn.init.zeros_(module.bias)
337
+
338
+
339
+ def vit_small(patch_size=16, num_register_tokens=0, **kwargs):
340
+ model = DinoVisionTransformer(
341
+ patch_size=patch_size,
342
+ embed_dim=384,
343
+ depth=12,
344
+ num_heads=6,
345
+ mlp_ratio=4,
346
+ block_fn=partial(Block, attn_class=MemEffAttention),
347
+ num_register_tokens=num_register_tokens,
348
+ **kwargs,
349
+ )
350
+ return model
351
+
352
+
353
+ def vit_base(patch_size=16, num_register_tokens=0, **kwargs):
354
+ model = DinoVisionTransformer(
355
+ patch_size=patch_size,
356
+ embed_dim=768,
357
+ depth=12,
358
+ num_heads=12,
359
+ mlp_ratio=4,
360
+ block_fn=partial(Block, attn_class=MemEffAttention),
361
+ num_register_tokens=num_register_tokens,
362
+ **kwargs,
363
+ )
364
+ return model
365
+
366
+
367
+ def vit_large(patch_size=16, num_register_tokens=0, **kwargs):
368
+ model = DinoVisionTransformer(
369
+ patch_size=patch_size,
370
+ embed_dim=1024,
371
+ depth=24,
372
+ num_heads=16,
373
+ mlp_ratio=4,
374
+ block_fn=partial(Block, attn_class=MemEffAttention),
375
+ num_register_tokens=num_register_tokens,
376
+ **kwargs,
377
+ )
378
+ return model
379
+
380
+
381
+ def vit_giant2(patch_size=16, num_register_tokens=0, **kwargs):
382
+ """
383
+ Close to ViT-giant, with embed-dim 1536 and 24 heads => embed-dim per head 64
384
+ """
385
+ model = DinoVisionTransformer(
386
+ patch_size=patch_size,
387
+ embed_dim=1536,
388
+ depth=40,
389
+ num_heads=24,
390
+ mlp_ratio=4,
391
+ block_fn=partial(Block, attn_class=MemEffAttention),
392
+ num_register_tokens=num_register_tokens,
393
+ **kwargs,
394
+ )
395
+ return model
396
+
397
+
398
+ def DINOv2(model_name):
399
+ model_zoo = {
400
+ "vits": vit_small,
401
+ "vitb": vit_base,
402
+ "vitl": vit_large,
403
+ "vitg": vit_giant2
404
+ }
405
+
406
+ return model_zoo[model_name](
407
+ img_size=518,
408
+ patch_size=14,
409
+ init_values=1.0,
410
+ ffn_layer="mlp" if model_name != "vitg" else "swiglufused",
411
+ block_chunks=0,
412
+ num_register_tokens=0,
413
+ interpolate_antialias=False,
414
+ interpolate_offset=0.1
415
+ )
code/umm/runtime/models/blip3o/model/anchors/DepthAnything/metric_depth/depth_anything_v2/dinov2_layers/__init__.py ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Meta Platforms, Inc. and affiliates.
2
+ # All rights reserved.
3
+ #
4
+ # This source code is licensed under the license found in the
5
+ # LICENSE file in the root directory of this source tree.
6
+
7
+ from .mlp import Mlp
8
+ from .patch_embed import PatchEmbed
9
+ from .swiglu_ffn import SwiGLUFFN, SwiGLUFFNFused
10
+ from .block import NestedTensorBlock
11
+ from .attention import MemEffAttention
code/umm/runtime/models/blip3o/model/anchors/DepthAnything/metric_depth/depth_anything_v2/dinov2_layers/attention.py ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Meta Platforms, Inc. and affiliates.
2
+ # All rights reserved.
3
+ #
4
+ # This source code is licensed under the license found in the
5
+ # LICENSE file in the root directory of this source tree.
6
+
7
+ # References:
8
+ # https://github.com/facebookresearch/dino/blob/master/vision_transformer.py
9
+ # https://github.com/rwightman/pytorch-image-models/tree/master/timm/models/vision_transformer.py
10
+
11
+ import logging
12
+
13
+ from torch import Tensor
14
+ from torch import nn
15
+
16
+
17
+ logger = logging.getLogger("dinov2")
18
+
19
+
20
+ try:
21
+ from xformers.ops import memory_efficient_attention, unbind, fmha
22
+
23
+ XFORMERS_AVAILABLE = True
24
+ except ImportError:
25
+ logger.debug("xFormers not available (optional; DINOv2 falls back without it).")
26
+ XFORMERS_AVAILABLE = False
27
+
28
+
29
+ class Attention(nn.Module):
30
+ def __init__(
31
+ self,
32
+ dim: int,
33
+ num_heads: int = 8,
34
+ qkv_bias: bool = False,
35
+ proj_bias: bool = True,
36
+ attn_drop: float = 0.0,
37
+ proj_drop: float = 0.0,
38
+ ) -> None:
39
+ super().__init__()
40
+ self.num_heads = num_heads
41
+ head_dim = dim // num_heads
42
+ self.scale = head_dim**-0.5
43
+
44
+ self.qkv = nn.Linear(dim, dim * 3, bias=qkv_bias)
45
+ self.attn_drop = nn.Dropout(attn_drop)
46
+ self.proj = nn.Linear(dim, dim, bias=proj_bias)
47
+ self.proj_drop = nn.Dropout(proj_drop)
48
+
49
+ def forward(self, x: Tensor) -> Tensor:
50
+ B, N, C = x.shape
51
+ qkv = self.qkv(x).reshape(B, N, 3, self.num_heads, C // self.num_heads).permute(2, 0, 3, 1, 4)
52
+
53
+ q, k, v = qkv[0] * self.scale, qkv[1], qkv[2]
54
+ attn = q @ k.transpose(-2, -1)
55
+
56
+ attn = attn.softmax(dim=-1)
57
+ attn = self.attn_drop(attn)
58
+
59
+ x = (attn @ v).transpose(1, 2).reshape(B, N, C)
60
+ x = self.proj(x)
61
+ x = self.proj_drop(x)
62
+ return x
63
+
64
+
65
+ class MemEffAttention(Attention):
66
+ def forward(self, x: Tensor, attn_bias=None) -> Tensor:
67
+ if not XFORMERS_AVAILABLE:
68
+ assert attn_bias is None, "xFormers is required for nested tensors usage"
69
+ return super().forward(x)
70
+
71
+ B, N, C = x.shape
72
+ qkv = self.qkv(x).reshape(B, N, 3, self.num_heads, C // self.num_heads)
73
+
74
+ q, k, v = unbind(qkv, 2)
75
+
76
+ x = memory_efficient_attention(q, k, v, attn_bias=attn_bias)
77
+ x = x.reshape([B, N, C])
78
+
79
+ x = self.proj(x)
80
+ x = self.proj_drop(x)
81
+ return x
82
+
83
+
code/umm/runtime/models/blip3o/model/anchors/DepthAnything/metric_depth/depth_anything_v2/dinov2_layers/block.py ADDED
@@ -0,0 +1,252 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Meta Platforms, Inc. and affiliates.
2
+ # All rights reserved.
3
+ #
4
+ # This source code is licensed under the license found in the
5
+ # LICENSE file in the root directory of this source tree.
6
+
7
+ # References:
8
+ # https://github.com/facebookresearch/dino/blob/master/vision_transformer.py
9
+ # https://github.com/rwightman/pytorch-image-models/tree/master/timm/layers/patch_embed.py
10
+
11
+ import logging
12
+ from typing import Callable, List, Any, Tuple, Dict
13
+
14
+ import torch
15
+ from torch import nn, Tensor
16
+
17
+ from .attention import Attention, MemEffAttention
18
+ from .drop_path import DropPath
19
+ from .layer_scale import LayerScale
20
+ from .mlp import Mlp
21
+
22
+
23
+ logger = logging.getLogger("dinov2")
24
+
25
+
26
+ try:
27
+ from xformers.ops import fmha
28
+ from xformers.ops import scaled_index_add, index_select_cat
29
+
30
+ XFORMERS_AVAILABLE = True
31
+ except ImportError:
32
+ logger.debug("xFormers not available (optional; DINOv2 falls back without it).")
33
+ XFORMERS_AVAILABLE = False
34
+
35
+
36
+ class Block(nn.Module):
37
+ def __init__(
38
+ self,
39
+ dim: int,
40
+ num_heads: int,
41
+ mlp_ratio: float = 4.0,
42
+ qkv_bias: bool = False,
43
+ proj_bias: bool = True,
44
+ ffn_bias: bool = True,
45
+ drop: float = 0.0,
46
+ attn_drop: float = 0.0,
47
+ init_values=None,
48
+ drop_path: float = 0.0,
49
+ act_layer: Callable[..., nn.Module] = nn.GELU,
50
+ norm_layer: Callable[..., nn.Module] = nn.LayerNorm,
51
+ attn_class: Callable[..., nn.Module] = Attention,
52
+ ffn_layer: Callable[..., nn.Module] = Mlp,
53
+ ) -> None:
54
+ super().__init__()
55
+ # print(f"biases: qkv: {qkv_bias}, proj: {proj_bias}, ffn: {ffn_bias}")
56
+ self.norm1 = norm_layer(dim)
57
+ self.attn = attn_class(
58
+ dim,
59
+ num_heads=num_heads,
60
+ qkv_bias=qkv_bias,
61
+ proj_bias=proj_bias,
62
+ attn_drop=attn_drop,
63
+ proj_drop=drop,
64
+ )
65
+ self.ls1 = LayerScale(dim, init_values=init_values) if init_values else nn.Identity()
66
+ self.drop_path1 = DropPath(drop_path) if drop_path > 0.0 else nn.Identity()
67
+
68
+ self.norm2 = norm_layer(dim)
69
+ mlp_hidden_dim = int(dim * mlp_ratio)
70
+ self.mlp = ffn_layer(
71
+ in_features=dim,
72
+ hidden_features=mlp_hidden_dim,
73
+ act_layer=act_layer,
74
+ drop=drop,
75
+ bias=ffn_bias,
76
+ )
77
+ self.ls2 = LayerScale(dim, init_values=init_values) if init_values else nn.Identity()
78
+ self.drop_path2 = DropPath(drop_path) if drop_path > 0.0 else nn.Identity()
79
+
80
+ self.sample_drop_ratio = drop_path
81
+
82
+ def forward(self, x: Tensor) -> Tensor:
83
+ def attn_residual_func(x: Tensor) -> Tensor:
84
+ return self.ls1(self.attn(self.norm1(x)))
85
+
86
+ def ffn_residual_func(x: Tensor) -> Tensor:
87
+ return self.ls2(self.mlp(self.norm2(x)))
88
+
89
+ if self.training and self.sample_drop_ratio > 0.1:
90
+ # the overhead is compensated only for a drop path rate larger than 0.1
91
+ x = drop_add_residual_stochastic_depth(
92
+ x,
93
+ residual_func=attn_residual_func,
94
+ sample_drop_ratio=self.sample_drop_ratio,
95
+ )
96
+ x = drop_add_residual_stochastic_depth(
97
+ x,
98
+ residual_func=ffn_residual_func,
99
+ sample_drop_ratio=self.sample_drop_ratio,
100
+ )
101
+ elif self.training and self.sample_drop_ratio > 0.0:
102
+ x = x + self.drop_path1(attn_residual_func(x))
103
+ x = x + self.drop_path1(ffn_residual_func(x)) # FIXME: drop_path2
104
+ else:
105
+ x = x + attn_residual_func(x)
106
+ x = x + ffn_residual_func(x)
107
+ return x
108
+
109
+
110
+ def drop_add_residual_stochastic_depth(
111
+ x: Tensor,
112
+ residual_func: Callable[[Tensor], Tensor],
113
+ sample_drop_ratio: float = 0.0,
114
+ ) -> Tensor:
115
+ # 1) extract subset using permutation
116
+ b, n, d = x.shape
117
+ sample_subset_size = max(int(b * (1 - sample_drop_ratio)), 1)
118
+ brange = (torch.randperm(b, device=x.device))[:sample_subset_size]
119
+ x_subset = x[brange]
120
+
121
+ # 2) apply residual_func to get residual
122
+ residual = residual_func(x_subset)
123
+
124
+ x_flat = x.flatten(1)
125
+ residual = residual.flatten(1)
126
+
127
+ residual_scale_factor = b / sample_subset_size
128
+
129
+ # 3) add the residual
130
+ x_plus_residual = torch.index_add(x_flat, 0, brange, residual.to(dtype=x.dtype), alpha=residual_scale_factor)
131
+ return x_plus_residual.view_as(x)
132
+
133
+
134
+ def get_branges_scales(x, sample_drop_ratio=0.0):
135
+ b, n, d = x.shape
136
+ sample_subset_size = max(int(b * (1 - sample_drop_ratio)), 1)
137
+ brange = (torch.randperm(b, device=x.device))[:sample_subset_size]
138
+ residual_scale_factor = b / sample_subset_size
139
+ return brange, residual_scale_factor
140
+
141
+
142
+ def add_residual(x, brange, residual, residual_scale_factor, scaling_vector=None):
143
+ if scaling_vector is None:
144
+ x_flat = x.flatten(1)
145
+ residual = residual.flatten(1)
146
+ x_plus_residual = torch.index_add(x_flat, 0, brange, residual.to(dtype=x.dtype), alpha=residual_scale_factor)
147
+ else:
148
+ x_plus_residual = scaled_index_add(
149
+ x, brange, residual.to(dtype=x.dtype), scaling=scaling_vector, alpha=residual_scale_factor
150
+ )
151
+ return x_plus_residual
152
+
153
+
154
+ attn_bias_cache: Dict[Tuple, Any] = {}
155
+
156
+
157
+ def get_attn_bias_and_cat(x_list, branges=None):
158
+ """
159
+ this will perform the index select, cat the tensors, and provide the attn_bias from cache
160
+ """
161
+ batch_sizes = [b.shape[0] for b in branges] if branges is not None else [x.shape[0] for x in x_list]
162
+ all_shapes = tuple((b, x.shape[1]) for b, x in zip(batch_sizes, x_list))
163
+ if all_shapes not in attn_bias_cache.keys():
164
+ seqlens = []
165
+ for b, x in zip(batch_sizes, x_list):
166
+ for _ in range(b):
167
+ seqlens.append(x.shape[1])
168
+ attn_bias = fmha.BlockDiagonalMask.from_seqlens(seqlens)
169
+ attn_bias._batch_sizes = batch_sizes
170
+ attn_bias_cache[all_shapes] = attn_bias
171
+
172
+ if branges is not None:
173
+ cat_tensors = index_select_cat([x.flatten(1) for x in x_list], branges).view(1, -1, x_list[0].shape[-1])
174
+ else:
175
+ tensors_bs1 = tuple(x.reshape([1, -1, *x.shape[2:]]) for x in x_list)
176
+ cat_tensors = torch.cat(tensors_bs1, dim=1)
177
+
178
+ return attn_bias_cache[all_shapes], cat_tensors
179
+
180
+
181
+ def drop_add_residual_stochastic_depth_list(
182
+ x_list: List[Tensor],
183
+ residual_func: Callable[[Tensor, Any], Tensor],
184
+ sample_drop_ratio: float = 0.0,
185
+ scaling_vector=None,
186
+ ) -> Tensor:
187
+ # 1) generate random set of indices for dropping samples in the batch
188
+ branges_scales = [get_branges_scales(x, sample_drop_ratio=sample_drop_ratio) for x in x_list]
189
+ branges = [s[0] for s in branges_scales]
190
+ residual_scale_factors = [s[1] for s in branges_scales]
191
+
192
+ # 2) get attention bias and index+concat the tensors
193
+ attn_bias, x_cat = get_attn_bias_and_cat(x_list, branges)
194
+
195
+ # 3) apply residual_func to get residual, and split the result
196
+ residual_list = attn_bias.split(residual_func(x_cat, attn_bias=attn_bias)) # type: ignore
197
+
198
+ outputs = []
199
+ for x, brange, residual, residual_scale_factor in zip(x_list, branges, residual_list, residual_scale_factors):
200
+ outputs.append(add_residual(x, brange, residual, residual_scale_factor, scaling_vector).view_as(x))
201
+ return outputs
202
+
203
+
204
+ class NestedTensorBlock(Block):
205
+ def forward_nested(self, x_list: List[Tensor]) -> List[Tensor]:
206
+ """
207
+ x_list contains a list of tensors to nest together and run
208
+ """
209
+ assert isinstance(self.attn, MemEffAttention)
210
+
211
+ if self.training and self.sample_drop_ratio > 0.0:
212
+
213
+ def attn_residual_func(x: Tensor, attn_bias=None) -> Tensor:
214
+ return self.attn(self.norm1(x), attn_bias=attn_bias)
215
+
216
+ def ffn_residual_func(x: Tensor, attn_bias=None) -> Tensor:
217
+ return self.mlp(self.norm2(x))
218
+
219
+ x_list = drop_add_residual_stochastic_depth_list(
220
+ x_list,
221
+ residual_func=attn_residual_func,
222
+ sample_drop_ratio=self.sample_drop_ratio,
223
+ scaling_vector=self.ls1.gamma if isinstance(self.ls1, LayerScale) else None,
224
+ )
225
+ x_list = drop_add_residual_stochastic_depth_list(
226
+ x_list,
227
+ residual_func=ffn_residual_func,
228
+ sample_drop_ratio=self.sample_drop_ratio,
229
+ scaling_vector=self.ls2.gamma if isinstance(self.ls1, LayerScale) else None,
230
+ )
231
+ return x_list
232
+ else:
233
+
234
+ def attn_residual_func(x: Tensor, attn_bias=None) -> Tensor:
235
+ return self.ls1(self.attn(self.norm1(x), attn_bias=attn_bias))
236
+
237
+ def ffn_residual_func(x: Tensor, attn_bias=None) -> Tensor:
238
+ return self.ls2(self.mlp(self.norm2(x)))
239
+
240
+ attn_bias, x = get_attn_bias_and_cat(x_list)
241
+ x = x + attn_residual_func(x, attn_bias=attn_bias)
242
+ x = x + ffn_residual_func(x)
243
+ return attn_bias.split(x)
244
+
245
+ def forward(self, x_or_x_list):
246
+ if isinstance(x_or_x_list, Tensor):
247
+ return super().forward(x_or_x_list)
248
+ elif isinstance(x_or_x_list, list):
249
+ assert XFORMERS_AVAILABLE, "Please install xFormers for nested tensors usage"
250
+ return self.forward_nested(x_or_x_list)
251
+ else:
252
+ raise AssertionError
code/umm/runtime/models/blip3o/model/anchors/DepthAnything/metric_depth/depth_anything_v2/dinov2_layers/drop_path.py ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Meta Platforms, Inc. and affiliates.
2
+ # All rights reserved.
3
+ #
4
+ # This source code is licensed under the license found in the
5
+ # LICENSE file in the root directory of this source tree.
6
+
7
+ # References:
8
+ # https://github.com/facebookresearch/dino/blob/master/vision_transformer.py
9
+ # https://github.com/rwightman/pytorch-image-models/tree/master/timm/layers/drop.py
10
+
11
+
12
+ from torch import nn
13
+
14
+
15
+ def drop_path(x, drop_prob: float = 0.0, training: bool = False):
16
+ if drop_prob == 0.0 or not training:
17
+ return x
18
+ keep_prob = 1 - drop_prob
19
+ shape = (x.shape[0],) + (1,) * (x.ndim - 1) # work with diff dim tensors, not just 2D ConvNets
20
+ random_tensor = x.new_empty(shape).bernoulli_(keep_prob)
21
+ if keep_prob > 0.0:
22
+ random_tensor.div_(keep_prob)
23
+ output = x * random_tensor
24
+ return output
25
+
26
+
27
+ class DropPath(nn.Module):
28
+ """Drop paths (Stochastic Depth) per sample (when applied in main path of residual blocks)."""
29
+
30
+ def __init__(self, drop_prob=None):
31
+ super(DropPath, self).__init__()
32
+ self.drop_prob = drop_prob
33
+
34
+ def forward(self, x):
35
+ return drop_path(x, self.drop_prob, self.training)
code/umm/runtime/models/blip3o/model/anchors/DepthAnything/metric_depth/depth_anything_v2/dinov2_layers/layer_scale.py ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Meta Platforms, Inc. and affiliates.
2
+ # All rights reserved.
3
+ #
4
+ # This source code is licensed under the license found in the
5
+ # LICENSE file in the root directory of this source tree.
6
+
7
+ # Modified from: https://github.com/huggingface/pytorch-image-models/blob/main/timm/models/vision_transformer.py#L103-L110
8
+
9
+ from typing import Union
10
+
11
+ import torch
12
+ from torch import Tensor
13
+ from torch import nn
14
+
15
+
16
+ class LayerScale(nn.Module):
17
+ def __init__(
18
+ self,
19
+ dim: int,
20
+ init_values: Union[float, Tensor] = 1e-5,
21
+ inplace: bool = False,
22
+ ) -> None:
23
+ super().__init__()
24
+ self.inplace = inplace
25
+ self.gamma = nn.Parameter(init_values * torch.ones(dim))
26
+
27
+ def forward(self, x: Tensor) -> Tensor:
28
+ return x.mul_(self.gamma) if self.inplace else x * self.gamma
code/umm/runtime/models/blip3o/model/anchors/DepthAnything/metric_depth/depth_anything_v2/dinov2_layers/mlp.py ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Meta Platforms, Inc. and affiliates.
2
+ # All rights reserved.
3
+ #
4
+ # This source code is licensed under the license found in the
5
+ # LICENSE file in the root directory of this source tree.
6
+
7
+ # References:
8
+ # https://github.com/facebookresearch/dino/blob/master/vision_transformer.py
9
+ # https://github.com/rwightman/pytorch-image-models/tree/master/timm/layers/mlp.py
10
+
11
+
12
+ from typing import Callable, Optional
13
+
14
+ from torch import Tensor, nn
15
+
16
+
17
+ class Mlp(nn.Module):
18
+ def __init__(
19
+ self,
20
+ in_features: int,
21
+ hidden_features: Optional[int] = None,
22
+ out_features: Optional[int] = None,
23
+ act_layer: Callable[..., nn.Module] = nn.GELU,
24
+ drop: float = 0.0,
25
+ bias: bool = True,
26
+ ) -> None:
27
+ super().__init__()
28
+ out_features = out_features or in_features
29
+ hidden_features = hidden_features or in_features
30
+ self.fc1 = nn.Linear(in_features, hidden_features, bias=bias)
31
+ self.act = act_layer()
32
+ self.fc2 = nn.Linear(hidden_features, out_features, bias=bias)
33
+ self.drop = nn.Dropout(drop)
34
+
35
+ def forward(self, x: Tensor) -> Tensor:
36
+ x = self.fc1(x)
37
+ x = self.act(x)
38
+ x = self.drop(x)
39
+ x = self.fc2(x)
40
+ x = self.drop(x)
41
+ return x
code/umm/runtime/models/blip3o/model/anchors/DepthAnything/metric_depth/depth_anything_v2/dinov2_layers/patch_embed.py ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Meta Platforms, Inc. and affiliates.
2
+ # All rights reserved.
3
+ #
4
+ # This source code is licensed under the license found in the
5
+ # LICENSE file in the root directory of this source tree.
6
+
7
+ # References:
8
+ # https://github.com/facebookresearch/dino/blob/master/vision_transformer.py
9
+ # https://github.com/rwightman/pytorch-image-models/tree/master/timm/layers/patch_embed.py
10
+
11
+ from typing import Callable, Optional, Tuple, Union
12
+
13
+ from torch import Tensor
14
+ import torch.nn as nn
15
+
16
+
17
+ def make_2tuple(x):
18
+ if isinstance(x, tuple):
19
+ assert len(x) == 2
20
+ return x
21
+
22
+ assert isinstance(x, int)
23
+ return (x, x)
24
+
25
+
26
+ class PatchEmbed(nn.Module):
27
+ """
28
+ 2D image to patch embedding: (B,C,H,W) -> (B,N,D)
29
+
30
+ Args:
31
+ img_size: Image size.
32
+ patch_size: Patch token size.
33
+ in_chans: Number of input image channels.
34
+ embed_dim: Number of linear projection output channels.
35
+ norm_layer: Normalization layer.
36
+ """
37
+
38
+ def __init__(
39
+ self,
40
+ img_size: Union[int, Tuple[int, int]] = 224,
41
+ patch_size: Union[int, Tuple[int, int]] = 16,
42
+ in_chans: int = 3,
43
+ embed_dim: int = 768,
44
+ norm_layer: Optional[Callable] = None,
45
+ flatten_embedding: bool = True,
46
+ ) -> None:
47
+ super().__init__()
48
+
49
+ image_HW = make_2tuple(img_size)
50
+ patch_HW = make_2tuple(patch_size)
51
+ patch_grid_size = (
52
+ image_HW[0] // patch_HW[0],
53
+ image_HW[1] // patch_HW[1],
54
+ )
55
+
56
+ self.img_size = image_HW
57
+ self.patch_size = patch_HW
58
+ self.patches_resolution = patch_grid_size
59
+ self.num_patches = patch_grid_size[0] * patch_grid_size[1]
60
+
61
+ self.in_chans = in_chans
62
+ self.embed_dim = embed_dim
63
+
64
+ self.flatten_embedding = flatten_embedding
65
+
66
+ self.proj = nn.Conv2d(in_chans, embed_dim, kernel_size=patch_HW, stride=patch_HW)
67
+ self.norm = norm_layer(embed_dim) if norm_layer else nn.Identity()
68
+
69
+ def forward(self, x: Tensor) -> Tensor:
70
+ _, _, H, W = x.shape
71
+ patch_H, patch_W = self.patch_size
72
+
73
+ assert H % patch_H == 0, f"Input image height {H} is not a multiple of patch height {patch_H}"
74
+ assert W % patch_W == 0, f"Input image width {W} is not a multiple of patch width: {patch_W}"
75
+
76
+ x = self.proj(x) # B C H W
77
+ H, W = x.size(2), x.size(3)
78
+ x = x.flatten(2).transpose(1, 2) # B HW C
79
+ x = self.norm(x)
80
+ if not self.flatten_embedding:
81
+ x = x.reshape(-1, H, W, self.embed_dim) # B H W C
82
+ return x
83
+
84
+ def flops(self) -> float:
85
+ Ho, Wo = self.patches_resolution
86
+ flops = Ho * Wo * self.embed_dim * self.in_chans * (self.patch_size[0] * self.patch_size[1])
87
+ if self.norm is not None:
88
+ flops += Ho * Wo * self.embed_dim
89
+ return flops
code/umm/runtime/models/blip3o/model/anchors/DepthAnything/metric_depth/depth_anything_v2/dinov2_layers/swiglu_ffn.py ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Meta Platforms, Inc. and affiliates.
2
+ # All rights reserved.
3
+ #
4
+ # This source code is licensed under the license found in the
5
+ # LICENSE file in the root directory of this source tree.
6
+
7
+ from typing import Callable, Optional
8
+
9
+ from torch import Tensor, nn
10
+ import torch.nn.functional as F
11
+
12
+
13
+ class SwiGLUFFN(nn.Module):
14
+ def __init__(
15
+ self,
16
+ in_features: int,
17
+ hidden_features: Optional[int] = None,
18
+ out_features: Optional[int] = None,
19
+ act_layer: Callable[..., nn.Module] = None,
20
+ drop: float = 0.0,
21
+ bias: bool = True,
22
+ ) -> None:
23
+ super().__init__()
24
+ out_features = out_features or in_features
25
+ hidden_features = hidden_features or in_features
26
+ self.w12 = nn.Linear(in_features, 2 * hidden_features, bias=bias)
27
+ self.w3 = nn.Linear(hidden_features, out_features, bias=bias)
28
+
29
+ def forward(self, x: Tensor) -> Tensor:
30
+ x12 = self.w12(x)
31
+ x1, x2 = x12.chunk(2, dim=-1)
32
+ hidden = F.silu(x1) * x2
33
+ return self.w3(hidden)
34
+
35
+
36
+ try:
37
+ from xformers.ops import SwiGLU
38
+
39
+ XFORMERS_AVAILABLE = True
40
+ except ImportError:
41
+ SwiGLU = SwiGLUFFN
42
+ XFORMERS_AVAILABLE = False
43
+
44
+
45
+ class SwiGLUFFNFused(SwiGLU):
46
+ def __init__(
47
+ self,
48
+ in_features: int,
49
+ hidden_features: Optional[int] = None,
50
+ out_features: Optional[int] = None,
51
+ act_layer: Callable[..., nn.Module] = None,
52
+ drop: float = 0.0,
53
+ bias: bool = True,
54
+ ) -> None:
55
+ out_features = out_features or in_features
56
+ hidden_features = hidden_features or in_features
57
+ hidden_features = (int(hidden_features * 2 / 3) + 7) // 8 * 8
58
+ super().__init__(
59
+ in_features=in_features,
60
+ hidden_features=hidden_features,
61
+ out_features=out_features,
62
+ bias=bias,
63
+ )