Genkanwall commited on
Commit ·
e0a2bd0
0
Parent(s):
Fresh start with UltraPixel implementation
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .gitattributes +35 -0
- .gitignore +14 -0
- README.md +116 -0
- app.py +373 -0
- configs/inference/controlnet_c_3b_canny.yaml +14 -0
- configs/inference/controlnet_c_3b_identity.yaml +17 -0
- configs/inference/controlnet_c_3b_inpainting.yaml +15 -0
- configs/inference/controlnet_c_3b_sr.yaml +15 -0
- configs/inference/lora_c_3b.yaml +15 -0
- configs/inference/stage_b_1b.yaml +13 -0
- configs/inference/stage_b_3b.yaml +13 -0
- configs/inference/stage_c_1b.yaml +7 -0
- configs/inference/stage_c_3b.yaml +7 -0
- configs/training/cfg_control_lr.yaml +48 -0
- configs/training/lora_personalization.yaml +38 -0
- configs/training/t2i.yaml +29 -0
- core/__init__.py +372 -0
- core/data/__init__.py +69 -0
- core/data/bucketeer.py +88 -0
- core/data/bucketeer_deg.py +91 -0
- core/scripts/__init__.py +0 -0
- core/scripts/cli.py +41 -0
- core/templates/__init__.py +1 -0
- core/templates/diffusion.py +236 -0
- core/utils/__init__.py +9 -0
- core/utils/base_dto.py +56 -0
- core/utils/save_and_load.py +59 -0
- gdf/__init__.py +205 -0
- gdf/loss_weights.py +101 -0
- gdf/noise_conditions.py +102 -0
- gdf/readme.md +86 -0
- gdf/samplers.py +43 -0
- gdf/scalers.py +42 -0
- gdf/schedulers.py +200 -0
- gdf/targets.py +46 -0
- inference/__init__.py +0 -0
- inference/test_controlnet.py +166 -0
- inference/test_personalized.py +180 -0
- inference/test_t2i.py +170 -0
- inference/utils.py +131 -0
- modules/__init__.py +6 -0
- modules/cnet_modules/face_id/arcface.py +276 -0
- modules/cnet_modules/inpainting/saliency_model.py +81 -0
- modules/cnet_modules/pidinet/__init__.py +37 -0
- modules/cnet_modules/pidinet/model.py +654 -0
- modules/cnet_modules/pidinet/util.py +97 -0
- modules/common.py +131 -0
- modules/common_ckpt.py +360 -0
- modules/controlnet.py +349 -0
- modules/effnet.py +17 -0
.gitattributes
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
*.7z filter=lfs diff=lfs merge=lfs -text
|
| 2 |
+
*.arrow filter=lfs diff=lfs merge=lfs -text
|
| 3 |
+
*.bin filter=lfs diff=lfs merge=lfs -text
|
| 4 |
+
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
| 5 |
+
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
| 6 |
+
*.ftz filter=lfs diff=lfs merge=lfs -text
|
| 7 |
+
*.gz filter=lfs diff=lfs merge=lfs -text
|
| 8 |
+
*.h5 filter=lfs diff=lfs merge=lfs -text
|
| 9 |
+
*.joblib filter=lfs diff=lfs merge=lfs -text
|
| 10 |
+
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
| 11 |
+
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
| 12 |
+
*.model filter=lfs diff=lfs merge=lfs -text
|
| 13 |
+
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
| 14 |
+
*.npy filter=lfs diff=lfs merge=lfs -text
|
| 15 |
+
*.npz filter=lfs diff=lfs merge=lfs -text
|
| 16 |
+
*.onnx filter=lfs diff=lfs merge=lfs -text
|
| 17 |
+
*.ot filter=lfs diff=lfs merge=lfs -text
|
| 18 |
+
*.parquet filter=lfs diff=lfs merge=lfs -text
|
| 19 |
+
*.pb filter=lfs diff=lfs merge=lfs -text
|
| 20 |
+
*.pickle filter=lfs diff=lfs merge=lfs -text
|
| 21 |
+
*.pkl filter=lfs diff=lfs merge=lfs -text
|
| 22 |
+
*.pt filter=lfs diff=lfs merge=lfs -text
|
| 23 |
+
*.pth filter=lfs diff=lfs merge=lfs -text
|
| 24 |
+
*.rar filter=lfs diff=lfs merge=lfs -text
|
| 25 |
+
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
| 26 |
+
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
| 27 |
+
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
| 28 |
+
*.tar filter=lfs diff=lfs merge=lfs -text
|
| 29 |
+
*.tflite filter=lfs diff=lfs merge=lfs -text
|
| 30 |
+
*.tgz filter=lfs diff=lfs merge=lfs -text
|
| 31 |
+
*.wasm filter=lfs diff=lfs merge=lfs -text
|
| 32 |
+
*.xz filter=lfs diff=lfs merge=lfs -text
|
| 33 |
+
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
+
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
+
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
.gitignore
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
__pycache__/
|
| 2 |
+
*.py[cod]
|
| 3 |
+
*$py.class
|
| 4 |
+
*.so
|
| 5 |
+
.Python
|
| 6 |
+
*.egg-info/
|
| 7 |
+
dist/
|
| 8 |
+
build/
|
| 9 |
+
*.egg
|
| 10 |
+
|
| 11 |
+
# Virtual environments
|
| 12 |
+
venv/
|
| 13 |
+
env/
|
| 14 |
+
ENV
|
README.md
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: UltraPixel Multi-Stage (Community Fixed)
|
| 3 |
+
emoji: 🎨
|
| 4 |
+
colorFrom: blue
|
| 5 |
+
colorTo: purple
|
| 6 |
+
sdk: gradio
|
| 7 |
+
sdk_version: 5.49.1
|
| 8 |
+
app_file: app.py
|
| 9 |
+
pinned: false
|
| 10 |
+
license: apache-2.0
|
| 11 |
+
---
|
| 12 |
+
|
| 13 |
+
# 🎨 UltraPixel Multi-Stage Generator (Community Fixed)
|
| 14 |
+
|
| 15 |
+
A **properly working** UltraPixel-style high-resolution image generator that actually respects your parameter inputs.
|
| 16 |
+
|
| 17 |
+
## What's Different From Original UltraPixel Spaces?
|
| 18 |
+
|
| 19 |
+
The original public UltraPixel spaces have a critical flaw - they **hardcode CFG and timesteps inside the generation function**, making the UI sliders meaningless:
|
| 20 |
+
|
| 21 |
+
```python
|
| 22 |
+
# Original broken code:
|
| 23 |
+
extras.sampling_configs['cfg'] = 4 # ← Always uses 4!
|
| 24 |
+
extras.sampling_configs['timesteps'] = 20 # ← Ignores your slider!
|
| 25 |
+
```
|
| 26 |
+
|
| 27 |
+
### This Space Fixes That ✅
|
| 28 |
+
|
| 29 |
+
- **Real CFG Control**: Your slider values are actually passed to the model
|
| 30 |
+
- **Real Steps Control**: Set your own timesteps (10-100) per stage
|
| 31 |
+
- **Memory Optimized**: Won't OOM on ZeroGPU (max 3072×3072 with tiling)
|
| 32 |
+
- **No Login Required**: Public access for easy testing
|
| 33 |
+
|
| 34 |
+
## Features
|
| 35 |
+
|
| 36 |
+
- 🎯 **3-Stage Pipeline**: Stable Cascade architecture (Stage C → B → A)
|
| 37 |
+
- 🔧 **Independent Controls**: Separate CFG/steps for each stage
|
| 38 |
+
- 💾 **Memory Safe**: Aggressive cleanup between stages, forced tiling
|
| 39 |
+
- ⏱️ **120s Per Stage**: Each stage gets fresh GPU allocation
|
| 40 |
+
- 🔓 **Public Access**: No authentication needed
|
| 41 |
+
|
| 42 |
+
## How to Use
|
| 43 |
+
|
| 44 |
+
### Standard Workflow (3-4 minutes total)
|
| 45 |
+
|
| 46 |
+
1. **Stage C - Generate Initial Latent** (~30-60s)
|
| 47 |
+
- Enter your prompt
|
| 48 |
+
- Set CFG (recommended: 7.5) and Steps (recommended: 30)
|
| 49 |
+
- Click "Generate Stage C"
|
| 50 |
+
- Wait for completion
|
| 51 |
+
|
| 52 |
+
2. **Wait for GPU availability** (if needed during high traffic)
|
| 53 |
+
|
| 54 |
+
3. **Stage B - Upscale Latent** (~30-50s)
|
| 55 |
+
- Adjust CFG (recommended: 5.0) and Steps (recommended: 15)
|
| 56 |
+
- Click "Generate Stage B"
|
| 57 |
+
- Uses the latent from Stage C automatically
|
| 58 |
+
|
| 59 |
+
4. **Wait again if needed**
|
| 60 |
+
|
| 61 |
+
5. **Stage A - Final Decode** (~60-90s)
|
| 62 |
+
- Keep "Use Tiling" checked (prevents OOM)
|
| 63 |
+
- Click "Generate Final Image"
|
| 64 |
+
- Download your high-res result!
|
| 65 |
+
|
| 66 |
+
### Optimal Settings 💡
|
| 67 |
+
|
| 68 |
+
- **Stage C**: CFG 7-8, Steps 30-40
|
| 69 |
+
- **Stage B**: CFG 4-6, Steps 15-20
|
| 70 |
+
- **Stage A**: Always use tiling
|
| 71 |
+
- **Resolution Limits**: Max 3072×3072 for stability (1536×1536 per stage C/B)
|
| 72 |
+
- **For Training Data**: Generate at 3072px, then downscale to 1024px for optimal quality
|
| 73 |
+
|
| 74 |
+
## Technical Details
|
| 75 |
+
|
| 76 |
+
### Memory Management
|
| 77 |
+
|
| 78 |
+
Each stage runs in isolated `@spaces.GPU(duration=120)` calls:
|
| 79 |
+
- Models loaded only when needed
|
| 80 |
+
- Aggressive `torch.cuda.empty_cache()` after each stage
|
| 81 |
+
- Latents stored in-memory (automatically cleaned after 1 hour)
|
| 82 |
+
- VAE tiling enabled for Stage A decode
|
| 83 |
+
|
| 84 |
+
### Resolution Scaling
|
| 85 |
+
|
| 86 |
+
- **Stage C Input**: 512-1536px (base resolution)
|
| 87 |
+
- **Stage B Output**: 2× Stage C (1024-3072px)
|
| 88 |
+
- **Stage A Output**: Full decode to target resolution
|
| 89 |
+
- **Memory Usage**: ~20-30GB peak per stage (safe for ZeroGPU)
|
| 90 |
+
|
| 91 |
+
## Why This Matters
|
| 92 |
+
|
| 93 |
+
Many public AI spaces claim to offer "full control" but secretly override your parameters. This leads to:
|
| 94 |
+
- ❌ Inconsistent results despite changing settings
|
| 95 |
+
- ❌ Users wasting time tweaking sliders that do nothing
|
| 96 |
+
- ❌ Frustration when trying to reproduce outputs
|
| 97 |
+
|
| 98 |
+
This space guarantees that **your inputs = actual model parameters**.
|
| 99 |
+
|
| 100 |
+
## Deployment Notes
|
| 101 |
+
|
| 102 |
+
Built specifically for:
|
| 103 |
+
- ZeroGPU compatibility (120s duration per stage)
|
| 104 |
+
- Public/unlogged access
|
| 105 |
+
- High-resolution output (up to 3072×3072 stable)
|
| 106 |
+
- Proper parameter control
|
| 107 |
+
|
| 108 |
+
## Credits
|
| 109 |
+
|
| 110 |
+
- **Stable Cascade**: Stability AI
|
| 111 |
+
- **Original UltraPixel Concept**: Various community implementations
|
| 112 |
+
- **This Implementation**: Community-fixed version with proper parameter control
|
| 113 |
+
|
| 114 |
+
## License
|
| 115 |
+
|
| 116 |
+
Apache 2.0
|
app.py
ADDED
|
@@ -0,0 +1,373 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python
|
| 2 |
+
"""
|
| 3 |
+
UltraPixel Multi-Stage High-Resolution Generator
|
| 4 |
+
Fixed parameter control with independent GPU allocation per stage
|
| 5 |
+
"""
|
| 6 |
+
|
| 7 |
+
import spaces
|
| 8 |
+
import os
|
| 9 |
+
import torch
|
| 10 |
+
import yaml
|
| 11 |
+
import sys
|
| 12 |
+
import gradio as gr
|
| 13 |
+
import numpy as np
|
| 14 |
+
from PIL import Image
|
| 15 |
+
from typing import Tuple
|
| 16 |
+
import datetime
|
| 17 |
+
import random
|
| 18 |
+
|
| 19 |
+
sys.path.append(os.path.abspath('./'))
|
| 20 |
+
|
| 21 |
+
# Environment optimization
|
| 22 |
+
os.environ['PYTORCH_NVML_BASED_CUDA_CHECK'] = '1'
|
| 23 |
+
os.environ['PYTORCH_ALLOC_CONF'] = 'expandable_segments:True'
|
| 24 |
+
os.environ["SAFETENSORS_FAST_GPU"] = "1"
|
| 25 |
+
os.environ['HF_HUB_ENABLE_HF_TRANSFER'] = '1'
|
| 26 |
+
|
| 27 |
+
torch.backends.cuda.matmul.allow_tf32 = True
|
| 28 |
+
torch.backends.cudnn.allow_tf32 = True
|
| 29 |
+
torch.set_float32_matmul_precision("high")
|
| 30 |
+
|
| 31 |
+
from inference.utils import *
|
| 32 |
+
from train import WurstCoreB, WurstCore_t2i as WurstCoreC
|
| 33 |
+
from gdf import DDPMSampler
|
| 34 |
+
from huggingface_hub import hf_hub_download
|
| 35 |
+
|
| 36 |
+
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
|
| 37 |
+
dtype = torch.bfloat16
|
| 38 |
+
|
| 39 |
+
# Persistent storage
|
| 40 |
+
LATENT_DIR = "/tmp/ultrapixel_latents"
|
| 41 |
+
os.makedirs(LATENT_DIR, exist_ok=True)
|
| 42 |
+
|
| 43 |
+
DESCRIPTION = """
|
| 44 |
+
# 🎨 UltraPixel High-Resolution Image Generator
|
| 45 |
+
|
| 46 |
+
Generate ultra-high-resolution images (up to 5120×4096) with full parameter control.
|
| 47 |
+
|
| 48 |
+
**Fixed Issues:**
|
| 49 |
+
- ✅ CFG and timestep sliders now actually work (not hardcoded)
|
| 50 |
+
- ✅ Memory optimized for large resolutions
|
| 51 |
+
- ✅ Independent stage execution
|
| 52 |
+
|
| 53 |
+
**Pipeline:**
|
| 54 |
+
- **Stage C**: Text → Latent (with UltraPixel high-res guidance)
|
| 55 |
+
- **Stage B+A**: Latent → Final ultra-high-res image
|
| 56 |
+
"""
|
| 57 |
+
|
| 58 |
+
# ==================== PERSISTENCE ====================
|
| 59 |
+
|
| 60 |
+
def save_latent_to_disk(latent_tensor, latent_id, metadata=None):
|
| 61 |
+
latent_path = os.path.join(LATENT_DIR, f"{latent_id}.pt")
|
| 62 |
+
save_data = {
|
| 63 |
+
'latent': latent_tensor.cpu(),
|
| 64 |
+
'metadata': metadata or {}
|
| 65 |
+
}
|
| 66 |
+
torch.save(save_data, latent_path)
|
| 67 |
+
|
| 68 |
+
def load_latent_from_disk(latent_id):
|
| 69 |
+
latent_path = os.path.join(LATENT_DIR, f"{latent_id}.pt")
|
| 70 |
+
if not os.path.exists(latent_path):
|
| 71 |
+
return None, None
|
| 72 |
+
data = torch.load(latent_path, map_location=device)
|
| 73 |
+
if isinstance(data, dict):
|
| 74 |
+
return data['latent'], data.get('metadata', {})
|
| 75 |
+
return data, {}
|
| 76 |
+
|
| 77 |
+
def cleanup_old_latents():
|
| 78 |
+
if not os.path.exists(LATENT_DIR):
|
| 79 |
+
return
|
| 80 |
+
current_time = datetime.datetime.now()
|
| 81 |
+
for filename in os.listdir(LATENT_DIR):
|
| 82 |
+
if not filename.endswith('.pt'):
|
| 83 |
+
continue
|
| 84 |
+
filepath = os.path.join(LATENT_DIR, filename)
|
| 85 |
+
file_time = datetime.datetime.fromtimestamp(os.path.getmtime(filepath))
|
| 86 |
+
if (current_time - file_time).total_seconds() > 3600:
|
| 87 |
+
try:
|
| 88 |
+
os.remove(filepath)
|
| 89 |
+
except:
|
| 90 |
+
pass
|
| 91 |
+
|
| 92 |
+
# ==================== MODEL SETUP ====================
|
| 93 |
+
|
| 94 |
+
def download_models():
|
| 95 |
+
"""Download all required models"""
|
| 96 |
+
model_files = [
|
| 97 |
+
'stage_a.safetensors',
|
| 98 |
+
'previewer.safetensors',
|
| 99 |
+
'effnet_encoder.safetensors',
|
| 100 |
+
'stage_b_lite_bf16.safetensors',
|
| 101 |
+
'stage_c_bf16.safetensors'
|
| 102 |
+
]
|
| 103 |
+
|
| 104 |
+
for filename in model_files:
|
| 105 |
+
hf_hub_download(
|
| 106 |
+
repo_id="stabilityai/stable-cascade",
|
| 107 |
+
filename=filename,
|
| 108 |
+
local_dir='models'
|
| 109 |
+
)
|
| 110 |
+
|
| 111 |
+
# UltraPixel weights
|
| 112 |
+
hf_hub_download(
|
| 113 |
+
repo_id="roubaofeipi/UltraPixel",
|
| 114 |
+
filename='ultrapixel_t2i.safetensors',
|
| 115 |
+
local_dir='models'
|
| 116 |
+
)
|
| 117 |
+
|
| 118 |
+
def load_models():
|
| 119 |
+
"""Initialize all models"""
|
| 120 |
+
global core, core_b, models, models_b, extras, extras_b
|
| 121 |
+
|
| 122 |
+
# Load Stage C
|
| 123 |
+
with open('configs/training/t2i.yaml', 'r', encoding='utf-8') as f:
|
| 124 |
+
config_c = yaml.safe_load(f)
|
| 125 |
+
|
| 126 |
+
core = WurstCoreC(config_dict=config_c, device=device, training=False)
|
| 127 |
+
extras = core.setup_extras_pre()
|
| 128 |
+
models = core.setup_models(extras)
|
| 129 |
+
models.generator.eval().requires_grad_(False)
|
| 130 |
+
|
| 131 |
+
# Load Stage B
|
| 132 |
+
with open('configs/inference/stage_b_1b.yaml', 'r', encoding='utf-8') as f:
|
| 133 |
+
config_b = yaml.safe_load(f)
|
| 134 |
+
|
| 135 |
+
core_b = WurstCoreB(config_dict=config_b, device=device, training=False)
|
| 136 |
+
extras_b = core_b.setup_extras_pre()
|
| 137 |
+
models_b = core_b.setup_models(extras_b, skip_clip=True)
|
| 138 |
+
models_b = WurstCoreB.Models(
|
| 139 |
+
**{**models_b.to_dict(), 'tokenizer': models.tokenizer, 'text_model': models.text_model}
|
| 140 |
+
)
|
| 141 |
+
models_b.generator.bfloat16().eval().requires_grad_(False)
|
| 142 |
+
|
| 143 |
+
# Load UltraPixel weights (the secret sauce!)
|
| 144 |
+
ultrapixel_weights = torch.load('models/ultrapixel_t2i.safetensors', map_location='cpu')
|
| 145 |
+
collect_sd = {}
|
| 146 |
+
for k, v in ultrapixel_weights.items():
|
| 147 |
+
collect_sd[k[7:]] = v
|
| 148 |
+
|
| 149 |
+
models.train_norm.load_state_dict(collect_sd)
|
| 150 |
+
models.train_norm.eval()
|
| 151 |
+
|
| 152 |
+
print("✅ All models loaded successfully")
|
| 153 |
+
|
| 154 |
+
# ==================== STAGE C ====================
|
| 155 |
+
|
| 156 |
+
@spaces.GPU(duration=120)
|
| 157 |
+
def generate_stage_c(
|
| 158 |
+
prompt: str,
|
| 159 |
+
height: int,
|
| 160 |
+
width: int,
|
| 161 |
+
seed: int,
|
| 162 |
+
cfg: float,
|
| 163 |
+
timesteps: int,
|
| 164 |
+
progress=gr.Progress(track_tqdm=True)
|
| 165 |
+
) -> Tuple[str, str]:
|
| 166 |
+
"""
|
| 167 |
+
Stage C: Generate high-resolution latent with UltraPixel guidance
|
| 168 |
+
"""
|
| 169 |
+
|
| 170 |
+
# Set seeds
|
| 171 |
+
torch.manual_seed(seed)
|
| 172 |
+
random.seed(seed)
|
| 173 |
+
np.random.seed(seed)
|
| 174 |
+
|
| 175 |
+
# Enhance prompt
|
| 176 |
+
full_prompt = prompt + ' rich detail, 4k, high quality'
|
| 177 |
+
|
| 178 |
+
# Calculate sizes
|
| 179 |
+
height_lr, width_lr = get_target_lr_size(height / width, std_size=32)
|
| 180 |
+
stage_c_latent_shape, _ = calculate_latent_sizes(height, width, batch_size=1)
|
| 181 |
+
stage_c_latent_shape_lr, _ = calculate_latent_sizes(height_lr, width_lr, batch_size=1)
|
| 182 |
+
|
| 183 |
+
# ⚠️ ACTUALLY USE THE USER'S PARAMETERS (not hardcoded!)
|
| 184 |
+
extras.sampling_configs['cfg'] = cfg
|
| 185 |
+
extras.sampling_configs['shift'] = 1
|
| 186 |
+
extras.sampling_configs['timesteps'] = timesteps
|
| 187 |
+
extras.sampling_configs['t_start'] = 1.0
|
| 188 |
+
extras.sampling_configs['sampler'] = DDPMSampler(extras.gdf)
|
| 189 |
+
|
| 190 |
+
batch = {'captions': [full_prompt]}
|
| 191 |
+
|
| 192 |
+
with torch.no_grad():
|
| 193 |
+
models.generator.cuda()
|
| 194 |
+
with torch.cuda.amp.autocast(dtype=dtype):
|
| 195 |
+
sampled_c = generation_c(
|
| 196 |
+
batch, models, extras, core,
|
| 197 |
+
stage_c_latent_shape, stage_c_latent_shape_lr, device
|
| 198 |
+
)
|
| 199 |
+
|
| 200 |
+
models.generator.cpu()
|
| 201 |
+
torch.cuda.empty_cache()
|
| 202 |
+
|
| 203 |
+
# Save latent
|
| 204 |
+
import uuid
|
| 205 |
+
latent_id = str(uuid.uuid4())
|
| 206 |
+
metadata = {
|
| 207 |
+
'prompt': full_prompt,
|
| 208 |
+
'height': height,
|
| 209 |
+
'width': width,
|
| 210 |
+
'seed': seed
|
| 211 |
+
}
|
| 212 |
+
save_latent_to_disk(sampled_c, latent_id, metadata)
|
| 213 |
+
|
| 214 |
+
del sampled_c
|
| 215 |
+
torch.cuda.empty_cache()
|
| 216 |
+
|
| 217 |
+
status = f"✅ Stage C Complete | ID: {latent_id[:8]}..."
|
| 218 |
+
return latent_id, status
|
| 219 |
+
|
| 220 |
+
# ==================== STAGE B+A ====================
|
| 221 |
+
|
| 222 |
+
@spaces.GPU(duration=120)
|
| 223 |
+
def generate_stage_b(
|
| 224 |
+
latent_id: str,
|
| 225 |
+
cfg: float,
|
| 226 |
+
timesteps: int,
|
| 227 |
+
stage_a_tiled: bool,
|
| 228 |
+
progress=gr.Progress(track_tqdm=True)
|
| 229 |
+
) -> Image.Image:
|
| 230 |
+
"""
|
| 231 |
+
Stage B+A: Decode latent to final ultra-high-res image
|
| 232 |
+
"""
|
| 233 |
+
|
| 234 |
+
if not latent_id:
|
| 235 |
+
raise gr.Error("Invalid latent ID from Stage C")
|
| 236 |
+
|
| 237 |
+
sampled_c, metadata = load_latent_from_disk(latent_id)
|
| 238 |
+
if sampled_c is None:
|
| 239 |
+
raise gr.Error("Could not load latent from Stage C")
|
| 240 |
+
|
| 241 |
+
prompt = metadata.get('prompt', '')
|
| 242 |
+
height = metadata.get('height', 2048)
|
| 243 |
+
width = metadata.get('width', 2048)
|
| 244 |
+
|
| 245 |
+
# Calculate Stage B size
|
| 246 |
+
_, stage_b_latent_shape = calculate_latent_sizes(height, width, batch_size=1)
|
| 247 |
+
|
| 248 |
+
# ⚠️ ACTUALLY USE THE USER'S PARAMETERS (not hardcoded!)
|
| 249 |
+
extras_b.sampling_configs['cfg'] = cfg
|
| 250 |
+
extras_b.sampling_configs['shift'] = 1
|
| 251 |
+
extras_b.sampling_configs['timesteps'] = timesteps
|
| 252 |
+
extras_b.sampling_configs['t_start'] = 1.0
|
| 253 |
+
|
| 254 |
+
batch = {'captions': [prompt]}
|
| 255 |
+
|
| 256 |
+
conditions_b = core_b.get_conditions(batch, models_b, extras_b, is_eval=True, is_unconditional=False)
|
| 257 |
+
unconditions_b = core_b.get_conditions(batch, models_b, extras_b, is_eval=True, is_unconditional=True)
|
| 258 |
+
conditions_b['effnet'] = sampled_c
|
| 259 |
+
unconditions_b['effnet'] = torch.zeros_like(sampled_c)
|
| 260 |
+
|
| 261 |
+
with torch.no_grad():
|
| 262 |
+
with torch.cuda.amp.autocast(dtype=dtype):
|
| 263 |
+
sampled = decode_b(
|
| 264 |
+
conditions_b, unconditions_b, models_b,
|
| 265 |
+
stage_b_latent_shape, extras_b, device,
|
| 266 |
+
stage_a_tiled=stage_a_tiled
|
| 267 |
+
)
|
| 268 |
+
|
| 269 |
+
torch.cuda.empty_cache()
|
| 270 |
+
imgs = show_images(sampled)
|
| 271 |
+
|
| 272 |
+
del sampled_c, sampled
|
| 273 |
+
torch.cuda.empty_cache()
|
| 274 |
+
|
| 275 |
+
return imgs[0]
|
| 276 |
+
|
| 277 |
+
# ==================== UI ====================
|
| 278 |
+
|
| 279 |
+
css = """
|
| 280 |
+
#col-container {
|
| 281 |
+
margin: 0 auto;
|
| 282 |
+
max-width: 1200px;
|
| 283 |
+
}
|
| 284 |
+
"""
|
| 285 |
+
|
| 286 |
+
with gr.Blocks(theme=gr.themes.Soft(), css=css) as demo:
|
| 287 |
+
gr.Markdown(DESCRIPTION)
|
| 288 |
+
|
| 289 |
+
latent_id = gr.State("")
|
| 290 |
+
|
| 291 |
+
with gr.Row():
|
| 292 |
+
with gr.Column(scale=1):
|
| 293 |
+
prompt = gr.Textbox(
|
| 294 |
+
label="Prompt",
|
| 295 |
+
placeholder="A breathtaking landscape...",
|
| 296 |
+
lines=3
|
| 297 |
+
)
|
| 298 |
+
|
| 299 |
+
with gr.Row():
|
| 300 |
+
height = gr.Slider(1536, 4096, value=2304, step=32, label="Height")
|
| 301 |
+
width = gr.Slider(1536, 5120, value=4096, step=32, label="Width")
|
| 302 |
+
|
| 303 |
+
seed = gr.Number(label="Seed", value=123, precision=0)
|
| 304 |
+
|
| 305 |
+
gr.Markdown("---")
|
| 306 |
+
gr.Markdown("### Stage C: Latent Generation")
|
| 307 |
+
|
| 308 |
+
with gr.Row():
|
| 309 |
+
cfg_c = gr.Slider(3, 10, value=4, step=0.1, label="CFG Scale")
|
| 310 |
+
steps_c = gr.Slider(10, 50, value=20, step=1, label="Timesteps")
|
| 311 |
+
|
| 312 |
+
btn_stage_c = gr.Button("🚀 Generate Latent (Stage C)", variant="primary", size="lg")
|
| 313 |
+
status_c = gr.Textbox(label="Status", interactive=False)
|
| 314 |
+
|
| 315 |
+
gr.Markdown("---")
|
| 316 |
+
gr.Markdown("### Stage B+A: Image Decoding")
|
| 317 |
+
|
| 318 |
+
with gr.Row():
|
| 319 |
+
cfg_b = gr.Slider(1, 5, value=1.1, step=0.1, label="CFG Scale")
|
| 320 |
+
steps_b = gr.Slider(5, 30, value=10, step=1, label="Timesteps")
|
| 321 |
+
|
| 322 |
+
stage_a_tiled = gr.Checkbox(label="Use Tiled Decoding (recommended for large images)", value=False)
|
| 323 |
+
|
| 324 |
+
btn_stage_b = gr.Button("🚀 Generate Image (Stage B+A)", variant="primary", size="lg")
|
| 325 |
+
|
| 326 |
+
with gr.Column(scale=1):
|
| 327 |
+
output_image = gr.Image(label="Output", type="pil")
|
| 328 |
+
|
| 329 |
+
gr.Markdown("""
|
| 330 |
+
### Usage
|
| 331 |
+
|
| 332 |
+
1. Enter your prompt and configure resolution
|
| 333 |
+
2. Click "Generate Latent" (60-90s)
|
| 334 |
+
3. Click "Generate Image" (60-90s)
|
| 335 |
+
|
| 336 |
+
**Recommended Settings:**
|
| 337 |
+
- Stage C: CFG 4, Steps 20
|
| 338 |
+
- Stage B: CFG 1.1, Steps 10
|
| 339 |
+
- Enable tiling for resolutions >3000px
|
| 340 |
+
|
| 341 |
+
**Note:** Each stage runs independently with separate GPU allocation.
|
| 342 |
+
""")
|
| 343 |
+
|
| 344 |
+
gr.Examples(
|
| 345 |
+
examples=[
|
| 346 |
+
"A detailed view of a blooming magnolia tree, with large, white flowers and dark green leaves, set against a clear blue sky.",
|
| 347 |
+
"A close-up portrait of a young woman with flawless skin, vibrant red lipstick, and wavy brown hair, wearing a vintage floral dress and standing in front of a blooming garden.",
|
| 348 |
+
"A highly detailed, high-quality image of the Banff National Park in Canada. The turquoise waters of Lake Louise are surrounded by snow-capped mountains and dense pine forests.",
|
| 349 |
+
"A cozy, rustic log cabin nestled in a snow-covered forest, with smoke rising from the stone chimney and warm lights glowing from the windows.",
|
| 350 |
+
],
|
| 351 |
+
inputs=[prompt],
|
| 352 |
+
outputs=[output_image]
|
| 353 |
+
)
|
| 354 |
+
|
| 355 |
+
# Event handlers
|
| 356 |
+
btn_stage_c.click(
|
| 357 |
+
fn=generate_stage_c,
|
| 358 |
+
inputs=[prompt, height, width, seed, cfg_c, steps_c],
|
| 359 |
+
outputs=[latent_id, status_c]
|
| 360 |
+
)
|
| 361 |
+
|
| 362 |
+
btn_stage_b.click(
|
| 363 |
+
fn=generate_stage_b,
|
| 364 |
+
inputs=[latent_id, cfg_b, steps_b, stage_a_tiled],
|
| 365 |
+
outputs=[output_image]
|
| 366 |
+
)
|
| 367 |
+
|
| 368 |
+
demo.load(cleanup_old_latents)
|
| 369 |
+
|
| 370 |
+
if __name__ == "__main__":
|
| 371 |
+
download_models()
|
| 372 |
+
load_models()
|
| 373 |
+
demo.queue(max_size=20).launch(show_api=False)
|
configs/inference/controlnet_c_3b_canny.yaml
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# GLOBAL STUFF
|
| 2 |
+
model_version: 3.6B
|
| 3 |
+
dtype: bfloat16
|
| 4 |
+
|
| 5 |
+
# ControlNet specific
|
| 6 |
+
controlnet_blocks: [0, 4, 8, 12, 51, 55, 59, 63]
|
| 7 |
+
controlnet_filter: CannyFilter
|
| 8 |
+
controlnet_filter_params:
|
| 9 |
+
resize: 224
|
| 10 |
+
|
| 11 |
+
effnet_checkpoint_path: models/effnet_encoder.safetensors
|
| 12 |
+
previewer_checkpoint_path: models/previewer.safetensors
|
| 13 |
+
generator_checkpoint_path: models/stage_c_bf16.safetensors
|
| 14 |
+
controlnet_checkpoint_path: models/canny.safetensors
|
configs/inference/controlnet_c_3b_identity.yaml
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# GLOBAL STUFF
|
| 2 |
+
model_version: 3.6B
|
| 3 |
+
dtype: bfloat16
|
| 4 |
+
|
| 5 |
+
# ControlNet specific
|
| 6 |
+
controlnet_bottleneck_mode: 'simple'
|
| 7 |
+
controlnet_blocks: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63]
|
| 8 |
+
controlnet_filter: IdentityFilter
|
| 9 |
+
controlnet_filter_params:
|
| 10 |
+
max_faces: 4
|
| 11 |
+
p_drop: 0.00
|
| 12 |
+
p_full: 0.0
|
| 13 |
+
|
| 14 |
+
effnet_checkpoint_path: models/effnet_encoder.safetensors
|
| 15 |
+
previewer_checkpoint_path: models/previewer.safetensors
|
| 16 |
+
generator_checkpoint_path: models/stage_c_bf16.safetensors
|
| 17 |
+
controlnet_checkpoint_path:
|
configs/inference/controlnet_c_3b_inpainting.yaml
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# GLOBAL STUFF
|
| 2 |
+
model_version: 3.6B
|
| 3 |
+
dtype: bfloat16
|
| 4 |
+
|
| 5 |
+
# ControlNet specific
|
| 6 |
+
controlnet_blocks: [0, 4, 8, 12, 51, 55, 59, 63]
|
| 7 |
+
controlnet_filter: InpaintFilter
|
| 8 |
+
controlnet_filter_params:
|
| 9 |
+
thresold: [0.04, 0.4]
|
| 10 |
+
p_outpaint: 0.4
|
| 11 |
+
|
| 12 |
+
effnet_checkpoint_path: models/effnet_encoder.safetensors
|
| 13 |
+
previewer_checkpoint_path: models/previewer.safetensors
|
| 14 |
+
generator_checkpoint_path: models/stage_c_bf16.safetensors
|
| 15 |
+
controlnet_checkpoint_path: models/inpainting.safetensors
|
configs/inference/controlnet_c_3b_sr.yaml
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# GLOBAL STUFF
|
| 2 |
+
model_version: 3.6B
|
| 3 |
+
dtype: bfloat16
|
| 4 |
+
|
| 5 |
+
# ControlNet specific
|
| 6 |
+
controlnet_bottleneck_mode: 'large'
|
| 7 |
+
controlnet_blocks: [0, 4, 8, 12, 51, 55, 59, 63]
|
| 8 |
+
controlnet_filter: SREffnetFilter
|
| 9 |
+
controlnet_filter_params:
|
| 10 |
+
scale_factor: 0.5
|
| 11 |
+
|
| 12 |
+
effnet_checkpoint_path: models/effnet_encoder.safetensors
|
| 13 |
+
previewer_checkpoint_path: models/previewer.safetensors
|
| 14 |
+
generator_checkpoint_path: models/stage_c_bf16.safetensors
|
| 15 |
+
controlnet_checkpoint_path: models/super_resolution.safetensors
|
configs/inference/lora_c_3b.yaml
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# GLOBAL STUFF
|
| 2 |
+
model_version: 3.6B
|
| 3 |
+
dtype: bfloat16
|
| 4 |
+
|
| 5 |
+
# LoRA specific
|
| 6 |
+
module_filters: ['.attn']
|
| 7 |
+
rank: 4
|
| 8 |
+
train_tokens:
|
| 9 |
+
# - ['^snail', null] # token starts with "snail" -> "snail" & "snails", don't need to be reinitialized
|
| 10 |
+
- ['[fernando]', '^dog</w>'] # custom token [snail], initialize as avg of snail & snails
|
| 11 |
+
|
| 12 |
+
effnet_checkpoint_path: models/effnet_encoder.safetensors
|
| 13 |
+
previewer_checkpoint_path: models/previewer.safetensors
|
| 14 |
+
generator_checkpoint_path: models/stage_c_bf16.safetensors
|
| 15 |
+
lora_checkpoint_path: models/lora_fernando_10k.safetensors
|
configs/inference/stage_b_1b.yaml
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# GLOBAL STUFF
|
| 2 |
+
model_version: 700M
|
| 3 |
+
dtype: bfloat16
|
| 4 |
+
|
| 5 |
+
# For demonstration purposes in reconstruct_images.ipynb
|
| 6 |
+
webdataset_path: path to your dataset
|
| 7 |
+
batch_size: 1
|
| 8 |
+
image_size: 2048
|
| 9 |
+
grad_accum_steps: 1
|
| 10 |
+
|
| 11 |
+
effnet_checkpoint_path: models/effnet_encoder.safetensors
|
| 12 |
+
stage_a_checkpoint_path: models/stage_a.safetensors
|
| 13 |
+
generator_checkpoint_path: models/stage_b_lite_bf16.safetensors
|
configs/inference/stage_b_3b.yaml
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# GLOBAL STUFF
|
| 2 |
+
model_version: 3B
|
| 3 |
+
dtype: bfloat16
|
| 4 |
+
|
| 5 |
+
# For demonstration purposes in reconstruct_images.ipynb
|
| 6 |
+
webdataset_path: path to your dataset
|
| 7 |
+
batch_size: 4
|
| 8 |
+
image_size: 1024
|
| 9 |
+
grad_accum_steps: 1
|
| 10 |
+
|
| 11 |
+
effnet_checkpoint_path: path to effnet of stablecascade / effnet_encoder.safetensors
|
| 12 |
+
stage_a_checkpoint_path: path to effnet of stablecascade stage a decoder/stage_a.safetensors
|
| 13 |
+
generator_checkpoint_path: path to effnet of stablecascade stage b decoer heavy version bf16/stage_b_lite_bf16.safetensors
|
configs/inference/stage_c_1b.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# GLOBAL STUFF
|
| 2 |
+
model_version: 1B
|
| 3 |
+
dtype: bfloat16
|
| 4 |
+
|
| 5 |
+
effnet_checkpoint_path: path to effnet of stablecascade / effnet_encoder.safetensors
|
| 6 |
+
previewer_checkpoint_path: path to previewer of stablecascade/previewer.safetensors
|
| 7 |
+
generator_checkpoint_path: path to generator of stablecascade stage c lite version bf16 /stage_c_lite_bf16.safetensors
|
configs/inference/stage_c_3b.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# GLOBAL STUFF
|
| 2 |
+
model_version: 3.6B
|
| 3 |
+
dtype: bfloat16
|
| 4 |
+
|
| 5 |
+
effnet_checkpoint_path: models/effnet_encoder.safetensors
|
| 6 |
+
previewer_checkpoint_path: models/previewer.safetensors
|
| 7 |
+
generator_checkpoint_path: models/stage_c_bf16.safetensors
|
configs/training/cfg_control_lr.yaml
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# GLOBAL STUFF
|
| 2 |
+
experiment_id: Ultrapixel_controlnet
|
| 3 |
+
|
| 4 |
+
checkpoint_path: checkpoint output path
|
| 5 |
+
output_path: visual results output path
|
| 6 |
+
model_version: 3.6B
|
| 7 |
+
dtype: float32
|
| 8 |
+
# # WandB
|
| 9 |
+
# wandb_project: StableCascade
|
| 10 |
+
# wandb_entity: wandb_username
|
| 11 |
+
#module_filters: ['.depthwise', '.mapper', '.attn', '.channelwise' ]
|
| 12 |
+
#rank: 32
|
| 13 |
+
# TRAINING PARAMS
|
| 14 |
+
lr: 1.0e-4
|
| 15 |
+
batch_size: 12
|
| 16 |
+
#image_size: [1536, 2048, 2560, 3072, 4096]
|
| 17 |
+
image_size: [1024, 2048, 2560, 3072, 3584, 3840, 4096, 4608]
|
| 18 |
+
#image_size: [ 1024, 1536, 2048, 2560, 3072, 3584, 3840, 4096, 4608]
|
| 19 |
+
#image_size: [ 1024, 1280]
|
| 20 |
+
multi_aspect_ratio: [1/1, 1/2, 1/3, 2/3, 3/4, 1/5, 2/5, 3/5, 4/5, 1/6, 5/6, 9/16]
|
| 21 |
+
grad_accum_steps: 2
|
| 22 |
+
updates: 40000
|
| 23 |
+
backup_every: 5000
|
| 24 |
+
save_every: 256
|
| 25 |
+
warmup_updates: 1
|
| 26 |
+
use_fsdp: True
|
| 27 |
+
|
| 28 |
+
# ControlNet specific
|
| 29 |
+
controlnet_blocks: [0, 4, 8, 12, 51, 55, 59, 63]
|
| 30 |
+
controlnet_filter: CannyFilter
|
| 31 |
+
controlnet_filter_params:
|
| 32 |
+
resize: 224
|
| 33 |
+
# offset_noise: 0.1
|
| 34 |
+
|
| 35 |
+
# GDF
|
| 36 |
+
adaptive_loss_weight: True
|
| 37 |
+
|
| 38 |
+
ema_start_iters: 10
|
| 39 |
+
ema_iters: 50
|
| 40 |
+
ema_beta: 0.9
|
| 41 |
+
|
| 42 |
+
webdataset_path: path to your training dataset
|
| 43 |
+
effnet_checkpoint_path: models/effnet_encoder.safetensors
|
| 44 |
+
previewer_checkpoint_path: models/previewer.safetensors
|
| 45 |
+
generator_checkpoint_path: models/stage_c_bf16.safetensors
|
| 46 |
+
controlnet_checkpoint_path: models/canny.safetensors
|
| 47 |
+
|
| 48 |
+
|
configs/training/lora_personalization.yaml
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# GLOBAL STUFF
|
| 2 |
+
experiment_id: roubao_cat_personalized
|
| 3 |
+
|
| 4 |
+
checkpoint_path: checkpoint output path
|
| 5 |
+
output_path: visual results output path
|
| 6 |
+
model_version: 3.6B
|
| 7 |
+
dtype: float32
|
| 8 |
+
|
| 9 |
+
module_filters: [ '.attn']
|
| 10 |
+
rank: 4
|
| 11 |
+
train_tokens:
|
| 12 |
+
# - ['^snail', null] # token starts with "snail" -> "snail" & "snails", don't need to be reinitialized
|
| 13 |
+
- ['[roubaobao]', '^cat</w>'] # custom token [snail], initialize as avg of snail & snails
|
| 14 |
+
# TRAINING PARAMS
|
| 15 |
+
lr: 1.0e-4
|
| 16 |
+
batch_size: 4
|
| 17 |
+
|
| 18 |
+
image_size: [1024, 2048, 2560, 3072, 3584, 3840, 4096, 4608]
|
| 19 |
+
multi_aspect_ratio: [1/1, 1/2, 1/3, 2/3, 3/4, 1/5, 2/5, 3/5, 4/5, 1/6, 5/6, 9/16]
|
| 20 |
+
grad_accum_steps: 2
|
| 21 |
+
updates: 40000
|
| 22 |
+
backup_every: 5000
|
| 23 |
+
save_every: 512
|
| 24 |
+
warmup_updates: 1
|
| 25 |
+
use_ddp: True
|
| 26 |
+
|
| 27 |
+
# GDF
|
| 28 |
+
adaptive_loss_weight: True
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
tmp_prompt: a photo of a cat [roubaobao]
|
| 32 |
+
webdataset_path: path to your personalized training dataset
|
| 33 |
+
effnet_checkpoint_path: models/effnet_encoder.safetensors
|
| 34 |
+
previewer_checkpoint_path: models/previewer.safetensors
|
| 35 |
+
generator_checkpoint_path: models/stage_c_bf16.safetensors
|
| 36 |
+
ultrapixel_path: models/ultrapixel_t2i.safetensors
|
| 37 |
+
|
| 38 |
+
|
configs/training/t2i.yaml
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# GLOBAL STUFF
|
| 2 |
+
experiment_id: ultrapixel_t2i
|
| 3 |
+
#strc_fixlrt_norm3_lite_1024_hrft_newdata
|
| 4 |
+
checkpoint_path: checkpoint output path #output model directory
|
| 5 |
+
output_path: visual results output path #experiment output directory
|
| 6 |
+
model_version: 3.6B # finetune large stage c model of stablecascade
|
| 7 |
+
dtype: float32
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
# TRAINING PARAMS
|
| 11 |
+
lr: 1.0e-4
|
| 12 |
+
batch_size: 4 # gpu_number * num_per_gpu * grad_accum_steps
|
| 13 |
+
image_size: [1024, 2048, 2560, 3072, 3584, 3840, 4096, 4608] # possible image resolution
|
| 14 |
+
multi_aspect_ratio: [1/1, 1/2, 1/3, 2/3, 3/4, 1/5, 2/5, 3/5, 4/5, 1/6, 5/6, 9/16]
|
| 15 |
+
grad_accum_steps: 2
|
| 16 |
+
updates: 40000
|
| 17 |
+
backup_every: 5000
|
| 18 |
+
save_every: 256
|
| 19 |
+
warmup_updates: 1
|
| 20 |
+
use_ddp: True
|
| 21 |
+
|
| 22 |
+
# GDF
|
| 23 |
+
adaptive_loss_weight: True
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
webdataset_path: path to your personalized training dataset
|
| 27 |
+
effnet_checkpoint_path: models/effnet_encoder.safetensors
|
| 28 |
+
previewer_checkpoint_path: models/previewer.safetensors
|
| 29 |
+
generator_checkpoint_path: models/stage_c_bf16.safetensors
|
core/__init__.py
ADDED
|
@@ -0,0 +1,372 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import yaml
|
| 3 |
+
import torch
|
| 4 |
+
from torch import nn
|
| 5 |
+
import wandb
|
| 6 |
+
import json
|
| 7 |
+
from abc import ABC, abstractmethod
|
| 8 |
+
from dataclasses import dataclass
|
| 9 |
+
from torch.utils.data import Dataset, DataLoader
|
| 10 |
+
|
| 11 |
+
from torch.distributed import init_process_group, destroy_process_group, barrier
|
| 12 |
+
from torch.distributed.fsdp import (
|
| 13 |
+
FullyShardedDataParallel as FSDP,
|
| 14 |
+
FullStateDictConfig,
|
| 15 |
+
MixedPrecision,
|
| 16 |
+
ShardingStrategy,
|
| 17 |
+
StateDictType
|
| 18 |
+
)
|
| 19 |
+
|
| 20 |
+
from .utils import Base, EXPECTED, EXPECTED_TRAIN
|
| 21 |
+
from .utils import create_folder_if_necessary, safe_save, load_or_fail
|
| 22 |
+
|
| 23 |
+
# pylint: disable=unused-argument
|
| 24 |
+
class WarpCore(ABC):
|
| 25 |
+
@dataclass(frozen=True)
|
| 26 |
+
class Config(Base):
|
| 27 |
+
experiment_id: str = EXPECTED_TRAIN
|
| 28 |
+
checkpoint_path: str = EXPECTED_TRAIN
|
| 29 |
+
output_path: str = EXPECTED_TRAIN
|
| 30 |
+
checkpoint_extension: str = "safetensors"
|
| 31 |
+
dist_file_subfolder: str = ""
|
| 32 |
+
allow_tf32: bool = True
|
| 33 |
+
|
| 34 |
+
wandb_project: str = None
|
| 35 |
+
wandb_entity: str = None
|
| 36 |
+
|
| 37 |
+
@dataclass() # not frozen, means that fields are mutable
|
| 38 |
+
class Info(): # not inheriting from Base, because we don't want to enforce the default fields
|
| 39 |
+
wandb_run_id: str = None
|
| 40 |
+
total_steps: int = 0
|
| 41 |
+
iter: int = 0
|
| 42 |
+
|
| 43 |
+
@dataclass(frozen=True)
|
| 44 |
+
class Data(Base):
|
| 45 |
+
dataset: Dataset = EXPECTED
|
| 46 |
+
dataloader: DataLoader = EXPECTED
|
| 47 |
+
iterator: any = EXPECTED
|
| 48 |
+
|
| 49 |
+
@dataclass(frozen=True)
|
| 50 |
+
class Models(Base):
|
| 51 |
+
pass
|
| 52 |
+
|
| 53 |
+
@dataclass(frozen=True)
|
| 54 |
+
class Optimizers(Base):
|
| 55 |
+
pass
|
| 56 |
+
|
| 57 |
+
@dataclass(frozen=True)
|
| 58 |
+
class Schedulers(Base):
|
| 59 |
+
pass
|
| 60 |
+
|
| 61 |
+
@dataclass(frozen=True)
|
| 62 |
+
class Extras(Base):
|
| 63 |
+
pass
|
| 64 |
+
# ---------------------------------------
|
| 65 |
+
info: Info
|
| 66 |
+
config: Config
|
| 67 |
+
|
| 68 |
+
# FSDP stuff
|
| 69 |
+
fsdp_defaults = {
|
| 70 |
+
"sharding_strategy": ShardingStrategy.SHARD_GRAD_OP,
|
| 71 |
+
"cpu_offload": None,
|
| 72 |
+
"mixed_precision": MixedPrecision(
|
| 73 |
+
param_dtype=torch.bfloat16,
|
| 74 |
+
reduce_dtype=torch.bfloat16,
|
| 75 |
+
buffer_dtype=torch.bfloat16,
|
| 76 |
+
),
|
| 77 |
+
"limit_all_gathers": True,
|
| 78 |
+
}
|
| 79 |
+
fsdp_fullstate_save_policy = FullStateDictConfig(
|
| 80 |
+
offload_to_cpu=True, rank0_only=True
|
| 81 |
+
)
|
| 82 |
+
# ------------
|
| 83 |
+
|
| 84 |
+
# OVERRIDEABLE METHODS
|
| 85 |
+
|
| 86 |
+
# [optionally] setup extra stuff, will be called BEFORE the models & optimizers are setup
|
| 87 |
+
def setup_extras_pre(self) -> Extras:
|
| 88 |
+
return self.Extras()
|
| 89 |
+
|
| 90 |
+
# setup dataset & dataloader, return a dict contained dataser, dataloader and/or iterator
|
| 91 |
+
@abstractmethod
|
| 92 |
+
def setup_data(self, extras: Extras) -> Data:
|
| 93 |
+
raise NotImplementedError("This method needs to be overriden")
|
| 94 |
+
|
| 95 |
+
# return a dict with all models that are going to be used in the training
|
| 96 |
+
@abstractmethod
|
| 97 |
+
def setup_models(self, extras: Extras) -> Models:
|
| 98 |
+
raise NotImplementedError("This method needs to be overriden")
|
| 99 |
+
|
| 100 |
+
# return a dict with all optimizers that are going to be used in the training
|
| 101 |
+
@abstractmethod
|
| 102 |
+
def setup_optimizers(self, extras: Extras, models: Models) -> Optimizers:
|
| 103 |
+
raise NotImplementedError("This method needs to be overriden")
|
| 104 |
+
|
| 105 |
+
# [optionally] return a dict with all schedulers that are going to be used in the training
|
| 106 |
+
def setup_schedulers(self, extras: Extras, models: Models, optimizers: Optimizers) -> Schedulers:
|
| 107 |
+
return self.Schedulers()
|
| 108 |
+
|
| 109 |
+
# [optionally] setup extra stuff, will be called AFTER the models & optimizers are setup
|
| 110 |
+
def setup_extras_post(self, extras: Extras, models: Models, optimizers: Optimizers, schedulers: Schedulers) -> Extras:
|
| 111 |
+
return self.Extras.from_dict(extras.to_dict())
|
| 112 |
+
|
| 113 |
+
# perform the training here
|
| 114 |
+
@abstractmethod
|
| 115 |
+
def train(self, data: Data, extras: Extras, models: Models, optimizers: Optimizers, schedulers: Schedulers):
|
| 116 |
+
raise NotImplementedError("This method needs to be overriden")
|
| 117 |
+
# ------------
|
| 118 |
+
|
| 119 |
+
def setup_info(self, full_path=None) -> Info:
|
| 120 |
+
if full_path is None:
|
| 121 |
+
full_path = (f"{self.config.checkpoint_path}/{self.config.experiment_id}/info.json")
|
| 122 |
+
info_dict = load_or_fail(full_path, wandb_run_id=None) or {}
|
| 123 |
+
info_dto = self.Info(**info_dict)
|
| 124 |
+
if info_dto.total_steps > 0 and self.is_main_node:
|
| 125 |
+
print(">>> RESUMING TRAINING FROM ITER ", info_dto.total_steps)
|
| 126 |
+
return info_dto
|
| 127 |
+
|
| 128 |
+
def setup_config(self, config_file_path=None, config_dict=None, training=True) -> Config:
|
| 129 |
+
if config_file_path is not None:
|
| 130 |
+
if config_file_path.endswith(".yml") or config_file_path.endswith(".yaml"):
|
| 131 |
+
with open(config_file_path, "r", encoding="utf-8") as file:
|
| 132 |
+
loaded_config = yaml.safe_load(file)
|
| 133 |
+
elif config_file_path.endswith(".json"):
|
| 134 |
+
with open(config_file_path, "r", encoding="utf-8") as file:
|
| 135 |
+
loaded_config = json.load(file)
|
| 136 |
+
else:
|
| 137 |
+
raise ValueError("Config file must be either a .yml|.yaml or .json file")
|
| 138 |
+
return self.Config.from_dict({**loaded_config, 'training': training})
|
| 139 |
+
if config_dict is not None:
|
| 140 |
+
return self.Config.from_dict({**config_dict, 'training': training})
|
| 141 |
+
return self.Config(training=training)
|
| 142 |
+
|
| 143 |
+
def setup_ddp(self, experiment_id, single_gpu=False):
|
| 144 |
+
if not single_gpu:
|
| 145 |
+
local_rank = int(os.environ.get("SLURM_LOCALID"))
|
| 146 |
+
process_id = int(os.environ.get("SLURM_PROCID"))
|
| 147 |
+
world_size = int(os.environ.get("SLURM_NNODES")) * torch.cuda.device_count()
|
| 148 |
+
|
| 149 |
+
self.process_id = process_id
|
| 150 |
+
self.is_main_node = process_id == 0
|
| 151 |
+
self.device = torch.device(local_rank)
|
| 152 |
+
self.world_size = world_size
|
| 153 |
+
|
| 154 |
+
dist_file_path = f"{os.getcwd()}/{self.config.dist_file_subfolder}dist_file_{experiment_id}"
|
| 155 |
+
# if os.path.exists(dist_file_path) and self.is_main_node:
|
| 156 |
+
# os.remove(dist_file_path)
|
| 157 |
+
|
| 158 |
+
torch.cuda.set_device(local_rank)
|
| 159 |
+
init_process_group(
|
| 160 |
+
backend="nccl",
|
| 161 |
+
rank=process_id,
|
| 162 |
+
world_size=world_size,
|
| 163 |
+
init_method=f"file://{dist_file_path}",
|
| 164 |
+
)
|
| 165 |
+
print(f"[GPU {process_id}] READY")
|
| 166 |
+
else:
|
| 167 |
+
print("Running in single thread, DDP not enabled.")
|
| 168 |
+
|
| 169 |
+
def setup_wandb(self):
|
| 170 |
+
if self.is_main_node and self.config.wandb_project is not None:
|
| 171 |
+
self.info.wandb_run_id = self.info.wandb_run_id or wandb.util.generate_id()
|
| 172 |
+
wandb.init(project=self.config.wandb_project, entity=self.config.wandb_entity, name=self.config.experiment_id, id=self.info.wandb_run_id, resume="allow", config=self.config.to_dict())
|
| 173 |
+
|
| 174 |
+
if self.info.total_steps > 0:
|
| 175 |
+
wandb.alert(title=f"Training {self.info.wandb_run_id} resumed", text=f"Training {self.info.wandb_run_id} resumed from step {self.info.total_steps}")
|
| 176 |
+
else:
|
| 177 |
+
wandb.alert(title=f"Training {self.info.wandb_run_id} started", text=f"Training {self.info.wandb_run_id} started")
|
| 178 |
+
|
| 179 |
+
# LOAD UTILITIES ----------
|
| 180 |
+
def load_model(self, model, model_id=None, full_path=None, strict=True):
|
| 181 |
+
print('in line 181 load model', type(model), model_id, full_path, strict)
|
| 182 |
+
if model_id is not None and full_path is None:
|
| 183 |
+
full_path = f"{self.config.checkpoint_path}/{self.config.experiment_id}/{model_id}.{self.config.checkpoint_extension}"
|
| 184 |
+
elif full_path is None and model_id is None:
|
| 185 |
+
raise ValueError(
|
| 186 |
+
"This method expects either 'model_id' or 'full_path' to be defined"
|
| 187 |
+
)
|
| 188 |
+
|
| 189 |
+
checkpoint = load_or_fail(full_path, wandb_run_id=self.info.wandb_run_id if self.is_main_node else None)
|
| 190 |
+
if checkpoint is not None:
|
| 191 |
+
model.load_state_dict(checkpoint, strict=strict)
|
| 192 |
+
del checkpoint
|
| 193 |
+
|
| 194 |
+
return model
|
| 195 |
+
|
| 196 |
+
def load_optimizer(self, optim, optim_id=None, full_path=None, fsdp_model=None):
|
| 197 |
+
if optim_id is not None and full_path is None:
|
| 198 |
+
full_path = f"{self.config.checkpoint_path}/{self.config.experiment_id}/{optim_id}.pt"
|
| 199 |
+
elif full_path is None and optim_id is None:
|
| 200 |
+
raise ValueError(
|
| 201 |
+
"This method expects either 'optim_id' or 'full_path' to be defined"
|
| 202 |
+
)
|
| 203 |
+
|
| 204 |
+
checkpoint = load_or_fail(full_path, wandb_run_id=self.info.wandb_run_id if self.is_main_node else None)
|
| 205 |
+
if checkpoint is not None:
|
| 206 |
+
try:
|
| 207 |
+
if fsdp_model is not None:
|
| 208 |
+
sharded_optimizer_state_dict = (
|
| 209 |
+
FSDP.scatter_full_optim_state_dict( # <---- FSDP
|
| 210 |
+
checkpoint
|
| 211 |
+
if (
|
| 212 |
+
self.is_main_node
|
| 213 |
+
or self.fsdp_defaults["sharding_strategy"]
|
| 214 |
+
== ShardingStrategy.NO_SHARD
|
| 215 |
+
)
|
| 216 |
+
else None,
|
| 217 |
+
fsdp_model,
|
| 218 |
+
)
|
| 219 |
+
)
|
| 220 |
+
optim.load_state_dict(sharded_optimizer_state_dict)
|
| 221 |
+
del checkpoint, sharded_optimizer_state_dict
|
| 222 |
+
else:
|
| 223 |
+
optim.load_state_dict(checkpoint)
|
| 224 |
+
# pylint: disable=broad-except
|
| 225 |
+
except Exception as e:
|
| 226 |
+
print("!!! Failed loading optimizer, skipping... Exception:", e)
|
| 227 |
+
|
| 228 |
+
return optim
|
| 229 |
+
|
| 230 |
+
# SAVE UTILITIES ----------
|
| 231 |
+
def save_info(self, info, suffix=""):
|
| 232 |
+
full_path = f"{self.config.checkpoint_path}/{self.config.experiment_id}/info{suffix}.json"
|
| 233 |
+
create_folder_if_necessary(full_path)
|
| 234 |
+
if self.is_main_node:
|
| 235 |
+
safe_save(vars(self.info), full_path)
|
| 236 |
+
|
| 237 |
+
def save_model(self, model, model_id=None, full_path=None, is_fsdp=False):
|
| 238 |
+
if model_id is not None and full_path is None:
|
| 239 |
+
full_path = f"{self.config.checkpoint_path}/{self.config.experiment_id}/{model_id}.{self.config.checkpoint_extension}"
|
| 240 |
+
elif full_path is None and model_id is None:
|
| 241 |
+
raise ValueError(
|
| 242 |
+
"This method expects either 'model_id' or 'full_path' to be defined"
|
| 243 |
+
)
|
| 244 |
+
create_folder_if_necessary(full_path)
|
| 245 |
+
if is_fsdp:
|
| 246 |
+
with FSDP.summon_full_params(model):
|
| 247 |
+
pass
|
| 248 |
+
with FSDP.state_dict_type(
|
| 249 |
+
model, StateDictType.FULL_STATE_DICT, self.fsdp_fullstate_save_policy
|
| 250 |
+
):
|
| 251 |
+
checkpoint = model.state_dict()
|
| 252 |
+
if self.is_main_node:
|
| 253 |
+
safe_save(checkpoint, full_path)
|
| 254 |
+
del checkpoint
|
| 255 |
+
else:
|
| 256 |
+
if self.is_main_node:
|
| 257 |
+
checkpoint = model.state_dict()
|
| 258 |
+
safe_save(checkpoint, full_path)
|
| 259 |
+
del checkpoint
|
| 260 |
+
|
| 261 |
+
def save_optimizer(self, optim, optim_id=None, full_path=None, fsdp_model=None):
|
| 262 |
+
if optim_id is not None and full_path is None:
|
| 263 |
+
full_path = f"{self.config.checkpoint_path}/{self.config.experiment_id}/{optim_id}.pt"
|
| 264 |
+
elif full_path is None and optim_id is None:
|
| 265 |
+
raise ValueError(
|
| 266 |
+
"This method expects either 'optim_id' or 'full_path' to be defined"
|
| 267 |
+
)
|
| 268 |
+
create_folder_if_necessary(full_path)
|
| 269 |
+
if fsdp_model is not None:
|
| 270 |
+
optim_statedict = FSDP.full_optim_state_dict(fsdp_model, optim)
|
| 271 |
+
if self.is_main_node:
|
| 272 |
+
safe_save(optim_statedict, full_path)
|
| 273 |
+
del optim_statedict
|
| 274 |
+
else:
|
| 275 |
+
if self.is_main_node:
|
| 276 |
+
checkpoint = optim.state_dict()
|
| 277 |
+
safe_save(checkpoint, full_path)
|
| 278 |
+
del checkpoint
|
| 279 |
+
# -----
|
| 280 |
+
|
| 281 |
+
def __init__(self, config_file_path=None, config_dict=None, device="cpu", training=True):
|
| 282 |
+
# Temporary setup, will be overriden by setup_ddp if required
|
| 283 |
+
self.device = device
|
| 284 |
+
self.process_id = 0
|
| 285 |
+
self.is_main_node = True
|
| 286 |
+
self.world_size = 1
|
| 287 |
+
# ----
|
| 288 |
+
|
| 289 |
+
self.config: self.Config = self.setup_config(config_file_path, config_dict, training)
|
| 290 |
+
self.info: self.Info = self.setup_info()
|
| 291 |
+
|
| 292 |
+
def __call__(self, single_gpu=False):
|
| 293 |
+
self.setup_ddp(self.config.experiment_id, single_gpu=single_gpu) # this will change the device to the CUDA rank
|
| 294 |
+
self.setup_wandb()
|
| 295 |
+
if self.config.allow_tf32:
|
| 296 |
+
torch.backends.cuda.matmul.allow_tf32 = True
|
| 297 |
+
torch.backends.cudnn.allow_tf32 = True
|
| 298 |
+
|
| 299 |
+
if self.is_main_node:
|
| 300 |
+
print()
|
| 301 |
+
print("**STARTIG JOB WITH CONFIG:**")
|
| 302 |
+
print(yaml.dump(self.config.to_dict(), default_flow_style=False))
|
| 303 |
+
print("------------------------------------")
|
| 304 |
+
print()
|
| 305 |
+
print("**INFO:**")
|
| 306 |
+
print(yaml.dump(vars(self.info), default_flow_style=False))
|
| 307 |
+
print("------------------------------------")
|
| 308 |
+
print()
|
| 309 |
+
|
| 310 |
+
# SETUP STUFF
|
| 311 |
+
extras = self.setup_extras_pre()
|
| 312 |
+
assert extras is not None, "setup_extras_pre() must return a DTO"
|
| 313 |
+
|
| 314 |
+
data = self.setup_data(extras)
|
| 315 |
+
assert data is not None, "setup_data() must return a DTO"
|
| 316 |
+
if self.is_main_node:
|
| 317 |
+
print("**DATA:**")
|
| 318 |
+
print(yaml.dump({k:type(v).__name__ for k, v in data.to_dict().items()}, default_flow_style=False))
|
| 319 |
+
print("------------------------------------")
|
| 320 |
+
print()
|
| 321 |
+
|
| 322 |
+
models = self.setup_models(extras)
|
| 323 |
+
assert models is not None, "setup_models() must return a DTO"
|
| 324 |
+
if self.is_main_node:
|
| 325 |
+
print("**MODELS:**")
|
| 326 |
+
print(yaml.dump({
|
| 327 |
+
k:f"{type(v).__name__} - {f'trainable params {sum(p.numel() for p in v.parameters() if p.requires_grad)}' if isinstance(v, nn.Module) else 'Not a nn.Module'}" for k, v in models.to_dict().items()
|
| 328 |
+
}, default_flow_style=False))
|
| 329 |
+
print("------------------------------------")
|
| 330 |
+
print()
|
| 331 |
+
|
| 332 |
+
optimizers = self.setup_optimizers(extras, models)
|
| 333 |
+
assert optimizers is not None, "setup_optimizers() must return a DTO"
|
| 334 |
+
if self.is_main_node:
|
| 335 |
+
print("**OPTIMIZERS:**")
|
| 336 |
+
print(yaml.dump({k:type(v).__name__ for k, v in optimizers.to_dict().items()}, default_flow_style=False))
|
| 337 |
+
print("------------------------------------")
|
| 338 |
+
print()
|
| 339 |
+
|
| 340 |
+
schedulers = self.setup_schedulers(extras, models, optimizers)
|
| 341 |
+
assert schedulers is not None, "setup_schedulers() must return a DTO"
|
| 342 |
+
if self.is_main_node:
|
| 343 |
+
print("**SCHEDULERS:**")
|
| 344 |
+
print(yaml.dump({k:type(v).__name__ for k, v in schedulers.to_dict().items()}, default_flow_style=False))
|
| 345 |
+
print("------------------------------------")
|
| 346 |
+
print()
|
| 347 |
+
|
| 348 |
+
post_extras =self.setup_extras_post(extras, models, optimizers, schedulers)
|
| 349 |
+
assert post_extras is not None, "setup_extras_post() must return a DTO"
|
| 350 |
+
extras = self.Extras.from_dict({ **extras.to_dict(),**post_extras.to_dict() })
|
| 351 |
+
if self.is_main_node:
|
| 352 |
+
print("**EXTRAS:**")
|
| 353 |
+
print(yaml.dump({k:f"{v}" for k, v in extras.to_dict().items()}, default_flow_style=False))
|
| 354 |
+
print("------------------------------------")
|
| 355 |
+
print()
|
| 356 |
+
# -------
|
| 357 |
+
|
| 358 |
+
# TRAIN
|
| 359 |
+
if self.is_main_node:
|
| 360 |
+
print("**TRAINING STARTING...**")
|
| 361 |
+
self.train(data, extras, models, optimizers, schedulers)
|
| 362 |
+
|
| 363 |
+
if single_gpu is False:
|
| 364 |
+
barrier()
|
| 365 |
+
destroy_process_group()
|
| 366 |
+
if self.is_main_node:
|
| 367 |
+
print()
|
| 368 |
+
print("------------------------------------")
|
| 369 |
+
print()
|
| 370 |
+
print("**TRAINING COMPLETE**")
|
| 371 |
+
if self.config.wandb_project is not None:
|
| 372 |
+
wandb.alert(title=f"Training {self.info.wandb_run_id} finished", text=f"Training {self.info.wandb_run_id} finished")
|
core/data/__init__.py
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
import subprocess
|
| 3 |
+
import yaml
|
| 4 |
+
import os
|
| 5 |
+
from .bucketeer import Bucketeer
|
| 6 |
+
|
| 7 |
+
class MultiFilter():
|
| 8 |
+
def __init__(self, rules, default=False):
|
| 9 |
+
self.rules = rules
|
| 10 |
+
self.default = default
|
| 11 |
+
|
| 12 |
+
def __call__(self, x):
|
| 13 |
+
try:
|
| 14 |
+
x_json = x['json']
|
| 15 |
+
if isinstance(x_json, bytes):
|
| 16 |
+
x_json = json.loads(x_json)
|
| 17 |
+
validations = []
|
| 18 |
+
for k, r in self.rules.items():
|
| 19 |
+
if isinstance(k, tuple):
|
| 20 |
+
v = r(*[x_json[kv] for kv in k])
|
| 21 |
+
else:
|
| 22 |
+
v = r(x_json[k])
|
| 23 |
+
validations.append(v)
|
| 24 |
+
return all(validations)
|
| 25 |
+
except Exception:
|
| 26 |
+
return False
|
| 27 |
+
|
| 28 |
+
class MultiGetter():
|
| 29 |
+
def __init__(self, rules):
|
| 30 |
+
self.rules = rules
|
| 31 |
+
|
| 32 |
+
def __call__(self, x_json):
|
| 33 |
+
if isinstance(x_json, bytes):
|
| 34 |
+
x_json = json.loads(x_json)
|
| 35 |
+
outputs = []
|
| 36 |
+
for k, r in self.rules.items():
|
| 37 |
+
if isinstance(k, tuple):
|
| 38 |
+
v = r(*[x_json[kv] for kv in k])
|
| 39 |
+
else:
|
| 40 |
+
v = r(x_json[k])
|
| 41 |
+
outputs.append(v)
|
| 42 |
+
if len(outputs) == 1:
|
| 43 |
+
outputs = outputs[0]
|
| 44 |
+
return outputs
|
| 45 |
+
|
| 46 |
+
def setup_webdataset_path(paths, cache_path=None):
|
| 47 |
+
if cache_path is None or not os.path.exists(cache_path):
|
| 48 |
+
tar_paths = []
|
| 49 |
+
if isinstance(paths, str):
|
| 50 |
+
paths = [paths]
|
| 51 |
+
for path in paths:
|
| 52 |
+
if path.strip().endswith(".tar"):
|
| 53 |
+
# Avoid looking up s3 if we already have a tar file
|
| 54 |
+
tar_paths.append(path)
|
| 55 |
+
continue
|
| 56 |
+
bucket = "/".join(path.split("/")[:3])
|
| 57 |
+
result = subprocess.run([f"aws s3 ls {path} --recursive | awk '{{print $4}}'"], stdout=subprocess.PIPE, shell=True, check=True)
|
| 58 |
+
files = result.stdout.decode('utf-8').split()
|
| 59 |
+
files = [f"{bucket}/{f}" for f in files if f.endswith(".tar")]
|
| 60 |
+
tar_paths += files
|
| 61 |
+
|
| 62 |
+
with open(cache_path, 'w', encoding='utf-8') as outfile:
|
| 63 |
+
yaml.dump(tar_paths, outfile, default_flow_style=False)
|
| 64 |
+
else:
|
| 65 |
+
with open(cache_path, 'r', encoding='utf-8') as file:
|
| 66 |
+
tar_paths = yaml.safe_load(file)
|
| 67 |
+
|
| 68 |
+
tar_paths_str = ",".join([f"{p}" for p in tar_paths])
|
| 69 |
+
return f"pipe:aws s3 cp {{ {tar_paths_str} }} -"
|
core/data/bucketeer.py
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
import torchvision
|
| 3 |
+
import numpy as np
|
| 4 |
+
from torchtools.transforms import SmartCrop
|
| 5 |
+
import math
|
| 6 |
+
|
| 7 |
+
class Bucketeer():
|
| 8 |
+
def __init__(self, dataloader, density=256*256, factor=8, ratios=[1/1, 1/2, 3/4, 3/5, 4/5, 6/9, 9/16], reverse_list=True, randomize_p=0.3, randomize_q=0.2, crop_mode='random', p_random_ratio=0.0, interpolate_nearest=False):
|
| 9 |
+
assert crop_mode in ['center', 'random', 'smart']
|
| 10 |
+
self.crop_mode = crop_mode
|
| 11 |
+
self.ratios = ratios
|
| 12 |
+
if reverse_list:
|
| 13 |
+
for r in list(ratios):
|
| 14 |
+
if 1/r not in self.ratios:
|
| 15 |
+
self.ratios.append(1/r)
|
| 16 |
+
self.sizes = {}
|
| 17 |
+
for dd in density:
|
| 18 |
+
self.sizes[dd]= [(int(((dd/r)**0.5//factor)*factor), int(((dd*r)**0.5//factor)*factor)) for r in ratios]
|
| 19 |
+
|
| 20 |
+
self.batch_size = dataloader.batch_size
|
| 21 |
+
self.iterator = iter(dataloader)
|
| 22 |
+
all_sizes = []
|
| 23 |
+
for k, vs in self.sizes.items():
|
| 24 |
+
all_sizes += vs
|
| 25 |
+
self.buckets = {s: [] for s in all_sizes}
|
| 26 |
+
self.smartcrop = SmartCrop(int(density**0.5), randomize_p, randomize_q) if self.crop_mode=='smart' else None
|
| 27 |
+
self.p_random_ratio = p_random_ratio
|
| 28 |
+
self.interpolate_nearest = interpolate_nearest
|
| 29 |
+
|
| 30 |
+
def get_available_batch(self):
|
| 31 |
+
for b in self.buckets:
|
| 32 |
+
if len(self.buckets[b]) >= self.batch_size:
|
| 33 |
+
batch = self.buckets[b][:self.batch_size]
|
| 34 |
+
self.buckets[b] = self.buckets[b][self.batch_size:]
|
| 35 |
+
return batch
|
| 36 |
+
return None
|
| 37 |
+
|
| 38 |
+
def get_closest_size(self, x):
|
| 39 |
+
w, h = x.size(-1), x.size(-2)
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
best_size_idx = np.argmin([abs(w/h-r) for r in self.ratios])
|
| 43 |
+
find_dict = {dd : abs(w*h - self.sizes[dd][best_size_idx][0]*self.sizes[dd][best_size_idx][1]) for dd, vv in self.sizes.items()}
|
| 44 |
+
min_ = find_dict[list(find_dict.keys())[0]]
|
| 45 |
+
find_size = self.sizes[list(find_dict.keys())[0]][best_size_idx]
|
| 46 |
+
for dd, val in find_dict.items():
|
| 47 |
+
if val < min_:
|
| 48 |
+
min_ = val
|
| 49 |
+
find_size = self.sizes[dd][best_size_idx]
|
| 50 |
+
|
| 51 |
+
return find_size
|
| 52 |
+
|
| 53 |
+
def get_resize_size(self, orig_size, tgt_size):
|
| 54 |
+
if (tgt_size[1]/tgt_size[0] - 1) * (orig_size[1]/orig_size[0] - 1) >= 0:
|
| 55 |
+
alt_min = int(math.ceil(max(tgt_size)*min(orig_size)/max(orig_size)))
|
| 56 |
+
resize_size = max(alt_min, min(tgt_size))
|
| 57 |
+
else:
|
| 58 |
+
alt_max = int(math.ceil(min(tgt_size)*max(orig_size)/min(orig_size)))
|
| 59 |
+
resize_size = max(alt_max, max(tgt_size))
|
| 60 |
+
|
| 61 |
+
return resize_size
|
| 62 |
+
|
| 63 |
+
def __next__(self):
|
| 64 |
+
batch = self.get_available_batch()
|
| 65 |
+
while batch is None:
|
| 66 |
+
elements = next(self.iterator)
|
| 67 |
+
for dct in elements:
|
| 68 |
+
img = dct['images']
|
| 69 |
+
size = self.get_closest_size(img)
|
| 70 |
+
resize_size = self.get_resize_size(img.shape[-2:], size)
|
| 71 |
+
|
| 72 |
+
if self.interpolate_nearest:
|
| 73 |
+
img = torchvision.transforms.functional.resize(img, resize_size, interpolation=torchvision.transforms.InterpolationMode.NEAREST)
|
| 74 |
+
else:
|
| 75 |
+
img = torchvision.transforms.functional.resize(img, resize_size, interpolation=torchvision.transforms.InterpolationMode.BILINEAR, antialias=True)
|
| 76 |
+
if self.crop_mode == 'center':
|
| 77 |
+
img = torchvision.transforms.functional.center_crop(img, size)
|
| 78 |
+
elif self.crop_mode == 'random':
|
| 79 |
+
img = torchvision.transforms.RandomCrop(size)(img)
|
| 80 |
+
elif self.crop_mode == 'smart':
|
| 81 |
+
self.smartcrop.output_size = size
|
| 82 |
+
img = self.smartcrop(img)
|
| 83 |
+
|
| 84 |
+
self.buckets[size].append({**{'images': img}, **{k:dct[k] for k in dct if k != 'images'}})
|
| 85 |
+
batch = self.get_available_batch()
|
| 86 |
+
|
| 87 |
+
out = {k:[batch[i][k] for i in range(len(batch))] for k in batch[0]}
|
| 88 |
+
return {k: torch.stack(o, dim=0) if isinstance(o[0], torch.Tensor) else o for k, o in out.items()}
|
core/data/bucketeer_deg.py
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
import torchvision
|
| 3 |
+
import numpy as np
|
| 4 |
+
from torchtools.transforms import SmartCrop
|
| 5 |
+
import math
|
| 6 |
+
|
| 7 |
+
class Bucketeer():
|
| 8 |
+
def __init__(self, dataloader, density=256*256, factor=8, ratios=[1/1, 1/2, 3/4, 3/5, 4/5, 6/9, 9/16], reverse_list=True, randomize_p=0.3, randomize_q=0.2, crop_mode='random', p_random_ratio=0.0, interpolate_nearest=False):
|
| 9 |
+
assert crop_mode in ['center', 'random', 'smart']
|
| 10 |
+
self.crop_mode = crop_mode
|
| 11 |
+
self.ratios = ratios
|
| 12 |
+
if reverse_list:
|
| 13 |
+
for r in list(ratios):
|
| 14 |
+
if 1/r not in self.ratios:
|
| 15 |
+
self.ratios.append(1/r)
|
| 16 |
+
self.sizes = {}
|
| 17 |
+
for dd in density:
|
| 18 |
+
self.sizes[dd]= [(int(((dd/r)**0.5//factor)*factor), int(((dd*r)**0.5//factor)*factor)) for r in ratios]
|
| 19 |
+
print('in line 17 buckteer', self.sizes)
|
| 20 |
+
self.batch_size = dataloader.batch_size
|
| 21 |
+
self.iterator = iter(dataloader)
|
| 22 |
+
all_sizes = []
|
| 23 |
+
for k, vs in self.sizes.items():
|
| 24 |
+
all_sizes += vs
|
| 25 |
+
self.buckets = {s: [] for s in all_sizes}
|
| 26 |
+
self.smartcrop = SmartCrop(int(density**0.5), randomize_p, randomize_q) if self.crop_mode=='smart' else None
|
| 27 |
+
self.p_random_ratio = p_random_ratio
|
| 28 |
+
self.interpolate_nearest = interpolate_nearest
|
| 29 |
+
|
| 30 |
+
def get_available_batch(self):
|
| 31 |
+
for b in self.buckets:
|
| 32 |
+
if len(self.buckets[b]) >= self.batch_size:
|
| 33 |
+
batch = self.buckets[b][:self.batch_size]
|
| 34 |
+
self.buckets[b] = self.buckets[b][self.batch_size:]
|
| 35 |
+
return batch
|
| 36 |
+
return None
|
| 37 |
+
|
| 38 |
+
def get_closest_size(self, x):
|
| 39 |
+
w, h = x.size(-1), x.size(-2)
|
| 40 |
+
#if self.p_random_ratio > 0 and np.random.rand() < self.p_random_ratio:
|
| 41 |
+
# best_size_idx = np.random.randint(len(self.ratios))
|
| 42 |
+
#print('in line 41 get closes size', best_size_idx, x.shape, self.p_random_ratio)
|
| 43 |
+
#else:
|
| 44 |
+
|
| 45 |
+
best_size_idx = np.argmin([abs(w/h-r) for r in self.ratios])
|
| 46 |
+
find_dict = {dd : abs(w*h - self.sizes[dd][best_size_idx][0]*self.sizes[dd][best_size_idx][1]) for dd, vv in self.sizes.items()}
|
| 47 |
+
min_ = find_dict[list(find_dict.keys())[0]]
|
| 48 |
+
find_size = self.sizes[list(find_dict.keys())[0]][best_size_idx]
|
| 49 |
+
for dd, val in find_dict.items():
|
| 50 |
+
if val < min_:
|
| 51 |
+
min_ = val
|
| 52 |
+
find_size = self.sizes[dd][best_size_idx]
|
| 53 |
+
|
| 54 |
+
return find_size
|
| 55 |
+
|
| 56 |
+
def get_resize_size(self, orig_size, tgt_size):
|
| 57 |
+
if (tgt_size[1]/tgt_size[0] - 1) * (orig_size[1]/orig_size[0] - 1) >= 0:
|
| 58 |
+
alt_min = int(math.ceil(max(tgt_size)*min(orig_size)/max(orig_size)))
|
| 59 |
+
resize_size = max(alt_min, min(tgt_size))
|
| 60 |
+
else:
|
| 61 |
+
alt_max = int(math.ceil(min(tgt_size)*max(orig_size)/min(orig_size)))
|
| 62 |
+
resize_size = max(alt_max, max(tgt_size))
|
| 63 |
+
#print('in line 50', orig_size, tgt_size, resize_size)
|
| 64 |
+
return resize_size
|
| 65 |
+
|
| 66 |
+
def __next__(self):
|
| 67 |
+
batch = self.get_available_batch()
|
| 68 |
+
while batch is None:
|
| 69 |
+
elements = next(self.iterator)
|
| 70 |
+
for dct in elements:
|
| 71 |
+
img = dct['images']
|
| 72 |
+
size = self.get_closest_size(img)
|
| 73 |
+
resize_size = self.get_resize_size(img.shape[-2:], size)
|
| 74 |
+
#print('in line 74', img.size(), resize_size)
|
| 75 |
+
if self.interpolate_nearest:
|
| 76 |
+
img = torchvision.transforms.functional.resize(img, resize_size, interpolation=torchvision.transforms.InterpolationMode.NEAREST)
|
| 77 |
+
else:
|
| 78 |
+
img = torchvision.transforms.functional.resize(img, resize_size, interpolation=torchvision.transforms.InterpolationMode.BILINEAR, antialias=True)
|
| 79 |
+
if self.crop_mode == 'center':
|
| 80 |
+
img = torchvision.transforms.functional.center_crop(img, size)
|
| 81 |
+
elif self.crop_mode == 'random':
|
| 82 |
+
img = torchvision.transforms.RandomCrop(size)(img)
|
| 83 |
+
elif self.crop_mode == 'smart':
|
| 84 |
+
self.smartcrop.output_size = size
|
| 85 |
+
img = self.smartcrop(img)
|
| 86 |
+
print('in line 86 bucketeer', type(img), img.shape, torch.max(img), torch.min(img))
|
| 87 |
+
self.buckets[size].append({**{'images': img}, **{k:dct[k] for k in dct if k != 'images'}})
|
| 88 |
+
batch = self.get_available_batch()
|
| 89 |
+
|
| 90 |
+
out = {k:[batch[i][k] for i in range(len(batch))] for k in batch[0]}
|
| 91 |
+
return {k: torch.stack(o, dim=0) if isinstance(o[0], torch.Tensor) else o for k, o in out.items()}
|
core/scripts/__init__.py
ADDED
|
File without changes
|
core/scripts/cli.py
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import sys
|
| 2 |
+
import argparse
|
| 3 |
+
from .. import WarpCore
|
| 4 |
+
from .. import templates
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
def template_init(args):
|
| 8 |
+
return ''''
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
'''.strip()
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def init_template(args):
|
| 15 |
+
parser = argparse.ArgumentParser(description='WarpCore template init tool')
|
| 16 |
+
parser.add_argument('-t', '--template', type=str, default='WarpCore')
|
| 17 |
+
args = parser.parse_args(args)
|
| 18 |
+
|
| 19 |
+
if args.template == 'WarpCore':
|
| 20 |
+
template_cls = WarpCore
|
| 21 |
+
else:
|
| 22 |
+
try:
|
| 23 |
+
template_cls = __import__(args.template)
|
| 24 |
+
except ModuleNotFoundError:
|
| 25 |
+
template_cls = getattr(templates, args.template)
|
| 26 |
+
print(template_cls)
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def main():
|
| 30 |
+
if len(sys.argv) < 2:
|
| 31 |
+
print('Usage: core <command>')
|
| 32 |
+
sys.exit(1)
|
| 33 |
+
if sys.argv[1] == 'init':
|
| 34 |
+
init_template(sys.argv[2:])
|
| 35 |
+
else:
|
| 36 |
+
print('Unknown command')
|
| 37 |
+
sys.exit(1)
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
if __name__ == '__main__':
|
| 41 |
+
main()
|
core/templates/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
from .diffusion import DiffusionCore
|
core/templates/diffusion.py
ADDED
|
@@ -0,0 +1,236 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .. import WarpCore
|
| 2 |
+
from ..utils import EXPECTED, EXPECTED_TRAIN, update_weights_ema, create_folder_if_necessary
|
| 3 |
+
from abc import abstractmethod
|
| 4 |
+
from dataclasses import dataclass
|
| 5 |
+
import torch
|
| 6 |
+
from torch import nn
|
| 7 |
+
from torch.utils.data import DataLoader
|
| 8 |
+
from gdf import GDF
|
| 9 |
+
import numpy as np
|
| 10 |
+
from tqdm import tqdm
|
| 11 |
+
import wandb
|
| 12 |
+
|
| 13 |
+
import webdataset as wds
|
| 14 |
+
from webdataset.handlers import warn_and_continue
|
| 15 |
+
from torch.distributed import barrier
|
| 16 |
+
from enum import Enum
|
| 17 |
+
|
| 18 |
+
class TargetReparametrization(Enum):
|
| 19 |
+
EPSILON = 'epsilon'
|
| 20 |
+
X0 = 'x0'
|
| 21 |
+
|
| 22 |
+
class DiffusionCore(WarpCore):
|
| 23 |
+
@dataclass(frozen=True)
|
| 24 |
+
class Config(WarpCore.Config):
|
| 25 |
+
# TRAINING PARAMS
|
| 26 |
+
lr: float = EXPECTED_TRAIN
|
| 27 |
+
grad_accum_steps: int = EXPECTED_TRAIN
|
| 28 |
+
batch_size: int = EXPECTED_TRAIN
|
| 29 |
+
updates: int = EXPECTED_TRAIN
|
| 30 |
+
warmup_updates: int = EXPECTED_TRAIN
|
| 31 |
+
save_every: int = 500
|
| 32 |
+
backup_every: int = 20000
|
| 33 |
+
use_fsdp: bool = True
|
| 34 |
+
|
| 35 |
+
# EMA UPDATE
|
| 36 |
+
ema_start_iters: int = None
|
| 37 |
+
ema_iters: int = None
|
| 38 |
+
ema_beta: float = None
|
| 39 |
+
|
| 40 |
+
# GDF setting
|
| 41 |
+
gdf_target_reparametrization: TargetReparametrization = None # epsilon or x0
|
| 42 |
+
|
| 43 |
+
@dataclass() # not frozen, means that fields are mutable. Doesn't support EXPECTED
|
| 44 |
+
class Info(WarpCore.Info):
|
| 45 |
+
ema_loss: float = None
|
| 46 |
+
|
| 47 |
+
@dataclass(frozen=True)
|
| 48 |
+
class Models(WarpCore.Models):
|
| 49 |
+
generator : nn.Module = EXPECTED
|
| 50 |
+
generator_ema : nn.Module = None # optional
|
| 51 |
+
|
| 52 |
+
@dataclass(frozen=True)
|
| 53 |
+
class Optimizers(WarpCore.Optimizers):
|
| 54 |
+
generator : any = EXPECTED
|
| 55 |
+
|
| 56 |
+
@dataclass(frozen=True)
|
| 57 |
+
class Schedulers(WarpCore.Schedulers):
|
| 58 |
+
generator: any = None
|
| 59 |
+
|
| 60 |
+
@dataclass(frozen=True)
|
| 61 |
+
class Extras(WarpCore.Extras):
|
| 62 |
+
gdf: GDF = EXPECTED
|
| 63 |
+
sampling_configs: dict = EXPECTED
|
| 64 |
+
|
| 65 |
+
# --------------------------------------------
|
| 66 |
+
info: Info
|
| 67 |
+
config: Config
|
| 68 |
+
|
| 69 |
+
@abstractmethod
|
| 70 |
+
def encode_latents(self, batch: dict, models: Models, extras: Extras) -> torch.Tensor:
|
| 71 |
+
raise NotImplementedError("This method needs to be overriden")
|
| 72 |
+
|
| 73 |
+
@abstractmethod
|
| 74 |
+
def decode_latents(self, latents: torch.Tensor, batch: dict, models: Models, extras: Extras) -> torch.Tensor:
|
| 75 |
+
raise NotImplementedError("This method needs to be overriden")
|
| 76 |
+
|
| 77 |
+
@abstractmethod
|
| 78 |
+
def get_conditions(self, batch: dict, models: Models, extras: Extras, is_eval=False, is_unconditional=False):
|
| 79 |
+
raise NotImplementedError("This method needs to be overriden")
|
| 80 |
+
|
| 81 |
+
@abstractmethod
|
| 82 |
+
def webdataset_path(self, extras: Extras):
|
| 83 |
+
raise NotImplementedError("This method needs to be overriden")
|
| 84 |
+
|
| 85 |
+
@abstractmethod
|
| 86 |
+
def webdataset_filters(self, extras: Extras):
|
| 87 |
+
raise NotImplementedError("This method needs to be overriden")
|
| 88 |
+
|
| 89 |
+
@abstractmethod
|
| 90 |
+
def webdataset_preprocessors(self, extras: Extras):
|
| 91 |
+
raise NotImplementedError("This method needs to be overriden")
|
| 92 |
+
|
| 93 |
+
@abstractmethod
|
| 94 |
+
def sample(self, models: Models, data: WarpCore.Data, extras: Extras):
|
| 95 |
+
raise NotImplementedError("This method needs to be overriden")
|
| 96 |
+
# -------------
|
| 97 |
+
|
| 98 |
+
def setup_data(self, extras: Extras) -> WarpCore.Data:
|
| 99 |
+
# SETUP DATASET
|
| 100 |
+
dataset_path = self.webdataset_path(extras)
|
| 101 |
+
preprocessors = self.webdataset_preprocessors(extras)
|
| 102 |
+
filters = self.webdataset_filters(extras)
|
| 103 |
+
|
| 104 |
+
handler = warn_and_continue # None
|
| 105 |
+
# handler = None
|
| 106 |
+
dataset = wds.WebDataset(
|
| 107 |
+
dataset_path, resampled=True, handler=handler
|
| 108 |
+
).select(filters).shuffle(690, handler=handler).decode(
|
| 109 |
+
"pilrgb", handler=handler
|
| 110 |
+
).to_tuple(
|
| 111 |
+
*[p[0] for p in preprocessors], handler=handler
|
| 112 |
+
).map_tuple(
|
| 113 |
+
*[p[1] for p in preprocessors], handler=handler
|
| 114 |
+
).map(lambda x: {p[2]:x[i] for i, p in enumerate(preprocessors)})
|
| 115 |
+
|
| 116 |
+
# SETUP DATALOADER
|
| 117 |
+
real_batch_size = self.config.batch_size//(self.world_size*self.config.grad_accum_steps)
|
| 118 |
+
dataloader = DataLoader(
|
| 119 |
+
dataset, batch_size=real_batch_size, num_workers=8, pin_memory=True
|
| 120 |
+
)
|
| 121 |
+
|
| 122 |
+
return self.Data(dataset=dataset, dataloader=dataloader, iterator=iter(dataloader))
|
| 123 |
+
|
| 124 |
+
def forward_pass(self, data: WarpCore.Data, extras: Extras, models: Models):
|
| 125 |
+
batch = next(data.iterator)
|
| 126 |
+
|
| 127 |
+
with torch.no_grad():
|
| 128 |
+
conditions = self.get_conditions(batch, models, extras)
|
| 129 |
+
latents = self.encode_latents(batch, models, extras)
|
| 130 |
+
noised, noise, target, logSNR, noise_cond, loss_weight = extras.gdf.diffuse(latents, shift=1, loss_shift=1)
|
| 131 |
+
|
| 132 |
+
# FORWARD PASS
|
| 133 |
+
with torch.cuda.amp.autocast(dtype=torch.bfloat16):
|
| 134 |
+
pred = models.generator(noised, noise_cond, **conditions)
|
| 135 |
+
if self.config.gdf_target_reparametrization == TargetReparametrization.EPSILON:
|
| 136 |
+
pred = extras.gdf.undiffuse(noised, logSNR, pred)[1] # transform whatever prediction to epsilon to use in the loss
|
| 137 |
+
target = noise
|
| 138 |
+
elif self.config.gdf_target_reparametrization == TargetReparametrization.X0:
|
| 139 |
+
pred = extras.gdf.undiffuse(noised, logSNR, pred)[0] # transform whatever prediction to x0 to use in the loss
|
| 140 |
+
target = latents
|
| 141 |
+
loss = nn.functional.mse_loss(pred, target, reduction='none').mean(dim=[1, 2, 3])
|
| 142 |
+
loss_adjusted = (loss * loss_weight).mean() / self.config.grad_accum_steps
|
| 143 |
+
|
| 144 |
+
return loss, loss_adjusted
|
| 145 |
+
|
| 146 |
+
def train(self, data: WarpCore.Data, extras: Extras, models: Models, optimizers: Optimizers, schedulers: Schedulers):
|
| 147 |
+
start_iter = self.info.iter+1
|
| 148 |
+
max_iters = self.config.updates * self.config.grad_accum_steps
|
| 149 |
+
if self.is_main_node:
|
| 150 |
+
print(f"STARTING AT STEP: {start_iter}/{max_iters}")
|
| 151 |
+
|
| 152 |
+
pbar = tqdm(range(start_iter, max_iters+1)) if self.is_main_node else range(start_iter, max_iters+1) # <--- DDP
|
| 153 |
+
models.generator.train()
|
| 154 |
+
for i in pbar:
|
| 155 |
+
# FORWARD PASS
|
| 156 |
+
loss, loss_adjusted = self.forward_pass(data, extras, models)
|
| 157 |
+
|
| 158 |
+
# BACKWARD PASS
|
| 159 |
+
if i % self.config.grad_accum_steps == 0 or i == max_iters:
|
| 160 |
+
loss_adjusted.backward()
|
| 161 |
+
grad_norm = nn.utils.clip_grad_norm_(models.generator.parameters(), 1.0)
|
| 162 |
+
optimizers_dict = optimizers.to_dict()
|
| 163 |
+
for k in optimizers_dict:
|
| 164 |
+
optimizers_dict[k].step()
|
| 165 |
+
schedulers_dict = schedulers.to_dict()
|
| 166 |
+
for k in schedulers_dict:
|
| 167 |
+
schedulers_dict[k].step()
|
| 168 |
+
models.generator.zero_grad(set_to_none=True)
|
| 169 |
+
self.info.total_steps += 1
|
| 170 |
+
else:
|
| 171 |
+
with models.generator.no_sync():
|
| 172 |
+
loss_adjusted.backward()
|
| 173 |
+
self.info.iter = i
|
| 174 |
+
|
| 175 |
+
# UPDATE EMA
|
| 176 |
+
if models.generator_ema is not None and i % self.config.ema_iters == 0:
|
| 177 |
+
update_weights_ema(
|
| 178 |
+
models.generator_ema, models.generator,
|
| 179 |
+
beta=(self.config.ema_beta if i > self.config.ema_start_iters else 0)
|
| 180 |
+
)
|
| 181 |
+
|
| 182 |
+
# UPDATE LOSS METRICS
|
| 183 |
+
self.info.ema_loss = loss.mean().item() if self.info.ema_loss is None else self.info.ema_loss * 0.99 + loss.mean().item() * 0.01
|
| 184 |
+
|
| 185 |
+
if self.is_main_node and self.config.wandb_project is not None and np.isnan(loss.mean().item()) or np.isnan(grad_norm.item()):
|
| 186 |
+
wandb.alert(
|
| 187 |
+
title=f"NaN value encountered in training run {self.info.wandb_run_id}",
|
| 188 |
+
text=f"Loss {loss.mean().item()} - Grad Norm {grad_norm.item()}. Run {self.info.wandb_run_id}",
|
| 189 |
+
wait_duration=60*30
|
| 190 |
+
)
|
| 191 |
+
|
| 192 |
+
if self.is_main_node:
|
| 193 |
+
logs = {
|
| 194 |
+
'loss': self.info.ema_loss,
|
| 195 |
+
'raw_loss': loss.mean().item(),
|
| 196 |
+
'grad_norm': grad_norm.item(),
|
| 197 |
+
'lr': optimizers.generator.param_groups[0]['lr'],
|
| 198 |
+
'total_steps': self.info.total_steps,
|
| 199 |
+
}
|
| 200 |
+
|
| 201 |
+
pbar.set_postfix(logs)
|
| 202 |
+
if self.config.wandb_project is not None:
|
| 203 |
+
wandb.log(logs)
|
| 204 |
+
|
| 205 |
+
if i == 1 or i % (self.config.save_every*self.config.grad_accum_steps) == 0 or i == max_iters:
|
| 206 |
+
# SAVE AND CHECKPOINT STUFF
|
| 207 |
+
if np.isnan(loss.mean().item()):
|
| 208 |
+
if self.is_main_node and self.config.wandb_project is not None:
|
| 209 |
+
tqdm.write("Skipping sampling & checkpoint because the loss is NaN")
|
| 210 |
+
wandb.alert(title=f"Skipping sampling & checkpoint for training run {self.config.run_id}", text=f"Skipping sampling & checkpoint at {self.info.total_steps} for training run {self.info.wandb_run_id} iters because loss is NaN")
|
| 211 |
+
else:
|
| 212 |
+
self.save_checkpoints(models, optimizers)
|
| 213 |
+
if self.is_main_node:
|
| 214 |
+
create_folder_if_necessary(f'{self.config.output_path}/{self.config.experiment_id}/')
|
| 215 |
+
self.sample(models, data, extras)
|
| 216 |
+
|
| 217 |
+
def models_to_save(self):
|
| 218 |
+
return ['generator', 'generator_ema']
|
| 219 |
+
|
| 220 |
+
def save_checkpoints(self, models: Models, optimizers: Optimizers, suffix=None):
|
| 221 |
+
barrier()
|
| 222 |
+
suffix = '' if suffix is None else suffix
|
| 223 |
+
self.save_info(self.info, suffix=suffix)
|
| 224 |
+
models_dict = models.to_dict()
|
| 225 |
+
optimizers_dict = optimizers.to_dict()
|
| 226 |
+
for key in self.models_to_save():
|
| 227 |
+
model = models_dict[key]
|
| 228 |
+
if model is not None:
|
| 229 |
+
self.save_model(model, f"{key}{suffix}", is_fsdp=self.config.use_fsdp)
|
| 230 |
+
for key in optimizers_dict:
|
| 231 |
+
optimizer = optimizers_dict[key]
|
| 232 |
+
if optimizer is not None:
|
| 233 |
+
self.save_optimizer(optimizer, f'{key}_optim{suffix}', fsdp_model=models.generator if self.config.use_fsdp else None)
|
| 234 |
+
if suffix == '' and self.info.total_steps > 1 and self.info.total_steps % self.config.backup_every == 0:
|
| 235 |
+
self.save_checkpoints(models, optimizers, suffix=f"_{self.info.total_steps//1000}k")
|
| 236 |
+
torch.cuda.empty_cache()
|
core/utils/__init__.py
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .base_dto import Base, nested_dto, EXPECTED, EXPECTED_TRAIN
|
| 2 |
+
from .save_and_load import create_folder_if_necessary, safe_save, load_or_fail
|
| 3 |
+
|
| 4 |
+
# MOVE IT SOMERWHERE ELSE
|
| 5 |
+
def update_weights_ema(tgt_model, src_model, beta=0.999):
|
| 6 |
+
for self_params, src_params in zip(tgt_model.parameters(), src_model.parameters()):
|
| 7 |
+
self_params.data = self_params.data * beta + src_params.data.clone().to(self_params.device) * (1-beta)
|
| 8 |
+
for self_buffers, src_buffers in zip(tgt_model.buffers(), src_model.buffers()):
|
| 9 |
+
self_buffers.data = self_buffers.data * beta + src_buffers.data.clone().to(self_buffers.device) * (1-beta)
|
core/utils/base_dto.py
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import dataclasses
|
| 2 |
+
from dataclasses import dataclass, _MISSING_TYPE
|
| 3 |
+
from munch import Munch
|
| 4 |
+
|
| 5 |
+
EXPECTED = "___REQUIRED___"
|
| 6 |
+
EXPECTED_TRAIN = "___REQUIRED_TRAIN___"
|
| 7 |
+
|
| 8 |
+
# pylint: disable=invalid-field-call
|
| 9 |
+
def nested_dto(x, raw=False):
|
| 10 |
+
return dataclasses.field(default_factory=lambda: x if raw else Munch.fromDict(x))
|
| 11 |
+
|
| 12 |
+
@dataclass(frozen=True)
|
| 13 |
+
class Base:
|
| 14 |
+
training: bool = None
|
| 15 |
+
def __new__(cls, **kwargs):
|
| 16 |
+
training = kwargs.get('training', True)
|
| 17 |
+
setteable_fields = cls.setteable_fields(**kwargs)
|
| 18 |
+
mandatory_fields = cls.mandatory_fields(**kwargs)
|
| 19 |
+
invalid_kwargs = [
|
| 20 |
+
{k: v} for k, v in kwargs.items() if k not in setteable_fields or v == EXPECTED or (v == EXPECTED_TRAIN and training is not False)
|
| 21 |
+
]
|
| 22 |
+
print(mandatory_fields)
|
| 23 |
+
assert (
|
| 24 |
+
len(invalid_kwargs) == 0
|
| 25 |
+
), f"Invalid fields detected when initializing this DTO: {invalid_kwargs}.\nDeclare this field and set it to None or EXPECTED in order to make it setteable."
|
| 26 |
+
missing_kwargs = [f for f in mandatory_fields if f not in kwargs]
|
| 27 |
+
assert (
|
| 28 |
+
len(missing_kwargs) == 0
|
| 29 |
+
), f"Required fields missing initializing this DTO: {missing_kwargs}."
|
| 30 |
+
return object.__new__(cls)
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
@classmethod
|
| 34 |
+
def setteable_fields(cls, **kwargs):
|
| 35 |
+
return [f.name for f in dataclasses.fields(cls) if f.default is None or isinstance(f.default, _MISSING_TYPE) or f.default == EXPECTED or f.default == EXPECTED_TRAIN]
|
| 36 |
+
|
| 37 |
+
@classmethod
|
| 38 |
+
def mandatory_fields(cls, **kwargs):
|
| 39 |
+
training = kwargs.get('training', True)
|
| 40 |
+
return [f.name for f in dataclasses.fields(cls) if isinstance(f.default, _MISSING_TYPE) and isinstance(f.default_factory, _MISSING_TYPE) or f.default == EXPECTED or (f.default == EXPECTED_TRAIN and training is not False)]
|
| 41 |
+
|
| 42 |
+
@classmethod
|
| 43 |
+
def from_dict(cls, kwargs):
|
| 44 |
+
for k in kwargs:
|
| 45 |
+
if isinstance(kwargs[k], (dict, list, tuple)):
|
| 46 |
+
kwargs[k] = Munch.fromDict(kwargs[k])
|
| 47 |
+
return cls(**kwargs)
|
| 48 |
+
|
| 49 |
+
def to_dict(self):
|
| 50 |
+
# selfdict = dataclasses.asdict(self) # needs to pickle stuff, doesn't support some more complex classes
|
| 51 |
+
selfdict = {}
|
| 52 |
+
for k in dataclasses.fields(self):
|
| 53 |
+
selfdict[k.name] = getattr(self, k.name)
|
| 54 |
+
if isinstance(selfdict[k.name], Munch):
|
| 55 |
+
selfdict[k.name] = selfdict[k.name].toDict()
|
| 56 |
+
return selfdict
|
core/utils/save_and_load.py
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import torch
|
| 3 |
+
import json
|
| 4 |
+
from pathlib import Path
|
| 5 |
+
import safetensors
|
| 6 |
+
import wandb
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
def create_folder_if_necessary(path):
|
| 10 |
+
path = "/".join(path.split("/")[:-1])
|
| 11 |
+
Path(path).mkdir(parents=True, exist_ok=True)
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def safe_save(ckpt, path):
|
| 15 |
+
try:
|
| 16 |
+
os.remove(f"{path}.bak")
|
| 17 |
+
except OSError:
|
| 18 |
+
pass
|
| 19 |
+
try:
|
| 20 |
+
os.rename(path, f"{path}.bak")
|
| 21 |
+
except OSError:
|
| 22 |
+
pass
|
| 23 |
+
if path.endswith(".pt") or path.endswith(".ckpt"):
|
| 24 |
+
torch.save(ckpt, path)
|
| 25 |
+
elif path.endswith(".json"):
|
| 26 |
+
with open(path, "w", encoding="utf-8") as f:
|
| 27 |
+
json.dump(ckpt, f, indent=4)
|
| 28 |
+
elif path.endswith(".safetensors"):
|
| 29 |
+
safetensors.torch.save_file(ckpt, path)
|
| 30 |
+
else:
|
| 31 |
+
raise ValueError(f"File extension not supported: {path}")
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
def load_or_fail(path, wandb_run_id=None):
|
| 35 |
+
accepted_extensions = [".pt", ".ckpt", ".json", ".safetensors"]
|
| 36 |
+
try:
|
| 37 |
+
assert any(
|
| 38 |
+
[path.endswith(ext) for ext in accepted_extensions]
|
| 39 |
+
), f"Automatic loading not supported for this extension: {path}"
|
| 40 |
+
if not os.path.exists(path):
|
| 41 |
+
checkpoint = None
|
| 42 |
+
elif path.endswith(".pt") or path.endswith(".ckpt"):
|
| 43 |
+
checkpoint = torch.load(path, map_location="cpu")
|
| 44 |
+
elif path.endswith(".json"):
|
| 45 |
+
with open(path, "r", encoding="utf-8") as f:
|
| 46 |
+
checkpoint = json.load(f)
|
| 47 |
+
elif path.endswith(".safetensors"):
|
| 48 |
+
checkpoint = {}
|
| 49 |
+
with safetensors.safe_open(path, framework="pt", device="cpu") as f:
|
| 50 |
+
for key in f.keys():
|
| 51 |
+
checkpoint[key] = f.get_tensor(key)
|
| 52 |
+
return checkpoint
|
| 53 |
+
except Exception as e:
|
| 54 |
+
if wandb_run_id is not None:
|
| 55 |
+
wandb.alert(
|
| 56 |
+
title=f"Corrupt checkpoint for run {wandb_run_id}",
|
| 57 |
+
text=f"Training {wandb_run_id} tried to load checkpoint {path} and failed",
|
| 58 |
+
)
|
| 59 |
+
raise e
|
gdf/__init__.py
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
from .scalers import *
|
| 3 |
+
from .targets import *
|
| 4 |
+
from .schedulers import *
|
| 5 |
+
from .noise_conditions import *
|
| 6 |
+
from .loss_weights import *
|
| 7 |
+
from .samplers import *
|
| 8 |
+
import torch.nn.functional as F
|
| 9 |
+
import math
|
| 10 |
+
class GDF():
|
| 11 |
+
def __init__(self, schedule, input_scaler, target, noise_cond, loss_weight, offset_noise=0):
|
| 12 |
+
self.schedule = schedule
|
| 13 |
+
self.input_scaler = input_scaler
|
| 14 |
+
self.target = target
|
| 15 |
+
self.noise_cond = noise_cond
|
| 16 |
+
self.loss_weight = loss_weight
|
| 17 |
+
self.offset_noise = offset_noise
|
| 18 |
+
|
| 19 |
+
def setup_limits(self, stretch_max=True, stretch_min=True, shift=1):
|
| 20 |
+
stretched_limits = self.input_scaler.setup_limits(self.schedule, self.input_scaler, stretch_max, stretch_min, shift)
|
| 21 |
+
return stretched_limits
|
| 22 |
+
|
| 23 |
+
def diffuse(self, x0, epsilon=None, t=None, shift=1, loss_shift=1, offset=None):
|
| 24 |
+
if epsilon is None:
|
| 25 |
+
epsilon = torch.randn_like(x0)
|
| 26 |
+
if self.offset_noise > 0:
|
| 27 |
+
if offset is None:
|
| 28 |
+
offset = torch.randn([x0.size(0), x0.size(1)] + [1]*(len(x0.shape)-2)).to(x0.device)
|
| 29 |
+
epsilon = epsilon + offset * self.offset_noise
|
| 30 |
+
logSNR = self.schedule(x0.size(0) if t is None else t, shift=shift).to(x0.device)
|
| 31 |
+
a, b = self.input_scaler(logSNR) # B
|
| 32 |
+
if len(a.shape) == 1:
|
| 33 |
+
a, b = a.view(-1, *[1]*(len(x0.shape)-1)), b.view(-1, *[1]*(len(x0.shape)-1)) # BxCxHxW
|
| 34 |
+
#print('in line 33 a b', a.shape, b.shape, x0.shape, logSNR.shape, logSNR, self.noise_cond(logSNR))
|
| 35 |
+
target = self.target(x0, epsilon, logSNR, a, b)
|
| 36 |
+
|
| 37 |
+
# noised, noise, logSNR, t_cond
|
| 38 |
+
#noised, noise, target, logSNR, noise_cond, loss_weight
|
| 39 |
+
return x0 * a + epsilon * b, epsilon, target, logSNR, self.noise_cond(logSNR), self.loss_weight(logSNR, shift=loss_shift)
|
| 40 |
+
|
| 41 |
+
def undiffuse(self, x, logSNR, pred):
|
| 42 |
+
a, b = self.input_scaler(logSNR)
|
| 43 |
+
if len(a.shape) == 1:
|
| 44 |
+
a, b = a.view(-1, *[1]*(len(x.shape)-1)), b.view(-1, *[1]*(len(x.shape)-1))
|
| 45 |
+
return self.target.x0(x, pred, logSNR, a, b), self.target.epsilon(x, pred, logSNR, a, b)
|
| 46 |
+
|
| 47 |
+
def sample(self, model, model_inputs, shape, unconditional_inputs=None, sampler=None, schedule=None, t_start=1.0, t_end=0.0, timesteps=20, x_init=None, cfg=3.0, cfg_t_stop=None, cfg_t_start=None, cfg_rho=0.7, sampler_params=None, shift=1, device="cpu"):
|
| 48 |
+
sampler_params = {} if sampler_params is None else sampler_params
|
| 49 |
+
if sampler is None:
|
| 50 |
+
sampler = DDPMSampler(self)
|
| 51 |
+
r_range = torch.linspace(t_start, t_end, timesteps+1)
|
| 52 |
+
schedule = self.schedule if schedule is None else schedule
|
| 53 |
+
logSNR_range = schedule(r_range, shift=shift)[:, None].expand(
|
| 54 |
+
-1, shape[0] if x_init is None else x_init.size(0)
|
| 55 |
+
).to(device)
|
| 56 |
+
|
| 57 |
+
x = sampler.init_x(shape).to(device) if x_init is None else x_init.clone()
|
| 58 |
+
|
| 59 |
+
if cfg is not None:
|
| 60 |
+
if unconditional_inputs is None:
|
| 61 |
+
unconditional_inputs = {k: torch.zeros_like(v) for k, v in model_inputs.items()}
|
| 62 |
+
model_inputs = {
|
| 63 |
+
k: torch.cat([v, v_u], dim=0) if isinstance(v, torch.Tensor)
|
| 64 |
+
else [torch.cat([vi, vi_u], dim=0) if isinstance(vi, torch.Tensor) and isinstance(vi_u, torch.Tensor) else None for vi, vi_u in zip(v, v_u)] if isinstance(v, list)
|
| 65 |
+
else {vk: torch.cat([v[vk], v_u.get(vk, torch.zeros_like(v[vk]))], dim=0) for vk in v} if isinstance(v, dict)
|
| 66 |
+
else None for (k, v), (k_u, v_u) in zip(model_inputs.items(), unconditional_inputs.items())
|
| 67 |
+
}
|
| 68 |
+
|
| 69 |
+
for i in range(0, timesteps):
|
| 70 |
+
noise_cond = self.noise_cond(logSNR_range[i])
|
| 71 |
+
if cfg is not None and (cfg_t_stop is None or r_range[i].item() >= cfg_t_stop) and (cfg_t_start is None or r_range[i].item() <= cfg_t_start):
|
| 72 |
+
cfg_val = cfg
|
| 73 |
+
if isinstance(cfg_val, (list, tuple)):
|
| 74 |
+
assert len(cfg_val) == 2, "cfg must be a float or a list/tuple of length 2"
|
| 75 |
+
cfg_val = cfg_val[0] * r_range[i].item() + cfg_val[1] * (1-r_range[i].item())
|
| 76 |
+
|
| 77 |
+
pred, pred_unconditional = model(torch.cat([x, x], dim=0), noise_cond.repeat(2), **model_inputs).chunk(2)
|
| 78 |
+
|
| 79 |
+
pred_cfg = torch.lerp(pred_unconditional, pred, cfg_val)
|
| 80 |
+
if cfg_rho > 0:
|
| 81 |
+
std_pos, std_cfg = pred.std(), pred_cfg.std()
|
| 82 |
+
pred = cfg_rho * (pred_cfg * std_pos/(std_cfg+1e-9)) + pred_cfg * (1-cfg_rho)
|
| 83 |
+
else:
|
| 84 |
+
pred = pred_cfg
|
| 85 |
+
else:
|
| 86 |
+
pred = model(x, noise_cond, **model_inputs)
|
| 87 |
+
x0, epsilon = self.undiffuse(x, logSNR_range[i], pred)
|
| 88 |
+
x = sampler(x, x0, epsilon, logSNR_range[i], logSNR_range[i+1], **sampler_params)
|
| 89 |
+
#print('in line 86', x0.shape, x.shape, i, )
|
| 90 |
+
altered_vars = yield (x0, x, pred)
|
| 91 |
+
|
| 92 |
+
# Update some running variables if the user wants
|
| 93 |
+
if altered_vars is not None:
|
| 94 |
+
cfg = altered_vars.get('cfg', cfg)
|
| 95 |
+
cfg_rho = altered_vars.get('cfg_rho', cfg_rho)
|
| 96 |
+
sampler = altered_vars.get('sampler', sampler)
|
| 97 |
+
model_inputs = altered_vars.get('model_inputs', model_inputs)
|
| 98 |
+
x = altered_vars.get('x', x)
|
| 99 |
+
x_init = altered_vars.get('x_init', x_init)
|
| 100 |
+
|
| 101 |
+
class GDF_dual_fixlrt(GDF):
|
| 102 |
+
def ref_noise(self, noised, x0, logSNR):
|
| 103 |
+
a, b = self.input_scaler(logSNR)
|
| 104 |
+
if len(a.shape) == 1:
|
| 105 |
+
a, b = a.view(-1, *[1]*(len(x0.shape)-1)), b.view(-1, *[1]*(len(x0.shape)-1))
|
| 106 |
+
#print('in line 210', a.shape, b.shape, x0.shape, noised.shape)
|
| 107 |
+
return self.target.noise_givenx0_noised(x0, noised, logSNR, a, b)
|
| 108 |
+
|
| 109 |
+
def sample(self, model, model_inputs, shape, shape_lr, unconditional_inputs=None, sampler=None,
|
| 110 |
+
schedule=None, t_start=1.0, t_end=0.0, timesteps=20, x_init=None, cfg=3.0, cfg_t_stop=None,
|
| 111 |
+
cfg_t_start=None, cfg_rho=0.7, sampler_params=None, shift=1, device="cpu"):
|
| 112 |
+
sampler_params = {} if sampler_params is None else sampler_params
|
| 113 |
+
if sampler is None:
|
| 114 |
+
sampler = DDPMSampler(self)
|
| 115 |
+
r_range = torch.linspace(t_start, t_end, timesteps+1)
|
| 116 |
+
schedule = self.schedule if schedule is None else schedule
|
| 117 |
+
logSNR_range = schedule(r_range, shift=shift)[:, None].expand(
|
| 118 |
+
-1, shape[0] if x_init is None else x_init.size(0)
|
| 119 |
+
).to(device)
|
| 120 |
+
|
| 121 |
+
x = sampler.init_x(shape).to(device) if x_init is None else x_init.clone()
|
| 122 |
+
x_lr = sampler.init_x(shape_lr).to(device) if x_init is None else x_init.clone()
|
| 123 |
+
if cfg is not None:
|
| 124 |
+
if unconditional_inputs is None:
|
| 125 |
+
unconditional_inputs = {k: torch.zeros_like(v) for k, v in model_inputs.items()}
|
| 126 |
+
model_inputs = {
|
| 127 |
+
k: torch.cat([v, v_u], dim=0) if isinstance(v, torch.Tensor)
|
| 128 |
+
else [torch.cat([vi, vi_u], dim=0) if isinstance(vi, torch.Tensor) and isinstance(vi_u, torch.Tensor) else None for vi, vi_u in zip(v, v_u)] if isinstance(v, list)
|
| 129 |
+
else {vk: torch.cat([v[vk], v_u.get(vk, torch.zeros_like(v[vk]))], dim=0) for vk in v} if isinstance(v, dict)
|
| 130 |
+
else None for (k, v), (k_u, v_u) in zip(model_inputs.items(), unconditional_inputs.items())
|
| 131 |
+
}
|
| 132 |
+
|
| 133 |
+
###############################################lr sampling
|
| 134 |
+
|
| 135 |
+
guide_feas = [None] * timesteps
|
| 136 |
+
|
| 137 |
+
for i in range(0, timesteps):
|
| 138 |
+
noise_cond = self.noise_cond(logSNR_range[i])
|
| 139 |
+
if cfg is not None and (cfg_t_stop is None or r_range[i].item() >= cfg_t_stop) and (cfg_t_start is None or r_range[i].item() <= cfg_t_start):
|
| 140 |
+
cfg_val = cfg
|
| 141 |
+
if isinstance(cfg_val, (list, tuple)):
|
| 142 |
+
assert len(cfg_val) == 2, "cfg must be a float or a list/tuple of length 2"
|
| 143 |
+
cfg_val = cfg_val[0] * r_range[i].item() + cfg_val[1] * (1-r_range[i].item())
|
| 144 |
+
|
| 145 |
+
|
| 146 |
+
|
| 147 |
+
if i == timesteps -1 :
|
| 148 |
+
output, guide_lr_enc, guide_lr_dec = model(torch.cat([x_lr, x_lr], dim=0), noise_cond.repeat(2), reuire_f=True, **model_inputs)
|
| 149 |
+
guide_feas[i] = ([f.chunk(2)[0].repeat(2, 1, 1, 1) for f in guide_lr_enc], [f.chunk(2)[0].repeat(2, 1, 1, 1) for f in guide_lr_dec])
|
| 150 |
+
else:
|
| 151 |
+
output, _, _ = model(torch.cat([x_lr, x_lr], dim=0), noise_cond.repeat(2), reuire_f=True, **model_inputs)
|
| 152 |
+
|
| 153 |
+
pred, pred_unconditional = output.chunk(2)
|
| 154 |
+
|
| 155 |
+
|
| 156 |
+
pred_cfg = torch.lerp(pred_unconditional, pred, cfg_val)
|
| 157 |
+
if cfg_rho > 0:
|
| 158 |
+
std_pos, std_cfg = pred.std(), pred_cfg.std()
|
| 159 |
+
pred = cfg_rho * (pred_cfg * std_pos/(std_cfg+1e-9)) + pred_cfg * (1-cfg_rho)
|
| 160 |
+
else:
|
| 161 |
+
pred = pred_cfg
|
| 162 |
+
else:
|
| 163 |
+
pred = model(x_lr, noise_cond, **model_inputs)
|
| 164 |
+
x0_lr, epsilon_lr = self.undiffuse(x_lr, logSNR_range[i], pred)
|
| 165 |
+
x_lr = sampler(x_lr, x0_lr, epsilon_lr, logSNR_range[i], logSNR_range[i+1], **sampler_params)
|
| 166 |
+
|
| 167 |
+
###############################################hr HR sampling
|
| 168 |
+
for i in range(0, timesteps):
|
| 169 |
+
noise_cond = self.noise_cond(logSNR_range[i])
|
| 170 |
+
if cfg is not None and (cfg_t_stop is None or r_range[i].item() >= cfg_t_stop) and (cfg_t_start is None or r_range[i].item() <= cfg_t_start):
|
| 171 |
+
cfg_val = cfg
|
| 172 |
+
if isinstance(cfg_val, (list, tuple)):
|
| 173 |
+
assert len(cfg_val) == 2, "cfg must be a float or a list/tuple of length 2"
|
| 174 |
+
cfg_val = cfg_val[0] * r_range[i].item() + cfg_val[1] * (1-r_range[i].item())
|
| 175 |
+
|
| 176 |
+
out_pred, t_emb = model(torch.cat([x, x], dim=0), noise_cond.repeat(2), \
|
| 177 |
+
lr_guide=guide_feas[timesteps -1] if i <=19 else None , **model_inputs, require_t=True, guide_weight=1 - i/timesteps)
|
| 178 |
+
pred, pred_unconditional = out_pred.chunk(2)
|
| 179 |
+
pred_cfg = torch.lerp(pred_unconditional, pred, cfg_val)
|
| 180 |
+
if cfg_rho > 0:
|
| 181 |
+
std_pos, std_cfg = pred.std(), pred_cfg.std()
|
| 182 |
+
pred = cfg_rho * (pred_cfg * std_pos/(std_cfg+1e-9)) + pred_cfg * (1-cfg_rho)
|
| 183 |
+
else:
|
| 184 |
+
pred = pred_cfg
|
| 185 |
+
else:
|
| 186 |
+
pred = model(x, noise_cond, guide_lr=(guide_lr_enc, guide_lr_dec), **model_inputs)
|
| 187 |
+
x0, epsilon = self.undiffuse(x, logSNR_range[i], pred)
|
| 188 |
+
|
| 189 |
+
x = sampler(x, x0, epsilon, logSNR_range[i], logSNR_range[i+1], **sampler_params)
|
| 190 |
+
altered_vars = yield (x0, x, pred, x_lr)
|
| 191 |
+
|
| 192 |
+
|
| 193 |
+
|
| 194 |
+
# Update some running variables if the user wants
|
| 195 |
+
if altered_vars is not None:
|
| 196 |
+
cfg = altered_vars.get('cfg', cfg)
|
| 197 |
+
cfg_rho = altered_vars.get('cfg_rho', cfg_rho)
|
| 198 |
+
sampler = altered_vars.get('sampler', sampler)
|
| 199 |
+
model_inputs = altered_vars.get('model_inputs', model_inputs)
|
| 200 |
+
x = altered_vars.get('x', x)
|
| 201 |
+
x_init = altered_vars.get('x_init', x_init)
|
| 202 |
+
|
| 203 |
+
|
| 204 |
+
|
| 205 |
+
|
gdf/loss_weights.py
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
import numpy as np
|
| 3 |
+
|
| 4 |
+
# --- Loss Weighting
|
| 5 |
+
class BaseLossWeight():
|
| 6 |
+
def weight(self, logSNR):
|
| 7 |
+
raise NotImplementedError("this method needs to be overridden")
|
| 8 |
+
|
| 9 |
+
def __call__(self, logSNR, *args, shift=1, clamp_range=None, **kwargs):
|
| 10 |
+
clamp_range = [-1e9, 1e9] if clamp_range is None else clamp_range
|
| 11 |
+
if shift != 1:
|
| 12 |
+
logSNR = logSNR.clone() + 2 * np.log(shift)
|
| 13 |
+
return self.weight(logSNR, *args, **kwargs).clamp(*clamp_range)
|
| 14 |
+
|
| 15 |
+
class ComposedLossWeight(BaseLossWeight):
|
| 16 |
+
def __init__(self, div, mul):
|
| 17 |
+
self.mul = [mul] if isinstance(mul, BaseLossWeight) else mul
|
| 18 |
+
self.div = [div] if isinstance(div, BaseLossWeight) else div
|
| 19 |
+
|
| 20 |
+
def weight(self, logSNR):
|
| 21 |
+
prod, div = 1, 1
|
| 22 |
+
for m in self.mul:
|
| 23 |
+
prod *= m.weight(logSNR)
|
| 24 |
+
for d in self.div:
|
| 25 |
+
div *= d.weight(logSNR)
|
| 26 |
+
return prod/div
|
| 27 |
+
|
| 28 |
+
class ConstantLossWeight(BaseLossWeight):
|
| 29 |
+
def __init__(self, v=1):
|
| 30 |
+
self.v = v
|
| 31 |
+
|
| 32 |
+
def weight(self, logSNR):
|
| 33 |
+
return torch.ones_like(logSNR) * self.v
|
| 34 |
+
|
| 35 |
+
class SNRLossWeight(BaseLossWeight):
|
| 36 |
+
def weight(self, logSNR):
|
| 37 |
+
return logSNR.exp()
|
| 38 |
+
|
| 39 |
+
class P2LossWeight(BaseLossWeight):
|
| 40 |
+
def __init__(self, k=1.0, gamma=1.0, s=1.0):
|
| 41 |
+
self.k, self.gamma, self.s = k, gamma, s
|
| 42 |
+
|
| 43 |
+
def weight(self, logSNR):
|
| 44 |
+
return (self.k + (logSNR * self.s).exp()) ** -self.gamma
|
| 45 |
+
|
| 46 |
+
class SNRPlusOneLossWeight(BaseLossWeight):
|
| 47 |
+
def weight(self, logSNR):
|
| 48 |
+
return logSNR.exp() + 1
|
| 49 |
+
|
| 50 |
+
class MinSNRLossWeight(BaseLossWeight):
|
| 51 |
+
def __init__(self, max_snr=5):
|
| 52 |
+
self.max_snr = max_snr
|
| 53 |
+
|
| 54 |
+
def weight(self, logSNR):
|
| 55 |
+
return logSNR.exp().clamp(max=self.max_snr)
|
| 56 |
+
|
| 57 |
+
class MinSNRPlusOneLossWeight(BaseLossWeight):
|
| 58 |
+
def __init__(self, max_snr=5):
|
| 59 |
+
self.max_snr = max_snr
|
| 60 |
+
|
| 61 |
+
def weight(self, logSNR):
|
| 62 |
+
return (logSNR.exp() + 1).clamp(max=self.max_snr)
|
| 63 |
+
|
| 64 |
+
class TruncatedSNRLossWeight(BaseLossWeight):
|
| 65 |
+
def __init__(self, min_snr=1):
|
| 66 |
+
self.min_snr = min_snr
|
| 67 |
+
|
| 68 |
+
def weight(self, logSNR):
|
| 69 |
+
return logSNR.exp().clamp(min=self.min_snr)
|
| 70 |
+
|
| 71 |
+
class SechLossWeight(BaseLossWeight):
|
| 72 |
+
def __init__(self, div=2):
|
| 73 |
+
self.div = div
|
| 74 |
+
|
| 75 |
+
def weight(self, logSNR):
|
| 76 |
+
return 1/(logSNR/self.div).cosh()
|
| 77 |
+
|
| 78 |
+
class DebiasedLossWeight(BaseLossWeight):
|
| 79 |
+
def weight(self, logSNR):
|
| 80 |
+
return 1/logSNR.exp().sqrt()
|
| 81 |
+
|
| 82 |
+
class SigmoidLossWeight(BaseLossWeight):
|
| 83 |
+
def __init__(self, s=1):
|
| 84 |
+
self.s = s
|
| 85 |
+
|
| 86 |
+
def weight(self, logSNR):
|
| 87 |
+
return (logSNR * self.s).sigmoid()
|
| 88 |
+
|
| 89 |
+
class AdaptiveLossWeight(BaseLossWeight):
|
| 90 |
+
def __init__(self, logsnr_range=[-10, 10], buckets=300, weight_range=[1e-7, 1e7]):
|
| 91 |
+
self.bucket_ranges = torch.linspace(logsnr_range[0], logsnr_range[1], buckets-1)
|
| 92 |
+
self.bucket_losses = torch.ones(buckets)
|
| 93 |
+
self.weight_range = weight_range
|
| 94 |
+
|
| 95 |
+
def weight(self, logSNR):
|
| 96 |
+
indices = torch.searchsorted(self.bucket_ranges.to(logSNR.device), logSNR)
|
| 97 |
+
return (1/self.bucket_losses.to(logSNR.device)[indices]).clamp(*self.weight_range)
|
| 98 |
+
|
| 99 |
+
def update_buckets(self, logSNR, loss, beta=0.99):
|
| 100 |
+
indices = torch.searchsorted(self.bucket_ranges.to(logSNR.device), logSNR).cpu()
|
| 101 |
+
self.bucket_losses[indices] = self.bucket_losses[indices]*beta + loss.detach().cpu() * (1-beta)
|
gdf/noise_conditions.py
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
import numpy as np
|
| 3 |
+
|
| 4 |
+
class BaseNoiseCond():
|
| 5 |
+
def __init__(self, *args, shift=1, clamp_range=None, **kwargs):
|
| 6 |
+
clamp_range = [-1e9, 1e9] if clamp_range is None else clamp_range
|
| 7 |
+
self.shift = shift
|
| 8 |
+
self.clamp_range = clamp_range
|
| 9 |
+
self.setup(*args, **kwargs)
|
| 10 |
+
|
| 11 |
+
def setup(self, *args, **kwargs):
|
| 12 |
+
pass # this method is optional, override it if required
|
| 13 |
+
|
| 14 |
+
def cond(self, logSNR):
|
| 15 |
+
raise NotImplementedError("this method needs to be overriden")
|
| 16 |
+
|
| 17 |
+
def __call__(self, logSNR):
|
| 18 |
+
if self.shift != 1:
|
| 19 |
+
logSNR = logSNR.clone() + 2 * np.log(self.shift)
|
| 20 |
+
return self.cond(logSNR).clamp(*self.clamp_range)
|
| 21 |
+
|
| 22 |
+
class CosineTNoiseCond(BaseNoiseCond):
|
| 23 |
+
def setup(self, s=0.008, clamp_range=[0, 1]): # [0.0001, 0.9999]
|
| 24 |
+
self.s = torch.tensor([s])
|
| 25 |
+
self.clamp_range = clamp_range
|
| 26 |
+
self.min_var = torch.cos(self.s / (1 + self.s) * torch.pi * 0.5) ** 2
|
| 27 |
+
|
| 28 |
+
def cond(self, logSNR):
|
| 29 |
+
var = logSNR.sigmoid()
|
| 30 |
+
var = var.clamp(*self.clamp_range)
|
| 31 |
+
s, min_var = self.s.to(var.device), self.min_var.to(var.device)
|
| 32 |
+
t = (((var * min_var) ** 0.5).acos() / (torch.pi * 0.5)) * (1 + s) - s
|
| 33 |
+
return t
|
| 34 |
+
|
| 35 |
+
class EDMNoiseCond(BaseNoiseCond):
|
| 36 |
+
def cond(self, logSNR):
|
| 37 |
+
return -logSNR/8
|
| 38 |
+
|
| 39 |
+
class SigmoidNoiseCond(BaseNoiseCond):
|
| 40 |
+
def cond(self, logSNR):
|
| 41 |
+
return (-logSNR).sigmoid()
|
| 42 |
+
|
| 43 |
+
class LogSNRNoiseCond(BaseNoiseCond):
|
| 44 |
+
def cond(self, logSNR):
|
| 45 |
+
return logSNR
|
| 46 |
+
|
| 47 |
+
class EDMSigmaNoiseCond(BaseNoiseCond):
|
| 48 |
+
def setup(self, sigma_data=1):
|
| 49 |
+
self.sigma_data = sigma_data
|
| 50 |
+
|
| 51 |
+
def cond(self, logSNR):
|
| 52 |
+
return torch.exp(-logSNR / 2) * self.sigma_data
|
| 53 |
+
|
| 54 |
+
class RectifiedFlowsNoiseCond(BaseNoiseCond):
|
| 55 |
+
def cond(self, logSNR):
|
| 56 |
+
_a = logSNR.exp() - 1
|
| 57 |
+
_a[_a == 0] = 1e-3 # Avoid division by zero
|
| 58 |
+
a = 1 + (2-(2**2 + 4*_a)**0.5) / (2*_a)
|
| 59 |
+
return a
|
| 60 |
+
|
| 61 |
+
# Any NoiseCond that cannot be described easily as a continuous function of t
|
| 62 |
+
# It needs to define self.x and self.y in the setup() method
|
| 63 |
+
class PiecewiseLinearNoiseCond(BaseNoiseCond):
|
| 64 |
+
def setup(self):
|
| 65 |
+
self.x = None
|
| 66 |
+
self.y = None
|
| 67 |
+
|
| 68 |
+
def piecewise_linear(self, y, xs, ys):
|
| 69 |
+
indices = (len(xs)-2) - torch.searchsorted(ys.flip(dims=(-1,))[:-2], y)
|
| 70 |
+
x_min, x_max = xs[indices], xs[indices+1]
|
| 71 |
+
y_min, y_max = ys[indices], ys[indices+1]
|
| 72 |
+
x = x_min + (x_max - x_min) * (y - y_min) / (y_max - y_min)
|
| 73 |
+
return x
|
| 74 |
+
|
| 75 |
+
def cond(self, logSNR):
|
| 76 |
+
var = logSNR.sigmoid()
|
| 77 |
+
t = self.piecewise_linear(var, self.x.to(var.device), self.y.to(var.device)) # .mul(1000).round().clamp(min=0)
|
| 78 |
+
return t
|
| 79 |
+
|
| 80 |
+
class StableDiffusionNoiseCond(PiecewiseLinearNoiseCond):
|
| 81 |
+
def setup(self, linear_range=[0.00085, 0.012], total_steps=1000):
|
| 82 |
+
self.total_steps = total_steps
|
| 83 |
+
linear_range_sqrt = [r**0.5 for r in linear_range]
|
| 84 |
+
self.x = torch.linspace(0, 1, total_steps+1)
|
| 85 |
+
|
| 86 |
+
alphas = 1-(linear_range_sqrt[0]*(1-self.x) + linear_range_sqrt[1]*self.x)**2
|
| 87 |
+
self.y = alphas.cumprod(dim=-1)
|
| 88 |
+
|
| 89 |
+
def cond(self, logSNR):
|
| 90 |
+
return super().cond(logSNR).clamp(0, 1)
|
| 91 |
+
|
| 92 |
+
class DiscreteNoiseCond(BaseNoiseCond):
|
| 93 |
+
def setup(self, noise_cond, steps=1000, continuous_range=[0, 1]):
|
| 94 |
+
self.noise_cond = noise_cond
|
| 95 |
+
self.steps = steps
|
| 96 |
+
self.continuous_range = continuous_range
|
| 97 |
+
|
| 98 |
+
def cond(self, logSNR):
|
| 99 |
+
cond = self.noise_cond(logSNR)
|
| 100 |
+
cond = (cond-self.continuous_range[0]) / (self.continuous_range[1]-self.continuous_range[0])
|
| 101 |
+
return cond.mul(self.steps).long()
|
| 102 |
+
|
gdf/readme.md
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Generic Diffusion Framework (GDF)
|
| 2 |
+
|
| 3 |
+
# Basic usage
|
| 4 |
+
GDF is a simple framework for working with diffusion models. It implements most common diffusion frameworks (DDPM / DDIM
|
| 5 |
+
, EDM, Rectified Flows, etc.) and makes it very easy to switch between them or combine different parts of different
|
| 6 |
+
frameworks
|
| 7 |
+
|
| 8 |
+
Using GDF is very straighforward, first of all just define an instance of the GDF class:
|
| 9 |
+
|
| 10 |
+
```python
|
| 11 |
+
from gdf import GDF
|
| 12 |
+
from gdf import CosineSchedule
|
| 13 |
+
from gdf import VPScaler, EpsilonTarget, CosineTNoiseCond, P2LossWeight
|
| 14 |
+
|
| 15 |
+
gdf = GDF(
|
| 16 |
+
schedule=CosineSchedule(clamp_range=[0.0001, 0.9999]),
|
| 17 |
+
input_scaler=VPScaler(), target=EpsilonTarget(),
|
| 18 |
+
noise_cond=CosineTNoiseCond(),
|
| 19 |
+
loss_weight=P2LossWeight(),
|
| 20 |
+
)
|
| 21 |
+
```
|
| 22 |
+
|
| 23 |
+
You need to define the following components:
|
| 24 |
+
* **Train Schedule**: This will return the logSNR schedule that will be used during training, some of the schedulers can be configured. A train schedule will then be called with a batch size and will randomly sample some values from the defined distribution.
|
| 25 |
+
* **Sample Schedule**: This is the schedule that will be used later on when sampling. It might be different from the training schedule.
|
| 26 |
+
* **Input Scaler**: If you want to use Variance Preserving or LERP (rectified flows)
|
| 27 |
+
* **Target**: What the target is during training, usually: epsilon, x0 or v
|
| 28 |
+
* **Noise Conditioning**: You could directly pass the logSNR to your model but usually a normalized value is used instead, for example the EDM framework proposes to use `-logSNR/8`
|
| 29 |
+
* **Loss Weight**: There are many proposed loss weighting strategies, here you define which one you'll use
|
| 30 |
+
|
| 31 |
+
All of those classes are actually very simple logSNR centric definitions, for example the VPScaler is defined as just:
|
| 32 |
+
```python
|
| 33 |
+
class VPScaler():
|
| 34 |
+
def __call__(self, logSNR):
|
| 35 |
+
a_squared = logSNR.sigmoid()
|
| 36 |
+
a = a_squared.sqrt()
|
| 37 |
+
b = (1-a_squared).sqrt()
|
| 38 |
+
return a, b
|
| 39 |
+
|
| 40 |
+
```
|
| 41 |
+
|
| 42 |
+
So it's very easy to extend this framework with custom schedulers, scalers, targets, loss weights, etc...
|
| 43 |
+
|
| 44 |
+
### Training
|
| 45 |
+
|
| 46 |
+
When you define your training loop you can get all you need by just doing:
|
| 47 |
+
```python
|
| 48 |
+
shift, loss_shift = 1, 1 # this can be set to higher values as per what the Simple Diffusion paper sugested for high resolution
|
| 49 |
+
for inputs, extra_conditions in dataloader_iterator:
|
| 50 |
+
noised, noise, target, logSNR, noise_cond, loss_weight = gdf.diffuse(inputs, shift=shift, loss_shift=loss_shift)
|
| 51 |
+
pred = diffusion_model(noised, noise_cond, extra_conditions)
|
| 52 |
+
|
| 53 |
+
loss = nn.functional.mse_loss(pred, target, reduction='none').mean(dim=[1, 2, 3])
|
| 54 |
+
loss_adjusted = (loss * loss_weight).mean()
|
| 55 |
+
|
| 56 |
+
loss_adjusted.backward()
|
| 57 |
+
optimizer.step()
|
| 58 |
+
optimizer.zero_grad(set_to_none=True)
|
| 59 |
+
```
|
| 60 |
+
|
| 61 |
+
And that's all, you have a diffusion model training, where it's very easy to customize the different elements of the
|
| 62 |
+
training from the GDF class.
|
| 63 |
+
|
| 64 |
+
### Sampling
|
| 65 |
+
|
| 66 |
+
The other important part is sampling, when you want to use this framework to sample you can just do the following:
|
| 67 |
+
|
| 68 |
+
```python
|
| 69 |
+
from gdf import DDPMSampler
|
| 70 |
+
|
| 71 |
+
shift = 1
|
| 72 |
+
sampling_configs = {
|
| 73 |
+
"timesteps": 30, "cfg": 7, "sampler": DDPMSampler(gdf), "shift": shift,
|
| 74 |
+
"schedule": CosineSchedule(clamp_range=[0.0001, 0.9999])
|
| 75 |
+
}
|
| 76 |
+
|
| 77 |
+
*_, (sampled, _, _) = gdf.sample(
|
| 78 |
+
diffusion_model, {"cond": extra_conditions}, latents.shape,
|
| 79 |
+
unconditional_inputs= {"cond": torch.zeros_like(extra_conditions)},
|
| 80 |
+
device=device, **sampling_configs
|
| 81 |
+
)
|
| 82 |
+
```
|
| 83 |
+
|
| 84 |
+
# Available modules
|
| 85 |
+
|
| 86 |
+
TODO
|
gdf/samplers.py
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
|
| 3 |
+
class SimpleSampler():
|
| 4 |
+
def __init__(self, gdf):
|
| 5 |
+
self.gdf = gdf
|
| 6 |
+
self.current_step = -1
|
| 7 |
+
|
| 8 |
+
def __call__(self, *args, **kwargs):
|
| 9 |
+
self.current_step += 1
|
| 10 |
+
return self.step(*args, **kwargs)
|
| 11 |
+
|
| 12 |
+
def init_x(self, shape):
|
| 13 |
+
return torch.randn(*shape)
|
| 14 |
+
|
| 15 |
+
def step(self, x, x0, epsilon, logSNR, logSNR_prev):
|
| 16 |
+
raise NotImplementedError("You should override the 'apply' function.")
|
| 17 |
+
|
| 18 |
+
class DDIMSampler(SimpleSampler):
|
| 19 |
+
def step(self, x, x0, epsilon, logSNR, logSNR_prev, eta=0):
|
| 20 |
+
a, b = self.gdf.input_scaler(logSNR)
|
| 21 |
+
if len(a.shape) == 1:
|
| 22 |
+
a, b = a.view(-1, *[1]*(len(x0.shape)-1)), b.view(-1, *[1]*(len(x0.shape)-1))
|
| 23 |
+
|
| 24 |
+
a_prev, b_prev = self.gdf.input_scaler(logSNR_prev)
|
| 25 |
+
if len(a_prev.shape) == 1:
|
| 26 |
+
a_prev, b_prev = a_prev.view(-1, *[1]*(len(x0.shape)-1)), b_prev.view(-1, *[1]*(len(x0.shape)-1))
|
| 27 |
+
|
| 28 |
+
sigma_tau = eta * (b_prev**2 / b**2).sqrt() * (1 - a**2 / a_prev**2).sqrt() if eta > 0 else 0
|
| 29 |
+
# x = a_prev * x0 + (1 - a_prev**2 - sigma_tau ** 2).sqrt() * epsilon + sigma_tau * torch.randn_like(x0)
|
| 30 |
+
x = a_prev * x0 + (b_prev**2 - sigma_tau**2).sqrt() * epsilon + sigma_tau * torch.randn_like(x0)
|
| 31 |
+
return x
|
| 32 |
+
|
| 33 |
+
class DDPMSampler(DDIMSampler):
|
| 34 |
+
def step(self, x, x0, epsilon, logSNR, logSNR_prev, eta=1):
|
| 35 |
+
return super().step(x, x0, epsilon, logSNR, logSNR_prev, eta)
|
| 36 |
+
|
| 37 |
+
class LCMSampler(SimpleSampler):
|
| 38 |
+
def step(self, x, x0, epsilon, logSNR, logSNR_prev):
|
| 39 |
+
a_prev, b_prev = self.gdf.input_scaler(logSNR_prev)
|
| 40 |
+
if len(a_prev.shape) == 1:
|
| 41 |
+
a_prev, b_prev = a_prev.view(-1, *[1]*(len(x0.shape)-1)), b_prev.view(-1, *[1]*(len(x0.shape)-1))
|
| 42 |
+
return x0 * a_prev + torch.randn_like(epsilon) * b_prev
|
| 43 |
+
|
gdf/scalers.py
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
|
| 3 |
+
class BaseScaler():
|
| 4 |
+
def __init__(self):
|
| 5 |
+
self.stretched_limits = None
|
| 6 |
+
|
| 7 |
+
def setup_limits(self, schedule, input_scaler, stretch_max=True, stretch_min=True, shift=1):
|
| 8 |
+
min_logSNR = schedule(torch.ones(1), shift=shift)
|
| 9 |
+
max_logSNR = schedule(torch.zeros(1), shift=shift)
|
| 10 |
+
|
| 11 |
+
min_a, max_b = [v.item() for v in input_scaler(min_logSNR)] if stretch_max else [0, 1]
|
| 12 |
+
max_a, min_b = [v.item() for v in input_scaler(max_logSNR)] if stretch_min else [1, 0]
|
| 13 |
+
self.stretched_limits = [min_a, max_a, min_b, max_b]
|
| 14 |
+
return self.stretched_limits
|
| 15 |
+
|
| 16 |
+
def stretch_limits(self, a, b):
|
| 17 |
+
min_a, max_a, min_b, max_b = self.stretched_limits
|
| 18 |
+
return (a - min_a) / (max_a - min_a), (b - min_b) / (max_b - min_b)
|
| 19 |
+
|
| 20 |
+
def scalers(self, logSNR):
|
| 21 |
+
raise NotImplementedError("this method needs to be overridden")
|
| 22 |
+
|
| 23 |
+
def __call__(self, logSNR):
|
| 24 |
+
a, b = self.scalers(logSNR)
|
| 25 |
+
if self.stretched_limits is not None:
|
| 26 |
+
a, b = self.stretch_limits(a, b)
|
| 27 |
+
return a, b
|
| 28 |
+
|
| 29 |
+
class VPScaler(BaseScaler):
|
| 30 |
+
def scalers(self, logSNR):
|
| 31 |
+
a_squared = logSNR.sigmoid()
|
| 32 |
+
a = a_squared.sqrt()
|
| 33 |
+
b = (1-a_squared).sqrt()
|
| 34 |
+
return a, b
|
| 35 |
+
|
| 36 |
+
class LERPScaler(BaseScaler):
|
| 37 |
+
def scalers(self, logSNR):
|
| 38 |
+
_a = logSNR.exp() - 1
|
| 39 |
+
_a[_a == 0] = 1e-3 # Avoid division by zero
|
| 40 |
+
a = 1 + (2-(2**2 + 4*_a)**0.5) / (2*_a)
|
| 41 |
+
b = 1-a
|
| 42 |
+
return a, b
|
gdf/schedulers.py
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
import numpy as np
|
| 3 |
+
|
| 4 |
+
class BaseSchedule():
|
| 5 |
+
def __init__(self, *args, force_limits=True, discrete_steps=None, shift=1, **kwargs):
|
| 6 |
+
self.setup(*args, **kwargs)
|
| 7 |
+
self.limits = None
|
| 8 |
+
self.discrete_steps = discrete_steps
|
| 9 |
+
self.shift = shift
|
| 10 |
+
if force_limits:
|
| 11 |
+
self.reset_limits()
|
| 12 |
+
|
| 13 |
+
def reset_limits(self, shift=1, disable=False):
|
| 14 |
+
try:
|
| 15 |
+
self.limits = None if disable else self(torch.tensor([1.0, 0.0]), shift=shift).tolist() # min, max
|
| 16 |
+
return self.limits
|
| 17 |
+
except Exception:
|
| 18 |
+
print("WARNING: this schedule doesn't support t and will be unbounded")
|
| 19 |
+
return None
|
| 20 |
+
|
| 21 |
+
def setup(self, *args, **kwargs):
|
| 22 |
+
raise NotImplementedError("this method needs to be overriden")
|
| 23 |
+
|
| 24 |
+
def schedule(self, *args, **kwargs):
|
| 25 |
+
raise NotImplementedError("this method needs to be overriden")
|
| 26 |
+
|
| 27 |
+
def __call__(self, t, *args, shift=1, **kwargs):
|
| 28 |
+
if isinstance(t, torch.Tensor):
|
| 29 |
+
batch_size = None
|
| 30 |
+
if self.discrete_steps is not None:
|
| 31 |
+
if t.dtype != torch.long:
|
| 32 |
+
t = (t * (self.discrete_steps-1)).round().long()
|
| 33 |
+
t = t / (self.discrete_steps-1)
|
| 34 |
+
t = t.clamp(0, 1)
|
| 35 |
+
else:
|
| 36 |
+
batch_size = t
|
| 37 |
+
t = None
|
| 38 |
+
logSNR = self.schedule(t, batch_size, *args, **kwargs)
|
| 39 |
+
if shift*self.shift != 1:
|
| 40 |
+
logSNR += 2 * np.log(1/(shift*self.shift))
|
| 41 |
+
if self.limits is not None:
|
| 42 |
+
logSNR = logSNR.clamp(*self.limits)
|
| 43 |
+
return logSNR
|
| 44 |
+
|
| 45 |
+
class CosineSchedule(BaseSchedule):
|
| 46 |
+
def setup(self, s=0.008, clamp_range=[0.0001, 0.9999], norm_instead=False):
|
| 47 |
+
self.s = torch.tensor([s])
|
| 48 |
+
self.clamp_range = clamp_range
|
| 49 |
+
self.norm_instead = norm_instead
|
| 50 |
+
self.min_var = torch.cos(self.s / (1 + self.s) * torch.pi * 0.5) ** 2
|
| 51 |
+
|
| 52 |
+
def schedule(self, t, batch_size):
|
| 53 |
+
if t is None:
|
| 54 |
+
t = (1-torch.rand(batch_size)).add(0.001).clamp(0.001, 1.0)
|
| 55 |
+
s, min_var = self.s.to(t.device), self.min_var.to(t.device)
|
| 56 |
+
var = torch.cos((s + t)/(1+s) * torch.pi * 0.5).clamp(0, 1) ** 2 / min_var
|
| 57 |
+
if self.norm_instead:
|
| 58 |
+
var = var * (self.clamp_range[1]-self.clamp_range[0]) + self.clamp_range[0]
|
| 59 |
+
else:
|
| 60 |
+
var = var.clamp(*self.clamp_range)
|
| 61 |
+
logSNR = (var/(1-var)).log()
|
| 62 |
+
return logSNR
|
| 63 |
+
|
| 64 |
+
class CosineSchedule2(BaseSchedule):
|
| 65 |
+
def setup(self, logsnr_range=[-15, 15]):
|
| 66 |
+
self.t_min = np.arctan(np.exp(-0.5 * logsnr_range[1]))
|
| 67 |
+
self.t_max = np.arctan(np.exp(-0.5 * logsnr_range[0]))
|
| 68 |
+
|
| 69 |
+
def schedule(self, t, batch_size):
|
| 70 |
+
if t is None:
|
| 71 |
+
t = 1-torch.rand(batch_size)
|
| 72 |
+
return -2 * (self.t_min + t*(self.t_max-self.t_min)).tan().log()
|
| 73 |
+
|
| 74 |
+
class SqrtSchedule(BaseSchedule):
|
| 75 |
+
def setup(self, s=1e-4, clamp_range=[0.0001, 0.9999], norm_instead=False):
|
| 76 |
+
self.s = s
|
| 77 |
+
self.clamp_range = clamp_range
|
| 78 |
+
self.norm_instead = norm_instead
|
| 79 |
+
|
| 80 |
+
def schedule(self, t, batch_size):
|
| 81 |
+
if t is None:
|
| 82 |
+
t = 1-torch.rand(batch_size)
|
| 83 |
+
var = 1 - (t + self.s)**0.5
|
| 84 |
+
if self.norm_instead:
|
| 85 |
+
var = var * (self.clamp_range[1]-self.clamp_range[0]) + self.clamp_range[0]
|
| 86 |
+
else:
|
| 87 |
+
var = var.clamp(*self.clamp_range)
|
| 88 |
+
logSNR = (var/(1-var)).log()
|
| 89 |
+
return logSNR
|
| 90 |
+
|
| 91 |
+
class RectifiedFlowsSchedule(BaseSchedule):
|
| 92 |
+
def setup(self, logsnr_range=[-15, 15]):
|
| 93 |
+
self.logsnr_range = logsnr_range
|
| 94 |
+
|
| 95 |
+
def schedule(self, t, batch_size):
|
| 96 |
+
if t is None:
|
| 97 |
+
t = 1-torch.rand(batch_size)
|
| 98 |
+
logSNR = (((1-t)**2)/(t**2)).log()
|
| 99 |
+
logSNR = logSNR.clamp(*self.logsnr_range)
|
| 100 |
+
return logSNR
|
| 101 |
+
|
| 102 |
+
class EDMSampleSchedule(BaseSchedule):
|
| 103 |
+
def setup(self, sigma_range=[0.002, 80], p=7):
|
| 104 |
+
self.sigma_range = sigma_range
|
| 105 |
+
self.p = p
|
| 106 |
+
|
| 107 |
+
def schedule(self, t, batch_size):
|
| 108 |
+
if t is None:
|
| 109 |
+
t = 1-torch.rand(batch_size)
|
| 110 |
+
smin, smax, p = *self.sigma_range, self.p
|
| 111 |
+
sigma = (smax ** (1/p) + (1-t) * (smin ** (1/p) - smax ** (1/p))) ** p
|
| 112 |
+
logSNR = (1/sigma**2).log()
|
| 113 |
+
return logSNR
|
| 114 |
+
|
| 115 |
+
class EDMTrainSchedule(BaseSchedule):
|
| 116 |
+
def setup(self, mu=-1.2, std=1.2):
|
| 117 |
+
self.mu = mu
|
| 118 |
+
self.std = std
|
| 119 |
+
|
| 120 |
+
def schedule(self, t, batch_size):
|
| 121 |
+
if t is not None:
|
| 122 |
+
raise Exception("EDMTrainSchedule doesn't support passing timesteps: t")
|
| 123 |
+
logSNR = -2*(torch.randn(batch_size) * self.std - self.mu)
|
| 124 |
+
return logSNR
|
| 125 |
+
|
| 126 |
+
class LinearSchedule(BaseSchedule):
|
| 127 |
+
def setup(self, logsnr_range=[-10, 10]):
|
| 128 |
+
self.logsnr_range = logsnr_range
|
| 129 |
+
|
| 130 |
+
def schedule(self, t, batch_size):
|
| 131 |
+
if t is None:
|
| 132 |
+
t = 1-torch.rand(batch_size)
|
| 133 |
+
logSNR = t * (self.logsnr_range[0]-self.logsnr_range[1]) + self.logsnr_range[1]
|
| 134 |
+
return logSNR
|
| 135 |
+
|
| 136 |
+
# Any schedule that cannot be described easily as a continuous function of t
|
| 137 |
+
# It needs to define self.x and self.y in the setup() method
|
| 138 |
+
class PiecewiseLinearSchedule(BaseSchedule):
|
| 139 |
+
def setup(self):
|
| 140 |
+
self.x = None
|
| 141 |
+
self.y = None
|
| 142 |
+
|
| 143 |
+
def piecewise_linear(self, x, xs, ys):
|
| 144 |
+
indices = torch.searchsorted(xs[:-1], x) - 1
|
| 145 |
+
x_min, x_max = xs[indices], xs[indices+1]
|
| 146 |
+
y_min, y_max = ys[indices], ys[indices+1]
|
| 147 |
+
var = y_min + (y_max - y_min) * (x - x_min) / (x_max - x_min)
|
| 148 |
+
return var
|
| 149 |
+
|
| 150 |
+
def schedule(self, t, batch_size):
|
| 151 |
+
if t is None:
|
| 152 |
+
t = 1-torch.rand(batch_size)
|
| 153 |
+
var = self.piecewise_linear(t, self.x.to(t.device), self.y.to(t.device))
|
| 154 |
+
logSNR = (var/(1-var)).log()
|
| 155 |
+
return logSNR
|
| 156 |
+
|
| 157 |
+
class StableDiffusionSchedule(PiecewiseLinearSchedule):
|
| 158 |
+
def setup(self, linear_range=[0.00085, 0.012], total_steps=1000):
|
| 159 |
+
linear_range_sqrt = [r**0.5 for r in linear_range]
|
| 160 |
+
self.x = torch.linspace(0, 1, total_steps+1)
|
| 161 |
+
|
| 162 |
+
alphas = 1-(linear_range_sqrt[0]*(1-self.x) + linear_range_sqrt[1]*self.x)**2
|
| 163 |
+
self.y = alphas.cumprod(dim=-1)
|
| 164 |
+
|
| 165 |
+
class AdaptiveTrainSchedule(BaseSchedule):
|
| 166 |
+
def setup(self, logsnr_range=[-10, 10], buckets=100, min_probs=0.0):
|
| 167 |
+
th = torch.linspace(logsnr_range[0], logsnr_range[1], buckets+1)
|
| 168 |
+
self.bucket_ranges = torch.tensor([(th[i], th[i+1]) for i in range(buckets)])
|
| 169 |
+
self.bucket_probs = torch.ones(buckets)
|
| 170 |
+
self.min_probs = min_probs
|
| 171 |
+
|
| 172 |
+
def schedule(self, t, batch_size):
|
| 173 |
+
if t is not None:
|
| 174 |
+
raise Exception("AdaptiveTrainSchedule doesn't support passing timesteps: t")
|
| 175 |
+
norm_probs = ((self.bucket_probs+self.min_probs) / (self.bucket_probs+self.min_probs).sum())
|
| 176 |
+
buckets = torch.multinomial(norm_probs, batch_size, replacement=True)
|
| 177 |
+
ranges = self.bucket_ranges[buckets]
|
| 178 |
+
logSNR = torch.rand(batch_size) * (ranges[:, 1]-ranges[:, 0]) + ranges[:, 0]
|
| 179 |
+
return logSNR
|
| 180 |
+
|
| 181 |
+
def update_buckets(self, logSNR, loss, beta=0.99):
|
| 182 |
+
range_mtx = self.bucket_ranges.unsqueeze(0).expand(logSNR.size(0), -1, -1).to(logSNR.device)
|
| 183 |
+
range_mask = (range_mtx[:, :, 0] <= logSNR[:, None]) * (range_mtx[:, :, 1] > logSNR[:, None]).float()
|
| 184 |
+
range_idx = range_mask.argmax(-1).cpu()
|
| 185 |
+
self.bucket_probs[range_idx] = self.bucket_probs[range_idx] * beta + loss.detach().cpu() * (1-beta)
|
| 186 |
+
|
| 187 |
+
class InterpolatedSchedule(BaseSchedule):
|
| 188 |
+
def setup(self, scheduler1, scheduler2, shifts=[1.0, 1.0]):
|
| 189 |
+
self.scheduler1 = scheduler1
|
| 190 |
+
self.scheduler2 = scheduler2
|
| 191 |
+
self.shifts = shifts
|
| 192 |
+
|
| 193 |
+
def schedule(self, t, batch_size):
|
| 194 |
+
if t is None:
|
| 195 |
+
t = 1-torch.rand(batch_size)
|
| 196 |
+
t = t.clamp(1e-7, 1-1e-7) # avoid infinities multiplied by 0 which cause nan
|
| 197 |
+
low_logSNR = self.scheduler1(t, shift=self.shifts[0])
|
| 198 |
+
high_logSNR = self.scheduler2(t, shift=self.shifts[1])
|
| 199 |
+
return low_logSNR * t + high_logSNR * (1-t)
|
| 200 |
+
|
gdf/targets.py
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
class EpsilonTarget():
|
| 2 |
+
def __call__(self, x0, epsilon, logSNR, a, b):
|
| 3 |
+
return epsilon
|
| 4 |
+
|
| 5 |
+
def x0(self, noised, pred, logSNR, a, b):
|
| 6 |
+
return (noised - pred * b) / a
|
| 7 |
+
|
| 8 |
+
def epsilon(self, noised, pred, logSNR, a, b):
|
| 9 |
+
return pred
|
| 10 |
+
def noise_givenx0_noised(self, x0, noised , logSNR, a, b):
|
| 11 |
+
return (noised - a * x0) / b
|
| 12 |
+
def xt(self, x0, noise, logSNR, a, b):
|
| 13 |
+
|
| 14 |
+
return x0 * a + noise*b
|
| 15 |
+
class X0Target():
|
| 16 |
+
def __call__(self, x0, epsilon, logSNR, a, b):
|
| 17 |
+
return x0
|
| 18 |
+
|
| 19 |
+
def x0(self, noised, pred, logSNR, a, b):
|
| 20 |
+
return pred
|
| 21 |
+
|
| 22 |
+
def epsilon(self, noised, pred, logSNR, a, b):
|
| 23 |
+
return (noised - pred * a) / b
|
| 24 |
+
|
| 25 |
+
class VTarget():
|
| 26 |
+
def __call__(self, x0, epsilon, logSNR, a, b):
|
| 27 |
+
return a * epsilon - b * x0
|
| 28 |
+
|
| 29 |
+
def x0(self, noised, pred, logSNR, a, b):
|
| 30 |
+
squared_sum = a**2 + b**2
|
| 31 |
+
return a/squared_sum * noised - b/squared_sum * pred
|
| 32 |
+
|
| 33 |
+
def epsilon(self, noised, pred, logSNR, a, b):
|
| 34 |
+
squared_sum = a**2 + b**2
|
| 35 |
+
return b/squared_sum * noised + a/squared_sum * pred
|
| 36 |
+
|
| 37 |
+
class RectifiedFlowsTarget():
|
| 38 |
+
def __call__(self, x0, epsilon, logSNR, a, b):
|
| 39 |
+
return epsilon - x0
|
| 40 |
+
|
| 41 |
+
def x0(self, noised, pred, logSNR, a, b):
|
| 42 |
+
return noised - pred * b
|
| 43 |
+
|
| 44 |
+
def epsilon(self, noised, pred, logSNR, a, b):
|
| 45 |
+
return noised + pred * a
|
| 46 |
+
|
inference/__init__.py
ADDED
|
File without changes
|
inference/test_controlnet.py
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import yaml
|
| 3 |
+
import torch
|
| 4 |
+
import torchvision
|
| 5 |
+
from tqdm import tqdm
|
| 6 |
+
import sys
|
| 7 |
+
sys.path.append(os.path.abspath('./'))
|
| 8 |
+
|
| 9 |
+
from inference.utils import *
|
| 10 |
+
from core.utils import load_or_fail
|
| 11 |
+
from train import WurstCore_control_lrguide, WurstCoreB
|
| 12 |
+
from PIL import Image
|
| 13 |
+
from core.utils import load_or_fail
|
| 14 |
+
import math
|
| 15 |
+
import argparse
|
| 16 |
+
import time
|
| 17 |
+
import random
|
| 18 |
+
import numpy as np
|
| 19 |
+
def parse_args():
|
| 20 |
+
parser = argparse.ArgumentParser()
|
| 21 |
+
parser.add_argument( '--height', type=int, default=3840, help='image height')
|
| 22 |
+
parser.add_argument('--width', type=int, default=2160, help='image width')
|
| 23 |
+
parser.add_argument('--control_weight', type=float, default=0.70, help='[ 0.3, 0.8]')
|
| 24 |
+
parser.add_argument('--dtype', type=str, default='bf16', help=' if bf16 does not work, change it to float32 ')
|
| 25 |
+
parser.add_argument('--seed', type=int, default=123, help='random seed')
|
| 26 |
+
parser.add_argument('--config_c', type=str,
|
| 27 |
+
default='configs/training/cfg_control_lr.yaml' ,help='config file for stage c, latent generation')
|
| 28 |
+
parser.add_argument('--config_b', type=str,
|
| 29 |
+
default='configs/inference/stage_b_1b.yaml' ,help='config file for stage b, latent decoding')
|
| 30 |
+
parser.add_argument( '--prompt', type=str,
|
| 31 |
+
default='A peaceful lake surrounded by mountain, white cloud in the sky, high quality,', help='text prompt')
|
| 32 |
+
parser.add_argument( '--num_image', type=int, default=4, help='how many images generated')
|
| 33 |
+
parser.add_argument( '--output_dir', type=str, default='figures/controlnet_results/', help='output directory for generated image')
|
| 34 |
+
parser.add_argument( '--stage_a_tiled', action='store_true', help='whther or nor to use tiled decoding for stage a to save memory')
|
| 35 |
+
parser.add_argument( '--pretrained_path', type=str, default='models/ultrapixel_t2i.safetensors', help='pretrained path of newly added paramter of UltraPixel')
|
| 36 |
+
parser.add_argument( '--canny_source_url', type=str, default="figures/California_000490.jpg", help='image used to extract canny edge map')
|
| 37 |
+
|
| 38 |
+
args = parser.parse_args()
|
| 39 |
+
return args
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
if __name__ == "__main__":
|
| 43 |
+
|
| 44 |
+
args = parse_args()
|
| 45 |
+
width = args.width
|
| 46 |
+
height = args.height
|
| 47 |
+
torch.manual_seed(args.seed)
|
| 48 |
+
random.seed(args.seed)
|
| 49 |
+
np.random.seed(args.seed)
|
| 50 |
+
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
|
| 51 |
+
dtype = torch.bfloat16 if args.dtype == 'bf16' else torch.float
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
# SETUP STAGE C
|
| 55 |
+
with open(args.config_c, "r", encoding="utf-8") as file:
|
| 56 |
+
loaded_config = yaml.safe_load(file)
|
| 57 |
+
core = WurstCore_control_lrguide(config_dict=loaded_config, device=device, training=False)
|
| 58 |
+
|
| 59 |
+
# SETUP STAGE B
|
| 60 |
+
with open(args.config_b, "r", encoding="utf-8") as file:
|
| 61 |
+
config_file_b = yaml.safe_load(file)
|
| 62 |
+
|
| 63 |
+
core_b = WurstCoreB(config_dict=config_file_b, device=device, training=False)
|
| 64 |
+
|
| 65 |
+
extras = core.setup_extras_pre()
|
| 66 |
+
models = core.setup_models(extras)
|
| 67 |
+
models.generator.eval().requires_grad_(False)
|
| 68 |
+
print("CONTROLNET READY")
|
| 69 |
+
|
| 70 |
+
extras_b = core_b.setup_extras_pre()
|
| 71 |
+
models_b = core_b.setup_models(extras_b, skip_clip=True)
|
| 72 |
+
models_b = WurstCoreB.Models(
|
| 73 |
+
**{**models_b.to_dict(), 'tokenizer': models.tokenizer, 'text_model': models.text_model}
|
| 74 |
+
)
|
| 75 |
+
models_b.generator.eval().requires_grad_(False)
|
| 76 |
+
print("STAGE B READY")
|
| 77 |
+
|
| 78 |
+
batch_size = 1
|
| 79 |
+
save_dir = args.output_dir
|
| 80 |
+
url = args.canny_source_url
|
| 81 |
+
images = resize_image(Image.open(url).convert("RGB")).unsqueeze(0).expand(batch_size, -1, -1, -1)
|
| 82 |
+
batch = {'images': images}
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
cnet_multiplier = args.control_weight # 0.8 0.6 0.3 control strength
|
| 90 |
+
caption_list = [args.prompt] * args.num_image
|
| 91 |
+
height_lr, width_lr = get_target_lr_size(height / width, std_size=32)
|
| 92 |
+
stage_c_latent_shape_lr, stage_b_latent_shape_lr = calculate_latent_sizes(height_lr, width_lr, batch_size=batch_size)
|
| 93 |
+
stage_c_latent_shape, stage_b_latent_shape = calculate_latent_sizes(height, width, batch_size=batch_size)
|
| 94 |
+
|
| 95 |
+
|
| 96 |
+
|
| 97 |
+
|
| 98 |
+
if not os.path.exists(save_dir):
|
| 99 |
+
os.makedirs(save_dir)
|
| 100 |
+
|
| 101 |
+
|
| 102 |
+
sdd = torch.load(args.pretrained_path, map_location='cpu')
|
| 103 |
+
collect_sd = {}
|
| 104 |
+
for k, v in sdd.items():
|
| 105 |
+
collect_sd[k[7:]] = v
|
| 106 |
+
models.train_norm.load_state_dict(collect_sd, strict=True)
|
| 107 |
+
|
| 108 |
+
|
| 109 |
+
|
| 110 |
+
|
| 111 |
+
models.controlnet.load_state_dict(load_or_fail(core.config.controlnet_checkpoint_path), strict=True)
|
| 112 |
+
# Stage C Parameters
|
| 113 |
+
extras.sampling_configs['cfg'] = 1
|
| 114 |
+
extras.sampling_configs['shift'] = 2
|
| 115 |
+
extras.sampling_configs['timesteps'] = 20
|
| 116 |
+
extras.sampling_configs['t_start'] = 1.0
|
| 117 |
+
|
| 118 |
+
# Stage B Parameters
|
| 119 |
+
extras_b.sampling_configs['cfg'] = 1.1
|
| 120 |
+
extras_b.sampling_configs['shift'] = 1
|
| 121 |
+
extras_b.sampling_configs['timesteps'] = 10
|
| 122 |
+
extras_b.sampling_configs['t_start'] = 1.0
|
| 123 |
+
|
| 124 |
+
# PREPARE CONDITIONS
|
| 125 |
+
|
| 126 |
+
|
| 127 |
+
|
| 128 |
+
|
| 129 |
+
for out_cnt, caption in enumerate(caption_list):
|
| 130 |
+
with torch.no_grad():
|
| 131 |
+
|
| 132 |
+
batch['captions'] = [caption + ' high quality'] * batch_size
|
| 133 |
+
conditions = core.get_conditions(batch, models, extras, is_eval=True, is_unconditional=False, eval_image_embeds=False)
|
| 134 |
+
unconditions = core.get_conditions(batch, models, extras, is_eval=True, is_unconditional=True, eval_image_embeds=False)
|
| 135 |
+
|
| 136 |
+
cnet, cnet_input = core.get_cnet(batch, models, extras)
|
| 137 |
+
cnet_uncond = cnet
|
| 138 |
+
conditions['cnet'] = [c.clone() * cnet_multiplier if c is not None else c for c in cnet]
|
| 139 |
+
unconditions['cnet'] = [c.clone() * cnet_multiplier if c is not None else c for c in cnet_uncond]
|
| 140 |
+
edge_images = show_images(cnet_input)
|
| 141 |
+
models.generator.cuda()
|
| 142 |
+
for idx, img in enumerate(edge_images):
|
| 143 |
+
img.save(os.path.join(save_dir, f"edge_{url.split('/')[-1]}"))
|
| 144 |
+
|
| 145 |
+
|
| 146 |
+
print('STAGE C GENERATION***************************')
|
| 147 |
+
with torch.cuda.amp.autocast(dtype=dtype):
|
| 148 |
+
sampled_c = generation_c(batch, models, extras, core, stage_c_latent_shape, stage_c_latent_shape_lr, device, conditions, unconditions)
|
| 149 |
+
models.generator.cpu()
|
| 150 |
+
torch.cuda.empty_cache()
|
| 151 |
+
|
| 152 |
+
conditions_b = core_b.get_conditions(batch, models_b, extras_b, is_eval=True, is_unconditional=False)
|
| 153 |
+
unconditions_b = core_b.get_conditions(batch, models_b, extras_b, is_eval=True, is_unconditional=True)
|
| 154 |
+
|
| 155 |
+
conditions_b['effnet'] = sampled_c
|
| 156 |
+
unconditions_b['effnet'] = torch.zeros_like(sampled_c)
|
| 157 |
+
print('STAGE B + A DECODING***************************')
|
| 158 |
+
with torch.cuda.amp.autocast(dtype=dtype):
|
| 159 |
+
sampled = decode_b(conditions_b, unconditions_b, models_b, stage_b_latent_shape, extras_b, device, stage_a_tiled=args.stage_a_tiled)
|
| 160 |
+
|
| 161 |
+
torch.cuda.empty_cache()
|
| 162 |
+
imgs = show_images(sampled)
|
| 163 |
+
|
| 164 |
+
for idx, img in enumerate(imgs):
|
| 165 |
+
img.save(os.path.join(save_dir, args.prompt[:20]+'_' + str(out_cnt).zfill(5) + '.jpg'))
|
| 166 |
+
print('finished! Results at ', save_dir )
|
inference/test_personalized.py
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
import os
|
| 3 |
+
import yaml
|
| 4 |
+
import torch
|
| 5 |
+
from tqdm import tqdm
|
| 6 |
+
import sys
|
| 7 |
+
sys.path.append(os.path.abspath('./'))
|
| 8 |
+
from inference.utils import *
|
| 9 |
+
from train import WurstCoreB
|
| 10 |
+
from gdf import VPScaler, CosineTNoiseCond, DDPMSampler, P2LossWeight, AdaptiveLossWeight
|
| 11 |
+
from train import WurstCore_personalized as WurstCoreC
|
| 12 |
+
import torch.nn.functional as F
|
| 13 |
+
import numpy as np
|
| 14 |
+
import random
|
| 15 |
+
import math
|
| 16 |
+
import argparse
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
def parse_args():
|
| 20 |
+
parser = argparse.ArgumentParser()
|
| 21 |
+
parser.add_argument( '--height', type=int, default=3072, help='image height')
|
| 22 |
+
parser.add_argument('--width', type=int, default=4096, help='image width')
|
| 23 |
+
parser.add_argument('--dtype', type=str, default='bf16', help=' if bf16 does not work, change it to float32 ')
|
| 24 |
+
parser.add_argument('--seed', type=int, default=23, help='random seed')
|
| 25 |
+
parser.add_argument('--config_c', type=str,
|
| 26 |
+
default="configs/training/lora_personalization.yaml" ,help='config file for stage c, latent generation')
|
| 27 |
+
parser.add_argument('--config_b', type=str,
|
| 28 |
+
default='configs/inference/stage_b_1b.yaml' ,help='config file for stage b, latent decoding')
|
| 29 |
+
parser.add_argument( '--prompt', type=str,
|
| 30 |
+
default='A photo of cat [roubaobao] with sunglasses, Time Square in the background, high quality, detail rich, 8k', help='text prompt')
|
| 31 |
+
parser.add_argument( '--num_image', type=int, default=4, help='how many images generated')
|
| 32 |
+
parser.add_argument( '--output_dir', type=str, default='figures/personalized/', help='output directory for generated image')
|
| 33 |
+
parser.add_argument( '--stage_a_tiled', action='store_true', help='whther or nor to use tiled decoding for stage a to save memory')
|
| 34 |
+
parser.add_argument( '--pretrained_path_lora', type=str, default='models/lora_cat.safetensors',help='pretrained path of personalized lora parameter')
|
| 35 |
+
parser.add_argument( '--pretrained_path', type=str, default='models/ultrapixel_t2i.safetensors', help='pretrained path of newly added paramter of UltraPixel')
|
| 36 |
+
args = parser.parse_args()
|
| 37 |
+
return args
|
| 38 |
+
|
| 39 |
+
if __name__ == "__main__":
|
| 40 |
+
args = parse_args()
|
| 41 |
+
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
|
| 42 |
+
torch.manual_seed(args.seed)
|
| 43 |
+
random.seed(args.seed)
|
| 44 |
+
np.random.seed(args.seed)
|
| 45 |
+
dtype = torch.bfloat16 if args.dtype == 'bf16' else torch.float
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
# SETUP STAGE C
|
| 49 |
+
with open(args.config_c, "r", encoding="utf-8") as file:
|
| 50 |
+
loaded_config = yaml.safe_load(file)
|
| 51 |
+
core = WurstCoreC(config_dict=loaded_config, device=device, training=False)
|
| 52 |
+
|
| 53 |
+
# SETUP STAGE B
|
| 54 |
+
with open(args.config_b, "r", encoding="utf-8") as file:
|
| 55 |
+
config_file_b = yaml.safe_load(file)
|
| 56 |
+
core_b = WurstCoreB(config_dict=config_file_b, device=device, training=False)
|
| 57 |
+
|
| 58 |
+
extras = core.setup_extras_pre()
|
| 59 |
+
models = core.setup_models(extras)
|
| 60 |
+
models.generator.eval().requires_grad_(False)
|
| 61 |
+
print("STAGE C READY")
|
| 62 |
+
|
| 63 |
+
extras_b = core_b.setup_extras_pre()
|
| 64 |
+
models_b = core_b.setup_models(extras_b, skip_clip=True)
|
| 65 |
+
models_b = WurstCoreB.Models(
|
| 66 |
+
**{**models_b.to_dict(), 'tokenizer': models.tokenizer, 'text_model': models.text_model}
|
| 67 |
+
)
|
| 68 |
+
models_b.generator.bfloat16().eval().requires_grad_(False)
|
| 69 |
+
print("STAGE B READY")
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
batch_size = 1
|
| 73 |
+
captions = [args.prompt] * args.num_image
|
| 74 |
+
height, width = args.height, args.width
|
| 75 |
+
save_dir = args.output_dir
|
| 76 |
+
|
| 77 |
+
if not os.path.exists(save_dir):
|
| 78 |
+
os.makedirs(save_dir)
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
pretrained_pth = args.pretrained_path
|
| 82 |
+
sdd = torch.load(pretrained_pth, map_location='cpu')
|
| 83 |
+
collect_sd = {}
|
| 84 |
+
for k, v in sdd.items():
|
| 85 |
+
collect_sd[k[7:]] = v
|
| 86 |
+
|
| 87 |
+
models.train_norm.load_state_dict(collect_sd)
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
pretrained_pth_lora = args.pretrained_path_lora
|
| 91 |
+
sdd = torch.load(pretrained_pth_lora, map_location='cpu')
|
| 92 |
+
collect_sd = {}
|
| 93 |
+
for k, v in sdd.items():
|
| 94 |
+
collect_sd[k[7:]] = v
|
| 95 |
+
|
| 96 |
+
models.train_lora.load_state_dict(collect_sd)
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
models.generator.eval()
|
| 100 |
+
models.train_norm.eval()
|
| 101 |
+
|
| 102 |
+
|
| 103 |
+
height_lr, width_lr = get_target_lr_size(height / width, std_size=32)
|
| 104 |
+
stage_c_latent_shape, stage_b_latent_shape = calculate_latent_sizes(height, width, batch_size=batch_size)
|
| 105 |
+
stage_c_latent_shape_lr, stage_b_latent_shape_lr = calculate_latent_sizes(height_lr, width_lr, batch_size=batch_size)
|
| 106 |
+
|
| 107 |
+
# Stage C Parameters
|
| 108 |
+
|
| 109 |
+
extras.sampling_configs['cfg'] = 4
|
| 110 |
+
extras.sampling_configs['shift'] = 1
|
| 111 |
+
extras.sampling_configs['timesteps'] = 20
|
| 112 |
+
extras.sampling_configs['t_start'] = 1.0
|
| 113 |
+
extras.sampling_configs['sampler'] = DDPMSampler(extras.gdf)
|
| 114 |
+
|
| 115 |
+
|
| 116 |
+
|
| 117 |
+
# Stage B Parameters
|
| 118 |
+
|
| 119 |
+
extras_b.sampling_configs['cfg'] = 1.1
|
| 120 |
+
extras_b.sampling_configs['shift'] = 1
|
| 121 |
+
extras_b.sampling_configs['timesteps'] = 10
|
| 122 |
+
extras_b.sampling_configs['t_start'] = 1.0
|
| 123 |
+
|
| 124 |
+
|
| 125 |
+
for cnt, caption in enumerate(captions):
|
| 126 |
+
|
| 127 |
+
batch = {'captions': [caption] * batch_size}
|
| 128 |
+
conditions = core.get_conditions(batch, models, extras, is_eval=True, is_unconditional=False, eval_image_embeds=False)
|
| 129 |
+
unconditions = core.get_conditions(batch, models, extras, is_eval=True, is_unconditional=True, eval_image_embeds=False)
|
| 130 |
+
|
| 131 |
+
conditions_b = core_b.get_conditions(batch, models_b, extras_b, is_eval=True, is_unconditional=False)
|
| 132 |
+
unconditions_b = core_b.get_conditions(batch, models_b, extras_b, is_eval=True, is_unconditional=True)
|
| 133 |
+
|
| 134 |
+
|
| 135 |
+
|
| 136 |
+
|
| 137 |
+
for cnt, caption in enumerate(captions):
|
| 138 |
+
|
| 139 |
+
|
| 140 |
+
batch = {'captions': [caption] * batch_size}
|
| 141 |
+
conditions = core.get_conditions(batch, models, extras, is_eval=True, is_unconditional=False, eval_image_embeds=False)
|
| 142 |
+
unconditions = core.get_conditions(batch, models, extras, is_eval=True, is_unconditional=True, eval_image_embeds=False)
|
| 143 |
+
|
| 144 |
+
conditions_b = core_b.get_conditions(batch, models_b, extras_b, is_eval=True, is_unconditional=False)
|
| 145 |
+
unconditions_b = core_b.get_conditions(batch, models_b, extras_b, is_eval=True, is_unconditional=True)
|
| 146 |
+
|
| 147 |
+
|
| 148 |
+
with torch.no_grad():
|
| 149 |
+
|
| 150 |
+
|
| 151 |
+
models.generator.cuda()
|
| 152 |
+
print('STAGE C GENERATION***************************')
|
| 153 |
+
with torch.cuda.amp.autocast(dtype=dtype):
|
| 154 |
+
sampled_c = generation_c(batch, models, extras, core, stage_c_latent_shape, stage_c_latent_shape_lr, device)
|
| 155 |
+
|
| 156 |
+
|
| 157 |
+
|
| 158 |
+
models.generator.cpu()
|
| 159 |
+
torch.cuda.empty_cache()
|
| 160 |
+
|
| 161 |
+
conditions_b = core_b.get_conditions(batch, models_b, extras_b, is_eval=True, is_unconditional=False)
|
| 162 |
+
unconditions_b = core_b.get_conditions(batch, models_b, extras_b, is_eval=True, is_unconditional=True)
|
| 163 |
+
conditions_b['effnet'] = sampled_c
|
| 164 |
+
unconditions_b['effnet'] = torch.zeros_like(sampled_c)
|
| 165 |
+
print('STAGE B + A DECODING***************************')
|
| 166 |
+
|
| 167 |
+
with torch.cuda.amp.autocast(dtype=dtype):
|
| 168 |
+
sampled = decode_b(conditions_b, unconditions_b, models_b, stage_b_latent_shape, extras_b, device, stage_a_tiled=args.stage_a_tiled)
|
| 169 |
+
|
| 170 |
+
torch.cuda.empty_cache()
|
| 171 |
+
imgs = show_images(sampled)
|
| 172 |
+
for idx, img in enumerate(imgs):
|
| 173 |
+
print(os.path.join(save_dir, args.prompt[:20]+'_' + str(cnt).zfill(5) + '.jpg'), idx)
|
| 174 |
+
img.save(os.path.join(save_dir, args.prompt[:20]+'_' + str(cnt).zfill(5) + '.jpg'))
|
| 175 |
+
|
| 176 |
+
|
| 177 |
+
print('finished! Results at ', save_dir )
|
| 178 |
+
|
| 179 |
+
|
| 180 |
+
|
inference/test_t2i.py
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
import os
|
| 3 |
+
import yaml
|
| 4 |
+
import torch
|
| 5 |
+
from tqdm import tqdm
|
| 6 |
+
import sys
|
| 7 |
+
sys.path.append(os.path.abspath('./'))
|
| 8 |
+
from inference.utils import *
|
| 9 |
+
from core.utils import load_or_fail
|
| 10 |
+
from train import WurstCoreB
|
| 11 |
+
from gdf import VPScaler, CosineTNoiseCond, DDPMSampler, P2LossWeight, AdaptiveLossWeight
|
| 12 |
+
from train import WurstCore_t2i as WurstCoreC
|
| 13 |
+
import torch.nn.functional as F
|
| 14 |
+
from core.utils import load_or_fail
|
| 15 |
+
import numpy as np
|
| 16 |
+
import random
|
| 17 |
+
import math
|
| 18 |
+
import argparse
|
| 19 |
+
from einops import rearrange
|
| 20 |
+
import math
|
| 21 |
+
#inrfft_3b_strc_WurstCore
|
| 22 |
+
def parse_args():
|
| 23 |
+
parser = argparse.ArgumentParser()
|
| 24 |
+
parser.add_argument( '--height', type=int, default=2560, help='image height')
|
| 25 |
+
parser.add_argument('--width', type=int, default=5120, help='image width')
|
| 26 |
+
parser.add_argument('--seed', type=int, default=123, help='random seed')
|
| 27 |
+
parser.add_argument('--dtype', type=str, default='bf16', help=' if bf16 does not work, change it to float32 ')
|
| 28 |
+
parser.add_argument('--config_c', type=str,
|
| 29 |
+
default='configs/training/t2i.yaml' ,help='config file for stage c, latent generation')
|
| 30 |
+
parser.add_argument('--config_b', type=str,
|
| 31 |
+
default='configs/inference/stage_b_1b.yaml' ,help='config file for stage b, latent decoding')
|
| 32 |
+
parser.add_argument( '--prompt', type=str,
|
| 33 |
+
default='A photo-realistic image of a west highland white terrier in the garden, high quality, detail rich, 8K', help='text prompt')
|
| 34 |
+
parser.add_argument( '--num_image', type=int, default=10, help='how many images generated')
|
| 35 |
+
parser.add_argument( '--output_dir', type=str, default='figures/output_results/', help='output directory for generated image')
|
| 36 |
+
parser.add_argument( '--stage_a_tiled', action='store_true', help='whther or nor to use tiled decoding for stage a to save memory')
|
| 37 |
+
parser.add_argument( '--pretrained_path', type=str, default='models/ultrapixel_t2i.safetensors', help='pretrained path of newly added paramter of UltraPixel')
|
| 38 |
+
args = parser.parse_args()
|
| 39 |
+
return args
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
if __name__ == "__main__":
|
| 44 |
+
|
| 45 |
+
args = parse_args()
|
| 46 |
+
print(args)
|
| 47 |
+
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
|
| 48 |
+
print(device)
|
| 49 |
+
torch.manual_seed(args.seed)
|
| 50 |
+
random.seed(args.seed)
|
| 51 |
+
np.random.seed(args.seed)
|
| 52 |
+
dtype = torch.bfloat16 if args.dtype == 'bf16' else torch.float
|
| 53 |
+
#gdf = gdf_refine(
|
| 54 |
+
# schedule=CosineSchedule(clamp_range=[0.0001, 0.9999]),
|
| 55 |
+
# input_scaler=VPScaler(), target=EpsilonTarget(),
|
| 56 |
+
# noise_cond=CosineTNoiseCond(),
|
| 57 |
+
# loss_weight=AdaptiveLossWeight() if self.config.adaptive_loss_weight is True else P2LossWeight(),
|
| 58 |
+
# )
|
| 59 |
+
# SETUP STAGE C
|
| 60 |
+
config_file = args.config_c
|
| 61 |
+
with open(config_file, "r", encoding="utf-8") as file:
|
| 62 |
+
loaded_config = yaml.safe_load(file)
|
| 63 |
+
|
| 64 |
+
core = WurstCoreC(config_dict=loaded_config, device=device, training=False)
|
| 65 |
+
|
| 66 |
+
# SETUP STAGE B
|
| 67 |
+
config_file_b = args.config_b
|
| 68 |
+
with open(config_file_b, "r", encoding="utf-8") as file:
|
| 69 |
+
config_file_b = yaml.safe_load(file)
|
| 70 |
+
|
| 71 |
+
core_b = WurstCoreB(config_dict=config_file_b, device=device, training=False)
|
| 72 |
+
|
| 73 |
+
extras = core.setup_extras_pre()
|
| 74 |
+
models = core.setup_models(extras)
|
| 75 |
+
models.generator.eval().requires_grad_(False)
|
| 76 |
+
print("STAGE C READY")
|
| 77 |
+
|
| 78 |
+
extras_b = core_b.setup_extras_pre()
|
| 79 |
+
models_b = core_b.setup_models(extras_b, skip_clip=True)
|
| 80 |
+
models_b = WurstCoreB.Models(
|
| 81 |
+
**{**models_b.to_dict(), 'tokenizer': models.tokenizer, 'text_model': models.text_model}
|
| 82 |
+
)
|
| 83 |
+
models_b.generator.bfloat16().eval().requires_grad_(False)
|
| 84 |
+
print("STAGE B READY")
|
| 85 |
+
|
| 86 |
+
captions = [args.prompt] * args.num_image
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
height, width = args.height, args.width
|
| 90 |
+
save_dir = args.output_dir
|
| 91 |
+
|
| 92 |
+
if not os.path.exists(save_dir):
|
| 93 |
+
os.makedirs(save_dir)
|
| 94 |
+
|
| 95 |
+
pretrained_path = args.pretrained_path
|
| 96 |
+
sdd = torch.load(pretrained_path, map_location='cpu')
|
| 97 |
+
collect_sd = {}
|
| 98 |
+
for k, v in sdd.items():
|
| 99 |
+
collect_sd[k[7:]] = v
|
| 100 |
+
|
| 101 |
+
models.train_norm.load_state_dict(collect_sd)
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
models.generator.eval()
|
| 105 |
+
models.train_norm.eval()
|
| 106 |
+
|
| 107 |
+
batch_size=1
|
| 108 |
+
height_lr, width_lr = get_target_lr_size(height / width, std_size=32)
|
| 109 |
+
stage_c_latent_shape, stage_b_latent_shape = calculate_latent_sizes(height, width, batch_size=batch_size)
|
| 110 |
+
stage_c_latent_shape_lr, stage_b_latent_shape_lr = calculate_latent_sizes(height_lr, width_lr, batch_size=batch_size)
|
| 111 |
+
|
| 112 |
+
# Stage C Parameters
|
| 113 |
+
extras.sampling_configs['cfg'] = 4
|
| 114 |
+
extras.sampling_configs['shift'] = 1
|
| 115 |
+
extras.sampling_configs['timesteps'] = 20
|
| 116 |
+
extras.sampling_configs['t_start'] = 1.0
|
| 117 |
+
extras.sampling_configs['sampler'] = DDPMSampler(extras.gdf)
|
| 118 |
+
|
| 119 |
+
|
| 120 |
+
|
| 121 |
+
# Stage B Parameters
|
| 122 |
+
extras_b.sampling_configs['cfg'] = 1.1
|
| 123 |
+
extras_b.sampling_configs['shift'] = 1
|
| 124 |
+
extras_b.sampling_configs['timesteps'] = 10
|
| 125 |
+
extras_b.sampling_configs['t_start'] = 1.0
|
| 126 |
+
|
| 127 |
+
|
| 128 |
+
|
| 129 |
+
|
| 130 |
+
for cnt, caption in enumerate(captions):
|
| 131 |
+
|
| 132 |
+
|
| 133 |
+
batch = {'captions': [caption] * batch_size}
|
| 134 |
+
conditions = core.get_conditions(batch, models, extras, is_eval=True, is_unconditional=False, eval_image_embeds=False)
|
| 135 |
+
unconditions = core.get_conditions(batch, models, extras, is_eval=True, is_unconditional=True, eval_image_embeds=False)
|
| 136 |
+
|
| 137 |
+
conditions_b = core_b.get_conditions(batch, models_b, extras_b, is_eval=True, is_unconditional=False)
|
| 138 |
+
unconditions_b = core_b.get_conditions(batch, models_b, extras_b, is_eval=True, is_unconditional=True)
|
| 139 |
+
|
| 140 |
+
|
| 141 |
+
with torch.no_grad():
|
| 142 |
+
|
| 143 |
+
|
| 144 |
+
models.generator.cuda()
|
| 145 |
+
print('STAGE C GENERATION***************************')
|
| 146 |
+
with torch.cuda.amp.autocast(dtype=dtype):
|
| 147 |
+
sampled_c = generation_c(batch, models, extras, core, stage_c_latent_shape, stage_c_latent_shape_lr, device)
|
| 148 |
+
|
| 149 |
+
|
| 150 |
+
|
| 151 |
+
models.generator.cpu()
|
| 152 |
+
torch.cuda.empty_cache()
|
| 153 |
+
|
| 154 |
+
conditions_b = core_b.get_conditions(batch, models_b, extras_b, is_eval=True, is_unconditional=False)
|
| 155 |
+
unconditions_b = core_b.get_conditions(batch, models_b, extras_b, is_eval=True, is_unconditional=True)
|
| 156 |
+
conditions_b['effnet'] = sampled_c
|
| 157 |
+
unconditions_b['effnet'] = torch.zeros_like(sampled_c)
|
| 158 |
+
print('STAGE B + A DECODING***************************')
|
| 159 |
+
|
| 160 |
+
with torch.cuda.amp.autocast(dtype=dtype):
|
| 161 |
+
sampled = decode_b(conditions_b, unconditions_b, models_b, stage_b_latent_shape, extras_b, device, stage_a_tiled=args.stage_a_tiled)
|
| 162 |
+
|
| 163 |
+
torch.cuda.empty_cache()
|
| 164 |
+
imgs = show_images(sampled)
|
| 165 |
+
for idx, img in enumerate(imgs):
|
| 166 |
+
print(os.path.join(save_dir, args.prompt[:20]+'_' + str(cnt).zfill(5) + '.jpg'), idx)
|
| 167 |
+
img.save(os.path.join(save_dir, args.prompt[:20]+'_' + str(cnt).zfill(5) + '.jpg'))
|
| 168 |
+
|
| 169 |
+
|
| 170 |
+
print('finished! Results at ', save_dir )
|
inference/utils.py
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import PIL
|
| 2 |
+
import torch
|
| 3 |
+
import requests
|
| 4 |
+
import torchvision
|
| 5 |
+
from math import ceil
|
| 6 |
+
from io import BytesIO
|
| 7 |
+
import matplotlib.pyplot as plt
|
| 8 |
+
import torchvision.transforms.functional as F
|
| 9 |
+
import math
|
| 10 |
+
from tqdm import tqdm
|
| 11 |
+
def download_image(url):
|
| 12 |
+
return PIL.Image.open(requests.get(url, stream=True).raw).convert("RGB")
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
def resize_image(image, size=768):
|
| 16 |
+
tensor_image = F.to_tensor(image)
|
| 17 |
+
resized_image = F.resize(tensor_image, size, antialias=True)
|
| 18 |
+
return resized_image
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def downscale_images(images, factor=3/4):
|
| 22 |
+
scaled_height, scaled_width = int(((images.size(-2)*factor)//32)*32), int(((images.size(-1)*factor)//32)*32)
|
| 23 |
+
scaled_image = torchvision.transforms.functional.resize(images, (scaled_height, scaled_width), interpolation=torchvision.transforms.InterpolationMode.NEAREST)
|
| 24 |
+
return scaled_image
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
def calculate_latent_sizes(height=1024, width=1024, batch_size=4, compression_factor_b=42.67, compression_factor_a=4.0):
|
| 29 |
+
resolution_multiple = 42.67
|
| 30 |
+
latent_height = ceil(height / compression_factor_b)
|
| 31 |
+
latent_width = ceil(width / compression_factor_b)
|
| 32 |
+
stage_c_latent_shape = (batch_size, 16, latent_height, latent_width)
|
| 33 |
+
|
| 34 |
+
latent_height = ceil(height / compression_factor_a)
|
| 35 |
+
latent_width = ceil(width / compression_factor_a)
|
| 36 |
+
stage_b_latent_shape = (batch_size, 4, latent_height, latent_width)
|
| 37 |
+
|
| 38 |
+
return stage_c_latent_shape, stage_b_latent_shape
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
def get_views(H, W, window_size=64, stride=16):
|
| 42 |
+
'''
|
| 43 |
+
- H, W: height and width of the latent
|
| 44 |
+
'''
|
| 45 |
+
num_blocks_height = (H - window_size) // stride + 1
|
| 46 |
+
num_blocks_width = (W - window_size) // stride + 1
|
| 47 |
+
total_num_blocks = int(num_blocks_height * num_blocks_width)
|
| 48 |
+
views = []
|
| 49 |
+
for i in range(total_num_blocks):
|
| 50 |
+
h_start = int((i // num_blocks_width) * stride)
|
| 51 |
+
h_end = h_start + window_size
|
| 52 |
+
w_start = int((i % num_blocks_width) * stride)
|
| 53 |
+
w_end = w_start + window_size
|
| 54 |
+
views.append((h_start, h_end, w_start, w_end))
|
| 55 |
+
return views
|
| 56 |
+
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
def show_images(images, rows=None, cols=None, **kwargs):
|
| 60 |
+
if images.size(1) == 1:
|
| 61 |
+
images = images.repeat(1, 3, 1, 1)
|
| 62 |
+
elif images.size(1) > 3:
|
| 63 |
+
images = images[:, :3]
|
| 64 |
+
|
| 65 |
+
if rows is None:
|
| 66 |
+
rows = 1
|
| 67 |
+
if cols is None:
|
| 68 |
+
cols = images.size(0) // rows
|
| 69 |
+
|
| 70 |
+
_, _, h, w = images.shape
|
| 71 |
+
|
| 72 |
+
imgs = []
|
| 73 |
+
for i, img in enumerate(images):
|
| 74 |
+
imgs.append( torchvision.transforms.functional.to_pil_image(img.clamp(0, 1)))
|
| 75 |
+
|
| 76 |
+
return imgs
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
def decode_b(conditions_b, unconditions_b, models_b, bshape, extras_b, device, \
|
| 81 |
+
stage_a_tiled=False, num_instance=4, patch_size=256, stride=24):
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
sampling_b = extras_b.gdf.sample(
|
| 85 |
+
models_b.generator.half(), conditions_b, bshape,
|
| 86 |
+
unconditions_b, device=device,
|
| 87 |
+
**extras_b.sampling_configs,
|
| 88 |
+
)
|
| 89 |
+
models_b.generator.cuda()
|
| 90 |
+
for (sampled_b, _, _) in tqdm(sampling_b, total=extras_b.sampling_configs['timesteps']):
|
| 91 |
+
sampled_b = sampled_b
|
| 92 |
+
models_b.generator.cpu()
|
| 93 |
+
torch.cuda.empty_cache()
|
| 94 |
+
if stage_a_tiled:
|
| 95 |
+
with torch.cuda.amp.autocast(dtype=torch.float16):
|
| 96 |
+
padding = (stride*2, stride*2, stride*2, stride*2)
|
| 97 |
+
sampled_b = torch.nn.functional.pad(sampled_b, padding, mode='reflect')
|
| 98 |
+
count = torch.zeros((sampled_b.shape[0], 3, sampled_b.shape[-2]*4, sampled_b.shape[-1]*4), requires_grad=False, device=sampled_b.device)
|
| 99 |
+
sampled = torch.zeros((sampled_b.shape[0], 3, sampled_b.shape[-2]*4, sampled_b.shape[-1]*4), requires_grad=False, device=sampled_b.device)
|
| 100 |
+
views = get_views(sampled_b.shape[-2], sampled_b.shape[-1], window_size=patch_size, stride=stride)
|
| 101 |
+
|
| 102 |
+
for view_idx, (h_start, h_end, w_start, w_end) in enumerate(tqdm(views, total=len(views))):
|
| 103 |
+
|
| 104 |
+
sampled[:, :, h_start*4:h_end*4, w_start*4:w_end*4] += models_b.stage_a.decode(sampled_b[:, :, h_start:h_end, w_start:w_end]).float()
|
| 105 |
+
count[:, :, h_start*4:h_end*4, w_start*4:w_end*4] += 1
|
| 106 |
+
sampled /= count
|
| 107 |
+
sampled = sampled[:, :, stride*4*2:-stride*4*2, stride*4*2:-stride*4*2]
|
| 108 |
+
else:
|
| 109 |
+
|
| 110 |
+
sampled = models_b.stage_a.decode(sampled_b, tiled_decoding=stage_a_tiled)
|
| 111 |
+
|
| 112 |
+
return sampled.float()
|
| 113 |
+
|
| 114 |
+
|
| 115 |
+
def generation_c(batch, models, extras, core, stage_c_latent_shape, stage_c_latent_shape_lr, device, conditions=None, unconditions=None):
|
| 116 |
+
if conditions is None:
|
| 117 |
+
conditions = core.get_conditions(batch, models, extras, is_eval=True, is_unconditional=False, eval_image_embeds=False)
|
| 118 |
+
if unconditions is None:
|
| 119 |
+
unconditions = core.get_conditions(batch, models, extras, is_eval=True, is_unconditional=True, eval_image_embeds=False)
|
| 120 |
+
sampling_c = extras.gdf.sample(
|
| 121 |
+
models.generator, conditions, stage_c_latent_shape, stage_c_latent_shape_lr,
|
| 122 |
+
unconditions, device=device, **extras.sampling_configs,
|
| 123 |
+
)
|
| 124 |
+
for idx, (sampled_c, sampled_c_curr, _, _) in enumerate(tqdm(sampling_c, total=extras.sampling_configs['timesteps'])):
|
| 125 |
+
sampled_c = sampled_c
|
| 126 |
+
return sampled_c
|
| 127 |
+
|
| 128 |
+
def get_target_lr_size(ratio, std_size=24):
|
| 129 |
+
w, h = int(std_size / math.sqrt(ratio)), int(std_size * math.sqrt(ratio))
|
| 130 |
+
return (h * 32 , w *32 )
|
| 131 |
+
|
modules/__init__.py
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .effnet import EfficientNetEncoder
|
| 2 |
+
from .stage_c import StageC
|
| 3 |
+
from .stage_c import ResBlock, AttnBlock, TimestepBlock, FeedForwardBlock
|
| 4 |
+
from .previewer import Previewer
|
| 5 |
+
from .controlnet import ControlNet, ControlNetDeliverer
|
| 6 |
+
from . import controlnet as controlnet_filters
|
modules/cnet_modules/face_id/arcface.py
ADDED
|
@@ -0,0 +1,276 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import numpy as np
|
| 2 |
+
import onnx, onnx2torch, cv2
|
| 3 |
+
import torch
|
| 4 |
+
from insightface.utils import face_align
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
class ArcFaceRecognizer:
|
| 8 |
+
def __init__(self, model_file=None, device='cpu', dtype=torch.float32):
|
| 9 |
+
assert model_file is not None
|
| 10 |
+
self.model_file = model_file
|
| 11 |
+
|
| 12 |
+
self.device = device
|
| 13 |
+
self.dtype = dtype
|
| 14 |
+
self.model = onnx2torch.convert(onnx.load(model_file)).to(device=device, dtype=dtype)
|
| 15 |
+
for param in self.model.parameters():
|
| 16 |
+
param.requires_grad = False
|
| 17 |
+
self.model.eval()
|
| 18 |
+
|
| 19 |
+
self.input_mean = 127.5
|
| 20 |
+
self.input_std = 127.5
|
| 21 |
+
self.input_size = (112, 112)
|
| 22 |
+
self.input_shape = ['None', 3, 112, 112]
|
| 23 |
+
|
| 24 |
+
def get(self, img, face):
|
| 25 |
+
aimg = face_align.norm_crop(img, landmark=face.kps, image_size=self.input_size[0])
|
| 26 |
+
face.embedding = self.get_feat(aimg).flatten()
|
| 27 |
+
return face.embedding
|
| 28 |
+
|
| 29 |
+
def compute_sim(self, feat1, feat2):
|
| 30 |
+
from numpy.linalg import norm
|
| 31 |
+
feat1 = feat1.ravel()
|
| 32 |
+
feat2 = feat2.ravel()
|
| 33 |
+
sim = np.dot(feat1, feat2) / (norm(feat1) * norm(feat2))
|
| 34 |
+
return sim
|
| 35 |
+
|
| 36 |
+
def get_feat(self, imgs):
|
| 37 |
+
if not isinstance(imgs, list):
|
| 38 |
+
imgs = [imgs]
|
| 39 |
+
input_size = self.input_size
|
| 40 |
+
|
| 41 |
+
blob = cv2.dnn.blobFromImages(imgs, 1.0 / self.input_std, input_size,
|
| 42 |
+
(self.input_mean, self.input_mean, self.input_mean), swapRB=True)
|
| 43 |
+
|
| 44 |
+
blob_torch = torch.tensor(blob).to(device=self.device, dtype=self.dtype)
|
| 45 |
+
net_out = self.model(blob_torch)
|
| 46 |
+
return net_out[0].float().cpu()
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
def distance2bbox(points, distance, max_shape=None):
|
| 50 |
+
"""Decode distance prediction to bounding box.
|
| 51 |
+
|
| 52 |
+
Args:
|
| 53 |
+
points (Tensor): Shape (n, 2), [x, y].
|
| 54 |
+
distance (Tensor): Distance from the given point to 4
|
| 55 |
+
boundaries (left, top, right, bottom).
|
| 56 |
+
max_shape (tuple): Shape of the image.
|
| 57 |
+
|
| 58 |
+
Returns:
|
| 59 |
+
Tensor: Decoded bboxes.
|
| 60 |
+
"""
|
| 61 |
+
x1 = points[:, 0] - distance[:, 0]
|
| 62 |
+
y1 = points[:, 1] - distance[:, 1]
|
| 63 |
+
x2 = points[:, 0] + distance[:, 2]
|
| 64 |
+
y2 = points[:, 1] + distance[:, 3]
|
| 65 |
+
if max_shape is not None:
|
| 66 |
+
x1 = x1.clamp(min=0, max=max_shape[1])
|
| 67 |
+
y1 = y1.clamp(min=0, max=max_shape[0])
|
| 68 |
+
x2 = x2.clamp(min=0, max=max_shape[1])
|
| 69 |
+
y2 = y2.clamp(min=0, max=max_shape[0])
|
| 70 |
+
return np.stack([x1, y1, x2, y2], axis=-1)
|
| 71 |
+
|
| 72 |
+
|
| 73 |
+
def distance2kps(points, distance, max_shape=None):
|
| 74 |
+
"""Decode distance prediction to bounding box.
|
| 75 |
+
|
| 76 |
+
Args:
|
| 77 |
+
points (Tensor): Shape (n, 2), [x, y].
|
| 78 |
+
distance (Tensor): Distance from the given point to 4
|
| 79 |
+
boundaries (left, top, right, bottom).
|
| 80 |
+
max_shape (tuple): Shape of the image.
|
| 81 |
+
|
| 82 |
+
Returns:
|
| 83 |
+
Tensor: Decoded bboxes.
|
| 84 |
+
"""
|
| 85 |
+
preds = []
|
| 86 |
+
for i in range(0, distance.shape[1], 2):
|
| 87 |
+
px = points[:, i % 2] + distance[:, i]
|
| 88 |
+
py = points[:, i % 2 + 1] + distance[:, i + 1]
|
| 89 |
+
if max_shape is not None:
|
| 90 |
+
px = px.clamp(min=0, max=max_shape[1])
|
| 91 |
+
py = py.clamp(min=0, max=max_shape[0])
|
| 92 |
+
preds.append(px)
|
| 93 |
+
preds.append(py)
|
| 94 |
+
return np.stack(preds, axis=-1)
|
| 95 |
+
|
| 96 |
+
|
| 97 |
+
class FaceDetector:
|
| 98 |
+
def __init__(self, model_file=None, dtype=torch.float32, device='cuda'):
|
| 99 |
+
self.model_file = model_file
|
| 100 |
+
self.taskname = 'detection'
|
| 101 |
+
self.center_cache = {}
|
| 102 |
+
self.nms_thresh = 0.4
|
| 103 |
+
self.det_thresh = 0.5
|
| 104 |
+
|
| 105 |
+
self.device = device
|
| 106 |
+
self.dtype = dtype
|
| 107 |
+
self.model = onnx2torch.convert(onnx.load(model_file)).to(device=device, dtype=dtype)
|
| 108 |
+
for param in self.model.parameters():
|
| 109 |
+
param.requires_grad = False
|
| 110 |
+
self.model.eval()
|
| 111 |
+
|
| 112 |
+
input_shape = (320, 320)
|
| 113 |
+
self.input_size = input_shape
|
| 114 |
+
self.input_shape = input_shape
|
| 115 |
+
|
| 116 |
+
self.input_mean = 127.5
|
| 117 |
+
self.input_std = 128.0
|
| 118 |
+
self._anchor_ratio = 1.0
|
| 119 |
+
self._num_anchors = 1
|
| 120 |
+
self.fmc = 3
|
| 121 |
+
self._feat_stride_fpn = [8, 16, 32]
|
| 122 |
+
self._num_anchors = 2
|
| 123 |
+
self.use_kps = True
|
| 124 |
+
|
| 125 |
+
self.det_thresh = 0.5
|
| 126 |
+
self.nms_thresh = 0.4
|
| 127 |
+
|
| 128 |
+
def forward(self, img, threshold):
|
| 129 |
+
scores_list = []
|
| 130 |
+
bboxes_list = []
|
| 131 |
+
kpss_list = []
|
| 132 |
+
input_size = tuple(img.shape[0:2][::-1])
|
| 133 |
+
blob = cv2.dnn.blobFromImage(img, 1.0 / self.input_std, input_size,
|
| 134 |
+
(self.input_mean, self.input_mean, self.input_mean), swapRB=True)
|
| 135 |
+
blob_torch = torch.tensor(blob).to(device=self.device, dtype=self.dtype)
|
| 136 |
+
net_outs_torch = self.model(blob_torch)
|
| 137 |
+
# print(list(map(lambda x: x.shape, net_outs_torch)))
|
| 138 |
+
net_outs = list(map(lambda x: x.float().cpu().numpy(), net_outs_torch))
|
| 139 |
+
|
| 140 |
+
input_height = blob.shape[2]
|
| 141 |
+
input_width = blob.shape[3]
|
| 142 |
+
fmc = self.fmc
|
| 143 |
+
for idx, stride in enumerate(self._feat_stride_fpn):
|
| 144 |
+
scores = net_outs[idx]
|
| 145 |
+
bbox_preds = net_outs[idx + fmc]
|
| 146 |
+
bbox_preds = bbox_preds * stride
|
| 147 |
+
if self.use_kps:
|
| 148 |
+
kps_preds = net_outs[idx + fmc * 2] * stride
|
| 149 |
+
height = input_height // stride
|
| 150 |
+
width = input_width // stride
|
| 151 |
+
K = height * width
|
| 152 |
+
key = (height, width, stride)
|
| 153 |
+
if key in self.center_cache:
|
| 154 |
+
anchor_centers = self.center_cache[key]
|
| 155 |
+
else:
|
| 156 |
+
# solution-1, c style:
|
| 157 |
+
# anchor_centers = np.zeros( (height, width, 2), dtype=np.float32 )
|
| 158 |
+
# for i in range(height):
|
| 159 |
+
# anchor_centers[i, :, 1] = i
|
| 160 |
+
# for i in range(width):
|
| 161 |
+
# anchor_centers[:, i, 0] = i
|
| 162 |
+
|
| 163 |
+
# solution-2:
|
| 164 |
+
# ax = np.arange(width, dtype=np.float32)
|
| 165 |
+
# ay = np.arange(height, dtype=np.float32)
|
| 166 |
+
# xv, yv = np.meshgrid(np.arange(width), np.arange(height))
|
| 167 |
+
# anchor_centers = np.stack([xv, yv], axis=-1).astype(np.float32)
|
| 168 |
+
|
| 169 |
+
# solution-3:
|
| 170 |
+
anchor_centers = np.stack(np.mgrid[:height, :width][::-1], axis=-1).astype(np.float32)
|
| 171 |
+
# print(anchor_centers.shape)
|
| 172 |
+
|
| 173 |
+
anchor_centers = (anchor_centers * stride).reshape((-1, 2))
|
| 174 |
+
if self._num_anchors > 1:
|
| 175 |
+
anchor_centers = np.stack([anchor_centers] * self._num_anchors, axis=1).reshape((-1, 2))
|
| 176 |
+
if len(self.center_cache) < 100:
|
| 177 |
+
self.center_cache[key] = anchor_centers
|
| 178 |
+
|
| 179 |
+
pos_inds = np.where(scores >= threshold)[0]
|
| 180 |
+
bboxes = distance2bbox(anchor_centers, bbox_preds)
|
| 181 |
+
pos_scores = scores[pos_inds]
|
| 182 |
+
pos_bboxes = bboxes[pos_inds]
|
| 183 |
+
scores_list.append(pos_scores)
|
| 184 |
+
bboxes_list.append(pos_bboxes)
|
| 185 |
+
if self.use_kps:
|
| 186 |
+
kpss = distance2kps(anchor_centers, kps_preds)
|
| 187 |
+
# kpss = kps_preds
|
| 188 |
+
kpss = kpss.reshape((kpss.shape[0], -1, 2))
|
| 189 |
+
pos_kpss = kpss[pos_inds]
|
| 190 |
+
kpss_list.append(pos_kpss)
|
| 191 |
+
return scores_list, bboxes_list, kpss_list
|
| 192 |
+
|
| 193 |
+
def detect(self, img, input_size=None, max_num=0, metric='default'):
|
| 194 |
+
assert input_size is not None or self.input_size is not None
|
| 195 |
+
input_size = self.input_size if input_size is None else input_size
|
| 196 |
+
|
| 197 |
+
im_ratio = float(img.shape[0]) / img.shape[1]
|
| 198 |
+
model_ratio = float(input_size[1]) / input_size[0]
|
| 199 |
+
if im_ratio > model_ratio:
|
| 200 |
+
new_height = input_size[1]
|
| 201 |
+
new_width = int(new_height / im_ratio)
|
| 202 |
+
else:
|
| 203 |
+
new_width = input_size[0]
|
| 204 |
+
new_height = int(new_width * im_ratio)
|
| 205 |
+
det_scale = float(new_height) / img.shape[0]
|
| 206 |
+
resized_img = cv2.resize(img, (new_width, new_height))
|
| 207 |
+
det_img = np.zeros((input_size[1], input_size[0], 3), dtype=np.uint8)
|
| 208 |
+
det_img[:new_height, :new_width, :] = resized_img
|
| 209 |
+
|
| 210 |
+
scores_list, bboxes_list, kpss_list = self.forward(det_img, self.det_thresh)
|
| 211 |
+
|
| 212 |
+
scores = np.vstack(scores_list)
|
| 213 |
+
scores_ravel = scores.ravel()
|
| 214 |
+
order = scores_ravel.argsort()[::-1]
|
| 215 |
+
bboxes = np.vstack(bboxes_list) / det_scale
|
| 216 |
+
if self.use_kps:
|
| 217 |
+
kpss = np.vstack(kpss_list) / det_scale
|
| 218 |
+
pre_det = np.hstack((bboxes, scores)).astype(np.float32, copy=False)
|
| 219 |
+
pre_det = pre_det[order, :]
|
| 220 |
+
keep = self.nms(pre_det)
|
| 221 |
+
det = pre_det[keep, :]
|
| 222 |
+
if self.use_kps:
|
| 223 |
+
kpss = kpss[order, :, :]
|
| 224 |
+
kpss = kpss[keep, :, :]
|
| 225 |
+
else:
|
| 226 |
+
kpss = None
|
| 227 |
+
if max_num > 0 and det.shape[0] > max_num:
|
| 228 |
+
area = (det[:, 2] - det[:, 0]) * (det[:, 3] -
|
| 229 |
+
det[:, 1])
|
| 230 |
+
img_center = img.shape[0] // 2, img.shape[1] // 2
|
| 231 |
+
offsets = np.vstack([
|
| 232 |
+
(det[:, 0] + det[:, 2]) / 2 - img_center[1],
|
| 233 |
+
(det[:, 1] + det[:, 3]) / 2 - img_center[0]
|
| 234 |
+
])
|
| 235 |
+
offset_dist_squared = np.sum(np.power(offsets, 2.0), 0)
|
| 236 |
+
if metric == 'max':
|
| 237 |
+
values = area
|
| 238 |
+
else:
|
| 239 |
+
values = area - offset_dist_squared * 2.0 # some extra weight on the centering
|
| 240 |
+
bindex = np.argsort(
|
| 241 |
+
values)[::-1] # some extra weight on the centering
|
| 242 |
+
bindex = bindex[0:max_num]
|
| 243 |
+
det = det[bindex, :]
|
| 244 |
+
if kpss is not None:
|
| 245 |
+
kpss = kpss[bindex, :]
|
| 246 |
+
return det, kpss
|
| 247 |
+
|
| 248 |
+
def nms(self, dets):
|
| 249 |
+
thresh = self.nms_thresh
|
| 250 |
+
x1 = dets[:, 0]
|
| 251 |
+
y1 = dets[:, 1]
|
| 252 |
+
x2 = dets[:, 2]
|
| 253 |
+
y2 = dets[:, 3]
|
| 254 |
+
scores = dets[:, 4]
|
| 255 |
+
|
| 256 |
+
areas = (x2 - x1 + 1) * (y2 - y1 + 1)
|
| 257 |
+
order = scores.argsort()[::-1]
|
| 258 |
+
|
| 259 |
+
keep = []
|
| 260 |
+
while order.size > 0:
|
| 261 |
+
i = order[0]
|
| 262 |
+
keep.append(i)
|
| 263 |
+
xx1 = np.maximum(x1[i], x1[order[1:]])
|
| 264 |
+
yy1 = np.maximum(y1[i], y1[order[1:]])
|
| 265 |
+
xx2 = np.minimum(x2[i], x2[order[1:]])
|
| 266 |
+
yy2 = np.minimum(y2[i], y2[order[1:]])
|
| 267 |
+
|
| 268 |
+
w = np.maximum(0.0, xx2 - xx1 + 1)
|
| 269 |
+
h = np.maximum(0.0, yy2 - yy1 + 1)
|
| 270 |
+
inter = w * h
|
| 271 |
+
ovr = inter / (areas[i] + areas[order[1:]] - inter)
|
| 272 |
+
|
| 273 |
+
inds = np.where(ovr <= thresh)[0]
|
| 274 |
+
order = order[inds + 1]
|
| 275 |
+
|
| 276 |
+
return keep
|
modules/cnet_modules/inpainting/saliency_model.py
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
import torchvision
|
| 3 |
+
from torch import nn
|
| 4 |
+
from PIL import Image
|
| 5 |
+
import numpy as np
|
| 6 |
+
import os
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
# MICRO RESNET
|
| 10 |
+
class ResBlock(nn.Module):
|
| 11 |
+
def __init__(self, channels):
|
| 12 |
+
super(ResBlock, self).__init__()
|
| 13 |
+
|
| 14 |
+
self.resblock = nn.Sequential(
|
| 15 |
+
nn.ReflectionPad2d(1),
|
| 16 |
+
nn.Conv2d(channels, channels, kernel_size=3),
|
| 17 |
+
nn.InstanceNorm2d(channels, affine=True),
|
| 18 |
+
nn.ReLU(),
|
| 19 |
+
nn.ReflectionPad2d(1),
|
| 20 |
+
nn.Conv2d(channels, channels, kernel_size=3),
|
| 21 |
+
nn.InstanceNorm2d(channels, affine=True),
|
| 22 |
+
)
|
| 23 |
+
|
| 24 |
+
def forward(self, x):
|
| 25 |
+
out = self.resblock(x)
|
| 26 |
+
return out + x
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
class Upsample2d(nn.Module):
|
| 30 |
+
def __init__(self, scale_factor):
|
| 31 |
+
super(Upsample2d, self).__init__()
|
| 32 |
+
|
| 33 |
+
self.interp = nn.functional.interpolate
|
| 34 |
+
self.scale_factor = scale_factor
|
| 35 |
+
|
| 36 |
+
def forward(self, x):
|
| 37 |
+
x = self.interp(x, scale_factor=self.scale_factor, mode='nearest')
|
| 38 |
+
return x
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
class MicroResNet(nn.Module):
|
| 42 |
+
def __init__(self):
|
| 43 |
+
super(MicroResNet, self).__init__()
|
| 44 |
+
|
| 45 |
+
self.downsampler = nn.Sequential(
|
| 46 |
+
nn.ReflectionPad2d(4),
|
| 47 |
+
nn.Conv2d(3, 8, kernel_size=9, stride=4),
|
| 48 |
+
nn.InstanceNorm2d(8, affine=True),
|
| 49 |
+
nn.ReLU(),
|
| 50 |
+
nn.ReflectionPad2d(1),
|
| 51 |
+
nn.Conv2d(8, 16, kernel_size=3, stride=2),
|
| 52 |
+
nn.InstanceNorm2d(16, affine=True),
|
| 53 |
+
nn.ReLU(),
|
| 54 |
+
nn.ReflectionPad2d(1),
|
| 55 |
+
nn.Conv2d(16, 32, kernel_size=3, stride=2),
|
| 56 |
+
nn.InstanceNorm2d(32, affine=True),
|
| 57 |
+
nn.ReLU(),
|
| 58 |
+
)
|
| 59 |
+
|
| 60 |
+
self.residual = nn.Sequential(
|
| 61 |
+
ResBlock(32),
|
| 62 |
+
nn.Conv2d(32, 64, kernel_size=1, bias=False, groups=32),
|
| 63 |
+
ResBlock(64),
|
| 64 |
+
)
|
| 65 |
+
|
| 66 |
+
self.segmentator = nn.Sequential(
|
| 67 |
+
nn.ReflectionPad2d(1),
|
| 68 |
+
nn.Conv2d(64, 16, kernel_size=3),
|
| 69 |
+
nn.InstanceNorm2d(16, affine=True),
|
| 70 |
+
nn.ReLU(),
|
| 71 |
+
Upsample2d(scale_factor=2),
|
| 72 |
+
nn.ReflectionPad2d(4),
|
| 73 |
+
nn.Conv2d(16, 1, kernel_size=9),
|
| 74 |
+
nn.Sigmoid()
|
| 75 |
+
)
|
| 76 |
+
|
| 77 |
+
def forward(self, x):
|
| 78 |
+
out = self.downsampler(x)
|
| 79 |
+
out = self.residual(out)
|
| 80 |
+
out = self.segmentator(out)
|
| 81 |
+
return out
|
modules/cnet_modules/pidinet/__init__.py
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Pidinet
|
| 2 |
+
# https://github.com/hellozhuo/pidinet
|
| 3 |
+
|
| 4 |
+
import os
|
| 5 |
+
import torch
|
| 6 |
+
import numpy as np
|
| 7 |
+
from einops import rearrange
|
| 8 |
+
from .model import pidinet
|
| 9 |
+
from .util import annotator_ckpts_path, safe_step
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
class PidiNetDetector:
|
| 13 |
+
def __init__(self, device):
|
| 14 |
+
remote_model_path = "https://huggingface.co/lllyasviel/Annotators/resolve/main/table5_pidinet.pth"
|
| 15 |
+
modelpath = os.path.join(annotator_ckpts_path, "table5_pidinet.pth")
|
| 16 |
+
if not os.path.exists(modelpath):
|
| 17 |
+
from basicsr.utils.download_util import load_file_from_url
|
| 18 |
+
load_file_from_url(remote_model_path, model_dir=annotator_ckpts_path)
|
| 19 |
+
self.netNetwork = pidinet()
|
| 20 |
+
self.netNetwork.load_state_dict(
|
| 21 |
+
{k.replace('module.', ''): v for k, v in torch.load(modelpath)['state_dict'].items()})
|
| 22 |
+
self.netNetwork.to(device).eval().requires_grad_(False)
|
| 23 |
+
|
| 24 |
+
def __call__(self, input_image): # , safe=False):
|
| 25 |
+
return self.netNetwork(input_image)[-1]
|
| 26 |
+
# assert input_image.ndim == 3
|
| 27 |
+
# input_image = input_image[:, :, ::-1].copy()
|
| 28 |
+
# with torch.no_grad():
|
| 29 |
+
# image_pidi = torch.from_numpy(input_image).float().cuda()
|
| 30 |
+
# image_pidi = image_pidi / 255.0
|
| 31 |
+
# image_pidi = rearrange(image_pidi, 'h w c -> 1 c h w')
|
| 32 |
+
# edge = self.netNetwork(image_pidi)[-1]
|
| 33 |
+
|
| 34 |
+
# if safe:
|
| 35 |
+
# edge = safe_step(edge)
|
| 36 |
+
# edge = (edge * 255.0).clip(0, 255).astype(np.uint8)
|
| 37 |
+
# return edge[0][0]
|
modules/cnet_modules/pidinet/model.py
ADDED
|
@@ -0,0 +1,654 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Author: Zhuo Su, Wenzhe Liu
|
| 3 |
+
Date: Feb 18, 2021
|
| 4 |
+
"""
|
| 5 |
+
|
| 6 |
+
import math
|
| 7 |
+
|
| 8 |
+
import cv2
|
| 9 |
+
import numpy as np
|
| 10 |
+
import torch
|
| 11 |
+
import torch.nn as nn
|
| 12 |
+
import torch.nn.functional as F
|
| 13 |
+
|
| 14 |
+
nets = {
|
| 15 |
+
'baseline': {
|
| 16 |
+
'layer0': 'cv',
|
| 17 |
+
'layer1': 'cv',
|
| 18 |
+
'layer2': 'cv',
|
| 19 |
+
'layer3': 'cv',
|
| 20 |
+
'layer4': 'cv',
|
| 21 |
+
'layer5': 'cv',
|
| 22 |
+
'layer6': 'cv',
|
| 23 |
+
'layer7': 'cv',
|
| 24 |
+
'layer8': 'cv',
|
| 25 |
+
'layer9': 'cv',
|
| 26 |
+
'layer10': 'cv',
|
| 27 |
+
'layer11': 'cv',
|
| 28 |
+
'layer12': 'cv',
|
| 29 |
+
'layer13': 'cv',
|
| 30 |
+
'layer14': 'cv',
|
| 31 |
+
'layer15': 'cv',
|
| 32 |
+
},
|
| 33 |
+
'c-v15': {
|
| 34 |
+
'layer0': 'cd',
|
| 35 |
+
'layer1': 'cv',
|
| 36 |
+
'layer2': 'cv',
|
| 37 |
+
'layer3': 'cv',
|
| 38 |
+
'layer4': 'cv',
|
| 39 |
+
'layer5': 'cv',
|
| 40 |
+
'layer6': 'cv',
|
| 41 |
+
'layer7': 'cv',
|
| 42 |
+
'layer8': 'cv',
|
| 43 |
+
'layer9': 'cv',
|
| 44 |
+
'layer10': 'cv',
|
| 45 |
+
'layer11': 'cv',
|
| 46 |
+
'layer12': 'cv',
|
| 47 |
+
'layer13': 'cv',
|
| 48 |
+
'layer14': 'cv',
|
| 49 |
+
'layer15': 'cv',
|
| 50 |
+
},
|
| 51 |
+
'a-v15': {
|
| 52 |
+
'layer0': 'ad',
|
| 53 |
+
'layer1': 'cv',
|
| 54 |
+
'layer2': 'cv',
|
| 55 |
+
'layer3': 'cv',
|
| 56 |
+
'layer4': 'cv',
|
| 57 |
+
'layer5': 'cv',
|
| 58 |
+
'layer6': 'cv',
|
| 59 |
+
'layer7': 'cv',
|
| 60 |
+
'layer8': 'cv',
|
| 61 |
+
'layer9': 'cv',
|
| 62 |
+
'layer10': 'cv',
|
| 63 |
+
'layer11': 'cv',
|
| 64 |
+
'layer12': 'cv',
|
| 65 |
+
'layer13': 'cv',
|
| 66 |
+
'layer14': 'cv',
|
| 67 |
+
'layer15': 'cv',
|
| 68 |
+
},
|
| 69 |
+
'r-v15': {
|
| 70 |
+
'layer0': 'rd',
|
| 71 |
+
'layer1': 'cv',
|
| 72 |
+
'layer2': 'cv',
|
| 73 |
+
'layer3': 'cv',
|
| 74 |
+
'layer4': 'cv',
|
| 75 |
+
'layer5': 'cv',
|
| 76 |
+
'layer6': 'cv',
|
| 77 |
+
'layer7': 'cv',
|
| 78 |
+
'layer8': 'cv',
|
| 79 |
+
'layer9': 'cv',
|
| 80 |
+
'layer10': 'cv',
|
| 81 |
+
'layer11': 'cv',
|
| 82 |
+
'layer12': 'cv',
|
| 83 |
+
'layer13': 'cv',
|
| 84 |
+
'layer14': 'cv',
|
| 85 |
+
'layer15': 'cv',
|
| 86 |
+
},
|
| 87 |
+
'cvvv4': {
|
| 88 |
+
'layer0': 'cd',
|
| 89 |
+
'layer1': 'cv',
|
| 90 |
+
'layer2': 'cv',
|
| 91 |
+
'layer3': 'cv',
|
| 92 |
+
'layer4': 'cd',
|
| 93 |
+
'layer5': 'cv',
|
| 94 |
+
'layer6': 'cv',
|
| 95 |
+
'layer7': 'cv',
|
| 96 |
+
'layer8': 'cd',
|
| 97 |
+
'layer9': 'cv',
|
| 98 |
+
'layer10': 'cv',
|
| 99 |
+
'layer11': 'cv',
|
| 100 |
+
'layer12': 'cd',
|
| 101 |
+
'layer13': 'cv',
|
| 102 |
+
'layer14': 'cv',
|
| 103 |
+
'layer15': 'cv',
|
| 104 |
+
},
|
| 105 |
+
'avvv4': {
|
| 106 |
+
'layer0': 'ad',
|
| 107 |
+
'layer1': 'cv',
|
| 108 |
+
'layer2': 'cv',
|
| 109 |
+
'layer3': 'cv',
|
| 110 |
+
'layer4': 'ad',
|
| 111 |
+
'layer5': 'cv',
|
| 112 |
+
'layer6': 'cv',
|
| 113 |
+
'layer7': 'cv',
|
| 114 |
+
'layer8': 'ad',
|
| 115 |
+
'layer9': 'cv',
|
| 116 |
+
'layer10': 'cv',
|
| 117 |
+
'layer11': 'cv',
|
| 118 |
+
'layer12': 'ad',
|
| 119 |
+
'layer13': 'cv',
|
| 120 |
+
'layer14': 'cv',
|
| 121 |
+
'layer15': 'cv',
|
| 122 |
+
},
|
| 123 |
+
'rvvv4': {
|
| 124 |
+
'layer0': 'rd',
|
| 125 |
+
'layer1': 'cv',
|
| 126 |
+
'layer2': 'cv',
|
| 127 |
+
'layer3': 'cv',
|
| 128 |
+
'layer4': 'rd',
|
| 129 |
+
'layer5': 'cv',
|
| 130 |
+
'layer6': 'cv',
|
| 131 |
+
'layer7': 'cv',
|
| 132 |
+
'layer8': 'rd',
|
| 133 |
+
'layer9': 'cv',
|
| 134 |
+
'layer10': 'cv',
|
| 135 |
+
'layer11': 'cv',
|
| 136 |
+
'layer12': 'rd',
|
| 137 |
+
'layer13': 'cv',
|
| 138 |
+
'layer14': 'cv',
|
| 139 |
+
'layer15': 'cv',
|
| 140 |
+
},
|
| 141 |
+
'cccv4': {
|
| 142 |
+
'layer0': 'cd',
|
| 143 |
+
'layer1': 'cd',
|
| 144 |
+
'layer2': 'cd',
|
| 145 |
+
'layer3': 'cv',
|
| 146 |
+
'layer4': 'cd',
|
| 147 |
+
'layer5': 'cd',
|
| 148 |
+
'layer6': 'cd',
|
| 149 |
+
'layer7': 'cv',
|
| 150 |
+
'layer8': 'cd',
|
| 151 |
+
'layer9': 'cd',
|
| 152 |
+
'layer10': 'cd',
|
| 153 |
+
'layer11': 'cv',
|
| 154 |
+
'layer12': 'cd',
|
| 155 |
+
'layer13': 'cd',
|
| 156 |
+
'layer14': 'cd',
|
| 157 |
+
'layer15': 'cv',
|
| 158 |
+
},
|
| 159 |
+
'aaav4': {
|
| 160 |
+
'layer0': 'ad',
|
| 161 |
+
'layer1': 'ad',
|
| 162 |
+
'layer2': 'ad',
|
| 163 |
+
'layer3': 'cv',
|
| 164 |
+
'layer4': 'ad',
|
| 165 |
+
'layer5': 'ad',
|
| 166 |
+
'layer6': 'ad',
|
| 167 |
+
'layer7': 'cv',
|
| 168 |
+
'layer8': 'ad',
|
| 169 |
+
'layer9': 'ad',
|
| 170 |
+
'layer10': 'ad',
|
| 171 |
+
'layer11': 'cv',
|
| 172 |
+
'layer12': 'ad',
|
| 173 |
+
'layer13': 'ad',
|
| 174 |
+
'layer14': 'ad',
|
| 175 |
+
'layer15': 'cv',
|
| 176 |
+
},
|
| 177 |
+
'rrrv4': {
|
| 178 |
+
'layer0': 'rd',
|
| 179 |
+
'layer1': 'rd',
|
| 180 |
+
'layer2': 'rd',
|
| 181 |
+
'layer3': 'cv',
|
| 182 |
+
'layer4': 'rd',
|
| 183 |
+
'layer5': 'rd',
|
| 184 |
+
'layer6': 'rd',
|
| 185 |
+
'layer7': 'cv',
|
| 186 |
+
'layer8': 'rd',
|
| 187 |
+
'layer9': 'rd',
|
| 188 |
+
'layer10': 'rd',
|
| 189 |
+
'layer11': 'cv',
|
| 190 |
+
'layer12': 'rd',
|
| 191 |
+
'layer13': 'rd',
|
| 192 |
+
'layer14': 'rd',
|
| 193 |
+
'layer15': 'cv',
|
| 194 |
+
},
|
| 195 |
+
'c16': {
|
| 196 |
+
'layer0': 'cd',
|
| 197 |
+
'layer1': 'cd',
|
| 198 |
+
'layer2': 'cd',
|
| 199 |
+
'layer3': 'cd',
|
| 200 |
+
'layer4': 'cd',
|
| 201 |
+
'layer5': 'cd',
|
| 202 |
+
'layer6': 'cd',
|
| 203 |
+
'layer7': 'cd',
|
| 204 |
+
'layer8': 'cd',
|
| 205 |
+
'layer9': 'cd',
|
| 206 |
+
'layer10': 'cd',
|
| 207 |
+
'layer11': 'cd',
|
| 208 |
+
'layer12': 'cd',
|
| 209 |
+
'layer13': 'cd',
|
| 210 |
+
'layer14': 'cd',
|
| 211 |
+
'layer15': 'cd',
|
| 212 |
+
},
|
| 213 |
+
'a16': {
|
| 214 |
+
'layer0': 'ad',
|
| 215 |
+
'layer1': 'ad',
|
| 216 |
+
'layer2': 'ad',
|
| 217 |
+
'layer3': 'ad',
|
| 218 |
+
'layer4': 'ad',
|
| 219 |
+
'layer5': 'ad',
|
| 220 |
+
'layer6': 'ad',
|
| 221 |
+
'layer7': 'ad',
|
| 222 |
+
'layer8': 'ad',
|
| 223 |
+
'layer9': 'ad',
|
| 224 |
+
'layer10': 'ad',
|
| 225 |
+
'layer11': 'ad',
|
| 226 |
+
'layer12': 'ad',
|
| 227 |
+
'layer13': 'ad',
|
| 228 |
+
'layer14': 'ad',
|
| 229 |
+
'layer15': 'ad',
|
| 230 |
+
},
|
| 231 |
+
'r16': {
|
| 232 |
+
'layer0': 'rd',
|
| 233 |
+
'layer1': 'rd',
|
| 234 |
+
'layer2': 'rd',
|
| 235 |
+
'layer3': 'rd',
|
| 236 |
+
'layer4': 'rd',
|
| 237 |
+
'layer5': 'rd',
|
| 238 |
+
'layer6': 'rd',
|
| 239 |
+
'layer7': 'rd',
|
| 240 |
+
'layer8': 'rd',
|
| 241 |
+
'layer9': 'rd',
|
| 242 |
+
'layer10': 'rd',
|
| 243 |
+
'layer11': 'rd',
|
| 244 |
+
'layer12': 'rd',
|
| 245 |
+
'layer13': 'rd',
|
| 246 |
+
'layer14': 'rd',
|
| 247 |
+
'layer15': 'rd',
|
| 248 |
+
},
|
| 249 |
+
'carv4': {
|
| 250 |
+
'layer0': 'cd',
|
| 251 |
+
'layer1': 'ad',
|
| 252 |
+
'layer2': 'rd',
|
| 253 |
+
'layer3': 'cv',
|
| 254 |
+
'layer4': 'cd',
|
| 255 |
+
'layer5': 'ad',
|
| 256 |
+
'layer6': 'rd',
|
| 257 |
+
'layer7': 'cv',
|
| 258 |
+
'layer8': 'cd',
|
| 259 |
+
'layer9': 'ad',
|
| 260 |
+
'layer10': 'rd',
|
| 261 |
+
'layer11': 'cv',
|
| 262 |
+
'layer12': 'cd',
|
| 263 |
+
'layer13': 'ad',
|
| 264 |
+
'layer14': 'rd',
|
| 265 |
+
'layer15': 'cv',
|
| 266 |
+
},
|
| 267 |
+
}
|
| 268 |
+
|
| 269 |
+
|
| 270 |
+
def createConvFunc(op_type):
|
| 271 |
+
assert op_type in ['cv', 'cd', 'ad', 'rd'], 'unknown op type: %s' % str(op_type)
|
| 272 |
+
if op_type == 'cv':
|
| 273 |
+
return F.conv2d
|
| 274 |
+
|
| 275 |
+
if op_type == 'cd':
|
| 276 |
+
def func(x, weights, bias=None, stride=1, padding=0, dilation=1, groups=1):
|
| 277 |
+
assert dilation in [1, 2], 'dilation for cd_conv should be in 1 or 2'
|
| 278 |
+
assert weights.size(2) == 3 and weights.size(3) == 3, 'kernel size for cd_conv should be 3x3'
|
| 279 |
+
assert padding == dilation, 'padding for cd_conv set wrong'
|
| 280 |
+
|
| 281 |
+
weights_c = weights.sum(dim=[2, 3], keepdim=True)
|
| 282 |
+
yc = F.conv2d(x, weights_c, stride=stride, padding=0, groups=groups)
|
| 283 |
+
y = F.conv2d(x, weights, bias, stride=stride, padding=padding, dilation=dilation, groups=groups)
|
| 284 |
+
return y - yc
|
| 285 |
+
|
| 286 |
+
return func
|
| 287 |
+
elif op_type == 'ad':
|
| 288 |
+
def func(x, weights, bias=None, stride=1, padding=0, dilation=1, groups=1):
|
| 289 |
+
assert dilation in [1, 2], 'dilation for ad_conv should be in 1 or 2'
|
| 290 |
+
assert weights.size(2) == 3 and weights.size(3) == 3, 'kernel size for ad_conv should be 3x3'
|
| 291 |
+
assert padding == dilation, 'padding for ad_conv set wrong'
|
| 292 |
+
|
| 293 |
+
shape = weights.shape
|
| 294 |
+
weights = weights.view(shape[0], shape[1], -1)
|
| 295 |
+
weights_conv = (weights - weights[:, :, [3, 0, 1, 6, 4, 2, 7, 8, 5]]).view(shape) # clock-wise
|
| 296 |
+
y = F.conv2d(x, weights_conv, bias, stride=stride, padding=padding, dilation=dilation, groups=groups)
|
| 297 |
+
return y
|
| 298 |
+
|
| 299 |
+
return func
|
| 300 |
+
elif op_type == 'rd':
|
| 301 |
+
def func(x, weights, bias=None, stride=1, padding=0, dilation=1, groups=1):
|
| 302 |
+
assert dilation in [1, 2], 'dilation for rd_conv should be in 1 or 2'
|
| 303 |
+
assert weights.size(2) == 3 and weights.size(3) == 3, 'kernel size for rd_conv should be 3x3'
|
| 304 |
+
padding = 2 * dilation
|
| 305 |
+
|
| 306 |
+
shape = weights.shape
|
| 307 |
+
if weights.is_cuda:
|
| 308 |
+
buffer = torch.cuda.FloatTensor(shape[0], shape[1], 5 * 5).fill_(0)
|
| 309 |
+
else:
|
| 310 |
+
buffer = torch.zeros(shape[0], shape[1], 5 * 5)
|
| 311 |
+
weights = weights.view(shape[0], shape[1], -1)
|
| 312 |
+
buffer[:, :, [0, 2, 4, 10, 14, 20, 22, 24]] = weights[:, :, 1:]
|
| 313 |
+
buffer[:, :, [6, 7, 8, 11, 13, 16, 17, 18]] = -weights[:, :, 1:]
|
| 314 |
+
buffer[:, :, 12] = 0
|
| 315 |
+
buffer = buffer.view(shape[0], shape[1], 5, 5)
|
| 316 |
+
y = F.conv2d(x, buffer, bias, stride=stride, padding=padding, dilation=dilation, groups=groups)
|
| 317 |
+
return y
|
| 318 |
+
|
| 319 |
+
return func
|
| 320 |
+
else:
|
| 321 |
+
print('impossible to be here unless you force that')
|
| 322 |
+
return None
|
| 323 |
+
|
| 324 |
+
|
| 325 |
+
class Conv2d(nn.Module):
|
| 326 |
+
def __init__(self, pdc, in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1,
|
| 327 |
+
bias=False):
|
| 328 |
+
super(Conv2d, self).__init__()
|
| 329 |
+
if in_channels % groups != 0:
|
| 330 |
+
raise ValueError('in_channels must be divisible by groups')
|
| 331 |
+
if out_channels % groups != 0:
|
| 332 |
+
raise ValueError('out_channels must be divisible by groups')
|
| 333 |
+
self.in_channels = in_channels
|
| 334 |
+
self.out_channels = out_channels
|
| 335 |
+
self.kernel_size = kernel_size
|
| 336 |
+
self.stride = stride
|
| 337 |
+
self.padding = padding
|
| 338 |
+
self.dilation = dilation
|
| 339 |
+
self.groups = groups
|
| 340 |
+
self.weight = nn.Parameter(torch.Tensor(out_channels, in_channels // groups, kernel_size, kernel_size))
|
| 341 |
+
if bias:
|
| 342 |
+
self.bias = nn.Parameter(torch.Tensor(out_channels))
|
| 343 |
+
else:
|
| 344 |
+
self.register_parameter('bias', None)
|
| 345 |
+
self.reset_parameters()
|
| 346 |
+
self.pdc = pdc
|
| 347 |
+
|
| 348 |
+
def reset_parameters(self):
|
| 349 |
+
nn.init.kaiming_uniform_(self.weight, a=math.sqrt(5))
|
| 350 |
+
if self.bias is not None:
|
| 351 |
+
fan_in, _ = nn.init._calculate_fan_in_and_fan_out(self.weight)
|
| 352 |
+
bound = 1 / math.sqrt(fan_in)
|
| 353 |
+
nn.init.uniform_(self.bias, -bound, bound)
|
| 354 |
+
|
| 355 |
+
def forward(self, input):
|
| 356 |
+
|
| 357 |
+
return self.pdc(input, self.weight, self.bias, self.stride, self.padding, self.dilation, self.groups)
|
| 358 |
+
|
| 359 |
+
|
| 360 |
+
class CSAM(nn.Module):
|
| 361 |
+
"""
|
| 362 |
+
Compact Spatial Attention Module
|
| 363 |
+
"""
|
| 364 |
+
|
| 365 |
+
def __init__(self, channels):
|
| 366 |
+
super(CSAM, self).__init__()
|
| 367 |
+
|
| 368 |
+
mid_channels = 4
|
| 369 |
+
self.relu1 = nn.ReLU()
|
| 370 |
+
self.conv1 = nn.Conv2d(channels, mid_channels, kernel_size=1, padding=0)
|
| 371 |
+
self.conv2 = nn.Conv2d(mid_channels, 1, kernel_size=3, padding=1, bias=False)
|
| 372 |
+
self.sigmoid = nn.Sigmoid()
|
| 373 |
+
nn.init.constant_(self.conv1.bias, 0)
|
| 374 |
+
|
| 375 |
+
def forward(self, x):
|
| 376 |
+
y = self.relu1(x)
|
| 377 |
+
y = self.conv1(y)
|
| 378 |
+
y = self.conv2(y)
|
| 379 |
+
y = self.sigmoid(y)
|
| 380 |
+
|
| 381 |
+
return x * y
|
| 382 |
+
|
| 383 |
+
|
| 384 |
+
class CDCM(nn.Module):
|
| 385 |
+
"""
|
| 386 |
+
Compact Dilation Convolution based Module
|
| 387 |
+
"""
|
| 388 |
+
|
| 389 |
+
def __init__(self, in_channels, out_channels):
|
| 390 |
+
super(CDCM, self).__init__()
|
| 391 |
+
|
| 392 |
+
self.relu1 = nn.ReLU()
|
| 393 |
+
self.conv1 = nn.Conv2d(in_channels, out_channels, kernel_size=1, padding=0)
|
| 394 |
+
self.conv2_1 = nn.Conv2d(out_channels, out_channels, kernel_size=3, dilation=5, padding=5, bias=False)
|
| 395 |
+
self.conv2_2 = nn.Conv2d(out_channels, out_channels, kernel_size=3, dilation=7, padding=7, bias=False)
|
| 396 |
+
self.conv2_3 = nn.Conv2d(out_channels, out_channels, kernel_size=3, dilation=9, padding=9, bias=False)
|
| 397 |
+
self.conv2_4 = nn.Conv2d(out_channels, out_channels, kernel_size=3, dilation=11, padding=11, bias=False)
|
| 398 |
+
nn.init.constant_(self.conv1.bias, 0)
|
| 399 |
+
|
| 400 |
+
def forward(self, x):
|
| 401 |
+
x = self.relu1(x)
|
| 402 |
+
x = self.conv1(x)
|
| 403 |
+
x1 = self.conv2_1(x)
|
| 404 |
+
x2 = self.conv2_2(x)
|
| 405 |
+
x3 = self.conv2_3(x)
|
| 406 |
+
x4 = self.conv2_4(x)
|
| 407 |
+
return x1 + x2 + x3 + x4
|
| 408 |
+
|
| 409 |
+
|
| 410 |
+
class MapReduce(nn.Module):
|
| 411 |
+
"""
|
| 412 |
+
Reduce feature maps into a single edge map
|
| 413 |
+
"""
|
| 414 |
+
|
| 415 |
+
def __init__(self, channels):
|
| 416 |
+
super(MapReduce, self).__init__()
|
| 417 |
+
self.conv = nn.Conv2d(channels, 1, kernel_size=1, padding=0)
|
| 418 |
+
nn.init.constant_(self.conv.bias, 0)
|
| 419 |
+
|
| 420 |
+
def forward(self, x):
|
| 421 |
+
return self.conv(x)
|
| 422 |
+
|
| 423 |
+
|
| 424 |
+
class PDCBlock(nn.Module):
|
| 425 |
+
def __init__(self, pdc, inplane, ouplane, stride=1):
|
| 426 |
+
super(PDCBlock, self).__init__()
|
| 427 |
+
self.stride = stride
|
| 428 |
+
|
| 429 |
+
self.stride = stride
|
| 430 |
+
if self.stride > 1:
|
| 431 |
+
self.pool = nn.MaxPool2d(kernel_size=2, stride=2)
|
| 432 |
+
self.shortcut = nn.Conv2d(inplane, ouplane, kernel_size=1, padding=0)
|
| 433 |
+
self.conv1 = Conv2d(pdc, inplane, inplane, kernel_size=3, padding=1, groups=inplane, bias=False)
|
| 434 |
+
self.relu2 = nn.ReLU()
|
| 435 |
+
self.conv2 = nn.Conv2d(inplane, ouplane, kernel_size=1, padding=0, bias=False)
|
| 436 |
+
|
| 437 |
+
def forward(self, x):
|
| 438 |
+
if self.stride > 1:
|
| 439 |
+
x = self.pool(x)
|
| 440 |
+
y = self.conv1(x)
|
| 441 |
+
y = self.relu2(y)
|
| 442 |
+
y = self.conv2(y)
|
| 443 |
+
if self.stride > 1:
|
| 444 |
+
x = self.shortcut(x)
|
| 445 |
+
y = y + x
|
| 446 |
+
return y
|
| 447 |
+
|
| 448 |
+
|
| 449 |
+
class PDCBlock_converted(nn.Module):
|
| 450 |
+
"""
|
| 451 |
+
CPDC, APDC can be converted to vanilla 3x3 convolution
|
| 452 |
+
RPDC can be converted to vanilla 5x5 convolution
|
| 453 |
+
"""
|
| 454 |
+
|
| 455 |
+
def __init__(self, pdc, inplane, ouplane, stride=1):
|
| 456 |
+
super(PDCBlock_converted, self).__init__()
|
| 457 |
+
self.stride = stride
|
| 458 |
+
|
| 459 |
+
if self.stride > 1:
|
| 460 |
+
self.pool = nn.MaxPool2d(kernel_size=2, stride=2)
|
| 461 |
+
self.shortcut = nn.Conv2d(inplane, ouplane, kernel_size=1, padding=0)
|
| 462 |
+
if pdc == 'rd':
|
| 463 |
+
self.conv1 = nn.Conv2d(inplane, inplane, kernel_size=5, padding=2, groups=inplane, bias=False)
|
| 464 |
+
else:
|
| 465 |
+
self.conv1 = nn.Conv2d(inplane, inplane, kernel_size=3, padding=1, groups=inplane, bias=False)
|
| 466 |
+
self.relu2 = nn.ReLU()
|
| 467 |
+
self.conv2 = nn.Conv2d(inplane, ouplane, kernel_size=1, padding=0, bias=False)
|
| 468 |
+
|
| 469 |
+
def forward(self, x):
|
| 470 |
+
if self.stride > 1:
|
| 471 |
+
x = self.pool(x)
|
| 472 |
+
y = self.conv1(x)
|
| 473 |
+
y = self.relu2(y)
|
| 474 |
+
y = self.conv2(y)
|
| 475 |
+
if self.stride > 1:
|
| 476 |
+
x = self.shortcut(x)
|
| 477 |
+
y = y + x
|
| 478 |
+
return y
|
| 479 |
+
|
| 480 |
+
|
| 481 |
+
class PiDiNet(nn.Module):
|
| 482 |
+
def __init__(self, inplane, pdcs, dil=None, sa=False, convert=False):
|
| 483 |
+
super(PiDiNet, self).__init__()
|
| 484 |
+
self.sa = sa
|
| 485 |
+
if dil is not None:
|
| 486 |
+
assert isinstance(dil, int), 'dil should be an int'
|
| 487 |
+
self.dil = dil
|
| 488 |
+
|
| 489 |
+
self.fuseplanes = []
|
| 490 |
+
|
| 491 |
+
self.inplane = inplane
|
| 492 |
+
if convert:
|
| 493 |
+
if pdcs[0] == 'rd':
|
| 494 |
+
init_kernel_size = 5
|
| 495 |
+
init_padding = 2
|
| 496 |
+
else:
|
| 497 |
+
init_kernel_size = 3
|
| 498 |
+
init_padding = 1
|
| 499 |
+
self.init_block = nn.Conv2d(3, self.inplane,
|
| 500 |
+
kernel_size=init_kernel_size, padding=init_padding, bias=False)
|
| 501 |
+
block_class = PDCBlock_converted
|
| 502 |
+
else:
|
| 503 |
+
self.init_block = Conv2d(pdcs[0], 3, self.inplane, kernel_size=3, padding=1)
|
| 504 |
+
block_class = PDCBlock
|
| 505 |
+
|
| 506 |
+
self.block1_1 = block_class(pdcs[1], self.inplane, self.inplane)
|
| 507 |
+
self.block1_2 = block_class(pdcs[2], self.inplane, self.inplane)
|
| 508 |
+
self.block1_3 = block_class(pdcs[3], self.inplane, self.inplane)
|
| 509 |
+
self.fuseplanes.append(self.inplane) # C
|
| 510 |
+
|
| 511 |
+
inplane = self.inplane
|
| 512 |
+
self.inplane = self.inplane * 2
|
| 513 |
+
self.block2_1 = block_class(pdcs[4], inplane, self.inplane, stride=2)
|
| 514 |
+
self.block2_2 = block_class(pdcs[5], self.inplane, self.inplane)
|
| 515 |
+
self.block2_3 = block_class(pdcs[6], self.inplane, self.inplane)
|
| 516 |
+
self.block2_4 = block_class(pdcs[7], self.inplane, self.inplane)
|
| 517 |
+
self.fuseplanes.append(self.inplane) # 2C
|
| 518 |
+
|
| 519 |
+
inplane = self.inplane
|
| 520 |
+
self.inplane = self.inplane * 2
|
| 521 |
+
self.block3_1 = block_class(pdcs[8], inplane, self.inplane, stride=2)
|
| 522 |
+
self.block3_2 = block_class(pdcs[9], self.inplane, self.inplane)
|
| 523 |
+
self.block3_3 = block_class(pdcs[10], self.inplane, self.inplane)
|
| 524 |
+
self.block3_4 = block_class(pdcs[11], self.inplane, self.inplane)
|
| 525 |
+
self.fuseplanes.append(self.inplane) # 4C
|
| 526 |
+
|
| 527 |
+
self.block4_1 = block_class(pdcs[12], self.inplane, self.inplane, stride=2)
|
| 528 |
+
self.block4_2 = block_class(pdcs[13], self.inplane, self.inplane)
|
| 529 |
+
self.block4_3 = block_class(pdcs[14], self.inplane, self.inplane)
|
| 530 |
+
self.block4_4 = block_class(pdcs[15], self.inplane, self.inplane)
|
| 531 |
+
self.fuseplanes.append(self.inplane) # 4C
|
| 532 |
+
|
| 533 |
+
self.conv_reduces = nn.ModuleList()
|
| 534 |
+
if self.sa and self.dil is not None:
|
| 535 |
+
self.attentions = nn.ModuleList()
|
| 536 |
+
self.dilations = nn.ModuleList()
|
| 537 |
+
for i in range(4):
|
| 538 |
+
self.dilations.append(CDCM(self.fuseplanes[i], self.dil))
|
| 539 |
+
self.attentions.append(CSAM(self.dil))
|
| 540 |
+
self.conv_reduces.append(MapReduce(self.dil))
|
| 541 |
+
elif self.sa:
|
| 542 |
+
self.attentions = nn.ModuleList()
|
| 543 |
+
for i in range(4):
|
| 544 |
+
self.attentions.append(CSAM(self.fuseplanes[i]))
|
| 545 |
+
self.conv_reduces.append(MapReduce(self.fuseplanes[i]))
|
| 546 |
+
elif self.dil is not None:
|
| 547 |
+
self.dilations = nn.ModuleList()
|
| 548 |
+
for i in range(4):
|
| 549 |
+
self.dilations.append(CDCM(self.fuseplanes[i], self.dil))
|
| 550 |
+
self.conv_reduces.append(MapReduce(self.dil))
|
| 551 |
+
else:
|
| 552 |
+
for i in range(4):
|
| 553 |
+
self.conv_reduces.append(MapReduce(self.fuseplanes[i]))
|
| 554 |
+
|
| 555 |
+
self.classifier = nn.Conv2d(4, 1, kernel_size=1) # has bias
|
| 556 |
+
nn.init.constant_(self.classifier.weight, 0.25)
|
| 557 |
+
nn.init.constant_(self.classifier.bias, 0)
|
| 558 |
+
|
| 559 |
+
# print('initialization done')
|
| 560 |
+
|
| 561 |
+
def get_weights(self):
|
| 562 |
+
conv_weights = []
|
| 563 |
+
bn_weights = []
|
| 564 |
+
relu_weights = []
|
| 565 |
+
for pname, p in self.named_parameters():
|
| 566 |
+
if 'bn' in pname:
|
| 567 |
+
bn_weights.append(p)
|
| 568 |
+
elif 'relu' in pname:
|
| 569 |
+
relu_weights.append(p)
|
| 570 |
+
else:
|
| 571 |
+
conv_weights.append(p)
|
| 572 |
+
|
| 573 |
+
return conv_weights, bn_weights, relu_weights
|
| 574 |
+
|
| 575 |
+
def forward(self, x):
|
| 576 |
+
H, W = x.size()[2:]
|
| 577 |
+
|
| 578 |
+
x = self.init_block(x)
|
| 579 |
+
|
| 580 |
+
x1 = self.block1_1(x)
|
| 581 |
+
x1 = self.block1_2(x1)
|
| 582 |
+
x1 = self.block1_3(x1)
|
| 583 |
+
|
| 584 |
+
x2 = self.block2_1(x1)
|
| 585 |
+
x2 = self.block2_2(x2)
|
| 586 |
+
x2 = self.block2_3(x2)
|
| 587 |
+
x2 = self.block2_4(x2)
|
| 588 |
+
|
| 589 |
+
x3 = self.block3_1(x2)
|
| 590 |
+
x3 = self.block3_2(x3)
|
| 591 |
+
x3 = self.block3_3(x3)
|
| 592 |
+
x3 = self.block3_4(x3)
|
| 593 |
+
|
| 594 |
+
x4 = self.block4_1(x3)
|
| 595 |
+
x4 = self.block4_2(x4)
|
| 596 |
+
x4 = self.block4_3(x4)
|
| 597 |
+
x4 = self.block4_4(x4)
|
| 598 |
+
|
| 599 |
+
x_fuses = []
|
| 600 |
+
if self.sa and self.dil is not None:
|
| 601 |
+
for i, xi in enumerate([x1, x2, x3, x4]):
|
| 602 |
+
x_fuses.append(self.attentions[i](self.dilations[i](xi)))
|
| 603 |
+
elif self.sa:
|
| 604 |
+
for i, xi in enumerate([x1, x2, x3, x4]):
|
| 605 |
+
x_fuses.append(self.attentions[i](xi))
|
| 606 |
+
elif self.dil is not None:
|
| 607 |
+
for i, xi in enumerate([x1, x2, x3, x4]):
|
| 608 |
+
x_fuses.append(self.dilations[i](xi))
|
| 609 |
+
else:
|
| 610 |
+
x_fuses = [x1, x2, x3, x4]
|
| 611 |
+
|
| 612 |
+
e1 = self.conv_reduces[0](x_fuses[0])
|
| 613 |
+
e1 = F.interpolate(e1, (H, W), mode="bilinear", align_corners=False)
|
| 614 |
+
|
| 615 |
+
e2 = self.conv_reduces[1](x_fuses[1])
|
| 616 |
+
e2 = F.interpolate(e2, (H, W), mode="bilinear", align_corners=False)
|
| 617 |
+
|
| 618 |
+
e3 = self.conv_reduces[2](x_fuses[2])
|
| 619 |
+
e3 = F.interpolate(e3, (H, W), mode="bilinear", align_corners=False)
|
| 620 |
+
|
| 621 |
+
e4 = self.conv_reduces[3](x_fuses[3])
|
| 622 |
+
e4 = F.interpolate(e4, (H, W), mode="bilinear", align_corners=False)
|
| 623 |
+
|
| 624 |
+
outputs = [e1, e2, e3, e4]
|
| 625 |
+
|
| 626 |
+
output = self.classifier(torch.cat(outputs, dim=1))
|
| 627 |
+
# if not self.training:
|
| 628 |
+
# return torch.sigmoid(output)
|
| 629 |
+
|
| 630 |
+
outputs.append(output)
|
| 631 |
+
outputs = [torch.sigmoid(r) for r in outputs]
|
| 632 |
+
return outputs
|
| 633 |
+
|
| 634 |
+
|
| 635 |
+
def config_model(model):
|
| 636 |
+
model_options = list(nets.keys())
|
| 637 |
+
assert model in model_options, \
|
| 638 |
+
'unrecognized model, please choose from %s' % str(model_options)
|
| 639 |
+
|
| 640 |
+
# print(str(nets[model]))
|
| 641 |
+
|
| 642 |
+
pdcs = []
|
| 643 |
+
for i in range(16):
|
| 644 |
+
layer_name = 'layer%d' % i
|
| 645 |
+
op = nets[model][layer_name]
|
| 646 |
+
pdcs.append(createConvFunc(op))
|
| 647 |
+
|
| 648 |
+
return pdcs
|
| 649 |
+
|
| 650 |
+
|
| 651 |
+
def pidinet():
|
| 652 |
+
pdcs = config_model('carv4')
|
| 653 |
+
dil = 24 # if args.dil else None
|
| 654 |
+
return PiDiNet(60, pdcs, dil=dil, sa=True)
|
modules/cnet_modules/pidinet/util.py
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import random
|
| 2 |
+
|
| 3 |
+
import numpy as np
|
| 4 |
+
import cv2
|
| 5 |
+
import os
|
| 6 |
+
|
| 7 |
+
annotator_ckpts_path = os.path.join(os.path.dirname(__file__), 'ckpts')
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
def HWC3(x):
|
| 11 |
+
assert x.dtype == np.uint8
|
| 12 |
+
if x.ndim == 2:
|
| 13 |
+
x = x[:, :, None]
|
| 14 |
+
assert x.ndim == 3
|
| 15 |
+
H, W, C = x.shape
|
| 16 |
+
assert C == 1 or C == 3 or C == 4
|
| 17 |
+
if C == 3:
|
| 18 |
+
return x
|
| 19 |
+
if C == 1:
|
| 20 |
+
return np.concatenate([x, x, x], axis=2)
|
| 21 |
+
if C == 4:
|
| 22 |
+
color = x[:, :, 0:3].astype(np.float32)
|
| 23 |
+
alpha = x[:, :, 3:4].astype(np.float32) / 255.0
|
| 24 |
+
y = color * alpha + 255.0 * (1.0 - alpha)
|
| 25 |
+
y = y.clip(0, 255).astype(np.uint8)
|
| 26 |
+
return y
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def resize_image(input_image, resolution):
|
| 30 |
+
H, W, C = input_image.shape
|
| 31 |
+
H = float(H)
|
| 32 |
+
W = float(W)
|
| 33 |
+
k = float(resolution) / min(H, W)
|
| 34 |
+
H *= k
|
| 35 |
+
W *= k
|
| 36 |
+
H = int(np.round(H / 64.0)) * 64
|
| 37 |
+
W = int(np.round(W / 64.0)) * 64
|
| 38 |
+
img = cv2.resize(input_image, (W, H), interpolation=cv2.INTER_LANCZOS4 if k > 1 else cv2.INTER_AREA)
|
| 39 |
+
return img
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
def nms(x, t, s):
|
| 43 |
+
x = cv2.GaussianBlur(x.astype(np.float32), (0, 0), s)
|
| 44 |
+
|
| 45 |
+
f1 = np.array([[0, 0, 0], [1, 1, 1], [0, 0, 0]], dtype=np.uint8)
|
| 46 |
+
f2 = np.array([[0, 1, 0], [0, 1, 0], [0, 1, 0]], dtype=np.uint8)
|
| 47 |
+
f3 = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]], dtype=np.uint8)
|
| 48 |
+
f4 = np.array([[0, 0, 1], [0, 1, 0], [1, 0, 0]], dtype=np.uint8)
|
| 49 |
+
|
| 50 |
+
y = np.zeros_like(x)
|
| 51 |
+
|
| 52 |
+
for f in [f1, f2, f3, f4]:
|
| 53 |
+
np.putmask(y, cv2.dilate(x, kernel=f) == x, x)
|
| 54 |
+
|
| 55 |
+
z = np.zeros_like(y, dtype=np.uint8)
|
| 56 |
+
z[y > t] = 255
|
| 57 |
+
return z
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
def make_noise_disk(H, W, C, F):
|
| 61 |
+
noise = np.random.uniform(low=0, high=1, size=((H // F) + 2, (W // F) + 2, C))
|
| 62 |
+
noise = cv2.resize(noise, (W + 2 * F, H + 2 * F), interpolation=cv2.INTER_CUBIC)
|
| 63 |
+
noise = noise[F: F + H, F: F + W]
|
| 64 |
+
noise -= np.min(noise)
|
| 65 |
+
noise /= np.max(noise)
|
| 66 |
+
if C == 1:
|
| 67 |
+
noise = noise[:, :, None]
|
| 68 |
+
return noise
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
def min_max_norm(x):
|
| 72 |
+
x -= np.min(x)
|
| 73 |
+
x /= np.maximum(np.max(x), 1e-5)
|
| 74 |
+
return x
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
def safe_step(x, step=2):
|
| 78 |
+
y = x.astype(np.float32) * float(step + 1)
|
| 79 |
+
y = y.astype(np.int32).astype(np.float32) / float(step)
|
| 80 |
+
return y
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
def img2mask(img, H, W, low=10, high=90):
|
| 84 |
+
assert img.ndim == 3 or img.ndim == 2
|
| 85 |
+
assert img.dtype == np.uint8
|
| 86 |
+
|
| 87 |
+
if img.ndim == 3:
|
| 88 |
+
y = img[:, :, random.randrange(0, img.shape[2])]
|
| 89 |
+
else:
|
| 90 |
+
y = img
|
| 91 |
+
|
| 92 |
+
y = cv2.resize(y, (W, H), interpolation=cv2.INTER_CUBIC)
|
| 93 |
+
|
| 94 |
+
if random.uniform(0, 1) < 0.5:
|
| 95 |
+
y = 255 - y
|
| 96 |
+
|
| 97 |
+
return y < np.percentile(y, random.randrange(low, high))
|
modules/common.py
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
import torch.nn as nn
|
| 3 |
+
import torch.nn.functional as F
|
| 4 |
+
import math
|
| 5 |
+
from einops import rearrange
|
| 6 |
+
import torch.fft as fft
|
| 7 |
+
class Linear(torch.nn.Linear):
|
| 8 |
+
def reset_parameters(self):
|
| 9 |
+
return None
|
| 10 |
+
|
| 11 |
+
class Conv2d(torch.nn.Conv2d):
|
| 12 |
+
def reset_parameters(self):
|
| 13 |
+
return None
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
class Attention2D(nn.Module):
|
| 18 |
+
def __init__(self, c, nhead, dropout=0.0):
|
| 19 |
+
super().__init__()
|
| 20 |
+
self.attn = nn.MultiheadAttention(c, nhead, dropout=dropout, bias=True, batch_first=True)
|
| 21 |
+
|
| 22 |
+
def forward(self, x, kv, self_attn=False):
|
| 23 |
+
orig_shape = x.shape
|
| 24 |
+
x = x.view(x.size(0), x.size(1), -1).permute(0, 2, 1) # Bx4xHxW -> Bx(HxW)x4
|
| 25 |
+
if self_attn:
|
| 26 |
+
#print('in line 23 algong self att ', kv.shape, x.shape)
|
| 27 |
+
kv = torch.cat([x, kv], dim=1)
|
| 28 |
+
#if x.shape[1] >= 72 * 72:
|
| 29 |
+
# x = x * math.sqrt(math.log(64*64, 24*24))
|
| 30 |
+
|
| 31 |
+
x = self.attn(x, kv, kv, need_weights=False)[0]
|
| 32 |
+
x = x.permute(0, 2, 1).view(*orig_shape)
|
| 33 |
+
return x
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
class LayerNorm2d(nn.LayerNorm):
|
| 37 |
+
def __init__(self, *args, **kwargs):
|
| 38 |
+
super().__init__(*args, **kwargs)
|
| 39 |
+
|
| 40 |
+
def forward(self, x):
|
| 41 |
+
return super().forward(x.permute(0, 2, 3, 1)).permute(0, 3, 1, 2)
|
| 42 |
+
|
| 43 |
+
class GlobalResponseNorm(nn.Module):
|
| 44 |
+
"from https://github.com/facebookresearch/ConvNeXt-V2/blob/3608f67cc1dae164790c5d0aead7bf2d73d9719b/models/utils.py#L105"
|
| 45 |
+
def __init__(self, dim):
|
| 46 |
+
super().__init__()
|
| 47 |
+
self.gamma = nn.Parameter(torch.zeros(1, 1, 1, dim))
|
| 48 |
+
self.beta = nn.Parameter(torch.zeros(1, 1, 1, dim))
|
| 49 |
+
|
| 50 |
+
def forward(self, x):
|
| 51 |
+
Gx = torch.norm(x, p=2, dim=(1, 2), keepdim=True)
|
| 52 |
+
Nx = Gx / (Gx.mean(dim=-1, keepdim=True) + 1e-6)
|
| 53 |
+
return self.gamma * (x * Nx) + self.beta + x
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
class ResBlock(nn.Module):
|
| 57 |
+
def __init__(self, c, c_skip=0, kernel_size=3, dropout=0.0): # , num_heads=4, expansion=2):
|
| 58 |
+
super().__init__()
|
| 59 |
+
self.depthwise = Conv2d(c, c, kernel_size=kernel_size, padding=kernel_size // 2, groups=c)
|
| 60 |
+
# self.depthwise = SAMBlock(c, num_heads, expansion)
|
| 61 |
+
self.norm = LayerNorm2d(c, elementwise_affine=False, eps=1e-6)
|
| 62 |
+
self.channelwise = nn.Sequential(
|
| 63 |
+
Linear(c + c_skip, c * 4),
|
| 64 |
+
nn.GELU(),
|
| 65 |
+
GlobalResponseNorm(c * 4),
|
| 66 |
+
nn.Dropout(dropout),
|
| 67 |
+
Linear(c * 4, c)
|
| 68 |
+
)
|
| 69 |
+
|
| 70 |
+
def forward(self, x, x_skip=None):
|
| 71 |
+
x_res = x
|
| 72 |
+
x = self.norm(self.depthwise(x))
|
| 73 |
+
if x_skip is not None:
|
| 74 |
+
x = torch.cat([x, x_skip], dim=1)
|
| 75 |
+
x = self.channelwise(x.permute(0, 2, 3, 1)).permute(0, 3, 1, 2)
|
| 76 |
+
return x + x_res
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
class AttnBlock(nn.Module):
|
| 80 |
+
def __init__(self, c, c_cond, nhead, self_attn=True, dropout=0.0):
|
| 81 |
+
super().__init__()
|
| 82 |
+
self.self_attn = self_attn
|
| 83 |
+
self.norm = LayerNorm2d(c, elementwise_affine=False, eps=1e-6)
|
| 84 |
+
self.attention = Attention2D(c, nhead, dropout)
|
| 85 |
+
self.kv_mapper = nn.Sequential(
|
| 86 |
+
nn.SiLU(),
|
| 87 |
+
Linear(c_cond, c)
|
| 88 |
+
)
|
| 89 |
+
|
| 90 |
+
def forward(self, x, kv):
|
| 91 |
+
kv = self.kv_mapper(kv)
|
| 92 |
+
res = self.attention(self.norm(x), kv, self_attn=self.self_attn)
|
| 93 |
+
|
| 94 |
+
#print(torch.unique(res), torch.unique(x), self.self_attn)
|
| 95 |
+
#scale = math.sqrt(math.log(x.shape[-2] * x.shape[-1], 24*24))
|
| 96 |
+
x = x + res
|
| 97 |
+
|
| 98 |
+
return x
|
| 99 |
+
|
| 100 |
+
class FeedForwardBlock(nn.Module):
|
| 101 |
+
def __init__(self, c, dropout=0.0):
|
| 102 |
+
super().__init__()
|
| 103 |
+
self.norm = LayerNorm2d(c, elementwise_affine=False, eps=1e-6)
|
| 104 |
+
self.channelwise = nn.Sequential(
|
| 105 |
+
Linear(c, c * 4),
|
| 106 |
+
nn.GELU(),
|
| 107 |
+
GlobalResponseNorm(c * 4),
|
| 108 |
+
nn.Dropout(dropout),
|
| 109 |
+
Linear(c * 4, c)
|
| 110 |
+
)
|
| 111 |
+
|
| 112 |
+
def forward(self, x):
|
| 113 |
+
x = x + self.channelwise(self.norm(x).permute(0, 2, 3, 1)).permute(0, 3, 1, 2)
|
| 114 |
+
return x
|
| 115 |
+
|
| 116 |
+
|
| 117 |
+
class TimestepBlock(nn.Module):
|
| 118 |
+
def __init__(self, c, c_timestep, conds=['sca']):
|
| 119 |
+
super().__init__()
|
| 120 |
+
self.mapper = Linear(c_timestep, c * 2)
|
| 121 |
+
self.conds = conds
|
| 122 |
+
for cname in conds:
|
| 123 |
+
setattr(self, f"mapper_{cname}", Linear(c_timestep, c * 2))
|
| 124 |
+
|
| 125 |
+
def forward(self, x, t):
|
| 126 |
+
t = t.chunk(len(self.conds) + 1, dim=1)
|
| 127 |
+
a, b = self.mapper(t[0])[:, :, None, None].chunk(2, dim=1)
|
| 128 |
+
for i, c in enumerate(self.conds):
|
| 129 |
+
ac, bc = getattr(self, f"mapper_{c}")(t[i + 1])[:, :, None, None].chunk(2, dim=1)
|
| 130 |
+
a, b = a + ac, b + bc
|
| 131 |
+
return x * (1 + a) + b
|
modules/common_ckpt.py
ADDED
|
@@ -0,0 +1,360 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
import torch.nn as nn
|
| 3 |
+
import torch.nn.functional as F
|
| 4 |
+
import math
|
| 5 |
+
from einops import rearrange
|
| 6 |
+
from modules.speed_util import checkpoint
|
| 7 |
+
class Linear(torch.nn.Linear):
|
| 8 |
+
def reset_parameters(self):
|
| 9 |
+
return None
|
| 10 |
+
|
| 11 |
+
class Conv2d(torch.nn.Conv2d):
|
| 12 |
+
def reset_parameters(self):
|
| 13 |
+
return None
|
| 14 |
+
|
| 15 |
+
class AttnBlock_lrfuse_backup(nn.Module):
|
| 16 |
+
def __init__(self, c, c_cond, nhead, self_attn=True, dropout=0.0, use_checkpoint=True):
|
| 17 |
+
super().__init__()
|
| 18 |
+
self.self_attn = self_attn
|
| 19 |
+
self.norm = LayerNorm2d(c, elementwise_affine=False, eps=1e-6)
|
| 20 |
+
self.attention = Attention2D(c, nhead, dropout)
|
| 21 |
+
self.kv_mapper = nn.Sequential(
|
| 22 |
+
nn.SiLU(),
|
| 23 |
+
Linear(c_cond, c)
|
| 24 |
+
)
|
| 25 |
+
self.fuse_mapper = nn.Sequential(
|
| 26 |
+
nn.SiLU(),
|
| 27 |
+
Linear(c_cond, c)
|
| 28 |
+
)
|
| 29 |
+
self.use_checkpoint = use_checkpoint
|
| 30 |
+
|
| 31 |
+
def forward(self, hr, lr):
|
| 32 |
+
return checkpoint(self._forward, (hr, lr), self.paramters(), self.use_checkpoint)
|
| 33 |
+
def _forward(self, hr, lr):
|
| 34 |
+
res = hr
|
| 35 |
+
hr = self.kv_mapper(rearrange(hr, 'b c h w -> b (h w ) c'))
|
| 36 |
+
lr_fuse = self.attention(self.norm(lr), hr, self_attn=False) + lr
|
| 37 |
+
|
| 38 |
+
lr_fuse = self.fuse_mapper(rearrange(lr_fuse, 'b c h w -> b (h w ) c'))
|
| 39 |
+
hr = self.attention(self.norm(res), lr_fuse, self_attn=False) + res
|
| 40 |
+
return hr
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
class AttnBlock_lrfuse(nn.Module):
|
| 44 |
+
def __init__(self, c, c_cond, nhead, self_attn=True, dropout=0.0, kernel_size=3, use_checkpoint=True):
|
| 45 |
+
super().__init__()
|
| 46 |
+
self.self_attn = self_attn
|
| 47 |
+
self.norm = LayerNorm2d(c, elementwise_affine=False, eps=1e-6)
|
| 48 |
+
self.attention = Attention2D(c, nhead, dropout)
|
| 49 |
+
self.kv_mapper = nn.Sequential(
|
| 50 |
+
nn.SiLU(),
|
| 51 |
+
Linear(c_cond, c)
|
| 52 |
+
)
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
self.depthwise = Conv2d(c, c , kernel_size=kernel_size, padding=kernel_size // 2, groups=c)
|
| 56 |
+
|
| 57 |
+
self.channelwise = nn.Sequential(
|
| 58 |
+
Linear(c + c, c ),
|
| 59 |
+
nn.GELU(),
|
| 60 |
+
GlobalResponseNorm(c ),
|
| 61 |
+
nn.Dropout(dropout),
|
| 62 |
+
Linear(c , c)
|
| 63 |
+
)
|
| 64 |
+
self.use_checkpoint = use_checkpoint
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
def forward(self, hr, lr):
|
| 68 |
+
return checkpoint(self._forward, (hr, lr), self.parameters(), self.use_checkpoint)
|
| 69 |
+
|
| 70 |
+
def _forward(self, hr, lr):
|
| 71 |
+
res = hr
|
| 72 |
+
hr = self.kv_mapper(rearrange(hr, 'b c h w -> b (h w ) c'))
|
| 73 |
+
lr_fuse = self.attention(self.norm(lr), hr, self_attn=False) + lr
|
| 74 |
+
|
| 75 |
+
lr_fuse = torch.nn.functional.interpolate(lr_fuse.float(), res.shape[2:])
|
| 76 |
+
#print('in line 65', lr_fuse.shape, res.shape)
|
| 77 |
+
media = torch.cat((self.depthwise(lr_fuse), res), dim=1)
|
| 78 |
+
out = self.channelwise(media.permute(0,2,3,1)).permute(0,3,1,2) + res
|
| 79 |
+
|
| 80 |
+
return out
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
class Attention2D(nn.Module):
|
| 86 |
+
def __init__(self, c, nhead, dropout=0.0):
|
| 87 |
+
super().__init__()
|
| 88 |
+
self.attn = nn.MultiheadAttention(c, nhead, dropout=dropout, bias=True, batch_first=True)
|
| 89 |
+
|
| 90 |
+
def forward(self, x, kv, self_attn=False):
|
| 91 |
+
orig_shape = x.shape
|
| 92 |
+
x = x.view(x.size(0), x.size(1), -1).permute(0, 2, 1) # Bx4xHxW -> Bx(HxW)x4
|
| 93 |
+
if self_attn:
|
| 94 |
+
#print('in line 23 algong self att ', kv.shape, x.shape)
|
| 95 |
+
|
| 96 |
+
kv = torch.cat([x, kv], dim=1)
|
| 97 |
+
#if x.shape[1] > 48 * 48 and not self.training:
|
| 98 |
+
# x = x * math.sqrt(math.log(x.shape[1] , 24*24))
|
| 99 |
+
|
| 100 |
+
x = self.attn(x, kv, kv, need_weights=False)[0]
|
| 101 |
+
x = x.permute(0, 2, 1).view(*orig_shape)
|
| 102 |
+
return x
|
| 103 |
+
class Attention2D_splitpatch(nn.Module):
|
| 104 |
+
def __init__(self, c, nhead, dropout=0.0):
|
| 105 |
+
super().__init__()
|
| 106 |
+
self.attn = nn.MultiheadAttention(c, nhead, dropout=dropout, bias=True, batch_first=True)
|
| 107 |
+
|
| 108 |
+
def forward(self, x, kv, self_attn=False):
|
| 109 |
+
orig_shape = x.shape
|
| 110 |
+
|
| 111 |
+
#x = rearrange(x, 'b c h w -> b c (nh wh) (nw ww)', wh=24, ww=24, nh=orig_shape[-2] // 24, nh=orig_shape[-1] // 24,)
|
| 112 |
+
x = rearrange(x, 'b c (nh wh) (nw ww) -> (b nh nw) (wh ww) c', wh=24, ww=24, nh=orig_shape[-2] // 24, nw=orig_shape[-1] // 24,)
|
| 113 |
+
#print('in line 168', x.shape)
|
| 114 |
+
#x = x.view(x.size(0), x.size(1), -1).permute(0, 2, 1) # Bx4xHxW -> Bx(HxW)x4
|
| 115 |
+
if self_attn:
|
| 116 |
+
#print('in line 23 algong self att ', kv.shape, x.shape)
|
| 117 |
+
num = (orig_shape[-2] // 24) * (orig_shape[-1] // 24)
|
| 118 |
+
kv = torch.cat([x, kv.repeat(num, 1, 1)], dim=1)
|
| 119 |
+
#if x.shape[1] > 48 * 48 and not self.training:
|
| 120 |
+
# x = x * math.sqrt(math.log(x.shape[1] / math.sqrt(16), 24*24))
|
| 121 |
+
|
| 122 |
+
x = self.attn(x, kv, kv, need_weights=False)[0]
|
| 123 |
+
x = rearrange(x, ' (b nh nw) (wh ww) c -> b c (nh wh) (nw ww)', b=orig_shape[0], wh=24, ww=24, nh=orig_shape[-2] // 24, nw=orig_shape[-1] // 24)
|
| 124 |
+
#x = x.permute(0, 2, 1).view(*orig_shape)
|
| 125 |
+
|
| 126 |
+
return x
|
| 127 |
+
class Attention2D_extra(nn.Module):
|
| 128 |
+
def __init__(self, c, nhead, dropout=0.0):
|
| 129 |
+
super().__init__()
|
| 130 |
+
self.attn = nn.MultiheadAttention(c, nhead, dropout=dropout, bias=True, batch_first=True)
|
| 131 |
+
|
| 132 |
+
def forward(self, x, kv, extra_emb=None, self_attn=False):
|
| 133 |
+
orig_shape = x.shape
|
| 134 |
+
x = x.view(x.size(0), x.size(1), -1).permute(0, 2, 1) # Bx4xHxW -> Bx(HxW)x4
|
| 135 |
+
num_x = x.shape[1]
|
| 136 |
+
|
| 137 |
+
|
| 138 |
+
if extra_emb is not None:
|
| 139 |
+
ori_extra_shape = extra_emb.shape
|
| 140 |
+
extra_emb = extra_emb.view(extra_emb.size(0), extra_emb.size(1), -1).permute(0, 2, 1)
|
| 141 |
+
x = torch.cat((x, extra_emb), dim=1)
|
| 142 |
+
if self_attn:
|
| 143 |
+
#print('in line 23 algong self att ', kv.shape, x.shape)
|
| 144 |
+
kv = torch.cat([x, kv], dim=1)
|
| 145 |
+
x = self.attn(x, kv, kv, need_weights=False)[0]
|
| 146 |
+
img = x[:, :num_x, :].permute(0, 2, 1).view(*orig_shape)
|
| 147 |
+
if extra_emb is not None:
|
| 148 |
+
fix = x[:, num_x:, :].permute(0, 2, 1).view(*ori_extra_shape)
|
| 149 |
+
return img, fix
|
| 150 |
+
else:
|
| 151 |
+
return img
|
| 152 |
+
class AttnBlock_extraq(nn.Module):
|
| 153 |
+
def __init__(self, c, c_cond, nhead, self_attn=True, dropout=0.0):
|
| 154 |
+
super().__init__()
|
| 155 |
+
self.self_attn = self_attn
|
| 156 |
+
self.norm = LayerNorm2d(c, elementwise_affine=False, eps=1e-6)
|
| 157 |
+
#self.norm2 = LayerNorm2d(c, elementwise_affine=False, eps=1e-6)
|
| 158 |
+
self.attention = Attention2D_extra(c, nhead, dropout)
|
| 159 |
+
self.kv_mapper = nn.Sequential(
|
| 160 |
+
nn.SiLU(),
|
| 161 |
+
Linear(c_cond, c)
|
| 162 |
+
)
|
| 163 |
+
# norm2 initialization in generator in init extra parameter
|
| 164 |
+
def forward(self, x, kv, extra_emb=None):
|
| 165 |
+
#print('in line 84', x.shape, kv.shape, self.self_attn, extra_emb if extra_emb is None else extra_emb.shape)
|
| 166 |
+
#in line 84 torch.Size([1, 1536, 32, 32]) torch.Size([1, 85, 1536]) True None
|
| 167 |
+
#if extra_emb is not None:
|
| 168 |
+
|
| 169 |
+
kv = self.kv_mapper(kv)
|
| 170 |
+
if extra_emb is not None:
|
| 171 |
+
res_x, res_extra = self.attention(self.norm(x), kv, extra_emb=self.norm2(extra_emb), self_attn=self.self_attn)
|
| 172 |
+
x = x + res_x
|
| 173 |
+
extra_emb = extra_emb + res_extra
|
| 174 |
+
return x, extra_emb
|
| 175 |
+
else:
|
| 176 |
+
x = x + self.attention(self.norm(x), kv, self_attn=self.self_attn)
|
| 177 |
+
return x
|
| 178 |
+
class AttnBlock_latent2ex(nn.Module):
|
| 179 |
+
def __init__(self, c, c_cond, nhead, self_attn=True, dropout=0.0):
|
| 180 |
+
super().__init__()
|
| 181 |
+
self.self_attn = self_attn
|
| 182 |
+
self.norm = LayerNorm2d(c, elementwise_affine=False, eps=1e-6)
|
| 183 |
+
self.attention = Attention2D(c, nhead, dropout)
|
| 184 |
+
self.kv_mapper = nn.Sequential(
|
| 185 |
+
nn.SiLU(),
|
| 186 |
+
Linear(c_cond, c)
|
| 187 |
+
)
|
| 188 |
+
|
| 189 |
+
def forward(self, x, kv):
|
| 190 |
+
#print('in line 84', x.shape, kv.shape, self.self_attn)
|
| 191 |
+
kv = F.interpolate(kv.float(), x.shape[2:])
|
| 192 |
+
kv = kv.view(kv.size(0), kv.size(1), -1).permute(0, 2, 1)
|
| 193 |
+
kv = self.kv_mapper(kv)
|
| 194 |
+
x = x + self.attention(self.norm(x), kv, self_attn=self.self_attn)
|
| 195 |
+
return x
|
| 196 |
+
|
| 197 |
+
class LayerNorm2d(nn.LayerNorm):
|
| 198 |
+
def __init__(self, *args, **kwargs):
|
| 199 |
+
super().__init__(*args, **kwargs)
|
| 200 |
+
|
| 201 |
+
def forward(self, x):
|
| 202 |
+
return super().forward(x.permute(0, 2, 3, 1)).permute(0, 3, 1, 2)
|
| 203 |
+
class AttnBlock_crossbranch(nn.Module):
|
| 204 |
+
def __init__(self, attnmodule, c, c_cond, nhead, self_attn=True, dropout=0.0):
|
| 205 |
+
super().__init__()
|
| 206 |
+
self.attn = AttnBlock(c, c_cond, nhead, self_attn, dropout)
|
| 207 |
+
#print('in line 108', attnmodule.device)
|
| 208 |
+
self.attn.load_state_dict(attnmodule.state_dict())
|
| 209 |
+
self.norm1 = LayerNorm2d(c, elementwise_affine=False, eps=1e-6)
|
| 210 |
+
|
| 211 |
+
self.channelwise1 = nn.Sequential(
|
| 212 |
+
Linear(c *2, c ),
|
| 213 |
+
nn.GELU(),
|
| 214 |
+
GlobalResponseNorm(c ),
|
| 215 |
+
nn.Dropout(dropout),
|
| 216 |
+
Linear(c, c)
|
| 217 |
+
)
|
| 218 |
+
self.channelwise2 = nn.Sequential(
|
| 219 |
+
Linear(c *2, c ),
|
| 220 |
+
nn.GELU(),
|
| 221 |
+
GlobalResponseNorm(c ),
|
| 222 |
+
nn.Dropout(dropout),
|
| 223 |
+
Linear(c, c)
|
| 224 |
+
)
|
| 225 |
+
self.c = c
|
| 226 |
+
def forward(self, x, kv, main_x):
|
| 227 |
+
#print('in line 84', x.shape, kv.shape, main_x.shape, self.c)
|
| 228 |
+
|
| 229 |
+
x = self.channelwise1(torch.cat((x, F.interpolate(main_x.float(), x.shape[2:])), dim=1).permute(0, 2, 3, 1)).permute(0, 3, 1, 2) + x
|
| 230 |
+
x = self.attn(x, kv)
|
| 231 |
+
main_x = self.channelwise2(torch.cat((main_x, F.interpolate(x.float(), main_x.shape[2:])), dim=1).permute(0, 2, 3, 1)).permute(0, 3, 1, 2) + main_x
|
| 232 |
+
return main_x, x
|
| 233 |
+
|
| 234 |
+
class GlobalResponseNorm(nn.Module):
|
| 235 |
+
"from https://github.com/facebookresearch/ConvNeXt-V2/blob/3608f67cc1dae164790c5d0aead7bf2d73d9719b/models/utils.py#L105"
|
| 236 |
+
def __init__(self, dim):
|
| 237 |
+
super().__init__()
|
| 238 |
+
self.gamma = nn.Parameter(torch.zeros(1, 1, 1, dim))
|
| 239 |
+
self.beta = nn.Parameter(torch.zeros(1, 1, 1, dim))
|
| 240 |
+
|
| 241 |
+
def forward(self, x):
|
| 242 |
+
Gx = torch.norm(x, p=2, dim=(1, 2), keepdim=True)
|
| 243 |
+
Nx = Gx / (Gx.mean(dim=-1, keepdim=True) + 1e-6)
|
| 244 |
+
return self.gamma * (x * Nx) + self.beta + x
|
| 245 |
+
|
| 246 |
+
|
| 247 |
+
class ResBlock(nn.Module):
|
| 248 |
+
def __init__(self, c, c_skip=0, kernel_size=3, dropout=0.0, use_checkpoint =True): # , num_heads=4, expansion=2):
|
| 249 |
+
super().__init__()
|
| 250 |
+
self.depthwise = Conv2d(c, c, kernel_size=kernel_size, padding=kernel_size // 2, groups=c)
|
| 251 |
+
# self.depthwise = SAMBlock(c, num_heads, expansion)
|
| 252 |
+
self.norm = LayerNorm2d(c, elementwise_affine=False, eps=1e-6)
|
| 253 |
+
self.channelwise = nn.Sequential(
|
| 254 |
+
Linear(c + c_skip, c * 4),
|
| 255 |
+
nn.GELU(),
|
| 256 |
+
GlobalResponseNorm(c * 4),
|
| 257 |
+
nn.Dropout(dropout),
|
| 258 |
+
Linear(c * 4, c)
|
| 259 |
+
)
|
| 260 |
+
self.use_checkpoint = use_checkpoint
|
| 261 |
+
def forward(self, x, x_skip=None):
|
| 262 |
+
|
| 263 |
+
if x_skip is not None:
|
| 264 |
+
return checkpoint(self._forward_skip, (x, x_skip), self.parameters(), self.use_checkpoint)
|
| 265 |
+
else:
|
| 266 |
+
#print('in line 298', x.shape)
|
| 267 |
+
return checkpoint(self._forward_woskip, (x, ), self.parameters(), self.use_checkpoint)
|
| 268 |
+
|
| 269 |
+
|
| 270 |
+
|
| 271 |
+
def _forward_skip(self, x, x_skip):
|
| 272 |
+
x_res = x
|
| 273 |
+
x = self.norm(self.depthwise(x))
|
| 274 |
+
if x_skip is not None:
|
| 275 |
+
x = torch.cat([x, x_skip], dim=1)
|
| 276 |
+
x = self.channelwise(x.permute(0, 2, 3, 1)).permute(0, 3, 1, 2)
|
| 277 |
+
return x + x_res
|
| 278 |
+
def _forward_woskip(self, x):
|
| 279 |
+
x_res = x
|
| 280 |
+
x = self.norm(self.depthwise(x))
|
| 281 |
+
|
| 282 |
+
x = self.channelwise(x.permute(0, 2, 3, 1)).permute(0, 3, 1, 2)
|
| 283 |
+
return x + x_res
|
| 284 |
+
|
| 285 |
+
class AttnBlock(nn.Module):
|
| 286 |
+
def __init__(self, c, c_cond, nhead, self_attn=True, dropout=0.0, use_checkpoint=True):
|
| 287 |
+
super().__init__()
|
| 288 |
+
self.self_attn = self_attn
|
| 289 |
+
self.norm = LayerNorm2d(c, elementwise_affine=False, eps=1e-6)
|
| 290 |
+
self.attention = Attention2D(c, nhead, dropout)
|
| 291 |
+
self.kv_mapper = nn.Sequential(
|
| 292 |
+
nn.SiLU(),
|
| 293 |
+
Linear(c_cond, c)
|
| 294 |
+
)
|
| 295 |
+
self.use_checkpoint = use_checkpoint
|
| 296 |
+
def forward(self, x, kv):
|
| 297 |
+
return checkpoint(self._forward, (x, kv), self.parameters(), self.use_checkpoint)
|
| 298 |
+
def _forward(self, x, kv):
|
| 299 |
+
kv = self.kv_mapper(kv)
|
| 300 |
+
res = self.attention(self.norm(x), kv, self_attn=self.self_attn)
|
| 301 |
+
|
| 302 |
+
#print(torch.unique(res), torch.unique(x), self.self_attn)
|
| 303 |
+
#scale = math.sqrt(math.log(x.shape[-2] * x.shape[-1], 24*24))
|
| 304 |
+
x = x + res
|
| 305 |
+
|
| 306 |
+
return x
|
| 307 |
+
class AttnBlock_mytest(nn.Module):
|
| 308 |
+
def __init__(self, c, c_cond, nhead, self_attn=True, dropout=0.0):
|
| 309 |
+
super().__init__()
|
| 310 |
+
self.self_attn = self_attn
|
| 311 |
+
self.norm = LayerNorm2d(c, elementwise_affine=False, eps=1e-6)
|
| 312 |
+
self.attention = Attention2D(c, nhead, dropout)
|
| 313 |
+
self.kv_mapper = nn.Sequential(
|
| 314 |
+
nn.SiLU(),
|
| 315 |
+
nn.Linear(c_cond, c)
|
| 316 |
+
)
|
| 317 |
+
|
| 318 |
+
def forward(self, x, kv):
|
| 319 |
+
kv = self.kv_mapper(kv)
|
| 320 |
+
x = x + self.attention(self.norm(x), kv, self_attn=self.self_attn)
|
| 321 |
+
return x
|
| 322 |
+
|
| 323 |
+
class FeedForwardBlock(nn.Module):
|
| 324 |
+
def __init__(self, c, dropout=0.0):
|
| 325 |
+
super().__init__()
|
| 326 |
+
self.norm = LayerNorm2d(c, elementwise_affine=False, eps=1e-6)
|
| 327 |
+
self.channelwise = nn.Sequential(
|
| 328 |
+
Linear(c, c * 4),
|
| 329 |
+
nn.GELU(),
|
| 330 |
+
GlobalResponseNorm(c * 4),
|
| 331 |
+
nn.Dropout(dropout),
|
| 332 |
+
Linear(c * 4, c)
|
| 333 |
+
)
|
| 334 |
+
|
| 335 |
+
def forward(self, x):
|
| 336 |
+
x = x + self.channelwise(self.norm(x).permute(0, 2, 3, 1)).permute(0, 3, 1, 2)
|
| 337 |
+
return x
|
| 338 |
+
|
| 339 |
+
|
| 340 |
+
class TimestepBlock(nn.Module):
|
| 341 |
+
def __init__(self, c, c_timestep, conds=['sca'], use_checkpoint=True):
|
| 342 |
+
super().__init__()
|
| 343 |
+
self.mapper = Linear(c_timestep, c * 2)
|
| 344 |
+
self.conds = conds
|
| 345 |
+
for cname in conds:
|
| 346 |
+
setattr(self, f"mapper_{cname}", Linear(c_timestep, c * 2))
|
| 347 |
+
|
| 348 |
+
self.use_checkpoint = use_checkpoint
|
| 349 |
+
def forward(self, x, t):
|
| 350 |
+
return checkpoint(self._forward, (x, t), self.parameters(), self.use_checkpoint)
|
| 351 |
+
|
| 352 |
+
def _forward(self, x, t):
|
| 353 |
+
#print('in line 284', x.shape, t.shape, self.conds)
|
| 354 |
+
#in line 284 torch.Size([4, 2048, 19, 29]) torch.Size([4, 192]) ['sca', 'crp']
|
| 355 |
+
t = t.chunk(len(self.conds) + 1, dim=1)
|
| 356 |
+
a, b = self.mapper(t[0])[:, :, None, None].chunk(2, dim=1)
|
| 357 |
+
for i, c in enumerate(self.conds):
|
| 358 |
+
ac, bc = getattr(self, f"mapper_{c}")(t[i + 1])[:, :, None, None].chunk(2, dim=1)
|
| 359 |
+
a, b = a + ac, b + bc
|
| 360 |
+
return x * (1 + a) + b
|
modules/controlnet.py
ADDED
|
@@ -0,0 +1,349 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torchvision
|
| 2 |
+
import torch
|
| 3 |
+
from torch import nn
|
| 4 |
+
import numpy as np
|
| 5 |
+
import kornia
|
| 6 |
+
import cv2
|
| 7 |
+
from core.utils import load_or_fail
|
| 8 |
+
#from insightface.app.common import Face
|
| 9 |
+
from .effnet import EfficientNetEncoder
|
| 10 |
+
from .cnet_modules.pidinet import PidiNetDetector
|
| 11 |
+
from .cnet_modules.inpainting.saliency_model import MicroResNet
|
| 12 |
+
#from .cnet_modules.face_id.arcface import FaceDetector, ArcFaceRecognizer
|
| 13 |
+
from .common import LayerNorm2d
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
class CNetResBlock(nn.Module):
|
| 17 |
+
def __init__(self, c):
|
| 18 |
+
super().__init__()
|
| 19 |
+
self.blocks = nn.Sequential(
|
| 20 |
+
LayerNorm2d(c),
|
| 21 |
+
nn.GELU(),
|
| 22 |
+
nn.Conv2d(c, c, kernel_size=3, padding=1),
|
| 23 |
+
LayerNorm2d(c),
|
| 24 |
+
nn.GELU(),
|
| 25 |
+
nn.Conv2d(c, c, kernel_size=3, padding=1),
|
| 26 |
+
)
|
| 27 |
+
|
| 28 |
+
def forward(self, x):
|
| 29 |
+
return x + self.blocks(x)
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
class ControlNet(nn.Module):
|
| 33 |
+
def __init__(self, c_in=3, c_proj=2048, proj_blocks=None, bottleneck_mode=None):
|
| 34 |
+
super().__init__()
|
| 35 |
+
if bottleneck_mode is None:
|
| 36 |
+
bottleneck_mode = 'effnet'
|
| 37 |
+
self.proj_blocks = proj_blocks
|
| 38 |
+
if bottleneck_mode == 'effnet':
|
| 39 |
+
embd_channels = 1280
|
| 40 |
+
#self.backbone = torchvision.models.efficientnet_v2_s(weights='DEFAULT').features.eval()
|
| 41 |
+
self.backbone = torchvision.models.efficientnet_v2_s().features.eval()
|
| 42 |
+
if c_in != 3:
|
| 43 |
+
in_weights = self.backbone[0][0].weight.data
|
| 44 |
+
self.backbone[0][0] = nn.Conv2d(c_in, 24, kernel_size=3, stride=2, bias=False)
|
| 45 |
+
if c_in > 3:
|
| 46 |
+
nn.init.constant_(self.backbone[0][0].weight, 0)
|
| 47 |
+
self.backbone[0][0].weight.data[:, :3] = in_weights[:, :3].clone()
|
| 48 |
+
else:
|
| 49 |
+
self.backbone[0][0].weight.data = in_weights[:, :c_in].clone()
|
| 50 |
+
elif bottleneck_mode == 'simple':
|
| 51 |
+
embd_channels = c_in
|
| 52 |
+
self.backbone = nn.Sequential(
|
| 53 |
+
nn.Conv2d(embd_channels, embd_channels * 4, kernel_size=3, padding=1),
|
| 54 |
+
nn.LeakyReLU(0.2, inplace=True),
|
| 55 |
+
nn.Conv2d(embd_channels * 4, embd_channels, kernel_size=3, padding=1),
|
| 56 |
+
)
|
| 57 |
+
elif bottleneck_mode == 'large':
|
| 58 |
+
self.backbone = nn.Sequential(
|
| 59 |
+
nn.Conv2d(c_in, 4096 * 4, kernel_size=1),
|
| 60 |
+
nn.LeakyReLU(0.2, inplace=True),
|
| 61 |
+
nn.Conv2d(4096 * 4, 1024, kernel_size=1),
|
| 62 |
+
*[CNetResBlock(1024) for _ in range(8)],
|
| 63 |
+
nn.Conv2d(1024, 1280, kernel_size=1),
|
| 64 |
+
)
|
| 65 |
+
embd_channels = 1280
|
| 66 |
+
else:
|
| 67 |
+
raise ValueError(f'Unknown bottleneck mode: {bottleneck_mode}')
|
| 68 |
+
self.projections = nn.ModuleList()
|
| 69 |
+
for _ in range(len(proj_blocks)):
|
| 70 |
+
self.projections.append(nn.Sequential(
|
| 71 |
+
nn.Conv2d(embd_channels, embd_channels, kernel_size=1, bias=False),
|
| 72 |
+
nn.LeakyReLU(0.2, inplace=True),
|
| 73 |
+
nn.Conv2d(embd_channels, c_proj, kernel_size=1, bias=False),
|
| 74 |
+
))
|
| 75 |
+
nn.init.constant_(self.projections[-1][-1].weight, 0) # zero output projection
|
| 76 |
+
|
| 77 |
+
def forward(self, x):
|
| 78 |
+
x = self.backbone(x)
|
| 79 |
+
proj_outputs = [None for _ in range(max(self.proj_blocks) + 1)]
|
| 80 |
+
for i, idx in enumerate(self.proj_blocks):
|
| 81 |
+
proj_outputs[idx] = self.projections[i](x)
|
| 82 |
+
return proj_outputs
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
class ControlNetDeliverer():
|
| 86 |
+
def __init__(self, controlnet_projections):
|
| 87 |
+
self.controlnet_projections = controlnet_projections
|
| 88 |
+
self.restart()
|
| 89 |
+
|
| 90 |
+
def restart(self):
|
| 91 |
+
self.idx = 0
|
| 92 |
+
return self
|
| 93 |
+
|
| 94 |
+
def __call__(self):
|
| 95 |
+
if self.idx < len(self.controlnet_projections):
|
| 96 |
+
output = self.controlnet_projections[self.idx]
|
| 97 |
+
else:
|
| 98 |
+
output = None
|
| 99 |
+
self.idx += 1
|
| 100 |
+
return output
|
| 101 |
+
|
| 102 |
+
|
| 103 |
+
# CONTROLNET FILTERS ----------------------------------------------------
|
| 104 |
+
|
| 105 |
+
class BaseFilter():
|
| 106 |
+
def __init__(self, device):
|
| 107 |
+
self.device = device
|
| 108 |
+
|
| 109 |
+
def num_channels(self):
|
| 110 |
+
return 3
|
| 111 |
+
|
| 112 |
+
def __call__(self, x):
|
| 113 |
+
return x
|
| 114 |
+
|
| 115 |
+
|
| 116 |
+
class CannyFilter(BaseFilter):
|
| 117 |
+
def __init__(self, device, resize=224):
|
| 118 |
+
super().__init__(device)
|
| 119 |
+
self.resize = resize
|
| 120 |
+
|
| 121 |
+
def num_channels(self):
|
| 122 |
+
return 1
|
| 123 |
+
|
| 124 |
+
def __call__(self, x):
|
| 125 |
+
orig_size = x.shape[-2:]
|
| 126 |
+
if self.resize is not None:
|
| 127 |
+
x = nn.functional.interpolate(x, size=(self.resize, self.resize), mode='bilinear')
|
| 128 |
+
edges = [cv2.Canny(x[i].mul(255).permute(1, 2, 0).cpu().numpy().astype(np.uint8), 100, 200) for i in range(len(x))]
|
| 129 |
+
edges = torch.stack([torch.tensor(e).div(255).unsqueeze(0) for e in edges], dim=0)
|
| 130 |
+
if self.resize is not None:
|
| 131 |
+
edges = nn.functional.interpolate(edges, size=orig_size, mode='bilinear')
|
| 132 |
+
return edges
|
| 133 |
+
|
| 134 |
+
|
| 135 |
+
class QRFilter(BaseFilter):
|
| 136 |
+
def __init__(self, device, resize=224, blobify=True, dilation_kernels=[3, 5, 7], blur_kernels=[15]):
|
| 137 |
+
super().__init__(device)
|
| 138 |
+
self.resize = resize
|
| 139 |
+
self.blobify = blobify
|
| 140 |
+
self.dilation_kernels = dilation_kernels
|
| 141 |
+
self.blur_kernels = blur_kernels
|
| 142 |
+
|
| 143 |
+
def num_channels(self):
|
| 144 |
+
return 1
|
| 145 |
+
|
| 146 |
+
def __call__(self, x):
|
| 147 |
+
x = x.to(self.device)
|
| 148 |
+
orig_size = x.shape[-2:]
|
| 149 |
+
if self.resize is not None:
|
| 150 |
+
x = nn.functional.interpolate(x, size=(self.resize, self.resize), mode='bilinear')
|
| 151 |
+
|
| 152 |
+
x = kornia.color.rgb_to_hsv(x)[:, -1:]
|
| 153 |
+
# blobify
|
| 154 |
+
if self.blobify:
|
| 155 |
+
d_kernel = np.random.choice(self.dilation_kernels)
|
| 156 |
+
d_blur = np.random.choice(self.blur_kernels)
|
| 157 |
+
if d_blur > 0:
|
| 158 |
+
x = torchvision.transforms.GaussianBlur(d_blur)(x)
|
| 159 |
+
if d_kernel > 0:
|
| 160 |
+
blob_mask = ((torch.linspace(-0.5, 0.5, d_kernel).pow(2)[None] + torch.linspace(-0.5, 0.5,
|
| 161 |
+
d_kernel).pow(2)[:,
|
| 162 |
+
None]) < 0.3).float().to(self.device)
|
| 163 |
+
x = kornia.morphology.dilation(x, blob_mask)
|
| 164 |
+
x = kornia.morphology.erosion(x, blob_mask)
|
| 165 |
+
# mask
|
| 166 |
+
vmax, vmin = x.amax(dim=[2, 3], keepdim=True)[0], x.amin(dim=[2, 3], keepdim=True)[0]
|
| 167 |
+
th = (vmax - vmin) * 0.33
|
| 168 |
+
high_brightness, low_brightness = (x > (vmax - th)).float(), (x < (vmin + th)).float()
|
| 169 |
+
mask = (torch.ones_like(x) - low_brightness + high_brightness) * 0.5
|
| 170 |
+
|
| 171 |
+
if self.resize is not None:
|
| 172 |
+
mask = nn.functional.interpolate(mask, size=orig_size, mode='bilinear')
|
| 173 |
+
return mask.cpu()
|
| 174 |
+
|
| 175 |
+
|
| 176 |
+
class PidiFilter(BaseFilter):
|
| 177 |
+
def __init__(self, device, resize=224, dilation_kernels=[0, 3, 5, 7, 9], binarize=True):
|
| 178 |
+
super().__init__(device)
|
| 179 |
+
self.resize = resize
|
| 180 |
+
self.model = PidiNetDetector(device)
|
| 181 |
+
self.dilation_kernels = dilation_kernels
|
| 182 |
+
self.binarize = binarize
|
| 183 |
+
|
| 184 |
+
def num_channels(self):
|
| 185 |
+
return 1
|
| 186 |
+
|
| 187 |
+
def __call__(self, x):
|
| 188 |
+
x = x.to(self.device)
|
| 189 |
+
orig_size = x.shape[-2:]
|
| 190 |
+
if self.resize is not None:
|
| 191 |
+
x = nn.functional.interpolate(x, size=(self.resize, self.resize), mode='bilinear')
|
| 192 |
+
|
| 193 |
+
x = self.model(x)
|
| 194 |
+
d_kernel = np.random.choice(self.dilation_kernels)
|
| 195 |
+
if d_kernel > 0:
|
| 196 |
+
blob_mask = ((torch.linspace(-0.5, 0.5, d_kernel).pow(2)[None] + torch.linspace(-0.5, 0.5, d_kernel).pow(2)[
|
| 197 |
+
:, None]) < 0.3).float().to(self.device)
|
| 198 |
+
x = kornia.morphology.dilation(x, blob_mask)
|
| 199 |
+
if self.binarize:
|
| 200 |
+
th = np.random.uniform(0.05, 0.7)
|
| 201 |
+
x = (x > th).float()
|
| 202 |
+
|
| 203 |
+
if self.resize is not None:
|
| 204 |
+
x = nn.functional.interpolate(x, size=orig_size, mode='bilinear')
|
| 205 |
+
return x.cpu()
|
| 206 |
+
|
| 207 |
+
|
| 208 |
+
class SRFilter(BaseFilter):
|
| 209 |
+
def __init__(self, device, scale_factor=1 / 4):
|
| 210 |
+
super().__init__(device)
|
| 211 |
+
self.scale_factor = scale_factor
|
| 212 |
+
|
| 213 |
+
def num_channels(self):
|
| 214 |
+
return 3
|
| 215 |
+
|
| 216 |
+
def __call__(self, x):
|
| 217 |
+
x = torch.nn.functional.interpolate(x.clone(), scale_factor=self.scale_factor, mode="nearest")
|
| 218 |
+
return torch.nn.functional.interpolate(x, scale_factor=1 / self.scale_factor, mode="nearest")
|
| 219 |
+
|
| 220 |
+
|
| 221 |
+
class SREffnetFilter(BaseFilter):
|
| 222 |
+
def __init__(self, device, scale_factor=1/2):
|
| 223 |
+
super().__init__(device)
|
| 224 |
+
self.scale_factor = scale_factor
|
| 225 |
+
|
| 226 |
+
self.effnet_preprocess = torchvision.transforms.Compose([
|
| 227 |
+
torchvision.transforms.Normalize(
|
| 228 |
+
mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225)
|
| 229 |
+
)
|
| 230 |
+
])
|
| 231 |
+
|
| 232 |
+
self.effnet = EfficientNetEncoder().to(self.device)
|
| 233 |
+
effnet_checkpoint = load_or_fail("models/effnet_encoder.safetensors")
|
| 234 |
+
self.effnet.load_state_dict(effnet_checkpoint)
|
| 235 |
+
self.effnet.eval().requires_grad_(False)
|
| 236 |
+
|
| 237 |
+
def num_channels(self):
|
| 238 |
+
return 16
|
| 239 |
+
|
| 240 |
+
def __call__(self, x):
|
| 241 |
+
x = torch.nn.functional.interpolate(x.clone(), scale_factor=self.scale_factor, mode="nearest")
|
| 242 |
+
with torch.no_grad():
|
| 243 |
+
effnet_embedding = self.effnet(self.effnet_preprocess(x.to(self.device))).cpu()
|
| 244 |
+
effnet_embedding = torch.nn.functional.interpolate(effnet_embedding, scale_factor=1/self.scale_factor, mode="nearest")
|
| 245 |
+
upscaled_image = torch.nn.functional.interpolate(x, scale_factor=1/self.scale_factor, mode="nearest")
|
| 246 |
+
return effnet_embedding, upscaled_image
|
| 247 |
+
|
| 248 |
+
|
| 249 |
+
class InpaintFilter(BaseFilter):
|
| 250 |
+
def __init__(self, device, thresold=[0.04, 0.4], p_outpaint=0.4):
|
| 251 |
+
super().__init__(device)
|
| 252 |
+
self.saliency_model = MicroResNet().eval().requires_grad_(False).to(device)
|
| 253 |
+
self.saliency_model.load_state_dict(load_or_fail("modules/cnet_modules/inpainting/saliency_model.pt"))
|
| 254 |
+
self.thresold = thresold
|
| 255 |
+
self.p_outpaint = p_outpaint
|
| 256 |
+
|
| 257 |
+
def num_channels(self):
|
| 258 |
+
return 4
|
| 259 |
+
|
| 260 |
+
def __call__(self, x, mask=None, threshold=None, outpaint=None):
|
| 261 |
+
x = x.to(self.device)
|
| 262 |
+
resized_x = torchvision.transforms.functional.resize(x, 240, antialias=True)
|
| 263 |
+
if threshold is None:
|
| 264 |
+
threshold = np.random.uniform(self.thresold[0], self.thresold[1])
|
| 265 |
+
if mask is None:
|
| 266 |
+
saliency_map = self.saliency_model(resized_x) > threshold
|
| 267 |
+
if outpaint is None:
|
| 268 |
+
if np.random.rand() < self.p_outpaint:
|
| 269 |
+
saliency_map = ~saliency_map
|
| 270 |
+
else:
|
| 271 |
+
if outpaint:
|
| 272 |
+
saliency_map = ~saliency_map
|
| 273 |
+
interpolated_saliency_map = torch.nn.functional.interpolate(saliency_map.float(), size=x.shape[2:], mode="nearest")
|
| 274 |
+
saliency_map = torchvision.transforms.functional.gaussian_blur(interpolated_saliency_map, 141) > 0.5
|
| 275 |
+
inpainted_images = torch.where(saliency_map, torch.ones_like(x), x)
|
| 276 |
+
mask = torch.nn.functional.interpolate(saliency_map.float(), size=inpainted_images.shape[2:], mode="nearest")
|
| 277 |
+
else:
|
| 278 |
+
mask = mask.to(self.device)
|
| 279 |
+
inpainted_images = torch.where(mask, torch.ones_like(x), x)
|
| 280 |
+
c_inpaint = torch.cat([inpainted_images, mask], dim=1)
|
| 281 |
+
return c_inpaint.cpu()
|
| 282 |
+
|
| 283 |
+
|
| 284 |
+
# IDENTITY
|
| 285 |
+
'''
|
| 286 |
+
class IdentityFilter(BaseFilter):
|
| 287 |
+
def __init__(self, device, max_faces=4, p_drop=0.05, p_full=0.3):
|
| 288 |
+
detector_path = 'modules/cnet_modules/face_id/models/buffalo_l/det_10g.onnx'
|
| 289 |
+
recognizer_path = 'modules/cnet_modules/face_id/models/buffalo_l/w600k_r50.onnx'
|
| 290 |
+
|
| 291 |
+
super().__init__(device)
|
| 292 |
+
self.max_faces = max_faces
|
| 293 |
+
self.p_drop = p_drop
|
| 294 |
+
self.p_full = p_full
|
| 295 |
+
|
| 296 |
+
self.detector = FaceDetector(detector_path, device=device)
|
| 297 |
+
self.recognizer = ArcFaceRecognizer(recognizer_path, device=device)
|
| 298 |
+
|
| 299 |
+
self.id_colors = torch.tensor([
|
| 300 |
+
[1.0, 0.0, 0.0], # RED
|
| 301 |
+
[0.0, 1.0, 0.0], # GREEN
|
| 302 |
+
[0.0, 0.0, 1.0], # BLUE
|
| 303 |
+
[1.0, 0.0, 1.0], # PURPLE
|
| 304 |
+
[0.0, 1.0, 1.0], # CYAN
|
| 305 |
+
[1.0, 1.0, 0.0], # YELLOW
|
| 306 |
+
[0.5, 0.0, 0.0], # DARK RED
|
| 307 |
+
[0.0, 0.5, 0.0], # DARK GREEN
|
| 308 |
+
[0.0, 0.0, 0.5], # DARK BLUE
|
| 309 |
+
[0.5, 0.0, 0.5], # DARK PURPLE
|
| 310 |
+
[0.0, 0.5, 0.5], # DARK CYAN
|
| 311 |
+
[0.5, 0.5, 0.0], # DARK YELLOW
|
| 312 |
+
])
|
| 313 |
+
|
| 314 |
+
def num_channels(self):
|
| 315 |
+
return 512
|
| 316 |
+
|
| 317 |
+
def get_faces(self, image):
|
| 318 |
+
npimg = image.permute(1, 2, 0).mul(255).to(device="cpu", dtype=torch.uint8).cpu().numpy()
|
| 319 |
+
bgr = cv2.cvtColor(npimg, cv2.COLOR_RGB2BGR)
|
| 320 |
+
bboxes, kpss = self.detector.detect(bgr, max_num=self.max_faces)
|
| 321 |
+
N = len(bboxes)
|
| 322 |
+
ids = torch.zeros((N, 512), dtype=torch.float32)
|
| 323 |
+
for i in range(N):
|
| 324 |
+
face = Face(bbox=bboxes[i, :4], kps=kpss[i], det_score=bboxes[i, 4])
|
| 325 |
+
ids[i, :] = self.recognizer.get(bgr, face)
|
| 326 |
+
tbboxes = torch.tensor(bboxes[:, :4], dtype=torch.int)
|
| 327 |
+
|
| 328 |
+
ids = ids / torch.linalg.norm(ids, dim=1, keepdim=True)
|
| 329 |
+
return tbboxes, ids # returns bounding boxes (N x 4) and ID vectors (N x 512)
|
| 330 |
+
|
| 331 |
+
def __call__(self, x):
|
| 332 |
+
visual_aid = x.clone().cpu()
|
| 333 |
+
face_mtx = torch.zeros(x.size(0), 512, x.size(-2) // 32, x.size(-1) // 32)
|
| 334 |
+
|
| 335 |
+
for i in range(x.size(0)):
|
| 336 |
+
bounding_boxes, ids = self.get_faces(x[i])
|
| 337 |
+
for j in range(bounding_boxes.size(0)):
|
| 338 |
+
if np.random.rand() > self.p_drop:
|
| 339 |
+
sx, sy, ex, ey = (bounding_boxes[j] / 32).clamp(min=0).round().int().tolist()
|
| 340 |
+
ex, ey = max(ex, sx + 1), max(ey, sy + 1)
|
| 341 |
+
if bounding_boxes.size(0) == 1 and np.random.rand() < self.p_full:
|
| 342 |
+
sx, sy, ex, ey = 0, 0, x.size(-1) // 32, x.size(-2) // 32
|
| 343 |
+
face_mtx[i, :, sy:ey, sx:ex] = ids[j:j + 1, :, None, None]
|
| 344 |
+
visual_aid[i, :, int(sy * 32):int(ey * 32), int(sx * 32):int(ex * 32)] += self.id_colors[j % 13, :,
|
| 345 |
+
None, None]
|
| 346 |
+
visual_aid[i, :, int(sy * 32):int(ey * 32), int(sx * 32):int(ex * 32)] *= 0.5
|
| 347 |
+
|
| 348 |
+
return face_mtx.to(x.device), visual_aid.to(x.device)
|
| 349 |
+
'''
|
modules/effnet.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torchvision
|
| 2 |
+
from torch import nn
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
# EfficientNet
|
| 6 |
+
class EfficientNetEncoder(nn.Module):
|
| 7 |
+
def __init__(self, c_latent=16):
|
| 8 |
+
super().__init__()
|
| 9 |
+
self.backbone = torchvision.models.efficientnet_v2_s().features.eval()
|
| 10 |
+
self.mapper = nn.Sequential(
|
| 11 |
+
nn.Conv2d(1280, c_latent, kernel_size=1, bias=False),
|
| 12 |
+
nn.BatchNorm2d(c_latent, affine=False), # then normalize them to have mean 0 and std 1
|
| 13 |
+
)
|
| 14 |
+
|
| 15 |
+
def forward(self, x):
|
| 16 |
+
return self.mapper(self.backbone(x))
|
| 17 |
+
|