Add files using upload-large-folder tool
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- code/umm/.gitignore +3 -0
- code/umm/README.md +95 -0
- code/umm/__init__.py +1 -0
- code/umm/inference/README.md +108 -0
- code/umm/inference/__init__.py +1 -0
- code/umm/inference/infer.py +994 -0
- code/umm/inference/run_infer.sh +72 -0
- code/umm/runtime/README.md +50 -0
- code/umm/runtime/check_imports.py +31 -0
- code/umm/runtime/deepspeed_configs/zero2.json +42 -0
- code/umm/runtime/models/__init__.py +0 -0
- code/umm/runtime/models/blip3o/constants.py +82 -0
- code/umm/runtime/models/blip3o/conversation.py +479 -0
- code/umm/runtime/models/blip3o/data/covt_data.py +827 -0
- code/umm/runtime/models/blip3o/data/covt_data_van.py +1056 -0
- code/umm/runtime/models/blip3o/data/covt_data_var.py +1039 -0
- code/umm/runtime/models/blip3o/data/mix_und_t2i_data.py +274 -0
- code/umm/runtime/models/blip3o/data/moe_data.py +0 -0
- code/umm/runtime/models/blip3o/data/t2I_covt_gen_data.py +894 -0
- code/umm/runtime/models/blip3o/data/t2i_covt_data.py +742 -0
- code/umm/runtime/models/blip3o/model/__init__.py +42 -0
- code/umm/runtime/models/blip3o/model/anchors/DepthAnything/DA-2K.md +51 -0
- code/umm/runtime/models/blip3o/model/anchors/DepthAnything/README.md +201 -0
- code/umm/runtime/models/blip3o/model/anchors/DepthAnything/app.py +88 -0
- code/umm/runtime/models/blip3o/model/anchors/DepthAnything/depth_anything_v2/dinov2.py +415 -0
- code/umm/runtime/models/blip3o/model/anchors/DepthAnything/depth_anything_v2/dinov2_layers/__init__.py +11 -0
- code/umm/runtime/models/blip3o/model/anchors/DepthAnything/depth_anything_v2/dinov2_layers/attention.py +83 -0
- code/umm/runtime/models/blip3o/model/anchors/DepthAnything/depth_anything_v2/dinov2_layers/block.py +252 -0
- code/umm/runtime/models/blip3o/model/anchors/DepthAnything/depth_anything_v2/dinov2_layers/drop_path.py +35 -0
- code/umm/runtime/models/blip3o/model/anchors/DepthAnything/depth_anything_v2/dinov2_layers/layer_scale.py +28 -0
- code/umm/runtime/models/blip3o/model/anchors/DepthAnything/depth_anything_v2/dinov2_layers/mlp.py +41 -0
- code/umm/runtime/models/blip3o/model/anchors/DepthAnything/depth_anything_v2/dinov2_layers/patch_embed.py +89 -0
- code/umm/runtime/models/blip3o/model/anchors/DepthAnything/depth_anything_v2/dinov2_layers/swiglu_ffn.py +63 -0
- code/umm/runtime/models/blip3o/model/anchors/DepthAnything/depth_anything_v2/dpt.py +221 -0
- code/umm/runtime/models/blip3o/model/anchors/DepthAnything/depth_anything_v2/util/blocks.py +148 -0
- code/umm/runtime/models/blip3o/model/anchors/DepthAnything/depth_anything_v2/util/transform.py +158 -0
- code/umm/runtime/models/blip3o/model/anchors/DepthAnything/metric_depth/README.md +114 -0
- code/umm/runtime/models/blip3o/model/anchors/DepthAnything/metric_depth/dataset/hypersim.py +74 -0
- code/umm/runtime/models/blip3o/model/anchors/DepthAnything/metric_depth/dataset/kitti.py +57 -0
- code/umm/runtime/models/blip3o/model/anchors/DepthAnything/metric_depth/dataset/transform.py +277 -0
- code/umm/runtime/models/blip3o/model/anchors/DepthAnything/metric_depth/dataset/vkitti2.py +54 -0
- code/umm/runtime/models/blip3o/model/anchors/DepthAnything/metric_depth/depth_anything_v2/dinov2.py +415 -0
- code/umm/runtime/models/blip3o/model/anchors/DepthAnything/metric_depth/depth_anything_v2/dinov2_layers/__init__.py +11 -0
- code/umm/runtime/models/blip3o/model/anchors/DepthAnything/metric_depth/depth_anything_v2/dinov2_layers/attention.py +83 -0
- code/umm/runtime/models/blip3o/model/anchors/DepthAnything/metric_depth/depth_anything_v2/dinov2_layers/block.py +252 -0
- code/umm/runtime/models/blip3o/model/anchors/DepthAnything/metric_depth/depth_anything_v2/dinov2_layers/drop_path.py +35 -0
- code/umm/runtime/models/blip3o/model/anchors/DepthAnything/metric_depth/depth_anything_v2/dinov2_layers/layer_scale.py +28 -0
- code/umm/runtime/models/blip3o/model/anchors/DepthAnything/metric_depth/depth_anything_v2/dinov2_layers/mlp.py +41 -0
- code/umm/runtime/models/blip3o/model/anchors/DepthAnything/metric_depth/depth_anything_v2/dinov2_layers/patch_embed.py +89 -0
- 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 |
+

|
| 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†</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   <sup>2</sup>TikTok
|
| 9 |
+
<br>
|
| 10 |
+
†project lead *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 |
+

|
| 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 |
+

|
| 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 |
+
)
|