Spaces:
Sleeping
Sleeping
Fix: un-ignore TRELLIS.2/trellis2/models/ — was blocked by 'models/' in .gitignore
Browse filesThe .gitignore rule 'models/' matched trellis2/models/ (the Python source
package), preventing all 7 model files from being committed and deployed.
HF container had no models/ directory, causing ModuleNotFoundError on import.
Changed 'models/' to '/models/' (root-only) and added explicit negation
'!TRELLIS.2/trellis2/models/' to ensure the package is always tracked.
- .gitignore +2 -1
- Hunyuan3D-2.1/hy3dshape/hy3dshape/models/__init__.py +28 -0
- Hunyuan3D-2.1/hy3dshape/hy3dshape/models/autoencoders/__init__.py +20 -0
- Hunyuan3D-2.1/hy3dshape/hy3dshape/models/autoencoders/attention_blocks.py +716 -0
- Hunyuan3D-2.1/hy3dshape/hy3dshape/models/autoencoders/attention_processors.py +96 -0
- Hunyuan3D-2.1/hy3dshape/hy3dshape/models/autoencoders/model.py +339 -0
- Hunyuan3D-2.1/hy3dshape/hy3dshape/models/autoencoders/surface_extractors.py +164 -0
- Hunyuan3D-2.1/hy3dshape/hy3dshape/models/autoencoders/volume_decoders.py +435 -0
- Hunyuan3D-2.1/hy3dshape/hy3dshape/models/conditioner.py +257 -0
- Hunyuan3D-2.1/hy3dshape/hy3dshape/models/denoisers/__init__.py +15 -0
- Hunyuan3D-2.1/hy3dshape/hy3dshape/models/denoisers/hunyuan3ddit.py +404 -0
- Hunyuan3D-2.1/hy3dshape/hy3dshape/models/denoisers/hunyuandit.py +596 -0
- Hunyuan3D-2.1/hy3dshape/hy3dshape/models/denoisers/moe_layers.py +177 -0
- Hunyuan3D-2.1/hy3dshape/hy3dshape/models/diffusion/flow_matching_sit.py +354 -0
- Hunyuan3D-2.1/hy3dshape/hy3dshape/models/diffusion/transport/__init__.py +97 -0
- Hunyuan3D-2.1/hy3dshape/hy3dshape/models/diffusion/transport/integrators.py +142 -0
- Hunyuan3D-2.1/hy3dshape/hy3dshape/models/diffusion/transport/path.py +220 -0
- Hunyuan3D-2.1/hy3dshape/hy3dshape/models/diffusion/transport/transport.py +534 -0
- Hunyuan3D-2.1/hy3dshape/hy3dshape/models/diffusion/transport/utils.py +54 -0
- TRELLIS.2/trellis2/models/__init__.py +78 -0
- TRELLIS.2/trellis2/models/sc_vaes/fdg_vae.py +110 -0
- TRELLIS.2/trellis2/models/sc_vaes/sparse_unet_vae.py +522 -0
- TRELLIS.2/trellis2/models/sparse_elastic_mixin.py +24 -0
- TRELLIS.2/trellis2/models/sparse_structure_flow.py +247 -0
- TRELLIS.2/trellis2/models/sparse_structure_vae.py +306 -0
- TRELLIS.2/trellis2/models/structured_latent_flow.py +207 -0
.gitignore
CHANGED
|
@@ -19,7 +19,8 @@ workspace/history/*
|
|
| 19 |
|
| 20 |
# Model weights (downloaded at runtime, not committed)
|
| 21 |
weights/
|
| 22 |
-
models/
|
|
|
|
| 23 |
*.safetensors
|
| 24 |
*.bin
|
| 25 |
*.pt
|
|
|
|
| 19 |
|
| 20 |
# Model weights (downloaded at runtime, not committed)
|
| 21 |
weights/
|
| 22 |
+
/models/
|
| 23 |
+
!TRELLIS.2/trellis2/models/
|
| 24 |
*.safetensors
|
| 25 |
*.bin
|
| 26 |
*.pt
|
Hunyuan3D-2.1/hy3dshape/hy3dshape/models/__init__.py
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Open Source Model Licensed under the Apache License Version 2.0
|
| 2 |
+
# and Other Licenses of the Third-Party Components therein:
|
| 3 |
+
# The below Model in this distribution may have been modified by THL A29 Limited
|
| 4 |
+
# ("Tencent Modifications"). All Tencent Modifications are Copyright (C) 2024 THL A29 Limited.
|
| 5 |
+
|
| 6 |
+
# Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved.
|
| 7 |
+
# The below software and/or models in this distribution may have been
|
| 8 |
+
# modified by THL A29 Limited ("Tencent Modifications").
|
| 9 |
+
# All Tencent Modifications are Copyright (C) THL A29 Limited.
|
| 10 |
+
|
| 11 |
+
# Hunyuan 3D is licensed under the TENCENT HUNYUAN NON-COMMERCIAL LICENSE AGREEMENT
|
| 12 |
+
# except for the third-party components listed below.
|
| 13 |
+
# Hunyuan 3D does not impose any additional limitations beyond what is outlined
|
| 14 |
+
# in the repsective licenses of these third-party components.
|
| 15 |
+
# Users must comply with all terms and conditions of original licenses of these third-party
|
| 16 |
+
# components and must ensure that the usage of the third party components adheres to
|
| 17 |
+
# all relevant laws and regulations.
|
| 18 |
+
|
| 19 |
+
# For avoidance of doubts, Hunyuan 3D means the large language models and
|
| 20 |
+
# their software and algorithms, including trained model weights, parameters (including
|
| 21 |
+
# optimizer states), machine-learning model code, inference-enabling code, training-enabling code,
|
| 22 |
+
# fine-tuning enabling code and other elements of the foregoing made publicly available
|
| 23 |
+
# by Tencent in accordance with TENCENT HUNYUAN COMMUNITY LICENSE AGREEMENT.
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
from .autoencoders import ShapeVAE
|
| 27 |
+
from .conditioner import DualImageEncoder, SingleImageEncoder, DinoImageEncoder, CLIPImageEncoder
|
| 28 |
+
from .denoisers import Hunyuan3DDiT
|
Hunyuan3D-2.1/hy3dshape/hy3dshape/models/autoencoders/__init__.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Hunyuan 3D is licensed under the TENCENT HUNYUAN NON-COMMERCIAL LICENSE AGREEMENT
|
| 2 |
+
# except for the third-party components listed below.
|
| 3 |
+
# Hunyuan 3D does not impose any additional limitations beyond what is outlined
|
| 4 |
+
# in the repsective licenses of these third-party components.
|
| 5 |
+
# Users must comply with all terms and conditions of original licenses of these third-party
|
| 6 |
+
# components and must ensure that the usage of the third party components adheres to
|
| 7 |
+
# all relevant laws and regulations.
|
| 8 |
+
|
| 9 |
+
# For avoidance of doubts, Hunyuan 3D means the large language models and
|
| 10 |
+
# their software and algorithms, including trained model weights, parameters (including
|
| 11 |
+
# optimizer states), machine-learning model code, inference-enabling code, training-enabling code,
|
| 12 |
+
# fine-tuning enabling code and other elements of the foregoing made publicly available
|
| 13 |
+
# by Tencent in accordance with TENCENT HUNYUAN COMMUNITY LICENSE AGREEMENT.
|
| 14 |
+
|
| 15 |
+
from .attention_blocks import CrossAttentionDecoder
|
| 16 |
+
from .attention_processors import FlashVDMCrossAttentionProcessor, CrossAttentionProcessor, \
|
| 17 |
+
FlashVDMTopMCrossAttentionProcessor
|
| 18 |
+
from .model import ShapeVAE, VectsetVAE
|
| 19 |
+
from .surface_extractors import SurfaceExtractors, MCSurfaceExtractor, DMCSurfaceExtractor, Latent2MeshOutput
|
| 20 |
+
from .volume_decoders import HierarchicalVolumeDecoding, FlashVDMVolumeDecoding, VanillaVolumeDecoder
|
Hunyuan3D-2.1/hy3dshape/hy3dshape/models/autoencoders/attention_blocks.py
ADDED
|
@@ -0,0 +1,716 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Open Source Model Licensed under the Apache License Version 2.0
|
| 2 |
+
# and Other Licenses of the Third-Party Components therein:
|
| 3 |
+
# The below Model in this distribution may have been modified by THL A29 Limited
|
| 4 |
+
# ("Tencent Modifications"). All Tencent Modifications are Copyright (C) 2024 THL A29 Limited.
|
| 5 |
+
|
| 6 |
+
# Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved.
|
| 7 |
+
# The below software and/or models in this distribution may have been
|
| 8 |
+
# modified by THL A29 Limited ("Tencent Modifications").
|
| 9 |
+
# All Tencent Modifications are Copyright (C) THL A29 Limited.
|
| 10 |
+
|
| 11 |
+
# Hunyuan 3D is licensed under the TENCENT HUNYUAN NON-COMMERCIAL LICENSE AGREEMENT
|
| 12 |
+
# except for the third-party components listed below.
|
| 13 |
+
# Hunyuan 3D does not impose any additional limitations beyond what is outlined
|
| 14 |
+
# in the repsective licenses of these third-party components.
|
| 15 |
+
# Users must comply with all terms and conditions of original licenses of these third-party
|
| 16 |
+
# components and must ensure that the usage of the third party components adheres to
|
| 17 |
+
# all relevant laws and regulations.
|
| 18 |
+
|
| 19 |
+
# For avoidance of doubts, Hunyuan 3D means the large language models and
|
| 20 |
+
# their software and algorithms, including trained model weights, parameters (including
|
| 21 |
+
# optimizer states), machine-learning model code, inference-enabling code, training-enabling code,
|
| 22 |
+
# fine-tuning enabling code and other elements of the foregoing made publicly available
|
| 23 |
+
# by Tencent in accordance with TENCENT HUNYUAN COMMUNITY LICENSE AGREEMENT.
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
import os
|
| 27 |
+
from typing import Optional, Union, List
|
| 28 |
+
|
| 29 |
+
import torch
|
| 30 |
+
import torch.nn as nn
|
| 31 |
+
from einops import rearrange
|
| 32 |
+
from torch import Tensor
|
| 33 |
+
|
| 34 |
+
from .attention_processors import CrossAttentionProcessor
|
| 35 |
+
from ...utils import logger
|
| 36 |
+
|
| 37 |
+
scaled_dot_product_attention = nn.functional.scaled_dot_product_attention
|
| 38 |
+
|
| 39 |
+
if os.environ.get('USE_SAGEATTN', '0') == '1':
|
| 40 |
+
try:
|
| 41 |
+
from sageattention import sageattn
|
| 42 |
+
except ImportError:
|
| 43 |
+
raise ImportError('Please install the package "sageattention" to use this USE_SAGEATTN.')
|
| 44 |
+
scaled_dot_product_attention = sageattn
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
class FourierEmbedder(nn.Module):
|
| 48 |
+
"""The sin/cosine positional embedding. Given an input tensor `x` of shape [n_batch, ..., c_dim], it converts
|
| 49 |
+
each feature dimension of `x[..., i]` into:
|
| 50 |
+
[
|
| 51 |
+
sin(x[..., i]),
|
| 52 |
+
sin(f_1*x[..., i]),
|
| 53 |
+
sin(f_2*x[..., i]),
|
| 54 |
+
...
|
| 55 |
+
sin(f_N * x[..., i]),
|
| 56 |
+
cos(x[..., i]),
|
| 57 |
+
cos(f_1*x[..., i]),
|
| 58 |
+
cos(f_2*x[..., i]),
|
| 59 |
+
...
|
| 60 |
+
cos(f_N * x[..., i]),
|
| 61 |
+
x[..., i] # only present if include_input is True.
|
| 62 |
+
], here f_i is the frequency.
|
| 63 |
+
|
| 64 |
+
Denote the space is [0 / num_freqs, 1 / num_freqs, 2 / num_freqs, 3 / num_freqs, ..., (num_freqs - 1) / num_freqs].
|
| 65 |
+
If logspace is True, then the frequency f_i is [2^(0 / num_freqs), ..., 2^(i / num_freqs), ...];
|
| 66 |
+
Otherwise, the frequencies are linearly spaced between [1.0, 2^(num_freqs - 1)].
|
| 67 |
+
|
| 68 |
+
Args:
|
| 69 |
+
num_freqs (int): the number of frequencies, default is 6;
|
| 70 |
+
logspace (bool): If logspace is True, then the frequency f_i is [..., 2^(i / num_freqs), ...],
|
| 71 |
+
otherwise, the frequencies are linearly spaced between [1.0, 2^(num_freqs - 1)];
|
| 72 |
+
input_dim (int): the input dimension, default is 3;
|
| 73 |
+
include_input (bool): include the input tensor or not, default is True.
|
| 74 |
+
|
| 75 |
+
Attributes:
|
| 76 |
+
frequencies (torch.Tensor): If logspace is True, then the frequency f_i is [..., 2^(i / num_freqs), ...],
|
| 77 |
+
otherwise, the frequencies are linearly spaced between [1.0, 2^(num_freqs - 1);
|
| 78 |
+
|
| 79 |
+
out_dim (int): the embedding size, if include_input is True, it is input_dim * (num_freqs * 2 + 1),
|
| 80 |
+
otherwise, it is input_dim * num_freqs * 2.
|
| 81 |
+
|
| 82 |
+
"""
|
| 83 |
+
|
| 84 |
+
def __init__(self,
|
| 85 |
+
num_freqs: int = 6,
|
| 86 |
+
logspace: bool = True,
|
| 87 |
+
input_dim: int = 3,
|
| 88 |
+
include_input: bool = True,
|
| 89 |
+
include_pi: bool = True) -> None:
|
| 90 |
+
|
| 91 |
+
"""The initialization"""
|
| 92 |
+
|
| 93 |
+
super().__init__()
|
| 94 |
+
|
| 95 |
+
if logspace:
|
| 96 |
+
frequencies = 2.0 ** torch.arange(
|
| 97 |
+
num_freqs,
|
| 98 |
+
dtype=torch.float32
|
| 99 |
+
)
|
| 100 |
+
else:
|
| 101 |
+
frequencies = torch.linspace(
|
| 102 |
+
1.0,
|
| 103 |
+
2.0 ** (num_freqs - 1),
|
| 104 |
+
num_freqs,
|
| 105 |
+
dtype=torch.float32
|
| 106 |
+
)
|
| 107 |
+
|
| 108 |
+
if include_pi:
|
| 109 |
+
frequencies *= torch.pi
|
| 110 |
+
|
| 111 |
+
self.register_buffer("frequencies", frequencies, persistent=False)
|
| 112 |
+
self.include_input = include_input
|
| 113 |
+
self.num_freqs = num_freqs
|
| 114 |
+
|
| 115 |
+
self.out_dim = self.get_dims(input_dim)
|
| 116 |
+
|
| 117 |
+
def get_dims(self, input_dim):
|
| 118 |
+
temp = 1 if self.include_input or self.num_freqs == 0 else 0
|
| 119 |
+
out_dim = input_dim * (self.num_freqs * 2 + temp)
|
| 120 |
+
|
| 121 |
+
return out_dim
|
| 122 |
+
|
| 123 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 124 |
+
""" Forward process.
|
| 125 |
+
|
| 126 |
+
Args:
|
| 127 |
+
x: tensor of shape [..., dim]
|
| 128 |
+
|
| 129 |
+
Returns:
|
| 130 |
+
embedding: an embedding of `x` of shape [..., dim * (num_freqs * 2 + temp)]
|
| 131 |
+
where temp is 1 if include_input is True and 0 otherwise.
|
| 132 |
+
"""
|
| 133 |
+
|
| 134 |
+
if self.num_freqs > 0:
|
| 135 |
+
embed = (x[..., None].contiguous() * self.frequencies).view(*x.shape[:-1], -1)
|
| 136 |
+
if self.include_input:
|
| 137 |
+
return torch.cat((x, embed.sin(), embed.cos()), dim=-1)
|
| 138 |
+
else:
|
| 139 |
+
return torch.cat((embed.sin(), embed.cos()), dim=-1)
|
| 140 |
+
else:
|
| 141 |
+
return x
|
| 142 |
+
|
| 143 |
+
|
| 144 |
+
class DropPath(nn.Module):
|
| 145 |
+
"""Drop paths (Stochastic Depth) per sample (when applied in main path of residual blocks).
|
| 146 |
+
"""
|
| 147 |
+
|
| 148 |
+
def __init__(self, drop_prob: float = 0., scale_by_keep: bool = True):
|
| 149 |
+
super(DropPath, self).__init__()
|
| 150 |
+
self.drop_prob = drop_prob
|
| 151 |
+
self.scale_by_keep = scale_by_keep
|
| 152 |
+
|
| 153 |
+
def forward(self, x):
|
| 154 |
+
"""Drop paths (Stochastic Depth) per sample (when applied in main path of residual blocks).
|
| 155 |
+
|
| 156 |
+
This is the same as the DropConnect impl I created for EfficientNet, etc networks, however,
|
| 157 |
+
the original name is misleading as 'Drop Connect' is a different form of dropout in a separate paper...
|
| 158 |
+
See discussion: https://github.com/tensorflow/tpu/issues/494#issuecomment-532968956 ... I've opted for
|
| 159 |
+
changing the layer and argument names to 'drop path' rather than mix DropConnect as a layer name and use
|
| 160 |
+
'survival rate' as the argument.
|
| 161 |
+
|
| 162 |
+
"""
|
| 163 |
+
if self.drop_prob == 0. or not self.training:
|
| 164 |
+
return x
|
| 165 |
+
keep_prob = 1 - self.drop_prob
|
| 166 |
+
shape = (x.shape[0],) + (1,) * (x.ndim - 1) # work with diff dim tensors, not just 2D ConvNets
|
| 167 |
+
random_tensor = x.new_empty(shape).bernoulli_(keep_prob)
|
| 168 |
+
if keep_prob > 0.0 and self.scale_by_keep:
|
| 169 |
+
random_tensor.div_(keep_prob)
|
| 170 |
+
return x * random_tensor
|
| 171 |
+
|
| 172 |
+
def extra_repr(self):
|
| 173 |
+
return f'drop_prob={round(self.drop_prob, 3):0.3f}'
|
| 174 |
+
|
| 175 |
+
|
| 176 |
+
class MLP(nn.Module):
|
| 177 |
+
def __init__(
|
| 178 |
+
self, *,
|
| 179 |
+
width: int,
|
| 180 |
+
expand_ratio: int = 4,
|
| 181 |
+
output_width: int = None,
|
| 182 |
+
drop_path_rate: float = 0.0
|
| 183 |
+
):
|
| 184 |
+
super().__init__()
|
| 185 |
+
self.width = width
|
| 186 |
+
self.c_fc = nn.Linear(width, width * expand_ratio)
|
| 187 |
+
self.c_proj = nn.Linear(width * expand_ratio, output_width if output_width is not None else width)
|
| 188 |
+
self.gelu = nn.GELU()
|
| 189 |
+
self.drop_path = DropPath(drop_path_rate) if drop_path_rate > 0. else nn.Identity()
|
| 190 |
+
|
| 191 |
+
def forward(self, x):
|
| 192 |
+
return self.drop_path(self.c_proj(self.gelu(self.c_fc(x))))
|
| 193 |
+
|
| 194 |
+
|
| 195 |
+
class QKVMultiheadCrossAttention(nn.Module):
|
| 196 |
+
def __init__(
|
| 197 |
+
self,
|
| 198 |
+
*,
|
| 199 |
+
heads: int,
|
| 200 |
+
n_data: Optional[int] = None,
|
| 201 |
+
width=None,
|
| 202 |
+
qk_norm=False,
|
| 203 |
+
norm_layer=nn.LayerNorm
|
| 204 |
+
):
|
| 205 |
+
super().__init__()
|
| 206 |
+
self.heads = heads
|
| 207 |
+
self.n_data = n_data
|
| 208 |
+
self.q_norm = norm_layer(width // heads, elementwise_affine=True, eps=1e-6) if qk_norm else nn.Identity()
|
| 209 |
+
self.k_norm = norm_layer(width // heads, elementwise_affine=True, eps=1e-6) if qk_norm else nn.Identity()
|
| 210 |
+
|
| 211 |
+
self.attn_processor = CrossAttentionProcessor()
|
| 212 |
+
|
| 213 |
+
def forward(self, q, kv):
|
| 214 |
+
_, n_ctx, _ = q.shape
|
| 215 |
+
bs, n_data, width = kv.shape
|
| 216 |
+
attn_ch = width // self.heads // 2
|
| 217 |
+
q = q.view(bs, n_ctx, self.heads, -1)
|
| 218 |
+
kv = kv.view(bs, n_data, self.heads, -1)
|
| 219 |
+
k, v = torch.split(kv, attn_ch, dim=-1)
|
| 220 |
+
|
| 221 |
+
q = self.q_norm(q)
|
| 222 |
+
k = self.k_norm(k)
|
| 223 |
+
q, k, v = map(lambda t: rearrange(t, 'b n h d -> b h n d', h=self.heads), (q, k, v))
|
| 224 |
+
out = self.attn_processor(self, q, k, v)
|
| 225 |
+
out = out.transpose(1, 2).reshape(bs, n_ctx, -1)
|
| 226 |
+
return out
|
| 227 |
+
|
| 228 |
+
|
| 229 |
+
class MultiheadCrossAttention(nn.Module):
|
| 230 |
+
def __init__(
|
| 231 |
+
self,
|
| 232 |
+
*,
|
| 233 |
+
width: int,
|
| 234 |
+
heads: int,
|
| 235 |
+
qkv_bias: bool = True,
|
| 236 |
+
n_data: Optional[int] = None,
|
| 237 |
+
data_width: Optional[int] = None,
|
| 238 |
+
norm_layer=nn.LayerNorm,
|
| 239 |
+
qk_norm: bool = False,
|
| 240 |
+
kv_cache: bool = False,
|
| 241 |
+
):
|
| 242 |
+
super().__init__()
|
| 243 |
+
self.n_data = n_data
|
| 244 |
+
self.width = width
|
| 245 |
+
self.heads = heads
|
| 246 |
+
self.data_width = width if data_width is None else data_width
|
| 247 |
+
self.c_q = nn.Linear(width, width, bias=qkv_bias)
|
| 248 |
+
self.c_kv = nn.Linear(self.data_width, width * 2, bias=qkv_bias)
|
| 249 |
+
self.c_proj = nn.Linear(width, width)
|
| 250 |
+
self.attention = QKVMultiheadCrossAttention(
|
| 251 |
+
heads=heads,
|
| 252 |
+
n_data=n_data,
|
| 253 |
+
width=width,
|
| 254 |
+
norm_layer=norm_layer,
|
| 255 |
+
qk_norm=qk_norm
|
| 256 |
+
)
|
| 257 |
+
self.kv_cache = kv_cache
|
| 258 |
+
self.data = None
|
| 259 |
+
|
| 260 |
+
def forward(self, x, data):
|
| 261 |
+
x = self.c_q(x)
|
| 262 |
+
if self.kv_cache:
|
| 263 |
+
if self.data is None:
|
| 264 |
+
self.data = self.c_kv(data)
|
| 265 |
+
logger.info('Save kv cache,this should be called only once for one mesh')
|
| 266 |
+
data = self.data
|
| 267 |
+
else:
|
| 268 |
+
data = self.c_kv(data)
|
| 269 |
+
x = self.attention(x, data)
|
| 270 |
+
x = self.c_proj(x)
|
| 271 |
+
return x
|
| 272 |
+
|
| 273 |
+
|
| 274 |
+
class ResidualCrossAttentionBlock(nn.Module):
|
| 275 |
+
def __init__(
|
| 276 |
+
self,
|
| 277 |
+
*,
|
| 278 |
+
n_data: Optional[int] = None,
|
| 279 |
+
width: int,
|
| 280 |
+
heads: int,
|
| 281 |
+
mlp_expand_ratio: int = 4,
|
| 282 |
+
data_width: Optional[int] = None,
|
| 283 |
+
qkv_bias: bool = True,
|
| 284 |
+
norm_layer=nn.LayerNorm,
|
| 285 |
+
qk_norm: bool = False
|
| 286 |
+
):
|
| 287 |
+
super().__init__()
|
| 288 |
+
|
| 289 |
+
if data_width is None:
|
| 290 |
+
data_width = width
|
| 291 |
+
|
| 292 |
+
self.attn = MultiheadCrossAttention(
|
| 293 |
+
n_data=n_data,
|
| 294 |
+
width=width,
|
| 295 |
+
heads=heads,
|
| 296 |
+
data_width=data_width,
|
| 297 |
+
qkv_bias=qkv_bias,
|
| 298 |
+
norm_layer=norm_layer,
|
| 299 |
+
qk_norm=qk_norm
|
| 300 |
+
)
|
| 301 |
+
self.ln_1 = norm_layer(width, elementwise_affine=True, eps=1e-6)
|
| 302 |
+
self.ln_2 = norm_layer(data_width, elementwise_affine=True, eps=1e-6)
|
| 303 |
+
self.ln_3 = norm_layer(width, elementwise_affine=True, eps=1e-6)
|
| 304 |
+
self.mlp = MLP(width=width, expand_ratio=mlp_expand_ratio)
|
| 305 |
+
|
| 306 |
+
def forward(self, x: torch.Tensor, data: torch.Tensor):
|
| 307 |
+
x = x + self.attn(self.ln_1(x), self.ln_2(data))
|
| 308 |
+
x = x + self.mlp(self.ln_3(x))
|
| 309 |
+
return x
|
| 310 |
+
|
| 311 |
+
|
| 312 |
+
class QKVMultiheadAttention(nn.Module):
|
| 313 |
+
def __init__(
|
| 314 |
+
self,
|
| 315 |
+
*,
|
| 316 |
+
heads: int,
|
| 317 |
+
n_ctx: int,
|
| 318 |
+
width=None,
|
| 319 |
+
qk_norm=False,
|
| 320 |
+
norm_layer=nn.LayerNorm
|
| 321 |
+
):
|
| 322 |
+
super().__init__()
|
| 323 |
+
self.heads = heads
|
| 324 |
+
self.n_ctx = n_ctx
|
| 325 |
+
self.q_norm = norm_layer(width // heads, elementwise_affine=True, eps=1e-6) if qk_norm else nn.Identity()
|
| 326 |
+
self.k_norm = norm_layer(width // heads, elementwise_affine=True, eps=1e-6) if qk_norm else nn.Identity()
|
| 327 |
+
|
| 328 |
+
def forward(self, qkv):
|
| 329 |
+
bs, n_ctx, width = qkv.shape
|
| 330 |
+
attn_ch = width // self.heads // 3
|
| 331 |
+
qkv = qkv.view(bs, n_ctx, self.heads, -1)
|
| 332 |
+
q, k, v = torch.split(qkv, attn_ch, dim=-1)
|
| 333 |
+
|
| 334 |
+
q = self.q_norm(q)
|
| 335 |
+
k = self.k_norm(k)
|
| 336 |
+
|
| 337 |
+
q, k, v = map(lambda t: rearrange(t, 'b n h d -> b h n d', h=self.heads), (q, k, v))
|
| 338 |
+
out = scaled_dot_product_attention(q, k, v).transpose(1, 2).reshape(bs, n_ctx, -1)
|
| 339 |
+
return out
|
| 340 |
+
|
| 341 |
+
|
| 342 |
+
class MultiheadAttention(nn.Module):
|
| 343 |
+
def __init__(
|
| 344 |
+
self,
|
| 345 |
+
*,
|
| 346 |
+
n_ctx: int,
|
| 347 |
+
width: int,
|
| 348 |
+
heads: int,
|
| 349 |
+
qkv_bias: bool,
|
| 350 |
+
norm_layer=nn.LayerNorm,
|
| 351 |
+
qk_norm: bool = False,
|
| 352 |
+
drop_path_rate: float = 0.0
|
| 353 |
+
):
|
| 354 |
+
super().__init__()
|
| 355 |
+
self.n_ctx = n_ctx
|
| 356 |
+
self.width = width
|
| 357 |
+
self.heads = heads
|
| 358 |
+
self.c_qkv = nn.Linear(width, width * 3, bias=qkv_bias)
|
| 359 |
+
self.c_proj = nn.Linear(width, width)
|
| 360 |
+
self.attention = QKVMultiheadAttention(
|
| 361 |
+
heads=heads,
|
| 362 |
+
n_ctx=n_ctx,
|
| 363 |
+
width=width,
|
| 364 |
+
norm_layer=norm_layer,
|
| 365 |
+
qk_norm=qk_norm
|
| 366 |
+
)
|
| 367 |
+
self.drop_path = DropPath(drop_path_rate) if drop_path_rate > 0. else nn.Identity()
|
| 368 |
+
|
| 369 |
+
def forward(self, x):
|
| 370 |
+
x = self.c_qkv(x)
|
| 371 |
+
x = self.attention(x)
|
| 372 |
+
x = self.drop_path(self.c_proj(x))
|
| 373 |
+
return x
|
| 374 |
+
|
| 375 |
+
|
| 376 |
+
class ResidualAttentionBlock(nn.Module):
|
| 377 |
+
def __init__(
|
| 378 |
+
self,
|
| 379 |
+
*,
|
| 380 |
+
n_ctx: int,
|
| 381 |
+
width: int,
|
| 382 |
+
heads: int,
|
| 383 |
+
qkv_bias: bool = True,
|
| 384 |
+
norm_layer=nn.LayerNorm,
|
| 385 |
+
qk_norm: bool = False,
|
| 386 |
+
drop_path_rate: float = 0.0,
|
| 387 |
+
):
|
| 388 |
+
super().__init__()
|
| 389 |
+
self.attn = MultiheadAttention(
|
| 390 |
+
n_ctx=n_ctx,
|
| 391 |
+
width=width,
|
| 392 |
+
heads=heads,
|
| 393 |
+
qkv_bias=qkv_bias,
|
| 394 |
+
norm_layer=norm_layer,
|
| 395 |
+
qk_norm=qk_norm,
|
| 396 |
+
drop_path_rate=drop_path_rate
|
| 397 |
+
)
|
| 398 |
+
self.ln_1 = norm_layer(width, elementwise_affine=True, eps=1e-6)
|
| 399 |
+
self.mlp = MLP(width=width, drop_path_rate=drop_path_rate)
|
| 400 |
+
self.ln_2 = norm_layer(width, elementwise_affine=True, eps=1e-6)
|
| 401 |
+
|
| 402 |
+
def forward(self, x: torch.Tensor):
|
| 403 |
+
x = x + self.attn(self.ln_1(x))
|
| 404 |
+
x = x + self.mlp(self.ln_2(x))
|
| 405 |
+
return x
|
| 406 |
+
|
| 407 |
+
|
| 408 |
+
class Transformer(nn.Module):
|
| 409 |
+
def __init__(
|
| 410 |
+
self,
|
| 411 |
+
*,
|
| 412 |
+
n_ctx: int,
|
| 413 |
+
width: int,
|
| 414 |
+
layers: int,
|
| 415 |
+
heads: int,
|
| 416 |
+
qkv_bias: bool = True,
|
| 417 |
+
norm_layer=nn.LayerNorm,
|
| 418 |
+
qk_norm: bool = False,
|
| 419 |
+
drop_path_rate: float = 0.0
|
| 420 |
+
):
|
| 421 |
+
super().__init__()
|
| 422 |
+
self.n_ctx = n_ctx
|
| 423 |
+
self.width = width
|
| 424 |
+
self.layers = layers
|
| 425 |
+
self.resblocks = nn.ModuleList(
|
| 426 |
+
[
|
| 427 |
+
ResidualAttentionBlock(
|
| 428 |
+
n_ctx=n_ctx,
|
| 429 |
+
width=width,
|
| 430 |
+
heads=heads,
|
| 431 |
+
qkv_bias=qkv_bias,
|
| 432 |
+
norm_layer=norm_layer,
|
| 433 |
+
qk_norm=qk_norm,
|
| 434 |
+
drop_path_rate=drop_path_rate
|
| 435 |
+
)
|
| 436 |
+
for _ in range(layers)
|
| 437 |
+
]
|
| 438 |
+
)
|
| 439 |
+
|
| 440 |
+
def forward(self, x: torch.Tensor):
|
| 441 |
+
for block in self.resblocks:
|
| 442 |
+
x = block(x)
|
| 443 |
+
return x
|
| 444 |
+
|
| 445 |
+
|
| 446 |
+
class CrossAttentionDecoder(nn.Module):
|
| 447 |
+
|
| 448 |
+
def __init__(
|
| 449 |
+
self,
|
| 450 |
+
*,
|
| 451 |
+
num_latents: int,
|
| 452 |
+
out_channels: int,
|
| 453 |
+
fourier_embedder: FourierEmbedder,
|
| 454 |
+
width: int,
|
| 455 |
+
heads: int,
|
| 456 |
+
mlp_expand_ratio: int = 4,
|
| 457 |
+
downsample_ratio: int = 1,
|
| 458 |
+
enable_ln_post: bool = True,
|
| 459 |
+
qkv_bias: bool = True,
|
| 460 |
+
qk_norm: bool = False,
|
| 461 |
+
label_type: str = "binary"
|
| 462 |
+
):
|
| 463 |
+
super().__init__()
|
| 464 |
+
|
| 465 |
+
self.enable_ln_post = enable_ln_post
|
| 466 |
+
self.fourier_embedder = fourier_embedder
|
| 467 |
+
self.downsample_ratio = downsample_ratio
|
| 468 |
+
self.query_proj = nn.Linear(self.fourier_embedder.out_dim, width)
|
| 469 |
+
if self.downsample_ratio != 1:
|
| 470 |
+
self.latents_proj = nn.Linear(width * downsample_ratio, width)
|
| 471 |
+
if self.enable_ln_post == False:
|
| 472 |
+
qk_norm = False
|
| 473 |
+
self.cross_attn_decoder = ResidualCrossAttentionBlock(
|
| 474 |
+
n_data=num_latents,
|
| 475 |
+
width=width,
|
| 476 |
+
mlp_expand_ratio=mlp_expand_ratio,
|
| 477 |
+
heads=heads,
|
| 478 |
+
qkv_bias=qkv_bias,
|
| 479 |
+
qk_norm=qk_norm
|
| 480 |
+
)
|
| 481 |
+
|
| 482 |
+
if self.enable_ln_post:
|
| 483 |
+
self.ln_post = nn.LayerNorm(width)
|
| 484 |
+
self.output_proj = nn.Linear(width, out_channels)
|
| 485 |
+
self.label_type = label_type
|
| 486 |
+
self.count = 0
|
| 487 |
+
|
| 488 |
+
def set_cross_attention_processor(self, processor):
|
| 489 |
+
self.cross_attn_decoder.attn.attention.attn_processor = processor
|
| 490 |
+
|
| 491 |
+
def set_default_cross_attention_processor(self):
|
| 492 |
+
self.cross_attn_decoder.attn.attention.attn_processor = CrossAttentionProcessor
|
| 493 |
+
|
| 494 |
+
def forward(self, queries=None, query_embeddings=None, latents=None):
|
| 495 |
+
if query_embeddings is None:
|
| 496 |
+
query_embeddings = self.query_proj(self.fourier_embedder(queries).to(latents.dtype))
|
| 497 |
+
self.count += query_embeddings.shape[1]
|
| 498 |
+
if self.downsample_ratio != 1:
|
| 499 |
+
latents = self.latents_proj(latents)
|
| 500 |
+
x = self.cross_attn_decoder(query_embeddings, latents)
|
| 501 |
+
if self.enable_ln_post:
|
| 502 |
+
x = self.ln_post(x)
|
| 503 |
+
occ = self.output_proj(x)
|
| 504 |
+
return occ
|
| 505 |
+
|
| 506 |
+
|
| 507 |
+
def fps(
|
| 508 |
+
src: torch.Tensor,
|
| 509 |
+
batch: Optional[Tensor] = None,
|
| 510 |
+
ratio: Optional[Union[Tensor, float]] = None,
|
| 511 |
+
random_start: bool = True,
|
| 512 |
+
batch_size: Optional[int] = None,
|
| 513 |
+
ptr: Optional[Union[Tensor, List[int]]] = None,
|
| 514 |
+
):
|
| 515 |
+
src = src.float()
|
| 516 |
+
from torch_cluster import fps as fps_fn
|
| 517 |
+
output = fps_fn(src, batch, ratio, random_start, batch_size, ptr)
|
| 518 |
+
return output
|
| 519 |
+
|
| 520 |
+
|
| 521 |
+
class PointCrossAttentionEncoder(nn.Module):
|
| 522 |
+
|
| 523 |
+
def __init__(
|
| 524 |
+
self, *,
|
| 525 |
+
num_latents: int,
|
| 526 |
+
downsample_ratio: float,
|
| 527 |
+
pc_size: int,
|
| 528 |
+
pc_sharpedge_size: int,
|
| 529 |
+
fourier_embedder: FourierEmbedder,
|
| 530 |
+
point_feats: int,
|
| 531 |
+
width: int,
|
| 532 |
+
heads: int,
|
| 533 |
+
layers: int,
|
| 534 |
+
normal_pe: bool = False,
|
| 535 |
+
qkv_bias: bool = True,
|
| 536 |
+
use_ln_post: bool = False,
|
| 537 |
+
use_checkpoint: bool = False,
|
| 538 |
+
qk_norm: bool = False
|
| 539 |
+
):
|
| 540 |
+
|
| 541 |
+
super().__init__()
|
| 542 |
+
|
| 543 |
+
self.use_checkpoint = use_checkpoint
|
| 544 |
+
self.num_latents = num_latents
|
| 545 |
+
self.downsample_ratio = downsample_ratio
|
| 546 |
+
self.point_feats = point_feats
|
| 547 |
+
self.normal_pe = normal_pe
|
| 548 |
+
|
| 549 |
+
if pc_sharpedge_size == 0:
|
| 550 |
+
print(
|
| 551 |
+
f'PointCrossAttentionEncoder INFO: pc_sharpedge_size is not given, using pc_size as pc_sharpedge_size')
|
| 552 |
+
else:
|
| 553 |
+
print(
|
| 554 |
+
f'PointCrossAttentionEncoder INFO: pc_sharpedge_size is given, using pc_size={pc_size}, pc_sharpedge_size={pc_sharpedge_size}')
|
| 555 |
+
|
| 556 |
+
self.pc_size = pc_size
|
| 557 |
+
self.pc_sharpedge_size = pc_sharpedge_size
|
| 558 |
+
|
| 559 |
+
self.fourier_embedder = fourier_embedder
|
| 560 |
+
|
| 561 |
+
self.input_proj = nn.Linear(self.fourier_embedder.out_dim + point_feats, width)
|
| 562 |
+
self.cross_attn = ResidualCrossAttentionBlock(
|
| 563 |
+
width=width,
|
| 564 |
+
heads=heads,
|
| 565 |
+
qkv_bias=qkv_bias,
|
| 566 |
+
qk_norm=qk_norm
|
| 567 |
+
)
|
| 568 |
+
|
| 569 |
+
self.self_attn = None
|
| 570 |
+
if layers > 0:
|
| 571 |
+
self.self_attn = Transformer(
|
| 572 |
+
n_ctx=num_latents,
|
| 573 |
+
width=width,
|
| 574 |
+
layers=layers,
|
| 575 |
+
heads=heads,
|
| 576 |
+
qkv_bias=qkv_bias,
|
| 577 |
+
qk_norm=qk_norm
|
| 578 |
+
)
|
| 579 |
+
|
| 580 |
+
if use_ln_post:
|
| 581 |
+
self.ln_post = nn.LayerNorm(width)
|
| 582 |
+
else:
|
| 583 |
+
self.ln_post = None
|
| 584 |
+
|
| 585 |
+
def sample_points_and_latents(self, pc: torch.FloatTensor, feats: Optional[torch.FloatTensor] = None):
|
| 586 |
+
B, N, D = pc.shape
|
| 587 |
+
num_pts = self.num_latents * self.downsample_ratio
|
| 588 |
+
|
| 589 |
+
# Compute number of latents
|
| 590 |
+
num_latents = int(num_pts / self.downsample_ratio)
|
| 591 |
+
|
| 592 |
+
# Compute the number of random and sharpedge latents
|
| 593 |
+
num_random_query = self.pc_size / (self.pc_size + self.pc_sharpedge_size) * num_latents
|
| 594 |
+
num_sharpedge_query = num_latents - num_random_query
|
| 595 |
+
|
| 596 |
+
# Split random and sharpedge surface points
|
| 597 |
+
random_pc, sharpedge_pc = torch.split(pc, [self.pc_size, self.pc_sharpedge_size], dim=1)
|
| 598 |
+
assert random_pc.shape[1] <= self.pc_size, "Random surface points size must be less than or equal to pc_size"
|
| 599 |
+
assert sharpedge_pc.shape[
|
| 600 |
+
1] <= self.pc_sharpedge_size, "Sharpedge surface points size must be less than or equal to pc_sharpedge_size"
|
| 601 |
+
|
| 602 |
+
# Randomly select random surface points and random query points
|
| 603 |
+
input_random_pc_size = int(num_random_query * self.downsample_ratio)
|
| 604 |
+
random_query_ratio = num_random_query / input_random_pc_size
|
| 605 |
+
idx_random_pc = torch.randperm(random_pc.shape[1], device=random_pc.device)[:input_random_pc_size]
|
| 606 |
+
input_random_pc = random_pc[:, idx_random_pc, :]
|
| 607 |
+
flatten_input_random_pc = input_random_pc.view(B * input_random_pc_size, D)
|
| 608 |
+
N_down = int(flatten_input_random_pc.shape[0] / B)
|
| 609 |
+
batch_down = torch.arange(B).to(pc.device)
|
| 610 |
+
batch_down = torch.repeat_interleave(batch_down, N_down)
|
| 611 |
+
idx_query_random = fps(flatten_input_random_pc, batch_down, ratio=random_query_ratio)
|
| 612 |
+
query_random_pc = flatten_input_random_pc[idx_query_random].view(B, -1, D)
|
| 613 |
+
|
| 614 |
+
# Randomly select sharpedge surface points and sharpedge query points
|
| 615 |
+
input_sharpedge_pc_size = int(num_sharpedge_query * self.downsample_ratio)
|
| 616 |
+
if input_sharpedge_pc_size == 0:
|
| 617 |
+
input_sharpedge_pc = torch.zeros(B, 0, D, dtype=input_random_pc.dtype).to(pc.device)
|
| 618 |
+
query_sharpedge_pc = torch.zeros(B, 0, D, dtype=query_random_pc.dtype).to(pc.device)
|
| 619 |
+
else:
|
| 620 |
+
sharpedge_query_ratio = num_sharpedge_query / input_sharpedge_pc_size
|
| 621 |
+
idx_sharpedge_pc = torch.randperm(sharpedge_pc.shape[1], device=sharpedge_pc.device)[
|
| 622 |
+
:input_sharpedge_pc_size]
|
| 623 |
+
input_sharpedge_pc = sharpedge_pc[:, idx_sharpedge_pc, :]
|
| 624 |
+
flatten_input_sharpedge_surface_points = input_sharpedge_pc.view(B * input_sharpedge_pc_size, D)
|
| 625 |
+
N_down = int(flatten_input_sharpedge_surface_points.shape[0] / B)
|
| 626 |
+
batch_down = torch.arange(B).to(pc.device)
|
| 627 |
+
batch_down = torch.repeat_interleave(batch_down, N_down)
|
| 628 |
+
idx_query_sharpedge = fps(flatten_input_sharpedge_surface_points, batch_down, ratio=sharpedge_query_ratio)
|
| 629 |
+
query_sharpedge_pc = flatten_input_sharpedge_surface_points[idx_query_sharpedge].view(B, -1, D)
|
| 630 |
+
|
| 631 |
+
# Concatenate random and sharpedge surface points and query points
|
| 632 |
+
query_pc = torch.cat([query_random_pc, query_sharpedge_pc], dim=1)
|
| 633 |
+
input_pc = torch.cat([input_random_pc, input_sharpedge_pc], dim=1)
|
| 634 |
+
|
| 635 |
+
# PE
|
| 636 |
+
query = self.fourier_embedder(query_pc)
|
| 637 |
+
data = self.fourier_embedder(input_pc)
|
| 638 |
+
|
| 639 |
+
# Concat normal if given
|
| 640 |
+
if self.point_feats != 0:
|
| 641 |
+
|
| 642 |
+
random_surface_feats, sharpedge_surface_feats = torch.split(feats, [self.pc_size, self.pc_sharpedge_size],
|
| 643 |
+
dim=1)
|
| 644 |
+
input_random_surface_feats = random_surface_feats[:, idx_random_pc, :]
|
| 645 |
+
flatten_input_random_surface_feats = input_random_surface_feats.view(B * input_random_pc_size, -1)
|
| 646 |
+
query_random_feats = flatten_input_random_surface_feats[idx_query_random].view(B, -1,
|
| 647 |
+
flatten_input_random_surface_feats.shape[
|
| 648 |
+
-1])
|
| 649 |
+
|
| 650 |
+
if input_sharpedge_pc_size == 0:
|
| 651 |
+
input_sharpedge_surface_feats = torch.zeros(B, 0, self.point_feats,
|
| 652 |
+
dtype=input_random_surface_feats.dtype).to(pc.device)
|
| 653 |
+
query_sharpedge_feats = torch.zeros(B, 0, self.point_feats, dtype=query_random_feats.dtype).to(
|
| 654 |
+
pc.device)
|
| 655 |
+
else:
|
| 656 |
+
input_sharpedge_surface_feats = sharpedge_surface_feats[:, idx_sharpedge_pc, :]
|
| 657 |
+
flatten_input_sharpedge_surface_feats = input_sharpedge_surface_feats.view(B * input_sharpedge_pc_size,
|
| 658 |
+
-1)
|
| 659 |
+
query_sharpedge_feats = flatten_input_sharpedge_surface_feats[idx_query_sharpedge].view(B, -1,
|
| 660 |
+
flatten_input_sharpedge_surface_feats.shape[
|
| 661 |
+
-1])
|
| 662 |
+
|
| 663 |
+
query_feats = torch.cat([query_random_feats, query_sharpedge_feats], dim=1)
|
| 664 |
+
input_feats = torch.cat([input_random_surface_feats, input_sharpedge_surface_feats], dim=1)
|
| 665 |
+
|
| 666 |
+
if self.normal_pe:
|
| 667 |
+
query_normal_pe = self.fourier_embedder(query_feats[..., :3])
|
| 668 |
+
input_normal_pe = self.fourier_embedder(input_feats[..., :3])
|
| 669 |
+
query_feats = torch.cat([query_normal_pe, query_feats[..., 3:]], dim=-1)
|
| 670 |
+
input_feats = torch.cat([input_normal_pe, input_feats[..., 3:]], dim=-1)
|
| 671 |
+
|
| 672 |
+
query = torch.cat([query, query_feats], dim=-1)
|
| 673 |
+
data = torch.cat([data, input_feats], dim=-1)
|
| 674 |
+
|
| 675 |
+
if input_sharpedge_pc_size == 0:
|
| 676 |
+
query_sharpedge_pc = torch.zeros(B, 1, D).to(pc.device)
|
| 677 |
+
input_sharpedge_pc = torch.zeros(B, 1, D).to(pc.device)
|
| 678 |
+
|
| 679 |
+
# print(f'query_pc: {query_pc.shape}')
|
| 680 |
+
# print(f'input_pc: {input_pc.shape}')
|
| 681 |
+
# print(f'query_random_pc: {query_random_pc.shape}')
|
| 682 |
+
# print(f'input_random_pc: {input_random_pc.shape}')
|
| 683 |
+
# print(f'query_sharpedge_pc: {query_sharpedge_pc.shape}')
|
| 684 |
+
# print(f'input_sharpedge_pc: {input_sharpedge_pc.shape}')
|
| 685 |
+
|
| 686 |
+
return query.view(B, -1, query.shape[-1]), data.view(B, -1, data.shape[-1]), [query_pc, input_pc,
|
| 687 |
+
query_random_pc, input_random_pc,
|
| 688 |
+
query_sharpedge_pc,
|
| 689 |
+
input_sharpedge_pc]
|
| 690 |
+
|
| 691 |
+
def forward(self, pc, feats):
|
| 692 |
+
"""
|
| 693 |
+
|
| 694 |
+
Args:
|
| 695 |
+
pc (torch.FloatTensor): [B, N, 3]
|
| 696 |
+
feats (torch.FloatTensor or None): [B, N, C]
|
| 697 |
+
|
| 698 |
+
Returns:
|
| 699 |
+
|
| 700 |
+
"""
|
| 701 |
+
|
| 702 |
+
query, data, pc_infos = self.sample_points_and_latents(pc, feats)
|
| 703 |
+
|
| 704 |
+
query = self.input_proj(query)
|
| 705 |
+
query = query
|
| 706 |
+
data = self.input_proj(data)
|
| 707 |
+
data = data
|
| 708 |
+
|
| 709 |
+
latents = self.cross_attn(query, data)
|
| 710 |
+
if self.self_attn is not None:
|
| 711 |
+
latents = self.self_attn(latents)
|
| 712 |
+
|
| 713 |
+
if self.ln_post is not None:
|
| 714 |
+
latents = self.ln_post(latents)
|
| 715 |
+
|
| 716 |
+
return latents, pc_infos
|
Hunyuan3D-2.1/hy3dshape/hy3dshape/models/autoencoders/attention_processors.py
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Hunyuan 3D is licensed under the TENCENT HUNYUAN NON-COMMERCIAL LICENSE AGREEMENT
|
| 2 |
+
# except for the third-party components listed below.
|
| 3 |
+
# Hunyuan 3D does not impose any additional limitations beyond what is outlined
|
| 4 |
+
# in the repsective licenses of these third-party components.
|
| 5 |
+
# Users must comply with all terms and conditions of original licenses of these third-party
|
| 6 |
+
# components and must ensure that the usage of the third party components adheres to
|
| 7 |
+
# all relevant laws and regulations.
|
| 8 |
+
|
| 9 |
+
# For avoidance of doubts, Hunyuan 3D means the large language models and
|
| 10 |
+
# their software and algorithms, including trained model weights, parameters (including
|
| 11 |
+
# optimizer states), machine-learning model code, inference-enabling code, training-enabling code,
|
| 12 |
+
# fine-tuning enabling code and other elements of the foregoing made publicly available
|
| 13 |
+
# by Tencent in accordance with TENCENT HUNYUAN COMMUNITY LICENSE AGREEMENT.
|
| 14 |
+
|
| 15 |
+
import os
|
| 16 |
+
|
| 17 |
+
import torch
|
| 18 |
+
import torch.nn.functional as F
|
| 19 |
+
|
| 20 |
+
scaled_dot_product_attention = F.scaled_dot_product_attention
|
| 21 |
+
if os.environ.get('CA_USE_SAGEATTN', '0') == '1':
|
| 22 |
+
try:
|
| 23 |
+
from sageattention import sageattn
|
| 24 |
+
except ImportError:
|
| 25 |
+
raise ImportError('Please install the package "sageattention" to use this USE_SAGEATTN.')
|
| 26 |
+
scaled_dot_product_attention = sageattn
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
class CrossAttentionProcessor:
|
| 30 |
+
def __call__(self, attn, q, k, v):
|
| 31 |
+
out = scaled_dot_product_attention(q, k, v)
|
| 32 |
+
return out
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
class FlashVDMCrossAttentionProcessor:
|
| 36 |
+
def __init__(self, topk=None):
|
| 37 |
+
self.topk = topk
|
| 38 |
+
|
| 39 |
+
def __call__(self, attn, q, k, v):
|
| 40 |
+
if k.shape[-2] == 3072:
|
| 41 |
+
topk = 1024
|
| 42 |
+
elif k.shape[-2] == 512:
|
| 43 |
+
topk = 256
|
| 44 |
+
else:
|
| 45 |
+
topk = k.shape[-2] // 3
|
| 46 |
+
|
| 47 |
+
if self.topk is True:
|
| 48 |
+
q1 = q[:, :, ::100, :]
|
| 49 |
+
sim = q1 @ k.transpose(-1, -2)
|
| 50 |
+
sim = torch.mean(sim, -2)
|
| 51 |
+
topk_ind = torch.topk(sim, dim=-1, k=topk).indices.squeeze(-2).unsqueeze(-1)
|
| 52 |
+
topk_ind = topk_ind.expand(-1, -1, -1, v.shape[-1])
|
| 53 |
+
v0 = torch.gather(v, dim=-2, index=topk_ind)
|
| 54 |
+
k0 = torch.gather(k, dim=-2, index=topk_ind)
|
| 55 |
+
out = scaled_dot_product_attention(q, k0, v0)
|
| 56 |
+
elif self.topk is False:
|
| 57 |
+
out = scaled_dot_product_attention(q, k, v)
|
| 58 |
+
else:
|
| 59 |
+
idx, counts = self.topk
|
| 60 |
+
start = 0
|
| 61 |
+
outs = []
|
| 62 |
+
for grid_coord, count in zip(idx, counts):
|
| 63 |
+
end = start + count
|
| 64 |
+
q_chunk = q[:, :, start:end, :]
|
| 65 |
+
k0, v0 = self.select_topkv(q_chunk, k, v, topk)
|
| 66 |
+
out = scaled_dot_product_attention(q_chunk, k0, v0)
|
| 67 |
+
outs.append(out)
|
| 68 |
+
start += count
|
| 69 |
+
out = torch.cat(outs, dim=-2)
|
| 70 |
+
self.topk = False
|
| 71 |
+
return out
|
| 72 |
+
|
| 73 |
+
def select_topkv(self, q_chunk, k, v, topk):
|
| 74 |
+
q1 = q_chunk[:, :, ::50, :]
|
| 75 |
+
sim = q1 @ k.transpose(-1, -2)
|
| 76 |
+
sim = torch.mean(sim, -2)
|
| 77 |
+
topk_ind = torch.topk(sim, dim=-1, k=topk).indices.squeeze(-2).unsqueeze(-1)
|
| 78 |
+
topk_ind = topk_ind.expand(-1, -1, -1, v.shape[-1])
|
| 79 |
+
v0 = torch.gather(v, dim=-2, index=topk_ind)
|
| 80 |
+
k0 = torch.gather(k, dim=-2, index=topk_ind)
|
| 81 |
+
return k0, v0
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
class FlashVDMTopMCrossAttentionProcessor(FlashVDMCrossAttentionProcessor):
|
| 85 |
+
def select_topkv(self, q_chunk, k, v, topk):
|
| 86 |
+
q1 = q_chunk[:, :, ::30, :]
|
| 87 |
+
sim = q1 @ k.transpose(-1, -2)
|
| 88 |
+
# sim = sim.to(torch.float32)
|
| 89 |
+
sim = sim.softmax(-1)
|
| 90 |
+
sim = torch.mean(sim, 1)
|
| 91 |
+
activated_token = torch.where(sim > 1e-6)[2]
|
| 92 |
+
index = torch.unique(activated_token, return_counts=True)[0].unsqueeze(0).unsqueeze(0).unsqueeze(-1)
|
| 93 |
+
index = index.expand(-1, v.shape[1], -1, v.shape[-1])
|
| 94 |
+
v0 = torch.gather(v, dim=-2, index=index)
|
| 95 |
+
k0 = torch.gather(k, dim=-2, index=index)
|
| 96 |
+
return k0, v0
|
Hunyuan3D-2.1/hy3dshape/hy3dshape/models/autoencoders/model.py
ADDED
|
@@ -0,0 +1,339 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Open Source Model Licensed under the Apache License Version 2.0
|
| 2 |
+
# and Other Licenses of the Third-Party Components therein:
|
| 3 |
+
# The below Model in this distribution may have been modified by THL A29 Limited
|
| 4 |
+
# ("Tencent Modifications"). All Tencent Modifications are Copyright (C) 2024 THL A29 Limited.
|
| 5 |
+
|
| 6 |
+
# Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved.
|
| 7 |
+
# The below software and/or models in this distribution may have been
|
| 8 |
+
# modified by THL A29 Limited ("Tencent Modifications").
|
| 9 |
+
# All Tencent Modifications are Copyright (C) THL A29 Limited.
|
| 10 |
+
|
| 11 |
+
# Hunyuan 3D is licensed under the TENCENT HUNYUAN NON-COMMERCIAL LICENSE AGREEMENT
|
| 12 |
+
# except for the third-party components listed below.
|
| 13 |
+
# Hunyuan 3D does not impose any additional limitations beyond what is outlined
|
| 14 |
+
# in the repsective licenses of these third-party components.
|
| 15 |
+
# Users must comply with all terms and conditions of original licenses of these third-party
|
| 16 |
+
# components and must ensure that the usage of the third party components adheres to
|
| 17 |
+
# all relevant laws and regulations.
|
| 18 |
+
|
| 19 |
+
# For avoidance of doubts, Hunyuan 3D means the large language models and
|
| 20 |
+
# their software and algorithms, including trained model weights, parameters (including
|
| 21 |
+
# optimizer states), machine-learning model code, inference-enabling code, training-enabling code,
|
| 22 |
+
# fine-tuning enabling code and other elements of the foregoing made publicly available
|
| 23 |
+
# by Tencent in accordance with TENCENT HUNYUAN COMMUNITY LICENSE AGREEMENT.
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
import os
|
| 27 |
+
from typing import Union, List
|
| 28 |
+
|
| 29 |
+
import numpy as np
|
| 30 |
+
import torch
|
| 31 |
+
import torch.nn as nn
|
| 32 |
+
import yaml
|
| 33 |
+
|
| 34 |
+
from .attention_blocks import FourierEmbedder, Transformer, CrossAttentionDecoder, PointCrossAttentionEncoder
|
| 35 |
+
from .surface_extractors import MCSurfaceExtractor, SurfaceExtractors
|
| 36 |
+
from .volume_decoders import VanillaVolumeDecoder, FlashVDMVolumeDecoding, HierarchicalVolumeDecoding
|
| 37 |
+
from ...utils import logger, synchronize_timer, smart_load_model
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
class DiagonalGaussianDistribution(object):
|
| 41 |
+
def __init__(self, parameters: Union[torch.Tensor, List[torch.Tensor]], deterministic=False, feat_dim=1):
|
| 42 |
+
"""
|
| 43 |
+
Initialize a diagonal Gaussian distribution with mean and log-variance parameters.
|
| 44 |
+
|
| 45 |
+
Args:
|
| 46 |
+
parameters (Union[torch.Tensor, List[torch.Tensor]]):
|
| 47 |
+
Either a single tensor containing concatenated mean and log-variance along `feat_dim`,
|
| 48 |
+
or a list of two tensors [mean, logvar].
|
| 49 |
+
deterministic (bool, optional): If True, the distribution is deterministic (zero variance).
|
| 50 |
+
Default is False. feat_dim (int, optional): Dimension along which mean and logvar are
|
| 51 |
+
concatenated if parameters is a single tensor. Default is 1.
|
| 52 |
+
"""
|
| 53 |
+
self.feat_dim = feat_dim
|
| 54 |
+
self.parameters = parameters
|
| 55 |
+
|
| 56 |
+
if isinstance(parameters, list):
|
| 57 |
+
self.mean = parameters[0]
|
| 58 |
+
self.logvar = parameters[1]
|
| 59 |
+
else:
|
| 60 |
+
self.mean, self.logvar = torch.chunk(parameters, 2, dim=feat_dim)
|
| 61 |
+
|
| 62 |
+
self.logvar = torch.clamp(self.logvar, -30.0, 20.0)
|
| 63 |
+
self.deterministic = deterministic
|
| 64 |
+
self.std = torch.exp(0.5 * self.logvar)
|
| 65 |
+
self.var = torch.exp(self.logvar)
|
| 66 |
+
if self.deterministic:
|
| 67 |
+
self.var = self.std = torch.zeros_like(self.mean)
|
| 68 |
+
|
| 69 |
+
def sample(self):
|
| 70 |
+
"""
|
| 71 |
+
Sample from the diagonal Gaussian distribution.
|
| 72 |
+
|
| 73 |
+
Returns:
|
| 74 |
+
torch.Tensor: A sample tensor with the same shape as the mean.
|
| 75 |
+
"""
|
| 76 |
+
x = self.mean + self.std * torch.randn_like(self.mean)
|
| 77 |
+
return x
|
| 78 |
+
|
| 79 |
+
def kl(self, other=None, dims=(1, 2, 3)):
|
| 80 |
+
"""
|
| 81 |
+
Compute the Kullback-Leibler (KL) divergence between this distribution and another.
|
| 82 |
+
|
| 83 |
+
If `other` is None, compute KL divergence to a standard normal distribution N(0, I).
|
| 84 |
+
|
| 85 |
+
Args:
|
| 86 |
+
other (DiagonalGaussianDistribution, optional): Another diagonal Gaussian distribution.
|
| 87 |
+
dims (tuple, optional): Dimensions along which to compute the mean KL divergence.
|
| 88 |
+
Default is (1, 2, 3).
|
| 89 |
+
|
| 90 |
+
Returns:
|
| 91 |
+
torch.Tensor: The mean KL divergence value.
|
| 92 |
+
"""
|
| 93 |
+
if self.deterministic:
|
| 94 |
+
return torch.Tensor([0.])
|
| 95 |
+
else:
|
| 96 |
+
if other is None:
|
| 97 |
+
return 0.5 * torch.mean(torch.pow(self.mean, 2)
|
| 98 |
+
+ self.var - 1.0 - self.logvar,
|
| 99 |
+
dim=dims)
|
| 100 |
+
else:
|
| 101 |
+
return 0.5 * torch.mean(
|
| 102 |
+
torch.pow(self.mean - other.mean, 2) / other.var
|
| 103 |
+
+ self.var / other.var - 1.0 - self.logvar + other.logvar,
|
| 104 |
+
dim=dims)
|
| 105 |
+
|
| 106 |
+
def nll(self, sample, dims=(1, 2, 3)):
|
| 107 |
+
if self.deterministic:
|
| 108 |
+
return torch.Tensor([0.])
|
| 109 |
+
logtwopi = np.log(2.0 * np.pi)
|
| 110 |
+
return 0.5 * torch.sum(
|
| 111 |
+
logtwopi + self.logvar + torch.pow(sample - self.mean, 2) / self.var,
|
| 112 |
+
dim=dims)
|
| 113 |
+
|
| 114 |
+
def mode(self):
|
| 115 |
+
return self.mean
|
| 116 |
+
|
| 117 |
+
|
| 118 |
+
class VectsetVAE(nn.Module):
|
| 119 |
+
|
| 120 |
+
@classmethod
|
| 121 |
+
@synchronize_timer('VectsetVAE Model Loading')
|
| 122 |
+
def from_single_file(
|
| 123 |
+
cls,
|
| 124 |
+
ckpt_path,
|
| 125 |
+
config_path,
|
| 126 |
+
device='cuda',
|
| 127 |
+
dtype=torch.float16,
|
| 128 |
+
use_safetensors=None,
|
| 129 |
+
**kwargs,
|
| 130 |
+
):
|
| 131 |
+
# load config
|
| 132 |
+
with open(config_path, 'r') as f:
|
| 133 |
+
config = yaml.safe_load(f)
|
| 134 |
+
|
| 135 |
+
# load ckpt
|
| 136 |
+
if use_safetensors:
|
| 137 |
+
ckpt_path = ckpt_path.replace('.ckpt', '.safetensors')
|
| 138 |
+
if not os.path.exists(ckpt_path):
|
| 139 |
+
raise FileNotFoundError(f"Model file {ckpt_path} not found")
|
| 140 |
+
|
| 141 |
+
logger.info(f"Loading model from {ckpt_path}")
|
| 142 |
+
if use_safetensors:
|
| 143 |
+
import safetensors.torch
|
| 144 |
+
ckpt = safetensors.torch.load_file(ckpt_path, device='cpu')
|
| 145 |
+
else:
|
| 146 |
+
ckpt = torch.load(ckpt_path, map_location='cpu', weights_only=True)
|
| 147 |
+
|
| 148 |
+
model_kwargs = config['params']
|
| 149 |
+
model_kwargs.update(kwargs)
|
| 150 |
+
|
| 151 |
+
model = cls(**model_kwargs)
|
| 152 |
+
model.load_state_dict(ckpt)
|
| 153 |
+
model.to(device=device, dtype=dtype)
|
| 154 |
+
return model
|
| 155 |
+
|
| 156 |
+
@classmethod
|
| 157 |
+
def from_pretrained(
|
| 158 |
+
cls,
|
| 159 |
+
model_path,
|
| 160 |
+
device='cuda',
|
| 161 |
+
dtype=torch.float16,
|
| 162 |
+
use_safetensors=False,
|
| 163 |
+
variant='fp16',
|
| 164 |
+
subfolder='hunyuan3d-vae-v2-1',
|
| 165 |
+
**kwargs,
|
| 166 |
+
):
|
| 167 |
+
config_path, ckpt_path = smart_load_model(
|
| 168 |
+
model_path,
|
| 169 |
+
subfolder=subfolder,
|
| 170 |
+
use_safetensors=use_safetensors,
|
| 171 |
+
variant=variant
|
| 172 |
+
)
|
| 173 |
+
|
| 174 |
+
return cls.from_single_file(
|
| 175 |
+
ckpt_path,
|
| 176 |
+
config_path,
|
| 177 |
+
device=device,
|
| 178 |
+
dtype=dtype,
|
| 179 |
+
use_safetensors=use_safetensors,
|
| 180 |
+
**kwargs
|
| 181 |
+
)
|
| 182 |
+
|
| 183 |
+
def init_from_ckpt(self, path, ignore_keys=()):
|
| 184 |
+
state_dict = torch.load(path, map_location="cpu")
|
| 185 |
+
state_dict = state_dict.get("state_dict", state_dict)
|
| 186 |
+
keys = list(state_dict.keys())
|
| 187 |
+
for k in keys:
|
| 188 |
+
for ik in ignore_keys:
|
| 189 |
+
if k.startswith(ik):
|
| 190 |
+
print("Deleting key {} from state_dict.".format(k))
|
| 191 |
+
del state_dict[k]
|
| 192 |
+
missing, unexpected = self.load_state_dict(state_dict, strict=False)
|
| 193 |
+
print(f"Restored from {path} with {len(missing)} missing and {len(unexpected)} unexpected keys")
|
| 194 |
+
if len(missing) > 0:
|
| 195 |
+
print(f"Missing Keys: {missing}")
|
| 196 |
+
print(f"Unexpected Keys: {unexpected}")
|
| 197 |
+
|
| 198 |
+
def __init__(
|
| 199 |
+
self,
|
| 200 |
+
volume_decoder=None,
|
| 201 |
+
surface_extractor=None
|
| 202 |
+
):
|
| 203 |
+
super().__init__()
|
| 204 |
+
if volume_decoder is None:
|
| 205 |
+
volume_decoder = VanillaVolumeDecoder()
|
| 206 |
+
if surface_extractor is None:
|
| 207 |
+
surface_extractor = MCSurfaceExtractor()
|
| 208 |
+
self.volume_decoder = volume_decoder
|
| 209 |
+
self.surface_extractor = surface_extractor
|
| 210 |
+
|
| 211 |
+
def latents2mesh(self, latents: torch.FloatTensor, **kwargs):
|
| 212 |
+
with synchronize_timer('Volume decoding'):
|
| 213 |
+
grid_logits = self.volume_decoder(latents, self.geo_decoder, **kwargs)
|
| 214 |
+
with synchronize_timer('Surface extraction'):
|
| 215 |
+
outputs = self.surface_extractor(grid_logits, **kwargs)
|
| 216 |
+
return outputs
|
| 217 |
+
|
| 218 |
+
def enable_flashvdm_decoder(
|
| 219 |
+
self,
|
| 220 |
+
enabled: bool = True,
|
| 221 |
+
adaptive_kv_selection=True,
|
| 222 |
+
topk_mode='mean',
|
| 223 |
+
mc_algo='dmc',
|
| 224 |
+
):
|
| 225 |
+
if enabled:
|
| 226 |
+
if adaptive_kv_selection:
|
| 227 |
+
self.volume_decoder = FlashVDMVolumeDecoding(topk_mode)
|
| 228 |
+
else:
|
| 229 |
+
self.volume_decoder = HierarchicalVolumeDecoding()
|
| 230 |
+
if mc_algo not in SurfaceExtractors.keys():
|
| 231 |
+
raise ValueError(f'Unsupported mc_algo {mc_algo}, available:{list(SurfaceExtractors.keys())}')
|
| 232 |
+
self.surface_extractor = SurfaceExtractors[mc_algo]()
|
| 233 |
+
else:
|
| 234 |
+
self.volume_decoder = VanillaVolumeDecoder()
|
| 235 |
+
self.surface_extractor = MCSurfaceExtractor()
|
| 236 |
+
|
| 237 |
+
|
| 238 |
+
class ShapeVAE(VectsetVAE):
|
| 239 |
+
def __init__(
|
| 240 |
+
self,
|
| 241 |
+
*,
|
| 242 |
+
num_latents: int,
|
| 243 |
+
embed_dim: int,
|
| 244 |
+
width: int,
|
| 245 |
+
heads: int,
|
| 246 |
+
num_decoder_layers: int,
|
| 247 |
+
num_encoder_layers: int = 8,
|
| 248 |
+
pc_size: int = 5120,
|
| 249 |
+
pc_sharpedge_size: int = 5120,
|
| 250 |
+
point_feats: int = 3,
|
| 251 |
+
downsample_ratio: int = 20,
|
| 252 |
+
geo_decoder_downsample_ratio: int = 1,
|
| 253 |
+
geo_decoder_mlp_expand_ratio: int = 4,
|
| 254 |
+
geo_decoder_ln_post: bool = True,
|
| 255 |
+
num_freqs: int = 8,
|
| 256 |
+
include_pi: bool = True,
|
| 257 |
+
qkv_bias: bool = True,
|
| 258 |
+
qk_norm: bool = False,
|
| 259 |
+
label_type: str = "binary",
|
| 260 |
+
drop_path_rate: float = 0.0,
|
| 261 |
+
scale_factor: float = 1.0,
|
| 262 |
+
use_ln_post: bool = True,
|
| 263 |
+
ckpt_path = None
|
| 264 |
+
):
|
| 265 |
+
super().__init__()
|
| 266 |
+
self.geo_decoder_ln_post = geo_decoder_ln_post
|
| 267 |
+
self.downsample_ratio = downsample_ratio
|
| 268 |
+
|
| 269 |
+
self.fourier_embedder = FourierEmbedder(num_freqs=num_freqs, include_pi=include_pi)
|
| 270 |
+
|
| 271 |
+
self.encoder = PointCrossAttentionEncoder(
|
| 272 |
+
fourier_embedder=self.fourier_embedder,
|
| 273 |
+
num_latents=num_latents,
|
| 274 |
+
downsample_ratio=self.downsample_ratio,
|
| 275 |
+
pc_size=pc_size,
|
| 276 |
+
pc_sharpedge_size=pc_sharpedge_size,
|
| 277 |
+
point_feats=point_feats,
|
| 278 |
+
width=width,
|
| 279 |
+
heads=heads,
|
| 280 |
+
layers=num_encoder_layers,
|
| 281 |
+
qkv_bias=qkv_bias,
|
| 282 |
+
use_ln_post=use_ln_post,
|
| 283 |
+
qk_norm=qk_norm
|
| 284 |
+
)
|
| 285 |
+
|
| 286 |
+
self.pre_kl = nn.Linear(width, embed_dim * 2)
|
| 287 |
+
self.post_kl = nn.Linear(embed_dim, width)
|
| 288 |
+
|
| 289 |
+
self.transformer = Transformer(
|
| 290 |
+
n_ctx=num_latents,
|
| 291 |
+
width=width,
|
| 292 |
+
layers=num_decoder_layers,
|
| 293 |
+
heads=heads,
|
| 294 |
+
qkv_bias=qkv_bias,
|
| 295 |
+
qk_norm=qk_norm,
|
| 296 |
+
drop_path_rate=drop_path_rate
|
| 297 |
+
)
|
| 298 |
+
|
| 299 |
+
self.geo_decoder = CrossAttentionDecoder(
|
| 300 |
+
fourier_embedder=self.fourier_embedder,
|
| 301 |
+
out_channels=1,
|
| 302 |
+
num_latents=num_latents,
|
| 303 |
+
mlp_expand_ratio=geo_decoder_mlp_expand_ratio,
|
| 304 |
+
downsample_ratio=geo_decoder_downsample_ratio,
|
| 305 |
+
enable_ln_post=self.geo_decoder_ln_post,
|
| 306 |
+
width=width // geo_decoder_downsample_ratio,
|
| 307 |
+
heads=heads // geo_decoder_downsample_ratio,
|
| 308 |
+
qkv_bias=qkv_bias,
|
| 309 |
+
qk_norm=qk_norm,
|
| 310 |
+
label_type=label_type,
|
| 311 |
+
)
|
| 312 |
+
|
| 313 |
+
self.scale_factor = scale_factor
|
| 314 |
+
self.latent_shape = (num_latents, embed_dim)
|
| 315 |
+
|
| 316 |
+
if ckpt_path is not None:
|
| 317 |
+
self.init_from_ckpt(ckpt_path)
|
| 318 |
+
|
| 319 |
+
def forward(self, latents):
|
| 320 |
+
latents = self.post_kl(latents)
|
| 321 |
+
latents = self.transformer(latents)
|
| 322 |
+
return latents
|
| 323 |
+
|
| 324 |
+
def encode(self, surface, sample_posterior=True):
|
| 325 |
+
pc, feats = surface[:, :, :3], surface[:, :, 3:]
|
| 326 |
+
latents, _ = self.encoder(pc, feats)
|
| 327 |
+
# print(latents.shape, self.pre_kl.weight.shape)
|
| 328 |
+
moments = self.pre_kl(latents)
|
| 329 |
+
posterior = DiagonalGaussianDistribution(moments, feat_dim=-1)
|
| 330 |
+
if sample_posterior:
|
| 331 |
+
latents = posterior.sample()
|
| 332 |
+
else:
|
| 333 |
+
latents = posterior.mode()
|
| 334 |
+
return latents
|
| 335 |
+
|
| 336 |
+
def decode(self, latents):
|
| 337 |
+
latents = self.post_kl(latents)
|
| 338 |
+
latents = self.transformer(latents)
|
| 339 |
+
return latents
|
Hunyuan3D-2.1/hy3dshape/hy3dshape/models/autoencoders/surface_extractors.py
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Hunyuan 3D is licensed under the TENCENT HUNYUAN NON-COMMERCIAL LICENSE AGREEMENT
|
| 2 |
+
# except for the third-party components listed below.
|
| 3 |
+
# Hunyuan 3D does not impose any additional limitations beyond what is outlined
|
| 4 |
+
# in the repsective licenses of these third-party components.
|
| 5 |
+
# Users must comply with all terms and conditions of original licenses of these third-party
|
| 6 |
+
# components and must ensure that the usage of the third party components adheres to
|
| 7 |
+
# all relevant laws and regulations.
|
| 8 |
+
|
| 9 |
+
# For avoidance of doubts, Hunyuan 3D means the large language models and
|
| 10 |
+
# their software and algorithms, including trained model weights, parameters (including
|
| 11 |
+
# optimizer states), machine-learning model code, inference-enabling code, training-enabling code,
|
| 12 |
+
# fine-tuning enabling code and other elements of the foregoing made publicly available
|
| 13 |
+
# by Tencent in accordance with TENCENT HUNYUAN COMMUNITY LICENSE AGREEMENT.
|
| 14 |
+
|
| 15 |
+
from typing import Union, Tuple, List
|
| 16 |
+
|
| 17 |
+
import numpy as np
|
| 18 |
+
import torch
|
| 19 |
+
from skimage import measure
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
class Latent2MeshOutput:
|
| 23 |
+
def __init__(self, mesh_v=None, mesh_f=None):
|
| 24 |
+
self.mesh_v = mesh_v
|
| 25 |
+
self.mesh_f = mesh_f
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
def center_vertices(vertices):
|
| 29 |
+
"""Translate the vertices so that bounding box is centered at zero."""
|
| 30 |
+
vert_min = vertices.min(dim=0)[0]
|
| 31 |
+
vert_max = vertices.max(dim=0)[0]
|
| 32 |
+
vert_center = 0.5 * (vert_min + vert_max)
|
| 33 |
+
return vertices - vert_center
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
class SurfaceExtractor:
|
| 37 |
+
def _compute_box_stat(self, bounds: Union[Tuple[float], List[float], float], octree_resolution: int):
|
| 38 |
+
"""
|
| 39 |
+
Compute grid size, bounding box minimum coordinates, and bounding box size based on input
|
| 40 |
+
bounds and resolution.
|
| 41 |
+
|
| 42 |
+
Args:
|
| 43 |
+
bounds (Union[Tuple[float], List[float], float]): Bounding box coordinates or a single
|
| 44 |
+
float representing half side length.
|
| 45 |
+
If float, bounds are assumed symmetric around zero in all axes.
|
| 46 |
+
Expected format if list/tuple: [xmin, ymin, zmin, xmax, ymax, zmax].
|
| 47 |
+
octree_resolution (int): Resolution of the octree grid.
|
| 48 |
+
|
| 49 |
+
Returns:
|
| 50 |
+
grid_size (List[int]): Grid size along each axis (x, y, z), each equal to octree_resolution + 1.
|
| 51 |
+
bbox_min (np.ndarray): Minimum coordinates of the bounding box (xmin, ymin, zmin).
|
| 52 |
+
bbox_size (np.ndarray): Size of the bounding box along each axis (xmax - xmin, etc.).
|
| 53 |
+
"""
|
| 54 |
+
if isinstance(bounds, float):
|
| 55 |
+
bounds = [-bounds, -bounds, -bounds, bounds, bounds, bounds]
|
| 56 |
+
|
| 57 |
+
bbox_min, bbox_max = np.array(bounds[0:3]), np.array(bounds[3:6])
|
| 58 |
+
bbox_size = bbox_max - bbox_min
|
| 59 |
+
grid_size = [int(octree_resolution) + 1, int(octree_resolution) + 1, int(octree_resolution) + 1]
|
| 60 |
+
return grid_size, bbox_min, bbox_size
|
| 61 |
+
|
| 62 |
+
def run(self, *args, **kwargs):
|
| 63 |
+
"""
|
| 64 |
+
Abstract method to extract surface mesh from grid logits.
|
| 65 |
+
|
| 66 |
+
This method should be implemented by subclasses.
|
| 67 |
+
|
| 68 |
+
Raises:
|
| 69 |
+
NotImplementedError: Always, since this is an abstract method.
|
| 70 |
+
"""
|
| 71 |
+
return NotImplementedError
|
| 72 |
+
|
| 73 |
+
def __call__(self, grid_logits, **kwargs):
|
| 74 |
+
"""
|
| 75 |
+
Process a batch of grid logits to extract surface meshes.
|
| 76 |
+
|
| 77 |
+
Args:
|
| 78 |
+
grid_logits (torch.Tensor): Batch of grid logits with shape (batch_size, ...).
|
| 79 |
+
**kwargs: Additional keyword arguments passed to the `run` method.
|
| 80 |
+
|
| 81 |
+
Returns:
|
| 82 |
+
List[Optional[Latent2MeshOutput]]: List of mesh outputs for each grid in the batch.
|
| 83 |
+
If extraction fails for a grid, None is appended at that position.
|
| 84 |
+
"""
|
| 85 |
+
outputs = []
|
| 86 |
+
for i in range(grid_logits.shape[0]):
|
| 87 |
+
try:
|
| 88 |
+
vertices, faces = self.run(grid_logits[i], **kwargs)
|
| 89 |
+
vertices = vertices.astype(np.float32)
|
| 90 |
+
faces = np.ascontiguousarray(faces)
|
| 91 |
+
outputs.append(Latent2MeshOutput(mesh_v=vertices, mesh_f=faces))
|
| 92 |
+
|
| 93 |
+
except Exception:
|
| 94 |
+
import traceback
|
| 95 |
+
traceback.print_exc()
|
| 96 |
+
outputs.append(None)
|
| 97 |
+
|
| 98 |
+
return outputs
|
| 99 |
+
|
| 100 |
+
|
| 101 |
+
class MCSurfaceExtractor(SurfaceExtractor):
|
| 102 |
+
def run(self, grid_logit, *, mc_level, bounds, octree_resolution, **kwargs):
|
| 103 |
+
"""
|
| 104 |
+
Extract surface mesh using the Marching Cubes algorithm.
|
| 105 |
+
|
| 106 |
+
Args:
|
| 107 |
+
grid_logit (torch.Tensor): 3D grid logits tensor representing the scalar field.
|
| 108 |
+
mc_level (float): The level (iso-value) at which to extract the surface.
|
| 109 |
+
bounds (Union[Tuple[float], List[float], float]): Bounding box coordinates or half side length.
|
| 110 |
+
octree_resolution (int): Resolution of the octree grid.
|
| 111 |
+
**kwargs: Additional keyword arguments (ignored).
|
| 112 |
+
|
| 113 |
+
Returns:
|
| 114 |
+
Tuple[np.ndarray, np.ndarray]: Tuple containing:
|
| 115 |
+
- vertices (np.ndarray): Extracted mesh vertices, scaled and translated to bounding
|
| 116 |
+
box coordinates.
|
| 117 |
+
- faces (np.ndarray): Extracted mesh faces (triangles).
|
| 118 |
+
"""
|
| 119 |
+
vertices, faces, normals, _ = measure.marching_cubes(grid_logit.cpu().numpy(),
|
| 120 |
+
mc_level,
|
| 121 |
+
method="lewiner")
|
| 122 |
+
grid_size, bbox_min, bbox_size = self._compute_box_stat(bounds, octree_resolution)
|
| 123 |
+
vertices = vertices / grid_size * bbox_size + bbox_min
|
| 124 |
+
return vertices, faces
|
| 125 |
+
|
| 126 |
+
|
| 127 |
+
class DMCSurfaceExtractor(SurfaceExtractor):
|
| 128 |
+
def run(self, grid_logit, *, octree_resolution, **kwargs):
|
| 129 |
+
"""
|
| 130 |
+
Extract surface mesh using Differentiable Marching Cubes (DMC) algorithm.
|
| 131 |
+
|
| 132 |
+
Args:
|
| 133 |
+
grid_logit (torch.Tensor): 3D grid logits tensor representing the scalar field.
|
| 134 |
+
octree_resolution (int): Resolution of the octree grid.
|
| 135 |
+
**kwargs: Additional keyword arguments (ignored).
|
| 136 |
+
|
| 137 |
+
Returns:
|
| 138 |
+
Tuple[np.ndarray, np.ndarray]: Tuple containing:
|
| 139 |
+
- vertices (np.ndarray): Extracted mesh vertices, centered and converted to numpy.
|
| 140 |
+
- faces (np.ndarray): Extracted mesh faces (triangles), with reversed vertex order.
|
| 141 |
+
|
| 142 |
+
Raises:
|
| 143 |
+
ImportError: If the 'diso' package is not installed.
|
| 144 |
+
"""
|
| 145 |
+
device = grid_logit.device
|
| 146 |
+
if not hasattr(self, 'dmc'):
|
| 147 |
+
try:
|
| 148 |
+
from diso import DiffDMC
|
| 149 |
+
self.dmc = DiffDMC(dtype=torch.float32).to(device)
|
| 150 |
+
except:
|
| 151 |
+
raise ImportError("Please install diso via `pip install diso`, or set mc_algo to 'mc'")
|
| 152 |
+
sdf = -grid_logit / octree_resolution
|
| 153 |
+
sdf = sdf.to(torch.float32).contiguous()
|
| 154 |
+
verts, faces = self.dmc(sdf, deform=None, return_quads=False, normalize=True)
|
| 155 |
+
verts = center_vertices(verts)
|
| 156 |
+
vertices = verts.detach().cpu().numpy()
|
| 157 |
+
faces = faces.detach().cpu().numpy()[:, ::-1]
|
| 158 |
+
return vertices, faces
|
| 159 |
+
|
| 160 |
+
|
| 161 |
+
SurfaceExtractors = {
|
| 162 |
+
'mc': MCSurfaceExtractor,
|
| 163 |
+
'dmc': DMCSurfaceExtractor,
|
| 164 |
+
}
|
Hunyuan3D-2.1/hy3dshape/hy3dshape/models/autoencoders/volume_decoders.py
ADDED
|
@@ -0,0 +1,435 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Hunyuan 3D is licensed under the TENCENT HUNYUAN NON-COMMERCIAL LICENSE AGREEMENT
|
| 2 |
+
# except for the third-party components listed below.
|
| 3 |
+
# Hunyuan 3D does not impose any additional limitations beyond what is outlined
|
| 4 |
+
# in the repsective licenses of these third-party components.
|
| 5 |
+
# Users must comply with all terms and conditions of original licenses of these third-party
|
| 6 |
+
# components and must ensure that the usage of the third party components adheres to
|
| 7 |
+
# all relevant laws and regulations.
|
| 8 |
+
|
| 9 |
+
# For avoidance of doubts, Hunyuan 3D means the large language models and
|
| 10 |
+
# their software and algorithms, including trained model weights, parameters (including
|
| 11 |
+
# optimizer states), machine-learning model code, inference-enabling code, training-enabling code,
|
| 12 |
+
# fine-tuning enabling code and other elements of the foregoing made publicly available
|
| 13 |
+
# by Tencent in accordance with TENCENT HUNYUAN COMMUNITY LICENSE AGREEMENT.
|
| 14 |
+
|
| 15 |
+
from typing import Union, Tuple, List, Callable
|
| 16 |
+
|
| 17 |
+
import numpy as np
|
| 18 |
+
import torch
|
| 19 |
+
import torch.nn as nn
|
| 20 |
+
import torch.nn.functional as F
|
| 21 |
+
from einops import repeat
|
| 22 |
+
from tqdm import tqdm
|
| 23 |
+
|
| 24 |
+
from .attention_blocks import CrossAttentionDecoder
|
| 25 |
+
from .attention_processors import FlashVDMCrossAttentionProcessor, FlashVDMTopMCrossAttentionProcessor
|
| 26 |
+
from ...utils import logger
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def extract_near_surface_volume_fn(input_tensor: torch.Tensor, alpha: float):
|
| 30 |
+
device = input_tensor.device
|
| 31 |
+
D = input_tensor.shape[0]
|
| 32 |
+
signed_val = 0.0
|
| 33 |
+
|
| 34 |
+
# 添加偏移并处理无效值
|
| 35 |
+
val = input_tensor + alpha
|
| 36 |
+
valid_mask = val > -9000 # 假设-9000是无效值
|
| 37 |
+
|
| 38 |
+
# 改进的邻居获取函数(保持维度一致)
|
| 39 |
+
def get_neighbor(t, shift, axis):
|
| 40 |
+
"""根据指定轴进行位移并保持维度一致"""
|
| 41 |
+
if shift == 0:
|
| 42 |
+
return t.clone()
|
| 43 |
+
|
| 44 |
+
# 确定填充轴(输入为[D, D, D]对应z,y,x轴)
|
| 45 |
+
pad_dims = [0, 0, 0, 0, 0, 0] # 格式:[x前,x后,y前,y后,z前,z后]
|
| 46 |
+
|
| 47 |
+
# 根据轴类型设置填充
|
| 48 |
+
if axis == 0: # x轴(最后一个维度)
|
| 49 |
+
pad_idx = 0 if shift > 0 else 1
|
| 50 |
+
pad_dims[pad_idx] = abs(shift)
|
| 51 |
+
elif axis == 1: # y轴(中间维度)
|
| 52 |
+
pad_idx = 2 if shift > 0 else 3
|
| 53 |
+
pad_dims[pad_idx] = abs(shift)
|
| 54 |
+
elif axis == 2: # z轴(第一个维度)
|
| 55 |
+
pad_idx = 4 if shift > 0 else 5
|
| 56 |
+
pad_dims[pad_idx] = abs(shift)
|
| 57 |
+
|
| 58 |
+
# 执行填充(添加batch和channel维度适配F.pad)
|
| 59 |
+
padded = F.pad(t.unsqueeze(0).unsqueeze(0), pad_dims[::-1], mode='replicate') # 反转顺序适配F.pad
|
| 60 |
+
|
| 61 |
+
# 构建动态切片索引
|
| 62 |
+
slice_dims = [slice(None)] * 3 # 初始化为全切片
|
| 63 |
+
if axis == 0: # x轴(dim=2)
|
| 64 |
+
if shift > 0:
|
| 65 |
+
slice_dims[0] = slice(shift, None)
|
| 66 |
+
else:
|
| 67 |
+
slice_dims[0] = slice(None, shift)
|
| 68 |
+
elif axis == 1: # y轴(dim=1)
|
| 69 |
+
if shift > 0:
|
| 70 |
+
slice_dims[1] = slice(shift, None)
|
| 71 |
+
else:
|
| 72 |
+
slice_dims[1] = slice(None, shift)
|
| 73 |
+
elif axis == 2: # z轴(dim=0)
|
| 74 |
+
if shift > 0:
|
| 75 |
+
slice_dims[2] = slice(shift, None)
|
| 76 |
+
else:
|
| 77 |
+
slice_dims[2] = slice(None, shift)
|
| 78 |
+
|
| 79 |
+
# 应用切片并恢复维度
|
| 80 |
+
padded = padded.squeeze(0).squeeze(0)
|
| 81 |
+
sliced = padded[slice_dims]
|
| 82 |
+
return sliced
|
| 83 |
+
|
| 84 |
+
# 获取各方向邻居(确保维度一致)
|
| 85 |
+
left = get_neighbor(val, 1, axis=0) # x方向
|
| 86 |
+
right = get_neighbor(val, -1, axis=0)
|
| 87 |
+
back = get_neighbor(val, 1, axis=1) # y方向
|
| 88 |
+
front = get_neighbor(val, -1, axis=1)
|
| 89 |
+
down = get_neighbor(val, 1, axis=2) # z方向
|
| 90 |
+
up = get_neighbor(val, -1, axis=2)
|
| 91 |
+
|
| 92 |
+
# 处理边界无效值(使用where保持维度一致)
|
| 93 |
+
def safe_where(neighbor):
|
| 94 |
+
return torch.where(neighbor > -9000, neighbor, val)
|
| 95 |
+
|
| 96 |
+
left = safe_where(left)
|
| 97 |
+
right = safe_where(right)
|
| 98 |
+
back = safe_where(back)
|
| 99 |
+
front = safe_where(front)
|
| 100 |
+
down = safe_where(down)
|
| 101 |
+
up = safe_where(up)
|
| 102 |
+
|
| 103 |
+
# 计算符号一致性(转换为float32确保精度)
|
| 104 |
+
sign = torch.sign(val.to(torch.float32))
|
| 105 |
+
neighbors_sign = torch.stack([
|
| 106 |
+
torch.sign(left.to(torch.float32)),
|
| 107 |
+
torch.sign(right.to(torch.float32)),
|
| 108 |
+
torch.sign(back.to(torch.float32)),
|
| 109 |
+
torch.sign(front.to(torch.float32)),
|
| 110 |
+
torch.sign(down.to(torch.float32)),
|
| 111 |
+
torch.sign(up.to(torch.float32))
|
| 112 |
+
], dim=0)
|
| 113 |
+
|
| 114 |
+
# 检查所有符号是否一致
|
| 115 |
+
same_sign = torch.all(neighbors_sign == sign, dim=0)
|
| 116 |
+
|
| 117 |
+
# 生成最终掩码
|
| 118 |
+
mask = (~same_sign).to(torch.int32)
|
| 119 |
+
return mask * valid_mask.to(torch.int32)
|
| 120 |
+
|
| 121 |
+
|
| 122 |
+
def generate_dense_grid_points(
|
| 123 |
+
bbox_min: np.ndarray,
|
| 124 |
+
bbox_max: np.ndarray,
|
| 125 |
+
octree_resolution: int,
|
| 126 |
+
indexing: str = "ij",
|
| 127 |
+
):
|
| 128 |
+
length = bbox_max - bbox_min
|
| 129 |
+
num_cells = octree_resolution
|
| 130 |
+
|
| 131 |
+
x = np.linspace(bbox_min[0], bbox_max[0], int(num_cells) + 1, dtype=np.float32)
|
| 132 |
+
y = np.linspace(bbox_min[1], bbox_max[1], int(num_cells) + 1, dtype=np.float32)
|
| 133 |
+
z = np.linspace(bbox_min[2], bbox_max[2], int(num_cells) + 1, dtype=np.float32)
|
| 134 |
+
[xs, ys, zs] = np.meshgrid(x, y, z, indexing=indexing)
|
| 135 |
+
xyz = np.stack((xs, ys, zs), axis=-1)
|
| 136 |
+
grid_size = [int(num_cells) + 1, int(num_cells) + 1, int(num_cells) + 1]
|
| 137 |
+
|
| 138 |
+
return xyz, grid_size, length
|
| 139 |
+
|
| 140 |
+
|
| 141 |
+
class VanillaVolumeDecoder:
|
| 142 |
+
@torch.no_grad()
|
| 143 |
+
def __call__(
|
| 144 |
+
self,
|
| 145 |
+
latents: torch.FloatTensor,
|
| 146 |
+
geo_decoder: Callable,
|
| 147 |
+
bounds: Union[Tuple[float], List[float], float] = 1.01,
|
| 148 |
+
num_chunks: int = 10000,
|
| 149 |
+
octree_resolution: int = None,
|
| 150 |
+
enable_pbar: bool = True,
|
| 151 |
+
**kwargs,
|
| 152 |
+
):
|
| 153 |
+
device = latents.device
|
| 154 |
+
dtype = latents.dtype
|
| 155 |
+
batch_size = latents.shape[0]
|
| 156 |
+
|
| 157 |
+
# 1. generate query points
|
| 158 |
+
if isinstance(bounds, float):
|
| 159 |
+
bounds = [-bounds, -bounds, -bounds, bounds, bounds, bounds]
|
| 160 |
+
|
| 161 |
+
bbox_min, bbox_max = np.array(bounds[0:3]), np.array(bounds[3:6])
|
| 162 |
+
xyz_samples, grid_size, length = generate_dense_grid_points(
|
| 163 |
+
bbox_min=bbox_min,
|
| 164 |
+
bbox_max=bbox_max,
|
| 165 |
+
octree_resolution=octree_resolution,
|
| 166 |
+
indexing="ij"
|
| 167 |
+
)
|
| 168 |
+
xyz_samples = torch.from_numpy(xyz_samples).to(device, dtype=dtype).contiguous().reshape(-1, 3)
|
| 169 |
+
|
| 170 |
+
# 2. latents to 3d volume
|
| 171 |
+
batch_logits = []
|
| 172 |
+
for start in tqdm(range(0, xyz_samples.shape[0], num_chunks), desc=f"Volume Decoding",
|
| 173 |
+
disable=not enable_pbar):
|
| 174 |
+
chunk_queries = xyz_samples[start: start + num_chunks, :]
|
| 175 |
+
chunk_queries = repeat(chunk_queries, "p c -> b p c", b=batch_size)
|
| 176 |
+
logits = geo_decoder(queries=chunk_queries, latents=latents)
|
| 177 |
+
batch_logits.append(logits)
|
| 178 |
+
|
| 179 |
+
grid_logits = torch.cat(batch_logits, dim=1)
|
| 180 |
+
grid_logits = grid_logits.view((batch_size, *grid_size)).float()
|
| 181 |
+
|
| 182 |
+
return grid_logits
|
| 183 |
+
|
| 184 |
+
|
| 185 |
+
class HierarchicalVolumeDecoding:
|
| 186 |
+
@torch.no_grad()
|
| 187 |
+
def __call__(
|
| 188 |
+
self,
|
| 189 |
+
latents: torch.FloatTensor,
|
| 190 |
+
geo_decoder: Callable,
|
| 191 |
+
bounds: Union[Tuple[float], List[float], float] = 1.01,
|
| 192 |
+
num_chunks: int = 10000,
|
| 193 |
+
mc_level: float = 0.0,
|
| 194 |
+
octree_resolution: int = None,
|
| 195 |
+
min_resolution: int = 63,
|
| 196 |
+
enable_pbar: bool = True,
|
| 197 |
+
**kwargs,
|
| 198 |
+
):
|
| 199 |
+
device = latents.device
|
| 200 |
+
dtype = latents.dtype
|
| 201 |
+
|
| 202 |
+
resolutions = []
|
| 203 |
+
if octree_resolution < min_resolution:
|
| 204 |
+
resolutions.append(octree_resolution)
|
| 205 |
+
while octree_resolution >= min_resolution:
|
| 206 |
+
resolutions.append(octree_resolution)
|
| 207 |
+
octree_resolution = octree_resolution // 2
|
| 208 |
+
resolutions.reverse()
|
| 209 |
+
|
| 210 |
+
# 1. generate query points
|
| 211 |
+
if isinstance(bounds, float):
|
| 212 |
+
bounds = [-bounds, -bounds, -bounds, bounds, bounds, bounds]
|
| 213 |
+
bbox_min = np.array(bounds[0:3])
|
| 214 |
+
bbox_max = np.array(bounds[3:6])
|
| 215 |
+
bbox_size = bbox_max - bbox_min
|
| 216 |
+
|
| 217 |
+
xyz_samples, grid_size, length = generate_dense_grid_points(
|
| 218 |
+
bbox_min=bbox_min,
|
| 219 |
+
bbox_max=bbox_max,
|
| 220 |
+
octree_resolution=resolutions[0],
|
| 221 |
+
indexing="ij"
|
| 222 |
+
)
|
| 223 |
+
|
| 224 |
+
dilate = nn.Conv3d(1, 1, 3, padding=1, bias=False, device=device, dtype=dtype)
|
| 225 |
+
dilate.weight = torch.nn.Parameter(torch.ones(dilate.weight.shape, dtype=dtype, device=device))
|
| 226 |
+
|
| 227 |
+
grid_size = np.array(grid_size)
|
| 228 |
+
xyz_samples = torch.from_numpy(xyz_samples).to(device, dtype=dtype).contiguous().reshape(-1, 3)
|
| 229 |
+
|
| 230 |
+
# 2. latents to 3d volume
|
| 231 |
+
batch_logits = []
|
| 232 |
+
batch_size = latents.shape[0]
|
| 233 |
+
for start in tqdm(range(0, xyz_samples.shape[0], num_chunks),
|
| 234 |
+
desc=f"Hierarchical Volume Decoding [r{resolutions[0] + 1}]"):
|
| 235 |
+
queries = xyz_samples[start: start + num_chunks, :]
|
| 236 |
+
batch_queries = repeat(queries, "p c -> b p c", b=batch_size)
|
| 237 |
+
logits = geo_decoder(queries=batch_queries, latents=latents)
|
| 238 |
+
batch_logits.append(logits)
|
| 239 |
+
|
| 240 |
+
grid_logits = torch.cat(batch_logits, dim=1).view((batch_size, grid_size[0], grid_size[1], grid_size[2]))
|
| 241 |
+
|
| 242 |
+
for octree_depth_now in resolutions[1:]:
|
| 243 |
+
grid_size = np.array([octree_depth_now + 1] * 3)
|
| 244 |
+
resolution = bbox_size / octree_depth_now
|
| 245 |
+
next_index = torch.zeros(tuple(grid_size), dtype=dtype, device=device)
|
| 246 |
+
next_logits = torch.full(next_index.shape, -10000., dtype=dtype, device=device)
|
| 247 |
+
curr_points = extract_near_surface_volume_fn(grid_logits.squeeze(0), mc_level)
|
| 248 |
+
curr_points += grid_logits.squeeze(0).abs() < 0.95
|
| 249 |
+
|
| 250 |
+
if octree_depth_now == resolutions[-1]:
|
| 251 |
+
expand_num = 0
|
| 252 |
+
else:
|
| 253 |
+
expand_num = 1
|
| 254 |
+
for i in range(expand_num):
|
| 255 |
+
curr_points = dilate(curr_points.unsqueeze(0).to(dtype)).squeeze(0)
|
| 256 |
+
(cidx_x, cidx_y, cidx_z) = torch.where(curr_points > 0)
|
| 257 |
+
next_index[cidx_x * 2, cidx_y * 2, cidx_z * 2] = 1
|
| 258 |
+
for i in range(2 - expand_num):
|
| 259 |
+
next_index = dilate(next_index.unsqueeze(0)).squeeze(0)
|
| 260 |
+
nidx = torch.where(next_index > 0)
|
| 261 |
+
|
| 262 |
+
next_points = torch.stack(nidx, dim=1)
|
| 263 |
+
next_points = (next_points * torch.tensor(resolution, dtype=next_points.dtype, device=device) +
|
| 264 |
+
torch.tensor(bbox_min, dtype=next_points.dtype, device=device))
|
| 265 |
+
batch_logits = []
|
| 266 |
+
for start in tqdm(range(0, next_points.shape[0], num_chunks),
|
| 267 |
+
desc=f"Hierarchical Volume Decoding [r{octree_depth_now + 1}]"):
|
| 268 |
+
queries = next_points[start: start + num_chunks, :]
|
| 269 |
+
batch_queries = repeat(queries, "p c -> b p c", b=batch_size)
|
| 270 |
+
logits = geo_decoder(queries=batch_queries.to(latents.dtype), latents=latents)
|
| 271 |
+
batch_logits.append(logits)
|
| 272 |
+
grid_logits = torch.cat(batch_logits, dim=1)
|
| 273 |
+
next_logits[nidx] = grid_logits[0, ..., 0]
|
| 274 |
+
grid_logits = next_logits.unsqueeze(0)
|
| 275 |
+
grid_logits[grid_logits == -10000.] = float('nan')
|
| 276 |
+
|
| 277 |
+
return grid_logits
|
| 278 |
+
|
| 279 |
+
|
| 280 |
+
class FlashVDMVolumeDecoding:
|
| 281 |
+
def __init__(self, topk_mode='mean'):
|
| 282 |
+
if topk_mode not in ['mean', 'merge']:
|
| 283 |
+
raise ValueError(f'Unsupported topk_mode {topk_mode}, available: {["mean", "merge"]}')
|
| 284 |
+
|
| 285 |
+
if topk_mode == 'mean':
|
| 286 |
+
self.processor = FlashVDMCrossAttentionProcessor()
|
| 287 |
+
else:
|
| 288 |
+
self.processor = FlashVDMTopMCrossAttentionProcessor()
|
| 289 |
+
|
| 290 |
+
@torch.no_grad()
|
| 291 |
+
def __call__(
|
| 292 |
+
self,
|
| 293 |
+
latents: torch.FloatTensor,
|
| 294 |
+
geo_decoder: CrossAttentionDecoder,
|
| 295 |
+
bounds: Union[Tuple[float], List[float], float] = 1.01,
|
| 296 |
+
num_chunks: int = 10000,
|
| 297 |
+
mc_level: float = 0.0,
|
| 298 |
+
octree_resolution: int = None,
|
| 299 |
+
min_resolution: int = 63,
|
| 300 |
+
mini_grid_num: int = 4,
|
| 301 |
+
enable_pbar: bool = True,
|
| 302 |
+
**kwargs,
|
| 303 |
+
):
|
| 304 |
+
processor = self.processor
|
| 305 |
+
geo_decoder.set_cross_attention_processor(processor)
|
| 306 |
+
|
| 307 |
+
device = latents.device
|
| 308 |
+
dtype = latents.dtype
|
| 309 |
+
|
| 310 |
+
resolutions = []
|
| 311 |
+
if octree_resolution < min_resolution:
|
| 312 |
+
resolutions.append(octree_resolution)
|
| 313 |
+
while octree_resolution >= min_resolution:
|
| 314 |
+
resolutions.append(octree_resolution)
|
| 315 |
+
octree_resolution = octree_resolution // 2
|
| 316 |
+
resolutions.reverse()
|
| 317 |
+
resolutions[0] = round(resolutions[0] / mini_grid_num) * mini_grid_num - 1
|
| 318 |
+
for i, resolution in enumerate(resolutions[1:]):
|
| 319 |
+
resolutions[i + 1] = resolutions[0] * 2 ** (i + 1)
|
| 320 |
+
|
| 321 |
+
logger.info(f"FlashVDMVolumeDecoding Resolution: {resolutions}")
|
| 322 |
+
|
| 323 |
+
# 1. generate query points
|
| 324 |
+
if isinstance(bounds, float):
|
| 325 |
+
bounds = [-bounds, -bounds, -bounds, bounds, bounds, bounds]
|
| 326 |
+
bbox_min = np.array(bounds[0:3])
|
| 327 |
+
bbox_max = np.array(bounds[3:6])
|
| 328 |
+
bbox_size = bbox_max - bbox_min
|
| 329 |
+
|
| 330 |
+
xyz_samples, grid_size, length = generate_dense_grid_points(
|
| 331 |
+
bbox_min=bbox_min,
|
| 332 |
+
bbox_max=bbox_max,
|
| 333 |
+
octree_resolution=resolutions[0],
|
| 334 |
+
indexing="ij"
|
| 335 |
+
)
|
| 336 |
+
|
| 337 |
+
dilate = nn.Conv3d(1, 1, 3, padding=1, bias=False, device=device, dtype=dtype)
|
| 338 |
+
dilate.weight = torch.nn.Parameter(torch.ones(dilate.weight.shape, dtype=dtype, device=device))
|
| 339 |
+
|
| 340 |
+
grid_size = np.array(grid_size)
|
| 341 |
+
|
| 342 |
+
# 2. latents to 3d volume
|
| 343 |
+
xyz_samples = torch.from_numpy(xyz_samples).to(device, dtype=dtype)
|
| 344 |
+
batch_size = latents.shape[0]
|
| 345 |
+
mini_grid_size = xyz_samples.shape[0] // mini_grid_num
|
| 346 |
+
xyz_samples = xyz_samples.view(
|
| 347 |
+
mini_grid_num, mini_grid_size,
|
| 348 |
+
mini_grid_num, mini_grid_size,
|
| 349 |
+
mini_grid_num, mini_grid_size, 3
|
| 350 |
+
).permute(
|
| 351 |
+
0, 2, 4, 1, 3, 5, 6
|
| 352 |
+
).reshape(
|
| 353 |
+
-1, mini_grid_size * mini_grid_size * mini_grid_size, 3
|
| 354 |
+
)
|
| 355 |
+
batch_logits = []
|
| 356 |
+
num_batchs = max(num_chunks // xyz_samples.shape[1], 1)
|
| 357 |
+
for start in tqdm(range(0, xyz_samples.shape[0], num_batchs),
|
| 358 |
+
desc=f"FlashVDM Volume Decoding", disable=not enable_pbar):
|
| 359 |
+
queries = xyz_samples[start: start + num_batchs, :]
|
| 360 |
+
batch = queries.shape[0]
|
| 361 |
+
batch_latents = repeat(latents.squeeze(0), "p c -> b p c", b=batch)
|
| 362 |
+
processor.topk = True
|
| 363 |
+
logits = geo_decoder(queries=queries, latents=batch_latents)
|
| 364 |
+
batch_logits.append(logits)
|
| 365 |
+
grid_logits = torch.cat(batch_logits, dim=0).reshape(
|
| 366 |
+
mini_grid_num, mini_grid_num, mini_grid_num,
|
| 367 |
+
mini_grid_size, mini_grid_size,
|
| 368 |
+
mini_grid_size
|
| 369 |
+
).permute(0, 3, 1, 4, 2, 5).contiguous().view(
|
| 370 |
+
(batch_size, grid_size[0], grid_size[1], grid_size[2])
|
| 371 |
+
)
|
| 372 |
+
|
| 373 |
+
for octree_depth_now in resolutions[1:]:
|
| 374 |
+
grid_size = np.array([octree_depth_now + 1] * 3)
|
| 375 |
+
resolution = bbox_size / octree_depth_now
|
| 376 |
+
next_index = torch.zeros(tuple(grid_size), dtype=dtype, device=device)
|
| 377 |
+
next_logits = torch.full(next_index.shape, -10000., dtype=dtype, device=device)
|
| 378 |
+
curr_points = extract_near_surface_volume_fn(grid_logits.squeeze(0), mc_level)
|
| 379 |
+
curr_points += grid_logits.squeeze(0).abs() < 0.95
|
| 380 |
+
|
| 381 |
+
if octree_depth_now == resolutions[-1]:
|
| 382 |
+
expand_num = 0
|
| 383 |
+
else:
|
| 384 |
+
expand_num = 1
|
| 385 |
+
for i in range(expand_num):
|
| 386 |
+
curr_points = dilate(curr_points.unsqueeze(0).to(dtype)).squeeze(0)
|
| 387 |
+
(cidx_x, cidx_y, cidx_z) = torch.where(curr_points > 0)
|
| 388 |
+
|
| 389 |
+
next_index[cidx_x * 2, cidx_y * 2, cidx_z * 2] = 1
|
| 390 |
+
for i in range(2 - expand_num):
|
| 391 |
+
next_index = dilate(next_index.unsqueeze(0)).squeeze(0)
|
| 392 |
+
nidx = torch.where(next_index > 0)
|
| 393 |
+
|
| 394 |
+
next_points = torch.stack(nidx, dim=1)
|
| 395 |
+
next_points = (next_points * torch.tensor(resolution, dtype=torch.float32, device=device) +
|
| 396 |
+
torch.tensor(bbox_min, dtype=torch.float32, device=device))
|
| 397 |
+
|
| 398 |
+
query_grid_num = 6
|
| 399 |
+
min_val = next_points.min(axis=0).values
|
| 400 |
+
max_val = next_points.max(axis=0).values
|
| 401 |
+
vol_queries_index = (next_points - min_val) / (max_val - min_val) * (query_grid_num - 0.001)
|
| 402 |
+
index = torch.floor(vol_queries_index).long()
|
| 403 |
+
index = index[..., 0] * (query_grid_num ** 2) + index[..., 1] * query_grid_num + index[..., 2]
|
| 404 |
+
index = index.sort()
|
| 405 |
+
next_points = next_points[index.indices].unsqueeze(0).contiguous()
|
| 406 |
+
unique_values = torch.unique(index.values, return_counts=True)
|
| 407 |
+
grid_logits = torch.zeros((next_points.shape[1]), dtype=latents.dtype, device=latents.device)
|
| 408 |
+
input_grid = [[], []]
|
| 409 |
+
logits_grid_list = []
|
| 410 |
+
start_num = 0
|
| 411 |
+
sum_num = 0
|
| 412 |
+
for grid_index, count in zip(unique_values[0].cpu().tolist(), unique_values[1].cpu().tolist()):
|
| 413 |
+
if sum_num + count < num_chunks or sum_num == 0:
|
| 414 |
+
sum_num += count
|
| 415 |
+
input_grid[0].append(grid_index)
|
| 416 |
+
input_grid[1].append(count)
|
| 417 |
+
else:
|
| 418 |
+
processor.topk = input_grid
|
| 419 |
+
logits_grid = geo_decoder(queries=next_points[:, start_num:start_num + sum_num], latents=latents)
|
| 420 |
+
start_num = start_num + sum_num
|
| 421 |
+
logits_grid_list.append(logits_grid)
|
| 422 |
+
input_grid = [[grid_index], [count]]
|
| 423 |
+
sum_num = count
|
| 424 |
+
if sum_num > 0:
|
| 425 |
+
processor.topk = input_grid
|
| 426 |
+
logits_grid = geo_decoder(queries=next_points[:, start_num:start_num + sum_num], latents=latents)
|
| 427 |
+
logits_grid_list.append(logits_grid)
|
| 428 |
+
logits_grid = torch.cat(logits_grid_list, dim=1)
|
| 429 |
+
grid_logits[index.indices] = logits_grid.squeeze(0).squeeze(-1)
|
| 430 |
+
next_logits[nidx] = grid_logits
|
| 431 |
+
grid_logits = next_logits.unsqueeze(0)
|
| 432 |
+
|
| 433 |
+
grid_logits[grid_logits == -10000.] = float('nan')
|
| 434 |
+
|
| 435 |
+
return grid_logits
|
Hunyuan3D-2.1/hy3dshape/hy3dshape/models/conditioner.py
ADDED
|
@@ -0,0 +1,257 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Open Source Model Licensed under the Apache License Version 2.0
|
| 2 |
+
# and Other Licenses of the Third-Party Components therein:
|
| 3 |
+
# The below Model in this distribution may have been modified by THL A29 Limited
|
| 4 |
+
# ("Tencent Modifications"). All Tencent Modifications are Copyright (C) 2024 THL A29 Limited.
|
| 5 |
+
|
| 6 |
+
# Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved.
|
| 7 |
+
# The below software and/or models in this distribution may have been
|
| 8 |
+
# modified by THL A29 Limited ("Tencent Modifications").
|
| 9 |
+
# All Tencent Modifications are Copyright (C) THL A29 Limited.
|
| 10 |
+
|
| 11 |
+
# Hunyuan 3D is licensed under the TENCENT HUNYUAN NON-COMMERCIAL LICENSE AGREEMENT
|
| 12 |
+
# except for the third-party components listed below.
|
| 13 |
+
# Hunyuan 3D does not impose any additional limitations beyond what is outlined
|
| 14 |
+
# in the repsective licenses of these third-party components.
|
| 15 |
+
# Users must comply with all terms and conditions of original licenses of these third-party
|
| 16 |
+
# components and must ensure that the usage of the third party components adheres to
|
| 17 |
+
# all relevant laws and regulations.
|
| 18 |
+
|
| 19 |
+
# For avoidance of doubts, Hunyuan 3D means the large language models and
|
| 20 |
+
# their software and algorithms, including trained model weights, parameters (including
|
| 21 |
+
# optimizer states), machine-learning model code, inference-enabling code, training-enabling code,
|
| 22 |
+
# fine-tuning enabling code and other elements of the foregoing made publicly available
|
| 23 |
+
# by Tencent in accordance with TENCENT HUNYUAN COMMUNITY LICENSE AGREEMENT.
|
| 24 |
+
|
| 25 |
+
import numpy as np
|
| 26 |
+
import torch
|
| 27 |
+
import torch.nn as nn
|
| 28 |
+
from torchvision import transforms
|
| 29 |
+
from transformers import (
|
| 30 |
+
CLIPVisionModelWithProjection,
|
| 31 |
+
CLIPVisionConfig,
|
| 32 |
+
Dinov2Model,
|
| 33 |
+
Dinov2Config,
|
| 34 |
+
)
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
def get_1d_sincos_pos_embed_from_grid(embed_dim, pos):
|
| 38 |
+
"""
|
| 39 |
+
embed_dim: output dimension for each position
|
| 40 |
+
pos: a list of positions to be encoded: size (M,)
|
| 41 |
+
out: (M, D)
|
| 42 |
+
"""
|
| 43 |
+
assert embed_dim % 2 == 0
|
| 44 |
+
omega = np.arange(embed_dim // 2, dtype=np.float64)
|
| 45 |
+
omega /= embed_dim / 2.
|
| 46 |
+
omega = 1. / 10000 ** omega # (D/2,)
|
| 47 |
+
|
| 48 |
+
pos = pos.reshape(-1) # (M,)
|
| 49 |
+
out = np.einsum('m,d->md', pos, omega) # (M, D/2), outer product
|
| 50 |
+
|
| 51 |
+
emb_sin = np.sin(out) # (M, D/2)
|
| 52 |
+
emb_cos = np.cos(out) # (M, D/2)
|
| 53 |
+
|
| 54 |
+
return np.concatenate([emb_sin, emb_cos], axis=1)
|
| 55 |
+
|
| 56 |
+
|
| 57 |
+
class ImageEncoder(nn.Module):
|
| 58 |
+
def __init__(
|
| 59 |
+
self,
|
| 60 |
+
version=None,
|
| 61 |
+
config=None,
|
| 62 |
+
use_cls_token=True,
|
| 63 |
+
image_size=224,
|
| 64 |
+
**kwargs,
|
| 65 |
+
):
|
| 66 |
+
super().__init__()
|
| 67 |
+
|
| 68 |
+
if config is None:
|
| 69 |
+
self.model = self.MODEL_CLASS.from_pretrained(version)
|
| 70 |
+
else:
|
| 71 |
+
self.model = self.MODEL_CLASS(self.MODEL_CONFIG_CLASS.from_dict(config))
|
| 72 |
+
self.model.eval()
|
| 73 |
+
self.model.requires_grad_(False)
|
| 74 |
+
self.use_cls_token = use_cls_token
|
| 75 |
+
self.size = image_size // 14
|
| 76 |
+
self.num_patches = (image_size // 14) ** 2
|
| 77 |
+
if self.use_cls_token:
|
| 78 |
+
self.num_patches += 1
|
| 79 |
+
|
| 80 |
+
self.transform = transforms.Compose(
|
| 81 |
+
[
|
| 82 |
+
transforms.Resize(image_size, transforms.InterpolationMode.BILINEAR, antialias=True),
|
| 83 |
+
transforms.CenterCrop(image_size),
|
| 84 |
+
transforms.Normalize(
|
| 85 |
+
mean=self.mean,
|
| 86 |
+
std=self.std,
|
| 87 |
+
),
|
| 88 |
+
]
|
| 89 |
+
)
|
| 90 |
+
|
| 91 |
+
def forward(self, image, mask=None, value_range=(-1, 1), **kwargs):
|
| 92 |
+
if value_range is not None:
|
| 93 |
+
low, high = value_range
|
| 94 |
+
image = (image - low) / (high - low)
|
| 95 |
+
|
| 96 |
+
image = image.to(self.model.device, dtype=self.model.dtype)
|
| 97 |
+
inputs = self.transform(image)
|
| 98 |
+
outputs = self.model(inputs)
|
| 99 |
+
|
| 100 |
+
last_hidden_state = outputs.last_hidden_state
|
| 101 |
+
if not self.use_cls_token:
|
| 102 |
+
last_hidden_state = last_hidden_state[:, 1:, :]
|
| 103 |
+
|
| 104 |
+
return last_hidden_state
|
| 105 |
+
|
| 106 |
+
def unconditional_embedding(self, batch_size, **kwargs):
|
| 107 |
+
device = next(self.model.parameters()).device
|
| 108 |
+
dtype = next(self.model.parameters()).dtype
|
| 109 |
+
zero = torch.zeros(
|
| 110 |
+
batch_size,
|
| 111 |
+
self.num_patches,
|
| 112 |
+
self.model.config.hidden_size,
|
| 113 |
+
device=device,
|
| 114 |
+
dtype=dtype,
|
| 115 |
+
)
|
| 116 |
+
|
| 117 |
+
return zero
|
| 118 |
+
|
| 119 |
+
|
| 120 |
+
class CLIPImageEncoder(ImageEncoder):
|
| 121 |
+
MODEL_CLASS = CLIPVisionModelWithProjection
|
| 122 |
+
MODEL_CONFIG_CLASS = CLIPVisionConfig
|
| 123 |
+
mean = [0.48145466, 0.4578275, 0.40821073]
|
| 124 |
+
std = [0.26862954, 0.26130258, 0.27577711]
|
| 125 |
+
|
| 126 |
+
|
| 127 |
+
class DinoImageEncoder(ImageEncoder):
|
| 128 |
+
MODEL_CLASS = Dinov2Model
|
| 129 |
+
MODEL_CONFIG_CLASS = Dinov2Config
|
| 130 |
+
mean = [0.485, 0.456, 0.406]
|
| 131 |
+
std = [0.229, 0.224, 0.225]
|
| 132 |
+
|
| 133 |
+
|
| 134 |
+
class DinoImageEncoderMV(DinoImageEncoder):
|
| 135 |
+
def __init__(
|
| 136 |
+
self,
|
| 137 |
+
version=None,
|
| 138 |
+
config=None,
|
| 139 |
+
use_cls_token=True,
|
| 140 |
+
image_size=224,
|
| 141 |
+
view_num=4,
|
| 142 |
+
**kwargs,
|
| 143 |
+
):
|
| 144 |
+
super().__init__(version, config, use_cls_token, image_size, **kwargs)
|
| 145 |
+
self.view_num = view_num
|
| 146 |
+
self.num_patches = self.num_patches
|
| 147 |
+
pos = np.arange(self.view_num, dtype=np.float32)
|
| 148 |
+
view_embedding = torch.from_numpy(
|
| 149 |
+
get_1d_sincos_pos_embed_from_grid(self.model.config.hidden_size, pos)).float()
|
| 150 |
+
|
| 151 |
+
view_embedding = view_embedding.unsqueeze(1).repeat(1, self.num_patches, 1)
|
| 152 |
+
self.view_embed = view_embedding.unsqueeze(0)
|
| 153 |
+
|
| 154 |
+
def forward(self, image, mask=None, value_range=(-1, 1), view_idxs=None):
|
| 155 |
+
if value_range is not None:
|
| 156 |
+
low, high = value_range
|
| 157 |
+
image = (image - low) / (high - low)
|
| 158 |
+
|
| 159 |
+
image = image.to(self.model.device, dtype=self.model.dtype)
|
| 160 |
+
|
| 161 |
+
bs, num_views, c, h, w = image.shape
|
| 162 |
+
image = image.view(bs * num_views, c, h, w)
|
| 163 |
+
|
| 164 |
+
inputs = self.transform(image)
|
| 165 |
+
outputs = self.model(inputs)
|
| 166 |
+
|
| 167 |
+
last_hidden_state = outputs.last_hidden_state
|
| 168 |
+
last_hidden_state = last_hidden_state.view(
|
| 169 |
+
bs, num_views, last_hidden_state.shape[-2],
|
| 170 |
+
last_hidden_state.shape[-1]
|
| 171 |
+
)
|
| 172 |
+
|
| 173 |
+
view_embedding = self.view_embed.to(last_hidden_state.dtype).to(last_hidden_state.device)
|
| 174 |
+
if view_idxs is not None:
|
| 175 |
+
assert len(view_idxs) == bs
|
| 176 |
+
view_embeddings = []
|
| 177 |
+
for i in range(bs):
|
| 178 |
+
view_idx = view_idxs[i]
|
| 179 |
+
assert num_views == len(view_idx)
|
| 180 |
+
view_embeddings.append(self.view_embed[:, view_idx, ...])
|
| 181 |
+
view_embedding = torch.cat(view_embeddings, 0).to(last_hidden_state.dtype).to(last_hidden_state.device)
|
| 182 |
+
|
| 183 |
+
if num_views != self.view_num:
|
| 184 |
+
view_embedding = view_embedding[:, :num_views, ...]
|
| 185 |
+
last_hidden_state = last_hidden_state + view_embedding
|
| 186 |
+
last_hidden_state = last_hidden_state.view(bs, num_views * last_hidden_state.shape[-2],
|
| 187 |
+
last_hidden_state.shape[-1])
|
| 188 |
+
return last_hidden_state
|
| 189 |
+
|
| 190 |
+
def unconditional_embedding(self, batch_size, view_idxs=None, **kwargs):
|
| 191 |
+
device = next(self.model.parameters()).device
|
| 192 |
+
dtype = next(self.model.parameters()).dtype
|
| 193 |
+
zero = torch.zeros(
|
| 194 |
+
batch_size,
|
| 195 |
+
self.num_patches * len(view_idxs[0]),
|
| 196 |
+
self.model.config.hidden_size,
|
| 197 |
+
device=device,
|
| 198 |
+
dtype=dtype,
|
| 199 |
+
)
|
| 200 |
+
return zero
|
| 201 |
+
|
| 202 |
+
|
| 203 |
+
def build_image_encoder(config):
|
| 204 |
+
if config['type'] == 'CLIPImageEncoder':
|
| 205 |
+
return CLIPImageEncoder(**config['kwargs'])
|
| 206 |
+
elif config['type'] == 'DinoImageEncoder':
|
| 207 |
+
return DinoImageEncoder(**config['kwargs'])
|
| 208 |
+
elif config['type'] == 'DinoImageEncoderMV':
|
| 209 |
+
return DinoImageEncoderMV(**config['kwargs'])
|
| 210 |
+
else:
|
| 211 |
+
raise ValueError(f'Unknown image encoder type: {config["type"]}')
|
| 212 |
+
|
| 213 |
+
|
| 214 |
+
class DualImageEncoder(nn.Module):
|
| 215 |
+
def __init__(
|
| 216 |
+
self,
|
| 217 |
+
main_image_encoder,
|
| 218 |
+
additional_image_encoder,
|
| 219 |
+
):
|
| 220 |
+
super().__init__()
|
| 221 |
+
self.main_image_encoder = build_image_encoder(main_image_encoder)
|
| 222 |
+
self.additional_image_encoder = build_image_encoder(additional_image_encoder)
|
| 223 |
+
|
| 224 |
+
def forward(self, image, mask=None, **kwargs):
|
| 225 |
+
outputs = {
|
| 226 |
+
'main': self.main_image_encoder(image, mask=mask, **kwargs),
|
| 227 |
+
'additional': self.additional_image_encoder(image, mask=mask, **kwargs),
|
| 228 |
+
}
|
| 229 |
+
return outputs
|
| 230 |
+
|
| 231 |
+
def unconditional_embedding(self, batch_size, **kwargs):
|
| 232 |
+
outputs = {
|
| 233 |
+
'main': self.main_image_encoder.unconditional_embedding(batch_size, **kwargs),
|
| 234 |
+
'additional': self.additional_image_encoder.unconditional_embedding(batch_size, **kwargs),
|
| 235 |
+
}
|
| 236 |
+
return outputs
|
| 237 |
+
|
| 238 |
+
|
| 239 |
+
class SingleImageEncoder(nn.Module):
|
| 240 |
+
def __init__(
|
| 241 |
+
self,
|
| 242 |
+
main_image_encoder,
|
| 243 |
+
):
|
| 244 |
+
super().__init__()
|
| 245 |
+
self.main_image_encoder = build_image_encoder(main_image_encoder)
|
| 246 |
+
|
| 247 |
+
def forward(self, image, mask=None, **kwargs):
|
| 248 |
+
outputs = {
|
| 249 |
+
'main': self.main_image_encoder(image, mask=mask, **kwargs),
|
| 250 |
+
}
|
| 251 |
+
return outputs
|
| 252 |
+
|
| 253 |
+
def unconditional_embedding(self, batch_size, **kwargs):
|
| 254 |
+
outputs = {
|
| 255 |
+
'main': self.main_image_encoder.unconditional_embedding(batch_size, **kwargs),
|
| 256 |
+
}
|
| 257 |
+
return outputs
|
Hunyuan3D-2.1/hy3dshape/hy3dshape/models/denoisers/__init__.py
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Hunyuan 3D is licensed under the TENCENT HUNYUAN NON-COMMERCIAL LICENSE AGREEMENT
|
| 2 |
+
# except for the third-party components listed below.
|
| 3 |
+
# Hunyuan 3D does not impose any additional limitations beyond what is outlined
|
| 4 |
+
# in the repsective licenses of these third-party components.
|
| 5 |
+
# Users must comply with all terms and conditions of original licenses of these third-party
|
| 6 |
+
# components and must ensure that the usage of the third party components adheres to
|
| 7 |
+
# all relevant laws and regulations.
|
| 8 |
+
|
| 9 |
+
# For avoidance of doubts, Hunyuan 3D means the large language models and
|
| 10 |
+
# their software and algorithms, including trained model weights, parameters (including
|
| 11 |
+
# optimizer states), machine-learning model code, inference-enabling code, training-enabling code,
|
| 12 |
+
# fine-tuning enabling code and other elements of the foregoing made publicly available
|
| 13 |
+
# by Tencent in accordance with TENCENT HUNYUAN COMMUNITY LICENSE AGREEMENT.
|
| 14 |
+
|
| 15 |
+
from .hunyuan3ddit import Hunyuan3DDiT
|
Hunyuan3D-2.1/hy3dshape/hy3dshape/models/denoisers/hunyuan3ddit.py
ADDED
|
@@ -0,0 +1,404 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Hunyuan 3D is licensed under the TENCENT HUNYUAN NON-COMMERCIAL LICENSE AGREEMENT
|
| 2 |
+
# except for the third-party components listed below.
|
| 3 |
+
# Hunyuan 3D does not impose any additional limitations beyond what is outlined
|
| 4 |
+
# in the repsective licenses of these third-party components.
|
| 5 |
+
# Users must comply with all terms and conditions of original licenses of these third-party
|
| 6 |
+
# components and must ensure that the usage of the third party components adheres to
|
| 7 |
+
# all relevant laws and regulations.
|
| 8 |
+
|
| 9 |
+
# For avoidance of doubts, Hunyuan 3D means the large language models and
|
| 10 |
+
# their software and algorithms, including trained model weights, parameters (including
|
| 11 |
+
# optimizer states), machine-learning model code, inference-enabling code, training-enabling code,
|
| 12 |
+
# fine-tuning enabling code and other elements of the foregoing made publicly available
|
| 13 |
+
# by Tencent in accordance with TENCENT HUNYUAN COMMUNITY LICENSE AGREEMENT.
|
| 14 |
+
|
| 15 |
+
import os
|
| 16 |
+
import math
|
| 17 |
+
from dataclasses import dataclass
|
| 18 |
+
from typing import List, Tuple, Optional
|
| 19 |
+
|
| 20 |
+
import torch
|
| 21 |
+
from torch import Tensor, nn
|
| 22 |
+
from einops import rearrange
|
| 23 |
+
|
| 24 |
+
# set up attention backend
|
| 25 |
+
scaled_dot_product_attention = nn.functional.scaled_dot_product_attention
|
| 26 |
+
if os.environ.get('USE_SAGEATTN', '0') == '1':
|
| 27 |
+
try:
|
| 28 |
+
from sageattention import sageattn
|
| 29 |
+
except ImportError:
|
| 30 |
+
raise ImportError('Please install the package "sageattention" to use this USE_SAGEATTN.')
|
| 31 |
+
scaled_dot_product_attention = sageattn
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
def attention(q: Tensor, k: Tensor, v: Tensor, **kwargs) -> Tensor:
|
| 35 |
+
x = scaled_dot_product_attention(q, k, v)
|
| 36 |
+
x = rearrange(x, "B H L D -> B L (H D)")
|
| 37 |
+
return x
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
def timestep_embedding(t: Tensor, dim, max_period=10000, time_factor: float = 1000.0):
|
| 41 |
+
"""
|
| 42 |
+
Create sinusoidal timestep embeddings.
|
| 43 |
+
:param t: a 1-D Tensor of N indices, one per batch element.
|
| 44 |
+
These may be fractional.
|
| 45 |
+
:param dim: the dimension of the output.
|
| 46 |
+
:param max_period: controls the minimum frequency of the embeddings.
|
| 47 |
+
:return: an (N, D) Tensor of positional embeddings.
|
| 48 |
+
"""
|
| 49 |
+
t = time_factor * t
|
| 50 |
+
half = dim // 2
|
| 51 |
+
freqs = torch.exp(-math.log(max_period) * torch.arange(start=0, end=half, dtype=torch.float32) / half)
|
| 52 |
+
freqs = freqs.to(t.device)
|
| 53 |
+
|
| 54 |
+
args = t[:, None].float() * freqs[None]
|
| 55 |
+
embedding = torch.cat([torch.cos(args), torch.sin(args)], dim=-1)
|
| 56 |
+
if dim % 2:
|
| 57 |
+
embedding = torch.cat([embedding, torch.zeros_like(embedding[:, :1])], dim=-1)
|
| 58 |
+
if torch.is_floating_point(t):
|
| 59 |
+
embedding = embedding.to(t)
|
| 60 |
+
return embedding
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
class GELU(nn.Module):
|
| 64 |
+
def __init__(self, approximate='tanh'):
|
| 65 |
+
super().__init__()
|
| 66 |
+
self.approximate = approximate
|
| 67 |
+
|
| 68 |
+
def forward(self, x: Tensor) -> Tensor:
|
| 69 |
+
return nn.functional.gelu(x.contiguous(), approximate=self.approximate)
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
class MLPEmbedder(nn.Module):
|
| 73 |
+
def __init__(self, in_dim: int, hidden_dim: int):
|
| 74 |
+
super().__init__()
|
| 75 |
+
self.in_layer = nn.Linear(in_dim, hidden_dim, bias=True)
|
| 76 |
+
self.silu = nn.SiLU()
|
| 77 |
+
self.out_layer = nn.Linear(hidden_dim, hidden_dim, bias=True)
|
| 78 |
+
|
| 79 |
+
def forward(self, x: Tensor) -> Tensor:
|
| 80 |
+
return self.out_layer(self.silu(self.in_layer(x)))
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
class RMSNorm(torch.nn.Module):
|
| 84 |
+
def __init__(self, dim: int):
|
| 85 |
+
super().__init__()
|
| 86 |
+
self.scale = nn.Parameter(torch.ones(dim))
|
| 87 |
+
|
| 88 |
+
def forward(self, x: Tensor):
|
| 89 |
+
x_dtype = x.dtype
|
| 90 |
+
x = x.float()
|
| 91 |
+
rrms = torch.rsqrt(torch.mean(x ** 2, dim=-1, keepdim=True) + 1e-6)
|
| 92 |
+
return (x * rrms).to(dtype=x_dtype) * self.scale
|
| 93 |
+
|
| 94 |
+
|
| 95 |
+
class QKNorm(torch.nn.Module):
|
| 96 |
+
def __init__(self, dim: int):
|
| 97 |
+
super().__init__()
|
| 98 |
+
self.query_norm = RMSNorm(dim)
|
| 99 |
+
self.key_norm = RMSNorm(dim)
|
| 100 |
+
|
| 101 |
+
def forward(self, q: Tensor, k: Tensor, v: Tensor) -> Tuple[Tensor, Tensor]:
|
| 102 |
+
q = self.query_norm(q)
|
| 103 |
+
k = self.key_norm(k)
|
| 104 |
+
return q.to(v), k.to(v)
|
| 105 |
+
|
| 106 |
+
|
| 107 |
+
class SelfAttention(nn.Module):
|
| 108 |
+
def __init__(
|
| 109 |
+
self,
|
| 110 |
+
dim: int,
|
| 111 |
+
num_heads: int = 8,
|
| 112 |
+
qkv_bias: bool = False,
|
| 113 |
+
):
|
| 114 |
+
super().__init__()
|
| 115 |
+
self.num_heads = num_heads
|
| 116 |
+
head_dim = dim // num_heads
|
| 117 |
+
|
| 118 |
+
self.qkv = nn.Linear(dim, dim * 3, bias=qkv_bias)
|
| 119 |
+
self.norm = QKNorm(head_dim)
|
| 120 |
+
self.proj = nn.Linear(dim, dim)
|
| 121 |
+
|
| 122 |
+
def forward(self, x: Tensor, pe: Tensor) -> Tensor:
|
| 123 |
+
qkv = self.qkv(x)
|
| 124 |
+
q, k, v = rearrange(qkv, "B L (K H D) -> K B H L D", K=3, H=self.num_heads)
|
| 125 |
+
q, k = self.norm(q, k, v)
|
| 126 |
+
x = attention(q, k, v, pe=pe)
|
| 127 |
+
x = self.proj(x)
|
| 128 |
+
return x
|
| 129 |
+
|
| 130 |
+
|
| 131 |
+
@dataclass
|
| 132 |
+
class ModulationOut:
|
| 133 |
+
shift: Tensor
|
| 134 |
+
scale: Tensor
|
| 135 |
+
gate: Tensor
|
| 136 |
+
|
| 137 |
+
|
| 138 |
+
class Modulation(nn.Module):
|
| 139 |
+
def __init__(self, dim: int, double: bool):
|
| 140 |
+
super().__init__()
|
| 141 |
+
self.is_double = double
|
| 142 |
+
self.multiplier = 6 if double else 3
|
| 143 |
+
self.lin = nn.Linear(dim, self.multiplier * dim, bias=True)
|
| 144 |
+
|
| 145 |
+
def forward(self, vec: Tensor) -> Tuple[ModulationOut, Optional[ModulationOut]]:
|
| 146 |
+
out = self.lin(nn.functional.silu(vec))[:, None, :]
|
| 147 |
+
out = out.chunk(self.multiplier, dim=-1)
|
| 148 |
+
|
| 149 |
+
return (
|
| 150 |
+
ModulationOut(*out[:3]),
|
| 151 |
+
ModulationOut(*out[3:]) if self.is_double else None,
|
| 152 |
+
)
|
| 153 |
+
|
| 154 |
+
|
| 155 |
+
class DoubleStreamBlock(nn.Module):
|
| 156 |
+
def __init__(
|
| 157 |
+
self,
|
| 158 |
+
hidden_size: int,
|
| 159 |
+
num_heads: int,
|
| 160 |
+
mlp_ratio: float,
|
| 161 |
+
qkv_bias: bool = False,
|
| 162 |
+
):
|
| 163 |
+
super().__init__()
|
| 164 |
+
mlp_hidden_dim = int(hidden_size * mlp_ratio)
|
| 165 |
+
self.num_heads = num_heads
|
| 166 |
+
self.hidden_size = hidden_size
|
| 167 |
+
self.img_mod = Modulation(hidden_size, double=True)
|
| 168 |
+
self.img_norm1 = nn.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6)
|
| 169 |
+
self.img_attn = SelfAttention(dim=hidden_size, num_heads=num_heads, qkv_bias=qkv_bias)
|
| 170 |
+
|
| 171 |
+
self.img_norm2 = nn.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6)
|
| 172 |
+
self.img_mlp = nn.Sequential(
|
| 173 |
+
nn.Linear(hidden_size, mlp_hidden_dim, bias=True),
|
| 174 |
+
GELU(approximate="tanh"),
|
| 175 |
+
nn.Linear(mlp_hidden_dim, hidden_size, bias=True),
|
| 176 |
+
)
|
| 177 |
+
|
| 178 |
+
self.txt_mod = Modulation(hidden_size, double=True)
|
| 179 |
+
self.txt_norm1 = nn.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6)
|
| 180 |
+
self.txt_attn = SelfAttention(dim=hidden_size, num_heads=num_heads, qkv_bias=qkv_bias)
|
| 181 |
+
|
| 182 |
+
self.txt_norm2 = nn.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6)
|
| 183 |
+
self.txt_mlp = nn.Sequential(
|
| 184 |
+
nn.Linear(hidden_size, mlp_hidden_dim, bias=True),
|
| 185 |
+
GELU(approximate="tanh"),
|
| 186 |
+
nn.Linear(mlp_hidden_dim, hidden_size, bias=True),
|
| 187 |
+
)
|
| 188 |
+
|
| 189 |
+
def forward(self, img: Tensor, txt: Tensor, vec: Tensor, pe: Tensor) -> Tuple[Tensor, Tensor]:
|
| 190 |
+
img_mod1, img_mod2 = self.img_mod(vec)
|
| 191 |
+
txt_mod1, txt_mod2 = self.txt_mod(vec)
|
| 192 |
+
|
| 193 |
+
img_modulated = self.img_norm1(img)
|
| 194 |
+
img_modulated = (1 + img_mod1.scale) * img_modulated + img_mod1.shift
|
| 195 |
+
img_qkv = self.img_attn.qkv(img_modulated)
|
| 196 |
+
img_q, img_k, img_v = rearrange(img_qkv, "B L (K H D) -> K B H L D", K=3, H=self.num_heads)
|
| 197 |
+
img_q, img_k = self.img_attn.norm(img_q, img_k, img_v)
|
| 198 |
+
|
| 199 |
+
txt_modulated = self.txt_norm1(txt)
|
| 200 |
+
txt_modulated = (1 + txt_mod1.scale) * txt_modulated + txt_mod1.shift
|
| 201 |
+
txt_qkv = self.txt_attn.qkv(txt_modulated)
|
| 202 |
+
txt_q, txt_k, txt_v = rearrange(txt_qkv, "B L (K H D) -> K B H L D", K=3, H=self.num_heads)
|
| 203 |
+
txt_q, txt_k = self.txt_attn.norm(txt_q, txt_k, txt_v)
|
| 204 |
+
|
| 205 |
+
q = torch.cat((txt_q, img_q), dim=2)
|
| 206 |
+
k = torch.cat((txt_k, img_k), dim=2)
|
| 207 |
+
v = torch.cat((txt_v, img_v), dim=2)
|
| 208 |
+
|
| 209 |
+
attn = attention(q, k, v, pe=pe)
|
| 210 |
+
txt_attn, img_attn = attn[:, : txt.shape[1]], attn[:, txt.shape[1]:]
|
| 211 |
+
|
| 212 |
+
img = img + img_mod1.gate * self.img_attn.proj(img_attn)
|
| 213 |
+
img = img + img_mod2.gate * self.img_mlp((1 + img_mod2.scale) * self.img_norm2(img) + img_mod2.shift)
|
| 214 |
+
|
| 215 |
+
txt = txt + txt_mod1.gate * self.txt_attn.proj(txt_attn)
|
| 216 |
+
txt = txt + txt_mod2.gate * self.txt_mlp((1 + txt_mod2.scale) * self.txt_norm2(txt) + txt_mod2.shift)
|
| 217 |
+
return img, txt
|
| 218 |
+
|
| 219 |
+
|
| 220 |
+
class SingleStreamBlock(nn.Module):
|
| 221 |
+
"""
|
| 222 |
+
A DiT block with parallel linear layers as described in
|
| 223 |
+
https://arxiv.org/abs/2302.05442 and adapted modulation interface.
|
| 224 |
+
"""
|
| 225 |
+
|
| 226 |
+
def __init__(
|
| 227 |
+
self,
|
| 228 |
+
hidden_size: int,
|
| 229 |
+
num_heads: int,
|
| 230 |
+
mlp_ratio: float = 4.0,
|
| 231 |
+
qk_scale: Optional[float] = None,
|
| 232 |
+
):
|
| 233 |
+
super().__init__()
|
| 234 |
+
|
| 235 |
+
self.hidden_dim = hidden_size
|
| 236 |
+
self.num_heads = num_heads
|
| 237 |
+
head_dim = hidden_size // num_heads
|
| 238 |
+
self.scale = qk_scale or head_dim ** -0.5
|
| 239 |
+
|
| 240 |
+
self.mlp_hidden_dim = int(hidden_size * mlp_ratio)
|
| 241 |
+
# qkv and mlp_in
|
| 242 |
+
self.linear1 = nn.Linear(hidden_size, hidden_size * 3 + self.mlp_hidden_dim)
|
| 243 |
+
# proj and mlp_out
|
| 244 |
+
self.linear2 = nn.Linear(hidden_size + self.mlp_hidden_dim, hidden_size)
|
| 245 |
+
|
| 246 |
+
self.norm = QKNorm(head_dim)
|
| 247 |
+
|
| 248 |
+
self.hidden_size = hidden_size
|
| 249 |
+
self.pre_norm = nn.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6)
|
| 250 |
+
|
| 251 |
+
self.mlp_act = GELU(approximate="tanh")
|
| 252 |
+
self.modulation = Modulation(hidden_size, double=False)
|
| 253 |
+
|
| 254 |
+
def forward(self, x: Tensor, vec: Tensor, pe: Tensor) -> Tensor:
|
| 255 |
+
mod, _ = self.modulation(vec)
|
| 256 |
+
|
| 257 |
+
x_mod = (1 + mod.scale) * self.pre_norm(x) + mod.shift
|
| 258 |
+
qkv, mlp = torch.split(self.linear1(x_mod), [3 * self.hidden_size, self.mlp_hidden_dim], dim=-1)
|
| 259 |
+
|
| 260 |
+
q, k, v = rearrange(qkv, "B L (K H D) -> K B H L D", K=3, H=self.num_heads)
|
| 261 |
+
q, k = self.norm(q, k, v)
|
| 262 |
+
|
| 263 |
+
# compute attention
|
| 264 |
+
attn = attention(q, k, v, pe=pe)
|
| 265 |
+
# compute activation in mlp stream, cat again and run second linear layer
|
| 266 |
+
output = self.linear2(torch.cat((attn, self.mlp_act(mlp)), 2))
|
| 267 |
+
return x + mod.gate * output
|
| 268 |
+
|
| 269 |
+
|
| 270 |
+
class LastLayer(nn.Module):
|
| 271 |
+
def __init__(self, hidden_size: int, patch_size: int, out_channels: int):
|
| 272 |
+
super().__init__()
|
| 273 |
+
self.norm_final = nn.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6)
|
| 274 |
+
self.linear = nn.Linear(hidden_size, patch_size * patch_size * out_channels, bias=True)
|
| 275 |
+
self.adaLN_modulation = nn.Sequential(nn.SiLU(), nn.Linear(hidden_size, 2 * hidden_size, bias=True))
|
| 276 |
+
|
| 277 |
+
def forward(self, x: Tensor, vec: Tensor) -> Tensor:
|
| 278 |
+
shift, scale = self.adaLN_modulation(vec).chunk(2, dim=1)
|
| 279 |
+
x = (1 + scale[:, None, :]) * self.norm_final(x) + shift[:, None, :]
|
| 280 |
+
x = self.linear(x)
|
| 281 |
+
return x
|
| 282 |
+
|
| 283 |
+
|
| 284 |
+
class Hunyuan3DDiT(nn.Module):
|
| 285 |
+
def __init__(
|
| 286 |
+
self,
|
| 287 |
+
in_channels: int = 64,
|
| 288 |
+
context_in_dim: int = 1536,
|
| 289 |
+
hidden_size: int = 1024,
|
| 290 |
+
mlp_ratio: float = 4.0,
|
| 291 |
+
num_heads: int = 16,
|
| 292 |
+
depth: int = 16,
|
| 293 |
+
depth_single_blocks: int = 32,
|
| 294 |
+
axes_dim: List[int] = [64],
|
| 295 |
+
theta: int = 10_000,
|
| 296 |
+
qkv_bias: bool = True,
|
| 297 |
+
time_factor: float = 1000,
|
| 298 |
+
guidance_embed: bool = False,
|
| 299 |
+
ckpt_path: Optional[str] = None,
|
| 300 |
+
**kwargs,
|
| 301 |
+
):
|
| 302 |
+
super().__init__()
|
| 303 |
+
self.in_channels = in_channels
|
| 304 |
+
self.context_in_dim = context_in_dim
|
| 305 |
+
self.hidden_size = hidden_size
|
| 306 |
+
self.mlp_ratio = mlp_ratio
|
| 307 |
+
self.num_heads = num_heads
|
| 308 |
+
self.depth = depth
|
| 309 |
+
self.depth_single_blocks = depth_single_blocks
|
| 310 |
+
self.axes_dim = axes_dim
|
| 311 |
+
self.theta = theta
|
| 312 |
+
self.qkv_bias = qkv_bias
|
| 313 |
+
self.time_factor = time_factor
|
| 314 |
+
self.out_channels = self.in_channels
|
| 315 |
+
self.guidance_embed = guidance_embed
|
| 316 |
+
|
| 317 |
+
if hidden_size % num_heads != 0:
|
| 318 |
+
raise ValueError(
|
| 319 |
+
f"Hidden size {hidden_size} must be divisible by num_heads {num_heads}"
|
| 320 |
+
)
|
| 321 |
+
pe_dim = hidden_size // num_heads
|
| 322 |
+
if sum(axes_dim) != pe_dim:
|
| 323 |
+
raise ValueError(f"Got {axes_dim} but expected positional dim {pe_dim}")
|
| 324 |
+
self.hidden_size = hidden_size
|
| 325 |
+
self.num_heads = num_heads
|
| 326 |
+
self.latent_in = nn.Linear(self.in_channels, self.hidden_size, bias=True)
|
| 327 |
+
self.time_in = MLPEmbedder(in_dim=256, hidden_dim=self.hidden_size)
|
| 328 |
+
self.cond_in = nn.Linear(context_in_dim, self.hidden_size)
|
| 329 |
+
self.guidance_in = (
|
| 330 |
+
MLPEmbedder(in_dim=256, hidden_dim=self.hidden_size) if guidance_embed else nn.Identity()
|
| 331 |
+
)
|
| 332 |
+
|
| 333 |
+
self.double_blocks = nn.ModuleList(
|
| 334 |
+
[
|
| 335 |
+
DoubleStreamBlock(
|
| 336 |
+
self.hidden_size,
|
| 337 |
+
self.num_heads,
|
| 338 |
+
mlp_ratio=mlp_ratio,
|
| 339 |
+
qkv_bias=qkv_bias,
|
| 340 |
+
)
|
| 341 |
+
for _ in range(depth)
|
| 342 |
+
]
|
| 343 |
+
)
|
| 344 |
+
|
| 345 |
+
self.single_blocks = nn.ModuleList(
|
| 346 |
+
[
|
| 347 |
+
SingleStreamBlock(
|
| 348 |
+
self.hidden_size,
|
| 349 |
+
self.num_heads,
|
| 350 |
+
mlp_ratio=mlp_ratio,
|
| 351 |
+
)
|
| 352 |
+
for _ in range(depth_single_blocks)
|
| 353 |
+
]
|
| 354 |
+
)
|
| 355 |
+
|
| 356 |
+
self.final_layer = LastLayer(self.hidden_size, 1, self.out_channels)
|
| 357 |
+
|
| 358 |
+
if ckpt_path is not None:
|
| 359 |
+
print('restored denoiser ckpt', ckpt_path)
|
| 360 |
+
|
| 361 |
+
ckpt = torch.load(ckpt_path, map_location="cpu")
|
| 362 |
+
if 'state_dict' not in ckpt:
|
| 363 |
+
# deepspeed ckpt
|
| 364 |
+
state_dict = {}
|
| 365 |
+
for k in ckpt.keys():
|
| 366 |
+
new_k = k.replace('_forward_module.', '')
|
| 367 |
+
state_dict[new_k] = ckpt[k]
|
| 368 |
+
else:
|
| 369 |
+
state_dict = ckpt["state_dict"]
|
| 370 |
+
|
| 371 |
+
final_state_dict = {}
|
| 372 |
+
for k, v in state_dict.items():
|
| 373 |
+
if k.startswith('model.'):
|
| 374 |
+
final_state_dict[k.replace('model.', '')] = v
|
| 375 |
+
else:
|
| 376 |
+
final_state_dict[k] = v
|
| 377 |
+
missing, unexpected = self.load_state_dict(final_state_dict, strict=False)
|
| 378 |
+
print('unexpected keys:', unexpected)
|
| 379 |
+
print('missing keys:', missing)
|
| 380 |
+
|
| 381 |
+
def forward(self, x, t, contexts, **kwargs) -> Tensor:
|
| 382 |
+
cond = contexts['main']
|
| 383 |
+
latent = self.latent_in(x)
|
| 384 |
+
|
| 385 |
+
vec = self.time_in(timestep_embedding(t, 256, self.time_factor).to(dtype=latent.dtype))
|
| 386 |
+
if self.guidance_embed:
|
| 387 |
+
guidance = kwargs.get('guidance', None)
|
| 388 |
+
if guidance is None:
|
| 389 |
+
raise ValueError("Didn't get guidance strength for guidance distilled model.")
|
| 390 |
+
vec = vec + self.guidance_in(timestep_embedding(guidance, 256, self.time_factor))
|
| 391 |
+
|
| 392 |
+
cond = self.cond_in(cond)
|
| 393 |
+
pe = None
|
| 394 |
+
|
| 395 |
+
for block in self.double_blocks:
|
| 396 |
+
latent, cond = block(img=latent, txt=cond, vec=vec, pe=pe)
|
| 397 |
+
|
| 398 |
+
latent = torch.cat((cond, latent), 1)
|
| 399 |
+
for block in self.single_blocks:
|
| 400 |
+
latent = block(latent, vec=vec, pe=pe)
|
| 401 |
+
|
| 402 |
+
latent = latent[:, cond.shape[1]:, ...]
|
| 403 |
+
latent = self.final_layer(latent, vec)
|
| 404 |
+
return latent
|
Hunyuan3D-2.1/hy3dshape/hy3dshape/models/denoisers/hunyuandit.py
ADDED
|
@@ -0,0 +1,596 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Open Source Model Licensed under the Apache License Version 2.0
|
| 2 |
+
# and Other Licenses of the Third-Party Components therein:
|
| 3 |
+
# The below Model in this distribution may have been modified by THL A29 Limited
|
| 4 |
+
# ("Tencent Modifications"). All Tencent Modifications are Copyright (C) 2024 THL A29 Limited.
|
| 5 |
+
|
| 6 |
+
# Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved.
|
| 7 |
+
# The below software and/or models in this distribution may have been
|
| 8 |
+
# modified by THL A29 Limited ("Tencent Modifications").
|
| 9 |
+
# All Tencent Modifications are Copyright (C) THL A29 Limited.
|
| 10 |
+
|
| 11 |
+
# Hunyuan 3D is licensed under the TENCENT HUNYUAN NON-COMMERCIAL LICENSE AGREEMENT
|
| 12 |
+
# except for the third-party components listed below.
|
| 13 |
+
# Hunyuan 3D does not impose any additional limitations beyond what is outlined
|
| 14 |
+
# in the repsective licenses of these third-party components.
|
| 15 |
+
# Users must comply with all terms and conditions of original licenses of these third-party
|
| 16 |
+
# components and must ensure that the usage of the third party components adheres to
|
| 17 |
+
# all relevant laws and regulations.
|
| 18 |
+
|
| 19 |
+
# For avoidance of doubts, Hunyuan 3D means the large language models and
|
| 20 |
+
# their software and algorithms, including trained model weights, parameters (including
|
| 21 |
+
# optimizer states), machine-learning model code, inference-enabling code, training-enabling code,
|
| 22 |
+
# fine-tuning enabling code and other elements of the foregoing made publicly available
|
| 23 |
+
# by Tencent in accordance with TENCENT HUNYUAN COMMUNITY LICENSE AGREEMENT.
|
| 24 |
+
|
| 25 |
+
import math
|
| 26 |
+
|
| 27 |
+
import numpy as np
|
| 28 |
+
import torch
|
| 29 |
+
import torch.nn as nn
|
| 30 |
+
import torch.nn.functional as F
|
| 31 |
+
from einops import rearrange
|
| 32 |
+
|
| 33 |
+
from .moe_layers import MoEBlock
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
def modulate(x, shift, scale):
|
| 37 |
+
return x * (1 + scale.unsqueeze(1)) + shift.unsqueeze(1)
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
def get_1d_sincos_pos_embed_from_grid(embed_dim, pos):
|
| 41 |
+
"""
|
| 42 |
+
embed_dim: output dimension for each position
|
| 43 |
+
pos: a list of positions to be encoded: size (M,)
|
| 44 |
+
out: (M, D)
|
| 45 |
+
"""
|
| 46 |
+
assert embed_dim % 2 == 0
|
| 47 |
+
omega = np.arange(embed_dim // 2, dtype=np.float64)
|
| 48 |
+
omega /= embed_dim / 2.
|
| 49 |
+
omega = 1. / 10000 ** omega # (D/2,)
|
| 50 |
+
|
| 51 |
+
pos = pos.reshape(-1) # (M,)
|
| 52 |
+
out = np.einsum('m,d->md', pos, omega) # (M, D/2), outer product
|
| 53 |
+
|
| 54 |
+
emb_sin = np.sin(out) # (M, D/2)
|
| 55 |
+
emb_cos = np.cos(out) # (M, D/2)
|
| 56 |
+
|
| 57 |
+
return np.concatenate([emb_sin, emb_cos], axis=1)
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
class Timesteps(nn.Module):
|
| 61 |
+
def __init__(self,
|
| 62 |
+
num_channels: int,
|
| 63 |
+
downscale_freq_shift: float = 0.0,
|
| 64 |
+
scale: int = 1,
|
| 65 |
+
max_period: int = 10000
|
| 66 |
+
):
|
| 67 |
+
super().__init__()
|
| 68 |
+
self.num_channels = num_channels
|
| 69 |
+
self.downscale_freq_shift = downscale_freq_shift
|
| 70 |
+
self.scale = scale
|
| 71 |
+
self.max_period = max_period
|
| 72 |
+
|
| 73 |
+
def forward(self, timesteps):
|
| 74 |
+
assert len(timesteps.shape) == 1, "Timesteps should be a 1d-array"
|
| 75 |
+
embedding_dim = self.num_channels
|
| 76 |
+
half_dim = embedding_dim // 2
|
| 77 |
+
exponent = -math.log(self.max_period) * torch.arange(
|
| 78 |
+
start=0, end=half_dim, dtype=torch.float32, device=timesteps.device)
|
| 79 |
+
exponent = exponent / (half_dim - self.downscale_freq_shift)
|
| 80 |
+
emb = torch.exp(exponent)
|
| 81 |
+
emb = timesteps[:, None].float() * emb[None, :]
|
| 82 |
+
emb = self.scale * emb
|
| 83 |
+
emb = torch.cat([torch.sin(emb), torch.cos(emb)], dim=-1)
|
| 84 |
+
if embedding_dim % 2 == 1:
|
| 85 |
+
emb = torch.nn.functional.pad(emb, (0, 1, 0, 0))
|
| 86 |
+
return emb
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
class TimestepEmbedder(nn.Module):
|
| 90 |
+
"""
|
| 91 |
+
Embeds scalar timesteps into vector representations.
|
| 92 |
+
"""
|
| 93 |
+
|
| 94 |
+
def __init__(self, hidden_size, frequency_embedding_size=256, cond_proj_dim=None, out_size=None):
|
| 95 |
+
super().__init__()
|
| 96 |
+
if out_size is None:
|
| 97 |
+
out_size = hidden_size
|
| 98 |
+
self.mlp = nn.Sequential(
|
| 99 |
+
nn.Linear(hidden_size, frequency_embedding_size, bias=True),
|
| 100 |
+
nn.GELU(),
|
| 101 |
+
nn.Linear(frequency_embedding_size, out_size, bias=True),
|
| 102 |
+
)
|
| 103 |
+
self.frequency_embedding_size = frequency_embedding_size
|
| 104 |
+
|
| 105 |
+
if cond_proj_dim is not None:
|
| 106 |
+
self.cond_proj = nn.Linear(cond_proj_dim, frequency_embedding_size, bias=False)
|
| 107 |
+
|
| 108 |
+
self.time_embed = Timesteps(hidden_size)
|
| 109 |
+
|
| 110 |
+
def forward(self, t, condition):
|
| 111 |
+
|
| 112 |
+
t_freq = self.time_embed(t).type(self.mlp[0].weight.dtype)
|
| 113 |
+
|
| 114 |
+
# t_freq = timestep_embedding(t, self.frequency_embedding_size).type(self.mlp[0].weight.dtype)
|
| 115 |
+
if condition is not None:
|
| 116 |
+
t_freq = t_freq + self.cond_proj(condition)
|
| 117 |
+
|
| 118 |
+
t = self.mlp(t_freq)
|
| 119 |
+
t = t.unsqueeze(dim=1)
|
| 120 |
+
return t
|
| 121 |
+
|
| 122 |
+
|
| 123 |
+
class MLP(nn.Module):
|
| 124 |
+
def __init__(self, *, width: int):
|
| 125 |
+
super().__init__()
|
| 126 |
+
self.width = width
|
| 127 |
+
self.fc1 = nn.Linear(width, width * 4)
|
| 128 |
+
self.fc2 = nn.Linear(width * 4, width)
|
| 129 |
+
self.gelu = nn.GELU()
|
| 130 |
+
|
| 131 |
+
def forward(self, x):
|
| 132 |
+
return self.fc2(self.gelu(self.fc1(x)))
|
| 133 |
+
|
| 134 |
+
|
| 135 |
+
class CrossAttention(nn.Module):
|
| 136 |
+
def __init__(
|
| 137 |
+
self,
|
| 138 |
+
qdim,
|
| 139 |
+
kdim,
|
| 140 |
+
num_heads,
|
| 141 |
+
qkv_bias=True,
|
| 142 |
+
qk_norm=False,
|
| 143 |
+
norm_layer=nn.LayerNorm,
|
| 144 |
+
with_decoupled_ca=False,
|
| 145 |
+
decoupled_ca_dim=16,
|
| 146 |
+
decoupled_ca_weight=1.0,
|
| 147 |
+
**kwargs,
|
| 148 |
+
):
|
| 149 |
+
super().__init__()
|
| 150 |
+
self.qdim = qdim
|
| 151 |
+
self.kdim = kdim
|
| 152 |
+
self.num_heads = num_heads
|
| 153 |
+
assert self.qdim % num_heads == 0, "self.qdim must be divisible by num_heads"
|
| 154 |
+
self.head_dim = self.qdim // num_heads
|
| 155 |
+
assert self.head_dim % 8 == 0 and self.head_dim <= 128, "Only support head_dim <= 128 and divisible by 8"
|
| 156 |
+
self.scale = self.head_dim ** -0.5
|
| 157 |
+
|
| 158 |
+
self.to_q = nn.Linear(qdim, qdim, bias=qkv_bias)
|
| 159 |
+
self.to_k = nn.Linear(kdim, qdim, bias=qkv_bias)
|
| 160 |
+
self.to_v = nn.Linear(kdim, qdim, bias=qkv_bias)
|
| 161 |
+
|
| 162 |
+
# TODO: eps should be 1 / 65530 if using fp16
|
| 163 |
+
self.q_norm = norm_layer(self.head_dim, elementwise_affine=True, eps=1e-6) if qk_norm else nn.Identity()
|
| 164 |
+
self.k_norm = norm_layer(self.head_dim, elementwise_affine=True, eps=1e-6) if qk_norm else nn.Identity()
|
| 165 |
+
self.out_proj = nn.Linear(qdim, qdim, bias=True)
|
| 166 |
+
|
| 167 |
+
self.with_dca = with_decoupled_ca
|
| 168 |
+
if self.with_dca:
|
| 169 |
+
self.kv_proj_dca = nn.Linear(kdim, 2 * qdim, bias=qkv_bias)
|
| 170 |
+
self.k_norm_dca = norm_layer(self.head_dim, elementwise_affine=True, eps=1e-6) if qk_norm else nn.Identity()
|
| 171 |
+
self.dca_dim = decoupled_ca_dim
|
| 172 |
+
self.dca_weight = decoupled_ca_weight
|
| 173 |
+
|
| 174 |
+
def forward(self, x, y):
|
| 175 |
+
"""
|
| 176 |
+
Parameters
|
| 177 |
+
----------
|
| 178 |
+
x: torch.Tensor
|
| 179 |
+
(batch, seqlen1, hidden_dim) (where hidden_dim = num heads * head dim)
|
| 180 |
+
y: torch.Tensor
|
| 181 |
+
(batch, seqlen2, hidden_dim2)
|
| 182 |
+
freqs_cis_img: torch.Tensor
|
| 183 |
+
(batch, hidden_dim // 2), RoPE for image
|
| 184 |
+
"""
|
| 185 |
+
b, s1, c = x.shape # [b, s1, D]
|
| 186 |
+
|
| 187 |
+
if self.with_dca:
|
| 188 |
+
token_len = y.shape[1]
|
| 189 |
+
context_dca = y[:, -self.dca_dim:, :]
|
| 190 |
+
kv_dca = self.kv_proj_dca(context_dca).view(b, self.dca_dim, 2, self.num_heads, self.head_dim)
|
| 191 |
+
k_dca, v_dca = kv_dca.unbind(dim=2) # [b, s, h, d]
|
| 192 |
+
k_dca = self.k_norm_dca(k_dca)
|
| 193 |
+
y = y[:, :(token_len - self.dca_dim), :]
|
| 194 |
+
|
| 195 |
+
_, s2, c = y.shape # [b, s2, 1024]
|
| 196 |
+
q = self.to_q(x)
|
| 197 |
+
k = self.to_k(y)
|
| 198 |
+
v = self.to_v(y)
|
| 199 |
+
|
| 200 |
+
kv = torch.cat((k, v), dim=-1)
|
| 201 |
+
split_size = kv.shape[-1] // self.num_heads // 2
|
| 202 |
+
kv = kv.view(1, -1, self.num_heads, split_size * 2)
|
| 203 |
+
k, v = torch.split(kv, split_size, dim=-1)
|
| 204 |
+
|
| 205 |
+
q = q.view(b, s1, self.num_heads, self.head_dim) # [b, s1, h, d]
|
| 206 |
+
k = k.view(b, s2, self.num_heads, self.head_dim) # [b, s2, h, d]
|
| 207 |
+
v = v.view(b, s2, self.num_heads, self.head_dim) # [b, s2, h, d]
|
| 208 |
+
|
| 209 |
+
q = self.q_norm(q)
|
| 210 |
+
k = self.k_norm(k)
|
| 211 |
+
|
| 212 |
+
with torch.backends.cuda.sdp_kernel(
|
| 213 |
+
enable_flash=True,
|
| 214 |
+
enable_math=False,
|
| 215 |
+
enable_mem_efficient=True
|
| 216 |
+
):
|
| 217 |
+
q, k, v = map(lambda t: rearrange(t, 'b n h d -> b h n d', h=self.num_heads), (q, k, v))
|
| 218 |
+
context = F.scaled_dot_product_attention(
|
| 219 |
+
q, k, v
|
| 220 |
+
).transpose(1, 2).reshape(b, s1, -1)
|
| 221 |
+
|
| 222 |
+
if self.with_dca:
|
| 223 |
+
with torch.backends.cuda.sdp_kernel(
|
| 224 |
+
enable_flash=True,
|
| 225 |
+
enable_math=False,
|
| 226 |
+
enable_mem_efficient=True
|
| 227 |
+
):
|
| 228 |
+
k_dca, v_dca = map(lambda t: rearrange(t, 'b n h d -> b h n d', h=self.num_heads),
|
| 229 |
+
(k_dca, v_dca))
|
| 230 |
+
context_dca = F.scaled_dot_product_attention(
|
| 231 |
+
q, k_dca, v_dca).transpose(1, 2).reshape(b, s1, -1)
|
| 232 |
+
|
| 233 |
+
context = context + self.dca_weight * context_dca
|
| 234 |
+
|
| 235 |
+
out = self.out_proj(context) # context.reshape - B, L1, -1
|
| 236 |
+
|
| 237 |
+
return out
|
| 238 |
+
|
| 239 |
+
|
| 240 |
+
class Attention(nn.Module):
|
| 241 |
+
"""
|
| 242 |
+
We rename some layer names to align with flash attention
|
| 243 |
+
"""
|
| 244 |
+
|
| 245 |
+
def __init__(
|
| 246 |
+
self,
|
| 247 |
+
dim,
|
| 248 |
+
num_heads,
|
| 249 |
+
qkv_bias=True,
|
| 250 |
+
qk_norm=False,
|
| 251 |
+
norm_layer=nn.LayerNorm,
|
| 252 |
+
):
|
| 253 |
+
super().__init__()
|
| 254 |
+
self.dim = dim
|
| 255 |
+
self.num_heads = num_heads
|
| 256 |
+
assert self.dim % num_heads == 0, 'dim should be divisible by num_heads'
|
| 257 |
+
self.head_dim = self.dim // num_heads
|
| 258 |
+
# This assertion is aligned with flash attention
|
| 259 |
+
assert self.head_dim % 8 == 0 and self.head_dim <= 128, "Only support head_dim <= 128 and divisible by 8"
|
| 260 |
+
self.scale = self.head_dim ** -0.5
|
| 261 |
+
|
| 262 |
+
self.to_q = nn.Linear(dim, dim, bias=qkv_bias)
|
| 263 |
+
self.to_k = nn.Linear(dim, dim, bias=qkv_bias)
|
| 264 |
+
self.to_v = nn.Linear(dim, dim, bias=qkv_bias)
|
| 265 |
+
# TODO: eps should be 1 / 65530 if using fp16
|
| 266 |
+
self.q_norm = norm_layer(self.head_dim, elementwise_affine=True, eps=1e-6) if qk_norm else nn.Identity()
|
| 267 |
+
self.k_norm = norm_layer(self.head_dim, elementwise_affine=True, eps=1e-6) if qk_norm else nn.Identity()
|
| 268 |
+
self.out_proj = nn.Linear(dim, dim)
|
| 269 |
+
|
| 270 |
+
def forward(self, x):
|
| 271 |
+
B, N, C = x.shape
|
| 272 |
+
|
| 273 |
+
q = self.to_q(x)
|
| 274 |
+
k = self.to_k(x)
|
| 275 |
+
v = self.to_v(x)
|
| 276 |
+
|
| 277 |
+
qkv = torch.cat((q, k, v), dim=-1)
|
| 278 |
+
split_size = qkv.shape[-1] // self.num_heads // 3
|
| 279 |
+
qkv = qkv.view(1, -1, self.num_heads, split_size * 3)
|
| 280 |
+
q, k, v = torch.split(qkv, split_size, dim=-1)
|
| 281 |
+
|
| 282 |
+
q = q.reshape(B, N, self.num_heads, self.head_dim).transpose(1, 2) # [b, h, s, d]
|
| 283 |
+
k = k.reshape(B, N, self.num_heads, self.head_dim).transpose(1, 2) # [b, h, s, d]
|
| 284 |
+
v = v.reshape(B, N, self.num_heads, self.head_dim).transpose(1, 2)
|
| 285 |
+
|
| 286 |
+
q = self.q_norm(q) # [b, h, s, d]
|
| 287 |
+
k = self.k_norm(k) # [b, h, s, d]
|
| 288 |
+
|
| 289 |
+
with torch.backends.cuda.sdp_kernel(
|
| 290 |
+
enable_flash=True,
|
| 291 |
+
enable_math=False,
|
| 292 |
+
enable_mem_efficient=True
|
| 293 |
+
):
|
| 294 |
+
x = F.scaled_dot_product_attention(q, k, v)
|
| 295 |
+
x = x.transpose(1, 2).reshape(B, N, -1)
|
| 296 |
+
|
| 297 |
+
x = self.out_proj(x)
|
| 298 |
+
return x
|
| 299 |
+
|
| 300 |
+
|
| 301 |
+
class HunYuanDiTBlock(nn.Module):
|
| 302 |
+
def __init__(
|
| 303 |
+
self,
|
| 304 |
+
hidden_size,
|
| 305 |
+
c_emb_size,
|
| 306 |
+
num_heads,
|
| 307 |
+
text_states_dim=1024,
|
| 308 |
+
use_flash_attn=False,
|
| 309 |
+
qk_norm=False,
|
| 310 |
+
norm_layer=nn.LayerNorm,
|
| 311 |
+
qk_norm_layer=nn.RMSNorm,
|
| 312 |
+
with_decoupled_ca=False,
|
| 313 |
+
decoupled_ca_dim=16,
|
| 314 |
+
decoupled_ca_weight=1.0,
|
| 315 |
+
init_scale=1.0,
|
| 316 |
+
qkv_bias=True,
|
| 317 |
+
skip_connection=True,
|
| 318 |
+
timested_modulate=False,
|
| 319 |
+
use_moe: bool = False,
|
| 320 |
+
num_experts: int = 8,
|
| 321 |
+
moe_top_k: int = 2,
|
| 322 |
+
**kwargs,
|
| 323 |
+
):
|
| 324 |
+
super().__init__()
|
| 325 |
+
self.use_flash_attn = use_flash_attn
|
| 326 |
+
use_ele_affine = True
|
| 327 |
+
|
| 328 |
+
# ========================= Self-Attention =========================
|
| 329 |
+
self.norm1 = norm_layer(hidden_size, elementwise_affine=use_ele_affine, eps=1e-6)
|
| 330 |
+
self.attn1 = Attention(hidden_size, num_heads=num_heads, qkv_bias=qkv_bias, qk_norm=qk_norm,
|
| 331 |
+
norm_layer=qk_norm_layer)
|
| 332 |
+
|
| 333 |
+
# ========================= FFN =========================
|
| 334 |
+
self.norm2 = norm_layer(hidden_size, elementwise_affine=use_ele_affine, eps=1e-6)
|
| 335 |
+
|
| 336 |
+
# ========================= Add =========================
|
| 337 |
+
# Simply use add like SDXL.
|
| 338 |
+
self.timested_modulate = timested_modulate
|
| 339 |
+
if self.timested_modulate:
|
| 340 |
+
self.default_modulation = nn.Sequential(
|
| 341 |
+
nn.SiLU(),
|
| 342 |
+
nn.Linear(c_emb_size, hidden_size, bias=True)
|
| 343 |
+
)
|
| 344 |
+
|
| 345 |
+
# ========================= Cross-Attention =========================
|
| 346 |
+
self.attn2 = CrossAttention(hidden_size, text_states_dim, num_heads=num_heads, qkv_bias=qkv_bias,
|
| 347 |
+
qk_norm=qk_norm, norm_layer=qk_norm_layer,
|
| 348 |
+
with_decoupled_ca=with_decoupled_ca, decoupled_ca_dim=decoupled_ca_dim,
|
| 349 |
+
decoupled_ca_weight=decoupled_ca_weight, init_scale=init_scale,
|
| 350 |
+
)
|
| 351 |
+
self.norm3 = norm_layer(hidden_size, elementwise_affine=True, eps=1e-6)
|
| 352 |
+
|
| 353 |
+
if skip_connection:
|
| 354 |
+
self.skip_norm = norm_layer(hidden_size, elementwise_affine=True, eps=1e-6)
|
| 355 |
+
self.skip_linear = nn.Linear(2 * hidden_size, hidden_size)
|
| 356 |
+
else:
|
| 357 |
+
self.skip_linear = None
|
| 358 |
+
|
| 359 |
+
self.use_moe = use_moe
|
| 360 |
+
if self.use_moe:
|
| 361 |
+
print("using moe")
|
| 362 |
+
self.moe = MoEBlock(
|
| 363 |
+
hidden_size,
|
| 364 |
+
num_experts=num_experts,
|
| 365 |
+
moe_top_k=moe_top_k,
|
| 366 |
+
dropout=0.0,
|
| 367 |
+
activation_fn="gelu",
|
| 368 |
+
final_dropout=False,
|
| 369 |
+
ff_inner_dim=int(hidden_size * 4.0),
|
| 370 |
+
ff_bias=True,
|
| 371 |
+
)
|
| 372 |
+
else:
|
| 373 |
+
self.mlp = MLP(width=hidden_size)
|
| 374 |
+
|
| 375 |
+
def forward(self, x, c=None, text_states=None, skip_value=None):
|
| 376 |
+
|
| 377 |
+
if self.skip_linear is not None:
|
| 378 |
+
cat = torch.cat([skip_value, x], dim=-1)
|
| 379 |
+
x = self.skip_linear(cat)
|
| 380 |
+
x = self.skip_norm(x)
|
| 381 |
+
|
| 382 |
+
# Self-Attention
|
| 383 |
+
if self.timested_modulate:
|
| 384 |
+
shift_msa = self.default_modulation(c).unsqueeze(dim=1)
|
| 385 |
+
x = x + shift_msa
|
| 386 |
+
|
| 387 |
+
attn_out = self.attn1(self.norm1(x))
|
| 388 |
+
|
| 389 |
+
x = x + attn_out
|
| 390 |
+
|
| 391 |
+
# Cross-Attention
|
| 392 |
+
x = x + self.attn2(self.norm2(x), text_states)
|
| 393 |
+
|
| 394 |
+
# FFN Layer
|
| 395 |
+
mlp_inputs = self.norm3(x)
|
| 396 |
+
|
| 397 |
+
if self.use_moe:
|
| 398 |
+
x = x + self.moe(mlp_inputs)
|
| 399 |
+
else:
|
| 400 |
+
x = x + self.mlp(mlp_inputs)
|
| 401 |
+
|
| 402 |
+
return x
|
| 403 |
+
|
| 404 |
+
|
| 405 |
+
class AttentionPool(nn.Module):
|
| 406 |
+
def __init__(self, spacial_dim: int, embed_dim: int, num_heads: int, output_dim: int = None):
|
| 407 |
+
super().__init__()
|
| 408 |
+
self.positional_embedding = nn.Parameter(torch.randn(spacial_dim + 1, embed_dim) / embed_dim ** 0.5)
|
| 409 |
+
self.k_proj = nn.Linear(embed_dim, embed_dim)
|
| 410 |
+
self.q_proj = nn.Linear(embed_dim, embed_dim)
|
| 411 |
+
self.v_proj = nn.Linear(embed_dim, embed_dim)
|
| 412 |
+
self.c_proj = nn.Linear(embed_dim, output_dim or embed_dim)
|
| 413 |
+
self.num_heads = num_heads
|
| 414 |
+
|
| 415 |
+
def forward(self, x, attention_mask=None):
|
| 416 |
+
x = x.permute(1, 0, 2) # NLC -> LNC
|
| 417 |
+
if attention_mask is not None:
|
| 418 |
+
attention_mask = attention_mask.unsqueeze(-1).permute(1, 0, 2)
|
| 419 |
+
global_emb = (x * attention_mask).sum(dim=0) / attention_mask.sum(dim=0)
|
| 420 |
+
x = torch.cat([global_emb[None,], x], dim=0)
|
| 421 |
+
|
| 422 |
+
else:
|
| 423 |
+
x = torch.cat([x.mean(dim=0, keepdim=True), x], dim=0) # (L+1)NC
|
| 424 |
+
x = x + self.positional_embedding[:, None, :].to(x.dtype) # (L+1)NC
|
| 425 |
+
x, _ = F.multi_head_attention_forward(
|
| 426 |
+
query=x[:1], key=x, value=x,
|
| 427 |
+
embed_dim_to_check=x.shape[-1],
|
| 428 |
+
num_heads=self.num_heads,
|
| 429 |
+
q_proj_weight=self.q_proj.weight,
|
| 430 |
+
k_proj_weight=self.k_proj.weight,
|
| 431 |
+
v_proj_weight=self.v_proj.weight,
|
| 432 |
+
in_proj_weight=None,
|
| 433 |
+
in_proj_bias=torch.cat([self.q_proj.bias, self.k_proj.bias, self.v_proj.bias]),
|
| 434 |
+
bias_k=None,
|
| 435 |
+
bias_v=None,
|
| 436 |
+
add_zero_attn=False,
|
| 437 |
+
dropout_p=0,
|
| 438 |
+
out_proj_weight=self.c_proj.weight,
|
| 439 |
+
out_proj_bias=self.c_proj.bias,
|
| 440 |
+
use_separate_proj_weight=True,
|
| 441 |
+
training=self.training,
|
| 442 |
+
need_weights=False
|
| 443 |
+
)
|
| 444 |
+
return x.squeeze(0)
|
| 445 |
+
|
| 446 |
+
|
| 447 |
+
class FinalLayer(nn.Module):
|
| 448 |
+
"""
|
| 449 |
+
The final layer of HunYuanDiT.
|
| 450 |
+
"""
|
| 451 |
+
|
| 452 |
+
def __init__(self, final_hidden_size, out_channels):
|
| 453 |
+
super().__init__()
|
| 454 |
+
self.final_hidden_size = final_hidden_size
|
| 455 |
+
self.norm_final = nn.LayerNorm(final_hidden_size, elementwise_affine=True, eps=1e-6)
|
| 456 |
+
self.linear = nn.Linear(final_hidden_size, out_channels, bias=True)
|
| 457 |
+
|
| 458 |
+
def forward(self, x):
|
| 459 |
+
x = self.norm_final(x)
|
| 460 |
+
x = x[:, 1:]
|
| 461 |
+
x = self.linear(x)
|
| 462 |
+
return x
|
| 463 |
+
|
| 464 |
+
|
| 465 |
+
class HunYuanDiTPlain(nn.Module):
|
| 466 |
+
|
| 467 |
+
def __init__(
|
| 468 |
+
self,
|
| 469 |
+
input_size=1024,
|
| 470 |
+
in_channels=4,
|
| 471 |
+
hidden_size=1024,
|
| 472 |
+
context_dim=1024,
|
| 473 |
+
depth=24,
|
| 474 |
+
num_heads=16,
|
| 475 |
+
mlp_ratio=4.0,
|
| 476 |
+
norm_type='layer',
|
| 477 |
+
qk_norm_type='rms',
|
| 478 |
+
qk_norm=False,
|
| 479 |
+
text_len=257,
|
| 480 |
+
with_decoupled_ca=False,
|
| 481 |
+
additional_cond_hidden_state=768,
|
| 482 |
+
decoupled_ca_dim=16,
|
| 483 |
+
decoupled_ca_weight=1.0,
|
| 484 |
+
use_pos_emb=False,
|
| 485 |
+
use_attention_pooling=True,
|
| 486 |
+
guidance_cond_proj_dim=None,
|
| 487 |
+
qkv_bias=True,
|
| 488 |
+
num_moe_layers: int = 6,
|
| 489 |
+
num_experts: int = 8,
|
| 490 |
+
moe_top_k: int = 2,
|
| 491 |
+
**kwargs
|
| 492 |
+
):
|
| 493 |
+
super().__init__()
|
| 494 |
+
self.input_size = input_size
|
| 495 |
+
self.depth = depth
|
| 496 |
+
self.in_channels = in_channels
|
| 497 |
+
self.out_channels = in_channels
|
| 498 |
+
self.num_heads = num_heads
|
| 499 |
+
|
| 500 |
+
self.hidden_size = hidden_size
|
| 501 |
+
self.norm = nn.LayerNorm if norm_type == 'layer' else nn.RMSNorm
|
| 502 |
+
self.qk_norm = nn.RMSNorm if qk_norm_type == 'rms' else nn.LayerNorm
|
| 503 |
+
self.context_dim = context_dim
|
| 504 |
+
|
| 505 |
+
self.with_decoupled_ca = with_decoupled_ca
|
| 506 |
+
self.decoupled_ca_dim = decoupled_ca_dim
|
| 507 |
+
self.decoupled_ca_weight = decoupled_ca_weight
|
| 508 |
+
self.use_pos_emb = use_pos_emb
|
| 509 |
+
self.use_attention_pooling = use_attention_pooling
|
| 510 |
+
self.guidance_cond_proj_dim = guidance_cond_proj_dim
|
| 511 |
+
|
| 512 |
+
self.text_len = text_len
|
| 513 |
+
|
| 514 |
+
self.x_embedder = nn.Linear(in_channels, hidden_size, bias=True)
|
| 515 |
+
self.t_embedder = TimestepEmbedder(hidden_size, hidden_size * 4, cond_proj_dim=guidance_cond_proj_dim)
|
| 516 |
+
|
| 517 |
+
# Will use fixed sin-cos embedding:
|
| 518 |
+
if self.use_pos_emb:
|
| 519 |
+
self.register_buffer("pos_embed", torch.zeros(1, input_size, hidden_size))
|
| 520 |
+
pos = np.arange(self.input_size, dtype=np.float32)
|
| 521 |
+
pos_embed = get_1d_sincos_pos_embed_from_grid(self.pos_embed.shape[-1], pos)
|
| 522 |
+
self.pos_embed.data.copy_(torch.from_numpy(pos_embed).float().unsqueeze(0))
|
| 523 |
+
|
| 524 |
+
self.use_attention_pooling = use_attention_pooling
|
| 525 |
+
if use_attention_pooling:
|
| 526 |
+
self.pooler = AttentionPool(self.text_len, context_dim, num_heads=8, output_dim=1024)
|
| 527 |
+
self.extra_embedder = nn.Sequential(
|
| 528 |
+
nn.Linear(1024, hidden_size * 4),
|
| 529 |
+
nn.SiLU(),
|
| 530 |
+
nn.Linear(hidden_size * 4, hidden_size, bias=True),
|
| 531 |
+
)
|
| 532 |
+
|
| 533 |
+
if with_decoupled_ca:
|
| 534 |
+
self.additional_cond_hidden_state = additional_cond_hidden_state
|
| 535 |
+
self.additional_cond_proj = nn.Sequential(
|
| 536 |
+
nn.Linear(additional_cond_hidden_state, hidden_size * 4),
|
| 537 |
+
nn.SiLU(),
|
| 538 |
+
nn.Linear(hidden_size * 4, 1024, bias=True),
|
| 539 |
+
)
|
| 540 |
+
|
| 541 |
+
# HUnYuanDiT Blocks
|
| 542 |
+
self.blocks = nn.ModuleList([
|
| 543 |
+
HunYuanDiTBlock(hidden_size=hidden_size,
|
| 544 |
+
c_emb_size=hidden_size,
|
| 545 |
+
num_heads=num_heads,
|
| 546 |
+
mlp_ratio=mlp_ratio,
|
| 547 |
+
text_states_dim=context_dim,
|
| 548 |
+
qk_norm=qk_norm,
|
| 549 |
+
norm_layer=self.norm,
|
| 550 |
+
qk_norm_layer=self.qk_norm,
|
| 551 |
+
skip_connection=layer > depth // 2,
|
| 552 |
+
with_decoupled_ca=with_decoupled_ca,
|
| 553 |
+
decoupled_ca_dim=decoupled_ca_dim,
|
| 554 |
+
decoupled_ca_weight=decoupled_ca_weight,
|
| 555 |
+
qkv_bias=qkv_bias,
|
| 556 |
+
use_moe=True if depth - layer <= num_moe_layers else False,
|
| 557 |
+
num_experts=num_experts,
|
| 558 |
+
moe_top_k=moe_top_k
|
| 559 |
+
)
|
| 560 |
+
for layer in range(depth)
|
| 561 |
+
])
|
| 562 |
+
self.depth = depth
|
| 563 |
+
|
| 564 |
+
self.final_layer = FinalLayer(hidden_size, self.out_channels)
|
| 565 |
+
|
| 566 |
+
def forward(self, x, t, contexts, **kwargs):
|
| 567 |
+
cond = contexts['main']
|
| 568 |
+
|
| 569 |
+
t = self.t_embedder(t, condition=kwargs.get('guidance_cond'))
|
| 570 |
+
x = self.x_embedder(x)
|
| 571 |
+
|
| 572 |
+
if self.use_pos_emb:
|
| 573 |
+
pos_embed = self.pos_embed.to(x.dtype)
|
| 574 |
+
x = x + pos_embed
|
| 575 |
+
|
| 576 |
+
if self.use_attention_pooling:
|
| 577 |
+
extra_vec = self.pooler(cond, None)
|
| 578 |
+
c = t + self.extra_embedder(extra_vec) # [B, D]
|
| 579 |
+
else:
|
| 580 |
+
c = t
|
| 581 |
+
|
| 582 |
+
if self.with_decoupled_ca:
|
| 583 |
+
additional_cond = self.additional_cond_proj(contexts['additional'])
|
| 584 |
+
cond = torch.cat([cond, additional_cond], dim=1)
|
| 585 |
+
|
| 586 |
+
x = torch.cat([c, x], dim=1)
|
| 587 |
+
|
| 588 |
+
skip_value_list = []
|
| 589 |
+
for layer, block in enumerate(self.blocks):
|
| 590 |
+
skip_value = None if layer <= self.depth // 2 else skip_value_list.pop()
|
| 591 |
+
x = block(x, c, cond, skip_value=skip_value)
|
| 592 |
+
if layer < self.depth // 2:
|
| 593 |
+
skip_value_list.append(x)
|
| 594 |
+
|
| 595 |
+
x = self.final_layer(x)
|
| 596 |
+
return x
|
Hunyuan3D-2.1/hy3dshape/hy3dshape/models/denoisers/moe_layers.py
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Hunyuan 3D is licensed under the TENCENT HUNYUAN NON-COMMERCIAL LICENSE AGREEMENT
|
| 2 |
+
# except for the third-party components listed below.
|
| 3 |
+
# Hunyuan 3D does not impose any additional limitations beyond what is outlined
|
| 4 |
+
# in the repsective licenses of these third-party components.
|
| 5 |
+
# Users must comply with all terms and conditions of original licenses of these third-party
|
| 6 |
+
# components and must ensure that the usage of the third party components adheres to
|
| 7 |
+
# all relevant laws and regulations.
|
| 8 |
+
|
| 9 |
+
# For avoidance of doubts, Hunyuan 3D means the large language models and
|
| 10 |
+
# their software and algorithms, including trained model weights, parameters (including
|
| 11 |
+
# optimizer states), machine-learning model code, inference-enabling code, training-enabling code,
|
| 12 |
+
# fine-tuning enabling code and other elements of the foregoing made publicly available
|
| 13 |
+
# by Tencent in accordance with TENCENT HUNYUAN COMMUNITY LICENSE AGREEMENT.
|
| 14 |
+
|
| 15 |
+
import torch
|
| 16 |
+
import torch.nn as nn
|
| 17 |
+
import numpy as np
|
| 18 |
+
import math
|
| 19 |
+
from timm.models.vision_transformer import PatchEmbed, Attention, Mlp
|
| 20 |
+
|
| 21 |
+
import torch.nn.functional as F
|
| 22 |
+
from diffusers.models.attention import FeedForward
|
| 23 |
+
|
| 24 |
+
class AddAuxiliaryLoss(torch.autograd.Function):
|
| 25 |
+
"""
|
| 26 |
+
The trick function of adding auxiliary (aux) loss,
|
| 27 |
+
which includes the gradient of the aux loss during backpropagation.
|
| 28 |
+
"""
|
| 29 |
+
@staticmethod
|
| 30 |
+
def forward(ctx, x, loss):
|
| 31 |
+
assert loss.numel() == 1
|
| 32 |
+
ctx.dtype = loss.dtype
|
| 33 |
+
ctx.required_aux_loss = loss.requires_grad
|
| 34 |
+
return x
|
| 35 |
+
|
| 36 |
+
@staticmethod
|
| 37 |
+
def backward(ctx, grad_output):
|
| 38 |
+
grad_loss = None
|
| 39 |
+
if ctx.required_aux_loss:
|
| 40 |
+
grad_loss = torch.ones(1, dtype=ctx.dtype, device=grad_output.device)
|
| 41 |
+
return grad_output, grad_loss
|
| 42 |
+
|
| 43 |
+
class MoEGate(nn.Module):
|
| 44 |
+
def __init__(self, embed_dim, num_experts=16, num_experts_per_tok=2, aux_loss_alpha=0.01):
|
| 45 |
+
super().__init__()
|
| 46 |
+
self.top_k = num_experts_per_tok
|
| 47 |
+
self.n_routed_experts = num_experts
|
| 48 |
+
|
| 49 |
+
self.scoring_func = 'softmax'
|
| 50 |
+
self.alpha = aux_loss_alpha
|
| 51 |
+
self.seq_aux = False
|
| 52 |
+
|
| 53 |
+
# topk selection algorithm
|
| 54 |
+
self.norm_topk_prob = False
|
| 55 |
+
self.gating_dim = embed_dim
|
| 56 |
+
self.weight = nn.Parameter(torch.empty((self.n_routed_experts, self.gating_dim)))
|
| 57 |
+
self.reset_parameters()
|
| 58 |
+
|
| 59 |
+
def reset_parameters(self) -> None:
|
| 60 |
+
import torch.nn.init as init
|
| 61 |
+
init.kaiming_uniform_(self.weight, a=math.sqrt(5))
|
| 62 |
+
|
| 63 |
+
def forward(self, hidden_states):
|
| 64 |
+
bsz, seq_len, h = hidden_states.shape
|
| 65 |
+
# print(bsz, seq_len, h)
|
| 66 |
+
### compute gating score
|
| 67 |
+
hidden_states = hidden_states.view(-1, h)
|
| 68 |
+
logits = F.linear(hidden_states, self.weight, None)
|
| 69 |
+
if self.scoring_func == 'softmax':
|
| 70 |
+
scores = logits.softmax(dim=-1)
|
| 71 |
+
else:
|
| 72 |
+
raise NotImplementedError(f'insupportable scoring function for MoE gating: {self.scoring_func}')
|
| 73 |
+
|
| 74 |
+
### select top-k experts
|
| 75 |
+
topk_weight, topk_idx = torch.topk(scores, k=self.top_k, dim=-1, sorted=False)
|
| 76 |
+
|
| 77 |
+
### norm gate to sum 1
|
| 78 |
+
if self.top_k > 1 and self.norm_topk_prob:
|
| 79 |
+
denominator = topk_weight.sum(dim=-1, keepdim=True) + 1e-20
|
| 80 |
+
topk_weight = topk_weight / denominator
|
| 81 |
+
|
| 82 |
+
### expert-level computation auxiliary loss
|
| 83 |
+
if self.training and self.alpha > 0.0:
|
| 84 |
+
scores_for_aux = scores
|
| 85 |
+
aux_topk = self.top_k
|
| 86 |
+
# always compute aux loss based on the naive greedy topk method
|
| 87 |
+
topk_idx_for_aux_loss = topk_idx.view(bsz, -1)
|
| 88 |
+
if self.seq_aux:
|
| 89 |
+
scores_for_seq_aux = scores_for_aux.view(bsz, seq_len, -1)
|
| 90 |
+
ce = torch.zeros(bsz, self.n_routed_experts, device=hidden_states.device)
|
| 91 |
+
ce.scatter_add_(
|
| 92 |
+
1,
|
| 93 |
+
topk_idx_for_aux_loss,
|
| 94 |
+
torch.ones(
|
| 95 |
+
bsz, seq_len * aux_topk,
|
| 96 |
+
device=hidden_states.device
|
| 97 |
+
)
|
| 98 |
+
).div_(seq_len * aux_topk / self.n_routed_experts)
|
| 99 |
+
aux_loss = (ce * scores_for_seq_aux.mean(dim = 1)).sum(dim = 1).mean()
|
| 100 |
+
aux_loss = aux_loss * self.alpha
|
| 101 |
+
else:
|
| 102 |
+
mask_ce = F.one_hot(topk_idx_for_aux_loss.view(-1),
|
| 103 |
+
num_classes=self.n_routed_experts)
|
| 104 |
+
ce = mask_ce.float().mean(0)
|
| 105 |
+
Pi = scores_for_aux.mean(0)
|
| 106 |
+
fi = ce * self.n_routed_experts
|
| 107 |
+
aux_loss = (Pi * fi).sum() * self.alpha
|
| 108 |
+
else:
|
| 109 |
+
aux_loss = None
|
| 110 |
+
return topk_idx, topk_weight, aux_loss
|
| 111 |
+
|
| 112 |
+
class MoEBlock(nn.Module):
|
| 113 |
+
def __init__(self, dim, num_experts=8, moe_top_k=2,
|
| 114 |
+
activation_fn = "gelu", dropout=0.0, final_dropout = False,
|
| 115 |
+
ff_inner_dim = None, ff_bias = True):
|
| 116 |
+
super().__init__()
|
| 117 |
+
self.moe_top_k = moe_top_k
|
| 118 |
+
self.experts = nn.ModuleList([
|
| 119 |
+
FeedForward(dim,dropout=dropout,
|
| 120 |
+
activation_fn=activation_fn,
|
| 121 |
+
final_dropout=final_dropout,
|
| 122 |
+
inner_dim=ff_inner_dim,
|
| 123 |
+
bias=ff_bias)
|
| 124 |
+
for i in range(num_experts)])
|
| 125 |
+
self.gate = MoEGate(embed_dim=dim, num_experts=num_experts, num_experts_per_tok=moe_top_k)
|
| 126 |
+
|
| 127 |
+
self.shared_experts = FeedForward(dim,dropout=dropout, activation_fn=activation_fn,
|
| 128 |
+
final_dropout=final_dropout, inner_dim=ff_inner_dim,
|
| 129 |
+
bias=ff_bias)
|
| 130 |
+
|
| 131 |
+
def initialize_weight(self):
|
| 132 |
+
pass
|
| 133 |
+
|
| 134 |
+
def forward(self, hidden_states):
|
| 135 |
+
identity = hidden_states
|
| 136 |
+
orig_shape = hidden_states.shape
|
| 137 |
+
topk_idx, topk_weight, aux_loss = self.gate(hidden_states)
|
| 138 |
+
|
| 139 |
+
hidden_states = hidden_states.view(-1, hidden_states.shape[-1])
|
| 140 |
+
flat_topk_idx = topk_idx.view(-1)
|
| 141 |
+
if self.training:
|
| 142 |
+
hidden_states = hidden_states.repeat_interleave(self.moe_top_k, dim=0)
|
| 143 |
+
y = torch.empty_like(hidden_states, dtype=hidden_states.dtype)
|
| 144 |
+
for i, expert in enumerate(self.experts):
|
| 145 |
+
tmp = expert(hidden_states[flat_topk_idx == i])
|
| 146 |
+
y[flat_topk_idx == i] = tmp.to(hidden_states.dtype)
|
| 147 |
+
y = (y.view(*topk_weight.shape, -1) * topk_weight.unsqueeze(-1)).sum(dim=1)
|
| 148 |
+
y = y.view(*orig_shape)
|
| 149 |
+
y = AddAuxiliaryLoss.apply(y, aux_loss)
|
| 150 |
+
else:
|
| 151 |
+
y = self.moe_infer(hidden_states, flat_topk_idx, topk_weight.view(-1, 1)).view(*orig_shape)
|
| 152 |
+
y = y + self.shared_experts(identity)
|
| 153 |
+
return y
|
| 154 |
+
|
| 155 |
+
|
| 156 |
+
@torch.no_grad()
|
| 157 |
+
def moe_infer(self, x, flat_expert_indices, flat_expert_weights):
|
| 158 |
+
expert_cache = torch.zeros_like(x)
|
| 159 |
+
idxs = flat_expert_indices.argsort()
|
| 160 |
+
tokens_per_expert = flat_expert_indices.bincount().cpu().numpy().cumsum(0)
|
| 161 |
+
token_idxs = idxs // self.moe_top_k
|
| 162 |
+
for i, end_idx in enumerate(tokens_per_expert):
|
| 163 |
+
start_idx = 0 if i == 0 else tokens_per_expert[i-1]
|
| 164 |
+
if start_idx == end_idx:
|
| 165 |
+
continue
|
| 166 |
+
expert = self.experts[i]
|
| 167 |
+
exp_token_idx = token_idxs[start_idx:end_idx]
|
| 168 |
+
expert_tokens = x[exp_token_idx]
|
| 169 |
+
expert_out = expert(expert_tokens)
|
| 170 |
+
expert_out.mul_(flat_expert_weights[idxs[start_idx:end_idx]])
|
| 171 |
+
|
| 172 |
+
# for fp16 and other dtype
|
| 173 |
+
expert_cache = expert_cache.to(expert_out.dtype)
|
| 174 |
+
expert_cache.scatter_reduce_(0, exp_token_idx.view(-1, 1).repeat(1, x.shape[-1]),
|
| 175 |
+
expert_out,
|
| 176 |
+
reduce='sum')
|
| 177 |
+
return expert_cache
|
Hunyuan3D-2.1/hy3dshape/hy3dshape/models/diffusion/flow_matching_sit.py
ADDED
|
@@ -0,0 +1,354 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
from contextlib import contextmanager
|
| 3 |
+
from typing import List, Tuple, Optional, Union
|
| 4 |
+
|
| 5 |
+
import torch
|
| 6 |
+
import torch.nn as nn
|
| 7 |
+
from torch.optim import lr_scheduler
|
| 8 |
+
import pytorch_lightning as pl
|
| 9 |
+
from pytorch_lightning.utilities import rank_zero_info
|
| 10 |
+
from pytorch_lightning.utilities import rank_zero_only
|
| 11 |
+
|
| 12 |
+
from ...utils.ema import LitEma
|
| 13 |
+
from ...utils.misc import instantiate_from_config, instantiate_non_trainable_model
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
class Diffuser(pl.LightningModule):
|
| 18 |
+
def __init__(
|
| 19 |
+
self,
|
| 20 |
+
*,
|
| 21 |
+
first_stage_config,
|
| 22 |
+
cond_stage_config,
|
| 23 |
+
denoiser_cfg,
|
| 24 |
+
scheduler_cfg,
|
| 25 |
+
optimizer_cfg,
|
| 26 |
+
pipeline_cfg=None,
|
| 27 |
+
image_processor_cfg=None,
|
| 28 |
+
lora_config=None,
|
| 29 |
+
ema_config=None,
|
| 30 |
+
first_stage_key: str = "surface",
|
| 31 |
+
cond_stage_key: str = "image",
|
| 32 |
+
scale_by_std: bool = False,
|
| 33 |
+
z_scale_factor: float = 1.0,
|
| 34 |
+
ckpt_path: Optional[str] = None,
|
| 35 |
+
ignore_keys: Union[Tuple[str], List[str]] = (),
|
| 36 |
+
torch_compile: bool = False,
|
| 37 |
+
):
|
| 38 |
+
super().__init__()
|
| 39 |
+
self.first_stage_key = first_stage_key
|
| 40 |
+
self.cond_stage_key = cond_stage_key
|
| 41 |
+
|
| 42 |
+
# ========= init optimizer config ========= #
|
| 43 |
+
self.optimizer_cfg = optimizer_cfg
|
| 44 |
+
|
| 45 |
+
# ========= init diffusion scheduler ========= #
|
| 46 |
+
self.scheduler_cfg = scheduler_cfg
|
| 47 |
+
self.sampler = None
|
| 48 |
+
if 'transport' in scheduler_cfg:
|
| 49 |
+
self.transport = instantiate_from_config(scheduler_cfg.transport)
|
| 50 |
+
self.sampler = instantiate_from_config(scheduler_cfg.sampler, transport=self.transport)
|
| 51 |
+
self.sample_fn = self.sampler.sample_ode(**scheduler_cfg.sampler.ode_params)
|
| 52 |
+
|
| 53 |
+
# ========= init the model ========= #
|
| 54 |
+
self.denoiser_cfg = denoiser_cfg
|
| 55 |
+
self.model = instantiate_from_config(denoiser_cfg, device=None, dtype=None)
|
| 56 |
+
self.cond_stage_model = instantiate_from_config(cond_stage_config)
|
| 57 |
+
|
| 58 |
+
self.ckpt_path = ckpt_path
|
| 59 |
+
if ckpt_path is not None:
|
| 60 |
+
self.init_from_ckpt(ckpt_path, ignore_keys=ignore_keys)
|
| 61 |
+
|
| 62 |
+
# ========= config lora model ========= #
|
| 63 |
+
if lora_config is not None:
|
| 64 |
+
from peft import LoraConfig, get_peft_model
|
| 65 |
+
loraconfig = LoraConfig(
|
| 66 |
+
r=lora_config.rank,
|
| 67 |
+
lora_alpha=lora_config.rank,
|
| 68 |
+
target_modules=lora_config.get('target_modules')
|
| 69 |
+
)
|
| 70 |
+
self.model = get_peft_model(self.model, loraconfig)
|
| 71 |
+
|
| 72 |
+
# ========= config ema model ========= #
|
| 73 |
+
self.ema_config = ema_config
|
| 74 |
+
if self.ema_config is not None:
|
| 75 |
+
if self.ema_config.ema_model == 'DSEma':
|
| 76 |
+
# from michelangelo.models.modules.ema_deepspeed import DSEma
|
| 77 |
+
from ..utils.ema_deepspeed import DSEma
|
| 78 |
+
self.model_ema = DSEma(self.model, decay=self.ema_config.ema_decay)
|
| 79 |
+
else:
|
| 80 |
+
self.model_ema = LitEma(self.model, decay=self.ema_config.ema_decay)
|
| 81 |
+
#do not initilize EMA weight from ckpt path, since I need to change moe layers
|
| 82 |
+
if ckpt_path is not None:
|
| 83 |
+
self.init_from_ckpt(ckpt_path, ignore_keys=ignore_keys)
|
| 84 |
+
print(f"Keeping EMAs of {len(list(self.model_ema.buffers()))}.")
|
| 85 |
+
|
| 86 |
+
# ========= init vae at last to prevent it is overridden by loaded ckpt ========= #
|
| 87 |
+
self.first_stage_model = instantiate_non_trainable_model(first_stage_config)
|
| 88 |
+
|
| 89 |
+
self.scale_by_std = scale_by_std
|
| 90 |
+
if scale_by_std:
|
| 91 |
+
self.register_buffer("z_scale_factor", torch.tensor(z_scale_factor))
|
| 92 |
+
else:
|
| 93 |
+
self.z_scale_factor = z_scale_factor
|
| 94 |
+
|
| 95 |
+
# ========= init pipeline for inference ========= #
|
| 96 |
+
self.image_processor_cfg = image_processor_cfg
|
| 97 |
+
self.image_processor = None
|
| 98 |
+
if self.image_processor_cfg is not None:
|
| 99 |
+
self.image_processor = instantiate_from_config(self.image_processor_cfg)
|
| 100 |
+
self.pipeline_cfg = pipeline_cfg
|
| 101 |
+
from ...schedulers import FlowMatchEulerDiscreteScheduler
|
| 102 |
+
scheduler = FlowMatchEulerDiscreteScheduler(num_train_timesteps=1000)
|
| 103 |
+
self.pipeline = instantiate_from_config(
|
| 104 |
+
pipeline_cfg,
|
| 105 |
+
vae=self.first_stage_model,
|
| 106 |
+
model=self.model,
|
| 107 |
+
scheduler=scheduler, # self.sampler,
|
| 108 |
+
conditioner=self.cond_stage_model,
|
| 109 |
+
image_processor=self.image_processor,
|
| 110 |
+
)
|
| 111 |
+
|
| 112 |
+
# ========= torch compile to accelerate ========= #
|
| 113 |
+
self.torch_compile = torch_compile
|
| 114 |
+
if self.torch_compile:
|
| 115 |
+
torch.nn.Module.compile(self.model)
|
| 116 |
+
torch.nn.Module.compile(self.first_stage_model)
|
| 117 |
+
torch.nn.Module.compile(self.cond_stage_model)
|
| 118 |
+
print(f'*' * 100)
|
| 119 |
+
print(f'Compile model for acceleration')
|
| 120 |
+
print(f'*' * 100)
|
| 121 |
+
|
| 122 |
+
@contextmanager
|
| 123 |
+
def ema_scope(self, context=None):
|
| 124 |
+
if self.ema_config is not None and self.ema_config.get('ema_inference', False):
|
| 125 |
+
self.model_ema.store(self.model)
|
| 126 |
+
self.model_ema.copy_to(self.model)
|
| 127 |
+
if context is not None:
|
| 128 |
+
print(f"{context}: Switched to EMA weights")
|
| 129 |
+
try:
|
| 130 |
+
yield None
|
| 131 |
+
finally:
|
| 132 |
+
if self.ema_config is not None and self.ema_config.get('ema_inference', False):
|
| 133 |
+
self.model_ema.restore(self.model)
|
| 134 |
+
if context is not None:
|
| 135 |
+
print(f"{context}: Restored training weights")
|
| 136 |
+
|
| 137 |
+
def init_from_ckpt(self, path, ignore_keys=()):
|
| 138 |
+
ckpt = torch.load(path, map_location="cpu")
|
| 139 |
+
if 'state_dict' not in ckpt:
|
| 140 |
+
# deepspeed ckpt
|
| 141 |
+
state_dict = {}
|
| 142 |
+
for k in ckpt.keys():
|
| 143 |
+
new_k = k.replace('_forward_module.', '')
|
| 144 |
+
state_dict[new_k] = ckpt[k]
|
| 145 |
+
else:
|
| 146 |
+
state_dict = ckpt["state_dict"]
|
| 147 |
+
|
| 148 |
+
keys = list(state_dict.keys())
|
| 149 |
+
for k in keys:
|
| 150 |
+
for ik in ignore_keys:
|
| 151 |
+
if ik in k:
|
| 152 |
+
print("Deleting key {} from state_dict.".format(k))
|
| 153 |
+
del state_dict[k]
|
| 154 |
+
|
| 155 |
+
missing, unexpected = self.load_state_dict(state_dict, strict=False)
|
| 156 |
+
print(f"Restored from {path} with {len(missing)} missing and {len(unexpected)} unexpected keys")
|
| 157 |
+
if len(missing) > 0:
|
| 158 |
+
print(f"Missing Keys: {missing}")
|
| 159 |
+
print(f"Unexpected Keys: {unexpected}")
|
| 160 |
+
|
| 161 |
+
def on_load_checkpoint(self, checkpoint):
|
| 162 |
+
"""
|
| 163 |
+
The pt_model is trained separately, so we already have access to its
|
| 164 |
+
checkpoint and load it separately with `self.set_pt_model`.
|
| 165 |
+
|
| 166 |
+
However, the PL Trainer is strict about
|
| 167 |
+
checkpoint loading (not configurable), so it expects the loaded state_dict
|
| 168 |
+
to match exactly the keys in the model state_dict.
|
| 169 |
+
|
| 170 |
+
So, when loading the checkpoint, before matching keys, we add all pt_model keys
|
| 171 |
+
from self.state_dict() to the checkpoint state dict, so that they match
|
| 172 |
+
"""
|
| 173 |
+
for key in self.state_dict().keys():
|
| 174 |
+
if key.startswith("model_ema") and key not in checkpoint["state_dict"]:
|
| 175 |
+
checkpoint["state_dict"][key] = self.state_dict()[key]
|
| 176 |
+
|
| 177 |
+
def configure_optimizers(self) -> Tuple[List, List]:
|
| 178 |
+
lr = self.learning_rate
|
| 179 |
+
|
| 180 |
+
params_list = []
|
| 181 |
+
trainable_parameters = list(self.model.parameters())
|
| 182 |
+
params_list.append({'params': trainable_parameters, 'lr': lr})
|
| 183 |
+
|
| 184 |
+
no_decay = ['bias', 'norm.weight', 'norm.bias', 'norm1.weight', 'norm1.bias', 'norm2.weight', 'norm2.bias']
|
| 185 |
+
|
| 186 |
+
|
| 187 |
+
if self.optimizer_cfg.get('train_image_encoder', False):
|
| 188 |
+
image_encoder_parameters = list(self.cond_stage_model.named_parameters())
|
| 189 |
+
image_encoder_parameters_decay = [param for name, param in image_encoder_parameters if
|
| 190 |
+
not any((no_decay_name in name) for no_decay_name in no_decay)]
|
| 191 |
+
image_encoder_parameters_nodecay = [param for name, param in image_encoder_parameters if
|
| 192 |
+
any((no_decay_name in name) for no_decay_name in no_decay)]
|
| 193 |
+
# filter trainable params
|
| 194 |
+
image_encoder_parameters_decay = [param for param in image_encoder_parameters_decay if
|
| 195 |
+
param.requires_grad]
|
| 196 |
+
image_encoder_parameters_nodecay = [param for param in image_encoder_parameters_nodecay if
|
| 197 |
+
param.requires_grad]
|
| 198 |
+
|
| 199 |
+
print(f"Image Encoder Params: {len(image_encoder_parameters_decay)} decay, ")
|
| 200 |
+
print(f"Image Encoder Params: {len(image_encoder_parameters_nodecay)} nodecay, ")
|
| 201 |
+
|
| 202 |
+
image_encoder_lr = self.optimizer_cfg['image_encoder_lr']
|
| 203 |
+
image_encoder_lr_multiply = self.optimizer_cfg.get('image_encoder_lr_multiply', 1.0)
|
| 204 |
+
image_encoder_lr = image_encoder_lr if image_encoder_lr is not None else lr * image_encoder_lr_multiply
|
| 205 |
+
params_list.append(
|
| 206 |
+
{'params': image_encoder_parameters_decay, 'lr': image_encoder_lr,
|
| 207 |
+
'weight_decay': 0.05})
|
| 208 |
+
params_list.append(
|
| 209 |
+
{'params': image_encoder_parameters_nodecay, 'lr': image_encoder_lr,
|
| 210 |
+
'weight_decay': 0.})
|
| 211 |
+
|
| 212 |
+
optimizer = instantiate_from_config(self.optimizer_cfg.optimizer, params=params_list, lr=lr)
|
| 213 |
+
if hasattr(self.optimizer_cfg, 'scheduler'):
|
| 214 |
+
scheduler_func = instantiate_from_config(
|
| 215 |
+
self.optimizer_cfg.scheduler,
|
| 216 |
+
max_decay_steps=self.trainer.max_steps,
|
| 217 |
+
lr_max=lr
|
| 218 |
+
)
|
| 219 |
+
scheduler = {
|
| 220 |
+
"scheduler": lr_scheduler.LambdaLR(optimizer, lr_lambda=scheduler_func.schedule),
|
| 221 |
+
"interval": "step",
|
| 222 |
+
"frequency": 1
|
| 223 |
+
}
|
| 224 |
+
schedulers = [scheduler]
|
| 225 |
+
else:
|
| 226 |
+
schedulers = []
|
| 227 |
+
optimizers = [optimizer]
|
| 228 |
+
|
| 229 |
+
return optimizers, schedulers
|
| 230 |
+
|
| 231 |
+
@rank_zero_only
|
| 232 |
+
@torch.no_grad()
|
| 233 |
+
def on_train_batch_start(self, batch, batch_idx):
|
| 234 |
+
# only for very first batch
|
| 235 |
+
if self.scale_by_std and self.current_epoch == 0 and self.global_step == 0 \
|
| 236 |
+
and batch_idx == 0 and self.ckpt_path is None:
|
| 237 |
+
# set rescale weight to 1./std of encodings
|
| 238 |
+
print("### USING STD-RESCALING ###")
|
| 239 |
+
|
| 240 |
+
z_q = self.encode_first_stage(batch[self.first_stage_key])
|
| 241 |
+
z = z_q.detach()
|
| 242 |
+
|
| 243 |
+
del self.z_scale_factor
|
| 244 |
+
self.register_buffer("z_scale_factor", 1. / z.flatten().std())
|
| 245 |
+
print(f"setting self.z_scale_factor to {self.z_scale_factor}")
|
| 246 |
+
|
| 247 |
+
print("### USING STD-RESCALING ###")
|
| 248 |
+
|
| 249 |
+
def on_train_batch_end(self, *args, **kwargs):
|
| 250 |
+
if self.ema_config is not None:
|
| 251 |
+
self.model_ema(self.model)
|
| 252 |
+
|
| 253 |
+
def on_train_epoch_start(self) -> None:
|
| 254 |
+
pl.seed_everything(self.trainer.global_rank)
|
| 255 |
+
|
| 256 |
+
def forward(self, batch):
|
| 257 |
+
with torch.autocast(device_type="cuda", dtype=torch.bfloat16): #float32 for text
|
| 258 |
+
contexts = self.cond_stage_model(image=batch.get('image'), text=batch.get('text'), mask=batch.get('mask'))
|
| 259 |
+
# t5_text = contexts['t5_text']['prompt_embeds']
|
| 260 |
+
# nan_count = torch.isnan(t5_text).sum()
|
| 261 |
+
# if nan_count > 0:
|
| 262 |
+
# print("t5_text has %d NaN values"%(nan_count))
|
| 263 |
+
with torch.autocast(device_type="cuda", dtype=torch.float16):
|
| 264 |
+
with torch.no_grad():
|
| 265 |
+
latents = self.first_stage_model.encode(batch[self.first_stage_key], sample_posterior=True)
|
| 266 |
+
latents = self.z_scale_factor * latents
|
| 267 |
+
# print(latents.shape)
|
| 268 |
+
|
| 269 |
+
# check vae encode and decode is ok? answer is ok !
|
| 270 |
+
# import time
|
| 271 |
+
# from hy3dshape.pipelines import export_to_trimesh
|
| 272 |
+
# latents = 1. / self.z_scale_factor * latents
|
| 273 |
+
# latents = self.first_stage_model(latents)
|
| 274 |
+
# outputs = self.first_stage_model.latents2mesh(
|
| 275 |
+
# latents,
|
| 276 |
+
# bounds=1.01,
|
| 277 |
+
# mc_level=0.0,
|
| 278 |
+
# num_chunks=20000,
|
| 279 |
+
# octree_resolution=256,
|
| 280 |
+
# mc_algo='mc',
|
| 281 |
+
# enable_pbar=True
|
| 282 |
+
# )
|
| 283 |
+
# mesh = export_to_trimesh(outputs)
|
| 284 |
+
# if isinstance(mesh, list):
|
| 285 |
+
# for midx, m in enumerate(mesh):
|
| 286 |
+
# m.export(f"check_{midx}_{time.time()}.glb")
|
| 287 |
+
# else:
|
| 288 |
+
# mesh.export(f"check_{time.time()}.glb")
|
| 289 |
+
|
| 290 |
+
with torch.autocast(device_type="cuda", dtype=torch.bfloat16):
|
| 291 |
+
loss = self.transport.training_losses(self.model, latents, dict(contexts=contexts))["loss"].mean()
|
| 292 |
+
return loss
|
| 293 |
+
|
| 294 |
+
def training_step(self, batch, batch_idx, optimizer_idx=0):
|
| 295 |
+
loss = self.forward(batch)
|
| 296 |
+
split = 'train'
|
| 297 |
+
loss_dict = {
|
| 298 |
+
f"{split}/simple": loss.detach(),
|
| 299 |
+
f"{split}/total_loss": loss.detach(),
|
| 300 |
+
f"{split}/lr_abs": self.optimizers().param_groups[0]['lr'],
|
| 301 |
+
}
|
| 302 |
+
self.log_dict(loss_dict, prog_bar=True, logger=True, sync_dist=False, rank_zero_only=True)
|
| 303 |
+
|
| 304 |
+
return loss
|
| 305 |
+
|
| 306 |
+
def validation_step(self, batch, batch_idx, optimizer_idx=0):
|
| 307 |
+
loss = self.forward(batch)
|
| 308 |
+
split = 'val'
|
| 309 |
+
loss_dict = {
|
| 310 |
+
f"{split}/simple": loss.detach(),
|
| 311 |
+
f"{split}/total_loss": loss.detach(),
|
| 312 |
+
f"{split}/lr_abs": self.optimizers().param_groups[0]['lr'],
|
| 313 |
+
}
|
| 314 |
+
self.log_dict(loss_dict, prog_bar=True, logger=True, sync_dist=False, rank_zero_only=True)
|
| 315 |
+
|
| 316 |
+
return loss
|
| 317 |
+
|
| 318 |
+
@torch.no_grad()
|
| 319 |
+
def sample(self, batch, output_type='trimesh', **kwargs):
|
| 320 |
+
self.cond_stage_model.disable_drop = True
|
| 321 |
+
|
| 322 |
+
generator = torch.Generator().manual_seed(0)
|
| 323 |
+
|
| 324 |
+
with self.ema_scope("Sample"):
|
| 325 |
+
with torch.amp.autocast(device_type='cuda'):
|
| 326 |
+
try:
|
| 327 |
+
self.pipeline.device = self.device
|
| 328 |
+
self.pipeline.dtype = self.dtype
|
| 329 |
+
print("### USING PIPELINE ###")
|
| 330 |
+
print(f'device: {self.device} dtype : {self.dtype}')
|
| 331 |
+
additional_params = {'output_type':output_type}
|
| 332 |
+
|
| 333 |
+
image = batch.get("image", None)
|
| 334 |
+
mask = batch.get('mask', None)
|
| 335 |
+
|
| 336 |
+
# if not isinstance(image, torch.Tensor): print(image.shape)
|
| 337 |
+
# if isinstance(mask, torch.Tensor): print(mask.shape)
|
| 338 |
+
|
| 339 |
+
outputs = self.pipeline(image=image,
|
| 340 |
+
mask=mask,
|
| 341 |
+
generator=generator,
|
| 342 |
+
**additional_params)
|
| 343 |
+
|
| 344 |
+
except Exception as e:
|
| 345 |
+
import traceback
|
| 346 |
+
traceback.print_exc()
|
| 347 |
+
print(f"Unexpected {e=}, {type(e)=}")
|
| 348 |
+
with open("error.txt", "a") as f:
|
| 349 |
+
f.write(str(e))
|
| 350 |
+
f.write(traceback.format_exc())
|
| 351 |
+
f.write("\n")
|
| 352 |
+
outputs = [None]
|
| 353 |
+
self.cond_stage_model.disable_drop = False
|
| 354 |
+
return [outputs]
|
Hunyuan3D-2.1/hy3dshape/hy3dshape/models/diffusion/transport/__init__.py
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# This file includes code derived from the SiT project (https://github.com/willisma/SiT),
|
| 2 |
+
# which is licensed under the MIT License.
|
| 3 |
+
#
|
| 4 |
+
# MIT License
|
| 5 |
+
#
|
| 6 |
+
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
| 7 |
+
#
|
| 8 |
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 9 |
+
# of this software and associated documentation files (the "Software"), to deal
|
| 10 |
+
# in the Software without restriction, including without limitation the rights
|
| 11 |
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 12 |
+
# copies of the Software, and to permit persons to whom the Software is
|
| 13 |
+
# furnished to do so, subject to the following conditions:
|
| 14 |
+
#
|
| 15 |
+
# The above copyright notice and this permission notice shall be included in all
|
| 16 |
+
# copies or substantial portions of the Software.
|
| 17 |
+
#
|
| 18 |
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 19 |
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 20 |
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 21 |
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 22 |
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 23 |
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
| 24 |
+
# SOFTWARE.
|
| 25 |
+
|
| 26 |
+
from .transport import Transport, ModelType, WeightType, PathType, Sampler
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def create_transport(
|
| 30 |
+
path_type='Linear',
|
| 31 |
+
prediction="velocity",
|
| 32 |
+
loss_weight=None,
|
| 33 |
+
train_eps=None,
|
| 34 |
+
sample_eps=None,
|
| 35 |
+
train_sample_type="uniform",
|
| 36 |
+
mean = 0.0,
|
| 37 |
+
std = 1.0,
|
| 38 |
+
shift_scale = 1.0,
|
| 39 |
+
):
|
| 40 |
+
"""function for creating Transport object
|
| 41 |
+
**Note**: model prediction defaults to velocity
|
| 42 |
+
Args:
|
| 43 |
+
- path_type: type of path to use; default to linear
|
| 44 |
+
- learn_score: set model prediction to score
|
| 45 |
+
- learn_noise: set model prediction to noise
|
| 46 |
+
- velocity_weighted: weight loss by velocity weight
|
| 47 |
+
- likelihood_weighted: weight loss by likelihood weight
|
| 48 |
+
- train_eps: small epsilon for avoiding instability during training
|
| 49 |
+
- sample_eps: small epsilon for avoiding instability during sampling
|
| 50 |
+
"""
|
| 51 |
+
|
| 52 |
+
if prediction == "noise":
|
| 53 |
+
model_type = ModelType.NOISE
|
| 54 |
+
elif prediction == "score":
|
| 55 |
+
model_type = ModelType.SCORE
|
| 56 |
+
else:
|
| 57 |
+
model_type = ModelType.VELOCITY
|
| 58 |
+
|
| 59 |
+
if loss_weight == "velocity":
|
| 60 |
+
loss_type = WeightType.VELOCITY
|
| 61 |
+
elif loss_weight == "likelihood":
|
| 62 |
+
loss_type = WeightType.LIKELIHOOD
|
| 63 |
+
else:
|
| 64 |
+
loss_type = WeightType.NONE
|
| 65 |
+
|
| 66 |
+
path_choice = {
|
| 67 |
+
"Linear": PathType.LINEAR,
|
| 68 |
+
"GVP": PathType.GVP,
|
| 69 |
+
"VP": PathType.VP,
|
| 70 |
+
}
|
| 71 |
+
|
| 72 |
+
path_type = path_choice[path_type]
|
| 73 |
+
|
| 74 |
+
if (path_type in [PathType.VP]):
|
| 75 |
+
train_eps = 1e-5 if train_eps is None else train_eps
|
| 76 |
+
sample_eps = 1e-3 if train_eps is None else sample_eps
|
| 77 |
+
elif (path_type in [PathType.GVP, PathType.LINEAR] and model_type != ModelType.VELOCITY):
|
| 78 |
+
train_eps = 1e-3 if train_eps is None else train_eps
|
| 79 |
+
sample_eps = 1e-3 if train_eps is None else sample_eps
|
| 80 |
+
else: # velocity & [GVP, LINEAR] is stable everywhere
|
| 81 |
+
train_eps = 0
|
| 82 |
+
sample_eps = 0
|
| 83 |
+
|
| 84 |
+
# create flow state
|
| 85 |
+
state = Transport(
|
| 86 |
+
model_type=model_type,
|
| 87 |
+
path_type=path_type,
|
| 88 |
+
loss_type=loss_type,
|
| 89 |
+
train_eps=train_eps,
|
| 90 |
+
sample_eps=sample_eps,
|
| 91 |
+
train_sample_type=train_sample_type,
|
| 92 |
+
mean=mean,
|
| 93 |
+
std=std,
|
| 94 |
+
shift_scale =shift_scale,
|
| 95 |
+
)
|
| 96 |
+
|
| 97 |
+
return state
|
Hunyuan3D-2.1/hy3dshape/hy3dshape/models/diffusion/transport/integrators.py
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# This file includes code derived from the SiT project (https://github.com/willisma/SiT),
|
| 2 |
+
# which is licensed under the MIT License.
|
| 3 |
+
#
|
| 4 |
+
# MIT License
|
| 5 |
+
#
|
| 6 |
+
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
| 7 |
+
#
|
| 8 |
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 9 |
+
# of this software and associated documentation files (the "Software"), to deal
|
| 10 |
+
# in the Software without restriction, including without limitation the rights
|
| 11 |
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 12 |
+
# copies of the Software, and to permit persons to whom the Software is
|
| 13 |
+
# furnished to do so, subject to the following conditions:
|
| 14 |
+
#
|
| 15 |
+
# The above copyright notice and this permission notice shall be included in all
|
| 16 |
+
# copies or substantial portions of the Software.
|
| 17 |
+
#
|
| 18 |
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 19 |
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 20 |
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 21 |
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 22 |
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 23 |
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
| 24 |
+
# SOFTWARE.
|
| 25 |
+
|
| 26 |
+
import numpy as np
|
| 27 |
+
import torch as th
|
| 28 |
+
import torch.nn as nn
|
| 29 |
+
from torchdiffeq import odeint
|
| 30 |
+
from functools import partial
|
| 31 |
+
from tqdm import tqdm
|
| 32 |
+
|
| 33 |
+
class sde:
|
| 34 |
+
"""SDE solver class"""
|
| 35 |
+
def __init__(
|
| 36 |
+
self,
|
| 37 |
+
drift,
|
| 38 |
+
diffusion,
|
| 39 |
+
*,
|
| 40 |
+
t0,
|
| 41 |
+
t1,
|
| 42 |
+
num_steps,
|
| 43 |
+
sampler_type,
|
| 44 |
+
):
|
| 45 |
+
assert t0 < t1, "SDE sampler has to be in forward time"
|
| 46 |
+
|
| 47 |
+
self.num_timesteps = num_steps
|
| 48 |
+
self.t = th.linspace(t0, t1, num_steps)
|
| 49 |
+
self.dt = self.t[1] - self.t[0]
|
| 50 |
+
self.drift = drift
|
| 51 |
+
self.diffusion = diffusion
|
| 52 |
+
self.sampler_type = sampler_type
|
| 53 |
+
|
| 54 |
+
def __Euler_Maruyama_step(self, x, mean_x, t, model, **model_kwargs):
|
| 55 |
+
w_cur = th.randn(x.size()).to(x)
|
| 56 |
+
t = th.ones(x.size(0)).to(x) * t
|
| 57 |
+
dw = w_cur * th.sqrt(self.dt)
|
| 58 |
+
drift = self.drift(x, t, model, **model_kwargs)
|
| 59 |
+
diffusion = self.diffusion(x, t)
|
| 60 |
+
mean_x = x + drift * self.dt
|
| 61 |
+
x = mean_x + th.sqrt(2 * diffusion) * dw
|
| 62 |
+
return x, mean_x
|
| 63 |
+
|
| 64 |
+
def __Heun_step(self, x, _, t, model, **model_kwargs):
|
| 65 |
+
w_cur = th.randn(x.size()).to(x)
|
| 66 |
+
dw = w_cur * th.sqrt(self.dt)
|
| 67 |
+
t_cur = th.ones(x.size(0)).to(x) * t
|
| 68 |
+
diffusion = self.diffusion(x, t_cur)
|
| 69 |
+
xhat = x + th.sqrt(2 * diffusion) * dw
|
| 70 |
+
K1 = self.drift(xhat, t_cur, model, **model_kwargs)
|
| 71 |
+
xp = xhat + self.dt * K1
|
| 72 |
+
K2 = self.drift(xp, t_cur + self.dt, model, **model_kwargs)
|
| 73 |
+
return xhat + 0.5 * self.dt * (K1 + K2), xhat # at last time point we do not perform the heun step
|
| 74 |
+
|
| 75 |
+
def __forward_fn(self):
|
| 76 |
+
"""TODO: generalize here by adding all private functions ending with steps to it"""
|
| 77 |
+
sampler_dict = {
|
| 78 |
+
"Euler": self.__Euler_Maruyama_step,
|
| 79 |
+
"Heun": self.__Heun_step,
|
| 80 |
+
}
|
| 81 |
+
|
| 82 |
+
try:
|
| 83 |
+
sampler = sampler_dict[self.sampler_type]
|
| 84 |
+
except:
|
| 85 |
+
raise NotImplementedError("Smapler type not implemented.")
|
| 86 |
+
|
| 87 |
+
return sampler
|
| 88 |
+
|
| 89 |
+
def sample(self, init, model, **model_kwargs):
|
| 90 |
+
"""forward loop of sde"""
|
| 91 |
+
x = init
|
| 92 |
+
mean_x = init
|
| 93 |
+
samples = []
|
| 94 |
+
sampler = self.__forward_fn()
|
| 95 |
+
for ti in self.t[:-1]:
|
| 96 |
+
with th.no_grad():
|
| 97 |
+
x, mean_x = sampler(x, mean_x, ti, model, **model_kwargs)
|
| 98 |
+
samples.append(x)
|
| 99 |
+
|
| 100 |
+
return samples
|
| 101 |
+
|
| 102 |
+
class ode:
|
| 103 |
+
"""ODE solver class"""
|
| 104 |
+
def __init__(
|
| 105 |
+
self,
|
| 106 |
+
drift,
|
| 107 |
+
*,
|
| 108 |
+
t0,
|
| 109 |
+
t1,
|
| 110 |
+
sampler_type,
|
| 111 |
+
num_steps,
|
| 112 |
+
atol,
|
| 113 |
+
rtol,
|
| 114 |
+
):
|
| 115 |
+
assert t0 < t1, "ODE sampler has to be in forward time"
|
| 116 |
+
|
| 117 |
+
self.drift = drift
|
| 118 |
+
self.t = th.linspace(t0, t1, num_steps)
|
| 119 |
+
self.atol = atol
|
| 120 |
+
self.rtol = rtol
|
| 121 |
+
self.sampler_type = sampler_type
|
| 122 |
+
|
| 123 |
+
def sample(self, x, model, **model_kwargs):
|
| 124 |
+
|
| 125 |
+
device = x[0].device if isinstance(x, tuple) else x.device
|
| 126 |
+
def _fn(t, x):
|
| 127 |
+
t = th.ones(x[0].size(0)).to(device) * t if isinstance(x, tuple) else th.ones(x.size(0)).to(device) * t
|
| 128 |
+
model_output = self.drift(x, t, model, **model_kwargs)
|
| 129 |
+
return model_output
|
| 130 |
+
|
| 131 |
+
t = self.t.to(device)
|
| 132 |
+
atol = [self.atol] * len(x) if isinstance(x, tuple) else [self.atol]
|
| 133 |
+
rtol = [self.rtol] * len(x) if isinstance(x, tuple) else [self.rtol]
|
| 134 |
+
samples = odeint(
|
| 135 |
+
_fn,
|
| 136 |
+
x,
|
| 137 |
+
t,
|
| 138 |
+
method=self.sampler_type,
|
| 139 |
+
atol=atol,
|
| 140 |
+
rtol=rtol
|
| 141 |
+
)
|
| 142 |
+
return samples
|
Hunyuan3D-2.1/hy3dshape/hy3dshape/models/diffusion/transport/path.py
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# This file includes code derived from the SiT project (https://github.com/willisma/SiT),
|
| 2 |
+
# which is licensed under the MIT License.
|
| 3 |
+
#
|
| 4 |
+
# MIT License
|
| 5 |
+
#
|
| 6 |
+
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
| 7 |
+
#
|
| 8 |
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 9 |
+
# of this software and associated documentation files (the "Software"), to deal
|
| 10 |
+
# in the Software without restriction, including without limitation the rights
|
| 11 |
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 12 |
+
# copies of the Software, and to permit persons to whom the Software is
|
| 13 |
+
# furnished to do so, subject to the following conditions:
|
| 14 |
+
#
|
| 15 |
+
# The above copyright notice and this permission notice shall be included in all
|
| 16 |
+
# copies or substantial portions of the Software.
|
| 17 |
+
#
|
| 18 |
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 19 |
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 20 |
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 21 |
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 22 |
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 23 |
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
| 24 |
+
# SOFTWARE.
|
| 25 |
+
|
| 26 |
+
import torch as th
|
| 27 |
+
import numpy as np
|
| 28 |
+
from functools import partial
|
| 29 |
+
|
| 30 |
+
def expand_t_like_x(t, x):
|
| 31 |
+
"""Function to reshape time t to broadcastable dimension of x
|
| 32 |
+
Args:
|
| 33 |
+
t: [batch_dim,], time vector
|
| 34 |
+
x: [batch_dim,...], data point
|
| 35 |
+
"""
|
| 36 |
+
dims = [1] * (len(x.size()) - 1)
|
| 37 |
+
t = t.view(t.size(0), *dims)
|
| 38 |
+
return t
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
#################### Coupling Plans ####################
|
| 42 |
+
|
| 43 |
+
class ICPlan:
|
| 44 |
+
"""Linear Coupling Plan"""
|
| 45 |
+
def __init__(self, sigma=0.0):
|
| 46 |
+
self.sigma = sigma
|
| 47 |
+
|
| 48 |
+
def compute_alpha_t(self, t):
|
| 49 |
+
"""Compute the data coefficient along the path"""
|
| 50 |
+
return t, 1
|
| 51 |
+
|
| 52 |
+
def compute_sigma_t(self, t):
|
| 53 |
+
"""Compute the noise coefficient along the path"""
|
| 54 |
+
return 1 - t, -1
|
| 55 |
+
|
| 56 |
+
def compute_d_alpha_alpha_ratio_t(self, t):
|
| 57 |
+
"""Compute the ratio between d_alpha and alpha"""
|
| 58 |
+
return 1 / t
|
| 59 |
+
|
| 60 |
+
def compute_drift(self, x, t):
|
| 61 |
+
"""We always output sde according to score parametrization; """
|
| 62 |
+
t = expand_t_like_x(t, x)
|
| 63 |
+
alpha_ratio = self.compute_d_alpha_alpha_ratio_t(t)
|
| 64 |
+
sigma_t, d_sigma_t = self.compute_sigma_t(t)
|
| 65 |
+
drift = alpha_ratio * x
|
| 66 |
+
diffusion = alpha_ratio * (sigma_t ** 2) - sigma_t * d_sigma_t
|
| 67 |
+
|
| 68 |
+
return -drift, diffusion
|
| 69 |
+
|
| 70 |
+
def compute_diffusion(self, x, t, form="constant", norm=1.0):
|
| 71 |
+
"""Compute the diffusion term of the SDE
|
| 72 |
+
Args:
|
| 73 |
+
x: [batch_dim, ...], data point
|
| 74 |
+
t: [batch_dim,], time vector
|
| 75 |
+
form: str, form of the diffusion term
|
| 76 |
+
norm: float, norm of the diffusion term
|
| 77 |
+
"""
|
| 78 |
+
t = expand_t_like_x(t, x)
|
| 79 |
+
choices = {
|
| 80 |
+
"constant": norm,
|
| 81 |
+
"SBDM": norm * self.compute_drift(x, t)[1],
|
| 82 |
+
"sigma": norm * self.compute_sigma_t(t)[0],
|
| 83 |
+
"linear": norm * (1 - t),
|
| 84 |
+
"decreasing": 0.25 * (norm * th.cos(np.pi * t) + 1) ** 2,
|
| 85 |
+
"inccreasing-decreasing": norm * th.sin(np.pi * t) ** 2,
|
| 86 |
+
}
|
| 87 |
+
|
| 88 |
+
try:
|
| 89 |
+
diffusion = choices[form]
|
| 90 |
+
except KeyError:
|
| 91 |
+
raise NotImplementedError(f"Diffusion form {form} not implemented")
|
| 92 |
+
|
| 93 |
+
return diffusion
|
| 94 |
+
|
| 95 |
+
def get_score_from_velocity(self, velocity, x, t):
|
| 96 |
+
"""Wrapper function: transfrom velocity prediction model to score
|
| 97 |
+
Args:
|
| 98 |
+
velocity: [batch_dim, ...] shaped tensor; velocity model output
|
| 99 |
+
x: [batch_dim, ...] shaped tensor; x_t data point
|
| 100 |
+
t: [batch_dim,] time tensor
|
| 101 |
+
"""
|
| 102 |
+
t = expand_t_like_x(t, x)
|
| 103 |
+
alpha_t, d_alpha_t = self.compute_alpha_t(t)
|
| 104 |
+
sigma_t, d_sigma_t = self.compute_sigma_t(t)
|
| 105 |
+
mean = x
|
| 106 |
+
reverse_alpha_ratio = alpha_t / d_alpha_t
|
| 107 |
+
var = sigma_t**2 - reverse_alpha_ratio * d_sigma_t * sigma_t
|
| 108 |
+
score = (reverse_alpha_ratio * velocity - mean) / var
|
| 109 |
+
return score
|
| 110 |
+
|
| 111 |
+
def get_noise_from_velocity(self, velocity, x, t):
|
| 112 |
+
"""Wrapper function: transfrom velocity prediction model to denoiser
|
| 113 |
+
Args:
|
| 114 |
+
velocity: [batch_dim, ...] shaped tensor; velocity model output
|
| 115 |
+
x: [batch_dim, ...] shaped tensor; x_t data point
|
| 116 |
+
t: [batch_dim,] time tensor
|
| 117 |
+
"""
|
| 118 |
+
t = expand_t_like_x(t, x)
|
| 119 |
+
alpha_t, d_alpha_t = self.compute_alpha_t(t)
|
| 120 |
+
sigma_t, d_sigma_t = self.compute_sigma_t(t)
|
| 121 |
+
mean = x
|
| 122 |
+
reverse_alpha_ratio = alpha_t / d_alpha_t
|
| 123 |
+
var = reverse_alpha_ratio * d_sigma_t - sigma_t
|
| 124 |
+
noise = (reverse_alpha_ratio * velocity - mean) / var
|
| 125 |
+
return noise
|
| 126 |
+
|
| 127 |
+
def get_velocity_from_score(self, score, x, t):
|
| 128 |
+
"""Wrapper function: transfrom score prediction model to velocity
|
| 129 |
+
Args:
|
| 130 |
+
score: [batch_dim, ...] shaped tensor; score model output
|
| 131 |
+
x: [batch_dim, ...] shaped tensor; x_t data point
|
| 132 |
+
t: [batch_dim,] time tensor
|
| 133 |
+
"""
|
| 134 |
+
t = expand_t_like_x(t, x)
|
| 135 |
+
drift, var = self.compute_drift(x, t)
|
| 136 |
+
velocity = var * score - drift
|
| 137 |
+
return velocity
|
| 138 |
+
|
| 139 |
+
def compute_mu_t(self, t, x0, x1):
|
| 140 |
+
"""Compute the mean of time-dependent density p_t"""
|
| 141 |
+
t = expand_t_like_x(t, x1)
|
| 142 |
+
alpha_t, _ = self.compute_alpha_t(t)
|
| 143 |
+
sigma_t, _ = self.compute_sigma_t(t)
|
| 144 |
+
# t*x1 + (1-t)*x0 ; t=0 x0; t=1 x1
|
| 145 |
+
return alpha_t * x1 + sigma_t * x0
|
| 146 |
+
|
| 147 |
+
def compute_xt(self, t, x0, x1):
|
| 148 |
+
"""Sample xt from time-dependent density p_t; rng is required"""
|
| 149 |
+
xt = self.compute_mu_t(t, x0, x1)
|
| 150 |
+
return xt
|
| 151 |
+
|
| 152 |
+
def compute_ut(self, t, x0, x1, xt):
|
| 153 |
+
"""Compute the vector field corresponding to p_t"""
|
| 154 |
+
t = expand_t_like_x(t, x1)
|
| 155 |
+
_, d_alpha_t = self.compute_alpha_t(t)
|
| 156 |
+
_, d_sigma_t = self.compute_sigma_t(t)
|
| 157 |
+
return d_alpha_t * x1 + d_sigma_t * x0
|
| 158 |
+
|
| 159 |
+
def plan(self, t, x0, x1):
|
| 160 |
+
xt = self.compute_xt(t, x0, x1)
|
| 161 |
+
ut = self.compute_ut(t, x0, x1, xt)
|
| 162 |
+
return t, xt, ut
|
| 163 |
+
|
| 164 |
+
|
| 165 |
+
class VPCPlan(ICPlan):
|
| 166 |
+
"""class for VP path flow matching"""
|
| 167 |
+
|
| 168 |
+
def __init__(self, sigma_min=0.1, sigma_max=20.0):
|
| 169 |
+
self.sigma_min = sigma_min
|
| 170 |
+
self.sigma_max = sigma_max
|
| 171 |
+
self.log_mean_coeff = lambda t: -0.25 * ((1 - t) ** 2) * \
|
| 172 |
+
(self.sigma_max - self.sigma_min) - 0.5 * (1 - t) * self.sigma_min
|
| 173 |
+
self.d_log_mean_coeff = lambda t: 0.5 * (1 - t) * \
|
| 174 |
+
(self.sigma_max - self.sigma_min) + 0.5 * self.sigma_min
|
| 175 |
+
|
| 176 |
+
|
| 177 |
+
def compute_alpha_t(self, t):
|
| 178 |
+
"""Compute coefficient of x1"""
|
| 179 |
+
alpha_t = self.log_mean_coeff(t)
|
| 180 |
+
alpha_t = th.exp(alpha_t)
|
| 181 |
+
d_alpha_t = alpha_t * self.d_log_mean_coeff(t)
|
| 182 |
+
return alpha_t, d_alpha_t
|
| 183 |
+
|
| 184 |
+
def compute_sigma_t(self, t):
|
| 185 |
+
"""Compute coefficient of x0"""
|
| 186 |
+
p_sigma_t = 2 * self.log_mean_coeff(t)
|
| 187 |
+
sigma_t = th.sqrt(1 - th.exp(p_sigma_t))
|
| 188 |
+
d_sigma_t = th.exp(p_sigma_t) * (2 * self.d_log_mean_coeff(t)) / (-2 * sigma_t)
|
| 189 |
+
return sigma_t, d_sigma_t
|
| 190 |
+
|
| 191 |
+
def compute_d_alpha_alpha_ratio_t(self, t):
|
| 192 |
+
"""Special purposed function for computing numerical stabled d_alpha_t / alpha_t"""
|
| 193 |
+
return self.d_log_mean_coeff(t)
|
| 194 |
+
|
| 195 |
+
def compute_drift(self, x, t):
|
| 196 |
+
"""Compute the drift term of the SDE"""
|
| 197 |
+
t = expand_t_like_x(t, x)
|
| 198 |
+
beta_t = self.sigma_min + (1 - t) * (self.sigma_max - self.sigma_min)
|
| 199 |
+
return -0.5 * beta_t * x, beta_t / 2
|
| 200 |
+
|
| 201 |
+
|
| 202 |
+
class GVPCPlan(ICPlan):
|
| 203 |
+
def __init__(self, sigma=0.0):
|
| 204 |
+
super().__init__(sigma)
|
| 205 |
+
|
| 206 |
+
def compute_alpha_t(self, t):
|
| 207 |
+
"""Compute coefficient of x1"""
|
| 208 |
+
alpha_t = th.sin(t * np.pi / 2)
|
| 209 |
+
d_alpha_t = np.pi / 2 * th.cos(t * np.pi / 2)
|
| 210 |
+
return alpha_t, d_alpha_t
|
| 211 |
+
|
| 212 |
+
def compute_sigma_t(self, t):
|
| 213 |
+
"""Compute coefficient of x0"""
|
| 214 |
+
sigma_t = th.cos(t * np.pi / 2)
|
| 215 |
+
d_sigma_t = -np.pi / 2 * th.sin(t * np.pi / 2)
|
| 216 |
+
return sigma_t, d_sigma_t
|
| 217 |
+
|
| 218 |
+
def compute_d_alpha_alpha_ratio_t(self, t):
|
| 219 |
+
"""Special purposed function for computing numerical stabled d_alpha_t / alpha_t"""
|
| 220 |
+
return np.pi / (2 * th.tan(t * np.pi / 2))
|
Hunyuan3D-2.1/hy3dshape/hy3dshape/models/diffusion/transport/transport.py
ADDED
|
@@ -0,0 +1,534 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# This file includes code derived from the SiT project (https://github.com/willisma/SiT),
|
| 2 |
+
# which is licensed under the MIT License.
|
| 3 |
+
#
|
| 4 |
+
# MIT License
|
| 5 |
+
#
|
| 6 |
+
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
| 7 |
+
#
|
| 8 |
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 9 |
+
# of this software and associated documentation files (the "Software"), to deal
|
| 10 |
+
# in the Software without restriction, including without limitation the rights
|
| 11 |
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 12 |
+
# copies of the Software, and to permit persons to whom the Software is
|
| 13 |
+
# furnished to do so, subject to the following conditions:
|
| 14 |
+
#
|
| 15 |
+
# The above copyright notice and this permission notice shall be included in all
|
| 16 |
+
# copies or substantial portions of the Software.
|
| 17 |
+
#
|
| 18 |
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 19 |
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 20 |
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 21 |
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 22 |
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 23 |
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
| 24 |
+
# SOFTWARE.
|
| 25 |
+
|
| 26 |
+
import torch as th
|
| 27 |
+
import numpy as np
|
| 28 |
+
import logging
|
| 29 |
+
|
| 30 |
+
import enum
|
| 31 |
+
|
| 32 |
+
from . import path
|
| 33 |
+
from .utils import EasyDict, log_state, mean_flat
|
| 34 |
+
from .integrators import ode, sde
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
class ModelType(enum.Enum):
|
| 38 |
+
"""
|
| 39 |
+
Which type of output the model predicts.
|
| 40 |
+
"""
|
| 41 |
+
|
| 42 |
+
NOISE = enum.auto() # the model predicts epsilon
|
| 43 |
+
SCORE = enum.auto() # the model predicts \nabla \log p(x)
|
| 44 |
+
VELOCITY = enum.auto() # the model predicts v(x)
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
class PathType(enum.Enum):
|
| 48 |
+
"""
|
| 49 |
+
Which type of path to use.
|
| 50 |
+
"""
|
| 51 |
+
|
| 52 |
+
LINEAR = enum.auto()
|
| 53 |
+
GVP = enum.auto()
|
| 54 |
+
VP = enum.auto()
|
| 55 |
+
|
| 56 |
+
|
| 57 |
+
class WeightType(enum.Enum):
|
| 58 |
+
"""
|
| 59 |
+
Which type of weighting to use.
|
| 60 |
+
"""
|
| 61 |
+
|
| 62 |
+
NONE = enum.auto()
|
| 63 |
+
VELOCITY = enum.auto()
|
| 64 |
+
LIKELIHOOD = enum.auto()
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
class Transport:
|
| 68 |
+
|
| 69 |
+
def __init__(
|
| 70 |
+
self,
|
| 71 |
+
*,
|
| 72 |
+
model_type,
|
| 73 |
+
path_type,
|
| 74 |
+
loss_type,
|
| 75 |
+
train_eps,
|
| 76 |
+
sample_eps,
|
| 77 |
+
train_sample_type = "uniform",
|
| 78 |
+
**kwargs,
|
| 79 |
+
):
|
| 80 |
+
path_options = {
|
| 81 |
+
PathType.LINEAR: path.ICPlan,
|
| 82 |
+
PathType.GVP: path.GVPCPlan,
|
| 83 |
+
PathType.VP: path.VPCPlan,
|
| 84 |
+
}
|
| 85 |
+
|
| 86 |
+
self.loss_type = loss_type
|
| 87 |
+
self.model_type = model_type
|
| 88 |
+
self.path_sampler = path_options[path_type]()
|
| 89 |
+
self.train_eps = train_eps
|
| 90 |
+
self.sample_eps = sample_eps
|
| 91 |
+
self.train_sample_type = train_sample_type
|
| 92 |
+
if self.train_sample_type == "logit_normal":
|
| 93 |
+
self.mean = kwargs['mean']
|
| 94 |
+
self.std = kwargs['std']
|
| 95 |
+
self.shift_scale = kwargs['shift_scale']
|
| 96 |
+
print(f"using logit normal sample, shift scale is {self.shift_scale}")
|
| 97 |
+
|
| 98 |
+
def prior_logp(self, z):
|
| 99 |
+
'''
|
| 100 |
+
Standard multivariate normal prior
|
| 101 |
+
Assume z is batched
|
| 102 |
+
'''
|
| 103 |
+
shape = th.tensor(z.size())
|
| 104 |
+
N = th.prod(shape[1:])
|
| 105 |
+
_fn = lambda x: -N / 2. * np.log(2 * np.pi) - th.sum(x ** 2) / 2.
|
| 106 |
+
return th.vmap(_fn)(z)
|
| 107 |
+
|
| 108 |
+
def check_interval(
|
| 109 |
+
self,
|
| 110 |
+
train_eps,
|
| 111 |
+
sample_eps,
|
| 112 |
+
*,
|
| 113 |
+
diffusion_form="SBDM",
|
| 114 |
+
sde=False,
|
| 115 |
+
reverse=False,
|
| 116 |
+
eval=False,
|
| 117 |
+
last_step_size=0.0,
|
| 118 |
+
):
|
| 119 |
+
t0 = 0
|
| 120 |
+
t1 = 1
|
| 121 |
+
eps = train_eps if not eval else sample_eps
|
| 122 |
+
if (type(self.path_sampler) in [path.VPCPlan]):
|
| 123 |
+
|
| 124 |
+
t1 = 1 - eps if (not sde or last_step_size == 0) else 1 - last_step_size
|
| 125 |
+
|
| 126 |
+
elif (type(self.path_sampler) in [path.ICPlan, path.GVPCPlan]) \
|
| 127 |
+
and (
|
| 128 |
+
self.model_type != ModelType.VELOCITY or sde): # avoid numerical issue by taking a first semi-implicit step
|
| 129 |
+
|
| 130 |
+
t0 = eps if (diffusion_form == "SBDM" and sde) or self.model_type != ModelType.VELOCITY else 0
|
| 131 |
+
t1 = 1 - eps if (not sde or last_step_size == 0) else 1 - last_step_size
|
| 132 |
+
|
| 133 |
+
if reverse:
|
| 134 |
+
t0, t1 = 1 - t0, 1 - t1
|
| 135 |
+
|
| 136 |
+
return t0, t1
|
| 137 |
+
|
| 138 |
+
def sample(self, x1):
|
| 139 |
+
"""Sampling x0 & t based on shape of x1 (if needed)
|
| 140 |
+
Args:
|
| 141 |
+
x1 - data point; [batch, *dim]
|
| 142 |
+
"""
|
| 143 |
+
|
| 144 |
+
x0 = th.randn_like(x1)
|
| 145 |
+
if self.train_sample_type=="uniform":
|
| 146 |
+
t0, t1 = self.check_interval(self.train_eps, self.sample_eps)
|
| 147 |
+
t = th.rand((x1.shape[0],)) * (t1 - t0) + t0
|
| 148 |
+
t = t.to(x1)
|
| 149 |
+
elif self.train_sample_type=="logit_normal":
|
| 150 |
+
t = th.randn((x1.shape[0],)) * self.std + self.mean
|
| 151 |
+
t = t.to(x1)
|
| 152 |
+
t = 1/(1+th.exp(-t))
|
| 153 |
+
|
| 154 |
+
t = np.sqrt(self.shift_scale)*t/(1+(np.sqrt(self.shift_scale)-1)*t)
|
| 155 |
+
|
| 156 |
+
return t, x0, x1
|
| 157 |
+
|
| 158 |
+
def training_losses(
|
| 159 |
+
self,
|
| 160 |
+
model,
|
| 161 |
+
x1,
|
| 162 |
+
model_kwargs=None
|
| 163 |
+
):
|
| 164 |
+
"""Loss for training the score model
|
| 165 |
+
Args:
|
| 166 |
+
- model: backbone model; could be score, noise, or velocity
|
| 167 |
+
- x1: datapoint
|
| 168 |
+
- model_kwargs: additional arguments for the model
|
| 169 |
+
"""
|
| 170 |
+
if model_kwargs == None:
|
| 171 |
+
model_kwargs = {}
|
| 172 |
+
|
| 173 |
+
t, x0, x1 = self.sample(x1)
|
| 174 |
+
t, xt, ut = self.path_sampler.plan(t, x0, x1)
|
| 175 |
+
model_output = model(xt, t, **model_kwargs)
|
| 176 |
+
B, *_, C = xt.shape
|
| 177 |
+
assert model_output.size() == (B, *xt.size()[1:-1], C)
|
| 178 |
+
|
| 179 |
+
terms = {}
|
| 180 |
+
terms['pred'] = model_output
|
| 181 |
+
if self.model_type == ModelType.VELOCITY:
|
| 182 |
+
terms['loss'] = mean_flat(((model_output - ut) ** 2))
|
| 183 |
+
else:
|
| 184 |
+
_, drift_var = self.path_sampler.compute_drift(xt, t)
|
| 185 |
+
sigma_t, _ = self.path_sampler.compute_sigma_t(path.expand_t_like_x(t, xt))
|
| 186 |
+
if self.loss_type in [WeightType.VELOCITY]:
|
| 187 |
+
weight = (drift_var / sigma_t) ** 2
|
| 188 |
+
elif self.loss_type in [WeightType.LIKELIHOOD]:
|
| 189 |
+
weight = drift_var / (sigma_t ** 2)
|
| 190 |
+
elif self.loss_type in [WeightType.NONE]:
|
| 191 |
+
weight = 1
|
| 192 |
+
else:
|
| 193 |
+
raise NotImplementedError()
|
| 194 |
+
|
| 195 |
+
if self.model_type == ModelType.NOISE:
|
| 196 |
+
terms['loss'] = mean_flat(weight * ((model_output - x0) ** 2))
|
| 197 |
+
else:
|
| 198 |
+
terms['loss'] = mean_flat(weight * ((model_output * sigma_t + x0) ** 2))
|
| 199 |
+
|
| 200 |
+
return terms
|
| 201 |
+
|
| 202 |
+
def get_drift(
|
| 203 |
+
self
|
| 204 |
+
):
|
| 205 |
+
"""member function for obtaining the drift of the probability flow ODE"""
|
| 206 |
+
|
| 207 |
+
def score_ode(x, t, model, **model_kwargs):
|
| 208 |
+
drift_mean, drift_var = self.path_sampler.compute_drift(x, t)
|
| 209 |
+
model_output = model(x, t, **model_kwargs)
|
| 210 |
+
return (-drift_mean + drift_var * model_output) # by change of variable
|
| 211 |
+
|
| 212 |
+
def noise_ode(x, t, model, **model_kwargs):
|
| 213 |
+
drift_mean, drift_var = self.path_sampler.compute_drift(x, t)
|
| 214 |
+
sigma_t, _ = self.path_sampler.compute_sigma_t(path.expand_t_like_x(t, x))
|
| 215 |
+
model_output = model(x, t, **model_kwargs)
|
| 216 |
+
score = model_output / -sigma_t
|
| 217 |
+
return (-drift_mean + drift_var * score)
|
| 218 |
+
|
| 219 |
+
def velocity_ode(x, t, model, **model_kwargs):
|
| 220 |
+
model_output = model(x, t, **model_kwargs)
|
| 221 |
+
return model_output
|
| 222 |
+
|
| 223 |
+
if self.model_type == ModelType.NOISE:
|
| 224 |
+
drift_fn = noise_ode
|
| 225 |
+
elif self.model_type == ModelType.SCORE:
|
| 226 |
+
drift_fn = score_ode
|
| 227 |
+
else:
|
| 228 |
+
drift_fn = velocity_ode
|
| 229 |
+
|
| 230 |
+
def body_fn(x, t, model, **model_kwargs):
|
| 231 |
+
model_output = drift_fn(x, t, model, **model_kwargs)
|
| 232 |
+
assert model_output.shape == x.shape, "Output shape from ODE solver must match input shape"
|
| 233 |
+
return model_output
|
| 234 |
+
|
| 235 |
+
return body_fn
|
| 236 |
+
|
| 237 |
+
def get_score(
|
| 238 |
+
self,
|
| 239 |
+
):
|
| 240 |
+
"""member function for obtaining score of
|
| 241 |
+
x_t = alpha_t * x + sigma_t * eps"""
|
| 242 |
+
if self.model_type == ModelType.NOISE:
|
| 243 |
+
score_fn = lambda x, t, model, **kwargs: model(x, t, **kwargs) / - \
|
| 244 |
+
self.path_sampler.compute_sigma_t(path.expand_t_like_x(t, x))[0]
|
| 245 |
+
elif self.model_type == ModelType.SCORE:
|
| 246 |
+
score_fn = lambda x, t, model, **kwagrs: model(x, t, **kwagrs)
|
| 247 |
+
elif self.model_type == ModelType.VELOCITY:
|
| 248 |
+
score_fn = lambda x, t, model, **kwargs: self.path_sampler.get_score_from_velocity(model(x, t, **kwargs), x,
|
| 249 |
+
t)
|
| 250 |
+
else:
|
| 251 |
+
raise NotImplementedError()
|
| 252 |
+
|
| 253 |
+
return score_fn
|
| 254 |
+
|
| 255 |
+
|
| 256 |
+
class Sampler:
|
| 257 |
+
"""Sampler class for the transport model"""
|
| 258 |
+
|
| 259 |
+
def __init__(
|
| 260 |
+
self,
|
| 261 |
+
transport,
|
| 262 |
+
):
|
| 263 |
+
"""Constructor for a general sampler; supporting different sampling methods
|
| 264 |
+
Args:
|
| 265 |
+
- transport: an tranport object specify model prediction & interpolant type
|
| 266 |
+
"""
|
| 267 |
+
|
| 268 |
+
self.transport = transport
|
| 269 |
+
self.drift = self.transport.get_drift()
|
| 270 |
+
self.score = self.transport.get_score()
|
| 271 |
+
|
| 272 |
+
def __get_sde_diffusion_and_drift(
|
| 273 |
+
self,
|
| 274 |
+
*,
|
| 275 |
+
diffusion_form="SBDM",
|
| 276 |
+
diffusion_norm=1.0,
|
| 277 |
+
):
|
| 278 |
+
|
| 279 |
+
def diffusion_fn(x, t):
|
| 280 |
+
diffusion = self.transport.path_sampler.compute_diffusion(x, t, form=diffusion_form, norm=diffusion_norm)
|
| 281 |
+
return diffusion
|
| 282 |
+
|
| 283 |
+
sde_drift = \
|
| 284 |
+
lambda x, t, model, **kwargs: \
|
| 285 |
+
self.drift(x, t, model, **kwargs) + diffusion_fn(x, t) * self.score(x, t, model, **kwargs)
|
| 286 |
+
|
| 287 |
+
sde_diffusion = diffusion_fn
|
| 288 |
+
|
| 289 |
+
return sde_drift, sde_diffusion
|
| 290 |
+
|
| 291 |
+
def __get_last_step(
|
| 292 |
+
self,
|
| 293 |
+
sde_drift,
|
| 294 |
+
*,
|
| 295 |
+
last_step,
|
| 296 |
+
last_step_size,
|
| 297 |
+
):
|
| 298 |
+
"""Get the last step function of the SDE solver"""
|
| 299 |
+
|
| 300 |
+
if last_step is None:
|
| 301 |
+
last_step_fn = \
|
| 302 |
+
lambda x, t, model, **model_kwargs: \
|
| 303 |
+
x
|
| 304 |
+
elif last_step == "Mean":
|
| 305 |
+
last_step_fn = \
|
| 306 |
+
lambda x, t, model, **model_kwargs: \
|
| 307 |
+
x + sde_drift(x, t, model, **model_kwargs) * last_step_size
|
| 308 |
+
elif last_step == "Tweedie":
|
| 309 |
+
alpha = self.transport.path_sampler.compute_alpha_t # simple aliasing; the original name was too long
|
| 310 |
+
sigma = self.transport.path_sampler.compute_sigma_t
|
| 311 |
+
last_step_fn = \
|
| 312 |
+
lambda x, t, model, **model_kwargs: \
|
| 313 |
+
x / alpha(t)[0][0] + (sigma(t)[0][0] ** 2) / alpha(t)[0][0] * self.score(x, t, model,
|
| 314 |
+
**model_kwargs)
|
| 315 |
+
elif last_step == "Euler":
|
| 316 |
+
last_step_fn = \
|
| 317 |
+
lambda x, t, model, **model_kwargs: \
|
| 318 |
+
x + self.drift(x, t, model, **model_kwargs) * last_step_size
|
| 319 |
+
else:
|
| 320 |
+
raise NotImplementedError()
|
| 321 |
+
|
| 322 |
+
return last_step_fn
|
| 323 |
+
|
| 324 |
+
def sample_sde(
|
| 325 |
+
self,
|
| 326 |
+
*,
|
| 327 |
+
sampling_method="Euler",
|
| 328 |
+
diffusion_form="SBDM",
|
| 329 |
+
diffusion_norm=1.0,
|
| 330 |
+
last_step="Mean",
|
| 331 |
+
last_step_size=0.04,
|
| 332 |
+
num_steps=250,
|
| 333 |
+
):
|
| 334 |
+
"""returns a sampling function with given SDE settings
|
| 335 |
+
Args:
|
| 336 |
+
- sampling_method: type of sampler used in solving the SDE; default to be Euler-Maruyama
|
| 337 |
+
- diffusion_form: function form of diffusion coefficient; default to be matching SBDM
|
| 338 |
+
- diffusion_norm: function magnitude of diffusion coefficient; default to 1
|
| 339 |
+
- last_step: type of the last step; default to identity
|
| 340 |
+
- last_step_size: size of the last step; default to match the stride of 250 steps over [0,1]
|
| 341 |
+
- num_steps: total integration step of SDE
|
| 342 |
+
"""
|
| 343 |
+
|
| 344 |
+
if last_step is None:
|
| 345 |
+
last_step_size = 0.0
|
| 346 |
+
|
| 347 |
+
sde_drift, sde_diffusion = self.__get_sde_diffusion_and_drift(
|
| 348 |
+
diffusion_form=diffusion_form,
|
| 349 |
+
diffusion_norm=diffusion_norm,
|
| 350 |
+
)
|
| 351 |
+
|
| 352 |
+
t0, t1 = self.transport.check_interval(
|
| 353 |
+
self.transport.train_eps,
|
| 354 |
+
self.transport.sample_eps,
|
| 355 |
+
diffusion_form=diffusion_form,
|
| 356 |
+
sde=True,
|
| 357 |
+
eval=True,
|
| 358 |
+
reverse=False,
|
| 359 |
+
last_step_size=last_step_size,
|
| 360 |
+
)
|
| 361 |
+
|
| 362 |
+
_sde = sde(
|
| 363 |
+
sde_drift,
|
| 364 |
+
sde_diffusion,
|
| 365 |
+
t0=t0,
|
| 366 |
+
t1=t1,
|
| 367 |
+
num_steps=num_steps,
|
| 368 |
+
sampler_type=sampling_method
|
| 369 |
+
)
|
| 370 |
+
|
| 371 |
+
last_step_fn = self.__get_last_step(sde_drift, last_step=last_step, last_step_size=last_step_size)
|
| 372 |
+
|
| 373 |
+
def _sample(init, model, **model_kwargs):
|
| 374 |
+
xs = _sde.sample(init, model, **model_kwargs)
|
| 375 |
+
ts = th.ones(init.size(0), device=init.device) * t1
|
| 376 |
+
x = last_step_fn(xs[-1], ts, model, **model_kwargs)
|
| 377 |
+
xs.append(x)
|
| 378 |
+
|
| 379 |
+
assert len(xs) == num_steps, "Samples does not match the number of steps"
|
| 380 |
+
|
| 381 |
+
return xs
|
| 382 |
+
|
| 383 |
+
return _sample
|
| 384 |
+
|
| 385 |
+
def sample_ode(
|
| 386 |
+
self,
|
| 387 |
+
*,
|
| 388 |
+
sampling_method="dopri5",
|
| 389 |
+
num_steps=50,
|
| 390 |
+
atol=1e-6,
|
| 391 |
+
rtol=1e-3,
|
| 392 |
+
reverse=False,
|
| 393 |
+
):
|
| 394 |
+
"""returns a sampling function with given ODE settings
|
| 395 |
+
Args:
|
| 396 |
+
- sampling_method: type of sampler used in solving the ODE; default to be Dopri5
|
| 397 |
+
- num_steps:
|
| 398 |
+
- fixed solver (Euler, Heun): the actual number of integration steps performed
|
| 399 |
+
- adaptive solver (Dopri5): the number of datapoints saved during integration; produced by interpolation
|
| 400 |
+
- atol: absolute error tolerance for the solver
|
| 401 |
+
- rtol: relative error tolerance for the solver
|
| 402 |
+
- reverse: whether solving the ODE in reverse (data to noise); default to False
|
| 403 |
+
"""
|
| 404 |
+
if reverse:
|
| 405 |
+
drift = lambda x, t, model, **kwargs: self.drift(x, th.ones_like(t) * (1 - t), model, **kwargs)
|
| 406 |
+
else:
|
| 407 |
+
drift = self.drift
|
| 408 |
+
|
| 409 |
+
t0, t1 = self.transport.check_interval(
|
| 410 |
+
self.transport.train_eps,
|
| 411 |
+
self.transport.sample_eps,
|
| 412 |
+
sde=False,
|
| 413 |
+
eval=True,
|
| 414 |
+
reverse=reverse,
|
| 415 |
+
last_step_size=0.0,
|
| 416 |
+
)
|
| 417 |
+
|
| 418 |
+
_ode = ode(
|
| 419 |
+
drift=drift,
|
| 420 |
+
t0=t0,
|
| 421 |
+
t1=t1,
|
| 422 |
+
sampler_type=sampling_method,
|
| 423 |
+
num_steps=num_steps,
|
| 424 |
+
atol=atol,
|
| 425 |
+
rtol=rtol,
|
| 426 |
+
)
|
| 427 |
+
|
| 428 |
+
return _ode.sample
|
| 429 |
+
|
| 430 |
+
def sample_ode_intermediate(
|
| 431 |
+
self,
|
| 432 |
+
*,
|
| 433 |
+
sampling_method="dopri5",
|
| 434 |
+
num_steps=50,
|
| 435 |
+
atol=1e-6,
|
| 436 |
+
rtol=1e-3,
|
| 437 |
+
t=0.5,
|
| 438 |
+
reverse=False,
|
| 439 |
+
):
|
| 440 |
+
"""returns a sampling function with given ODE settings
|
| 441 |
+
Args:
|
| 442 |
+
- sampling_method: type of sampler used in solving the ODE; default to be Dopri5
|
| 443 |
+
- num_steps:
|
| 444 |
+
- fixed solver (Euler, Heun): the actual number of integration steps performed
|
| 445 |
+
- adaptive solver (Dopri5): the number of datapoints saved during integration; produced by interpolation
|
| 446 |
+
- atol: absolute error tolerance for the solver
|
| 447 |
+
- rtol: relative error tolerance for the solver
|
| 448 |
+
- reverse: whether solving the ODE in reverse (data to noise); default to False
|
| 449 |
+
"""
|
| 450 |
+
if reverse:
|
| 451 |
+
drift = lambda x, t, model, **kwargs: self.drift(x, th.ones_like(t) * (1 - t), model, **kwargs)
|
| 452 |
+
else:
|
| 453 |
+
drift = self.drift
|
| 454 |
+
|
| 455 |
+
t0, t1 = self.transport.check_interval(
|
| 456 |
+
self.transport.train_eps,
|
| 457 |
+
self.transport.sample_eps,
|
| 458 |
+
sde=False,
|
| 459 |
+
eval=True,
|
| 460 |
+
reverse=reverse,
|
| 461 |
+
last_step_size=0.0,
|
| 462 |
+
)
|
| 463 |
+
|
| 464 |
+
_ode = ode(
|
| 465 |
+
drift=drift,
|
| 466 |
+
t0=t,
|
| 467 |
+
t1=t1,
|
| 468 |
+
sampler_type=sampling_method,
|
| 469 |
+
num_steps=num_steps,
|
| 470 |
+
atol=atol,
|
| 471 |
+
rtol=rtol,
|
| 472 |
+
)
|
| 473 |
+
|
| 474 |
+
return _ode.sample
|
| 475 |
+
|
| 476 |
+
def sample_ode_likelihood(
|
| 477 |
+
self,
|
| 478 |
+
*,
|
| 479 |
+
sampling_method="dopri5",
|
| 480 |
+
num_steps=50,
|
| 481 |
+
atol=1e-6,
|
| 482 |
+
rtol=1e-3,
|
| 483 |
+
):
|
| 484 |
+
|
| 485 |
+
"""returns a sampling function for calculating likelihood with given ODE settings
|
| 486 |
+
Args:
|
| 487 |
+
- sampling_method: type of sampler used in solving the ODE; default to be Dopri5
|
| 488 |
+
- num_steps:
|
| 489 |
+
- fixed solver (Euler, Heun): the actual number of integration steps performed
|
| 490 |
+
- adaptive solver (Dopri5): the number of datapoints saved during integration; produced by interpolation
|
| 491 |
+
- atol: absolute error tolerance for the solver
|
| 492 |
+
- rtol: relative error tolerance for the solver
|
| 493 |
+
"""
|
| 494 |
+
|
| 495 |
+
def _likelihood_drift(x, t, model, **model_kwargs):
|
| 496 |
+
x, _ = x
|
| 497 |
+
eps = th.randint(2, x.size(), dtype=th.float, device=x.device) * 2 - 1
|
| 498 |
+
t = th.ones_like(t) * (1 - t)
|
| 499 |
+
with th.enable_grad():
|
| 500 |
+
x.requires_grad = True
|
| 501 |
+
grad = th.autograd.grad(th.sum(self.drift(x, t, model, **model_kwargs) * eps), x)[0]
|
| 502 |
+
logp_grad = th.sum(grad * eps, dim=tuple(range(1, len(x.size()))))
|
| 503 |
+
drift = self.drift(x, t, model, **model_kwargs)
|
| 504 |
+
return (-drift, logp_grad)
|
| 505 |
+
|
| 506 |
+
t0, t1 = self.transport.check_interval(
|
| 507 |
+
self.transport.train_eps,
|
| 508 |
+
self.transport.sample_eps,
|
| 509 |
+
sde=False,
|
| 510 |
+
eval=True,
|
| 511 |
+
reverse=False,
|
| 512 |
+
last_step_size=0.0,
|
| 513 |
+
)
|
| 514 |
+
|
| 515 |
+
_ode = ode(
|
| 516 |
+
drift=_likelihood_drift,
|
| 517 |
+
t0=t0,
|
| 518 |
+
t1=t1,
|
| 519 |
+
sampler_type=sampling_method,
|
| 520 |
+
num_steps=num_steps,
|
| 521 |
+
atol=atol,
|
| 522 |
+
rtol=rtol,
|
| 523 |
+
)
|
| 524 |
+
|
| 525 |
+
def _sample_fn(x, model, **model_kwargs):
|
| 526 |
+
init_logp = th.zeros(x.size(0)).to(x)
|
| 527 |
+
input = (x, init_logp)
|
| 528 |
+
drift, delta_logp = _ode.sample(input, model, **model_kwargs)
|
| 529 |
+
drift, delta_logp = drift[-1], delta_logp[-1]
|
| 530 |
+
prior_logp = self.transport.prior_logp(drift)
|
| 531 |
+
logp = prior_logp - delta_logp
|
| 532 |
+
return logp, drift
|
| 533 |
+
|
| 534 |
+
return _sample_fn
|
Hunyuan3D-2.1/hy3dshape/hy3dshape/models/diffusion/transport/utils.py
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# This file includes code derived from the SiT project (https://github.com/willisma/SiT),
|
| 2 |
+
# which is licensed under the MIT License.
|
| 3 |
+
#
|
| 4 |
+
# MIT License
|
| 5 |
+
#
|
| 6 |
+
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
| 7 |
+
#
|
| 8 |
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 9 |
+
# of this software and associated documentation files (the "Software"), to deal
|
| 10 |
+
# in the Software without restriction, including without limitation the rights
|
| 11 |
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 12 |
+
# copies of the Software, and to permit persons to whom the Software is
|
| 13 |
+
# furnished to do so, subject to the following conditions:
|
| 14 |
+
#
|
| 15 |
+
# The above copyright notice and this permission notice shall be included in all
|
| 16 |
+
# copies or substantial portions of the Software.
|
| 17 |
+
#
|
| 18 |
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 19 |
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 20 |
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 21 |
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 22 |
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 23 |
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
| 24 |
+
# SOFTWARE.
|
| 25 |
+
|
| 26 |
+
import torch as th
|
| 27 |
+
|
| 28 |
+
class EasyDict:
|
| 29 |
+
|
| 30 |
+
def __init__(self, sub_dict):
|
| 31 |
+
for k, v in sub_dict.items():
|
| 32 |
+
setattr(self, k, v)
|
| 33 |
+
|
| 34 |
+
def __getitem__(self, key):
|
| 35 |
+
return getattr(self, key)
|
| 36 |
+
|
| 37 |
+
def mean_flat(x):
|
| 38 |
+
"""
|
| 39 |
+
Take the mean over all non-batch dimensions.
|
| 40 |
+
"""
|
| 41 |
+
return th.mean(x, dim=list(range(1, len(x.size()))))
|
| 42 |
+
|
| 43 |
+
def log_state(state):
|
| 44 |
+
result = []
|
| 45 |
+
|
| 46 |
+
sorted_state = dict(sorted(state.items()))
|
| 47 |
+
for key, value in sorted_state.items():
|
| 48 |
+
# Check if the value is an instance of a class
|
| 49 |
+
if "<object" in str(value) or "object at" in str(value):
|
| 50 |
+
result.append(f"{key}: [{value.__class__.__name__}]")
|
| 51 |
+
else:
|
| 52 |
+
result.append(f"{key}: {value}")
|
| 53 |
+
|
| 54 |
+
return '\n'.join(result)
|
TRELLIS.2/trellis2/models/__init__.py
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import importlib
|
| 2 |
+
|
| 3 |
+
__attributes = {
|
| 4 |
+
# Sparse Structure
|
| 5 |
+
'SparseStructureEncoder': 'sparse_structure_vae',
|
| 6 |
+
'SparseStructureDecoder': 'sparse_structure_vae',
|
| 7 |
+
'SparseStructureFlowModel': 'sparse_structure_flow',
|
| 8 |
+
|
| 9 |
+
# SLat Generation
|
| 10 |
+
'SLatFlowModel': 'structured_latent_flow',
|
| 11 |
+
'ElasticSLatFlowModel': 'structured_latent_flow',
|
| 12 |
+
|
| 13 |
+
# SC-VAEs
|
| 14 |
+
'SparseUnetVaeEncoder': 'sc_vaes.sparse_unet_vae',
|
| 15 |
+
'SparseUnetVaeDecoder': 'sc_vaes.sparse_unet_vae',
|
| 16 |
+
'FlexiDualGridVaeEncoder': 'sc_vaes.fdg_vae',
|
| 17 |
+
'FlexiDualGridVaeDecoder': 'sc_vaes.fdg_vae'
|
| 18 |
+
}
|
| 19 |
+
|
| 20 |
+
__submodules = []
|
| 21 |
+
|
| 22 |
+
__all__ = list(__attributes.keys()) + __submodules
|
| 23 |
+
|
| 24 |
+
def __getattr__(name):
|
| 25 |
+
if name not in globals():
|
| 26 |
+
if name in __attributes:
|
| 27 |
+
module_name = __attributes[name]
|
| 28 |
+
module = importlib.import_module(f".{module_name}", __name__)
|
| 29 |
+
globals()[name] = getattr(module, name)
|
| 30 |
+
elif name in __submodules:
|
| 31 |
+
module = importlib.import_module(f".{name}", __name__)
|
| 32 |
+
globals()[name] = module
|
| 33 |
+
else:
|
| 34 |
+
raise AttributeError(f"module {__name__} has no attribute {name}")
|
| 35 |
+
return globals()[name]
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
def from_pretrained(path: str, **kwargs):
|
| 39 |
+
"""
|
| 40 |
+
Load a model from a pretrained checkpoint.
|
| 41 |
+
|
| 42 |
+
Args:
|
| 43 |
+
path: The path to the checkpoint. Can be either local path or a Hugging Face model name.
|
| 44 |
+
NOTE: config file and model file should take the name f'{path}.json' and f'{path}.safetensors' respectively.
|
| 45 |
+
**kwargs: Additional arguments for the model constructor.
|
| 46 |
+
"""
|
| 47 |
+
import os
|
| 48 |
+
import json
|
| 49 |
+
from safetensors.torch import load_file
|
| 50 |
+
is_local = os.path.exists(f"{path}.json") and os.path.exists(f"{path}.safetensors")
|
| 51 |
+
|
| 52 |
+
if is_local:
|
| 53 |
+
config_file = f"{path}.json"
|
| 54 |
+
model_file = f"{path}.safetensors"
|
| 55 |
+
else:
|
| 56 |
+
from huggingface_hub import hf_hub_download
|
| 57 |
+
path_parts = path.split('/')
|
| 58 |
+
repo_id = f'{path_parts[0]}/{path_parts[1]}'
|
| 59 |
+
model_name = '/'.join(path_parts[2:])
|
| 60 |
+
config_file = hf_hub_download(repo_id, f"{model_name}.json")
|
| 61 |
+
model_file = hf_hub_download(repo_id, f"{model_name}.safetensors")
|
| 62 |
+
|
| 63 |
+
with open(config_file, 'r') as f:
|
| 64 |
+
config = json.load(f)
|
| 65 |
+
model = __getattr__(config['name'])(**config['args'], **kwargs)
|
| 66 |
+
model.load_state_dict(load_file(model_file), strict=False)
|
| 67 |
+
|
| 68 |
+
return model
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
# For Pylance
|
| 72 |
+
if __name__ == '__main__':
|
| 73 |
+
from .sparse_structure_vae import SparseStructureEncoder, SparseStructureDecoder
|
| 74 |
+
from .sparse_structure_flow import SparseStructureFlowModel
|
| 75 |
+
from .structured_latent_flow import SLatFlowModel, ElasticSLatFlowModel
|
| 76 |
+
|
| 77 |
+
from .sc_vaes.sparse_unet_vae import SparseUnetVaeEncoder, SparseUnetVaeDecoder
|
| 78 |
+
from .sc_vaes.fdg_vae import FlexiDualGridVaeEncoder, FlexiDualGridVaeDecoder
|
TRELLIS.2/trellis2/models/sc_vaes/fdg_vae.py
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import *
|
| 2 |
+
import torch
|
| 3 |
+
import torch.nn as nn
|
| 4 |
+
import torch.nn.functional as F
|
| 5 |
+
from ...modules import sparse as sp
|
| 6 |
+
from .sparse_unet_vae import (
|
| 7 |
+
SparseResBlock3d,
|
| 8 |
+
SparseConvNeXtBlock3d,
|
| 9 |
+
|
| 10 |
+
SparseResBlockDownsample3d,
|
| 11 |
+
SparseResBlockUpsample3d,
|
| 12 |
+
SparseResBlockS2C3d,
|
| 13 |
+
SparseResBlockC2S3d,
|
| 14 |
+
)
|
| 15 |
+
from .sparse_unet_vae import (
|
| 16 |
+
SparseUnetVaeEncoder,
|
| 17 |
+
SparseUnetVaeDecoder,
|
| 18 |
+
)
|
| 19 |
+
from ...representations import Mesh
|
| 20 |
+
from o_voxel.convert import flexible_dual_grid_to_mesh
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
class FlexiDualGridVaeEncoder(SparseUnetVaeEncoder):
|
| 24 |
+
def __init__(
|
| 25 |
+
self,
|
| 26 |
+
model_channels: List[int],
|
| 27 |
+
latent_channels: int,
|
| 28 |
+
num_blocks: List[int],
|
| 29 |
+
block_type: List[str],
|
| 30 |
+
down_block_type: List[str],
|
| 31 |
+
block_args: List[Dict[str, Any]],
|
| 32 |
+
use_fp16: bool = False,
|
| 33 |
+
):
|
| 34 |
+
super().__init__(
|
| 35 |
+
6,
|
| 36 |
+
model_channels,
|
| 37 |
+
latent_channels,
|
| 38 |
+
num_blocks,
|
| 39 |
+
block_type,
|
| 40 |
+
down_block_type,
|
| 41 |
+
block_args,
|
| 42 |
+
use_fp16,
|
| 43 |
+
)
|
| 44 |
+
|
| 45 |
+
def forward(self, vertices: sp.SparseTensor, intersected: sp.SparseTensor, sample_posterior=False, return_raw=False):
|
| 46 |
+
x = vertices.replace(torch.cat([
|
| 47 |
+
vertices.feats - 0.5,
|
| 48 |
+
intersected.feats.float() - 0.5,
|
| 49 |
+
], dim=1))
|
| 50 |
+
return super().forward(x, sample_posterior, return_raw)
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
class FlexiDualGridVaeDecoder(SparseUnetVaeDecoder):
|
| 54 |
+
def __init__(
|
| 55 |
+
self,
|
| 56 |
+
resolution: int,
|
| 57 |
+
model_channels: List[int],
|
| 58 |
+
latent_channels: int,
|
| 59 |
+
num_blocks: List[int],
|
| 60 |
+
block_type: List[str],
|
| 61 |
+
up_block_type: List[str],
|
| 62 |
+
block_args: List[Dict[str, Any]],
|
| 63 |
+
voxel_margin: float = 0.5,
|
| 64 |
+
use_fp16: bool = False,
|
| 65 |
+
):
|
| 66 |
+
self.resolution = resolution
|
| 67 |
+
self.voxel_margin = voxel_margin
|
| 68 |
+
|
| 69 |
+
super().__init__(
|
| 70 |
+
7,
|
| 71 |
+
model_channels,
|
| 72 |
+
latent_channels,
|
| 73 |
+
num_blocks,
|
| 74 |
+
block_type,
|
| 75 |
+
up_block_type,
|
| 76 |
+
block_args,
|
| 77 |
+
use_fp16,
|
| 78 |
+
)
|
| 79 |
+
|
| 80 |
+
def set_resolution(self, resolution: int) -> None:
|
| 81 |
+
self.resolution = resolution
|
| 82 |
+
|
| 83 |
+
def forward(self, x: sp.SparseTensor, gt_intersected: sp.SparseTensor = None, **kwargs):
|
| 84 |
+
decoded = super().forward(x, **kwargs)
|
| 85 |
+
if self.training:
|
| 86 |
+
h, subs_gt, subs = decoded
|
| 87 |
+
vertices = h.replace((1 + 2 * self.voxel_margin) * F.sigmoid(h.feats[..., 0:3]) - self.voxel_margin)
|
| 88 |
+
intersected_logits = h.replace(h.feats[..., 3:6])
|
| 89 |
+
quad_lerp = h.replace(F.softplus(h.feats[..., 6:7]))
|
| 90 |
+
mesh = [Mesh(flexible_dual_grid_to_mesh(
|
| 91 |
+
h.coords[:, 1:], v.feats, i.feats, q.feats,
|
| 92 |
+
aabb=[[-0.5, -0.5, -0.5], [0.5, 0.5, 0.5]],
|
| 93 |
+
grid_size=self.resolution,
|
| 94 |
+
train=True
|
| 95 |
+
)) for v, i, q in zip(vertices, gt_intersected, quad_lerp)]
|
| 96 |
+
return mesh, vertices, intersected_logits, subs_gt, subs
|
| 97 |
+
else:
|
| 98 |
+
out_list = list(decoded) if isinstance(decoded, tuple) else [decoded]
|
| 99 |
+
h = out_list[0]
|
| 100 |
+
vertices = h.replace((1 + 2 * self.voxel_margin) * F.sigmoid(h.feats[..., 0:3]) - self.voxel_margin)
|
| 101 |
+
intersected = h.replace(h.feats[..., 3:6] > 0)
|
| 102 |
+
quad_lerp = h.replace(F.softplus(h.feats[..., 6:7]))
|
| 103 |
+
mesh = [Mesh(*flexible_dual_grid_to_mesh(
|
| 104 |
+
h.coords[:, 1:], v.feats, i.feats, q.feats,
|
| 105 |
+
aabb=[[-0.5, -0.5, -0.5], [0.5, 0.5, 0.5]],
|
| 106 |
+
grid_size=self.resolution,
|
| 107 |
+
train=False
|
| 108 |
+
)) for v, i, q in zip(vertices, intersected, quad_lerp)]
|
| 109 |
+
out_list[0] = mesh
|
| 110 |
+
return out_list[0] if len(out_list) == 1 else tuple(out_list)
|
TRELLIS.2/trellis2/models/sc_vaes/sparse_unet_vae.py
ADDED
|
@@ -0,0 +1,522 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import *
|
| 2 |
+
import torch
|
| 3 |
+
import torch.nn as nn
|
| 4 |
+
import torch.nn.functional as F
|
| 5 |
+
import torch.utils.checkpoint
|
| 6 |
+
from ...modules.utils import convert_module_to_f16, convert_module_to_f32, zero_module
|
| 7 |
+
from ...modules import sparse as sp
|
| 8 |
+
from ...modules.norm import LayerNorm32
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
class SparseResBlock3d(nn.Module):
|
| 12 |
+
def __init__(
|
| 13 |
+
self,
|
| 14 |
+
channels: int,
|
| 15 |
+
out_channels: Optional[int] = None,
|
| 16 |
+
downsample: bool = False,
|
| 17 |
+
upsample: bool = False,
|
| 18 |
+
resample_mode: Literal['nearest', 'spatial2channel'] = 'nearest',
|
| 19 |
+
use_checkpoint: bool = False,
|
| 20 |
+
):
|
| 21 |
+
super().__init__()
|
| 22 |
+
self.channels = channels
|
| 23 |
+
self.out_channels = out_channels or channels
|
| 24 |
+
self.downsample = downsample
|
| 25 |
+
self.upsample = upsample
|
| 26 |
+
self.resample_mode = resample_mode
|
| 27 |
+
self.use_checkpoint = use_checkpoint
|
| 28 |
+
|
| 29 |
+
assert not (downsample and upsample), "Cannot downsample and upsample at the same time"
|
| 30 |
+
|
| 31 |
+
self.norm1 = LayerNorm32(channels, elementwise_affine=True, eps=1e-6)
|
| 32 |
+
self.norm2 = LayerNorm32(self.out_channels, elementwise_affine=False, eps=1e-6)
|
| 33 |
+
if resample_mode == 'nearest':
|
| 34 |
+
self.conv1 = sp.SparseConv3d(channels, self.out_channels, 3)
|
| 35 |
+
elif resample_mode =='spatial2channel' and not self.downsample:
|
| 36 |
+
self.conv1 = sp.SparseConv3d(channels, self.out_channels * 8, 3)
|
| 37 |
+
elif resample_mode =='spatial2channel' and self.downsample:
|
| 38 |
+
self.conv1 = sp.SparseConv3d(channels, self.out_channels // 8, 3)
|
| 39 |
+
self.conv2 = zero_module(sp.SparseConv3d(self.out_channels, self.out_channels, 3))
|
| 40 |
+
if resample_mode == 'nearest':
|
| 41 |
+
self.skip_connection = sp.SparseLinear(channels, self.out_channels) if channels != self.out_channels else nn.Identity()
|
| 42 |
+
elif resample_mode =='spatial2channel' and self.downsample:
|
| 43 |
+
self.skip_connection = lambda x: x.replace(x.feats.reshape(x.feats.shape[0], out_channels, channels * 8 // out_channels).mean(dim=-1))
|
| 44 |
+
elif resample_mode =='spatial2channel' and not self.downsample:
|
| 45 |
+
self.skip_connection = lambda x: x.replace(x.feats.repeat_interleave(out_channels // (channels // 8), dim=1))
|
| 46 |
+
self.updown = None
|
| 47 |
+
if self.downsample:
|
| 48 |
+
if resample_mode == 'nearest':
|
| 49 |
+
self.updown = sp.SparseDownsample(2)
|
| 50 |
+
elif resample_mode =='spatial2channel':
|
| 51 |
+
self.updown = sp.SparseSpatial2Channel(2)
|
| 52 |
+
elif self.upsample:
|
| 53 |
+
self.to_subdiv = sp.SparseLinear(channels, 8)
|
| 54 |
+
if resample_mode == 'nearest':
|
| 55 |
+
self.updown = sp.SparseUpsample(2)
|
| 56 |
+
elif resample_mode =='spatial2channel':
|
| 57 |
+
self.updown = sp.SparseChannel2Spatial(2)
|
| 58 |
+
|
| 59 |
+
def _updown(self, x: sp.SparseTensor, subdiv: sp.SparseTensor = None) -> sp.SparseTensor:
|
| 60 |
+
if self.downsample:
|
| 61 |
+
x = self.updown(x)
|
| 62 |
+
elif self.upsample:
|
| 63 |
+
x = self.updown(x, subdiv.replace(subdiv.feats > 0))
|
| 64 |
+
return x
|
| 65 |
+
|
| 66 |
+
def _forward(self, x: sp.SparseTensor) -> sp.SparseTensor:
|
| 67 |
+
subdiv = None
|
| 68 |
+
if self.upsample:
|
| 69 |
+
subdiv = self.to_subdiv(x)
|
| 70 |
+
h = x.replace(self.norm1(x.feats))
|
| 71 |
+
h = h.replace(F.silu(h.feats))
|
| 72 |
+
if self.resample_mode == 'spatial2channel':
|
| 73 |
+
h = self.conv1(h)
|
| 74 |
+
h = self._updown(h, subdiv)
|
| 75 |
+
x = self._updown(x, subdiv)
|
| 76 |
+
if self.resample_mode == 'nearest':
|
| 77 |
+
h = self.conv1(h)
|
| 78 |
+
h = h.replace(self.norm2(h.feats))
|
| 79 |
+
h = h.replace(F.silu(h.feats))
|
| 80 |
+
h = self.conv2(h)
|
| 81 |
+
h = h + self.skip_connection(x)
|
| 82 |
+
if self.upsample:
|
| 83 |
+
return h, subdiv
|
| 84 |
+
return h
|
| 85 |
+
|
| 86 |
+
def forward(self, x: sp.SparseTensor) -> sp.SparseTensor:
|
| 87 |
+
if self.use_checkpoint:
|
| 88 |
+
return torch.utils.checkpoint.checkpoint(self._forward, x, use_reentrant=False)
|
| 89 |
+
else:
|
| 90 |
+
return self._forward(x)
|
| 91 |
+
|
| 92 |
+
|
| 93 |
+
class SparseResBlockDownsample3d(nn.Module):
|
| 94 |
+
def __init__(
|
| 95 |
+
self,
|
| 96 |
+
channels: int,
|
| 97 |
+
out_channels: Optional[int] = None,
|
| 98 |
+
use_checkpoint: bool = False,
|
| 99 |
+
):
|
| 100 |
+
super().__init__()
|
| 101 |
+
self.channels = channels
|
| 102 |
+
self.out_channels = out_channels or channels
|
| 103 |
+
self.use_checkpoint = use_checkpoint
|
| 104 |
+
|
| 105 |
+
self.norm1 = LayerNorm32(channels, elementwise_affine=True, eps=1e-6)
|
| 106 |
+
self.norm2 = LayerNorm32(self.out_channels, elementwise_affine=False, eps=1e-6)
|
| 107 |
+
self.conv1 = sp.SparseConv3d(channels, self.out_channels, 3)
|
| 108 |
+
self.conv2 = zero_module(sp.SparseConv3d(self.out_channels, self.out_channels, 3))
|
| 109 |
+
self.skip_connection = sp.SparseLinear(channels, self.out_channels) if channels != self.out_channels else nn.Identity()
|
| 110 |
+
self.updown = sp.SparseDownsample(2)
|
| 111 |
+
|
| 112 |
+
def _forward(self, x: sp.SparseTensor) -> sp.SparseTensor:
|
| 113 |
+
h = x.replace(self.norm1(x.feats))
|
| 114 |
+
h = h.replace(F.silu(h.feats))
|
| 115 |
+
h = self.updown(h)
|
| 116 |
+
x = self.updown(x)
|
| 117 |
+
h = self.conv1(h)
|
| 118 |
+
h = h.replace(self.norm2(h.feats))
|
| 119 |
+
h = h.replace(F.silu(h.feats))
|
| 120 |
+
h = self.conv2(h)
|
| 121 |
+
h = h + self.skip_connection(x)
|
| 122 |
+
return h
|
| 123 |
+
|
| 124 |
+
def forward(self, x: sp.SparseTensor) -> sp.SparseTensor:
|
| 125 |
+
if self.use_checkpoint:
|
| 126 |
+
return torch.utils.checkpoint.checkpoint(self._forward, x, use_reentrant=False)
|
| 127 |
+
else:
|
| 128 |
+
return self._forward(x)
|
| 129 |
+
|
| 130 |
+
|
| 131 |
+
class SparseResBlockUpsample3d(nn.Module):
|
| 132 |
+
def __init__(
|
| 133 |
+
self,
|
| 134 |
+
channels: int,
|
| 135 |
+
out_channels: Optional[int] = None,
|
| 136 |
+
use_checkpoint: bool = False,
|
| 137 |
+
pred_subdiv: bool = True,
|
| 138 |
+
):
|
| 139 |
+
super().__init__()
|
| 140 |
+
self.channels = channels
|
| 141 |
+
self.out_channels = out_channels or channels
|
| 142 |
+
self.use_checkpoint = use_checkpoint
|
| 143 |
+
self.pred_subdiv = pred_subdiv
|
| 144 |
+
|
| 145 |
+
self.norm1 = LayerNorm32(channels, elementwise_affine=True, eps=1e-6)
|
| 146 |
+
self.norm2 = LayerNorm32(self.out_channels, elementwise_affine=False, eps=1e-6)
|
| 147 |
+
self.conv1 = sp.SparseConv3d(channels, self.out_channels, 3)
|
| 148 |
+
self.conv2 = zero_module(sp.SparseConv3d(self.out_channels, self.out_channels, 3))
|
| 149 |
+
self.skip_connection = sp.SparseLinear(channels, self.out_channels) if channels != self.out_channels else nn.Identity()
|
| 150 |
+
if self.pred_subdiv:
|
| 151 |
+
self.to_subdiv = sp.SparseLinear(channels, 8)
|
| 152 |
+
self.updown = sp.SparseUpsample(2)
|
| 153 |
+
|
| 154 |
+
def _forward(self, x: sp.SparseTensor, subdiv: sp.SparseTensor = None) -> sp.SparseTensor:
|
| 155 |
+
if self.pred_subdiv:
|
| 156 |
+
subdiv = self.to_subdiv(x)
|
| 157 |
+
h = x.replace(self.norm1(x.feats))
|
| 158 |
+
h = h.replace(F.silu(h.feats))
|
| 159 |
+
subdiv_binarized = subdiv.replace(subdiv.feats > 0) if subdiv is not None else None
|
| 160 |
+
h = self.updown(h, subdiv_binarized)
|
| 161 |
+
x = self.updown(x, subdiv_binarized)
|
| 162 |
+
h = self.conv1(h)
|
| 163 |
+
h = h.replace(self.norm2(h.feats))
|
| 164 |
+
h = h.replace(F.silu(h.feats))
|
| 165 |
+
h = self.conv2(h)
|
| 166 |
+
h = h + self.skip_connection(x)
|
| 167 |
+
if self.pred_subdiv:
|
| 168 |
+
return h, subdiv
|
| 169 |
+
else:
|
| 170 |
+
return h
|
| 171 |
+
|
| 172 |
+
def forward(self, x: sp.SparseTensor) -> sp.SparseTensor:
|
| 173 |
+
if self.use_checkpoint:
|
| 174 |
+
return torch.utils.checkpoint.checkpoint(self._forward, x, use_reentrant=False)
|
| 175 |
+
else:
|
| 176 |
+
return self._forward(x)
|
| 177 |
+
|
| 178 |
+
|
| 179 |
+
class SparseResBlockS2C3d(nn.Module):
|
| 180 |
+
def __init__(
|
| 181 |
+
self,
|
| 182 |
+
channels: int,
|
| 183 |
+
out_channels: Optional[int] = None,
|
| 184 |
+
use_checkpoint: bool = False,
|
| 185 |
+
):
|
| 186 |
+
super().__init__()
|
| 187 |
+
self.channels = channels
|
| 188 |
+
self.out_channels = out_channels or channels
|
| 189 |
+
self.use_checkpoint = use_checkpoint
|
| 190 |
+
|
| 191 |
+
self.norm1 = LayerNorm32(channels, elementwise_affine=True, eps=1e-6)
|
| 192 |
+
self.norm2 = LayerNorm32(self.out_channels, elementwise_affine=False, eps=1e-6)
|
| 193 |
+
self.conv1 = sp.SparseConv3d(channels, self.out_channels // 8, 3)
|
| 194 |
+
self.conv2 = zero_module(sp.SparseConv3d(self.out_channels, self.out_channels, 3))
|
| 195 |
+
self.skip_connection = lambda x: x.replace(x.feats.reshape(x.feats.shape[0], out_channels, channels * 8 // out_channels).mean(dim=-1))
|
| 196 |
+
self.updown = sp.SparseSpatial2Channel(2)
|
| 197 |
+
|
| 198 |
+
def _forward(self, x: sp.SparseTensor) -> sp.SparseTensor:
|
| 199 |
+
h = x.replace(self.norm1(x.feats))
|
| 200 |
+
h = h.replace(F.silu(h.feats))
|
| 201 |
+
h = self.conv1(h)
|
| 202 |
+
h = self.updown(h)
|
| 203 |
+
x = self.updown(x)
|
| 204 |
+
h = h.replace(self.norm2(h.feats))
|
| 205 |
+
h = h.replace(F.silu(h.feats))
|
| 206 |
+
h = self.conv2(h)
|
| 207 |
+
h = h + self.skip_connection(x)
|
| 208 |
+
return h
|
| 209 |
+
|
| 210 |
+
def forward(self, x: sp.SparseTensor) -> sp.SparseTensor:
|
| 211 |
+
if self.use_checkpoint:
|
| 212 |
+
return torch.utils.checkpoint.checkpoint(self._forward, x, use_reentrant=False)
|
| 213 |
+
else:
|
| 214 |
+
return self._forward(x)
|
| 215 |
+
|
| 216 |
+
|
| 217 |
+
class SparseResBlockC2S3d(nn.Module):
|
| 218 |
+
def __init__(
|
| 219 |
+
self,
|
| 220 |
+
channels: int,
|
| 221 |
+
out_channels: Optional[int] = None,
|
| 222 |
+
use_checkpoint: bool = False,
|
| 223 |
+
pred_subdiv: bool = True,
|
| 224 |
+
):
|
| 225 |
+
super().__init__()
|
| 226 |
+
self.channels = channels
|
| 227 |
+
self.out_channels = out_channels or channels
|
| 228 |
+
self.use_checkpoint = use_checkpoint
|
| 229 |
+
self.pred_subdiv = pred_subdiv
|
| 230 |
+
|
| 231 |
+
self.norm1 = LayerNorm32(channels, elementwise_affine=True, eps=1e-6)
|
| 232 |
+
self.norm2 = LayerNorm32(self.out_channels, elementwise_affine=False, eps=1e-6)
|
| 233 |
+
self.conv1 = sp.SparseConv3d(channels, self.out_channels * 8, 3)
|
| 234 |
+
self.conv2 = zero_module(sp.SparseConv3d(self.out_channels, self.out_channels, 3))
|
| 235 |
+
self.skip_connection = lambda x: x.replace(x.feats.repeat_interleave(out_channels // (channels // 8), dim=1))
|
| 236 |
+
if pred_subdiv:
|
| 237 |
+
self.to_subdiv = sp.SparseLinear(channels, 8)
|
| 238 |
+
self.updown = sp.SparseChannel2Spatial(2)
|
| 239 |
+
|
| 240 |
+
def _forward(self, x: sp.SparseTensor, subdiv: sp.SparseTensor = None) -> sp.SparseTensor:
|
| 241 |
+
if self.pred_subdiv:
|
| 242 |
+
subdiv = self.to_subdiv(x)
|
| 243 |
+
h = x.replace(self.norm1(x.feats))
|
| 244 |
+
h = h.replace(F.silu(h.feats))
|
| 245 |
+
h = self.conv1(h)
|
| 246 |
+
subdiv_binarized = subdiv.replace(subdiv.feats > 0) if subdiv is not None else None
|
| 247 |
+
h = self.updown(h, subdiv_binarized)
|
| 248 |
+
x = self.updown(x, subdiv_binarized)
|
| 249 |
+
h = h.replace(self.norm2(h.feats))
|
| 250 |
+
h = h.replace(F.silu(h.feats))
|
| 251 |
+
h = self.conv2(h)
|
| 252 |
+
h = h + self.skip_connection(x)
|
| 253 |
+
if self.pred_subdiv:
|
| 254 |
+
return h, subdiv
|
| 255 |
+
else:
|
| 256 |
+
return h
|
| 257 |
+
|
| 258 |
+
def forward(self, x: sp.SparseTensor, subdiv: sp.SparseTensor = None) -> sp.SparseTensor:
|
| 259 |
+
if self.use_checkpoint:
|
| 260 |
+
return torch.utils.checkpoint.checkpoint(self._forward, x, subdiv, use_reentrant=False)
|
| 261 |
+
else:
|
| 262 |
+
return self._forward(x, subdiv)
|
| 263 |
+
|
| 264 |
+
|
| 265 |
+
class SparseConvNeXtBlock3d(nn.Module):
|
| 266 |
+
def __init__(
|
| 267 |
+
self,
|
| 268 |
+
channels: int,
|
| 269 |
+
mlp_ratio: float = 4.0,
|
| 270 |
+
use_checkpoint: bool = False,
|
| 271 |
+
):
|
| 272 |
+
super().__init__()
|
| 273 |
+
self.channels = channels
|
| 274 |
+
self.use_checkpoint = use_checkpoint
|
| 275 |
+
|
| 276 |
+
self.norm = LayerNorm32(channels, elementwise_affine=True, eps=1e-6)
|
| 277 |
+
self.conv = sp.SparseConv3d(channels, channels, 3)
|
| 278 |
+
self.mlp = nn.Sequential(
|
| 279 |
+
nn.Linear(channels, int(channels * mlp_ratio)),
|
| 280 |
+
nn.SiLU(),
|
| 281 |
+
zero_module(nn.Linear(int(channels * mlp_ratio), channels)),
|
| 282 |
+
)
|
| 283 |
+
|
| 284 |
+
def _forward(self, x: sp.SparseTensor) -> sp.SparseTensor:
|
| 285 |
+
h = self.conv(x)
|
| 286 |
+
h = h.replace(self.norm(h.feats))
|
| 287 |
+
h = h.replace(self.mlp(h.feats))
|
| 288 |
+
return h + x
|
| 289 |
+
|
| 290 |
+
def forward(self, x: sp.SparseTensor) -> sp.SparseTensor:
|
| 291 |
+
if self.use_checkpoint:
|
| 292 |
+
return torch.utils.checkpoint.checkpoint(self._forward, x, use_reentrant=False)
|
| 293 |
+
else:
|
| 294 |
+
return self._forward(x)
|
| 295 |
+
|
| 296 |
+
|
| 297 |
+
class SparseUnetVaeEncoder(nn.Module):
|
| 298 |
+
"""
|
| 299 |
+
Sparse Swin Transformer Unet VAE model.
|
| 300 |
+
"""
|
| 301 |
+
def __init__(
|
| 302 |
+
self,
|
| 303 |
+
in_channels: int,
|
| 304 |
+
model_channels: List[int],
|
| 305 |
+
latent_channels: int,
|
| 306 |
+
num_blocks: List[int],
|
| 307 |
+
block_type: List[str],
|
| 308 |
+
down_block_type: List[str],
|
| 309 |
+
block_args: List[Dict[str, Any]],
|
| 310 |
+
use_fp16: bool = False,
|
| 311 |
+
):
|
| 312 |
+
super().__init__()
|
| 313 |
+
self.in_channels = in_channels
|
| 314 |
+
self.model_channels = model_channels
|
| 315 |
+
self.num_blocks = num_blocks
|
| 316 |
+
self.dtype = torch.float16 if use_fp16 else torch.float32
|
| 317 |
+
self.dtype = torch.float16 if use_fp16 else torch.float32
|
| 318 |
+
|
| 319 |
+
self.input_layer = sp.SparseLinear(in_channels, model_channels[0])
|
| 320 |
+
self.to_latent = sp.SparseLinear(model_channels[-1], 2 * latent_channels)
|
| 321 |
+
|
| 322 |
+
self.blocks = nn.ModuleList([])
|
| 323 |
+
for i in range(len(num_blocks)):
|
| 324 |
+
self.blocks.append(nn.ModuleList([]))
|
| 325 |
+
for j in range(num_blocks[i]):
|
| 326 |
+
self.blocks[-1].append(
|
| 327 |
+
globals()[block_type[i]](
|
| 328 |
+
model_channels[i],
|
| 329 |
+
**block_args[i],
|
| 330 |
+
)
|
| 331 |
+
)
|
| 332 |
+
if i < len(num_blocks) - 1:
|
| 333 |
+
self.blocks[-1].append(
|
| 334 |
+
globals()[down_block_type[i]](
|
| 335 |
+
model_channels[i],
|
| 336 |
+
model_channels[i+1],
|
| 337 |
+
**block_args[i],
|
| 338 |
+
)
|
| 339 |
+
)
|
| 340 |
+
|
| 341 |
+
self.initialize_weights()
|
| 342 |
+
if use_fp16:
|
| 343 |
+
self.convert_to_fp16()
|
| 344 |
+
|
| 345 |
+
@property
|
| 346 |
+
def device(self) -> torch.device:
|
| 347 |
+
"""
|
| 348 |
+
Return the device of the model.
|
| 349 |
+
"""
|
| 350 |
+
return next(self.parameters()).device
|
| 351 |
+
|
| 352 |
+
def convert_to_fp16(self) -> None:
|
| 353 |
+
"""
|
| 354 |
+
Convert the torso of the model to float16.
|
| 355 |
+
"""
|
| 356 |
+
self.blocks.apply(convert_module_to_f16)
|
| 357 |
+
|
| 358 |
+
def convert_to_fp32(self) -> None:
|
| 359 |
+
"""
|
| 360 |
+
Convert the torso of the model to float32.
|
| 361 |
+
"""
|
| 362 |
+
self.blocks.apply(convert_module_to_f32)
|
| 363 |
+
|
| 364 |
+
def initialize_weights(self) -> None:
|
| 365 |
+
# Initialize transformer layers:
|
| 366 |
+
def _basic_init(module):
|
| 367 |
+
if isinstance(module, nn.Linear):
|
| 368 |
+
torch.nn.init.xavier_uniform_(module.weight)
|
| 369 |
+
if module.bias is not None:
|
| 370 |
+
nn.init.constant_(module.bias, 0)
|
| 371 |
+
self.apply(_basic_init)
|
| 372 |
+
|
| 373 |
+
def forward(self, x: sp.SparseTensor, sample_posterior=False, return_raw=False):
|
| 374 |
+
h = self.input_layer(x)
|
| 375 |
+
h = h.type(self.dtype)
|
| 376 |
+
for i, res in enumerate(self.blocks):
|
| 377 |
+
for j, block in enumerate(res):
|
| 378 |
+
h = block(h)
|
| 379 |
+
h = h.type(x.dtype)
|
| 380 |
+
h = h.replace(F.layer_norm(h.feats, h.feats.shape[-1:]))
|
| 381 |
+
h = self.to_latent(h)
|
| 382 |
+
|
| 383 |
+
# Sample from the posterior distribution
|
| 384 |
+
mean, logvar = h.feats.chunk(2, dim=-1)
|
| 385 |
+
if sample_posterior:
|
| 386 |
+
std = torch.exp(0.5 * logvar)
|
| 387 |
+
z = mean + std * torch.randn_like(std)
|
| 388 |
+
else:
|
| 389 |
+
z = mean
|
| 390 |
+
z = h.replace(z)
|
| 391 |
+
|
| 392 |
+
if return_raw:
|
| 393 |
+
return z, mean, logvar
|
| 394 |
+
else:
|
| 395 |
+
return z
|
| 396 |
+
|
| 397 |
+
|
| 398 |
+
class SparseUnetVaeDecoder(nn.Module):
|
| 399 |
+
"""
|
| 400 |
+
Sparse Swin Transformer Unet VAE model.
|
| 401 |
+
"""
|
| 402 |
+
def __init__(
|
| 403 |
+
self,
|
| 404 |
+
out_channels: int,
|
| 405 |
+
model_channels: List[int],
|
| 406 |
+
latent_channels: int,
|
| 407 |
+
num_blocks: List[int],
|
| 408 |
+
block_type: List[str],
|
| 409 |
+
up_block_type: List[str],
|
| 410 |
+
block_args: List[Dict[str, Any]],
|
| 411 |
+
use_fp16: bool = False,
|
| 412 |
+
pred_subdiv: bool = True,
|
| 413 |
+
):
|
| 414 |
+
super().__init__()
|
| 415 |
+
self.out_channels = out_channels
|
| 416 |
+
self.model_channels = model_channels
|
| 417 |
+
self.num_blocks = num_blocks
|
| 418 |
+
self.use_fp16 = use_fp16
|
| 419 |
+
self.pred_subdiv = pred_subdiv
|
| 420 |
+
self.dtype = torch.float16 if use_fp16 else torch.float32
|
| 421 |
+
self.low_vram = False
|
| 422 |
+
|
| 423 |
+
self.output_layer = sp.SparseLinear(model_channels[-1], out_channels)
|
| 424 |
+
self.from_latent = sp.SparseLinear(latent_channels, model_channels[0])
|
| 425 |
+
|
| 426 |
+
self.blocks = nn.ModuleList([])
|
| 427 |
+
for i in range(len(num_blocks)):
|
| 428 |
+
self.blocks.append(nn.ModuleList([]))
|
| 429 |
+
for j in range(num_blocks[i]):
|
| 430 |
+
self.blocks[-1].append(
|
| 431 |
+
globals()[block_type[i]](
|
| 432 |
+
model_channels[i],
|
| 433 |
+
**block_args[i],
|
| 434 |
+
)
|
| 435 |
+
)
|
| 436 |
+
if i < len(num_blocks) - 1:
|
| 437 |
+
self.blocks[-1].append(
|
| 438 |
+
globals()[up_block_type[i]](
|
| 439 |
+
model_channels[i],
|
| 440 |
+
model_channels[i+1],
|
| 441 |
+
pred_subdiv=pred_subdiv,
|
| 442 |
+
**block_args[i],
|
| 443 |
+
)
|
| 444 |
+
)
|
| 445 |
+
|
| 446 |
+
self.initialize_weights()
|
| 447 |
+
if use_fp16:
|
| 448 |
+
self.convert_to_fp16()
|
| 449 |
+
|
| 450 |
+
@property
|
| 451 |
+
def device(self) -> torch.device:
|
| 452 |
+
"""
|
| 453 |
+
Return the device of the model.
|
| 454 |
+
"""
|
| 455 |
+
return next(self.parameters()).device
|
| 456 |
+
|
| 457 |
+
def convert_to_fp16(self) -> None:
|
| 458 |
+
"""
|
| 459 |
+
Convert the torso of the model to float16.
|
| 460 |
+
"""
|
| 461 |
+
self.blocks.apply(convert_module_to_f16)
|
| 462 |
+
|
| 463 |
+
def convert_to_fp32(self) -> None:
|
| 464 |
+
"""
|
| 465 |
+
Convert the torso of the model to float32.
|
| 466 |
+
"""
|
| 467 |
+
self.blocks.apply(convert_module_to_f32)
|
| 468 |
+
|
| 469 |
+
def initialize_weights(self) -> None:
|
| 470 |
+
# Initialize transformer layers:
|
| 471 |
+
def _basic_init(module):
|
| 472 |
+
if isinstance(module, nn.Linear):
|
| 473 |
+
torch.nn.init.xavier_uniform_(module.weight)
|
| 474 |
+
if module.bias is not None:
|
| 475 |
+
nn.init.constant_(module.bias, 0)
|
| 476 |
+
self.apply(_basic_init)
|
| 477 |
+
|
| 478 |
+
def forward(self, x: sp.SparseTensor, guide_subs: Optional[List[sp.SparseTensor]] = None, return_subs: bool = False) -> sp.SparseTensor:
|
| 479 |
+
assert guide_subs is None or self.pred_subdiv == False, "Only decoders with pred_subdiv=False can be used with guide_subs"
|
| 480 |
+
assert return_subs == False or self.pred_subdiv == True, "Only decoders with pred_subdiv=True can be used with return_subs"
|
| 481 |
+
|
| 482 |
+
h = self.from_latent(x)
|
| 483 |
+
h = h.type(self.dtype)
|
| 484 |
+
subs_gt = []
|
| 485 |
+
subs = []
|
| 486 |
+
for i, res in enumerate(self.blocks):
|
| 487 |
+
for j, block in enumerate(res):
|
| 488 |
+
if i < len(self.blocks) - 1 and j == len(res) - 1:
|
| 489 |
+
if self.pred_subdiv:
|
| 490 |
+
if self.training:
|
| 491 |
+
subs_gt.append(h.get_spatial_cache('subdivision'))
|
| 492 |
+
h, sub = block(h)
|
| 493 |
+
subs.append(sub)
|
| 494 |
+
else:
|
| 495 |
+
h = block(h, subdiv=guide_subs[i] if guide_subs is not None else None)
|
| 496 |
+
else:
|
| 497 |
+
h = block(h)
|
| 498 |
+
h = h.type(x.dtype)
|
| 499 |
+
h = h.replace(F.layer_norm(h.feats, h.feats.shape[-1:]))
|
| 500 |
+
h = self.output_layer(h)
|
| 501 |
+
if self.training and self.pred_subdiv:
|
| 502 |
+
return h, subs_gt, subs
|
| 503 |
+
else:
|
| 504 |
+
if return_subs:
|
| 505 |
+
return h, subs
|
| 506 |
+
else:
|
| 507 |
+
return h
|
| 508 |
+
|
| 509 |
+
def upsample(self, x: sp.SparseTensor, upsample_times: int) -> torch.Tensor:
|
| 510 |
+
assert self.pred_subdiv == True, "Only decoders with pred_subdiv=True can be used with upsampling"
|
| 511 |
+
|
| 512 |
+
h = self.from_latent(x)
|
| 513 |
+
h = h.type(self.dtype)
|
| 514 |
+
for i, res in enumerate(self.blocks):
|
| 515 |
+
if i == upsample_times:
|
| 516 |
+
return h.coords
|
| 517 |
+
for j, block in enumerate(res):
|
| 518 |
+
if i < len(self.blocks) - 1 and j == len(res) - 1:
|
| 519 |
+
h, sub = block(h)
|
| 520 |
+
else:
|
| 521 |
+
h = block(h)
|
| 522 |
+
|
TRELLIS.2/trellis2/models/sparse_elastic_mixin.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from contextlib import contextmanager
|
| 2 |
+
from typing import *
|
| 3 |
+
import math
|
| 4 |
+
from ..modules import sparse as sp
|
| 5 |
+
from ..utils.elastic_utils import ElasticModuleMixin
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
class SparseTransformerElasticMixin(ElasticModuleMixin):
|
| 9 |
+
def _get_input_size(self, x: sp.SparseTensor, *args, **kwargs):
|
| 10 |
+
return x.feats.shape[0]
|
| 11 |
+
|
| 12 |
+
@contextmanager
|
| 13 |
+
def with_mem_ratio(self, mem_ratio=1.0):
|
| 14 |
+
if mem_ratio == 1.0:
|
| 15 |
+
yield 1.0
|
| 16 |
+
return
|
| 17 |
+
num_blocks = len(self.blocks)
|
| 18 |
+
num_checkpoint_blocks = min(math.ceil((1 - mem_ratio) * num_blocks) + 1, num_blocks)
|
| 19 |
+
exact_mem_ratio = 1 - (num_checkpoint_blocks - 1) / num_blocks
|
| 20 |
+
for i in range(num_blocks):
|
| 21 |
+
self.blocks[i].use_checkpoint = i < num_checkpoint_blocks
|
| 22 |
+
yield exact_mem_ratio
|
| 23 |
+
for i in range(num_blocks):
|
| 24 |
+
self.blocks[i].use_checkpoint = False
|
TRELLIS.2/trellis2/models/sparse_structure_flow.py
ADDED
|
@@ -0,0 +1,247 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import *
|
| 2 |
+
from functools import partial
|
| 3 |
+
import torch
|
| 4 |
+
import torch.nn as nn
|
| 5 |
+
import torch.nn.functional as F
|
| 6 |
+
import numpy as np
|
| 7 |
+
from ..modules.utils import convert_module_to, manual_cast, str_to_dtype
|
| 8 |
+
from ..modules.transformer import AbsolutePositionEmbedder, ModulatedTransformerCrossBlock
|
| 9 |
+
from ..modules.attention import RotaryPositionEmbedder
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
class TimestepEmbedder(nn.Module):
|
| 13 |
+
"""
|
| 14 |
+
Embeds scalar timesteps into vector representations.
|
| 15 |
+
"""
|
| 16 |
+
def __init__(self, hidden_size, frequency_embedding_size=256):
|
| 17 |
+
super().__init__()
|
| 18 |
+
self.mlp = nn.Sequential(
|
| 19 |
+
nn.Linear(frequency_embedding_size, hidden_size, bias=True),
|
| 20 |
+
nn.SiLU(),
|
| 21 |
+
nn.Linear(hidden_size, hidden_size, bias=True),
|
| 22 |
+
)
|
| 23 |
+
self.frequency_embedding_size = frequency_embedding_size
|
| 24 |
+
|
| 25 |
+
@staticmethod
|
| 26 |
+
def timestep_embedding(t, dim, max_period=10000):
|
| 27 |
+
"""
|
| 28 |
+
Create sinusoidal timestep embeddings.
|
| 29 |
+
|
| 30 |
+
Args:
|
| 31 |
+
t: a 1-D Tensor of N indices, one per batch element.
|
| 32 |
+
These may be fractional.
|
| 33 |
+
dim: the dimension of the output.
|
| 34 |
+
max_period: controls the minimum frequency of the embeddings.
|
| 35 |
+
|
| 36 |
+
Returns:
|
| 37 |
+
an (N, D) Tensor of positional embeddings.
|
| 38 |
+
"""
|
| 39 |
+
# https://github.com/openai/glide-text2im/blob/main/glide_text2im/nn.py
|
| 40 |
+
half = dim // 2
|
| 41 |
+
freqs = torch.exp(
|
| 42 |
+
-np.log(max_period) * torch.arange(start=0, end=half, dtype=torch.float32) / half
|
| 43 |
+
).to(device=t.device)
|
| 44 |
+
args = t[:, None].float() * freqs[None]
|
| 45 |
+
embedding = torch.cat([torch.cos(args), torch.sin(args)], dim=-1)
|
| 46 |
+
if dim % 2:
|
| 47 |
+
embedding = torch.cat([embedding, torch.zeros_like(embedding[:, :1])], dim=-1)
|
| 48 |
+
return embedding
|
| 49 |
+
|
| 50 |
+
def forward(self, t):
|
| 51 |
+
t_freq = self.timestep_embedding(t, self.frequency_embedding_size)
|
| 52 |
+
t_emb = self.mlp(t_freq)
|
| 53 |
+
return t_emb
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
class SparseStructureFlowModel(nn.Module):
|
| 57 |
+
def __init__(
|
| 58 |
+
self,
|
| 59 |
+
resolution: int,
|
| 60 |
+
in_channels: int,
|
| 61 |
+
model_channels: int,
|
| 62 |
+
cond_channels: int,
|
| 63 |
+
out_channels: int,
|
| 64 |
+
num_blocks: int,
|
| 65 |
+
num_heads: Optional[int] = None,
|
| 66 |
+
num_head_channels: Optional[int] = 64,
|
| 67 |
+
mlp_ratio: float = 4,
|
| 68 |
+
pe_mode: Literal["ape", "rope"] = "ape",
|
| 69 |
+
rope_freq: Tuple[float, float] = (1.0, 10000.0),
|
| 70 |
+
dtype: str = 'float32',
|
| 71 |
+
use_checkpoint: bool = False,
|
| 72 |
+
share_mod: bool = False,
|
| 73 |
+
initialization: str = 'vanilla',
|
| 74 |
+
qk_rms_norm: bool = False,
|
| 75 |
+
qk_rms_norm_cross: bool = False,
|
| 76 |
+
**kwargs
|
| 77 |
+
):
|
| 78 |
+
super().__init__()
|
| 79 |
+
self.resolution = resolution
|
| 80 |
+
self.in_channels = in_channels
|
| 81 |
+
self.model_channels = model_channels
|
| 82 |
+
self.cond_channels = cond_channels
|
| 83 |
+
self.out_channels = out_channels
|
| 84 |
+
self.num_blocks = num_blocks
|
| 85 |
+
self.num_heads = num_heads or model_channels // num_head_channels
|
| 86 |
+
self.mlp_ratio = mlp_ratio
|
| 87 |
+
self.pe_mode = pe_mode
|
| 88 |
+
self.use_checkpoint = use_checkpoint
|
| 89 |
+
self.share_mod = share_mod
|
| 90 |
+
self.initialization = initialization
|
| 91 |
+
self.qk_rms_norm = qk_rms_norm
|
| 92 |
+
self.qk_rms_norm_cross = qk_rms_norm_cross
|
| 93 |
+
self.dtype = str_to_dtype(dtype)
|
| 94 |
+
|
| 95 |
+
self.t_embedder = TimestepEmbedder(model_channels)
|
| 96 |
+
if share_mod:
|
| 97 |
+
self.adaLN_modulation = nn.Sequential(
|
| 98 |
+
nn.SiLU(),
|
| 99 |
+
nn.Linear(model_channels, 6 * model_channels, bias=True)
|
| 100 |
+
)
|
| 101 |
+
|
| 102 |
+
if pe_mode == "ape":
|
| 103 |
+
pos_embedder = AbsolutePositionEmbedder(model_channels, 3)
|
| 104 |
+
coords = torch.meshgrid(*[torch.arange(res, device=self.device) for res in [resolution] * 3], indexing='ij')
|
| 105 |
+
coords = torch.stack(coords, dim=-1).reshape(-1, 3)
|
| 106 |
+
pos_emb = pos_embedder(coords)
|
| 107 |
+
self.register_buffer("pos_emb", pos_emb)
|
| 108 |
+
elif pe_mode == "rope":
|
| 109 |
+
pos_embedder = RotaryPositionEmbedder(self.model_channels // self.num_heads, 3)
|
| 110 |
+
coords = torch.meshgrid(*[torch.arange(res, device=self.device) for res in [resolution] * 3], indexing='ij')
|
| 111 |
+
coords = torch.stack(coords, dim=-1).reshape(-1, 3)
|
| 112 |
+
rope_phases = pos_embedder(coords)
|
| 113 |
+
self.register_buffer("rope_phases", rope_phases)
|
| 114 |
+
|
| 115 |
+
if pe_mode != "rope":
|
| 116 |
+
self.rope_phases = None
|
| 117 |
+
|
| 118 |
+
self.input_layer = nn.Linear(in_channels, model_channels)
|
| 119 |
+
|
| 120 |
+
self.blocks = nn.ModuleList([
|
| 121 |
+
ModulatedTransformerCrossBlock(
|
| 122 |
+
model_channels,
|
| 123 |
+
cond_channels,
|
| 124 |
+
num_heads=self.num_heads,
|
| 125 |
+
mlp_ratio=self.mlp_ratio,
|
| 126 |
+
attn_mode='full',
|
| 127 |
+
use_checkpoint=self.use_checkpoint,
|
| 128 |
+
use_rope=(pe_mode == "rope"),
|
| 129 |
+
rope_freq=rope_freq,
|
| 130 |
+
share_mod=share_mod,
|
| 131 |
+
qk_rms_norm=self.qk_rms_norm,
|
| 132 |
+
qk_rms_norm_cross=self.qk_rms_norm_cross,
|
| 133 |
+
)
|
| 134 |
+
for _ in range(num_blocks)
|
| 135 |
+
])
|
| 136 |
+
|
| 137 |
+
self.out_layer = nn.Linear(model_channels, out_channels)
|
| 138 |
+
|
| 139 |
+
self.initialize_weights()
|
| 140 |
+
self.convert_to(self.dtype)
|
| 141 |
+
|
| 142 |
+
@property
|
| 143 |
+
def device(self) -> torch.device:
|
| 144 |
+
"""
|
| 145 |
+
Return the device of the model.
|
| 146 |
+
"""
|
| 147 |
+
return next(self.parameters()).device
|
| 148 |
+
|
| 149 |
+
def convert_to(self, dtype: torch.dtype) -> None:
|
| 150 |
+
"""
|
| 151 |
+
Convert the torso of the model to the specified dtype.
|
| 152 |
+
"""
|
| 153 |
+
self.dtype = dtype
|
| 154 |
+
self.blocks.apply(partial(convert_module_to, dtype=dtype))
|
| 155 |
+
|
| 156 |
+
def initialize_weights(self) -> None:
|
| 157 |
+
if self.initialization == 'vanilla':
|
| 158 |
+
# Initialize transformer layers:
|
| 159 |
+
def _basic_init(module):
|
| 160 |
+
if isinstance(module, nn.Linear):
|
| 161 |
+
torch.nn.init.xavier_uniform_(module.weight)
|
| 162 |
+
if module.bias is not None:
|
| 163 |
+
nn.init.constant_(module.bias, 0)
|
| 164 |
+
self.apply(_basic_init)
|
| 165 |
+
|
| 166 |
+
# Initialize timestep embedding MLP:
|
| 167 |
+
nn.init.normal_(self.t_embedder.mlp[0].weight, std=0.02)
|
| 168 |
+
nn.init.normal_(self.t_embedder.mlp[2].weight, std=0.02)
|
| 169 |
+
|
| 170 |
+
# Zero-out adaLN modulation layers in DiT blocks:
|
| 171 |
+
if self.share_mod:
|
| 172 |
+
nn.init.constant_(self.adaLN_modulation[-1].weight, 0)
|
| 173 |
+
nn.init.constant_(self.adaLN_modulation[-1].bias, 0)
|
| 174 |
+
else:
|
| 175 |
+
for block in self.blocks:
|
| 176 |
+
nn.init.constant_(block.adaLN_modulation[-1].weight, 0)
|
| 177 |
+
nn.init.constant_(block.adaLN_modulation[-1].bias, 0)
|
| 178 |
+
|
| 179 |
+
# Zero-out output layers:
|
| 180 |
+
nn.init.constant_(self.out_layer.weight, 0)
|
| 181 |
+
nn.init.constant_(self.out_layer.bias, 0)
|
| 182 |
+
|
| 183 |
+
elif self.initialization == 'scaled':
|
| 184 |
+
# Initialize transformer layers:
|
| 185 |
+
def _basic_init(module):
|
| 186 |
+
if isinstance(module, nn.Linear):
|
| 187 |
+
torch.nn.init.normal_(module.weight, std=np.sqrt(2.0 / (5.0 * self.model_channels)))
|
| 188 |
+
if module.bias is not None:
|
| 189 |
+
nn.init.constant_(module.bias, 0)
|
| 190 |
+
self.apply(_basic_init)
|
| 191 |
+
|
| 192 |
+
# Scaled init for to_out and ffn2
|
| 193 |
+
def _scaled_init(module):
|
| 194 |
+
if isinstance(module, nn.Linear):
|
| 195 |
+
torch.nn.init.normal_(module.weight, std=1.0 / np.sqrt(5 * self.num_blocks * self.model_channels))
|
| 196 |
+
if module.bias is not None:
|
| 197 |
+
nn.init.constant_(module.bias, 0)
|
| 198 |
+
for block in self.blocks:
|
| 199 |
+
block.self_attn.to_out.apply(_scaled_init)
|
| 200 |
+
block.cross_attn.to_out.apply(_scaled_init)
|
| 201 |
+
block.mlp.mlp[2].apply(_scaled_init)
|
| 202 |
+
|
| 203 |
+
# Initialize input layer to make the initial representation have variance 1
|
| 204 |
+
nn.init.normal_(self.input_layer.weight, std=1.0 / np.sqrt(self.in_channels))
|
| 205 |
+
nn.init.zeros_(self.input_layer.bias)
|
| 206 |
+
|
| 207 |
+
# Initialize timestep embedding MLP:
|
| 208 |
+
nn.init.normal_(self.t_embedder.mlp[0].weight, std=0.02)
|
| 209 |
+
nn.init.normal_(self.t_embedder.mlp[2].weight, std=0.02)
|
| 210 |
+
|
| 211 |
+
# Zero-out adaLN modulation layers in DiT blocks:
|
| 212 |
+
if self.share_mod:
|
| 213 |
+
nn.init.constant_(self.adaLN_modulation[-1].weight, 0)
|
| 214 |
+
nn.init.constant_(self.adaLN_modulation[-1].bias, 0)
|
| 215 |
+
else:
|
| 216 |
+
for block in self.blocks:
|
| 217 |
+
nn.init.constant_(block.adaLN_modulation[-1].weight, 0)
|
| 218 |
+
nn.init.constant_(block.adaLN_modulation[-1].bias, 0)
|
| 219 |
+
|
| 220 |
+
# Zero-out output layers:
|
| 221 |
+
nn.init.constant_(self.out_layer.weight, 0)
|
| 222 |
+
nn.init.constant_(self.out_layer.bias, 0)
|
| 223 |
+
|
| 224 |
+
def forward(self, x: torch.Tensor, t: torch.Tensor, cond: torch.Tensor) -> torch.Tensor:
|
| 225 |
+
assert [*x.shape] == [x.shape[0], self.in_channels, *[self.resolution] * 3], \
|
| 226 |
+
f"Input shape mismatch, got {x.shape}, expected {[x.shape[0], self.in_channels, *[self.resolution] * 3]}"
|
| 227 |
+
|
| 228 |
+
h = x.view(*x.shape[:2], -1).permute(0, 2, 1).contiguous()
|
| 229 |
+
|
| 230 |
+
h = self.input_layer(h)
|
| 231 |
+
if self.pe_mode == "ape":
|
| 232 |
+
h = h + self.pos_emb[None]
|
| 233 |
+
t_emb = self.t_embedder(t)
|
| 234 |
+
if self.share_mod:
|
| 235 |
+
t_emb = self.adaLN_modulation(t_emb)
|
| 236 |
+
t_emb = manual_cast(t_emb, self.dtype)
|
| 237 |
+
h = manual_cast(h, self.dtype)
|
| 238 |
+
cond = manual_cast(cond, self.dtype)
|
| 239 |
+
for block in self.blocks:
|
| 240 |
+
h = block(h, t_emb, cond, self.rope_phases)
|
| 241 |
+
h = manual_cast(h, x.dtype)
|
| 242 |
+
h = F.layer_norm(h, h.shape[-1:])
|
| 243 |
+
h = self.out_layer(h)
|
| 244 |
+
|
| 245 |
+
h = h.permute(0, 2, 1).view(h.shape[0], h.shape[2], *[self.resolution] * 3).contiguous()
|
| 246 |
+
|
| 247 |
+
return h
|
TRELLIS.2/trellis2/models/sparse_structure_vae.py
ADDED
|
@@ -0,0 +1,306 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import *
|
| 2 |
+
import torch
|
| 3 |
+
import torch.nn as nn
|
| 4 |
+
import torch.nn.functional as F
|
| 5 |
+
from ..modules.norm import GroupNorm32, ChannelLayerNorm32
|
| 6 |
+
from ..modules.spatial import pixel_shuffle_3d
|
| 7 |
+
from ..modules.utils import zero_module, convert_module_to_f16, convert_module_to_f32
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
def norm_layer(norm_type: str, *args, **kwargs) -> nn.Module:
|
| 11 |
+
"""
|
| 12 |
+
Return a normalization layer.
|
| 13 |
+
"""
|
| 14 |
+
if norm_type == "group":
|
| 15 |
+
return GroupNorm32(32, *args, **kwargs)
|
| 16 |
+
elif norm_type == "layer":
|
| 17 |
+
return ChannelLayerNorm32(*args, **kwargs)
|
| 18 |
+
else:
|
| 19 |
+
raise ValueError(f"Invalid norm type {norm_type}")
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
class ResBlock3d(nn.Module):
|
| 23 |
+
def __init__(
|
| 24 |
+
self,
|
| 25 |
+
channels: int,
|
| 26 |
+
out_channels: Optional[int] = None,
|
| 27 |
+
norm_type: Literal["group", "layer"] = "layer",
|
| 28 |
+
):
|
| 29 |
+
super().__init__()
|
| 30 |
+
self.channels = channels
|
| 31 |
+
self.out_channels = out_channels or channels
|
| 32 |
+
|
| 33 |
+
self.norm1 = norm_layer(norm_type, channels)
|
| 34 |
+
self.norm2 = norm_layer(norm_type, self.out_channels)
|
| 35 |
+
self.conv1 = nn.Conv3d(channels, self.out_channels, 3, padding=1)
|
| 36 |
+
self.conv2 = zero_module(nn.Conv3d(self.out_channels, self.out_channels, 3, padding=1))
|
| 37 |
+
self.skip_connection = nn.Conv3d(channels, self.out_channels, 1) if channels != self.out_channels else nn.Identity()
|
| 38 |
+
|
| 39 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 40 |
+
h = self.norm1(x)
|
| 41 |
+
h = F.silu(h)
|
| 42 |
+
h = self.conv1(h)
|
| 43 |
+
h = self.norm2(h)
|
| 44 |
+
h = F.silu(h)
|
| 45 |
+
h = self.conv2(h)
|
| 46 |
+
h = h + self.skip_connection(x)
|
| 47 |
+
return h
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
class DownsampleBlock3d(nn.Module):
|
| 51 |
+
def __init__(
|
| 52 |
+
self,
|
| 53 |
+
in_channels: int,
|
| 54 |
+
out_channels: int,
|
| 55 |
+
mode: Literal["conv", "avgpool"] = "conv",
|
| 56 |
+
):
|
| 57 |
+
assert mode in ["conv", "avgpool"], f"Invalid mode {mode}"
|
| 58 |
+
|
| 59 |
+
super().__init__()
|
| 60 |
+
self.in_channels = in_channels
|
| 61 |
+
self.out_channels = out_channels
|
| 62 |
+
|
| 63 |
+
if mode == "conv":
|
| 64 |
+
self.conv = nn.Conv3d(in_channels, out_channels, 2, stride=2)
|
| 65 |
+
elif mode == "avgpool":
|
| 66 |
+
assert in_channels == out_channels, "Pooling mode requires in_channels to be equal to out_channels"
|
| 67 |
+
|
| 68 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 69 |
+
if hasattr(self, "conv"):
|
| 70 |
+
return self.conv(x)
|
| 71 |
+
else:
|
| 72 |
+
return F.avg_pool3d(x, 2)
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
class UpsampleBlock3d(nn.Module):
|
| 76 |
+
def __init__(
|
| 77 |
+
self,
|
| 78 |
+
in_channels: int,
|
| 79 |
+
out_channels: int,
|
| 80 |
+
mode: Literal["conv", "nearest"] = "conv",
|
| 81 |
+
):
|
| 82 |
+
assert mode in ["conv", "nearest"], f"Invalid mode {mode}"
|
| 83 |
+
|
| 84 |
+
super().__init__()
|
| 85 |
+
self.in_channels = in_channels
|
| 86 |
+
self.out_channels = out_channels
|
| 87 |
+
|
| 88 |
+
if mode == "conv":
|
| 89 |
+
self.conv = nn.Conv3d(in_channels, out_channels*8, 3, padding=1)
|
| 90 |
+
elif mode == "nearest":
|
| 91 |
+
assert in_channels == out_channels, "Nearest mode requires in_channels to be equal to out_channels"
|
| 92 |
+
|
| 93 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 94 |
+
if hasattr(self, "conv"):
|
| 95 |
+
x = self.conv(x)
|
| 96 |
+
return pixel_shuffle_3d(x, 2)
|
| 97 |
+
else:
|
| 98 |
+
return F.interpolate(x, scale_factor=2, mode="nearest")
|
| 99 |
+
|
| 100 |
+
|
| 101 |
+
class SparseStructureEncoder(nn.Module):
|
| 102 |
+
"""
|
| 103 |
+
Encoder for Sparse Structure (\mathcal{E}_S in the paper Sec. 3.3).
|
| 104 |
+
|
| 105 |
+
Args:
|
| 106 |
+
in_channels (int): Channels of the input.
|
| 107 |
+
latent_channels (int): Channels of the latent representation.
|
| 108 |
+
num_res_blocks (int): Number of residual blocks at each resolution.
|
| 109 |
+
channels (List[int]): Channels of the encoder blocks.
|
| 110 |
+
num_res_blocks_middle (int): Number of residual blocks in the middle.
|
| 111 |
+
norm_type (Literal["group", "layer"]): Type of normalization layer.
|
| 112 |
+
use_fp16 (bool): Whether to use FP16.
|
| 113 |
+
"""
|
| 114 |
+
def __init__(
|
| 115 |
+
self,
|
| 116 |
+
in_channels: int,
|
| 117 |
+
latent_channels: int,
|
| 118 |
+
num_res_blocks: int,
|
| 119 |
+
channels: List[int],
|
| 120 |
+
num_res_blocks_middle: int = 2,
|
| 121 |
+
norm_type: Literal["group", "layer"] = "layer",
|
| 122 |
+
use_fp16: bool = False,
|
| 123 |
+
):
|
| 124 |
+
super().__init__()
|
| 125 |
+
self.in_channels = in_channels
|
| 126 |
+
self.latent_channels = latent_channels
|
| 127 |
+
self.num_res_blocks = num_res_blocks
|
| 128 |
+
self.channels = channels
|
| 129 |
+
self.num_res_blocks_middle = num_res_blocks_middle
|
| 130 |
+
self.norm_type = norm_type
|
| 131 |
+
self.use_fp16 = use_fp16
|
| 132 |
+
self.dtype = torch.float16 if use_fp16 else torch.float32
|
| 133 |
+
|
| 134 |
+
self.input_layer = nn.Conv3d(in_channels, channels[0], 3, padding=1)
|
| 135 |
+
|
| 136 |
+
self.blocks = nn.ModuleList([])
|
| 137 |
+
for i, ch in enumerate(channels):
|
| 138 |
+
self.blocks.extend([
|
| 139 |
+
ResBlock3d(ch, ch)
|
| 140 |
+
for _ in range(num_res_blocks)
|
| 141 |
+
])
|
| 142 |
+
if i < len(channels) - 1:
|
| 143 |
+
self.blocks.append(
|
| 144 |
+
DownsampleBlock3d(ch, channels[i+1])
|
| 145 |
+
)
|
| 146 |
+
|
| 147 |
+
self.middle_block = nn.Sequential(*[
|
| 148 |
+
ResBlock3d(channels[-1], channels[-1])
|
| 149 |
+
for _ in range(num_res_blocks_middle)
|
| 150 |
+
])
|
| 151 |
+
|
| 152 |
+
self.out_layer = nn.Sequential(
|
| 153 |
+
norm_layer(norm_type, channels[-1]),
|
| 154 |
+
nn.SiLU(),
|
| 155 |
+
nn.Conv3d(channels[-1], latent_channels*2, 3, padding=1)
|
| 156 |
+
)
|
| 157 |
+
|
| 158 |
+
if use_fp16:
|
| 159 |
+
self.convert_to_fp16()
|
| 160 |
+
|
| 161 |
+
@property
|
| 162 |
+
def device(self) -> torch.device:
|
| 163 |
+
"""
|
| 164 |
+
Return the device of the model.
|
| 165 |
+
"""
|
| 166 |
+
return next(self.parameters()).device
|
| 167 |
+
|
| 168 |
+
def convert_to_fp16(self) -> None:
|
| 169 |
+
"""
|
| 170 |
+
Convert the torso of the model to float16.
|
| 171 |
+
"""
|
| 172 |
+
self.use_fp16 = True
|
| 173 |
+
self.dtype = torch.float16
|
| 174 |
+
self.blocks.apply(convert_module_to_f16)
|
| 175 |
+
self.middle_block.apply(convert_module_to_f16)
|
| 176 |
+
|
| 177 |
+
def convert_to_fp32(self) -> None:
|
| 178 |
+
"""
|
| 179 |
+
Convert the torso of the model to float32.
|
| 180 |
+
"""
|
| 181 |
+
self.use_fp16 = False
|
| 182 |
+
self.dtype = torch.float32
|
| 183 |
+
self.blocks.apply(convert_module_to_f32)
|
| 184 |
+
self.middle_block.apply(convert_module_to_f32)
|
| 185 |
+
|
| 186 |
+
def forward(self, x: torch.Tensor, sample_posterior: bool = False, return_raw: bool = False) -> torch.Tensor:
|
| 187 |
+
h = self.input_layer(x)
|
| 188 |
+
h = h.type(self.dtype)
|
| 189 |
+
|
| 190 |
+
for block in self.blocks:
|
| 191 |
+
h = block(h)
|
| 192 |
+
h = self.middle_block(h)
|
| 193 |
+
|
| 194 |
+
h = h.type(x.dtype)
|
| 195 |
+
h = self.out_layer(h)
|
| 196 |
+
|
| 197 |
+
mean, logvar = h.chunk(2, dim=1)
|
| 198 |
+
|
| 199 |
+
if sample_posterior:
|
| 200 |
+
std = torch.exp(0.5 * logvar)
|
| 201 |
+
z = mean + std * torch.randn_like(std)
|
| 202 |
+
else:
|
| 203 |
+
z = mean
|
| 204 |
+
|
| 205 |
+
if return_raw:
|
| 206 |
+
return z, mean, logvar
|
| 207 |
+
return z
|
| 208 |
+
|
| 209 |
+
|
| 210 |
+
class SparseStructureDecoder(nn.Module):
|
| 211 |
+
"""
|
| 212 |
+
Decoder for Sparse Structure (\mathcal{D}_S in the paper Sec. 3.3).
|
| 213 |
+
|
| 214 |
+
Args:
|
| 215 |
+
out_channels (int): Channels of the output.
|
| 216 |
+
latent_channels (int): Channels of the latent representation.
|
| 217 |
+
num_res_blocks (int): Number of residual blocks at each resolution.
|
| 218 |
+
channels (List[int]): Channels of the decoder blocks.
|
| 219 |
+
num_res_blocks_middle (int): Number of residual blocks in the middle.
|
| 220 |
+
norm_type (Literal["group", "layer"]): Type of normalization layer.
|
| 221 |
+
use_fp16 (bool): Whether to use FP16.
|
| 222 |
+
"""
|
| 223 |
+
def __init__(
|
| 224 |
+
self,
|
| 225 |
+
out_channels: int,
|
| 226 |
+
latent_channels: int,
|
| 227 |
+
num_res_blocks: int,
|
| 228 |
+
channels: List[int],
|
| 229 |
+
num_res_blocks_middle: int = 2,
|
| 230 |
+
norm_type: Literal["group", "layer"] = "layer",
|
| 231 |
+
use_fp16: bool = False,
|
| 232 |
+
):
|
| 233 |
+
super().__init__()
|
| 234 |
+
self.out_channels = out_channels
|
| 235 |
+
self.latent_channels = latent_channels
|
| 236 |
+
self.num_res_blocks = num_res_blocks
|
| 237 |
+
self.channels = channels
|
| 238 |
+
self.num_res_blocks_middle = num_res_blocks_middle
|
| 239 |
+
self.norm_type = norm_type
|
| 240 |
+
self.use_fp16 = use_fp16
|
| 241 |
+
self.dtype = torch.float16 if use_fp16 else torch.float32
|
| 242 |
+
|
| 243 |
+
self.input_layer = nn.Conv3d(latent_channels, channels[0], 3, padding=1)
|
| 244 |
+
|
| 245 |
+
self.middle_block = nn.Sequential(*[
|
| 246 |
+
ResBlock3d(channels[0], channels[0])
|
| 247 |
+
for _ in range(num_res_blocks_middle)
|
| 248 |
+
])
|
| 249 |
+
|
| 250 |
+
self.blocks = nn.ModuleList([])
|
| 251 |
+
for i, ch in enumerate(channels):
|
| 252 |
+
self.blocks.extend([
|
| 253 |
+
ResBlock3d(ch, ch)
|
| 254 |
+
for _ in range(num_res_blocks)
|
| 255 |
+
])
|
| 256 |
+
if i < len(channels) - 1:
|
| 257 |
+
self.blocks.append(
|
| 258 |
+
UpsampleBlock3d(ch, channels[i+1])
|
| 259 |
+
)
|
| 260 |
+
|
| 261 |
+
self.out_layer = nn.Sequential(
|
| 262 |
+
norm_layer(norm_type, channels[-1]),
|
| 263 |
+
nn.SiLU(),
|
| 264 |
+
nn.Conv3d(channels[-1], out_channels, 3, padding=1)
|
| 265 |
+
)
|
| 266 |
+
|
| 267 |
+
if use_fp16:
|
| 268 |
+
self.convert_to_fp16()
|
| 269 |
+
|
| 270 |
+
@property
|
| 271 |
+
def device(self) -> torch.device:
|
| 272 |
+
"""
|
| 273 |
+
Return the device of the model.
|
| 274 |
+
"""
|
| 275 |
+
return next(self.parameters()).device
|
| 276 |
+
|
| 277 |
+
def convert_to_fp16(self) -> None:
|
| 278 |
+
"""
|
| 279 |
+
Convert the torso of the model to float16.
|
| 280 |
+
"""
|
| 281 |
+
self.use_fp16 = True
|
| 282 |
+
self.dtype = torch.float16
|
| 283 |
+
self.blocks.apply(convert_module_to_f16)
|
| 284 |
+
self.middle_block.apply(convert_module_to_f16)
|
| 285 |
+
|
| 286 |
+
def convert_to_fp32(self) -> None:
|
| 287 |
+
"""
|
| 288 |
+
Convert the torso of the model to float32.
|
| 289 |
+
"""
|
| 290 |
+
self.use_fp16 = False
|
| 291 |
+
self.dtype = torch.float32
|
| 292 |
+
self.blocks.apply(convert_module_to_f32)
|
| 293 |
+
self.middle_block.apply(convert_module_to_f32)
|
| 294 |
+
|
| 295 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 296 |
+
h = self.input_layer(x)
|
| 297 |
+
|
| 298 |
+
h = h.type(self.dtype)
|
| 299 |
+
|
| 300 |
+
h = self.middle_block(h)
|
| 301 |
+
for block in self.blocks:
|
| 302 |
+
h = block(h)
|
| 303 |
+
|
| 304 |
+
h = h.type(x.dtype)
|
| 305 |
+
h = self.out_layer(h)
|
| 306 |
+
return h
|
TRELLIS.2/trellis2/models/structured_latent_flow.py
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import *
|
| 2 |
+
from functools import partial
|
| 3 |
+
import torch
|
| 4 |
+
import torch.nn as nn
|
| 5 |
+
import torch.nn.functional as F
|
| 6 |
+
import numpy as np
|
| 7 |
+
from ..modules.utils import convert_module_to, manual_cast, str_to_dtype
|
| 8 |
+
from ..modules.transformer import AbsolutePositionEmbedder
|
| 9 |
+
from ..modules import sparse as sp
|
| 10 |
+
from ..modules.sparse.transformer import ModulatedSparseTransformerCrossBlock
|
| 11 |
+
from .sparse_structure_flow import TimestepEmbedder
|
| 12 |
+
from .sparse_elastic_mixin import SparseTransformerElasticMixin
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
class SLatFlowModel(nn.Module):
|
| 16 |
+
def __init__(
|
| 17 |
+
self,
|
| 18 |
+
resolution: int,
|
| 19 |
+
in_channels: int,
|
| 20 |
+
model_channels: int,
|
| 21 |
+
cond_channels: int,
|
| 22 |
+
out_channels: int,
|
| 23 |
+
num_blocks: int,
|
| 24 |
+
num_heads: Optional[int] = None,
|
| 25 |
+
num_head_channels: Optional[int] = 64,
|
| 26 |
+
mlp_ratio: float = 4,
|
| 27 |
+
pe_mode: Literal["ape", "rope"] = "ape",
|
| 28 |
+
rope_freq: Tuple[float, float] = (1.0, 10000.0),
|
| 29 |
+
dtype: str = 'float32',
|
| 30 |
+
use_checkpoint: bool = False,
|
| 31 |
+
share_mod: bool = False,
|
| 32 |
+
initialization: str = 'vanilla',
|
| 33 |
+
qk_rms_norm: bool = False,
|
| 34 |
+
qk_rms_norm_cross: bool = False,
|
| 35 |
+
):
|
| 36 |
+
super().__init__()
|
| 37 |
+
self.resolution = resolution
|
| 38 |
+
self.in_channels = in_channels
|
| 39 |
+
self.model_channels = model_channels
|
| 40 |
+
self.cond_channels = cond_channels
|
| 41 |
+
self.out_channels = out_channels
|
| 42 |
+
self.num_blocks = num_blocks
|
| 43 |
+
self.num_heads = num_heads or model_channels // num_head_channels
|
| 44 |
+
self.mlp_ratio = mlp_ratio
|
| 45 |
+
self.pe_mode = pe_mode
|
| 46 |
+
self.use_checkpoint = use_checkpoint
|
| 47 |
+
self.share_mod = share_mod
|
| 48 |
+
self.initialization = initialization
|
| 49 |
+
self.qk_rms_norm = qk_rms_norm
|
| 50 |
+
self.qk_rms_norm_cross = qk_rms_norm_cross
|
| 51 |
+
self.dtype = str_to_dtype(dtype)
|
| 52 |
+
|
| 53 |
+
self.t_embedder = TimestepEmbedder(model_channels)
|
| 54 |
+
if share_mod:
|
| 55 |
+
self.adaLN_modulation = nn.Sequential(
|
| 56 |
+
nn.SiLU(),
|
| 57 |
+
nn.Linear(model_channels, 6 * model_channels, bias=True)
|
| 58 |
+
)
|
| 59 |
+
|
| 60 |
+
if pe_mode == "ape":
|
| 61 |
+
self.pos_embedder = AbsolutePositionEmbedder(model_channels)
|
| 62 |
+
|
| 63 |
+
self.input_layer = sp.SparseLinear(in_channels, model_channels)
|
| 64 |
+
|
| 65 |
+
self.blocks = nn.ModuleList([
|
| 66 |
+
ModulatedSparseTransformerCrossBlock(
|
| 67 |
+
model_channels,
|
| 68 |
+
cond_channels,
|
| 69 |
+
num_heads=self.num_heads,
|
| 70 |
+
mlp_ratio=self.mlp_ratio,
|
| 71 |
+
attn_mode='full',
|
| 72 |
+
use_checkpoint=self.use_checkpoint,
|
| 73 |
+
use_rope=(pe_mode == "rope"),
|
| 74 |
+
rope_freq=rope_freq,
|
| 75 |
+
share_mod=self.share_mod,
|
| 76 |
+
qk_rms_norm=self.qk_rms_norm,
|
| 77 |
+
qk_rms_norm_cross=self.qk_rms_norm_cross,
|
| 78 |
+
)
|
| 79 |
+
for _ in range(num_blocks)
|
| 80 |
+
])
|
| 81 |
+
|
| 82 |
+
self.out_layer = sp.SparseLinear(model_channels, out_channels)
|
| 83 |
+
|
| 84 |
+
self.initialize_weights()
|
| 85 |
+
self.convert_to(self.dtype)
|
| 86 |
+
|
| 87 |
+
@property
|
| 88 |
+
def device(self) -> torch.device:
|
| 89 |
+
"""
|
| 90 |
+
Return the device of the model.
|
| 91 |
+
"""
|
| 92 |
+
return next(self.parameters()).device
|
| 93 |
+
|
| 94 |
+
def convert_to(self, dtype: torch.dtype) -> None:
|
| 95 |
+
"""
|
| 96 |
+
Convert the torso of the model to the specified dtype.
|
| 97 |
+
"""
|
| 98 |
+
self.dtype = dtype
|
| 99 |
+
self.blocks.apply(partial(convert_module_to, dtype=dtype))
|
| 100 |
+
|
| 101 |
+
def initialize_weights(self) -> None:
|
| 102 |
+
if self.initialization == 'vanilla':
|
| 103 |
+
# Initialize transformer layers:
|
| 104 |
+
def _basic_init(module):
|
| 105 |
+
if isinstance(module, nn.Linear):
|
| 106 |
+
torch.nn.init.xavier_uniform_(module.weight)
|
| 107 |
+
if module.bias is not None:
|
| 108 |
+
nn.init.constant_(module.bias, 0)
|
| 109 |
+
self.apply(_basic_init)
|
| 110 |
+
|
| 111 |
+
# Initialize timestep embedding MLP:
|
| 112 |
+
nn.init.normal_(self.t_embedder.mlp[0].weight, std=0.02)
|
| 113 |
+
nn.init.normal_(self.t_embedder.mlp[2].weight, std=0.02)
|
| 114 |
+
|
| 115 |
+
# Zero-out adaLN modulation layers in DiT blocks:
|
| 116 |
+
if self.share_mod:
|
| 117 |
+
nn.init.constant_(self.adaLN_modulation[-1].weight, 0)
|
| 118 |
+
nn.init.constant_(self.adaLN_modulation[-1].bias, 0)
|
| 119 |
+
else:
|
| 120 |
+
for block in self.blocks:
|
| 121 |
+
nn.init.constant_(block.adaLN_modulation[-1].weight, 0)
|
| 122 |
+
nn.init.constant_(block.adaLN_modulation[-1].bias, 0)
|
| 123 |
+
|
| 124 |
+
# Zero-out output layers:
|
| 125 |
+
nn.init.constant_(self.out_layer.weight, 0)
|
| 126 |
+
nn.init.constant_(self.out_layer.bias, 0)
|
| 127 |
+
|
| 128 |
+
elif self.initialization == 'scaled':
|
| 129 |
+
# Initialize transformer layers:
|
| 130 |
+
def _basic_init(module):
|
| 131 |
+
if isinstance(module, nn.Linear):
|
| 132 |
+
torch.nn.init.normal_(module.weight, std=np.sqrt(2.0 / (5.0 * self.model_channels)))
|
| 133 |
+
if module.bias is not None:
|
| 134 |
+
nn.init.constant_(module.bias, 0)
|
| 135 |
+
self.apply(_basic_init)
|
| 136 |
+
|
| 137 |
+
# Scaled init for to_out and ffn2
|
| 138 |
+
def _scaled_init(module):
|
| 139 |
+
if isinstance(module, nn.Linear):
|
| 140 |
+
torch.nn.init.normal_(module.weight, std=1.0 / np.sqrt(5 * self.num_blocks * self.model_channels))
|
| 141 |
+
if module.bias is not None:
|
| 142 |
+
nn.init.constant_(module.bias, 0)
|
| 143 |
+
for block in self.blocks:
|
| 144 |
+
block.self_attn.to_out.apply(_scaled_init)
|
| 145 |
+
block.cross_attn.to_out.apply(_scaled_init)
|
| 146 |
+
block.mlp.mlp[2].apply(_scaled_init)
|
| 147 |
+
|
| 148 |
+
# Initialize input layer to make the initial representation have variance 1
|
| 149 |
+
nn.init.normal_(self.input_layer.weight, std=1.0 / np.sqrt(self.in_channels))
|
| 150 |
+
nn.init.zeros_(self.input_layer.bias)
|
| 151 |
+
|
| 152 |
+
# Initialize timestep embedding MLP:
|
| 153 |
+
nn.init.normal_(self.t_embedder.mlp[0].weight, std=0.02)
|
| 154 |
+
nn.init.normal_(self.t_embedder.mlp[2].weight, std=0.02)
|
| 155 |
+
|
| 156 |
+
# Zero-out adaLN modulation layers in DiT blocks:
|
| 157 |
+
if self.share_mod:
|
| 158 |
+
nn.init.constant_(self.adaLN_modulation[-1].weight, 0)
|
| 159 |
+
nn.init.constant_(self.adaLN_modulation[-1].bias, 0)
|
| 160 |
+
else:
|
| 161 |
+
for block in self.blocks:
|
| 162 |
+
nn.init.constant_(block.adaLN_modulation[-1].weight, 0)
|
| 163 |
+
nn.init.constant_(block.adaLN_modulation[-1].bias, 0)
|
| 164 |
+
|
| 165 |
+
# Zero-out output layers:
|
| 166 |
+
nn.init.constant_(self.out_layer.weight, 0)
|
| 167 |
+
nn.init.constant_(self.out_layer.bias, 0)
|
| 168 |
+
|
| 169 |
+
def forward(
|
| 170 |
+
self,
|
| 171 |
+
x: sp.SparseTensor,
|
| 172 |
+
t: torch.Tensor,
|
| 173 |
+
cond: Union[torch.Tensor, List[torch.Tensor]],
|
| 174 |
+
concat_cond: Optional[sp.SparseTensor] = None,
|
| 175 |
+
**kwargs
|
| 176 |
+
) -> sp.SparseTensor:
|
| 177 |
+
if concat_cond is not None:
|
| 178 |
+
x = sp.sparse_cat([x, concat_cond], dim=-1)
|
| 179 |
+
if isinstance(cond, list):
|
| 180 |
+
cond = sp.VarLenTensor.from_tensor_list(cond)
|
| 181 |
+
|
| 182 |
+
h = self.input_layer(x)
|
| 183 |
+
h = manual_cast(h, self.dtype)
|
| 184 |
+
t_emb = self.t_embedder(t)
|
| 185 |
+
if self.share_mod:
|
| 186 |
+
t_emb = self.adaLN_modulation(t_emb)
|
| 187 |
+
t_emb = manual_cast(t_emb, self.dtype)
|
| 188 |
+
cond = manual_cast(cond, self.dtype)
|
| 189 |
+
|
| 190 |
+
if self.pe_mode == "ape":
|
| 191 |
+
pe = self.pos_embedder(h.coords[:, 1:])
|
| 192 |
+
h = h + manual_cast(pe, self.dtype)
|
| 193 |
+
for block in self.blocks:
|
| 194 |
+
h = block(h, t_emb, cond)
|
| 195 |
+
|
| 196 |
+
h = manual_cast(h, x.dtype)
|
| 197 |
+
h = h.replace(F.layer_norm(h.feats, h.feats.shape[-1:]))
|
| 198 |
+
h = self.out_layer(h)
|
| 199 |
+
return h
|
| 200 |
+
|
| 201 |
+
|
| 202 |
+
class ElasticSLatFlowModel(SparseTransformerElasticMixin, SLatFlowModel):
|
| 203 |
+
"""
|
| 204 |
+
SLat Flow Model with elastic memory management.
|
| 205 |
+
Used for training with low VRAM.
|
| 206 |
+
"""
|
| 207 |
+
pass
|