Zyriix commited on
Commit
fd684da
·
verified ·
1 Parent(s): 6c75c1e

init opensource code

Browse files
.gitignore ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ __pycache__/
2
+ *.py[cod]
3
+ .env
4
+ .venv/
5
+ .DS_Store
6
+ *.local.*
7
+ /outputs
8
+ /cache
9
+ /data
10
+ /release
11
+ wandb/
12
+ *.ckpt
13
+ *.pt
14
+ *.pth
15
+ *.npz
16
+ *.pb
17
+ *.log
18
+
19
+ # Model assets distributed on Hugging Face.
20
+ !/checkpoints/*.pt
21
+ !/refs/*.npz
LICENSE ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Bowen Zheng, The Chinese University of Hong Kong, Shenzhen
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
README.md CHANGED
@@ -1,3 +1,130 @@
1
- ---
2
- license: mit
3
- ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # wAR-Tok: Learning Discrete Autoregressive Priors with Wasserstein Gradient Flow
2
+
3
+ CIFAR-10 implementation of [our paper](https://arxiv.org/abs/2605.06148).
4
+
5
+ ## Features
6
+
7
+ - Discrete image tokenization with Wasserstein gradient flow regularization.
8
+ - Transformer encoder and decoder with cross-attention, RMSNorm and GEGLU.
9
+ - Six variants: baseline, tail dropout, flow matching, FM + tail dropout, FM + tied tail dropout, and wAR-Tok.
10
+ - Two-stage training: tokenizer training followed by an autoregressive prior.
11
+ - Mixed precision, multi-GPU training and optional W&B logging.
12
+
13
+ ## Quick Start
14
+
15
+ ### Installation
16
+
17
+ Use Python 3.12 and CUDA. The training environment uses PyTorch 2.8.0 / CUDA 12.8.
18
+
19
+ ```bash
20
+ python -m venv .venv
21
+ source .venv/bin/activate
22
+ pip install -r requirements.txt
23
+ ```
24
+
25
+ ### Data Preparation
26
+
27
+ ```bash
28
+ python scripts/prepare_cifar10.py --download
29
+ ```
30
+
31
+ This prepares the CIFAR-10 training split under `data/cifar10`. Download the [FID reference](https://huggingface.co/Zyriix/wARTok/resolve/main/refs/cifar10-32x32_local.npz?download=true) to `refs/cifar10-32x32_local.npz`.
32
+
33
+ ### Training
34
+
35
+ The default configuration uses 8 GPUs, global batch size 512 and BF16. Each method has a `*_tokenizer.yaml` and a `*_ar.yaml` in [configs/cifar10](configs/cifar10): train the tokenizer for 400K steps, then the AR prior for 200K steps.
36
+
37
+ **Stage 1: Train the tokenizer**
38
+
39
+ ```bash
40
+ python train.py --config=configs/cifar10/war_tokenizer.yaml
41
+ ```
42
+
43
+ **Stage 2: Train the AR prior**
44
+
45
+ ```bash
46
+ python train.py --config=configs/cifar10/war_ar.yaml \
47
+ ae_ckpt_path=./outputs/war_tokenizer/000/ckpts/last.ckpt
48
+ ```
49
+
50
+ The AR configuration defaults to the matching tokenizer's `000/ckpts/last.ckpt`. Set `WANDB_MODE=online` to enable experiment logging.
51
+
52
+ ### Evaluation
53
+
54
+ Use the matching tokenizer configuration for reconstruction and the AR configuration for generation. FM reconstruction uses 25 Euler steps; generation uses a single decoder step. Evaluation uses FP32; generation uses AR EMA, temperature 1, CFG 0 and no top-k/top-p filtering.
55
+
56
+ **Reconstruction**
57
+
58
+ ```bash
59
+ CUDA_VISIBLE_DEVICES=0 python evaluate.py \
60
+ --config configs/cifar10/war_tokenizer.yaml --checkpoint checkpoints/war.pt \
61
+ --mode reconstruct --data data/cifar10 --num-samples 50000 \
62
+ --output outputs/war-reconstruction --fid-reference refs/cifar10-32x32_local.npz
63
+ ```
64
+
65
+ **Generation**
66
+
67
+ ```bash
68
+ CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7 torchrun --standalone --nproc-per-node=8 evaluate.py \
69
+ --config configs/cifar10/war_ar.yaml --checkpoint checkpoints/war.pt \
70
+ --num-samples 50000 --batch-size 64 --seed 42 --no-legacy \
71
+ --output outputs/war-generation --fid-reference refs/cifar10-32x32_local.npz
72
+ ```
73
+
74
+ Non-legacy mode (`--no-legacy`) uses a different seed on each GPU and generally yields [lower gFID scores](#checkpoints), without changing the relative ranking of the methods or the conclusions supported by the results.
75
+
76
+ To evaluate all six checkpoints with `--no-legacy`:
77
+
78
+ ```bash
79
+ python scripts/evaluate_all.py --gpus 0,1,2,3,4,5,6,7 \
80
+ --output outputs/cifar10-nonlegacy
81
+ ```
82
+
83
+ Use `--methods baseline war` for a subset or `--resume` to reuse completed evaluations.
84
+
85
+ ### Exporting Checkpoints
86
+
87
+ ```bash
88
+ python scripts/export_checkpoint.py --source path/to/raw-ar.ckpt \
89
+ --config configs/cifar10/war_ar.yaml --output checkpoints/war.pt
90
+ ```
91
+
92
+ The export includes tokenizer and raw/EMA AR weights without optimizer states.
93
+
94
+ ## Checkpoints
95
+
96
+ Name checkpoints after their configuration prefix (e.g. `baseline_ar.yaml` → `checkpoints/baseline.pt`, `war_ar.yaml` → `checkpoints/war.pt`); each includes the tokenizer and raw/EMA AR weights. Hugging Face repository: [wARTok](https://huggingface.co/Zyriix/wARTok).
97
+
98
+ | Method | rFID | Historical gFID | Non-legacy gFID | Download |
99
+ |---|---:|---:|---:|---|
100
+ | [Baseline](configs/cifar10/baseline_ar.yaml) | 2.3015 | 52.5343 | 49.4191 | [Download](https://huggingface.co/Zyriix/wARTok/resolve/main/checkpoints/baseline.pt?download=true) |
101
+ | [Tail Dropout](configs/cifar10/tail_dropout_ar.yaml) | 3.3567 | 23.3713 | 19.8997 | [Download](https://huggingface.co/Zyriix/wARTok/resolve/main/checkpoints/tail_dropout.pt?download=true) |
102
+ | [FM](configs/cifar10/flow_matching_ar.yaml) | 3.4194 | 38.6309 | 35.8593 | [Download](https://huggingface.co/Zyriix/wARTok/resolve/main/checkpoints/flow_matching.pt?download=true) |
103
+ | [FM + Tail](configs/cifar10/flow_matching_tail_dropout_ar.yaml) | 4.4247 | 22.7613 | 19.0569 | [Download](https://huggingface.co/Zyriix/wARTok/resolve/main/checkpoints/flow_matching_tail_dropout.pt?download=true) |
104
+ | [FM + Tail + Tied](configs/cifar10/flow_matching_tied_tail_dropout_ar.yaml) | 6.4911 | 44.7971 | 41.1334 | [Download](https://huggingface.co/Zyriix/wARTok/resolve/main/checkpoints/flow_matching_tied_tail_dropout.pt?download=true) |
105
+ | [wAR-Tok](configs/cifar10/war_ar.yaml) | 3.0648 | 11.3059 | 7.3749 | [Download](https://huggingface.co/Zyriix/wARTok/resolve/main/checkpoints/war.pt?download=true) |
106
+
107
+ Download the [FID reference](https://huggingface.co/Zyriix/wARTok/resolve/main/refs/cifar10-32x32_local.npz?download=true) to `refs/cifar10-32x32_local.npz`.
108
+
109
+ ## Project Structure
110
+
111
+ ```text
112
+ wAR/
113
+ ├── configs/cifar10/ # Training configurations
114
+ ├── checkpoints/ # Downloaded weights
115
+ ├── scripts/ # Data preparation and evaluation tools
116
+ ├── models.py # Model architectures
117
+ ├── train.py # Tokenizer and AR training
118
+ ├── evaluate.py # Reconstruction and generation
119
+ ├── dataset.py # Data loading
120
+ ├── lpips.py # Perceptual loss
121
+ └── calc_fid.py # ADM FID evaluation
122
+ ```
123
+
124
+ ## References
125
+
126
+ This project builds on [EDM](https://github.com/NVlabs/edm), [ADM](https://github.com/openai/guided-diffusion), and [LPIPS](https://github.com/richzhang/PerceptualSimilarity).
127
+
128
+ ## License
129
+
130
+ Project-authored code is covered by [MIT](LICENSE). Third-party components retain their original licenses.
calc_fid.py ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """ADM FID, preserving the input reference archive."""
2
+ import argparse
3
+ import math
4
+ import numpy as np
5
+ import tensorflow.compat.v1 as tf
6
+ from evaluator import Evaluator
7
+
8
+
9
+ def main():
10
+ parser = argparse.ArgumentParser(description=__doc__)
11
+ parser.add_argument('--ref_batch', required=True)
12
+ parser.add_argument('--sample_batch', required=True)
13
+ parser.add_argument('--batch_size', type=int, default=64)
14
+ parser.add_argument('--write-reference-stats', help='Optional NEW cache file; never overwrites the reference')
15
+ args = parser.parse_args()
16
+ if args.batch_size < 1: parser.error('batch_size must be positive')
17
+ config = tf.ConfigProto(allow_soft_placement=True, intra_op_parallelism_threads=8, inter_op_parallelism_threads=8)
18
+ config.gpu_options.allow_growth = True
19
+ with tf.Session(config=config) as session:
20
+ evaluator = Evaluator(session, batch_size=args.batch_size)
21
+ evaluator.warmup()
22
+ ref_acts = evaluator.read_activations(args.ref_batch)
23
+ ref, spatial = evaluator.read_statistics(args.ref_batch, ref_acts)
24
+ sample_acts = evaluator.read_activations(args.sample_batch)
25
+ sample, _ = evaluator.read_statistics(args.sample_batch, sample_acts)
26
+ fid = sample.frechet_distance(ref)
27
+ if not math.isfinite(fid): raise ValueError(f'Non-finite FID: {fid}')
28
+ if args.write_reference_stats:
29
+ with open(args.write_reference_stats, 'xb') as f:
30
+ np.savez_compressed(f, mu=ref.mu, sigma=ref.sigma, mu_s=spatial.mu, sigma_s=spatial.sigma)
31
+ print(f'FID_RESULT:{fid}')
32
+
33
+
34
+ if __name__ == '__main__': main()
checkpoints/baseline.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:ec76675438aa159dfcddf8b4f153ed5f418fc8d2cb01d2f38f082ad32c3e0d85
3
+ size 887697419
checkpoints/flow_matching.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:6d9a7c13f0b199b07fec4f500296a7d5f1f815aec2d81f8635e6ee37a2d30e61
3
+ size 887699762
checkpoints/flow_matching_tail_dropout.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:043dc2f5e32100d2b531007bbdf33c72d57a721d4826672742aa1a85a871a7aa
3
+ size 887699762
checkpoints/flow_matching_tied_tail_dropout.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:490c18b0542a92a7950a370b26b3a9660597b1e1a0977c8fc593ee0bfceab728
3
+ size 887699762
checkpoints/tail_dropout.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:8c790a89b69de1d3bb55acc14f72bc46f723bbf13e5d5753a2fef1b8d201e0b3
3
+ size 887697419
checkpoints/war.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:9c58f2a9520de64e1a29f0e431be15f069b16a88d5a5938da1f36b7b049fbf5c
3
+ size 887697419
configs/cifar10/baseline_ar.yaml ADDED
@@ -0,0 +1,198 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ wandb_project: wAR
2
+ wandb_name: baseline_ar
3
+ data_dir: ./data/cifar10
4
+ xflip: true
5
+ crop_type: center
6
+ image_size: 32
7
+ num_channels: 3
8
+ num_classes: 11
9
+ patch_size: 4
10
+ use_label: false
11
+ patch_dim: 48
12
+ x_len: 64
13
+ diffusion_decoder: false
14
+ train_ae: false
15
+ train_ar: true
16
+ phases: 200000:DO_PRIOR_AR:1
17
+ batch_size: 512
18
+ eval_batch_size: 512
19
+ lr_enc: 0.00015
20
+ lr_dec: 0.00015
21
+ lr_ar: 0.00015
22
+ lr_gan_loss: 0.00015
23
+ weight_decay_enc: 0.0
24
+ weight_decay_dec: 0.0
25
+ weight_decay_ar: 0.0001
26
+ weight_decay_gan: 0.0001
27
+ lr_enc_min: 3.0e-05
28
+ lr_dec_min: 3.0e-05
29
+ lr_ar_min: 3.0e-05
30
+ lr_gan_loss_min: 3.0e-05
31
+ use_tf32: false
32
+ lr_scheduler: none
33
+ drop: 0.0
34
+ ar_drop: 0.1
35
+ grad_clip: 0.0
36
+ seed: 42
37
+ beta1: 0.5
38
+ beta2: 0.95
39
+ ar_beta1: 0.9
40
+ ar_beta2: 0.99
41
+ label_drop_prob: 0.1
42
+ precision: bf16-mixed
43
+ replace_cur_with_ema: false
44
+ num_workers: 4
45
+ devices: 8
46
+ reverse_t: true
47
+ use_noise_query: false
48
+ use_tail_dropout: false
49
+ l2_v_target: false
50
+ tail_dropout_t2k: 1.0
51
+ tied_timestep: false
52
+ time_stages: 200,400,600,800,1000
53
+ k_per_stage: 192,184,72,48,16
54
+ load_ar_model: false
55
+ load_ar_model_ema: false
56
+ prior_G_loss: false
57
+ use_l1_loss: false
58
+ use_l2_loss: false
59
+ use_lpips_loss: false
60
+ use_gan_loss: false
61
+ use_prior_loss: false
62
+ teacher_forcing_ae: false
63
+ disc_adaptive_weight: false
64
+ lpips_weight: 0.1
65
+ gan_G_weight: 0.1
66
+ prior_enc_weight: 1.0
67
+ l1_weight: 0.1
68
+ l2_weight: 1.0
69
+ gan_start: 5000
70
+ prior_start: 0
71
+ tail_dropout_start: 0
72
+ tail_dropout_p: 0.0
73
+ torch_compile: true
74
+ compile_ae: true
75
+ compile_ar: true
76
+ compile_gan: false
77
+ ar_ema_halflife: 2000
78
+ ae_ema_halflife: 0
79
+ ema_sampling: true
80
+ ema_reconstruction: false
81
+ cache_kv: true
82
+ topK: 0.0
83
+ topP: 0.0
84
+ cfg: 0.0
85
+ cfg_schedule: cosine
86
+ cfg_power: 2.75
87
+ temperature: 1.0
88
+ eval_data_dir: ./data/cifar10
89
+ eval_crop_type: center
90
+ eval_fid_ref_path: ./refs/cifar10-32x32_local.npz
91
+ save_dir: ./outputs/baseline_ar
92
+ log_freq: 25
93
+ visualize_freq: 5000
94
+ visualize_img_num: 8
95
+ ckpt_freq: 25000
96
+ eval_freq: 25000
97
+ save_ckpt: true
98
+ ar_ckpt_path: ''
99
+ ae_ckpt_path: ./outputs/baseline_tokenizer/000/ckpts/last.ckpt
100
+ resume_optimizer: false
101
+ cond_emb_dim: 32
102
+ greedy_coding_prob: 0.0
103
+ z_len: 64
104
+ z_dim: 512
105
+ vq_type: VQ
106
+ codebook_size: 1024
107
+ norm_layer: RMSNorm
108
+ ffn_type: geglu
109
+ context_type: cross_attn
110
+ hidden_dim: 512
111
+ Encoder:
112
+ input_dim: 48
113
+ output_dim: 512
114
+ cond_dim: 32
115
+ cond_input_dim: 11
116
+ max_seq_len: 64
117
+ ctx_max_seq_len: 64
118
+ dim: 512
119
+ layer_num: 10
120
+ mlp_ratio: 4.0
121
+ heads: 8
122
+ emb_dropout: 0.0
123
+ drop: 0.0
124
+ l2_norm: false
125
+ context_type: cross_attn
126
+ rope: false
127
+ abs_pos: true
128
+ causal: false
129
+ norm_layer: RMSNorm
130
+ ffn_type: geglu
131
+ Quantizer:
132
+ codebook_size: 1024
133
+ z_dim: 12
134
+ dim: 512
135
+ length: 64
136
+ cond_dim: 32
137
+ cond_affine_norm: false
138
+ vq_beta: 0.25
139
+ use_norm: true
140
+ norm_layer: l2
141
+ ffn_type: geglu
142
+ temperature: 1.0
143
+ Decoder:
144
+ input_dim: 48
145
+ ctx_input_dim: 512
146
+ output_dim: 48
147
+ cond_dim: 32
148
+ cond_input_dim: 11
149
+ max_seq_len: 64
150
+ ctx_max_seq_len: 64
151
+ dim: 512
152
+ layer_num: 10
153
+ heads: 8
154
+ emb_dropout: 0.0
155
+ drop: 0.0
156
+ l2_norm: false
157
+ context_type: cross_attn
158
+ rope: false
159
+ abs_pos: true
160
+ causal: false
161
+ norm_layer: RMSNorm
162
+ ffn_type: geglu
163
+ mlp_ratio: 4.0
164
+ zero_out: true
165
+ query_type: learnable
166
+ ARModel:
167
+ input_dim: 512
168
+ output_dim: 1024
169
+ max_seq_len: 64
170
+ cond_dim: 32
171
+ cond_input_dim: 11
172
+ dim: 512
173
+ layer_num: 13
174
+ heads: 8
175
+ emb_dropout: 0.0
176
+ drop: 0.1
177
+ l2_norm: false
178
+ context_type: none
179
+ rope: false
180
+ abs_pos: true
181
+ causal: true
182
+ out_act: false
183
+ norm_layer: RMSNorm
184
+ ffn_type: geglu
185
+ mlp_ratio: 4.0
186
+ zero_out: false
187
+ temperature: 1.0
188
+ GANLoss:
189
+ disc_loss: vanilla
190
+ disc_dim: 64
191
+ disc_type: patchgan
192
+ image_size: 32
193
+ disc_num_layers: 3
194
+ disc_in_channels: 3
195
+ gen_adv_loss: vanilla
196
+ lecam_loss_weight: 0.001
197
+ norm_type: bn
198
+ aug_prob: 1.0
configs/cifar10/baseline_tokenizer.yaml ADDED
@@ -0,0 +1,198 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ wandb_project: wAR
2
+ wandb_name: baseline_tokenizer
3
+ data_dir: ./data/cifar10
4
+ xflip: true
5
+ crop_type: center
6
+ image_size: 32
7
+ num_channels: 3
8
+ num_classes: 11
9
+ patch_size: 4
10
+ use_label: false
11
+ patch_dim: 48
12
+ x_len: 64
13
+ diffusion_decoder: false
14
+ train_ae: true
15
+ train_ar: false
16
+ phases: 400000:DO_L1-DO_L2-DO_LPIPS:1
17
+ batch_size: 512
18
+ eval_batch_size: 512
19
+ lr_enc: 0.00015
20
+ lr_dec: 0.00015
21
+ lr_ar: 0.00015
22
+ lr_gan_loss: 0.00015
23
+ weight_decay_enc: 0.0
24
+ weight_decay_dec: 0.0
25
+ weight_decay_ar: 0.0
26
+ weight_decay_gan: 0.0001
27
+ lr_enc_min: 3.0e-05
28
+ lr_dec_min: 3.0e-05
29
+ lr_ar_min: 3.0e-05
30
+ lr_gan_loss_min: 3.0e-05
31
+ use_tf32: false
32
+ lr_scheduler: none
33
+ drop: 0.0
34
+ ar_drop: 0.0
35
+ grad_clip: 0.0
36
+ seed: 42
37
+ beta1: 0.5
38
+ beta2: 0.95
39
+ ar_beta1: 0.5
40
+ ar_beta2: 0.95
41
+ label_drop_prob: 0.1
42
+ precision: bf16-mixed
43
+ replace_cur_with_ema: false
44
+ num_workers: 4
45
+ devices: 8
46
+ reverse_t: false
47
+ use_noise_query: false
48
+ use_tail_dropout: false
49
+ l2_v_target: false
50
+ tail_dropout_t2k: 1.0
51
+ tied_timestep: false
52
+ time_stages: 200,400,600,800,1000
53
+ k_per_stage: 192,184,72,48,16
54
+ load_ar_model: false
55
+ load_ar_model_ema: false
56
+ prior_G_loss: false
57
+ use_l1_loss: true
58
+ use_l2_loss: true
59
+ use_lpips_loss: true
60
+ use_gan_loss: false
61
+ use_prior_loss: false
62
+ teacher_forcing_ae: false
63
+ disc_adaptive_weight: false
64
+ lpips_weight: 0.1
65
+ gan_G_weight: 0.1
66
+ prior_enc_weight: 1.0
67
+ l1_weight: 0.1
68
+ l2_weight: 1.0
69
+ gan_start: 5000
70
+ prior_start: 0
71
+ tail_dropout_start: 0
72
+ tail_dropout_p: 0
73
+ torch_compile: true
74
+ compile_ae: true
75
+ compile_ar: true
76
+ compile_gan: false
77
+ ar_ema_halflife: 2500
78
+ ae_ema_halflife: 1000
79
+ ema_sampling: false
80
+ ema_reconstruction: false
81
+ cache_kv: true
82
+ topK: 0.0
83
+ topP: 0.0
84
+ cfg: 0.0
85
+ cfg_schedule: cosine
86
+ cfg_power: 2.75
87
+ temperature: 1.0
88
+ eval_data_dir: ./data/cifar10
89
+ eval_crop_type: center
90
+ eval_fid_ref_path: ./refs/cifar10-32x32_local.npz
91
+ save_dir: ./outputs/baseline_tokenizer
92
+ log_freq: 25
93
+ visualize_freq: 5000
94
+ visualize_img_num: 8
95
+ ckpt_freq: 25000
96
+ eval_freq: 25000
97
+ save_ckpt: true
98
+ ar_ckpt_path: ''
99
+ ae_ckpt_path: ''
100
+ resume_optimizer: false
101
+ cond_emb_dim: 32
102
+ greedy_coding_prob: 0.0
103
+ z_len: 64
104
+ z_dim: 512
105
+ vq_type: VQ
106
+ codebook_size: 1024
107
+ norm_layer: RMSNorm
108
+ ffn_type: geglu
109
+ context_type: cross_attn
110
+ hidden_dim: 512
111
+ Encoder:
112
+ input_dim: 48
113
+ output_dim: 512
114
+ cond_dim: 32
115
+ cond_input_dim: 11
116
+ max_seq_len: 64
117
+ ctx_max_seq_len: 64
118
+ dim: 512
119
+ layer_num: 10
120
+ mlp_ratio: 4.0
121
+ heads: 8
122
+ emb_dropout: 0.0
123
+ drop: 0.0
124
+ l2_norm: false
125
+ context_type: cross_attn
126
+ rope: false
127
+ abs_pos: true
128
+ causal: false
129
+ norm_layer: RMSNorm
130
+ ffn_type: geglu
131
+ Quantizer:
132
+ codebook_size: 1024
133
+ z_dim: 12
134
+ dim: 512
135
+ length: 64
136
+ cond_dim: 32
137
+ cond_affine_norm: false
138
+ vq_beta: 0.25
139
+ use_norm: true
140
+ norm_layer: l2
141
+ ffn_type: geglu
142
+ temperature: 1.0
143
+ Decoder:
144
+ input_dim: 48
145
+ ctx_input_dim: 512
146
+ output_dim: 48
147
+ cond_dim: 32
148
+ cond_input_dim: 11
149
+ max_seq_len: 64
150
+ ctx_max_seq_len: 64
151
+ dim: 512
152
+ layer_num: 10
153
+ heads: 8
154
+ emb_dropout: 0.0
155
+ drop: 0.0
156
+ l2_norm: false
157
+ context_type: cross_attn
158
+ rope: false
159
+ abs_pos: true
160
+ causal: false
161
+ norm_layer: RMSNorm
162
+ ffn_type: geglu
163
+ mlp_ratio: 4.0
164
+ zero_out: true
165
+ query_type: learnable
166
+ ARModel:
167
+ input_dim: 512
168
+ output_dim: 1024
169
+ max_seq_len: 64
170
+ cond_dim: 32
171
+ cond_input_dim: 11
172
+ dim: 512
173
+ layer_num: 6
174
+ heads: 8
175
+ emb_dropout: 0.0
176
+ drop: 0.0
177
+ l2_norm: false
178
+ context_type: none
179
+ rope: false
180
+ abs_pos: true
181
+ causal: true
182
+ out_act: false
183
+ norm_layer: RMSNorm
184
+ ffn_type: geglu
185
+ mlp_ratio: 4
186
+ zero_out: false
187
+ temperature: 1.0
188
+ GANLoss:
189
+ disc_loss: vanilla
190
+ disc_dim: 64
191
+ disc_type: patchgan
192
+ image_size: 32
193
+ disc_num_layers: 3
194
+ disc_in_channels: 3
195
+ gen_adv_loss: vanilla
196
+ lecam_loss_weight: 0.001
197
+ norm_type: bn
198
+ aug_prob: 1.0
configs/cifar10/flow_matching_ar.yaml ADDED
@@ -0,0 +1,198 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ wandb_project: wAR
2
+ wandb_name: flow_matching_ar
3
+ data_dir: ./data/cifar10
4
+ xflip: true
5
+ crop_type: center
6
+ image_size: 32
7
+ num_channels: 3
8
+ num_classes: 11
9
+ patch_size: 4
10
+ use_label: false
11
+ patch_dim: 48
12
+ x_len: 64
13
+ diffusion_decoder: false
14
+ train_ae: false
15
+ train_ar: true
16
+ phases: 200000:DO_PRIOR_AR:1
17
+ batch_size: 512
18
+ eval_batch_size: 512
19
+ lr_enc: 0.00015
20
+ lr_dec: 0.00015
21
+ lr_ar: 0.00015
22
+ lr_gan_loss: 0.00015
23
+ weight_decay_enc: 0.0
24
+ weight_decay_dec: 0.0
25
+ weight_decay_ar: 0.0001
26
+ weight_decay_gan: 0.0001
27
+ lr_enc_min: 3.0e-05
28
+ lr_dec_min: 3.0e-05
29
+ lr_ar_min: 3.0e-05
30
+ lr_gan_loss_min: 3.0e-05
31
+ use_tf32: false
32
+ lr_scheduler: none
33
+ drop: 0.0
34
+ ar_drop: 0.1
35
+ grad_clip: 0.0
36
+ seed: 42
37
+ beta1: 0.5
38
+ beta2: 0.95
39
+ ar_beta1: 0.9
40
+ ar_beta2: 0.99
41
+ label_drop_prob: 0.1
42
+ precision: bf16-mixed
43
+ replace_cur_with_ema: false
44
+ num_workers: 4
45
+ devices: 8
46
+ reverse_t: true
47
+ use_noise_query: true
48
+ use_tail_dropout: false
49
+ l2_v_target: true
50
+ tail_dropout_t2k: 1.0
51
+ tied_timestep: false
52
+ time_stages: 200,400,600,800,1000
53
+ k_per_stage: 192,184,72,48,16
54
+ load_ar_model: false
55
+ load_ar_model_ema: false
56
+ prior_G_loss: false
57
+ use_l1_loss: false
58
+ use_l2_loss: false
59
+ use_lpips_loss: false
60
+ use_gan_loss: false
61
+ use_prior_loss: false
62
+ teacher_forcing_ae: false
63
+ disc_adaptive_weight: false
64
+ lpips_weight: 0.1
65
+ gan_G_weight: 0.1
66
+ prior_enc_weight: 1.0
67
+ l1_weight: 0.1
68
+ l2_weight: 1.0
69
+ gan_start: 5000
70
+ prior_start: 0
71
+ tail_dropout_start: 0
72
+ tail_dropout_p: 0.0
73
+ torch_compile: true
74
+ compile_ae: true
75
+ compile_ar: true
76
+ compile_gan: false
77
+ ar_ema_halflife: 2000
78
+ ae_ema_halflife: 0
79
+ ema_sampling: true
80
+ ema_reconstruction: false
81
+ cache_kv: true
82
+ topK: 0.0
83
+ topP: 0.0
84
+ cfg: 0.0
85
+ cfg_schedule: cosine
86
+ cfg_power: 2.75
87
+ temperature: 1.0
88
+ eval_data_dir: ./data/cifar10
89
+ eval_crop_type: center
90
+ eval_fid_ref_path: ./refs/cifar10-32x32_local.npz
91
+ save_dir: ./outputs/flow_matching_ar
92
+ log_freq: 25
93
+ visualize_freq: 5000
94
+ visualize_img_num: 8
95
+ ckpt_freq: 25000
96
+ eval_freq: 25000
97
+ save_ckpt: true
98
+ ar_ckpt_path: ''
99
+ ae_ckpt_path: ./outputs/flow_matching_tokenizer/000/ckpts/last.ckpt
100
+ resume_optimizer: false
101
+ cond_emb_dim: 32
102
+ greedy_coding_prob: 0.0
103
+ z_len: 64
104
+ z_dim: 512
105
+ vq_type: VQ
106
+ codebook_size: 1024
107
+ norm_layer: RMSNorm
108
+ ffn_type: geglu
109
+ context_type: cross_attn
110
+ hidden_dim: 512
111
+ Encoder:
112
+ input_dim: 48
113
+ output_dim: 512
114
+ cond_dim: 32
115
+ cond_input_dim: 11
116
+ max_seq_len: 64
117
+ ctx_max_seq_len: 64
118
+ dim: 512
119
+ layer_num: 10
120
+ mlp_ratio: 4.0
121
+ heads: 8
122
+ emb_dropout: 0.0
123
+ drop: 0.0
124
+ l2_norm: false
125
+ context_type: cross_attn
126
+ rope: false
127
+ abs_pos: true
128
+ causal: false
129
+ norm_layer: RMSNorm
130
+ ffn_type: geglu
131
+ Quantizer:
132
+ codebook_size: 1024
133
+ z_dim: 12
134
+ dim: 512
135
+ length: 64
136
+ cond_dim: 32
137
+ cond_affine_norm: false
138
+ vq_beta: 0.25
139
+ use_norm: true
140
+ norm_layer: l2
141
+ ffn_type: geglu
142
+ temperature: 1.0
143
+ Decoder:
144
+ input_dim: 48
145
+ ctx_input_dim: 512
146
+ output_dim: 48
147
+ cond_dim: 32
148
+ cond_input_dim: 11
149
+ max_seq_len: 64
150
+ ctx_max_seq_len: 64
151
+ dim: 512
152
+ layer_num: 10
153
+ heads: 8
154
+ emb_dropout: 0.0
155
+ drop: 0.0
156
+ l2_norm: false
157
+ context_type: cross_attn
158
+ rope: false
159
+ abs_pos: true
160
+ causal: false
161
+ norm_layer: RMSNorm
162
+ ffn_type: geglu
163
+ mlp_ratio: 4.0
164
+ zero_out: true
165
+ query_type: noise
166
+ ARModel:
167
+ input_dim: 512
168
+ output_dim: 1024
169
+ max_seq_len: 64
170
+ cond_dim: 32
171
+ cond_input_dim: 11
172
+ dim: 512
173
+ layer_num: 13
174
+ heads: 8
175
+ emb_dropout: 0.0
176
+ drop: 0.1
177
+ l2_norm: false
178
+ context_type: none
179
+ rope: false
180
+ abs_pos: true
181
+ causal: true
182
+ out_act: false
183
+ norm_layer: RMSNorm
184
+ ffn_type: geglu
185
+ mlp_ratio: 4.0
186
+ zero_out: false
187
+ temperature: 1.0
188
+ GANLoss:
189
+ disc_loss: vanilla
190
+ disc_dim: 64
191
+ disc_type: patchgan
192
+ image_size: 32
193
+ disc_num_layers: 3
194
+ disc_in_channels: 3
195
+ gen_adv_loss: vanilla
196
+ lecam_loss_weight: 0.001
197
+ norm_type: bn
198
+ aug_prob: 1.0
configs/cifar10/flow_matching_tail_dropout_ar.yaml ADDED
@@ -0,0 +1,198 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ wandb_project: wAR
2
+ wandb_name: flow_matching_tail_dropout_ar
3
+ data_dir: ./data/cifar10
4
+ xflip: true
5
+ crop_type: center
6
+ image_size: 32
7
+ num_channels: 3
8
+ num_classes: 11
9
+ patch_size: 4
10
+ use_label: false
11
+ patch_dim: 48
12
+ x_len: 64
13
+ diffusion_decoder: false
14
+ train_ae: false
15
+ train_ar: true
16
+ phases: 200000:DO_PRIOR_AR:1
17
+ batch_size: 512
18
+ eval_batch_size: 512
19
+ lr_enc: 0.00015
20
+ lr_dec: 0.00015
21
+ lr_ar: 0.00015
22
+ lr_gan_loss: 0.00015
23
+ weight_decay_enc: 0.0
24
+ weight_decay_dec: 0.0
25
+ weight_decay_ar: 0.0001
26
+ weight_decay_gan: 0.0001
27
+ lr_enc_min: 3.0e-05
28
+ lr_dec_min: 3.0e-05
29
+ lr_ar_min: 3.0e-05
30
+ lr_gan_loss_min: 3.0e-05
31
+ use_tf32: false
32
+ lr_scheduler: none
33
+ drop: 0.0
34
+ ar_drop: 0.1
35
+ grad_clip: 0.0
36
+ seed: 42
37
+ beta1: 0.5
38
+ beta2: 0.95
39
+ ar_beta1: 0.9
40
+ ar_beta2: 0.99
41
+ label_drop_prob: 0.1
42
+ precision: bf16-mixed
43
+ replace_cur_with_ema: false
44
+ num_workers: 4
45
+ devices: 8
46
+ reverse_t: true
47
+ use_noise_query: true
48
+ use_tail_dropout: true
49
+ l2_v_target: true
50
+ tail_dropout_t2k: 1.0
51
+ tied_timestep: false
52
+ time_stages: 200,400,600,800,1000
53
+ k_per_stage: 192,184,72,48,16
54
+ load_ar_model: false
55
+ load_ar_model_ema: false
56
+ prior_G_loss: false
57
+ use_l1_loss: false
58
+ use_l2_loss: false
59
+ use_lpips_loss: false
60
+ use_gan_loss: false
61
+ use_prior_loss: false
62
+ teacher_forcing_ae: false
63
+ disc_adaptive_weight: false
64
+ lpips_weight: 0.1
65
+ gan_G_weight: 0.1
66
+ prior_enc_weight: 1.0
67
+ l1_weight: 0.1
68
+ l2_weight: 1.0
69
+ gan_start: 5000
70
+ prior_start: 0
71
+ tail_dropout_start: 0
72
+ tail_dropout_p: 0.5
73
+ torch_compile: true
74
+ compile_ae: true
75
+ compile_ar: true
76
+ compile_gan: false
77
+ ar_ema_halflife: 2000
78
+ ae_ema_halflife: 0
79
+ ema_sampling: true
80
+ ema_reconstruction: false
81
+ cache_kv: true
82
+ topK: 0.0
83
+ topP: 0.0
84
+ cfg: 0.0
85
+ cfg_schedule: cosine
86
+ cfg_power: 2.75
87
+ temperature: 1.0
88
+ eval_data_dir: ./data/cifar10
89
+ eval_crop_type: center
90
+ eval_fid_ref_path: ./refs/cifar10-32x32_local.npz
91
+ save_dir: ./outputs/flow_matching_tail_dropout_ar
92
+ log_freq: 25
93
+ visualize_freq: 5000
94
+ visualize_img_num: 8
95
+ ckpt_freq: 25000
96
+ eval_freq: 25000
97
+ save_ckpt: true
98
+ ar_ckpt_path: ''
99
+ ae_ckpt_path: ./outputs/flow_matching_tail_dropout_tokenizer/000/ckpts/last.ckpt
100
+ resume_optimizer: false
101
+ cond_emb_dim: 32
102
+ greedy_coding_prob: 0.0
103
+ z_len: 64
104
+ z_dim: 512
105
+ vq_type: VQ
106
+ codebook_size: 1024
107
+ norm_layer: RMSNorm
108
+ ffn_type: geglu
109
+ context_type: cross_attn
110
+ hidden_dim: 512
111
+ Encoder:
112
+ input_dim: 48
113
+ output_dim: 512
114
+ cond_dim: 32
115
+ cond_input_dim: 11
116
+ max_seq_len: 64
117
+ ctx_max_seq_len: 64
118
+ dim: 512
119
+ layer_num: 10
120
+ mlp_ratio: 4.0
121
+ heads: 8
122
+ emb_dropout: 0.0
123
+ drop: 0.0
124
+ l2_norm: false
125
+ context_type: cross_attn
126
+ rope: false
127
+ abs_pos: true
128
+ causal: false
129
+ norm_layer: RMSNorm
130
+ ffn_type: geglu
131
+ Quantizer:
132
+ codebook_size: 1024
133
+ z_dim: 12
134
+ dim: 512
135
+ length: 64
136
+ cond_dim: 32
137
+ cond_affine_norm: false
138
+ vq_beta: 0.25
139
+ use_norm: true
140
+ norm_layer: l2
141
+ ffn_type: geglu
142
+ temperature: 1.0
143
+ Decoder:
144
+ input_dim: 48
145
+ ctx_input_dim: 512
146
+ output_dim: 48
147
+ cond_dim: 32
148
+ cond_input_dim: 11
149
+ max_seq_len: 64
150
+ ctx_max_seq_len: 64
151
+ dim: 512
152
+ layer_num: 10
153
+ heads: 8
154
+ emb_dropout: 0.0
155
+ drop: 0.0
156
+ l2_norm: false
157
+ context_type: cross_attn
158
+ rope: false
159
+ abs_pos: true
160
+ causal: false
161
+ norm_layer: RMSNorm
162
+ ffn_type: geglu
163
+ mlp_ratio: 4.0
164
+ zero_out: true
165
+ query_type: noise
166
+ ARModel:
167
+ input_dim: 512
168
+ output_dim: 1024
169
+ max_seq_len: 64
170
+ cond_dim: 32
171
+ cond_input_dim: 11
172
+ dim: 512
173
+ layer_num: 13
174
+ heads: 8
175
+ emb_dropout: 0.0
176
+ drop: 0.1
177
+ l2_norm: false
178
+ context_type: none
179
+ rope: false
180
+ abs_pos: true
181
+ causal: true
182
+ out_act: false
183
+ norm_layer: RMSNorm
184
+ ffn_type: geglu
185
+ mlp_ratio: 4.0
186
+ zero_out: false
187
+ temperature: 1.0
188
+ GANLoss:
189
+ disc_loss: vanilla
190
+ disc_dim: 64
191
+ disc_type: patchgan
192
+ image_size: 32
193
+ disc_num_layers: 3
194
+ disc_in_channels: 3
195
+ gen_adv_loss: vanilla
196
+ lecam_loss_weight: 0.001
197
+ norm_type: bn
198
+ aug_prob: 1.0
configs/cifar10/flow_matching_tail_dropout_tokenizer.yaml ADDED
@@ -0,0 +1,198 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ wandb_project: wAR
2
+ wandb_name: flow_matching_tail_dropout_tokenizer
3
+ data_dir: ./data/cifar10
4
+ xflip: true
5
+ crop_type: center
6
+ image_size: 32
7
+ num_channels: 3
8
+ num_classes: 11
9
+ patch_size: 4
10
+ use_label: false
11
+ patch_dim: 48
12
+ x_len: 64
13
+ diffusion_decoder: true
14
+ train_ae: true
15
+ train_ar: false
16
+ phases: 400000:DO_L2-DO_LPIPS:1
17
+ batch_size: 512
18
+ eval_batch_size: 512
19
+ lr_enc: 0.00015
20
+ lr_dec: 0.00015
21
+ lr_ar: 0.00015
22
+ lr_gan_loss: 0.00015
23
+ weight_decay_enc: 0.0
24
+ weight_decay_dec: 0.0
25
+ weight_decay_ar: 0.0
26
+ weight_decay_gan: 0.0001
27
+ lr_enc_min: 3.0e-05
28
+ lr_dec_min: 3.0e-05
29
+ lr_ar_min: 3.0e-05
30
+ lr_gan_loss_min: 3.0e-05
31
+ use_tf32: false
32
+ lr_scheduler: none
33
+ drop: 0.0
34
+ ar_drop: 0.0
35
+ grad_clip: 0.0
36
+ seed: 42
37
+ beta1: 0.5
38
+ beta2: 0.95
39
+ ar_beta1: 0.5
40
+ ar_beta2: 0.95
41
+ label_drop_prob: 0.1
42
+ precision: bf16-mixed
43
+ replace_cur_with_ema: false
44
+ num_workers: 4
45
+ devices: 8
46
+ reverse_t: false
47
+ use_noise_query: true
48
+ use_tail_dropout: true
49
+ l2_v_target: true
50
+ tail_dropout_t2k: 1.0
51
+ tied_timestep: false
52
+ time_stages: 200,400,600,800,1000
53
+ k_per_stage: 192,184,72,48,16
54
+ load_ar_model: false
55
+ load_ar_model_ema: false
56
+ prior_G_loss: false
57
+ use_l1_loss: false
58
+ use_l2_loss: true
59
+ use_lpips_loss: true
60
+ use_gan_loss: false
61
+ use_prior_loss: false
62
+ teacher_forcing_ae: false
63
+ disc_adaptive_weight: false
64
+ lpips_weight: 0.1
65
+ gan_G_weight: 0.1
66
+ prior_enc_weight: 1.0
67
+ l1_weight: 0.1
68
+ l2_weight: 1.0
69
+ gan_start: 5000
70
+ prior_start: 0
71
+ tail_dropout_start: 0
72
+ tail_dropout_p: 0.5
73
+ torch_compile: true
74
+ compile_ae: true
75
+ compile_ar: true
76
+ compile_gan: false
77
+ ar_ema_halflife: 2500
78
+ ae_ema_halflife: 1000
79
+ ema_sampling: false
80
+ ema_reconstruction: false
81
+ cache_kv: true
82
+ topK: 0.0
83
+ topP: 0.0
84
+ cfg: 0.0
85
+ cfg_schedule: cosine
86
+ cfg_power: 2.75
87
+ temperature: 1.0
88
+ eval_data_dir: ./data/cifar10
89
+ eval_crop_type: center
90
+ eval_fid_ref_path: ./refs/cifar10-32x32_local.npz
91
+ save_dir: ./outputs/flow_matching_tail_dropout_tokenizer
92
+ log_freq: 25
93
+ visualize_freq: 5000
94
+ visualize_img_num: 8
95
+ ckpt_freq: 25000
96
+ eval_freq: 25000
97
+ save_ckpt: true
98
+ ar_ckpt_path: ''
99
+ ae_ckpt_path: ''
100
+ resume_optimizer: false
101
+ cond_emb_dim: 32
102
+ greedy_coding_prob: 0.0
103
+ z_len: 64
104
+ z_dim: 512
105
+ vq_type: VQ
106
+ codebook_size: 1024
107
+ norm_layer: RMSNorm
108
+ ffn_type: geglu
109
+ context_type: cross_attn
110
+ hidden_dim: 512
111
+ Encoder:
112
+ input_dim: 48
113
+ output_dim: 512
114
+ cond_dim: 32
115
+ cond_input_dim: 11
116
+ max_seq_len: 64
117
+ ctx_max_seq_len: 64
118
+ dim: 512
119
+ layer_num: 10
120
+ mlp_ratio: 4.0
121
+ heads: 8
122
+ emb_dropout: 0.0
123
+ drop: 0.0
124
+ l2_norm: false
125
+ context_type: cross_attn
126
+ rope: false
127
+ abs_pos: true
128
+ causal: false
129
+ norm_layer: RMSNorm
130
+ ffn_type: geglu
131
+ Quantizer:
132
+ codebook_size: 1024
133
+ z_dim: 12
134
+ dim: 512
135
+ length: 64
136
+ cond_dim: 32
137
+ cond_affine_norm: false
138
+ vq_beta: 0.25
139
+ use_norm: true
140
+ norm_layer: l2
141
+ ffn_type: geglu
142
+ temperature: 1.0
143
+ Decoder:
144
+ input_dim: 48
145
+ ctx_input_dim: 512
146
+ output_dim: 48
147
+ cond_dim: 32
148
+ cond_input_dim: 11
149
+ max_seq_len: 64
150
+ ctx_max_seq_len: 64
151
+ dim: 512
152
+ layer_num: 10
153
+ heads: 8
154
+ emb_dropout: 0.0
155
+ drop: 0.0
156
+ l2_norm: false
157
+ context_type: cross_attn
158
+ rope: false
159
+ abs_pos: true
160
+ causal: false
161
+ norm_layer: RMSNorm
162
+ ffn_type: geglu
163
+ mlp_ratio: 4.0
164
+ zero_out: true
165
+ query_type: noise
166
+ ARModel:
167
+ input_dim: 512
168
+ output_dim: 1024
169
+ max_seq_len: 64
170
+ cond_dim: 32
171
+ cond_input_dim: 11
172
+ dim: 512
173
+ layer_num: 6
174
+ heads: 8
175
+ emb_dropout: 0.0
176
+ drop: 0.0
177
+ l2_norm: false
178
+ context_type: none
179
+ rope: false
180
+ abs_pos: true
181
+ causal: true
182
+ out_act: false
183
+ norm_layer: RMSNorm
184
+ ffn_type: geglu
185
+ mlp_ratio: 4
186
+ zero_out: false
187
+ temperature: 1.0
188
+ GANLoss:
189
+ disc_loss: vanilla
190
+ disc_dim: 64
191
+ disc_type: patchgan
192
+ image_size: 32
193
+ disc_num_layers: 3
194
+ disc_in_channels: 3
195
+ gen_adv_loss: vanilla
196
+ lecam_loss_weight: 0.001
197
+ norm_type: bn
198
+ aug_prob: 1.0
configs/cifar10/flow_matching_tied_tail_dropout_ar.yaml ADDED
@@ -0,0 +1,198 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ wandb_project: wAR
2
+ wandb_name: flow_matching_tied_tail_dropout_ar
3
+ data_dir: ./data/cifar10
4
+ xflip: true
5
+ crop_type: center
6
+ image_size: 32
7
+ num_channels: 3
8
+ num_classes: 11
9
+ patch_size: 4
10
+ use_label: false
11
+ patch_dim: 48
12
+ x_len: 64
13
+ diffusion_decoder: false
14
+ train_ae: false
15
+ train_ar: true
16
+ phases: 200000:DO_PRIOR_AR:1
17
+ batch_size: 512
18
+ eval_batch_size: 512
19
+ lr_enc: 0.00015
20
+ lr_dec: 0.00015
21
+ lr_ar: 0.00015
22
+ lr_gan_loss: 0.00015
23
+ weight_decay_enc: 0.0
24
+ weight_decay_dec: 0.0
25
+ weight_decay_ar: 0.0001
26
+ weight_decay_gan: 0.0001
27
+ lr_enc_min: 3.0e-05
28
+ lr_dec_min: 3.0e-05
29
+ lr_ar_min: 3.0e-05
30
+ lr_gan_loss_min: 3.0e-05
31
+ use_tf32: false
32
+ lr_scheduler: none
33
+ drop: 0.0
34
+ ar_drop: 0.1
35
+ grad_clip: 0.0
36
+ seed: 42
37
+ beta1: 0.5
38
+ beta2: 0.95
39
+ ar_beta1: 0.9
40
+ ar_beta2: 0.99
41
+ label_drop_prob: 0.1
42
+ precision: bf16-mixed
43
+ replace_cur_with_ema: false
44
+ num_workers: 4
45
+ devices: 8
46
+ reverse_t: true
47
+ use_noise_query: true
48
+ use_tail_dropout: true
49
+ l2_v_target: true
50
+ tail_dropout_t2k: 1.0
51
+ tied_timestep: true
52
+ time_stages: 200,400,600,800,1000
53
+ k_per_stage: 192,184,72,48,16
54
+ load_ar_model: false
55
+ load_ar_model_ema: false
56
+ prior_G_loss: false
57
+ use_l1_loss: false
58
+ use_l2_loss: false
59
+ use_lpips_loss: false
60
+ use_gan_loss: false
61
+ use_prior_loss: false
62
+ teacher_forcing_ae: false
63
+ disc_adaptive_weight: false
64
+ lpips_weight: 0.1
65
+ gan_G_weight: 0.1
66
+ prior_enc_weight: 1.0
67
+ l1_weight: 0.1
68
+ l2_weight: 1.0
69
+ gan_start: 5000
70
+ prior_start: 0
71
+ tail_dropout_start: 0
72
+ tail_dropout_p: 1.0
73
+ torch_compile: true
74
+ compile_ae: true
75
+ compile_ar: true
76
+ compile_gan: false
77
+ ar_ema_halflife: 2000
78
+ ae_ema_halflife: 0
79
+ ema_sampling: true
80
+ ema_reconstruction: false
81
+ cache_kv: true
82
+ topK: 0.0
83
+ topP: 0.0
84
+ cfg: 0.0
85
+ cfg_schedule: cosine
86
+ cfg_power: 2.75
87
+ temperature: 1.0
88
+ eval_data_dir: ./data/cifar10
89
+ eval_crop_type: center
90
+ eval_fid_ref_path: ./refs/cifar10-32x32_local.npz
91
+ save_dir: ./outputs/flow_matching_tied_tail_dropout_ar
92
+ log_freq: 25
93
+ visualize_freq: 5000
94
+ visualize_img_num: 8
95
+ ckpt_freq: 25000
96
+ eval_freq: 25000
97
+ save_ckpt: true
98
+ ar_ckpt_path: ''
99
+ ae_ckpt_path: ./outputs/flow_matching_tied_tail_dropout_tokenizer/000/ckpts/last.ckpt
100
+ resume_optimizer: false
101
+ cond_emb_dim: 32
102
+ greedy_coding_prob: 0.0
103
+ z_len: 64
104
+ z_dim: 512
105
+ vq_type: VQ
106
+ codebook_size: 1024
107
+ norm_layer: RMSNorm
108
+ ffn_type: geglu
109
+ context_type: cross_attn
110
+ hidden_dim: 512
111
+ Encoder:
112
+ input_dim: 48
113
+ output_dim: 512
114
+ cond_dim: 32
115
+ cond_input_dim: 11
116
+ max_seq_len: 64
117
+ ctx_max_seq_len: 64
118
+ dim: 512
119
+ layer_num: 10
120
+ mlp_ratio: 4.0
121
+ heads: 8
122
+ emb_dropout: 0.0
123
+ drop: 0.0
124
+ l2_norm: false
125
+ context_type: cross_attn
126
+ rope: false
127
+ abs_pos: true
128
+ causal: false
129
+ norm_layer: RMSNorm
130
+ ffn_type: geglu
131
+ Quantizer:
132
+ codebook_size: 1024
133
+ z_dim: 12
134
+ dim: 512
135
+ length: 64
136
+ cond_dim: 32
137
+ cond_affine_norm: false
138
+ vq_beta: 0.25
139
+ use_norm: true
140
+ norm_layer: l2
141
+ ffn_type: geglu
142
+ temperature: 1.0
143
+ Decoder:
144
+ input_dim: 48
145
+ ctx_input_dim: 512
146
+ output_dim: 48
147
+ cond_dim: 32
148
+ cond_input_dim: 11
149
+ max_seq_len: 64
150
+ ctx_max_seq_len: 64
151
+ dim: 512
152
+ layer_num: 10
153
+ heads: 8
154
+ emb_dropout: 0.0
155
+ drop: 0.0
156
+ l2_norm: false
157
+ context_type: cross_attn
158
+ rope: false
159
+ abs_pos: true
160
+ causal: false
161
+ norm_layer: RMSNorm
162
+ ffn_type: geglu
163
+ mlp_ratio: 4.0
164
+ zero_out: true
165
+ query_type: noise
166
+ ARModel:
167
+ input_dim: 512
168
+ output_dim: 1024
169
+ max_seq_len: 64
170
+ cond_dim: 32
171
+ cond_input_dim: 11
172
+ dim: 512
173
+ layer_num: 13
174
+ heads: 8
175
+ emb_dropout: 0.0
176
+ drop: 0.1
177
+ l2_norm: false
178
+ context_type: none
179
+ rope: false
180
+ abs_pos: true
181
+ causal: true
182
+ out_act: false
183
+ norm_layer: RMSNorm
184
+ ffn_type: geglu
185
+ mlp_ratio: 4.0
186
+ zero_out: false
187
+ temperature: 1.0
188
+ GANLoss:
189
+ disc_loss: vanilla
190
+ disc_dim: 64
191
+ disc_type: patchgan
192
+ image_size: 32
193
+ disc_num_layers: 3
194
+ disc_in_channels: 3
195
+ gen_adv_loss: vanilla
196
+ lecam_loss_weight: 0.001
197
+ norm_type: bn
198
+ aug_prob: 1.0
configs/cifar10/flow_matching_tied_tail_dropout_tokenizer.yaml ADDED
@@ -0,0 +1,198 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ wandb_project: wAR
2
+ wandb_name: flow_matching_tied_tail_dropout_tokenizer
3
+ data_dir: ./data/cifar10
4
+ xflip: true
5
+ crop_type: center
6
+ image_size: 32
7
+ num_channels: 3
8
+ num_classes: 11
9
+ patch_size: 4
10
+ use_label: false
11
+ patch_dim: 48
12
+ x_len: 64
13
+ diffusion_decoder: true
14
+ train_ae: true
15
+ train_ar: false
16
+ phases: 400000:DO_L2-DO_LPIPS:1
17
+ batch_size: 512
18
+ eval_batch_size: 512
19
+ lr_enc: 0.00015
20
+ lr_dec: 0.00015
21
+ lr_ar: 0.00015
22
+ lr_gan_loss: 0.00015
23
+ weight_decay_enc: 0.0
24
+ weight_decay_dec: 0.0
25
+ weight_decay_ar: 0.0
26
+ weight_decay_gan: 0.0001
27
+ lr_enc_min: 3.0e-05
28
+ lr_dec_min: 3.0e-05
29
+ lr_ar_min: 3.0e-05
30
+ lr_gan_loss_min: 3.0e-05
31
+ use_tf32: false
32
+ lr_scheduler: none
33
+ drop: 0.0
34
+ ar_drop: 0.0
35
+ grad_clip: 0.0
36
+ seed: 42
37
+ beta1: 0.5
38
+ beta2: 0.95
39
+ ar_beta1: 0.5
40
+ ar_beta2: 0.95
41
+ label_drop_prob: 0.1
42
+ precision: bf16-mixed
43
+ replace_cur_with_ema: false
44
+ num_workers: 4
45
+ devices: 8
46
+ reverse_t: false
47
+ use_noise_query: true
48
+ use_tail_dropout: true
49
+ l2_v_target: true
50
+ tail_dropout_t2k: 1.0
51
+ tied_timestep: true
52
+ time_stages: 200,400,600,800,1000
53
+ k_per_stage: 192,184,72,48,16
54
+ load_ar_model: false
55
+ load_ar_model_ema: false
56
+ prior_G_loss: false
57
+ use_l1_loss: false
58
+ use_l2_loss: true
59
+ use_lpips_loss: true
60
+ use_gan_loss: false
61
+ use_prior_loss: false
62
+ teacher_forcing_ae: false
63
+ disc_adaptive_weight: false
64
+ lpips_weight: 0.1
65
+ gan_G_weight: 0.1
66
+ prior_enc_weight: 1.0
67
+ l1_weight: 0.1
68
+ l2_weight: 1.0
69
+ gan_start: 5000
70
+ prior_start: 0
71
+ tail_dropout_start: 0
72
+ tail_dropout_p: 1.0
73
+ torch_compile: true
74
+ compile_ae: true
75
+ compile_ar: true
76
+ compile_gan: false
77
+ ar_ema_halflife: 2500
78
+ ae_ema_halflife: 1000
79
+ ema_sampling: false
80
+ ema_reconstruction: false
81
+ cache_kv: true
82
+ topK: 0.0
83
+ topP: 0.0
84
+ cfg: 0.0
85
+ cfg_schedule: cosine
86
+ cfg_power: 2.75
87
+ temperature: 1.0
88
+ eval_data_dir: ./data/cifar10
89
+ eval_crop_type: center
90
+ eval_fid_ref_path: ./refs/cifar10-32x32_local.npz
91
+ save_dir: ./outputs/flow_matching_tied_tail_dropout_tokenizer
92
+ log_freq: 25
93
+ visualize_freq: 5000
94
+ visualize_img_num: 8
95
+ ckpt_freq: 25000
96
+ eval_freq: 25000
97
+ save_ckpt: true
98
+ ar_ckpt_path: ''
99
+ ae_ckpt_path: ''
100
+ resume_optimizer: false
101
+ cond_emb_dim: 32
102
+ greedy_coding_prob: 0.0
103
+ z_len: 64
104
+ z_dim: 512
105
+ vq_type: VQ
106
+ codebook_size: 1024
107
+ norm_layer: RMSNorm
108
+ ffn_type: geglu
109
+ context_type: cross_attn
110
+ hidden_dim: 512
111
+ Encoder:
112
+ input_dim: 48
113
+ output_dim: 512
114
+ cond_dim: 32
115
+ cond_input_dim: 11
116
+ max_seq_len: 64
117
+ ctx_max_seq_len: 64
118
+ dim: 512
119
+ layer_num: 10
120
+ mlp_ratio: 4.0
121
+ heads: 8
122
+ emb_dropout: 0.0
123
+ drop: 0.0
124
+ l2_norm: false
125
+ context_type: cross_attn
126
+ rope: false
127
+ abs_pos: true
128
+ causal: false
129
+ norm_layer: RMSNorm
130
+ ffn_type: geglu
131
+ Quantizer:
132
+ codebook_size: 1024
133
+ z_dim: 12
134
+ dim: 512
135
+ length: 64
136
+ cond_dim: 32
137
+ cond_affine_norm: false
138
+ vq_beta: 0.25
139
+ use_norm: true
140
+ norm_layer: l2
141
+ ffn_type: geglu
142
+ temperature: 1.0
143
+ Decoder:
144
+ input_dim: 48
145
+ ctx_input_dim: 512
146
+ output_dim: 48
147
+ cond_dim: 32
148
+ cond_input_dim: 11
149
+ max_seq_len: 64
150
+ ctx_max_seq_len: 64
151
+ dim: 512
152
+ layer_num: 10
153
+ heads: 8
154
+ emb_dropout: 0.0
155
+ drop: 0.0
156
+ l2_norm: false
157
+ context_type: cross_attn
158
+ rope: false
159
+ abs_pos: true
160
+ causal: false
161
+ norm_layer: RMSNorm
162
+ ffn_type: geglu
163
+ mlp_ratio: 4.0
164
+ zero_out: true
165
+ query_type: noise
166
+ ARModel:
167
+ input_dim: 512
168
+ output_dim: 1024
169
+ max_seq_len: 64
170
+ cond_dim: 32
171
+ cond_input_dim: 11
172
+ dim: 512
173
+ layer_num: 6
174
+ heads: 8
175
+ emb_dropout: 0.0
176
+ drop: 0.0
177
+ l2_norm: false
178
+ context_type: none
179
+ rope: false
180
+ abs_pos: true
181
+ causal: true
182
+ out_act: false
183
+ norm_layer: RMSNorm
184
+ ffn_type: geglu
185
+ mlp_ratio: 4
186
+ zero_out: false
187
+ temperature: 1.0
188
+ GANLoss:
189
+ disc_loss: vanilla
190
+ disc_dim: 64
191
+ disc_type: patchgan
192
+ image_size: 32
193
+ disc_num_layers: 3
194
+ disc_in_channels: 3
195
+ gen_adv_loss: vanilla
196
+ lecam_loss_weight: 0.001
197
+ norm_type: bn
198
+ aug_prob: 1.0
configs/cifar10/flow_matching_tokenizer.yaml ADDED
@@ -0,0 +1,198 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ wandb_project: wAR
2
+ wandb_name: flow_matching_tokenizer
3
+ data_dir: ./data/cifar10
4
+ xflip: true
5
+ crop_type: center
6
+ image_size: 32
7
+ num_channels: 3
8
+ num_classes: 11
9
+ patch_size: 4
10
+ use_label: false
11
+ patch_dim: 48
12
+ x_len: 64
13
+ diffusion_decoder: true
14
+ train_ae: true
15
+ train_ar: false
16
+ phases: 400000:DO_L2-DO_LPIPS:1
17
+ batch_size: 512
18
+ eval_batch_size: 512
19
+ lr_enc: 0.00015
20
+ lr_dec: 0.00015
21
+ lr_ar: 0.00015
22
+ lr_gan_loss: 0.00015
23
+ weight_decay_enc: 0.0
24
+ weight_decay_dec: 0.0
25
+ weight_decay_ar: 0.0
26
+ weight_decay_gan: 0.0001
27
+ lr_enc_min: 3.0e-05
28
+ lr_dec_min: 3.0e-05
29
+ lr_ar_min: 3.0e-05
30
+ lr_gan_loss_min: 3.0e-05
31
+ use_tf32: false
32
+ lr_scheduler: none
33
+ drop: 0.0
34
+ ar_drop: 0.0
35
+ grad_clip: 0.0
36
+ seed: 42
37
+ beta1: 0.5
38
+ beta2: 0.95
39
+ ar_beta1: 0.5
40
+ ar_beta2: 0.95
41
+ label_drop_prob: 0.1
42
+ precision: bf16-mixed
43
+ replace_cur_with_ema: false
44
+ num_workers: 4
45
+ devices: 8
46
+ reverse_t: false
47
+ use_noise_query: true
48
+ use_tail_dropout: true
49
+ l2_v_target: true
50
+ tail_dropout_t2k: 1.0
51
+ tied_timestep: false
52
+ time_stages: 200,400,600,800,1000
53
+ k_per_stage: 192,184,72,48,16
54
+ load_ar_model: false
55
+ load_ar_model_ema: false
56
+ prior_G_loss: false
57
+ use_l1_loss: false
58
+ use_l2_loss: true
59
+ use_lpips_loss: true
60
+ use_gan_loss: false
61
+ use_prior_loss: false
62
+ teacher_forcing_ae: false
63
+ disc_adaptive_weight: false
64
+ lpips_weight: 0.1
65
+ gan_G_weight: 0.1
66
+ prior_enc_weight: 1.0
67
+ l1_weight: 0.1
68
+ l2_weight: 1.0
69
+ gan_start: 5000
70
+ prior_start: 0
71
+ tail_dropout_start: 0
72
+ tail_dropout_p: 0
73
+ torch_compile: true
74
+ compile_ae: true
75
+ compile_ar: true
76
+ compile_gan: false
77
+ ar_ema_halflife: 2500
78
+ ae_ema_halflife: 1000
79
+ ema_sampling: false
80
+ ema_reconstruction: false
81
+ cache_kv: true
82
+ topK: 0.0
83
+ topP: 0.0
84
+ cfg: 0.0
85
+ cfg_schedule: cosine
86
+ cfg_power: 2.75
87
+ temperature: 1.0
88
+ eval_data_dir: ./data/cifar10
89
+ eval_crop_type: center
90
+ eval_fid_ref_path: ./refs/cifar10-32x32_local.npz
91
+ save_dir: ./outputs/flow_matching_tokenizer
92
+ log_freq: 25
93
+ visualize_freq: 5000
94
+ visualize_img_num: 8
95
+ ckpt_freq: 25000
96
+ eval_freq: 25000
97
+ save_ckpt: true
98
+ ar_ckpt_path: ''
99
+ ae_ckpt_path: ''
100
+ resume_optimizer: false
101
+ cond_emb_dim: 32
102
+ greedy_coding_prob: 0.0
103
+ z_len: 64
104
+ z_dim: 512
105
+ vq_type: VQ
106
+ codebook_size: 1024
107
+ norm_layer: RMSNorm
108
+ ffn_type: geglu
109
+ context_type: cross_attn
110
+ hidden_dim: 512
111
+ Encoder:
112
+ input_dim: 48
113
+ output_dim: 512
114
+ cond_dim: 32
115
+ cond_input_dim: 11
116
+ max_seq_len: 64
117
+ ctx_max_seq_len: 64
118
+ dim: 512
119
+ layer_num: 10
120
+ mlp_ratio: 4.0
121
+ heads: 8
122
+ emb_dropout: 0.0
123
+ drop: 0.0
124
+ l2_norm: false
125
+ context_type: cross_attn
126
+ rope: false
127
+ abs_pos: true
128
+ causal: false
129
+ norm_layer: RMSNorm
130
+ ffn_type: geglu
131
+ Quantizer:
132
+ codebook_size: 1024
133
+ z_dim: 12
134
+ dim: 512
135
+ length: 64
136
+ cond_dim: 32
137
+ cond_affine_norm: false
138
+ vq_beta: 0.25
139
+ use_norm: true
140
+ norm_layer: l2
141
+ ffn_type: geglu
142
+ temperature: 1.0
143
+ Decoder:
144
+ input_dim: 48
145
+ ctx_input_dim: 512
146
+ output_dim: 48
147
+ cond_dim: 32
148
+ cond_input_dim: 11
149
+ max_seq_len: 64
150
+ ctx_max_seq_len: 64
151
+ dim: 512
152
+ layer_num: 10
153
+ heads: 8
154
+ emb_dropout: 0.0
155
+ drop: 0.0
156
+ l2_norm: false
157
+ context_type: cross_attn
158
+ rope: false
159
+ abs_pos: true
160
+ causal: false
161
+ norm_layer: RMSNorm
162
+ ffn_type: geglu
163
+ mlp_ratio: 4.0
164
+ zero_out: true
165
+ query_type: noise
166
+ ARModel:
167
+ input_dim: 512
168
+ output_dim: 1024
169
+ max_seq_len: 64
170
+ cond_dim: 32
171
+ cond_input_dim: 11
172
+ dim: 512
173
+ layer_num: 6
174
+ heads: 8
175
+ emb_dropout: 0.0
176
+ drop: 0.0
177
+ l2_norm: false
178
+ context_type: none
179
+ rope: false
180
+ abs_pos: true
181
+ causal: true
182
+ out_act: false
183
+ norm_layer: RMSNorm
184
+ ffn_type: geglu
185
+ mlp_ratio: 4
186
+ zero_out: false
187
+ temperature: 1.0
188
+ GANLoss:
189
+ disc_loss: vanilla
190
+ disc_dim: 64
191
+ disc_type: patchgan
192
+ image_size: 32
193
+ disc_num_layers: 3
194
+ disc_in_channels: 3
195
+ gen_adv_loss: vanilla
196
+ lecam_loss_weight: 0.001
197
+ norm_type: bn
198
+ aug_prob: 1.0
configs/cifar10/tail_dropout_ar.yaml ADDED
@@ -0,0 +1,198 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ wandb_project: wAR
2
+ wandb_name: tail_dropout_ar
3
+ data_dir: ./data/cifar10
4
+ xflip: true
5
+ crop_type: center
6
+ image_size: 32
7
+ num_channels: 3
8
+ num_classes: 11
9
+ patch_size: 4
10
+ use_label: false
11
+ patch_dim: 48
12
+ x_len: 64
13
+ diffusion_decoder: false
14
+ train_ae: false
15
+ train_ar: true
16
+ phases: 200000:DO_PRIOR_AR:1
17
+ batch_size: 512
18
+ eval_batch_size: 512
19
+ lr_enc: 0.00015
20
+ lr_dec: 0.00015
21
+ lr_ar: 0.00015
22
+ lr_gan_loss: 0.00015
23
+ weight_decay_enc: 0.0
24
+ weight_decay_dec: 0.0
25
+ weight_decay_ar: 0.0001
26
+ weight_decay_gan: 0.0001
27
+ lr_enc_min: 3.0e-05
28
+ lr_dec_min: 3.0e-05
29
+ lr_ar_min: 3.0e-05
30
+ lr_gan_loss_min: 3.0e-05
31
+ use_tf32: false
32
+ lr_scheduler: none
33
+ drop: 0.0
34
+ ar_drop: 0.1
35
+ grad_clip: 0.0
36
+ seed: 42
37
+ beta1: 0.5
38
+ beta2: 0.95
39
+ ar_beta1: 0.9
40
+ ar_beta2: 0.99
41
+ label_drop_prob: 0.1
42
+ precision: bf16-mixed
43
+ replace_cur_with_ema: false
44
+ num_workers: 4
45
+ devices: 8
46
+ reverse_t: true
47
+ use_noise_query: false
48
+ use_tail_dropout: true
49
+ l2_v_target: false
50
+ tail_dropout_t2k: 1.0
51
+ tied_timestep: false
52
+ time_stages: 200,400,600,800,1000
53
+ k_per_stage: 192,184,72,48,16
54
+ load_ar_model: false
55
+ load_ar_model_ema: false
56
+ prior_G_loss: false
57
+ use_l1_loss: false
58
+ use_l2_loss: false
59
+ use_lpips_loss: false
60
+ use_gan_loss: false
61
+ use_prior_loss: false
62
+ teacher_forcing_ae: false
63
+ disc_adaptive_weight: false
64
+ lpips_weight: 0.1
65
+ gan_G_weight: 0.1
66
+ prior_enc_weight: 1.0
67
+ l1_weight: 0.1
68
+ l2_weight: 1.0
69
+ gan_start: 5000
70
+ prior_start: 0
71
+ tail_dropout_start: 0
72
+ tail_dropout_p: 0.5
73
+ torch_compile: true
74
+ compile_ae: true
75
+ compile_ar: true
76
+ compile_gan: false
77
+ ar_ema_halflife: 2000
78
+ ae_ema_halflife: 0
79
+ ema_sampling: true
80
+ ema_reconstruction: false
81
+ cache_kv: true
82
+ topK: 0.0
83
+ topP: 0.0
84
+ cfg: 0.0
85
+ cfg_schedule: cosine
86
+ cfg_power: 2.75
87
+ temperature: 1.0
88
+ eval_data_dir: ./data/cifar10
89
+ eval_crop_type: center
90
+ eval_fid_ref_path: ./refs/cifar10-32x32_local.npz
91
+ save_dir: ./outputs/tail_dropout_ar
92
+ log_freq: 25
93
+ visualize_freq: 5000
94
+ visualize_img_num: 8
95
+ ckpt_freq: 25000
96
+ eval_freq: 25000
97
+ save_ckpt: true
98
+ ar_ckpt_path: ''
99
+ ae_ckpt_path: ./outputs/tail_dropout_tokenizer/000/ckpts/last.ckpt
100
+ resume_optimizer: false
101
+ cond_emb_dim: 32
102
+ greedy_coding_prob: 0.0
103
+ z_len: 64
104
+ z_dim: 512
105
+ vq_type: VQ
106
+ codebook_size: 1024
107
+ norm_layer: RMSNorm
108
+ ffn_type: geglu
109
+ context_type: cross_attn
110
+ hidden_dim: 512
111
+ Encoder:
112
+ input_dim: 48
113
+ output_dim: 512
114
+ cond_dim: 32
115
+ cond_input_dim: 11
116
+ max_seq_len: 64
117
+ ctx_max_seq_len: 64
118
+ dim: 512
119
+ layer_num: 10
120
+ mlp_ratio: 4.0
121
+ heads: 8
122
+ emb_dropout: 0.0
123
+ drop: 0.0
124
+ l2_norm: false
125
+ context_type: cross_attn
126
+ rope: false
127
+ abs_pos: true
128
+ causal: false
129
+ norm_layer: RMSNorm
130
+ ffn_type: geglu
131
+ Quantizer:
132
+ codebook_size: 1024
133
+ z_dim: 12
134
+ dim: 512
135
+ length: 64
136
+ cond_dim: 32
137
+ cond_affine_norm: false
138
+ vq_beta: 0.25
139
+ use_norm: true
140
+ norm_layer: l2
141
+ ffn_type: geglu
142
+ temperature: 1.0
143
+ Decoder:
144
+ input_dim: 48
145
+ ctx_input_dim: 512
146
+ output_dim: 48
147
+ cond_dim: 32
148
+ cond_input_dim: 11
149
+ max_seq_len: 64
150
+ ctx_max_seq_len: 64
151
+ dim: 512
152
+ layer_num: 10
153
+ heads: 8
154
+ emb_dropout: 0.0
155
+ drop: 0.0
156
+ l2_norm: false
157
+ context_type: cross_attn
158
+ rope: false
159
+ abs_pos: true
160
+ causal: false
161
+ norm_layer: RMSNorm
162
+ ffn_type: geglu
163
+ mlp_ratio: 4.0
164
+ zero_out: true
165
+ query_type: learnable
166
+ ARModel:
167
+ input_dim: 512
168
+ output_dim: 1024
169
+ max_seq_len: 64
170
+ cond_dim: 32
171
+ cond_input_dim: 11
172
+ dim: 512
173
+ layer_num: 13
174
+ heads: 8
175
+ emb_dropout: 0.0
176
+ drop: 0.1
177
+ l2_norm: false
178
+ context_type: none
179
+ rope: false
180
+ abs_pos: true
181
+ causal: true
182
+ out_act: false
183
+ norm_layer: RMSNorm
184
+ ffn_type: geglu
185
+ mlp_ratio: 4.0
186
+ zero_out: false
187
+ temperature: 1.0
188
+ GANLoss:
189
+ disc_loss: vanilla
190
+ disc_dim: 64
191
+ disc_type: patchgan
192
+ image_size: 32
193
+ disc_num_layers: 3
194
+ disc_in_channels: 3
195
+ gen_adv_loss: vanilla
196
+ lecam_loss_weight: 0.001
197
+ norm_type: bn
198
+ aug_prob: 1.0
configs/cifar10/tail_dropout_tokenizer.yaml ADDED
@@ -0,0 +1,198 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ wandb_project: wAR
2
+ wandb_name: tail_dropout_tokenizer
3
+ data_dir: ./data/cifar10
4
+ xflip: true
5
+ crop_type: center
6
+ image_size: 32
7
+ num_channels: 3
8
+ num_classes: 11
9
+ patch_size: 4
10
+ use_label: false
11
+ patch_dim: 48
12
+ x_len: 64
13
+ diffusion_decoder: false
14
+ train_ae: true
15
+ train_ar: false
16
+ phases: 400000:DO_L1-DO_L2-DO_LPIPS:1
17
+ batch_size: 512
18
+ eval_batch_size: 512
19
+ lr_enc: 0.00015
20
+ lr_dec: 0.00015
21
+ lr_ar: 0.00015
22
+ lr_gan_loss: 0.00015
23
+ weight_decay_enc: 0.0
24
+ weight_decay_dec: 0.0
25
+ weight_decay_ar: 0.0
26
+ weight_decay_gan: 0.0001
27
+ lr_enc_min: 3.0e-05
28
+ lr_dec_min: 3.0e-05
29
+ lr_ar_min: 3.0e-05
30
+ lr_gan_loss_min: 3.0e-05
31
+ use_tf32: false
32
+ lr_scheduler: none
33
+ drop: 0.0
34
+ ar_drop: 0.0
35
+ grad_clip: 0.0
36
+ seed: 42
37
+ beta1: 0.5
38
+ beta2: 0.95
39
+ ar_beta1: 0.5
40
+ ar_beta2: 0.95
41
+ label_drop_prob: 0.1
42
+ precision: bf16-mixed
43
+ replace_cur_with_ema: false
44
+ num_workers: 4
45
+ devices: 8
46
+ reverse_t: false
47
+ use_noise_query: false
48
+ use_tail_dropout: true
49
+ l2_v_target: false
50
+ tail_dropout_t2k: 1.0
51
+ tied_timestep: false
52
+ time_stages: 200,400,600,800,1000
53
+ k_per_stage: 192,184,72,48,16
54
+ load_ar_model: false
55
+ load_ar_model_ema: false
56
+ prior_G_loss: false
57
+ use_l1_loss: true
58
+ use_l2_loss: true
59
+ use_lpips_loss: true
60
+ use_gan_loss: false
61
+ use_prior_loss: false
62
+ teacher_forcing_ae: false
63
+ disc_adaptive_weight: false
64
+ lpips_weight: 0.1
65
+ gan_G_weight: 0.1
66
+ prior_enc_weight: 1.0
67
+ l1_weight: 0.1
68
+ l2_weight: 1.0
69
+ gan_start: 5000
70
+ prior_start: 0
71
+ tail_dropout_start: 0
72
+ tail_dropout_p: 0.5
73
+ torch_compile: true
74
+ compile_ae: true
75
+ compile_ar: true
76
+ compile_gan: false
77
+ ar_ema_halflife: 2500
78
+ ae_ema_halflife: 1000
79
+ ema_sampling: false
80
+ ema_reconstruction: false
81
+ cache_kv: true
82
+ topK: 0.0
83
+ topP: 0.0
84
+ cfg: 0.0
85
+ cfg_schedule: cosine
86
+ cfg_power: 2.75
87
+ temperature: 1.0
88
+ eval_data_dir: ./data/cifar10
89
+ eval_crop_type: center
90
+ eval_fid_ref_path: ./refs/cifar10-32x32_local.npz
91
+ save_dir: ./outputs/tail_dropout_tokenizer
92
+ log_freq: 25
93
+ visualize_freq: 5000
94
+ visualize_img_num: 8
95
+ ckpt_freq: 25000
96
+ eval_freq: 25000
97
+ save_ckpt: true
98
+ ar_ckpt_path: ''
99
+ ae_ckpt_path: ''
100
+ resume_optimizer: false
101
+ cond_emb_dim: 32
102
+ greedy_coding_prob: 0.0
103
+ z_len: 64
104
+ z_dim: 512
105
+ vq_type: VQ
106
+ codebook_size: 1024
107
+ norm_layer: RMSNorm
108
+ ffn_type: geglu
109
+ context_type: cross_attn
110
+ hidden_dim: 512
111
+ Encoder:
112
+ input_dim: 48
113
+ output_dim: 512
114
+ cond_dim: 32
115
+ cond_input_dim: 11
116
+ max_seq_len: 64
117
+ ctx_max_seq_len: 64
118
+ dim: 512
119
+ layer_num: 10
120
+ mlp_ratio: 4.0
121
+ heads: 8
122
+ emb_dropout: 0.0
123
+ drop: 0.0
124
+ l2_norm: false
125
+ context_type: cross_attn
126
+ rope: false
127
+ abs_pos: true
128
+ causal: false
129
+ norm_layer: RMSNorm
130
+ ffn_type: geglu
131
+ Quantizer:
132
+ codebook_size: 1024
133
+ z_dim: 12
134
+ dim: 512
135
+ length: 64
136
+ cond_dim: 32
137
+ cond_affine_norm: false
138
+ vq_beta: 0.25
139
+ use_norm: true
140
+ norm_layer: l2
141
+ ffn_type: geglu
142
+ temperature: 1.0
143
+ Decoder:
144
+ input_dim: 48
145
+ ctx_input_dim: 512
146
+ output_dim: 48
147
+ cond_dim: 32
148
+ cond_input_dim: 11
149
+ max_seq_len: 64
150
+ ctx_max_seq_len: 64
151
+ dim: 512
152
+ layer_num: 10
153
+ heads: 8
154
+ emb_dropout: 0.0
155
+ drop: 0.0
156
+ l2_norm: false
157
+ context_type: cross_attn
158
+ rope: false
159
+ abs_pos: true
160
+ causal: false
161
+ norm_layer: RMSNorm
162
+ ffn_type: geglu
163
+ mlp_ratio: 4.0
164
+ zero_out: true
165
+ query_type: learnable
166
+ ARModel:
167
+ input_dim: 512
168
+ output_dim: 1024
169
+ max_seq_len: 64
170
+ cond_dim: 32
171
+ cond_input_dim: 11
172
+ dim: 512
173
+ layer_num: 6
174
+ heads: 8
175
+ emb_dropout: 0.0
176
+ drop: 0.0
177
+ l2_norm: false
178
+ context_type: none
179
+ rope: false
180
+ abs_pos: true
181
+ causal: true
182
+ out_act: false
183
+ norm_layer: RMSNorm
184
+ ffn_type: geglu
185
+ mlp_ratio: 4
186
+ zero_out: false
187
+ temperature: 1.0
188
+ GANLoss:
189
+ disc_loss: vanilla
190
+ disc_dim: 64
191
+ disc_type: patchgan
192
+ image_size: 32
193
+ disc_num_layers: 3
194
+ disc_in_channels: 3
195
+ gen_adv_loss: vanilla
196
+ lecam_loss_weight: 0.001
197
+ norm_type: bn
198
+ aug_prob: 1.0
configs/cifar10/war_ar.yaml ADDED
@@ -0,0 +1,201 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ wandb_project: wAR
2
+ wandb_name: war_ar
3
+ data_dir: ./data/cifar10
4
+ xflip: true
5
+ crop_type: center
6
+ image_size: 32
7
+ num_channels: 3
8
+ num_classes: 11
9
+ patch_size: 4
10
+ use_label: false
11
+ patch_dim: 48
12
+ x_len: 64
13
+ diffusion_decoder: false
14
+ train_ae: false
15
+ train_ar: true
16
+ phases: 200000:DO_PRIOR_AR:1
17
+ batch_size: 512
18
+ eval_batch_size: 512
19
+ lr_enc: 0.00015
20
+ lr_dec: 0.00015
21
+ lr_ar: 0.00015
22
+ lr_gan_loss: 0.00015
23
+ weight_decay_enc: 0.0
24
+ weight_decay_dec: 0.0
25
+ weight_decay_ar: 0.0001
26
+ weight_decay_gan: 0.0001
27
+ lr_enc_min: 3.0e-05
28
+ lr_dec_min: 3.0e-05
29
+ lr_ar_min: 3.0e-05
30
+ lr_gan_loss_min: 3.0e-05
31
+ use_tf32: false
32
+ lr_scheduler: none
33
+ drop: 0.0
34
+ ar_drop: 0.1
35
+ grad_clip: 0.0
36
+ seed: 42
37
+ beta1: 0.5
38
+ beta2: 0.95
39
+ ar_beta1: 0.9
40
+ ar_beta2: 0.99
41
+ label_drop_prob: 0.1
42
+ precision: bf16-mixed
43
+ replace_cur_with_ema: false
44
+ num_workers: 4
45
+ devices: 8
46
+ reverse_t: true
47
+ use_noise_query: false
48
+ use_tail_dropout: false
49
+ l2_v_target: false
50
+ tail_dropout_t2k: 1.0
51
+ tied_timestep: false
52
+ time_stages: 200,400,600,800,1000
53
+ k_per_stage: 192,184,72,48,16
54
+ load_ar_model: false
55
+ load_ar_model_ema: false
56
+ prior_G_loss: false
57
+ use_l1_loss: false
58
+ use_l2_loss: false
59
+ use_lpips_loss: false
60
+ use_gan_loss: false
61
+ use_prior_loss: false
62
+ teacher_forcing_ae: false
63
+ disc_adaptive_weight: false
64
+ lpips_weight: 0.1
65
+ gan_G_weight: 0.1
66
+ prior_enc_weight: 1.0
67
+ l1_weight: 0.1
68
+ l2_weight: 1.0
69
+ gan_start: 5000
70
+ prior_start: 0
71
+ tail_dropout_start: 0
72
+ tail_dropout_p: 0.0
73
+ torch_compile: true
74
+ compile_ae: true
75
+ compile_ar: true
76
+ compile_gan: false
77
+ ar_ema_halflife: 2000
78
+ ae_ema_halflife: 0
79
+ ema_sampling: true
80
+ ema_reconstruction: false
81
+ cache_kv: true
82
+ topK: 0.0
83
+ topP: 0.0
84
+ cfg: 0.0
85
+ cfg_schedule: cosine
86
+ cfg_power: 2.75
87
+ temperature: 1.0
88
+ eval_data_dir: ./data/cifar10
89
+ eval_crop_type: center
90
+ eval_fid_ref_path: ./refs/cifar10-32x32_local.npz
91
+ save_dir: ./outputs/war_ar
92
+ log_freq: 25
93
+ visualize_freq: 5000
94
+ visualize_img_num: 8
95
+ ckpt_freq: 25000
96
+ eval_freq: 25000
97
+ save_ckpt: true
98
+ ar_ckpt_path: ''
99
+ ae_ckpt_path: ./outputs/war_tokenizer/000/ckpts/last.ckpt
100
+ resume_optimizer: false
101
+ cond_emb_dim: 32
102
+ greedy_coding_prob: 0.0
103
+ z_len: 64
104
+ z_dim: 512
105
+ vq_type: VQ
106
+ codebook_size: 1024
107
+ norm_layer: RMSNorm
108
+ ffn_type: geglu
109
+ context_type: cross_attn
110
+ hidden_dim: 512
111
+ Encoder:
112
+ input_dim: 48
113
+ output_dim: 512
114
+ cond_dim: 32
115
+ cond_input_dim: 11
116
+ max_seq_len: 64
117
+ ctx_max_seq_len: 64
118
+ dim: 512
119
+ layer_num: 10
120
+ mlp_ratio: 4.0
121
+ heads: 8
122
+ emb_dropout: 0.0
123
+ drop: 0.0
124
+ l2_norm: false
125
+ context_type: cross_attn
126
+ rope: false
127
+ abs_pos: true
128
+ causal: false
129
+ norm_layer: RMSNorm
130
+ ffn_type: geglu
131
+ Quantizer:
132
+ codebook_size: 1024
133
+ z_dim: 12
134
+ dim: 512
135
+ length: 64
136
+ cond_dim: 32
137
+ cond_affine_norm: false
138
+ vq_beta: 0.25
139
+ use_norm: true
140
+ norm_layer: l2
141
+ ffn_type: geglu
142
+ temperature: 0.05
143
+ Decoder:
144
+ input_dim: 48
145
+ ctx_input_dim: 512
146
+ output_dim: 48
147
+ cond_dim: 32
148
+ cond_input_dim: 11
149
+ max_seq_len: 64
150
+ ctx_max_seq_len: 64
151
+ dim: 512
152
+ layer_num: 10
153
+ heads: 8
154
+ emb_dropout: 0.0
155
+ drop: 0.0
156
+ l2_norm: false
157
+ context_type: cross_attn
158
+ rope: false
159
+ abs_pos: true
160
+ causal: false
161
+ norm_layer: RMSNorm
162
+ ffn_type: geglu
163
+ mlp_ratio: 4.0
164
+ zero_out: true
165
+ query_type: learnable
166
+ ARModel:
167
+ input_dim: 512
168
+ output_dim: 1024
169
+ max_seq_len: 64
170
+ cond_dim: 32
171
+ cond_input_dim: 11
172
+ dim: 512
173
+ layer_num: 13
174
+ heads: 8
175
+ emb_dropout: 0.0
176
+ drop: 0.1
177
+ l2_norm: false
178
+ context_type: none
179
+ rope: false
180
+ abs_pos: true
181
+ causal: true
182
+ out_act: false
183
+ norm_layer: RMSNorm
184
+ ffn_type: geglu
185
+ mlp_ratio: 4.0
186
+ zero_out: false
187
+ temperature: 1.0
188
+ GANLoss:
189
+ disc_loss: vanilla
190
+ disc_dim: 64
191
+ disc_type: patchgan
192
+ image_size: 32
193
+ disc_num_layers: 3
194
+ disc_in_channels: 3
195
+ gen_adv_loss: vanilla
196
+ lecam_loss_weight: 0.001
197
+ norm_type: bn
198
+ aug_prob: 1.0
199
+ prior_ar_temp_low: 0.1
200
+ prior_ar_temp_high: 1.0
201
+ prior_ar_temp_schedule: constant
configs/cifar10/war_tokenizer.yaml ADDED
@@ -0,0 +1,201 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ wandb_project: wAR
2
+ wandb_name: war_tokenizer
3
+ data_dir: ./data/cifar10
4
+ xflip: true
5
+ crop_type: center
6
+ image_size: 32
7
+ num_channels: 3
8
+ num_classes: 11
9
+ patch_size: 4
10
+ use_label: false
11
+ patch_dim: 48
12
+ x_len: 64
13
+ diffusion_decoder: false
14
+ train_ae: true
15
+ train_ar: true
16
+ phases: 400000:DO_L1-DO_L2-DO_LPIPS-DO_PRIOR_ENC-DO_PRIOR_AR:1
17
+ batch_size: 512
18
+ eval_batch_size: 512
19
+ lr_enc: 0.00015
20
+ lr_dec: 0.00015
21
+ lr_ar: 0.00015
22
+ lr_gan_loss: 0.00015
23
+ weight_decay_enc: 0.0
24
+ weight_decay_dec: 0.0
25
+ weight_decay_ar: 0.0
26
+ weight_decay_gan: 0.0001
27
+ lr_enc_min: 3.0e-05
28
+ lr_dec_min: 3.0e-05
29
+ lr_ar_min: 3.0e-05
30
+ lr_gan_loss_min: 3.0e-05
31
+ use_tf32: false
32
+ lr_scheduler: none
33
+ drop: 0.0
34
+ ar_drop: 0.0
35
+ grad_clip: 0.0
36
+ seed: 42
37
+ beta1: 0.5
38
+ beta2: 0.95
39
+ ar_beta1: 0.5
40
+ ar_beta2: 0.95
41
+ label_drop_prob: 0.1
42
+ precision: bf16-mixed
43
+ replace_cur_with_ema: false
44
+ num_workers: 4
45
+ devices: 8
46
+ reverse_t: true
47
+ use_noise_query: false
48
+ use_tail_dropout: false
49
+ l2_v_target: false
50
+ tail_dropout_t2k: 1.0
51
+ tied_timestep: false
52
+ time_stages: 200,400,600,800,1000
53
+ k_per_stage: 192,184,72,48,16
54
+ load_ar_model: false
55
+ load_ar_model_ema: false
56
+ prior_G_loss: false
57
+ use_l1_loss: true
58
+ use_l2_loss: true
59
+ use_lpips_loss: true
60
+ use_gan_loss: false
61
+ use_prior_loss: true
62
+ teacher_forcing_ae: false
63
+ disc_adaptive_weight: false
64
+ lpips_weight: 0.1
65
+ gan_G_weight: 0.1
66
+ prior_enc_weight: 1.0
67
+ l1_weight: 0.1
68
+ l2_weight: 1.0
69
+ gan_start: 5000
70
+ prior_start: 0
71
+ tail_dropout_start: 0
72
+ tail_dropout_p: 0.0
73
+ torch_compile: true
74
+ compile_ae: true
75
+ compile_ar: true
76
+ compile_gan: false
77
+ ar_ema_halflife: 0
78
+ ae_ema_halflife: 0
79
+ ema_sampling: false
80
+ ema_reconstruction: false
81
+ cache_kv: true
82
+ topK: 0.0
83
+ topP: 0.0
84
+ cfg: 0.0
85
+ cfg_schedule: cosine
86
+ cfg_power: 2.75
87
+ temperature: 1.0
88
+ eval_data_dir: ./data/cifar10
89
+ eval_crop_type: center
90
+ eval_fid_ref_path: ./refs/cifar10-32x32_local.npz
91
+ save_dir: ./outputs/war_tokenizer
92
+ log_freq: 25
93
+ visualize_freq: 5000
94
+ visualize_img_num: 8
95
+ ckpt_freq: 25000
96
+ eval_freq: 25000
97
+ save_ckpt: true
98
+ ar_ckpt_path: ''
99
+ ae_ckpt_path: ''
100
+ resume_optimizer: false
101
+ cond_emb_dim: 32
102
+ greedy_coding_prob: 0.0
103
+ z_len: 64
104
+ z_dim: 512
105
+ vq_type: VQ
106
+ codebook_size: 1024
107
+ norm_layer: RMSNorm
108
+ ffn_type: geglu
109
+ context_type: cross_attn
110
+ hidden_dim: 512
111
+ Encoder:
112
+ input_dim: 48
113
+ output_dim: 512
114
+ cond_dim: 32
115
+ cond_input_dim: 11
116
+ max_seq_len: 64
117
+ ctx_max_seq_len: 64
118
+ dim: 512
119
+ layer_num: 10
120
+ mlp_ratio: 4.0
121
+ heads: 8
122
+ emb_dropout: 0.0
123
+ drop: 0.0
124
+ l2_norm: false
125
+ context_type: cross_attn
126
+ rope: false
127
+ abs_pos: true
128
+ causal: false
129
+ norm_layer: RMSNorm
130
+ ffn_type: geglu
131
+ Quantizer:
132
+ codebook_size: 1024
133
+ z_dim: 12
134
+ dim: 512
135
+ length: 64
136
+ cond_dim: 32
137
+ cond_affine_norm: false
138
+ vq_beta: 0.25
139
+ use_norm: true
140
+ norm_layer: l2
141
+ ffn_type: geglu
142
+ temperature: 0.05
143
+ Decoder:
144
+ input_dim: 48
145
+ ctx_input_dim: 512
146
+ output_dim: 48
147
+ cond_dim: 32
148
+ cond_input_dim: 11
149
+ max_seq_len: 64
150
+ ctx_max_seq_len: 64
151
+ dim: 512
152
+ layer_num: 10
153
+ heads: 8
154
+ emb_dropout: 0.0
155
+ drop: 0.0
156
+ l2_norm: false
157
+ context_type: cross_attn
158
+ rope: false
159
+ abs_pos: true
160
+ causal: false
161
+ norm_layer: RMSNorm
162
+ ffn_type: geglu
163
+ mlp_ratio: 4.0
164
+ zero_out: true
165
+ query_type: learnable
166
+ ARModel:
167
+ input_dim: 512
168
+ output_dim: 1024
169
+ max_seq_len: 64
170
+ cond_dim: 32
171
+ cond_input_dim: 11
172
+ dim: 512
173
+ layer_num: 4
174
+ heads: 8
175
+ emb_dropout: 0.0
176
+ drop: 0.0
177
+ l2_norm: false
178
+ context_type: none
179
+ rope: false
180
+ abs_pos: true
181
+ causal: true
182
+ out_act: false
183
+ norm_layer: RMSNorm
184
+ ffn_type: geglu
185
+ mlp_ratio: 4
186
+ zero_out: false
187
+ temperature: 1.0
188
+ GANLoss:
189
+ disc_loss: vanilla
190
+ disc_dim: 64
191
+ disc_type: patchgan
192
+ image_size: 32
193
+ disc_num_layers: 3
194
+ disc_in_channels: 3
195
+ gen_adv_loss: vanilla
196
+ lecam_loss_weight: 0.001
197
+ norm_type: bn
198
+ aug_prob: 1.0
199
+ prior_ar_temp_low: 0.1
200
+ prior_ar_temp_high: 1.0
201
+ prior_ar_temp_schedule: constant
dataset.py ADDED
@@ -0,0 +1,415 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ #
3
+ # Modified work Copyright 2025 Bowen Zheng
4
+ # The Chinese University of Hongkong, Shenzhen
5
+ #
6
+ # This work is licensed under a Creative Commons
7
+ # Attribution-NonCommercial-ShareAlike 4.0 International License.
8
+ # You should have received a copy of the license along with this
9
+ # work. If not, see http://creativecommons.org/licenses/by-nc-sa/4.0/
10
+
11
+ import os
12
+ import sys
13
+ import math
14
+ import subprocess
15
+ import numpy as np
16
+ import zipfile
17
+ import PIL.Image
18
+ import json
19
+ import torch
20
+ import dnnlib
21
+ from pathlib import Path
22
+
23
+ try:
24
+ import pyspng
25
+ except ImportError:
26
+ pyspng = None
27
+
28
+ #----------------------------------------------------------------------------
29
+ # Simple augmentations (center crop / random crop) adapted from ADM
30
+
31
+ def center_crop_arr(pil_image, image_size):
32
+ """
33
+ Center cropping implementation from ADM.
34
+ https://github.com/openai/guided-diffusion/blob/8fb3ad9197f16bbc40620447b2742e13458d2831/guided_diffusion/image_datasets.py#L126
35
+ """
36
+ while min(*pil_image.size) >= 2 * image_size:
37
+ pil_image = pil_image.resize(
38
+ tuple(x // 2 for x in pil_image.size), resample=PIL.Image.BOX
39
+ )
40
+
41
+ scale = image_size / min(*pil_image.size)
42
+ pil_image = pil_image.resize(
43
+ tuple(round(x * scale) for x in pil_image.size), resample=PIL.Image.BICUBIC
44
+ )
45
+
46
+ arr = np.array(pil_image)
47
+ crop_y = (arr.shape[0] - image_size) // 2
48
+ crop_x = (arr.shape[1] - image_size) // 2
49
+ return PIL.Image.fromarray(arr[crop_y: crop_y + image_size, crop_x: crop_x + image_size])
50
+
51
+
52
+ def random_crop_arr(pil_image, image_size, min_crop_frac=0.8, max_crop_frac=1.0, rng=None):
53
+ min_smaller_dim_size = math.ceil(image_size / max_crop_frac)
54
+ max_smaller_dim_size = math.ceil(image_size / min_crop_frac)
55
+ # Use an explicit RNG when provided (e.g., deterministic per-sample cropping).
56
+ # Otherwise fall back to the global NumPy RNG (which can be controlled via seeding).
57
+ rng = np.random if rng is None else rng
58
+ smaller_dim_size = rng.randint(min_smaller_dim_size, max_smaller_dim_size + 1)
59
+
60
+ # Repeated BOX downsampling preserves the original crop preprocessing.
61
+ while min(*pil_image.size) >= 2 * smaller_dim_size:
62
+ pil_image = pil_image.resize(
63
+ tuple(x // 2 for x in pil_image.size), resample=PIL.Image.BOX
64
+ )
65
+
66
+ scale = smaller_dim_size / min(*pil_image.size)
67
+ pil_image = pil_image.resize(
68
+ tuple(round(x * scale) for x in pil_image.size), resample=PIL.Image.BICUBIC
69
+ )
70
+
71
+ arr = np.array(pil_image)
72
+ crop_y = rng.randint(0, arr.shape[0] - image_size + 1)
73
+ crop_x = rng.randint(0, arr.shape[1] - image_size + 1)
74
+ return PIL.Image.fromarray(arr[crop_y: crop_y + image_size, crop_x: crop_x + image_size])
75
+
76
+ class Dataset(torch.utils.data.Dataset):
77
+ def __init__(self,
78
+ name, # Name of the dataset.
79
+ raw_shape, # Shape of the raw image data (NCHW).
80
+ max_size = None, # Artificially limit the size of the dataset. None = no limit. Applied before xflip.
81
+ use_label = False, # Enable conditioning labels? False = label dimension is zero.
82
+ xflip = False, # Artificially double the size of the dataset via x-flips. Applied after max_size.
83
+ random_seed = 0, # Random seed to use when applying max_size.
84
+ ):
85
+ self._name = name
86
+ self._raw_shape = list(raw_shape)
87
+ self._use_label = use_label
88
+ self._raw_labels = None
89
+ self._label_shape = None
90
+
91
+ # Apply max_size.
92
+ self._raw_idx = np.arange(self._raw_shape[0], dtype=np.int64)
93
+ if (max_size is not None) and (self._raw_idx.size > max_size):
94
+ np.random.RandomState(random_seed % (1 << 31)).shuffle(self._raw_idx)
95
+ self._raw_idx = np.sort(self._raw_idx[:max_size])
96
+
97
+ # Apply xflip.
98
+ self._xflip = np.zeros(self._raw_idx.size, dtype=np.uint8)
99
+ if xflip:
100
+ self._raw_idx = np.tile(self._raw_idx, 2)
101
+ self._xflip = np.concatenate([self._xflip, np.ones_like(self._xflip)])
102
+
103
+ def _get_raw_labels(self):
104
+ if self._raw_labels is None:
105
+ self._raw_labels = self._load_raw_labels() if self._use_label else None
106
+ if self._raw_labels is None:
107
+ self._raw_labels = np.zeros([self._raw_shape[0], 0], dtype=np.float32)
108
+ assert isinstance(self._raw_labels, np.ndarray)
109
+ assert self._raw_labels.shape[0] == self._raw_shape[0]
110
+ assert self._raw_labels.dtype in [np.float32, np.int64]
111
+ if self._raw_labels.dtype == np.int64:
112
+ assert self._raw_labels.ndim == 1
113
+ assert np.all(self._raw_labels >= 0)
114
+ return self._raw_labels
115
+
116
+ def close(self): # to be overridden by subclass
117
+ pass
118
+
119
+ def _load_raw_image(self, raw_idx): # to be overridden by subclass
120
+ raise NotImplementedError
121
+
122
+ def _load_raw_labels(self): # to be overridden by subclass
123
+ raise NotImplementedError
124
+
125
+ def __getstate__(self):
126
+ return dict(self.__dict__, _raw_labels=None)
127
+
128
+ def __del__(self):
129
+ try:
130
+ self.close()
131
+ except:
132
+ pass
133
+
134
+ def __len__(self):
135
+ return self._raw_idx.size
136
+
137
+ def __getitem__(self, idx):
138
+ raw_idx = self._raw_idx[idx]
139
+ image = self._load_raw_image(raw_idx)
140
+ assert isinstance(image, np.ndarray)
141
+ assert list(image.shape) == self.image_shape
142
+ assert image.dtype == np.uint8
143
+ if self._xflip[idx]:
144
+ assert image.ndim == 3 # CHW
145
+ image = image[:, :, ::-1]
146
+ return image.copy(), self.get_label(idx)
147
+
148
+ def get_label(self, idx):
149
+ label = self._get_raw_labels()[self._raw_idx[idx]]
150
+ if label.dtype == np.int64:
151
+ onehot = np.zeros(self.label_shape, dtype=np.float32)
152
+ onehot[label] = 1
153
+ label = onehot
154
+ return label.copy()
155
+
156
+ def get_details(self, idx):
157
+ d = dnnlib.EasyDict()
158
+ d.raw_idx = int(self._raw_idx[idx])
159
+ d.xflip = (int(self._xflip[idx]) != 0)
160
+ d.raw_label = self._get_raw_labels()[d.raw_idx].copy()
161
+ return d
162
+
163
+ @property
164
+ def name(self):
165
+ return self._name
166
+
167
+ @property
168
+ def image_shape(self):
169
+ return list(self._raw_shape[1:])
170
+
171
+ @property
172
+ def num_channels(self):
173
+ assert len(self.image_shape) == 3 # CHW
174
+ return self.image_shape[0]
175
+
176
+ @property
177
+ def resolution(self):
178
+ assert len(self.image_shape) == 3 # CHW
179
+ assert self.image_shape[1] == self.image_shape[2]
180
+ return self.image_shape[1]
181
+
182
+ @property
183
+ def label_shape(self):
184
+ if self._label_shape is None:
185
+ raw_labels = self._get_raw_labels()
186
+ if raw_labels.dtype == np.int64:
187
+ self._label_shape = [int(np.max(raw_labels)) + 1]
188
+ else:
189
+ self._label_shape = raw_labels.shape[1:]
190
+ return list(self._label_shape)
191
+
192
+ @property
193
+ def label_dim(self):
194
+ assert len(self.label_shape) == 1
195
+ return self.label_shape[0]
196
+
197
+ @property
198
+ def has_labels(self):
199
+ return any(x != 0 for x in self.label_shape)
200
+
201
+ @property
202
+ def has_onehot_labels(self):
203
+ return self._get_raw_labels().dtype == np.int64
204
+
205
+ # ------------------------------------------------------------------------
206
+ # Simple augmentations (center crop / random crop) as Dataset helpers,
207
+ def _center_crop_arr(self, pil_image, image_size):
208
+ """
209
+ Center cropping implementation from ADM.
210
+ https://github.com/openai/guided-diffusion/blob/8fb3ad9197f16bbc40620447b2742e13458d2831/guided_diffusion/image_datasets.py#L126
211
+ """
212
+ while min(*pil_image.size) >= 2 * image_size:
213
+ pil_image = pil_image.resize(
214
+ tuple(x // 2 for x in pil_image.size), resample=PIL.Image.BOX
215
+ )
216
+
217
+ scale = image_size / min(*pil_image.size)
218
+ pil_image = pil_image.resize(
219
+ tuple(round(x * scale) for x in pil_image.size), resample=PIL.Image.BICUBIC
220
+ )
221
+
222
+ arr = np.array(pil_image)
223
+ crop_y = (arr.shape[0] - image_size) // 2
224
+ crop_x = (arr.shape[1] - image_size) // 2
225
+ return PIL.Image.fromarray(arr[crop_y: crop_y + image_size, crop_x: crop_x + image_size])
226
+
227
+ def _random_crop_arr(self, pil_image, image_size, min_crop_frac=0.8, max_crop_frac=1.0, rng=None):
228
+ min_smaller_dim_size = math.ceil(image_size / max_crop_frac)
229
+ max_smaller_dim_size = math.ceil(image_size / min_crop_frac)
230
+ rng = np.random if rng is None else rng
231
+ smaller_dim_size = rng.randint(min_smaller_dim_size, max_smaller_dim_size + 1)
232
+
233
+ # Repeated BOX downsampling preserves the original crop preprocessing.
234
+ while min(*pil_image.size) >= 2 * smaller_dim_size:
235
+ pil_image = pil_image.resize(
236
+ tuple(x // 2 for x in pil_image.size), resample=PIL.Image.BOX
237
+ )
238
+
239
+ scale = smaller_dim_size / min(*pil_image.size)
240
+ pil_image = pil_image.resize(
241
+ tuple(round(x * scale) for x in pil_image.size), resample=PIL.Image.BICUBIC
242
+ )
243
+
244
+ arr = np.array(pil_image)
245
+ crop_y = rng.randint(0, arr.shape[0] - image_size + 1)
246
+ crop_x = rng.randint(0, arr.shape[1] - image_size + 1)
247
+ return PIL.Image.fromarray(arr[crop_y: crop_y + image_size, crop_x: crop_x + image_size])
248
+
249
+ class ImageFolderDataset(Dataset):
250
+ def __init__(self,
251
+ path, # Path to directory or zip.
252
+ resolution = None, # Ensure specific resolution, None = highest available.
253
+ use_pyspng = True, # Use pyspng if available?
254
+ crop_type = None, # None / 'center' / 'random'
255
+ deterministic_crop = False, # If True and crop_type=='random', crop depends only on (crop_seed, raw_idx).
256
+ crop_seed = 0,
257
+ **super_kwargs, # Additional arguments for the Dataset base class.
258
+ ):
259
+ self._path = path
260
+ self._use_pyspng = use_pyspng
261
+ self._zipfile = None
262
+ self._crop_type = crop_type
263
+ self._resolution = resolution
264
+ self._deterministic_crop = bool(deterministic_crop)
265
+ self._crop_seed = int(crop_seed)
266
+
267
+ # If labels are requested and no dataset.json is present, try to
268
+ # create it in-place using dataset_tools.py (labels-only mode).
269
+ use_label = bool(super_kwargs.get('use_label', False))
270
+ if use_label:
271
+ self._ensure_dataset_json()
272
+
273
+ if os.path.isdir(self._path):
274
+ self._type = 'dir'
275
+ self._all_fnames = {os.path.relpath(os.path.join(root, fname), start=self._path) for root, _dirs, files in os.walk(self._path) for fname in files}
276
+ elif self._file_ext(self._path) == '.zip':
277
+ self._type = 'zip'
278
+ self._all_fnames = set(self._get_zipfile().namelist())
279
+ else:
280
+ raise IOError('Path must point to a directory or zip')
281
+
282
+ PIL.Image.init()
283
+ self._image_fnames = sorted(fname for fname in self._all_fnames if self._file_ext(fname) in PIL.Image.EXTENSION)
284
+ if len(self._image_fnames) == 0:
285
+ raise IOError('No image files found in the specified path')
286
+
287
+ name = os.path.splitext(os.path.basename(self._path))[0]
288
+ raw_shape = [len(self._image_fnames)] + list(self._load_raw_image(0).shape)
289
+ if resolution is not None and (raw_shape[2] != resolution or raw_shape[3] != resolution):
290
+ raise IOError('Image files do not match the specified resolution')
291
+ super().__init__(name=name, raw_shape=raw_shape, **super_kwargs)
292
+
293
+ @staticmethod
294
+ def _file_ext(fname):
295
+ return os.path.splitext(fname)[1].lower()
296
+
297
+ def _get_zipfile(self):
298
+ assert self._type == 'zip'
299
+ if self._zipfile is None:
300
+ self._zipfile = zipfile.ZipFile(self._path)
301
+ return self._zipfile
302
+
303
+ def _open_file(self, fname):
304
+ if self._type == 'dir':
305
+ return open(os.path.join(self._path, fname), 'rb')
306
+ if self._type == 'zip':
307
+ return self._get_zipfile().open(fname, 'r')
308
+ return None
309
+
310
+ def close(self):
311
+ try:
312
+ if self._zipfile is not None:
313
+ self._zipfile.close()
314
+ finally:
315
+ self._zipfile = None
316
+
317
+ def __getstate__(self):
318
+ return dict(super().__getstate__(), _zipfile=None)
319
+
320
+ def _ensure_dataset_json(self):
321
+ """Ensure dataset.json with labels exists for this dataset path.
322
+
323
+ If missing, call dataset_tools.py in labels-only mode to generate it.
324
+ """
325
+ # Directory: check for dataset.json at root.
326
+ if os.path.isdir(self._path):
327
+ json_path = os.path.join(self._path, 'dataset.json')
328
+ if os.path.isfile(json_path):
329
+ return
330
+ # Zip: check if dataset.json is inside archive.
331
+ elif self._file_ext(self._path) == '.zip':
332
+ try:
333
+ with zipfile.ZipFile(self._path, mode='r') as z:
334
+ if 'dataset.json' in z.namelist():
335
+ return
336
+ except Exception:
337
+ return
338
+ else:
339
+ return
340
+
341
+ # Use the dataset preparation tool bundled with this package.
342
+ project_root = Path(__file__).resolve().parent
343
+ tools_path = project_root / 'scripts' / 'dataset_tools.py'
344
+ if not tools_path.is_file():
345
+ print(f"Warning: dataset_tools.py not found at {tools_path}, cannot auto-generate dataset.json labels.")
346
+ return
347
+
348
+ cmd = [
349
+ sys.executable,
350
+ str(tools_path),
351
+ '--source', str(self._path),
352
+ '--dest', str(self._path),
353
+ '--labels-only',
354
+ ]
355
+ try:
356
+ subprocess.run(cmd, check=True)
357
+ except Exception as e:
358
+ print(f"Warning: failed to generate dataset.json via dataset_tools.py: {e}")
359
+
360
+ def _load_raw_image(self, raw_idx):
361
+ fname = self._image_fnames[raw_idx]
362
+
363
+ # If no crop augmentation is requested, keep original fast path
364
+ # (optionally using pyspng for PNGs).
365
+ if self._crop_type is None:
366
+ with self._open_file(fname) as f:
367
+ if self._use_pyspng and pyspng is not None and self._file_ext(fname) == '.png':
368
+ image = pyspng.load(f.read())
369
+ else:
370
+ image = np.array(PIL.Image.open(f))
371
+ else:
372
+ # Use PIL path so we can apply center/random crop.
373
+ if self._resolution is None:
374
+ raise ValueError("resolution must be set when using crop_type for ImageFolderDataset.")
375
+ with self._open_file(fname) as f:
376
+ pil_img = PIL.Image.open(f).convert("RGB")
377
+ if self._crop_type == 'center':
378
+ pil_img = self._center_crop_arr(pil_img, self._resolution)
379
+ elif self._crop_type == 'random':
380
+ rng = None
381
+ if self._deterministic_crop:
382
+ # Make cropping independent of call order / other RNG consumers.
383
+ # Note: this fixes the crop for a given raw_idx across epochs.
384
+ seed = (self._crop_seed + int(raw_idx)) % (1 << 31)
385
+ rng = np.random.RandomState(seed)
386
+ pil_img = self._random_crop_arr(pil_img, self._resolution, rng=rng)
387
+ else:
388
+ raise ValueError(f"Unknown crop_type: {self._crop_type}")
389
+ image = np.array(pil_img)
390
+
391
+ # Normalize channel count to 3 (RGB) where possible.
392
+ if image.ndim == 2:
393
+ image = np.stack([image] * 3, axis=-1) # HW => HWC (3-channel grayscale)
394
+ elif image.ndim == 3:
395
+ if image.shape[2] == 1:
396
+ image = np.repeat(image, 3, axis=2)
397
+ elif image.shape[2] > 3:
398
+ image = image[:, :, :3]
399
+
400
+ image = image.transpose(2, 0, 1) # HWC => CHW
401
+ return image
402
+
403
+ def _load_raw_labels(self):
404
+ fname = 'dataset.json'
405
+ if fname not in self._all_fnames:
406
+ return None
407
+ with self._open_file(fname) as f:
408
+ labels = json.load(f)['labels']
409
+ if labels is None:
410
+ return None
411
+ labels = dict(labels)
412
+ labels = [labels[fname.replace('\\', '/')] for fname in self._image_fnames]
413
+ labels = np.array(labels)
414
+ labels = labels.astype({1: np.int64, 2: np.float32}[labels.ndim])
415
+ return labels
dnnlib/__init__.py ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ #
3
+ # This work is licensed under a Creative Commons
4
+ # Attribution-NonCommercial-ShareAlike 4.0 International License.
5
+ # You should have received a copy of the license along with this
6
+ # work. If not, see http://creativecommons.org/licenses/by-nc-sa/4.0/
7
+
8
+ from .util import EasyDict, make_cache_dir_path
dnnlib/util.py ADDED
@@ -0,0 +1,491 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ #
3
+ # This work is licensed under a Creative Commons
4
+ # Attribution-NonCommercial-ShareAlike 4.0 International License.
5
+ # You should have received a copy of the license along with this
6
+ # work. If not, see http://creativecommons.org/licenses/by-nc-sa/4.0/
7
+
8
+ """Miscellaneous utility classes and functions."""
9
+
10
+ import ctypes
11
+ import fnmatch
12
+ import importlib
13
+ import inspect
14
+ import numpy as np
15
+ import os
16
+ import shutil
17
+ import sys
18
+ import types
19
+ import io
20
+ import pickle
21
+ import re
22
+ import requests
23
+ import html
24
+ import hashlib
25
+ import glob
26
+ import tempfile
27
+ import urllib
28
+ import urllib.request
29
+ import uuid
30
+
31
+ from distutils.util import strtobool
32
+ from typing import Any, List, Tuple, Union, Optional
33
+
34
+
35
+ # Util classes
36
+ # ------------------------------------------------------------------------------------------
37
+
38
+
39
+ class EasyDict(dict):
40
+ """Convenience class that behaves like a dict but allows access with the attribute syntax."""
41
+
42
+ def __getattr__(self, name: str) -> Any:
43
+ try:
44
+ return self[name]
45
+ except KeyError:
46
+ raise AttributeError(name)
47
+
48
+ def __setattr__(self, name: str, value: Any) -> None:
49
+ self[name] = value
50
+
51
+ def __delattr__(self, name: str) -> None:
52
+ del self[name]
53
+
54
+
55
+ class Logger(object):
56
+ """Redirect stderr to stdout, optionally print stdout to a file, and optionally force flushing on both stdout and the file."""
57
+
58
+ def __init__(self, file_name: Optional[str] = None, file_mode: str = "w", should_flush: bool = True):
59
+ self.file = None
60
+
61
+ if file_name is not None:
62
+ self.file = open(file_name, file_mode)
63
+
64
+ self.should_flush = should_flush
65
+ self.stdout = sys.stdout
66
+ self.stderr = sys.stderr
67
+
68
+ sys.stdout = self
69
+ sys.stderr = self
70
+
71
+ def __enter__(self) -> "Logger":
72
+ return self
73
+
74
+ def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None:
75
+ self.close()
76
+
77
+ def write(self, text: Union[str, bytes]) -> None:
78
+ """Write text to stdout (and a file) and optionally flush."""
79
+ if isinstance(text, bytes):
80
+ text = text.decode()
81
+ if len(text) == 0: # workaround for a bug in VSCode debugger: sys.stdout.write(''); sys.stdout.flush() => crash
82
+ return
83
+
84
+ if self.file is not None:
85
+ self.file.write(text)
86
+
87
+ self.stdout.write(text)
88
+
89
+ if self.should_flush:
90
+ self.flush()
91
+
92
+ def flush(self) -> None:
93
+ """Flush written text to both stdout and a file, if open."""
94
+ if self.file is not None:
95
+ self.file.flush()
96
+
97
+ self.stdout.flush()
98
+
99
+ def close(self) -> None:
100
+ """Flush, close possible files, and remove stdout/stderr mirroring."""
101
+ self.flush()
102
+
103
+ # if using multiple loggers, prevent closing in wrong order
104
+ if sys.stdout is self:
105
+ sys.stdout = self.stdout
106
+ if sys.stderr is self:
107
+ sys.stderr = self.stderr
108
+
109
+ if self.file is not None:
110
+ self.file.close()
111
+ self.file = None
112
+
113
+
114
+ # Cache directories
115
+ # ------------------------------------------------------------------------------------------
116
+
117
+ _dnnlib_cache_dir = None
118
+
119
+ def set_cache_dir(path: str) -> None:
120
+ global _dnnlib_cache_dir
121
+ _dnnlib_cache_dir = path
122
+
123
+ def make_cache_dir_path(*paths: str) -> str:
124
+ if _dnnlib_cache_dir is not None:
125
+ return os.path.join(_dnnlib_cache_dir, *paths)
126
+ if 'DNNLIB_CACHE_DIR' in os.environ:
127
+ return os.path.join(os.environ['DNNLIB_CACHE_DIR'], *paths)
128
+ if 'HOME' in os.environ:
129
+ return os.path.join(os.environ['HOME'], '.cache', 'dnnlib', *paths)
130
+ if 'USERPROFILE' in os.environ:
131
+ return os.path.join(os.environ['USERPROFILE'], '.cache', 'dnnlib', *paths)
132
+ return os.path.join(tempfile.gettempdir(), '.cache', 'dnnlib', *paths)
133
+
134
+ # Small util functions
135
+ # ------------------------------------------------------------------------------------------
136
+
137
+
138
+ def format_time(seconds: Union[int, float]) -> str:
139
+ """Convert the seconds to human readable string with days, hours, minutes and seconds."""
140
+ s = int(np.rint(seconds))
141
+
142
+ if s < 60:
143
+ return "{0}s".format(s)
144
+ elif s < 60 * 60:
145
+ return "{0}m {1:02}s".format(s // 60, s % 60)
146
+ elif s < 24 * 60 * 60:
147
+ return "{0}h {1:02}m {2:02}s".format(s // (60 * 60), (s // 60) % 60, s % 60)
148
+ else:
149
+ return "{0}d {1:02}h {2:02}m".format(s // (24 * 60 * 60), (s // (60 * 60)) % 24, (s // 60) % 60)
150
+
151
+
152
+ def format_time_brief(seconds: Union[int, float]) -> str:
153
+ """Convert the seconds to human readable string with days, hours, minutes and seconds."""
154
+ s = int(np.rint(seconds))
155
+
156
+ if s < 60:
157
+ return "{0}s".format(s)
158
+ elif s < 60 * 60:
159
+ return "{0}m {1:02}s".format(s // 60, s % 60)
160
+ elif s < 24 * 60 * 60:
161
+ return "{0}h {1:02}m".format(s // (60 * 60), (s // 60) % 60)
162
+ else:
163
+ return "{0}d {1:02}h".format(s // (24 * 60 * 60), (s // (60 * 60)) % 24)
164
+
165
+
166
+ def ask_yes_no(question: str) -> bool:
167
+ """Ask the user the question until the user inputs a valid answer."""
168
+ while True:
169
+ try:
170
+ print("{0} [y/n]".format(question))
171
+ return strtobool(input().lower())
172
+ except ValueError:
173
+ pass
174
+
175
+
176
+ def tuple_product(t: Tuple) -> Any:
177
+ """Calculate the product of the tuple elements."""
178
+ result = 1
179
+
180
+ for v in t:
181
+ result *= v
182
+
183
+ return result
184
+
185
+
186
+ _str_to_ctype = {
187
+ "uint8": ctypes.c_ubyte,
188
+ "uint16": ctypes.c_uint16,
189
+ "uint32": ctypes.c_uint32,
190
+ "uint64": ctypes.c_uint64,
191
+ "int8": ctypes.c_byte,
192
+ "int16": ctypes.c_int16,
193
+ "int32": ctypes.c_int32,
194
+ "int64": ctypes.c_int64,
195
+ "float32": ctypes.c_float,
196
+ "float64": ctypes.c_double
197
+ }
198
+
199
+
200
+ def get_dtype_and_ctype(type_obj: Any) -> Tuple[np.dtype, Any]:
201
+ """Given a type name string (or an object having a __name__ attribute), return matching Numpy and ctypes types that have the same size in bytes."""
202
+ type_str = None
203
+
204
+ if isinstance(type_obj, str):
205
+ type_str = type_obj
206
+ elif hasattr(type_obj, "__name__"):
207
+ type_str = type_obj.__name__
208
+ elif hasattr(type_obj, "name"):
209
+ type_str = type_obj.name
210
+ else:
211
+ raise RuntimeError("Cannot infer type name from input")
212
+
213
+ assert type_str in _str_to_ctype.keys()
214
+
215
+ my_dtype = np.dtype(type_str)
216
+ my_ctype = _str_to_ctype[type_str]
217
+
218
+ assert my_dtype.itemsize == ctypes.sizeof(my_ctype)
219
+
220
+ return my_dtype, my_ctype
221
+
222
+
223
+ def is_pickleable(obj: Any) -> bool:
224
+ try:
225
+ with io.BytesIO() as stream:
226
+ pickle.dump(obj, stream)
227
+ return True
228
+ except:
229
+ return False
230
+
231
+
232
+ # Functionality to import modules/objects by name, and call functions by name
233
+ # ------------------------------------------------------------------------------------------
234
+
235
+ def get_module_from_obj_name(obj_name: str) -> Tuple[types.ModuleType, str]:
236
+ """Searches for the underlying module behind the name to some python object.
237
+ Returns the module and the object name (original name with module part removed)."""
238
+
239
+ # allow convenience shorthands, substitute them by full names
240
+ obj_name = re.sub("^np.", "numpy.", obj_name)
241
+ obj_name = re.sub("^tf.", "tensorflow.", obj_name)
242
+
243
+ # list alternatives for (module_name, local_obj_name)
244
+ parts = obj_name.split(".")
245
+ name_pairs = [(".".join(parts[:i]), ".".join(parts[i:])) for i in range(len(parts), 0, -1)]
246
+
247
+ # try each alternative in turn
248
+ for module_name, local_obj_name in name_pairs:
249
+ try:
250
+ module = importlib.import_module(module_name) # may raise ImportError
251
+ get_obj_from_module(module, local_obj_name) # may raise AttributeError
252
+ return module, local_obj_name
253
+ except:
254
+ pass
255
+
256
+ # maybe some of the modules themselves contain errors?
257
+ for module_name, _local_obj_name in name_pairs:
258
+ try:
259
+ importlib.import_module(module_name) # may raise ImportError
260
+ except ImportError:
261
+ if not str(sys.exc_info()[1]).startswith("No module named '" + module_name + "'"):
262
+ raise
263
+
264
+ # maybe the requested attribute is missing?
265
+ for module_name, local_obj_name in name_pairs:
266
+ try:
267
+ module = importlib.import_module(module_name) # may raise ImportError
268
+ get_obj_from_module(module, local_obj_name) # may raise AttributeError
269
+ except ImportError:
270
+ pass
271
+
272
+ # we are out of luck, but we have no idea why
273
+ raise ImportError(obj_name)
274
+
275
+
276
+ def get_obj_from_module(module: types.ModuleType, obj_name: str) -> Any:
277
+ """Traverses the object name and returns the last (rightmost) python object."""
278
+ if obj_name == '':
279
+ return module
280
+ obj = module
281
+ for part in obj_name.split("."):
282
+ obj = getattr(obj, part)
283
+ return obj
284
+
285
+
286
+ def get_obj_by_name(name: str) -> Any:
287
+ """Finds the python object with the given name."""
288
+ module, obj_name = get_module_from_obj_name(name)
289
+ return get_obj_from_module(module, obj_name)
290
+
291
+
292
+ def call_func_by_name(*args, func_name: str = None, **kwargs) -> Any:
293
+ """Finds the python object with the given name and calls it as a function."""
294
+ assert func_name is not None
295
+ func_obj = get_obj_by_name(func_name)
296
+ assert callable(func_obj)
297
+ return func_obj(*args, **kwargs)
298
+
299
+
300
+ def construct_class_by_name(*args, class_name: str = None, **kwargs) -> Any:
301
+ """Finds the python class with the given name and constructs it with the given arguments."""
302
+ return call_func_by_name(*args, func_name=class_name, **kwargs)
303
+
304
+
305
+ def get_module_dir_by_obj_name(obj_name: str) -> str:
306
+ """Get the directory path of the module containing the given object name."""
307
+ module, _ = get_module_from_obj_name(obj_name)
308
+ return os.path.dirname(inspect.getfile(module))
309
+
310
+
311
+ def is_top_level_function(obj: Any) -> bool:
312
+ """Determine whether the given object is a top-level function, i.e., defined at module scope using 'def'."""
313
+ return callable(obj) and obj.__name__ in sys.modules[obj.__module__].__dict__
314
+
315
+
316
+ def get_top_level_function_name(obj: Any) -> str:
317
+ """Return the fully-qualified name of a top-level function."""
318
+ assert is_top_level_function(obj)
319
+ module = obj.__module__
320
+ if module == '__main__':
321
+ module = os.path.splitext(os.path.basename(sys.modules[module].__file__))[0]
322
+ return module + "." + obj.__name__
323
+
324
+
325
+ # File system helpers
326
+ # ------------------------------------------------------------------------------------------
327
+
328
+ def list_dir_recursively_with_ignore(dir_path: str, ignores: List[str] = None, add_base_to_relative: bool = False) -> List[Tuple[str, str]]:
329
+ """List all files recursively in a given directory while ignoring given file and directory names.
330
+ Returns list of tuples containing both absolute and relative paths."""
331
+ assert os.path.isdir(dir_path)
332
+ base_name = os.path.basename(os.path.normpath(dir_path))
333
+
334
+ if ignores is None:
335
+ ignores = []
336
+
337
+ result = []
338
+
339
+ for root, dirs, files in os.walk(dir_path, topdown=True):
340
+ for ignore_ in ignores:
341
+ dirs_to_remove = [d for d in dirs if fnmatch.fnmatch(d, ignore_)]
342
+
343
+ # dirs need to be edited in-place
344
+ for d in dirs_to_remove:
345
+ dirs.remove(d)
346
+
347
+ files = [f for f in files if not fnmatch.fnmatch(f, ignore_)]
348
+
349
+ absolute_paths = [os.path.join(root, f) for f in files]
350
+ relative_paths = [os.path.relpath(p, dir_path) for p in absolute_paths]
351
+
352
+ if add_base_to_relative:
353
+ relative_paths = [os.path.join(base_name, p) for p in relative_paths]
354
+
355
+ assert len(absolute_paths) == len(relative_paths)
356
+ result += zip(absolute_paths, relative_paths)
357
+
358
+ return result
359
+
360
+
361
+ def copy_files_and_create_dirs(files: List[Tuple[str, str]]) -> None:
362
+ """Takes in a list of tuples of (src, dst) paths and copies files.
363
+ Will create all necessary directories."""
364
+ for file in files:
365
+ target_dir_name = os.path.dirname(file[1])
366
+
367
+ # will create all intermediate-level directories
368
+ if not os.path.exists(target_dir_name):
369
+ os.makedirs(target_dir_name)
370
+
371
+ shutil.copyfile(file[0], file[1])
372
+
373
+
374
+ # URL helpers
375
+ # ------------------------------------------------------------------------------------------
376
+
377
+ def is_url(obj: Any, allow_file_urls: bool = False) -> bool:
378
+ """Determine whether the given object is a valid URL string."""
379
+ if not isinstance(obj, str) or not "://" in obj:
380
+ return False
381
+ if allow_file_urls and obj.startswith('file://'):
382
+ return True
383
+ try:
384
+ res = requests.compat.urlparse(obj)
385
+ if not res.scheme or not res.netloc or not "." in res.netloc:
386
+ return False
387
+ res = requests.compat.urlparse(requests.compat.urljoin(obj, "/"))
388
+ if not res.scheme or not res.netloc or not "." in res.netloc:
389
+ return False
390
+ except:
391
+ return False
392
+ return True
393
+
394
+
395
+ def open_url(url: str, cache_dir: str = None, num_attempts: int = 10, verbose: bool = True, return_filename: bool = False, cache: bool = True) -> Any:
396
+ """Download the given URL and return a binary-mode file object to access the data."""
397
+ assert num_attempts >= 1
398
+ assert not (return_filename and (not cache))
399
+
400
+ # Doesn't look like an URL scheme so interpret it as a local filename.
401
+ if not re.match('^[a-z]+://', url):
402
+ return url if return_filename else open(url, "rb")
403
+
404
+ # Handle file URLs. This code handles unusual file:// patterns that
405
+ # arise on Windows:
406
+ #
407
+ # file:///c:/foo.txt
408
+ #
409
+ # which would translate to a local '/c:/foo.txt' filename that's
410
+ # invalid. Drop the forward slash for such pathnames.
411
+ #
412
+ # If you touch this code path, you should test it on both Linux and
413
+ # Windows.
414
+ #
415
+ # Some internet resources suggest using urllib.request.url2pathname() but
416
+ # but that converts forward slashes to backslashes and this causes
417
+ # its own set of problems.
418
+ if url.startswith('file://'):
419
+ filename = urllib.parse.urlparse(url).path
420
+ if re.match(r'^/[a-zA-Z]:', filename):
421
+ filename = filename[1:]
422
+ return filename if return_filename else open(filename, "rb")
423
+
424
+ assert is_url(url)
425
+
426
+ # Lookup from cache.
427
+ if cache_dir is None:
428
+ cache_dir = make_cache_dir_path('downloads')
429
+
430
+ url_md5 = hashlib.md5(url.encode("utf-8")).hexdigest()
431
+ if cache:
432
+ cache_files = glob.glob(os.path.join(cache_dir, url_md5 + "_*"))
433
+ if len(cache_files) == 1:
434
+ filename = cache_files[0]
435
+ return filename if return_filename else open(filename, "rb")
436
+
437
+ # Download.
438
+ url_name = None
439
+ url_data = None
440
+ with requests.Session() as session:
441
+ if verbose:
442
+ print("Downloading %s ..." % url, end="", flush=True)
443
+ for attempts_left in reversed(range(num_attempts)):
444
+ try:
445
+ with session.get(url) as res:
446
+ res.raise_for_status()
447
+ if len(res.content) == 0:
448
+ raise IOError("No data received")
449
+
450
+ if len(res.content) < 8192:
451
+ content_str = res.content.decode("utf-8")
452
+ if "download_warning" in res.headers.get("Set-Cookie", ""):
453
+ links = [html.unescape(link) for link in content_str.split('"') if "export=download" in link]
454
+ if len(links) == 1:
455
+ url = requests.compat.urljoin(url, links[0])
456
+ raise IOError("Google Drive virus checker nag")
457
+ if "Google Drive - Quota exceeded" in content_str:
458
+ raise IOError("Google Drive download quota exceeded -- please try again later")
459
+
460
+ match = re.search(r'filename="([^"]*)"', res.headers.get("Content-Disposition", ""))
461
+ url_name = match[1] if match else url
462
+ url_data = res.content
463
+ if verbose:
464
+ print(" done")
465
+ break
466
+ except KeyboardInterrupt:
467
+ raise
468
+ except:
469
+ if not attempts_left:
470
+ if verbose:
471
+ print(" failed")
472
+ raise
473
+ if verbose:
474
+ print(".", end="", flush=True)
475
+
476
+ # Save to cache.
477
+ if cache:
478
+ safe_name = re.sub(r"[^0-9a-zA-Z-._]", "_", url_name)
479
+ safe_name = safe_name[:min(len(safe_name), 128)]
480
+ cache_file = os.path.join(cache_dir, url_md5 + "_" + safe_name)
481
+ temp_file = os.path.join(cache_dir, "tmp_" + uuid.uuid4().hex + "_" + url_md5 + "_" + safe_name)
482
+ os.makedirs(cache_dir, exist_ok=True)
483
+ with open(temp_file, "wb") as f:
484
+ f.write(url_data)
485
+ os.replace(temp_file, cache_file) # atomic
486
+ if return_filename:
487
+ return cache_file
488
+
489
+ # Return data as file object.
490
+ assert not return_filename
491
+ return io.BytesIO(url_data)
evaluate.py ADDED
@@ -0,0 +1,181 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Pure evaluation: no optimizers, training steps, or EMA updates."""
2
+ import argparse
3
+ import json
4
+ import hashlib
5
+ import os
6
+ from pathlib import Path
7
+ import subprocess
8
+ import sys
9
+ import numpy as np
10
+ import torch
11
+ from torch.utils.data import DataLoader, Subset
12
+ from torchvision.utils import save_image
13
+ from tqdm import tqdm
14
+ from dataset import ImageFolderDataset
15
+ from omegaconf import OmegaConf
16
+ import torch.nn.functional as F
17
+ from models import Encoder, Decoder, ARModel
18
+ from train import (seed_everything, patchify, img_unint8_to_norm, DiTiScheduler,
19
+ reconstruction, sampling, euler_recon_loop, euler_sample_loop,
20
+ img_norm_to_uint8, unpatchify)
21
+
22
+
23
+ def state_dict(state):
24
+ return {k.removeprefix('_orig_mod.'): v for k, v in state.items()}
25
+
26
+
27
+ def load_models(config, checkpoint, device='cuda', ema=True, need_ar=True):
28
+ state = torch.load(checkpoint, map_location='cpu', weights_only=True, mmap=True)
29
+ enc, dec = Encoder(config), Decoder(config)
30
+ enc.load_state_dict(state_dict(state['enc']), strict=True)
31
+ dec.load_state_dict(state_dict(state['dec']), strict=True)
32
+ ar = None
33
+ if need_ar:
34
+ ar = ARModel(config)
35
+ key = 'ar_model_ema' if ema else 'ar_model'
36
+ if state.get(key) is None:
37
+ raise ValueError(f'Checkpoint has no {key}; select a matching AR checkpoint')
38
+ ar.load_state_dict(state_dict(state[key]), strict=True)
39
+ for model in (enc, dec, ar):
40
+ if model is not None:
41
+ model.to(device).eval().requires_grad_(False)
42
+ return enc, dec, ar
43
+
44
+
45
+ def scheduler(config):
46
+ if config.get('time_stages') and config.get('k_per_stage'):
47
+ return DiTiScheduler(1000, config.z_len, config.time_stages,
48
+ config.k_per_stage, config.reverse_t)
49
+ return None
50
+
51
+
52
+ def labels_for(config, batch_size, device):
53
+ if config.use_label:
54
+ raise ValueError('The released CIFAR-10 protocols are unconditional')
55
+ return F.one_hot(torch.full((batch_size,), config.num_classes-1,
56
+ device=device, dtype=torch.long), config.num_classes).float()
57
+
58
+
59
+ @torch.no_grad()
60
+ def decode_batch(config, enc, dec, ar, x, labels, mode, diti=None):
61
+ """x is a patchified batch; its shape also defines unconditional generation."""
62
+ t = torch.ones(x.shape[0], device=x.device) if config.use_noise_query else None
63
+ # wARTok draws reconstruction noise before the generation branch, even for AR-only eval.
64
+ noise_recon = torch.randn_like(x) if config.use_noise_query else None
65
+ if mode == 'reconstruct':
66
+ if config.use_noise_query and config.diffusion_decoder:
67
+ result, _ = euler_recon_loop(enc, dec, x, labels, config=config, diti_scheduler=diti)
68
+ else:
69
+ result, _ = reconstruction(enc, dec, x, labels, query_tokens=noise_recon, t=t)
70
+ if config.l2_v_target:
71
+ result = noise_recon - result
72
+ elif mode == 'generate':
73
+ kwargs = dict(bz=x.shape[0], class_label=labels, temperature=config.temperature,
74
+ topK=config.topK, topP=config.topP, cfg=config.cfg,
75
+ cfg_schedule=config.cfg_schedule, cfg_power=config.cfg_power,
76
+ cache_kv=config.cache_kv, config=config)
77
+ if config.use_noise_query and config.diffusion_decoder:
78
+ result = euler_sample_loop(enc, dec, ar, **kwargs, diti_scheduler=diti)
79
+ else:
80
+ noise = torch.randn_like(x) if config.use_noise_query else None
81
+ result = sampling(enc, dec, ar, **kwargs, t=t, query_tokens=noise)
82
+ if config.l2_v_target:
83
+ result = noise - result
84
+ else:
85
+ raise ValueError(mode)
86
+ return img_norm_to_uint8(unpatchify(result, config.image_size, config.patch_size))
87
+
88
+
89
+ def build_parser():
90
+ parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.ArgumentDefaultsHelpFormatter)
91
+ parser.add_argument('--config', required=True)
92
+ parser.add_argument('--checkpoint', required=True)
93
+ parser.add_argument('--mode', choices=['generate', 'reconstruct'], default='generate')
94
+ parser.add_argument('--data', help='Required for reconstruction; use the CIFAR-10 training split')
95
+ parser.add_argument('--output', required=True)
96
+ parser.add_argument('--num-samples', type=int, default=50000)
97
+ parser.add_argument('--batch-size', type=int, default=64, help='Per process batch size (original evaluation: 64)')
98
+ parser.add_argument('--seed', type=int, default=42)
99
+ parser.add_argument('--legacy', action=argparse.BooleanOptionalAction, default=True,
100
+ help='Use the same seed on every rank (wARTok behavior); --no-legacy uses seed + rank')
101
+ parser.add_argument('--device', default='cuda')
102
+ parser.add_argument('--no-ema', action='store_true')
103
+ parser.add_argument('--fid-reference')
104
+ return parser
105
+
106
+
107
+ def main():
108
+ parser = build_parser()
109
+ args = parser.parse_args()
110
+ if args.num_samples < 1 or args.batch_size < 1:
111
+ parser.error('sample count and batch size must be positive')
112
+ world = int(os.environ.get('WORLD_SIZE', 1)); rank = int(os.environ.get('RANK', 0))
113
+ if world > 1:
114
+ if args.device != 'cuda': parser.error('distributed evaluation requires CUDA')
115
+ torch.cuda.set_device(int(os.environ['LOCAL_RANK']))
116
+ # Ranks exchange files, not CUDA tensors; CPU barriers avoid requiring GPU P2P.
117
+ torch.distributed.init_process_group('gloo')
118
+ c = OmegaConf.load(args.config)
119
+ torch.backends.cuda.matmul.allow_tf32 = bool(c.use_tf32)
120
+ torch.backends.cudnn.allow_tf32 = bool(c.use_tf32)
121
+ seed_everything(args.seed)
122
+ enc, dec, ar = load_models(c, args.checkpoint, args.device, not args.no_ema,
123
+ args.mode == 'generate')
124
+ diti = scheduler(c)
125
+ # Strided partition, without DistributedSampler padding: exactly N images globally.
126
+ indices = list(range(rank, args.num_samples, world))
127
+ loader = None
128
+ if args.mode == 'reconstruct':
129
+ if not args.data: parser.error('--data is required for reconstruction')
130
+ dataset = ImageFolderDataset(path=args.data, resolution=c.image_size,
131
+ use_label=False, xflip=False, crop_type=c.eval_crop_type)
132
+ if args.num_samples > len(dataset): parser.error('num-samples exceeds dataset size')
133
+ loader = iter(DataLoader(Subset(dataset, indices), batch_size=args.batch_size, shuffle=False))
134
+ # A fixed evaluation seed is explicit; historical training-loop RNG was not checkpointed.
135
+ seed_everything(args.seed + (0 if args.legacy else rank))
136
+ samples = []
137
+ with torch.inference_mode():
138
+ for start in tqdm(range(0, len(indices), args.batch_size), desc=args.mode, disable=rank != 0):
139
+ n = min(args.batch_size, len(indices)-start)
140
+ if loader is None:
141
+ x = torch.empty(n, c.x_len, c.patch_dim, device=args.device)
142
+ else:
143
+ images, _ = next(loader)
144
+ x = patchify(img_unint8_to_norm(images.to(args.device)), c.patch_size)
145
+ labels = labels_for(c, n, args.device)
146
+ images = decode_batch(c, enc, dec, ar, x, labels, args.mode, diti)
147
+ samples.append(images.permute(0,2,3,1).cpu().numpy())
148
+ arr = np.concatenate(samples) if samples else np.empty((0,c.image_size,c.image_size,3),dtype=np.uint8)
149
+ out = Path(args.output); out.mkdir(parents=True, exist_ok=True)
150
+ if world > 1:
151
+ np.savez(out/f'rank-{rank}.npz', images=arr, indices=np.asarray(indices))
152
+ torch.distributed.barrier()
153
+ if rank == 0:
154
+ if world > 1:
155
+ arr = np.empty((args.num_samples,c.image_size,c.image_size,3),dtype=np.uint8)
156
+ for r in range(world):
157
+ with np.load(out/f'rank-{r}.npz') as shard: arr[shard['indices']]=shard['images']
158
+ assert len(arr) == args.num_samples
159
+ path = out/'samples.npz'; np.savez_compressed(path, arr)
160
+ save_image(torch.from_numpy(arr[:64]).permute(0,3,1,2).float()/255, out/'preview.png',nrow=8)
161
+ report = dict(mode=args.mode, num_samples=len(arr),
162
+ unique_images=len({hashlib.sha256(im.tobytes()).digest() for im in arr}), seed=args.seed,
163
+ world_size=world, batch_size=args.batch_size, legacy=args.legacy, rank_seed_mode='shared' if args.legacy else 'independent', config=args.config,
164
+ checkpoint=args.checkpoint, ar_ema=(not args.no_ema) if args.mode == 'generate' else None,
165
+ decoder='euler25' if c.use_noise_query and c.diffusion_decoder else 'single_step',
166
+ precision='32-true', fid=None, torch_version=torch.__version__,
167
+ config_sha256=hashlib.sha256(Path(args.config).read_bytes()).hexdigest())
168
+ if args.fid_reference:
169
+ report['reference_sha256']=hashlib.sha256(Path(args.fid_reference).read_bytes()).hexdigest()
170
+ result = subprocess.run([sys.executable, str(Path(__file__).with_name('calc_fid.py')),
171
+ '--ref_batch', args.fid_reference, '--sample_batch', str(path), '--batch_size','64'],
172
+ text=True, check=True, stdout=subprocess.PIPE)
173
+ print(result.stdout)
174
+ report['fid']=float(next(line.split(':',1)[1] for line in result.stdout.splitlines() if line.startswith('FID_RESULT:')))
175
+ (out/'metrics.json').write_text(json.dumps(report,indent=2)+'\n')
176
+ print(json.dumps(report, indent=2))
177
+ if world > 1:
178
+ torch.distributed.barrier(); torch.distributed.destroy_process_group()
179
+
180
+
181
+ if __name__ == '__main__': main()
evaluator.py ADDED
@@ -0,0 +1,675 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import argparse
2
+ import io
3
+ import os
4
+ import random
5
+ import warnings
6
+ import zipfile
7
+ from abc import ABC, abstractmethod
8
+ from contextlib import contextmanager
9
+ from functools import partial
10
+ from multiprocessing import cpu_count
11
+ from multiprocessing.pool import ThreadPool
12
+ from typing import Iterable, Optional, Tuple, Union
13
+
14
+ import numpy as np
15
+ import requests
16
+ import tensorflow.compat.v1 as tf
17
+ from scipy import linalg
18
+ from tqdm.auto import tqdm
19
+
20
+ INCEPTION_V3_URL = "https://openaipublic.blob.core.windows.net/diffusion/jul-2021/ref_batches/classify_image_graph_def.pb"
21
+ INCEPTION_V3_PATH = "classify_image_graph_def.pb"
22
+
23
+ FID_POOL_NAME = "pool_3:0"
24
+ FID_SPATIAL_NAME = "mixed_6/conv:0"
25
+
26
+
27
+ def main():
28
+ parser = argparse.ArgumentParser()
29
+ parser.add_argument("ref_batch", help="path to reference batch npz file")
30
+ parser.add_argument("sample_batch", help="path to sample batch npz file")
31
+ args = parser.parse_args()
32
+
33
+ config = tf.ConfigProto(
34
+ allow_soft_placement=True # allows DecodeJpeg to run on CPU in Inception graph
35
+ )
36
+ config.gpu_options.allow_growth = True
37
+ evaluator = Evaluator(tf.Session(config=config))
38
+
39
+ print("warming up TensorFlow...")
40
+ # This will cause TF to print a bunch of verbose stuff now rather
41
+ # than after the next print(), to help prevent confusion.
42
+ evaluator.warmup()
43
+
44
+ print("computing reference batch activations...")
45
+ ref_acts = evaluator.read_activations(args.ref_batch)
46
+ print("computing/reading reference batch statistics...")
47
+ ref_stats, ref_stats_spatial = evaluator.read_statistics(args.ref_batch, ref_acts)
48
+
49
+ print("computing sample batch activations...")
50
+ sample_acts = evaluator.read_activations(args.sample_batch)
51
+ print("computing/reading sample batch statistics...")
52
+ sample_stats, sample_stats_spatial = evaluator.read_statistics(args.sample_batch, sample_acts)
53
+
54
+ print("Computing evaluations...")
55
+ print("Inception Score:", evaluator.compute_inception_score(sample_acts[0]))
56
+ print("FID:", sample_stats.frechet_distance(ref_stats))
57
+ print("sFID:", sample_stats_spatial.frechet_distance(ref_stats_spatial))
58
+ prec, recall = evaluator.compute_prec_recall(ref_acts[0], sample_acts[0])
59
+ print("Precision:", prec)
60
+ print("Recall:", recall)
61
+
62
+
63
+ class InvalidFIDException(Exception):
64
+ pass
65
+
66
+
67
+ class FIDStatistics:
68
+ def __init__(self, mu: np.ndarray, sigma: np.ndarray):
69
+ self.mu = mu
70
+ self.sigma = sigma
71
+
72
+ def frechet_distance(self, other, eps=1e-6):
73
+ """
74
+ Compute the Frechet distance between two sets of statistics.
75
+ """
76
+ # https://github.com/bioinf-jku/TTUR/blob/73ab375cdf952a12686d9aa7978567771084da42/fid.py#L132
77
+ mu1, sigma1 = self.mu, self.sigma
78
+ mu2, sigma2 = other.mu, other.sigma
79
+
80
+ mu1 = np.atleast_1d(mu1)
81
+ mu2 = np.atleast_1d(mu2)
82
+
83
+ sigma1 = np.atleast_2d(sigma1)
84
+ sigma2 = np.atleast_2d(sigma2)
85
+
86
+ assert (
87
+ mu1.shape == mu2.shape
88
+ ), f"Training and test mean vectors have different lengths: {mu1.shape}, {mu2.shape}"
89
+ assert (
90
+ sigma1.shape == sigma2.shape
91
+ ), f"Training and test covariances have different dimensions: {sigma1.shape}, {sigma2.shape}"
92
+
93
+ diff = mu1 - mu2
94
+
95
+ # product might be almost singular
96
+ covmean, _ = linalg.sqrtm(sigma1.dot(sigma2), disp=False)
97
+ if not np.isfinite(covmean).all():
98
+ msg = (
99
+ "fid calculation produces singular product; adding %s to diagonal of cov estimates"
100
+ % eps
101
+ )
102
+ warnings.warn(msg)
103
+ offset = np.eye(sigma1.shape[0]) * eps
104
+ covmean = linalg.sqrtm((sigma1 + offset).dot(sigma2 + offset))
105
+
106
+ # numerical error might give slight imaginary component
107
+ if np.iscomplexobj(covmean):
108
+ if not np.allclose(np.diagonal(covmean).imag, 0, atol=1e-3):
109
+ m = np.max(np.abs(covmean.imag))
110
+ raise ValueError("Imaginary component {}".format(m))
111
+ covmean = covmean.real
112
+
113
+ tr_covmean = np.trace(covmean)
114
+
115
+ return diff.dot(diff) + np.trace(sigma1) + np.trace(sigma2) - 2 * tr_covmean
116
+
117
+
118
+ class Evaluator:
119
+ def __init__(
120
+ self,
121
+ session,
122
+ batch_size=64,
123
+ softmax_batch_size=512,
124
+ ):
125
+ self.sess = session
126
+ self.batch_size = batch_size
127
+ self.softmax_batch_size = softmax_batch_size
128
+ self.manifold_estimator = ManifoldEstimator(session)
129
+ with self.sess.graph.as_default():
130
+ self.image_input = tf.placeholder(tf.float32, shape=[None, None, None, 3])
131
+ self.softmax_input = tf.placeholder(tf.float32, shape=[None, 2048])
132
+ self.pool_features, self.spatial_features = _create_feature_graph(self.image_input)
133
+ self.softmax = _create_softmax_graph(self.softmax_input)
134
+
135
+ def warmup(self):
136
+ self.compute_activations(np.zeros([1, 8, 64, 64, 3]))
137
+
138
+ def read_activations(self, npz_path: Union[str, np.ndarray]) -> Tuple[np.ndarray, np.ndarray]:
139
+ if isinstance(npz_path, str):
140
+ # Archives contain either images (arr_0) or precomputed FID statistics.
141
+ with np.load(npz_path) as obj:
142
+ keys = set(obj.files)
143
+
144
+ # Statistics-only archives do not require feature extraction.
145
+ if "mu" in keys and "sigma" in keys and "arr_0" not in keys:
146
+ dummy = np.zeros((0, 1), dtype=np.float32)
147
+ return dummy, dummy
148
+
149
+ # Image archives store the image batch in arr_0.
150
+ with open_npz_array(npz_path, "arr_0") as reader:
151
+ return self.compute_activations(reader.read_batches(self.batch_size))
152
+ elif isinstance(npz_path, np.ndarray):
153
+ # If npz_path is a numpy array, split it into batches manually
154
+ batches = np.array_split(npz_path, range(self.batch_size, npz_path.shape[0], self.batch_size))
155
+ return self.compute_activations(batches)
156
+ else:
157
+ raise ValueError("npz_path must be either a file path (str) or a numpy array (np.ndarray)")
158
+
159
+
160
+ def compute_activations(self, batches: Iterable[np.ndarray]) -> Tuple[np.ndarray, np.ndarray]:
161
+ """
162
+ Compute image features for downstream evals.
163
+
164
+ :param batches: a iterator over NHWC numpy arrays in [0, 255].
165
+ :return: a tuple of numpy arrays of shape [N x X], where X is a feature
166
+ dimension. The tuple is (pool_3, spatial).
167
+ """
168
+ preds = []
169
+ spatial_preds = []
170
+ for batch in tqdm(batches):
171
+ batch = batch.astype(np.float32)
172
+ pred, spatial_pred = self.sess.run(
173
+ [self.pool_features, self.spatial_features], {self.image_input: batch}
174
+ )
175
+ preds.append(pred.reshape([pred.shape[0], -1]))
176
+ spatial_preds.append(spatial_pred.reshape([spatial_pred.shape[0], -1]))
177
+ return (
178
+ np.concatenate(preds, axis=0),
179
+ np.concatenate(spatial_preds, axis=0),
180
+ )
181
+
182
+ def read_statistics(
183
+ self, npz_path: Union[str, np.ndarray], activations: Tuple[np.ndarray, np.ndarray]
184
+ ) -> Tuple[FIDStatistics, FIDStatistics]:
185
+ if isinstance(npz_path, str):
186
+ obj = np.load(npz_path)
187
+ if "mu" in list(obj.keys()):
188
+ return FIDStatistics(obj["mu"], obj["sigma"]), FIDStatistics(
189
+ obj["mu_s"], obj["sigma_s"]
190
+ )
191
+ elif isinstance(npz_path, np.ndarray):
192
+ obj = npz_path
193
+ else:
194
+ raise ValueError("npz_path must be either a file path (str) or a numpy array (np.ndarray)")
195
+ return tuple(self.compute_statistics(x) for x in activations)
196
+
197
+ def compute_statistics(self, activations: np.ndarray) -> FIDStatistics:
198
+ mu = np.mean(activations, axis=0)
199
+ sigma = np.cov(activations, rowvar=False)
200
+ return FIDStatistics(mu, sigma)
201
+
202
+ def compute_inception_score(self, activations: np.ndarray, split_size: int = 5000) -> float:
203
+ softmax_out = []
204
+ for i in range(0, len(activations), self.softmax_batch_size):
205
+ acts = activations[i : i + self.softmax_batch_size]
206
+ softmax_out.append(self.sess.run(self.softmax, feed_dict={self.softmax_input: acts}))
207
+ preds = np.concatenate(softmax_out, axis=0)
208
+ # https://github.com/openai/improved-gan/blob/4f5d1ec5c16a7eceb206f42bfc652693601e1d5c/inception_score/model.py#L46
209
+ scores = []
210
+ for i in range(0, len(preds), split_size):
211
+ part = preds[i : i + split_size]
212
+ kl = part * (np.log(part) - np.log(np.expand_dims(np.mean(part, 0), 0)))
213
+ kl = np.mean(np.sum(kl, 1))
214
+ scores.append(np.exp(kl))
215
+ return float(np.mean(scores))
216
+
217
+ def compute_prec_recall(
218
+ self, activations_ref: np.ndarray, activations_sample: np.ndarray
219
+ ) -> Tuple[float, float]:
220
+ radii_1 = self.manifold_estimator.manifold_radii(activations_ref)
221
+ radii_2 = self.manifold_estimator.manifold_radii(activations_sample)
222
+ pr = self.manifold_estimator.evaluate_pr(
223
+ activations_ref, radii_1, activations_sample, radii_2
224
+ )
225
+ return (float(pr[0][0]), float(pr[1][0]))
226
+
227
+
228
+ class ManifoldEstimator:
229
+ """
230
+ A helper for comparing manifolds of feature vectors.
231
+
232
+ Adapted from https://github.com/kynkaat/improved-precision-and-recall-metric/blob/f60f25e5ad933a79135c783fcda53de30f42c9b9/precision_recall.py#L57
233
+ """
234
+
235
+ def __init__(
236
+ self,
237
+ session,
238
+ row_batch_size=10000,
239
+ col_batch_size=10000,
240
+ nhood_sizes=(3,),
241
+ clamp_to_percentile=None,
242
+ eps=1e-5,
243
+ ):
244
+ """
245
+ Estimate the manifold of given feature vectors.
246
+
247
+ :param session: the TensorFlow session.
248
+ :param row_batch_size: row batch size to compute pairwise distances
249
+ (parameter to trade-off between memory usage and performance).
250
+ :param col_batch_size: column batch size to compute pairwise distances.
251
+ :param nhood_sizes: number of neighbors used to estimate the manifold.
252
+ :param clamp_to_percentile: prune hyperspheres that have radius larger than
253
+ the given percentile.
254
+ :param eps: small number for numerical stability.
255
+ """
256
+ self.distance_block = DistanceBlock(session)
257
+ self.row_batch_size = row_batch_size
258
+ self.col_batch_size = col_batch_size
259
+ self.nhood_sizes = nhood_sizes
260
+ self.num_nhoods = len(nhood_sizes)
261
+ self.clamp_to_percentile = clamp_to_percentile
262
+ self.eps = eps
263
+
264
+ def warmup(self):
265
+ feats, radii = (
266
+ np.zeros([1, 2048], dtype=np.float32),
267
+ np.zeros([1, 1], dtype=np.float32),
268
+ )
269
+ self.evaluate_pr(feats, radii, feats, radii)
270
+
271
+ def manifold_radii(self, features: np.ndarray) -> np.ndarray:
272
+ num_images = len(features)
273
+
274
+ # Estimate manifold of features by calculating distances to k-NN of each sample.
275
+ radii = np.zeros([num_images, self.num_nhoods], dtype=np.float32)
276
+ distance_batch = np.zeros([self.row_batch_size, num_images], dtype=np.float32)
277
+ seq = np.arange(max(self.nhood_sizes) + 1, dtype=np.int32)
278
+
279
+ for begin1 in range(0, num_images, self.row_batch_size):
280
+ end1 = min(begin1 + self.row_batch_size, num_images)
281
+ row_batch = features[begin1:end1]
282
+
283
+ for begin2 in range(0, num_images, self.col_batch_size):
284
+ end2 = min(begin2 + self.col_batch_size, num_images)
285
+ col_batch = features[begin2:end2]
286
+
287
+ # Compute distances between batches.
288
+ distance_batch[
289
+ 0 : end1 - begin1, begin2:end2
290
+ ] = self.distance_block.pairwise_distances(row_batch, col_batch)
291
+
292
+ # Find the k-nearest neighbor from the current batch.
293
+ radii[begin1:end1, :] = np.concatenate(
294
+ [
295
+ x[:, self.nhood_sizes]
296
+ for x in _numpy_partition(distance_batch[0 : end1 - begin1, :], seq, axis=1)
297
+ ],
298
+ axis=0,
299
+ )
300
+
301
+ if self.clamp_to_percentile is not None:
302
+ max_distances = np.percentile(radii, self.clamp_to_percentile, axis=0)
303
+ radii[radii > max_distances] = 0
304
+ return radii
305
+
306
+ def evaluate(self, features: np.ndarray, radii: np.ndarray, eval_features: np.ndarray):
307
+ """
308
+ Evaluate if new feature vectors are at the manifold.
309
+ """
310
+ num_eval_images = eval_features.shape[0]
311
+ num_ref_images = radii.shape[0]
312
+ distance_batch = np.zeros([self.row_batch_size, num_ref_images], dtype=np.float32)
313
+ batch_predictions = np.zeros([num_eval_images, self.num_nhoods], dtype=np.int32)
314
+ max_realism_score = np.zeros([num_eval_images], dtype=np.float32)
315
+ nearest_indices = np.zeros([num_eval_images], dtype=np.int32)
316
+
317
+ for begin1 in range(0, num_eval_images, self.row_batch_size):
318
+ end1 = min(begin1 + self.row_batch_size, num_eval_images)
319
+ feature_batch = eval_features[begin1:end1]
320
+
321
+ for begin2 in range(0, num_ref_images, self.col_batch_size):
322
+ end2 = min(begin2 + self.col_batch_size, num_ref_images)
323
+ ref_batch = features[begin2:end2]
324
+
325
+ distance_batch[
326
+ 0 : end1 - begin1, begin2:end2
327
+ ] = self.distance_block.pairwise_distances(feature_batch, ref_batch)
328
+
329
+ # From the minibatch of new feature vectors, determine if they are in the estimated manifold.
330
+ # If a feature vector is inside a hypersphere of some reference sample, then
331
+ # the new sample lies at the estimated manifold.
332
+ # The radii of the hyperspheres are determined from distances of neighborhood size k.
333
+ samples_in_manifold = distance_batch[0 : end1 - begin1, :, None] <= radii
334
+ batch_predictions[begin1:end1] = np.any(samples_in_manifold, axis=1).astype(np.int32)
335
+
336
+ max_realism_score[begin1:end1] = np.max(
337
+ radii[:, 0] / (distance_batch[0 : end1 - begin1, :] + self.eps), axis=1
338
+ )
339
+ nearest_indices[begin1:end1] = np.argmin(distance_batch[0 : end1 - begin1, :], axis=1)
340
+
341
+ return {
342
+ "fraction": float(np.mean(batch_predictions)),
343
+ "batch_predictions": batch_predictions,
344
+ "max_realisim_score": max_realism_score,
345
+ "nearest_indices": nearest_indices,
346
+ }
347
+
348
+ def evaluate_pr(
349
+ self,
350
+ features_1: np.ndarray,
351
+ radii_1: np.ndarray,
352
+ features_2: np.ndarray,
353
+ radii_2: np.ndarray,
354
+ ) -> Tuple[np.ndarray, np.ndarray]:
355
+ """
356
+ Evaluate precision and recall efficiently.
357
+
358
+ :param features_1: [N1 x D] feature vectors for reference batch.
359
+ :param radii_1: [N1 x K1] radii for reference vectors.
360
+ :param features_2: [N2 x D] feature vectors for the other batch.
361
+ :param radii_2: [N x K2] radii for other vectors.
362
+ :return: a tuple of arrays for (precision, recall):
363
+ - precision: an np.ndarray of length K1
364
+ - recall: an np.ndarray of length K2
365
+ """
366
+ features_1_status = np.zeros([len(features_1), radii_2.shape[1]], dtype=np.bool)
367
+ features_2_status = np.zeros([len(features_2), radii_1.shape[1]], dtype=np.bool)
368
+ for begin_1 in range(0, len(features_1), self.row_batch_size):
369
+ end_1 = begin_1 + self.row_batch_size
370
+ batch_1 = features_1[begin_1:end_1]
371
+ for begin_2 in range(0, len(features_2), self.col_batch_size):
372
+ end_2 = begin_2 + self.col_batch_size
373
+ batch_2 = features_2[begin_2:end_2]
374
+ batch_1_in, batch_2_in = self.distance_block.less_thans(
375
+ batch_1, radii_1[begin_1:end_1], batch_2, radii_2[begin_2:end_2]
376
+ )
377
+ features_1_status[begin_1:end_1] |= batch_1_in
378
+ features_2_status[begin_2:end_2] |= batch_2_in
379
+ return (
380
+ np.mean(features_2_status.astype(np.float64), axis=0),
381
+ np.mean(features_1_status.astype(np.float64), axis=0),
382
+ )
383
+
384
+
385
+ class DistanceBlock:
386
+ """
387
+ Calculate pairwise distances between vectors.
388
+
389
+ Adapted from https://github.com/kynkaat/improved-precision-and-recall-metric/blob/f60f25e5ad933a79135c783fcda53de30f42c9b9/precision_recall.py#L34
390
+ """
391
+
392
+ def __init__(self, session):
393
+ self.session = session
394
+
395
+ # Initialize TF graph to calculate pairwise distances.
396
+ with session.graph.as_default():
397
+ self._features_batch1 = tf.placeholder(tf.float32, shape=[None, None])
398
+ self._features_batch2 = tf.placeholder(tf.float32, shape=[None, None])
399
+ distance_block_16 = _batch_pairwise_distances(
400
+ tf.cast(self._features_batch1, tf.float16),
401
+ tf.cast(self._features_batch2, tf.float16),
402
+ )
403
+ self.distance_block = tf.cond(
404
+ tf.reduce_all(tf.math.is_finite(distance_block_16)),
405
+ lambda: tf.cast(distance_block_16, tf.float32),
406
+ lambda: _batch_pairwise_distances(self._features_batch1, self._features_batch2),
407
+ )
408
+
409
+ # Extra logic for less thans.
410
+ self._radii1 = tf.placeholder(tf.float32, shape=[None, None])
411
+ self._radii2 = tf.placeholder(tf.float32, shape=[None, None])
412
+ dist32 = tf.cast(self.distance_block, tf.float32)[..., None]
413
+ self._batch_1_in = tf.math.reduce_any(dist32 <= self._radii2, axis=1)
414
+ self._batch_2_in = tf.math.reduce_any(dist32 <= self._radii1[:, None], axis=0)
415
+
416
+ def pairwise_distances(self, U, V):
417
+ """
418
+ Evaluate pairwise distances between two batches of feature vectors.
419
+ """
420
+ return self.session.run(
421
+ self.distance_block,
422
+ feed_dict={self._features_batch1: U, self._features_batch2: V},
423
+ )
424
+
425
+ def less_thans(self, batch_1, radii_1, batch_2, radii_2):
426
+ return self.session.run(
427
+ [self._batch_1_in, self._batch_2_in],
428
+ feed_dict={
429
+ self._features_batch1: batch_1,
430
+ self._features_batch2: batch_2,
431
+ self._radii1: radii_1,
432
+ self._radii2: radii_2,
433
+ },
434
+ )
435
+
436
+
437
+ def _batch_pairwise_distances(U, V):
438
+ """
439
+ Compute pairwise distances between two batches of feature vectors.
440
+ """
441
+ with tf.variable_scope("pairwise_dist_block"):
442
+ # Squared norms of each row in U and V.
443
+ norm_u = tf.reduce_sum(tf.square(U), 1)
444
+ norm_v = tf.reduce_sum(tf.square(V), 1)
445
+
446
+ # norm_u as a column and norm_v as a row vectors.
447
+ norm_u = tf.reshape(norm_u, [-1, 1])
448
+ norm_v = tf.reshape(norm_v, [1, -1])
449
+
450
+ # Pairwise squared Euclidean distances.
451
+ D = tf.maximum(norm_u - 2 * tf.matmul(U, V, False, True) + norm_v, 0.0)
452
+
453
+ return D
454
+
455
+
456
+ class NpzArrayReader(ABC):
457
+ @abstractmethod
458
+ def read_batch(self, batch_size: int) -> Optional[np.ndarray]:
459
+ pass
460
+
461
+ @abstractmethod
462
+ def remaining(self) -> int:
463
+ pass
464
+
465
+ def read_batches(self, batch_size: int) -> Iterable[np.ndarray]:
466
+ def gen_fn():
467
+ while True:
468
+ batch = self.read_batch(batch_size)
469
+ if batch is None:
470
+ break
471
+ yield batch
472
+
473
+ rem = self.remaining()
474
+ num_batches = rem // batch_size + int(rem % batch_size != 0)
475
+ return BatchIterator(gen_fn, num_batches)
476
+
477
+
478
+ class BatchIterator:
479
+ def __init__(self, gen_fn, length):
480
+ self.gen_fn = gen_fn
481
+ self.length = length
482
+
483
+ def __len__(self):
484
+ return self.length
485
+
486
+ def __iter__(self):
487
+ return self.gen_fn()
488
+
489
+
490
+ class StreamingNpzArrayReader(NpzArrayReader):
491
+ def __init__(self, arr_f, shape, dtype):
492
+ self.arr_f = arr_f
493
+ self.shape = shape
494
+ self.dtype = dtype
495
+ self.idx = 0
496
+
497
+ def read_batch(self, batch_size: int) -> Optional[np.ndarray]:
498
+ if self.idx >= self.shape[0]:
499
+ return None
500
+
501
+ bs = min(batch_size, self.shape[0] - self.idx)
502
+ self.idx += bs
503
+
504
+ if self.dtype.itemsize == 0:
505
+ return np.ndarray([bs, *self.shape[1:]], dtype=self.dtype)
506
+
507
+ read_count = bs * np.prod(self.shape[1:])
508
+ read_size = int(read_count * self.dtype.itemsize)
509
+ data = _read_bytes(self.arr_f, read_size, "array data")
510
+ return np.frombuffer(data, dtype=self.dtype).reshape([bs, *self.shape[1:]])
511
+
512
+ def remaining(self) -> int:
513
+ return max(0, self.shape[0] - self.idx)
514
+
515
+
516
+ class MemoryNpzArrayReader(NpzArrayReader):
517
+ def __init__(self, arr):
518
+ self.arr = arr
519
+ self.idx = 0
520
+
521
+ @classmethod
522
+ def load(cls, path: str, arr_name: str):
523
+ with open(path, "rb") as f:
524
+ arr = np.load(f)[arr_name]
525
+ return cls(arr)
526
+
527
+ def read_batch(self, batch_size: int) -> Optional[np.ndarray]:
528
+ if self.idx >= self.arr.shape[0]:
529
+ return None
530
+
531
+ res = self.arr[self.idx : self.idx + batch_size]
532
+ self.idx += batch_size
533
+ return res
534
+
535
+ def remaining(self) -> int:
536
+ return max(0, self.arr.shape[0] - self.idx)
537
+
538
+
539
+ @contextmanager
540
+ def open_npz_array(path: str, arr_name: str) -> NpzArrayReader:
541
+ with _open_npy_file(path, arr_name) as arr_f:
542
+ version = np.lib.format.read_magic(arr_f)
543
+ if version == (1, 0):
544
+ header = np.lib.format.read_array_header_1_0(arr_f)
545
+ elif version == (2, 0):
546
+ header = np.lib.format.read_array_header_2_0(arr_f)
547
+ else:
548
+ yield MemoryNpzArrayReader.load(path, arr_name)
549
+ return
550
+ shape, fortran, dtype = header
551
+ if fortran or dtype.hasobject:
552
+ yield MemoryNpzArrayReader.load(path, arr_name)
553
+ else:
554
+ yield StreamingNpzArrayReader(arr_f, shape, dtype)
555
+
556
+
557
+ def _read_bytes(fp, size, error_template="ran out of data"):
558
+ """
559
+ Copied from: https://github.com/numpy/numpy/blob/fb215c76967739268de71aa4bda55dd1b062bc2e/numpy/lib/format.py#L788-L886
560
+
561
+ Read from file-like object until size bytes are read.
562
+ Raises ValueError if not EOF is encountered before size bytes are read.
563
+ Non-blocking objects only supported if they derive from io objects.
564
+ Required as e.g. ZipExtFile in python 2.6 can return less data than
565
+ requested.
566
+ """
567
+ data = bytes()
568
+ while True:
569
+ # io files (default in python3) return None or raise on
570
+ # would-block, python2 file will truncate, probably nothing can be
571
+ # done about that. note that regular files can't be non-blocking
572
+ try:
573
+ r = fp.read(size - len(data))
574
+ data += r
575
+ if len(r) == 0 or len(data) == size:
576
+ break
577
+ except io.BlockingIOError:
578
+ pass
579
+ if len(data) != size:
580
+ msg = "EOF: reading %s, expected %d bytes got %d"
581
+ raise ValueError(msg % (error_template, size, len(data)))
582
+ else:
583
+ return data
584
+
585
+
586
+ @contextmanager
587
+ def _open_npy_file(path: str, arr_name: str):
588
+ with open(path, "rb") as f:
589
+ with zipfile.ZipFile(f, "r") as zip_f:
590
+ if f"{arr_name}.npy" not in zip_f.namelist():
591
+ raise ValueError(f"missing {arr_name} in npz file")
592
+ with zip_f.open(f"{arr_name}.npy", "r") as arr_f:
593
+ yield arr_f
594
+
595
+
596
+ def _download_inception_model():
597
+ if os.path.exists(INCEPTION_V3_PATH):
598
+ return
599
+ print("downloading InceptionV3 model...")
600
+ with requests.get(INCEPTION_V3_URL, stream=True) as r:
601
+ r.raise_for_status()
602
+ tmp_path = INCEPTION_V3_PATH + ".tmp"
603
+ with open(tmp_path, "wb") as f:
604
+ for chunk in tqdm(r.iter_content(chunk_size=8192)):
605
+ f.write(chunk)
606
+ os.rename(tmp_path, INCEPTION_V3_PATH)
607
+
608
+
609
+ def _create_feature_graph(input_batch):
610
+ _download_inception_model()
611
+ prefix = f"{random.randrange(2**32)}_{random.randrange(2**32)}"
612
+ with open(INCEPTION_V3_PATH, "rb") as f:
613
+ graph_def = tf.GraphDef()
614
+ graph_def.ParseFromString(f.read())
615
+ pool3, spatial = tf.import_graph_def(
616
+ graph_def,
617
+ input_map={f"ExpandDims:0": input_batch},
618
+ return_elements=[FID_POOL_NAME, FID_SPATIAL_NAME],
619
+ name=prefix,
620
+ )
621
+ _update_shapes(pool3)
622
+ spatial = spatial[..., :7]
623
+ return pool3, spatial
624
+
625
+
626
+ def _create_softmax_graph(input_batch):
627
+ _download_inception_model()
628
+ prefix = f"{random.randrange(2**32)}_{random.randrange(2**32)}"
629
+ with open(INCEPTION_V3_PATH, "rb") as f:
630
+ graph_def = tf.GraphDef()
631
+ graph_def.ParseFromString(f.read())
632
+ (matmul,) = tf.import_graph_def(
633
+ graph_def, return_elements=[f"softmax/logits/MatMul"], name=prefix
634
+ )
635
+ w = matmul.inputs[1]
636
+ logits = tf.matmul(input_batch, w)
637
+ return tf.nn.softmax(logits)
638
+
639
+
640
+ def _update_shapes(pool3):
641
+ # https://github.com/bioinf-jku/TTUR/blob/73ab375cdf952a12686d9aa7978567771084da42/fid.py#L50-L63
642
+ ops = pool3.graph.get_operations()
643
+ for op in ops:
644
+ for o in op.outputs:
645
+ shape = o.get_shape()
646
+ if shape._dims is not None: # pylint: disable=protected-access
647
+ shape = [s for s in shape] # TF 2.x
648
+ new_shape = []
649
+ for j, s in enumerate(shape):
650
+ if s == 1 and j == 0:
651
+ new_shape.append(None)
652
+ else:
653
+ new_shape.append(s)
654
+ o.__dict__["_shape_val"] = tf.TensorShape(new_shape)
655
+ return pool3
656
+
657
+
658
+ def _numpy_partition(arr, kth, **kwargs):
659
+ num_workers = min(cpu_count(), len(arr))
660
+ chunk_size = len(arr) // num_workers
661
+ extra = len(arr) % num_workers
662
+
663
+ start_idx = 0
664
+ batches = []
665
+ for i in range(num_workers):
666
+ size = chunk_size + (1 if i < extra else 0)
667
+ batches.append(arr[start_idx : start_idx + size])
668
+ start_idx += size
669
+
670
+ with ThreadPool(num_workers) as pool:
671
+ return list(pool.map(partial(np.partition, kth=kth, **kwargs), batches))
672
+
673
+
674
+ if __name__ == "__main__":
675
+ main()
gan_loss.py ADDED
@@ -0,0 +1,1229 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Single-file GAN loss module with discriminators and perceptual loss.
3
+ """
4
+
5
+ import functools
6
+ import hashlib
7
+ import math
8
+ import os
9
+ import random
10
+ from collections import namedtuple
11
+ from typing import List, Tuple
12
+
13
+ import numpy as np
14
+ import requests
15
+ import torch
16
+ import torch.nn as nn
17
+ import torch.nn.functional as F
18
+ from torch.nn.utils.spectral_norm import SpectralNorm
19
+ from torchvision import models
20
+ from torchvision.transforms import RandomCrop
21
+ from tqdm import tqdm
22
+
23
+ ###############################################################################
24
+ # PatchGAN discriminator and ActNorm
25
+ ###############################################################################
26
+
27
+
28
+ class NLayerDiscriminator(nn.Module):
29
+ """Defines a PatchGAN discriminator as in Pix2Pix.
30
+
31
+ Reference:
32
+ https://github.com/junyanz/pytorch-CycleGAN-and-pix2pix/blob/master/models/networks.py
33
+ """
34
+
35
+ def __init__(self, input_nc=3, ndf=64, n_layers=3, use_actnorm=False):
36
+ """Construct a PatchGAN discriminator.
37
+
38
+ Args:
39
+ input_nc (int): number of channels in input images.
40
+ ndf (int): number of filters in the last conv layer.
41
+ n_layers (int): number of conv layers in the discriminator.
42
+ use_actnorm (bool): whether to use ActNorm instead of BatchNorm.
43
+ """
44
+ super().__init__()
45
+ if not use_actnorm:
46
+ norm_layer = nn.BatchNorm2d
47
+ else:
48
+ norm_layer = ActNorm
49
+
50
+ if isinstance(norm_layer, functools.partial):
51
+ # no need to use bias as BatchNorm2d has affine parameters
52
+ use_bias = norm_layer.func != nn.BatchNorm2d
53
+ else:
54
+ use_bias = norm_layer != nn.BatchNorm2d
55
+
56
+ kw = 4
57
+ padw = 1
58
+ sequence = [
59
+ nn.Conv2d(input_nc, ndf, kernel_size=kw, stride=2, padding=padw),
60
+ nn.LeakyReLU(0.2, True),
61
+ ]
62
+ nf_mult = 1
63
+ nf_mult_prev = 1
64
+ # gradually increase the number of filters
65
+ for n in range(1, n_layers):
66
+ nf_mult_prev = nf_mult
67
+ nf_mult = min(2 ** n, 8)
68
+ sequence += [
69
+ nn.Conv2d(
70
+ ndf * nf_mult_prev,
71
+ ndf * nf_mult,
72
+ kernel_size=kw,
73
+ stride=2,
74
+ padding=padw,
75
+ bias=use_bias,
76
+ ),
77
+ norm_layer(ndf * nf_mult),
78
+ nn.LeakyReLU(0.2, True),
79
+ ]
80
+
81
+ nf_mult_prev = nf_mult
82
+ nf_mult = min(2 ** n_layers, 8)
83
+ sequence += [
84
+ nn.Conv2d(
85
+ ndf * nf_mult_prev,
86
+ ndf * nf_mult,
87
+ kernel_size=kw,
88
+ stride=1,
89
+ padding=padw,
90
+ bias=use_bias,
91
+ ),
92
+ norm_layer(ndf * nf_mult),
93
+ nn.LeakyReLU(0.2, True),
94
+ ]
95
+
96
+ # output 1 channel prediction map
97
+ sequence += [
98
+ nn.Conv2d(
99
+ ndf * nf_mult,
100
+ 1,
101
+ kernel_size=kw,
102
+ stride=1,
103
+ padding=padw,
104
+ )
105
+ ]
106
+ self.main = nn.Sequential(*sequence)
107
+
108
+ self.apply(self._init_weights)
109
+
110
+ def _init_weights(self, module):
111
+ if isinstance(module, nn.Conv2d):
112
+ nn.init.normal_(module.weight.data, 0.0, 0.02)
113
+ elif isinstance(module, nn.BatchNorm2d):
114
+ nn.init.normal_(module.weight.data, 1.0, 0.02)
115
+ nn.init.constant_(module.bias.data, 0)
116
+
117
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
118
+ """Standard forward."""
119
+ return self.main(x)
120
+
121
+
122
+ class ActNorm(nn.Module):
123
+ def __init__(self, num_features, logdet=False, affine=True, allow_reverse_init=False):
124
+ assert affine
125
+ super().__init__()
126
+ self.logdet = logdet
127
+ self.loc = nn.Parameter(torch.zeros(1, num_features, 1, 1))
128
+ self.scale = nn.Parameter(torch.ones(1, num_features, 1, 1))
129
+ self.allow_reverse_init = allow_reverse_init
130
+
131
+ self.register_buffer("initialized", torch.tensor(0, dtype=torch.uint8))
132
+
133
+ def initialize(self, x: torch.Tensor):
134
+ with torch.no_grad():
135
+ flatten = x.permute(1, 0, 2, 3).contiguous().view(x.shape[1], -1)
136
+ mean = (
137
+ flatten.mean(1)
138
+ .unsqueeze(1)
139
+ .unsqueeze(2)
140
+ .unsqueeze(3)
141
+ .permute(1, 0, 2, 3)
142
+ )
143
+ std = (
144
+ flatten.std(1)
145
+ .unsqueeze(1)
146
+ .unsqueeze(2)
147
+ .unsqueeze(3)
148
+ .permute(1, 0, 2, 3)
149
+ )
150
+
151
+ self.loc.data.copy_(-mean)
152
+ self.scale.data.copy_(1 / (std + 1e-6))
153
+
154
+ def forward(self, x: torch.Tensor, reverse: bool = False):
155
+ if reverse:
156
+ return self.reverse(x)
157
+ if len(x.shape) == 2:
158
+ x = x[:, :, None, None]
159
+ squeeze = True
160
+ else:
161
+ squeeze = False
162
+
163
+ _, _, height, width = x.shape
164
+
165
+ if self.training and self.initialized.item() == 0:
166
+ self.initialize(x)
167
+ self.initialized.fill_(1)
168
+
169
+ h = self.scale * (x + self.loc)
170
+
171
+ if squeeze:
172
+ h = h.squeeze(-1).squeeze(-1)
173
+
174
+ if self.logdet:
175
+ log_abs = torch.log(torch.abs(self.scale))
176
+ logdet = height * width * torch.sum(log_abs)
177
+ logdet = logdet * torch.ones(x.shape[0]).to(x)
178
+ return h, logdet
179
+
180
+ return h
181
+
182
+ def reverse(self, y: torch.Tensor):
183
+ if self.training and self.initialized.item() == 0:
184
+ if not self.allow_reverse_init:
185
+ raise RuntimeError(
186
+ "Initializing ActNorm in reverse direction is disabled by default. "
187
+ "Use allow_reverse_init=True to enable."
188
+ )
189
+ else:
190
+ self.initialize(y)
191
+ self.initialized.fill_(1)
192
+
193
+ if len(y.shape) == 2:
194
+ y = y[:, :, None, None]
195
+ squeeze = True
196
+ else:
197
+ squeeze = False
198
+
199
+ h = y / self.scale - self.loc
200
+
201
+ if squeeze:
202
+ h = h.squeeze(-1).squeeze(-1)
203
+ return h
204
+
205
+
206
+ ###############################################################################
207
+ # StyleGAN-like image discriminator
208
+ ###############################################################################
209
+
210
+ try:
211
+ from kornia.filters import filter2d
212
+ except Exception:
213
+ filter2d = None
214
+
215
+
216
+ def leaky_relu(p: float = 0.2):
217
+ return nn.LeakyReLU(p, inplace=True)
218
+
219
+
220
+ def exists(val):
221
+ return val is not None
222
+
223
+
224
+ class Blur(nn.Module):
225
+ def __init__(self):
226
+ super().__init__()
227
+ f = torch.Tensor([1, 2, 1])
228
+ self.register_buffer("f", f)
229
+
230
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
231
+ if filter2d is None:
232
+ return x
233
+ f = self.f
234
+ f = f[None, None, :] * f[None, :, None]
235
+ return filter2d(x, f, normalized=True)
236
+
237
+
238
+ class DiscriminatorBlock(nn.Module):
239
+ def __init__(self, input_channels, filters, downsample=True):
240
+ super().__init__()
241
+ self.conv_res = nn.Conv2d(input_channels, filters, 1, stride=(2 if downsample else 1))
242
+
243
+ self.net = nn.Sequential(
244
+ nn.Conv2d(input_channels, filters, 3, padding=1),
245
+ leaky_relu(),
246
+ nn.Conv2d(filters, filters, 3, padding=1),
247
+ leaky_relu(),
248
+ )
249
+
250
+ self.downsample = (
251
+ nn.Sequential(
252
+ Blur(),
253
+ nn.Conv2d(filters, filters, 3, padding=1, stride=2),
254
+ )
255
+ if downsample
256
+ else None
257
+ )
258
+
259
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
260
+ res = self.conv_res(x)
261
+ x = self.net(x)
262
+ if exists(self.downsample):
263
+ x = self.downsample(x)
264
+ x = (x + res) * (1 / math.sqrt(2))
265
+ return x
266
+
267
+
268
+ class StyleGANDiscriminator(nn.Module):
269
+ def __init__(self, input_nc=3, ndf=64, n_layers=3, channel_multiplier=1, image_size=256):
270
+ super().__init__()
271
+ channels = {
272
+ 4: 512,
273
+ 8: 512,
274
+ 16: 512,
275
+ 32: 512,
276
+ 64: 256 * channel_multiplier,
277
+ 128: 128 * channel_multiplier,
278
+ 256: 64 * channel_multiplier,
279
+ 512: 32 * channel_multiplier,
280
+ 1024: 16 * channel_multiplier,
281
+ }
282
+
283
+ log_size = int(math.log(image_size, 2))
284
+ in_channel = channels[image_size]
285
+
286
+ blocks = [nn.Conv2d(input_nc, in_channel, 3, padding=1), leaky_relu()]
287
+ for i in range(log_size, 2, -1):
288
+ out_channel = channels[2 ** (i - 1)]
289
+ blocks.append(DiscriminatorBlock(in_channel, out_channel))
290
+ in_channel = out_channel
291
+ self.blocks = nn.ModuleList(blocks)
292
+
293
+ self.final_conv = nn.Sequential(
294
+ nn.Conv2d(in_channel, channels[4], 3, padding=1),
295
+ leaky_relu(),
296
+ )
297
+ self.final_linear = nn.Sequential(
298
+ nn.Linear(channels[4] * 4 * 4, channels[4]),
299
+ leaky_relu(),
300
+ nn.Linear(channels[4], 1),
301
+ )
302
+
303
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
304
+ for block in self.blocks:
305
+ x = block(x)
306
+ x = self.final_conv(x)
307
+ x = x.view(x.shape[0], -1)
308
+ x = self.final_linear(x)
309
+ return x
310
+
311
+
312
+ ###############################################################################
313
+ # DiffAug (data augmentation for discriminator)
314
+ ###############################################################################
315
+
316
+
317
+ class DiffAug(object):
318
+ def __init__(self, prob: float = 1.0, cutout: float = 0.2):
319
+ self.grids = {}
320
+ self.prob = abs(prob)
321
+ self.using_cutout = prob > 0
322
+ self.cutout = cutout
323
+ self.img_channels = -1
324
+ self.last_blur_radius = -1
325
+ self.last_blur_kernel_h = None
326
+ self.last_blur_kernel_w = None
327
+
328
+ def get_grids(self, B, x, y, dev):
329
+ if (B, x, y) in self.grids:
330
+ return self.grids[(B, x, y)]
331
+
332
+ self.grids[(B, x, y)] = ret = torch.meshgrid(
333
+ torch.arange(B, dtype=torch.long, device=dev),
334
+ torch.arange(x, dtype=torch.long, device=dev),
335
+ torch.arange(y, dtype=torch.long, device=dev),
336
+ indexing="ij",
337
+ )
338
+ return ret
339
+
340
+ def aug(self, BCHW: torch.Tensor, warmup_blur_schedule: float = 0) -> torch.Tensor:
341
+ # warmup blurring
342
+ if BCHW.dtype != torch.float32:
343
+ BCHW = BCHW.float()
344
+ if warmup_blur_schedule > 0:
345
+ self.img_channels = BCHW.shape[1]
346
+ sigma0 = (BCHW.shape[-2] * 0.5) ** 0.5
347
+ sigma = sigma0 * warmup_blur_schedule
348
+ blur_radius = math.floor(sigma * 3) # 3-sigma is enough for Gaussian
349
+ if blur_radius >= 1:
350
+ if self.last_blur_radius != blur_radius:
351
+ self.last_blur_radius = blur_radius
352
+ gaussian = torch.arange(
353
+ -blur_radius, blur_radius + 1, dtype=torch.float32, device=BCHW.device
354
+ )
355
+ gaussian = gaussian.mul_(1 / sigma).square_().neg_().exp2_()
356
+ gaussian.div_(gaussian.sum()) # normalize
357
+ self.last_blur_kernel_h = (
358
+ gaussian.view(1, 1, 2 * blur_radius + 1, 1)
359
+ .repeat(self.img_channels, 1, 1, 1)
360
+ .contiguous()
361
+ )
362
+ self.last_blur_kernel_w = (
363
+ gaussian.view(1, 1, 1, 2 * blur_radius + 1)
364
+ .repeat(self.img_channels, 1, 1, 1)
365
+ .contiguous()
366
+ )
367
+
368
+ BCHW = F.pad(BCHW, [blur_radius, blur_radius, blur_radius, blur_radius], mode="reflect")
369
+ BCHW = F.conv2d(
370
+ input=BCHW, weight=self.last_blur_kernel_h, bias=None, groups=self.img_channels
371
+ )
372
+ BCHW = F.conv2d(
373
+ input=BCHW, weight=self.last_blur_kernel_w, bias=None, groups=self.img_channels
374
+ )
375
+
376
+ if self.prob < 1e-6:
377
+ return BCHW
378
+ trans, color, cut = torch.rand(3) <= self.prob
379
+ trans, color, cut = trans.item(), color.item(), cut.item()
380
+ B, dev = BCHW.shape[0], BCHW.device
381
+ rand01 = torch.rand(7, B, 1, 1, device=dev) if (trans or color or cut) else None
382
+
383
+ raw_h, raw_w = BCHW.shape[-2:]
384
+ if trans:
385
+ ratio = 0.125
386
+ delta_h = round(raw_h * ratio)
387
+ delta_w = round(raw_w * ratio)
388
+ translation_h = rand01[0].mul(delta_h + delta_h + 1).floor().long() - delta_h
389
+ translation_w = rand01[1].mul(delta_w + delta_w + 1).floor().long() - delta_w
390
+
391
+ grid_B, grid_h, grid_w = self.get_grids(B, raw_h, raw_w, dev)
392
+ grid_h = (grid_h + translation_h).add_(1).clamp_(0, raw_h + 1)
393
+ grid_w = (grid_w + translation_w).add_(1).clamp_(0, raw_w + 1)
394
+ bchw_pad = F.pad(BCHW, [1, 1, 1, 1, 0, 0, 0, 0])
395
+ BCHW = (
396
+ bchw_pad.permute(0, 2, 3, 1)
397
+ .contiguous()[grid_B, grid_h, grid_w]
398
+ .permute(0, 3, 1, 2)
399
+ .contiguous()
400
+ )
401
+
402
+ if color:
403
+ BCHW = BCHW.add(rand01[2].unsqueeze(-1).sub(0.5))
404
+ bchw_mean = BCHW.mean(dim=1, keepdim=True)
405
+ BCHW = BCHW.sub(bchw_mean).mul(rand01[3].unsqueeze(-1).mul(2)).add_(bchw_mean)
406
+ bchw_mean = BCHW.mean(dim=(1, 2, 3), keepdim=True)
407
+ BCHW = BCHW.sub(bchw_mean).mul(rand01[4].unsqueeze(-1).add(0.5)).add_(bchw_mean)
408
+
409
+ if self.using_cutout and cut:
410
+ ratio = self.cutout
411
+ cutout_h = round(raw_h * ratio)
412
+ cutout_w = round(raw_w * ratio)
413
+ offset_h = rand01[5].mul(raw_h + (1 - cutout_h % 2)).floor().long()
414
+ offset_w = rand01[6].mul(raw_w + (1 - cutout_w % 2)).floor().long()
415
+
416
+ grid_B, grid_h, grid_w = self.get_grids(B, cutout_h, cutout_w, dev)
417
+ grid_h = (grid_h + offset_h).sub_(cutout_h // 2).clamp(min=0, max=raw_h - 1)
418
+ grid_w = (grid_w + offset_w).sub_(cutout_w // 2).clamp(min=0, max=raw_w - 1)
419
+ mask = torch.ones(B, raw_h, raw_w, dtype=BCHW.dtype, device=dev)
420
+ mask[grid_B, grid_h, grid_w] = 0
421
+ BCHW = BCHW.mul(mask.unsqueeze(1))
422
+
423
+ return BCHW
424
+
425
+
426
+ ###############################################################################
427
+ # LPIPS perceptual similarity loss
428
+ ###############################################################################
429
+
430
+ URL_MAP = {
431
+ "vgg_lpips": "https://heibox.uni-heidelberg.de/f/607503859c864bc1b30b/?dl=1",
432
+ }
433
+
434
+ CKPT_MAP = {
435
+ "vgg_lpips": "vgg.pth",
436
+ }
437
+
438
+ MD5_MAP = {
439
+ "vgg_lpips": "d507d7349b931f0638a25a48a722f98a",
440
+ }
441
+
442
+
443
+ def download(url, local_path, chunk_size=1024):
444
+ os.makedirs(os.path.split(local_path)[0], exist_ok=True)
445
+ with requests.get(url, stream=True) as r:
446
+ total_size = int(r.headers.get("content-length", 0))
447
+ with tqdm(total=total_size, unit="B", unit_scale=True) as pbar:
448
+ with open(local_path, "wb") as f:
449
+ for data in r.iter_content(chunk_size=chunk_size):
450
+ if data:
451
+ f.write(data)
452
+ pbar.update(chunk_size)
453
+
454
+
455
+ def md5_hash(path):
456
+ with open(path, "rb") as f:
457
+ content = f.read()
458
+ return hashlib.md5(content).hexdigest()
459
+
460
+
461
+ def get_ckpt_path(name, root, check=False):
462
+ assert name in URL_MAP
463
+ path = os.path.join(root, CKPT_MAP[name])
464
+ if not os.path.exists(path) or (check and not md5_hash(path) == MD5_MAP[name]):
465
+ print(f"Downloading {name} model from {URL_MAP[name]} to {path}")
466
+ download(URL_MAP[name], path)
467
+ md5 = md5_hash(path)
468
+ assert md5 == MD5_MAP[name], md5
469
+ return path
470
+
471
+
472
+ class ScalingLayer(nn.Module):
473
+ def __init__(self):
474
+ super().__init__()
475
+ self.register_buffer("shift", torch.Tensor([-.030, -.088, -.188])[None, :, None, None])
476
+ self.register_buffer("scale", torch.Tensor([.458, .448, .450])[None, :, None, None])
477
+
478
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
479
+ return (x - self.shift) / self.scale
480
+
481
+
482
+ class NetLinLayer(nn.Module):
483
+ """A single linear layer which does a 1x1 conv."""
484
+
485
+ def __init__(self, chn_in, chn_out=1, use_dropout=False):
486
+ super().__init__()
487
+ layers = [nn.Dropout()] if use_dropout else []
488
+ layers += [nn.Conv2d(chn_in, chn_out, 1, stride=1, padding=0, bias=False)]
489
+ self.model = nn.Sequential(*layers)
490
+
491
+
492
+ class VGG16Features(nn.Module):
493
+ def __init__(self, requires_grad=False, pretrained=True):
494
+ super().__init__()
495
+ vgg_pretrained_features = models.vgg16(pretrained=pretrained).features
496
+ self.slice1 = nn.Sequential()
497
+ self.slice2 = nn.Sequential()
498
+ self.slice3 = nn.Sequential()
499
+ self.slice4 = nn.Sequential()
500
+ self.slice5 = nn.Sequential()
501
+ for x in range(4):
502
+ self.slice1.add_module(str(x), vgg_pretrained_features[x])
503
+ for x in range(4, 9):
504
+ self.slice2.add_module(str(x), vgg_pretrained_features[x])
505
+ for x in range(9, 16):
506
+ self.slice3.add_module(str(x), vgg_pretrained_features[x])
507
+ for x in range(16, 23):
508
+ self.slice4.add_module(str(x), vgg_pretrained_features[x])
509
+ for x in range(23, 30):
510
+ self.slice5.add_module(str(x), vgg_pretrained_features[x])
511
+ if not requires_grad:
512
+ for param in self.parameters():
513
+ param.requires_grad = False
514
+
515
+ def forward(self, x: torch.Tensor):
516
+ h = self.slice1(x)
517
+ h_relu1_2 = h
518
+ h = self.slice2(h)
519
+ h_relu2_2 = h
520
+ h = self.slice3(h)
521
+ h_relu3_3 = h
522
+ h = self.slice4(h)
523
+ h_relu4_3 = h
524
+ h = self.slice5(h)
525
+ h_relu5_3 = h
526
+ vgg_outputs = namedtuple(
527
+ "VggOutputs", ["relu1_2", "relu2_2", "relu3_3", "relu4_3", "relu5_3"]
528
+ )
529
+ out = vgg_outputs(h_relu1_2, h_relu2_2, h_relu3_3, h_relu4_3, h_relu5_3)
530
+ return out
531
+
532
+
533
+ def normalize_tensor(x, eps: float = 1e-10):
534
+ norm_factor = torch.sqrt(torch.sum(x ** 2, dim=1, keepdim=True))
535
+ return x / (norm_factor + eps)
536
+
537
+
538
+ def spatial_average(x, keepdim: bool = True):
539
+ return x.mean([2, 3], keepdim=keepdim)
540
+
541
+
542
+ class LPIPS(nn.Module):
543
+ """Learned perceptual image patch similarity."""
544
+
545
+ def __init__(self, use_dropout: bool = True):
546
+ super().__init__()
547
+ self.scaling_layer = ScalingLayer()
548
+ self.chns = [64, 128, 256, 512, 512] # VGG16 feature sizes
549
+ self.net = VGG16Features(pretrained=True, requires_grad=False)
550
+ self.lin0 = NetLinLayer(self.chns[0], use_dropout=use_dropout)
551
+ self.lin1 = NetLinLayer(self.chns[1], use_dropout=use_dropout)
552
+ self.lin2 = NetLinLayer(self.chns[2], use_dropout=use_dropout)
553
+ self.lin3 = NetLinLayer(self.chns[3], use_dropout=use_dropout)
554
+ self.lin4 = NetLinLayer(self.chns[4], use_dropout=use_dropout)
555
+ self.load_from_pretrained()
556
+ for param in self.parameters():
557
+ param.requires_grad = False
558
+
559
+ def load_from_pretrained(self, name: str = "vgg_lpips"):
560
+ ckpt = get_ckpt_path(
561
+ name, os.path.join(os.path.dirname(os.path.abspath(__file__)), "cache")
562
+ )
563
+ self.load_state_dict(torch.load(ckpt, map_location=torch.device("cpu")), strict=False)
564
+ print(f"loaded pretrained LPIPS loss from {ckpt}")
565
+
566
+ def forward(self, x: torch.Tensor, y: torch.Tensor) -> torch.Tensor:
567
+ x_in, y_in = self.scaling_layer(x), self.scaling_layer(y)
568
+ outs0, outs1 = self.net(x_in), self.net(y_in)
569
+ feats0, feats1, diffs = {}, {}, {}
570
+ lins = [self.lin0, self.lin1, self.lin2, self.lin3, self.lin4]
571
+ for kk in range(len(self.chns)):
572
+ feats0[kk], feats1[kk] = normalize_tensor(outs0[kk]), normalize_tensor(outs1[kk])
573
+ diffs[kk] = (feats0[kk] - feats1[kk]) ** 2
574
+
575
+ res = [spatial_average(lins[kk].model(diffs[kk]), keepdim=True) for kk in range(len(self.chns))]
576
+ val = res[0]
577
+ for l in range(1, len(self.chns)):
578
+ val += res[l]
579
+ return val
580
+
581
+
582
+ ###############################################################################
583
+ # DINO-based discriminator (DINODiscriminator)
584
+ ###############################################################################
585
+
586
+
587
+ class MLPNoDrop(nn.Module):
588
+ def __init__(self, in_features, hidden_features=None, out_features=None, fused_if_available=True):
589
+ super().__init__()
590
+ try:
591
+ from flash_attn.ops.fused_dense import fused_mlp_func as _fused_mlp_func
592
+ except Exception:
593
+ _fused_mlp_func = None
594
+
595
+ self.fused_mlp_func = (
596
+ _fused_mlp_func if (torch.cuda.is_available() and fused_if_available) else None
597
+ )
598
+ out_features = out_features or in_features
599
+ hidden_features = hidden_features or in_features
600
+ self.fc1 = nn.Linear(in_features, hidden_features)
601
+ self.act = nn.GELU(approximate="tanh")
602
+ self.fc2 = nn.Linear(hidden_features, out_features)
603
+
604
+ def forward(self, x):
605
+ if self.fused_mlp_func is not None:
606
+ return self.fused_mlp_func(
607
+ x=x,
608
+ weight1=self.fc1.weight,
609
+ weight2=self.fc2.weight,
610
+ bias1=self.fc1.bias,
611
+ bias2=self.fc2.bias,
612
+ activation="gelu_approx",
613
+ save_pre_act=self.training,
614
+ return_residual=False,
615
+ checkpoint_lvl=0,
616
+ heuristic=0,
617
+ process_group=None,
618
+ )
619
+ else:
620
+ return self.fc2(self.act(self.fc1(x)))
621
+
622
+
623
+ class SelfAttentionNoDrop(nn.Module):
624
+ def __init__(self, block_idx, embed_dim=768, num_heads=12, flash_if_available=True):
625
+ super().__init__()
626
+ assert embed_dim % num_heads == 0
627
+ self.block_idx = block_idx
628
+ self.num_heads = num_heads
629
+ self.head_dim = embed_dim // num_heads
630
+ self.scale = 1 / math.sqrt(self.head_dim)
631
+ self.qkv = nn.Linear(embed_dim, embed_dim * 3, bias=True)
632
+ self.proj = nn.Linear(embed_dim, embed_dim, bias=True)
633
+
634
+ try:
635
+ from flash_attn import flash_attn_qkvpacked_func as _flash_attn_qkvpacked_func
636
+ except Exception:
637
+ _flash_attn_qkvpacked_func = None
638
+
639
+ self.using_flash_attn = (
640
+ torch.cuda.is_available() and flash_if_available and _flash_attn_qkvpacked_func is not None
641
+ )
642
+ self._flash_attn_qkvpacked_func = _flash_attn_qkvpacked_func
643
+
644
+ def forward(self, x):
645
+ B, L, C = x.shape
646
+ qkv = self.qkv(x).view(B, L, 3, self.num_heads, self.head_dim)
647
+ if self.using_flash_attn and qkv.dtype != torch.float32:
648
+ oup = self._flash_attn_qkvpacked_func(qkv, softmax_scale=self.scale).view(B, L, C)
649
+ else:
650
+ q, k, v = qkv.permute(2, 0, 3, 1, 4).unbind(dim=0) # BHLc
651
+ scale = self.scale
652
+ attn = q.mul(scale) @ k.transpose(-2, -1)
653
+ attn = attn.softmax(dim=-1)
654
+ oup = (attn @ v).transpose(1, 2).reshape(B, L, C)
655
+ return self.proj(oup)
656
+
657
+
658
+ class SABlockNoDrop(nn.Module):
659
+ def __init__(self, block_idx, embed_dim, num_heads, mlp_ratio, norm_eps):
660
+ super().__init__()
661
+ self.norm1 = nn.LayerNorm(embed_dim, eps=norm_eps)
662
+ self.attn = SelfAttentionNoDrop(
663
+ block_idx=block_idx,
664
+ embed_dim=embed_dim,
665
+ num_heads=num_heads,
666
+ flash_if_available=True,
667
+ )
668
+ self.norm2 = nn.LayerNorm(embed_dim, eps=norm_eps)
669
+ self.mlp = MLPNoDrop(
670
+ in_features=embed_dim,
671
+ hidden_features=round(embed_dim * mlp_ratio),
672
+ fused_if_available=True,
673
+ )
674
+
675
+ def forward(self, x):
676
+ x = x + self.attn(self.norm1(x))
677
+ x = x + self.mlp(self.norm2(x))
678
+ return x
679
+
680
+
681
+ class ResidualBlock(nn.Module):
682
+ def __init__(self, fn):
683
+ super().__init__()
684
+ self.fn = fn
685
+ self.ratio = 1 / np.sqrt(2)
686
+
687
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
688
+ return (self.fn(x).add(x)).mul_(self.ratio)
689
+
690
+
691
+ class SpectralConv1d(nn.Conv1d):
692
+ def __init__(self, *args, **kwargs):
693
+ super().__init__(*args, **kwargs)
694
+ SpectralNorm.apply(self, name="weight", n_power_iterations=1, dim=0, eps=1e-12)
695
+
696
+
697
+ class BatchNormLocal(nn.Module):
698
+ def __init__(self, num_features: int, affine: bool = True, virtual_bs: int = 8, eps: float = 1e-6):
699
+ super().__init__()
700
+ self.virtual_bs = virtual_bs
701
+ self.eps = eps
702
+ self.affine = affine
703
+
704
+ if self.affine:
705
+ self.weight = nn.Parameter(torch.ones(num_features))
706
+ self.bias = nn.Parameter(torch.zeros(num_features))
707
+
708
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
709
+ shape = x.size()
710
+ x = x.float()
711
+
712
+ # Reshape batch into groups.
713
+ G = int(np.ceil(x.size(0) / self.virtual_bs))
714
+ x = x.view(G, -1, x.size(-2), x.size(-1))
715
+
716
+ # Calculate stats.
717
+ mean = x.mean([1, 3], keepdim=True)
718
+ var = x.var([1, 3], keepdim=True, unbiased=False)
719
+ x = (x - mean) / (torch.sqrt(var + self.eps))
720
+
721
+ if self.affine:
722
+ x = x * self.weight[None, :, None] + self.bias[None, :, None]
723
+
724
+ return x.view(shape)
725
+
726
+
727
+ def make_block(channels: int, kernel_size: int, norm_type: str, norm_eps: float, using_spec_norm: bool):
728
+ if norm_type == "bn":
729
+ norm = BatchNormLocal(channels, eps=norm_eps)
730
+ elif norm_type == "sbn":
731
+ norm = nn.SyncBatchNorm(channels, eps=norm_eps, process_group=None)
732
+ elif norm_type in {"lbn", "hbn"}:
733
+ # Fallback to SyncBatchNorm without a custom local machine group.
734
+ norm = nn.SyncBatchNorm(channels, eps=norm_eps, process_group=None)
735
+ elif norm_type == "gn":
736
+ norm = nn.GroupNorm(num_groups=32, num_channels=channels, eps=norm_eps, affine=True)
737
+ else:
738
+ raise NotImplementedError
739
+
740
+ conv_cls = SpectralConv1d if using_spec_norm else nn.Conv1d
741
+ return nn.Sequential(
742
+ conv_cls(channels, channels, kernel_size=kernel_size, padding=kernel_size // 2, padding_mode="circular"),
743
+ norm,
744
+ nn.LeakyReLU(negative_slope=0.2, inplace=True),
745
+ )
746
+
747
+
748
+ class PatchEmbed(nn.Module):
749
+ def __init__(self, img_size=224, patch_size=16, in_chans=3, embed_dim=768, norm_layer=None, flatten=True):
750
+ super().__init__()
751
+ self.img_size = img_size
752
+ self.patch_size = patch_size
753
+ self.num_patches = (img_size // patch_size) ** 2
754
+ self.flatten = flatten
755
+
756
+ self.proj = nn.Conv2d(in_chans, embed_dim, kernel_size=patch_size, stride=patch_size)
757
+ self.norm = norm_layer(embed_dim) if norm_layer else nn.Identity()
758
+
759
+ def forward(self, x):
760
+ x = self.proj(x).flatten(2).transpose(1, 2) # BCHW => BCL => BLC
761
+ return self.norm(x)
762
+
763
+
764
+ class FrozenDINOSmallNoDrop(nn.Module):
765
+ """
766
+ Frozen DINO ViT without any dropout or droppath layers (eval-only),
767
+ based on timm.create_model('vit_small_patch16_224', pretrained=False, num_classes=0).
768
+ """
769
+
770
+ def __init__(
771
+ self,
772
+ depth=12,
773
+ key_depths=(2, 5, 8, 11),
774
+ norm_eps=1e-6,
775
+ patch_size=16,
776
+ in_chans=3,
777
+ num_classes=0,
778
+ embed_dim=384,
779
+ num_heads=6,
780
+ mlp_ratio=4.0,
781
+ ):
782
+ super().__init__()
783
+ self.num_classes = num_classes
784
+ self.num_features = self.embed_dim = embed_dim
785
+
786
+ self.img_size = 224
787
+ self.patch_embed = PatchEmbed(
788
+ img_size=self.img_size,
789
+ patch_size=patch_size,
790
+ in_chans=in_chans,
791
+ embed_dim=embed_dim,
792
+ )
793
+ self.patch_size = patch_size
794
+ self.patch_nums = self.img_size // patch_size
795
+
796
+ # x \in [-1, 1]
797
+ m = torch.tensor((0.485, 0.456, 0.406))
798
+ s = torch.tensor((0.229, 0.224, 0.225))
799
+ self.register_buffer("x_scale", (0.5 / s).reshape(1, 3, 1, 1))
800
+ self.register_buffer("x_shift", ((0.5 - m) / s).reshape(1, 3, 1, 1))
801
+ self.crop = RandomCrop(self.img_size)
802
+
803
+ self.cls_token = nn.Parameter(torch.zeros(1, 1, embed_dim))
804
+ self.dist_token = None
805
+ self.pos_embed = nn.Parameter(
806
+ torch.zeros(1, self.patch_nums * self.patch_nums + 1, embed_dim)
807
+ ) # +1: for cls
808
+
809
+ self.key_depths = set(d for d in key_depths if d < depth)
810
+ self.blocks = nn.Sequential(
811
+ *[
812
+ SABlockNoDrop(
813
+ block_idx=i,
814
+ embed_dim=embed_dim,
815
+ num_heads=num_heads,
816
+ mlp_ratio=mlp_ratio,
817
+ norm_eps=norm_eps,
818
+ )
819
+ for i in range(max(depth, 1 + max(self.key_depths)))
820
+ ]
821
+ )
822
+ self.norm = nn.LayerNorm(embed_dim, eps=norm_eps)
823
+
824
+ # eval mode only
825
+ self.eval()
826
+ for p in self.parameters():
827
+ p.requires_grad_(False)
828
+
829
+ def inter_pos_embed(self, patch_nums=(14, 14)):
830
+ if patch_nums[0] == self.patch_nums and patch_nums[1] == self.patch_nums:
831
+ return self.pos_embed
832
+ pe_cls, pe_grid = self.pos_embed[:, :1], self.pos_embed[0, 1:]
833
+ pe_grid = pe_grid.reshape(1, self.patch_nums, self.patch_nums, -1).permute(0, 3, 1, 2)
834
+ pe_grid = F.interpolate(
835
+ pe_grid, size=(patch_nums[0], patch_nums[1]), mode="bilinear", align_corners=False
836
+ )
837
+ pe_grid = pe_grid.permute(0, 2, 3, 1).reshape(1, patch_nums[0] * patch_nums[1], -1)
838
+ return torch.cat([pe_cls, pe_grid], dim=1)
839
+
840
+ def forward(self, x, grad_ckpt: bool = False):
841
+ with torch.cuda.amp.autocast(enabled=False):
842
+ x = (self.x_scale * x.float()).add_(self.x_shift)
843
+ H, W = x.shape[-2], x.shape[-1]
844
+ if H > self.img_size and W > self.img_size and random.random() <= 0.5:
845
+ x = self.crop(x)
846
+ else:
847
+ x = F.interpolate(
848
+ x,
849
+ size=(self.img_size, self.img_size),
850
+ mode="area" if H > self.img_size else "bicubic",
851
+ )
852
+
853
+ x = self.patch_embed(x)
854
+
855
+ with torch.cuda.amp.autocast(enabled=False):
856
+ x = torch.cat((self.cls_token.expand(x.shape[0], -1, -1), x.float()), dim=1)
857
+ x = x + self.pos_embed
858
+ activations = [(x[:, 1:] + x[:, :1]).transpose_(1, 2)]
859
+ for i, b in enumerate(self.blocks):
860
+ if not grad_ckpt:
861
+ x = b(x)
862
+ else:
863
+ x = torch.utils.checkpoint.checkpoint(b, x, use_reentrant=False)
864
+ if i in self.key_depths:
865
+ activations.append((x[:, 1:].float() + x[:, :1].float()).transpose_(1, 2))
866
+ return activations
867
+
868
+
869
+ class DinoDisc(nn.Module):
870
+ def __init__(
871
+ self,
872
+ dino_ckpt_path="https://dl.fbaipublicfiles.com/dino/dino_deitsmall16_pretrain/dino_deitsmall16_pretrain.pth",
873
+ device="cuda",
874
+ ks=9,
875
+ depth=12,
876
+ key_depths=(2, 5, 8, 11),
877
+ norm_type="bn",
878
+ using_spec_norm=True,
879
+ norm_eps=1e-6,
880
+ ):
881
+ super().__init__()
882
+ # load state
883
+ state = torch.hub.load_state_dict_from_url(dino_ckpt_path, map_location="cpu")
884
+ for k in sorted(state.keys()):
885
+ if ".attn.qkv.bias" in k:
886
+ bias = state[k]
887
+ C = bias.numel() // 3
888
+ bias[C : 2 * C].zero_() # zero out k_bias
889
+
890
+ key_depths = tuple(d for d in key_depths if d < depth)
891
+ d = FrozenDINOSmallNoDrop(depth=depth, key_depths=key_depths, norm_eps=norm_eps)
892
+ missing, unexpected = d.load_state_dict(state, strict=False)
893
+ missing = [
894
+ m
895
+ for m in missing
896
+ if all(x not in m for x in {"x_scale", "x_shift"})
897
+ ]
898
+ if torch.cuda.is_available():
899
+ assert len(missing) == 0, f"missing keys: {missing}"
900
+ assert len(unexpected) == 0, f"unexpected keys: {unexpected}"
901
+
902
+ self.dino_proxy: Tuple[FrozenDINOSmallNoDrop] = (d.to(device=device),)
903
+ dino_C = self.dino_proxy[0].embed_dim
904
+
905
+ self.heads = nn.ModuleList(
906
+ [
907
+ nn.Sequential(
908
+ make_block(
909
+ dino_C,
910
+ kernel_size=1,
911
+ norm_type=norm_type,
912
+ norm_eps=norm_eps,
913
+ using_spec_norm=using_spec_norm,
914
+ ),
915
+ ResidualBlock(
916
+ make_block(
917
+ dino_C,
918
+ kernel_size=ks,
919
+ norm_type=norm_type,
920
+ norm_eps=norm_eps,
921
+ using_spec_norm=using_spec_norm,
922
+ )
923
+ ),
924
+ (
925
+ SpectralConv1d
926
+ if using_spec_norm
927
+ else nn.Conv1d
928
+ )(dino_C, 1, kernel_size=1, padding=0),
929
+ )
930
+ for _ in range(len(key_depths) + 1)
931
+ ]
932
+ )
933
+
934
+ def reinit(
935
+ self,
936
+ dino_ckpt_path="https://dl.fbaipublicfiles.com/dino/dino_deitsmall16_pretrain/dino_deitsmall16_pretrain.pth",
937
+ device="cuda",
938
+ ks=9,
939
+ depth=12,
940
+ key_depths=(2, 5, 8, 11),
941
+ norm_type="bn",
942
+ using_spec_norm=True,
943
+ norm_eps=1e-6,
944
+ ):
945
+ dino_C = self.dino_proxy[0].embed_dim
946
+ heads = nn.ModuleList(
947
+ [
948
+ nn.Sequential(
949
+ make_block(
950
+ dino_C,
951
+ kernel_size=1,
952
+ norm_type=norm_type,
953
+ norm_eps=norm_eps,
954
+ using_spec_norm=using_spec_norm,
955
+ ),
956
+ ResidualBlock(
957
+ make_block(
958
+ dino_C,
959
+ kernel_size=ks,
960
+ norm_type=norm_type,
961
+ norm_eps=norm_eps,
962
+ using_spec_norm=using_spec_norm,
963
+ )
964
+ ),
965
+ (
966
+ SpectralConv1d
967
+ if using_spec_norm
968
+ else nn.Conv1d
969
+ )(dino_C, 1, kernel_size=1, padding=0),
970
+ )
971
+ for _ in range(len(key_depths) + 1)
972
+ ]
973
+ )
974
+
975
+ self.heads.load_state_dict(heads.state_dict())
976
+
977
+ def forward(self, x_in_pm1: torch.Tensor, grad_ckpt: bool = False) -> torch.Tensor:
978
+ # x_in_pm1: image tensor normalized to [-1, 1]
979
+ dino_grad_ckpt = grad_ckpt and x_in_pm1.requires_grad
980
+ FrozenDINOSmallNoDrop.forward
981
+ activations: List[torch.Tensor] = self.dino_proxy[0](
982
+ x_in_pm1.float(), grad_ckpt=dino_grad_ckpt
983
+ )
984
+ B = x_in_pm1.shape[0]
985
+ return torch.cat(
986
+ [
987
+ (
988
+ h(act)
989
+ if not grad_ckpt
990
+ else torch.utils.checkpoint.checkpoint(h, act, use_reentrant=False)
991
+ ).view(B, -1)
992
+ for h, act in zip(self.heads, activations)
993
+ ],
994
+ dim=1,
995
+ )
996
+
997
+
998
+ ###############################################################################
999
+ # GAN loss helpers
1000
+ ###############################################################################
1001
+
1002
+
1003
+ def hinge_d_loss(logits_real: torch.Tensor, logits_fake: torch.Tensor) -> torch.Tensor:
1004
+ loss_real = torch.mean(F.relu(1.0 - logits_real))
1005
+ loss_fake = torch.mean(F.relu(1.0 + logits_fake))
1006
+ d_loss = 0.5 * (loss_real + loss_fake)
1007
+ return d_loss
1008
+
1009
+
1010
+ def vanilla_d_loss(logits_real: torch.Tensor, logits_fake: torch.Tensor) -> torch.Tensor:
1011
+ loss_real = torch.mean(F.softplus(-logits_real))
1012
+ loss_fake = torch.mean(F.softplus(logits_fake))
1013
+ d_loss = loss_real + loss_fake
1014
+ return d_loss
1015
+
1016
+
1017
+ def non_saturating_d_loss(logits_real: torch.Tensor, logits_fake: torch.Tensor) -> torch.Tensor:
1018
+ loss_real = torch.mean(
1019
+ F.binary_cross_entropy_with_logits(torch.ones_like(logits_real), logits_real)
1020
+ )
1021
+ loss_fake = torch.mean(
1022
+ F.binary_cross_entropy_with_logits(torch.zeros_like(logits_fake), logits_fake)
1023
+ )
1024
+ d_loss = 0.5 * (loss_real + loss_fake)
1025
+ return d_loss
1026
+
1027
+
1028
+ def hinge_gen_loss(logit_fake: torch.Tensor) -> torch.Tensor:
1029
+ return -torch.mean(logit_fake)
1030
+
1031
+ def vanilla_gen_loss(logit_fake: torch.Tensor) -> torch.Tensor:
1032
+ return torch.mean(F.softplus(-logit_fake))
1033
+
1034
+ def non_saturating_gen_loss(logit_fake: torch.Tensor) -> torch.Tensor:
1035
+ return torch.mean(
1036
+ F.binary_cross_entropy_with_logits(torch.ones_like(logit_fake), logit_fake)
1037
+ )
1038
+
1039
+
1040
+ def adopt_weight(weight: float, global_step: int, threshold: int = 0, value: float = 0.0):
1041
+ if global_step < threshold:
1042
+ weight = value
1043
+ return weight
1044
+
1045
+
1046
+ class LeCAM_EMA(object):
1047
+ def __init__(self, init: float = 0.0, decay: float = 0.999):
1048
+ self.logits_real_ema = init
1049
+ self.logits_fake_ema = init
1050
+ self.decay = decay
1051
+
1052
+ def update(self, logits_real: torch.Tensor, logits_fake: torch.Tensor):
1053
+ self.logits_real_ema = (
1054
+ self.logits_real_ema * self.decay
1055
+ + torch.mean(logits_real).item() * (1 - self.decay)
1056
+ )
1057
+ self.logits_fake_ema = (
1058
+ self.logits_fake_ema * self.decay
1059
+ + torch.mean(logits_fake).item() * (1 - self.decay)
1060
+ )
1061
+
1062
+
1063
+ def lecam_reg(real_pred: torch.Tensor, fake_pred: torch.Tensor, lecam_ema: LeCAM_EMA):
1064
+ reg = torch.mean(F.relu(real_pred - lecam_ema.logits_fake_ema).pow(2)) + torch.mean(
1065
+ F.relu(lecam_ema.logits_real_ema - fake_pred).pow(2)
1066
+ )
1067
+ return reg
1068
+
1069
+
1070
+ ###############################################################################
1071
+ # Combined GAN loss (generator + discriminator) with perceptual & VQ terms
1072
+ ###############################################################################
1073
+
1074
+
1075
+ class GANLoss(nn.Module):
1076
+ """
1077
+ Combined loss used for training VQ-style autoencoders with GAN objectives.
1078
+
1079
+ This is functionally similar to the original `VQLoss` but packaged so it can
1080
+ live in a single file together with its discriminators and LPIPS.
1081
+ """
1082
+
1083
+ def __init__(
1084
+ self,
1085
+ disc_loss: str = "hinge",
1086
+ disc_dim: int = 64,
1087
+ disc_type: str = "patchgan",
1088
+ image_size: int = 256,
1089
+ disc_num_layers: int = 3,
1090
+ disc_in_channels: int = 3,
1091
+ disc_adaptive_weight: bool = False,
1092
+ gen_adv_loss: str = "hinge",
1093
+ reconstruction_loss: str = "l2",
1094
+ reconstruction_weight: float = 1.0,
1095
+ codebook_weight: float = 1.0,
1096
+ perceptual_weight: float = 1.0,
1097
+ lecam_loss_weight: float = None,
1098
+ norm_type: str = "bn",
1099
+ aug_prob: float = 1.0,
1100
+ ):
1101
+ super().__init__()
1102
+ # discriminator type & architecture
1103
+ assert disc_type in ["patchgan", "stylegan", "dinodisc"]
1104
+ assert disc_loss in ["hinge", "vanilla", "non-saturating"]
1105
+ self.disc_type = disc_type
1106
+ if disc_type == "patchgan":
1107
+ self.discriminator = NLayerDiscriminator(
1108
+ input_nc=disc_in_channels,
1109
+ n_layers=disc_num_layers,
1110
+ ndf=disc_dim,
1111
+ )
1112
+ elif disc_type == "stylegan":
1113
+ self.discriminator = StyleGANDiscriminator(
1114
+ input_nc=disc_in_channels,
1115
+ image_size=image_size,
1116
+ )
1117
+ elif disc_type == "dinodisc":
1118
+ self.discriminator = DinoDisc(norm_type=norm_type) # default 224 otherwise crop
1119
+ self.daug = DiffAug(prob=aug_prob, cutout=0.2)
1120
+ else:
1121
+ raise ValueError(f"Unknown GAN discriminator type '{disc_type}'.")
1122
+
1123
+ if disc_loss == "hinge":
1124
+ self.disc_loss = hinge_d_loss
1125
+ elif disc_loss == "vanilla":
1126
+ self.disc_loss = vanilla_d_loss
1127
+ elif disc_loss == "non-saturating":
1128
+ self.disc_loss = non_saturating_d_loss
1129
+ else:
1130
+ raise ValueError(f"Unknown GAN discriminator loss '{disc_loss}'.")
1131
+
1132
+
1133
+ # generator adversarial loss
1134
+ if gen_adv_loss == "hinge":
1135
+ self.gen_adv_loss = hinge_gen_loss
1136
+ elif gen_adv_loss == "non-saturating":
1137
+ self.gen_adv_loss = non_saturating_gen_loss
1138
+ elif gen_adv_loss == 'vanilla':
1139
+ self.gen_adv_loss = vanilla_gen_loss
1140
+ else:
1141
+ raise ValueError(f"Unknown GAN generator loss '{gen_adv_loss}'.")
1142
+
1143
+ # perceptual loss
1144
+ self.perceptual_loss = LPIPS().eval()
1145
+ self.perceptual_weight = perceptual_weight
1146
+
1147
+ # reconstruction loss
1148
+ if reconstruction_loss == "l1":
1149
+ self.rec_loss = F.l1_loss
1150
+ elif reconstruction_loss == "l2":
1151
+ self.rec_loss = F.mse_loss
1152
+ else:
1153
+ raise ValueError(f"Unknown rec loss '{reconstruction_loss}'.")
1154
+ self.rec_weight = reconstruction_weight
1155
+
1156
+ # codebook loss weight (used externally when aggregating)
1157
+ self.codebook_weight = codebook_weight
1158
+
1159
+ # LeCAM regularization
1160
+ self.lecam_loss_weight = lecam_loss_weight
1161
+ if self.lecam_loss_weight is not None:
1162
+ self.lecam_ema = LeCAM_EMA()
1163
+
1164
+ def calculate_adaptive_weight(self, nll_loss, g_loss, last_layer):
1165
+ with fabric.no_backward_sync(last_layer):
1166
+ nll_grads = torch.autograd.grad(nll_loss, last_layer, retain_graph=True)[0]
1167
+ g_grads = torch.autograd.grad(g_loss, last_layer, retain_graph=True)[0]
1168
+
1169
+ d_weight = torch.norm(nll_grads) / (torch.norm(g_grads) + 1e-4)
1170
+ d_weight = torch.clamp(d_weight, 0.0, 1e4).detach()
1171
+ return d_weight.detach()
1172
+
1173
+ def forward(
1174
+ self,
1175
+ x,
1176
+ x_hat,
1177
+ global_step,
1178
+ loss='G'
1179
+ ):
1180
+ """
1181
+ Args are kept compatible with the original `VQLoss` for drop-in usage.
1182
+
1183
+ optimizer_idx = 0: generator (autoencoder) update
1184
+ optimizer_idx = 1: discriminator update
1185
+ """
1186
+ # generator update
1187
+ if loss == 'G':
1188
+ # discriminator loss (for generator)
1189
+ if self.disc_type == "dinodisc":
1190
+ if fade_blur_schedule < 1e-6:
1191
+ fade_blur_schedule = 0.0
1192
+ logits_fake = self.discriminator(
1193
+ self.daug.aug(x_hat.contiguous(), fade_blur_schedule)
1194
+ )
1195
+ else:
1196
+ logits_fake = self.discriminator(x_hat.contiguous())
1197
+ generator_adv_loss = self.gen_adv_loss(logits_fake)
1198
+
1199
+ loss = generator_adv_loss
1200
+ return (loss,{'gen_G_loss_vanilla':generator_adv_loss.item()})
1201
+
1202
+ # discriminator update
1203
+ if loss == 'D':
1204
+ if self.disc_type == "dinodisc":
1205
+ logits_fake = self.discriminator(
1206
+ self.daug.aug(x_hat.contiguous().detach(), 0.)
1207
+ )
1208
+ logits_real = self.discriminator(
1209
+ self.daug.aug(x.contiguous().detach(), 0.)
1210
+ )
1211
+ else:
1212
+ logits_fake = self.discriminator(x_hat.contiguous().detach())
1213
+ logits_real = self.discriminator(x.contiguous().detach())
1214
+
1215
+
1216
+
1217
+ if self.lecam_loss_weight is not None:
1218
+ self.lecam_ema.update(logits_real, logits_fake)
1219
+ lecam_loss = self.lecam_loss_weight * lecam_reg(logits_real, logits_fake, self.lecam_ema)
1220
+ non_saturate_d = self.disc_loss(logits_real, logits_fake)
1221
+ d_adversarial_loss = lecam_loss + non_saturate_d
1222
+
1223
+ else:
1224
+ d_adversarial_loss = self.disc_weight * self.disc_loss(logits_real, logits_fake)
1225
+
1226
+ logits_real_log = logits_real.detach()
1227
+ logits_fake_log = logits_fake.detach()
1228
+
1229
+ return (d_adversarial_loss, { "disc_loss":non_saturate_d.item(), "lecam_loss": lecam_loss.item(), "prob_real":logits_real_log.sigmoid().mean().item(), "prob_fake":logits_fake_log.sigmoid().mean().item()})
lpips.py ADDED
@@ -0,0 +1,164 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Stripped version of https://github.com/richzhang/PerceptualSimilarity/tree/master/models"""
2
+
3
+ import os, hashlib
4
+ import requests
5
+ from tqdm import tqdm
6
+
7
+ import torch
8
+ import torch.nn as nn
9
+ from torchvision import models
10
+ from collections import namedtuple
11
+
12
+ URL_MAP = {
13
+ "vgg_lpips": "https://heibox.uni-heidelberg.de/f/607503859c864bc1b30b/?dl=1"
14
+ }
15
+
16
+ CKPT_MAP = {
17
+ "vgg_lpips": "vgg.pth"
18
+ }
19
+
20
+ MD5_MAP = {
21
+ "vgg_lpips": "d507d7349b931f0638a25a48a722f98a"
22
+ }
23
+
24
+ def download(url, local_path, chunk_size=1024):
25
+ os.makedirs(os.path.split(local_path)[0], exist_ok=True)
26
+ with requests.get(url, stream=True) as r:
27
+ total_size = int(r.headers.get("content-length", 0))
28
+ with tqdm(total=total_size, unit="B", unit_scale=True) as pbar:
29
+ with open(local_path, "wb") as f:
30
+ for data in r.iter_content(chunk_size=chunk_size):
31
+ if data:
32
+ f.write(data)
33
+ pbar.update(chunk_size)
34
+
35
+
36
+ def md5_hash(path):
37
+ with open(path, "rb") as f:
38
+ content = f.read()
39
+ return hashlib.md5(content).hexdigest()
40
+
41
+
42
+ def get_ckpt_path(name, root, check=False):
43
+ assert name in URL_MAP
44
+ path = os.path.join(root, CKPT_MAP[name])
45
+ if not os.path.exists(path) or (check and not md5_hash(path) == MD5_MAP[name]):
46
+ print("Downloading {} model from {} to {}".format(name, URL_MAP[name], path))
47
+ download(URL_MAP[name], path)
48
+ md5 = md5_hash(path)
49
+ assert md5 == MD5_MAP[name], md5
50
+ return path
51
+
52
+
53
+ class LPIPS(nn.Module):
54
+ # Learned perceptual metric
55
+ def __init__(self, use_dropout=True):
56
+ super().__init__()
57
+ self.scaling_layer = ScalingLayer()
58
+ self.chns = [64, 128, 256, 512, 512] # vg16 features
59
+ self.net = vgg16(pretrained=True, requires_grad=False)
60
+ self.lin0 = NetLinLayer(self.chns[0], use_dropout=use_dropout)
61
+ self.lin1 = NetLinLayer(self.chns[1], use_dropout=use_dropout)
62
+ self.lin2 = NetLinLayer(self.chns[2], use_dropout=use_dropout)
63
+ self.lin3 = NetLinLayer(self.chns[3], use_dropout=use_dropout)
64
+ self.lin4 = NetLinLayer(self.chns[4], use_dropout=use_dropout)
65
+ self.load_from_pretrained()
66
+ for param in self.parameters():
67
+ param.requires_grad = False
68
+
69
+ def load_from_pretrained(self, name="vgg_lpips"):
70
+ ckpt = get_ckpt_path(name, os.path.join(os.path.dirname(os.path.abspath(__file__)), "cache"))
71
+ self.load_state_dict(torch.load(ckpt, map_location=torch.device("cpu")), strict=False)
72
+ print("loaded pretrained LPIPS loss from {}".format(ckpt))
73
+
74
+ @classmethod
75
+ def from_pretrained(cls, name="vgg_lpips"):
76
+ if name != "vgg_lpips":
77
+ raise NotImplementedError
78
+ model = cls()
79
+ ckpt = get_ckpt_path(name, os.path.join(os.path.dirname(os.path.abspath(__file__)), "cache"))
80
+ model.load_state_dict(torch.load(ckpt, map_location=torch.device("cpu")), strict=False)
81
+ return model
82
+
83
+ def forward(self, input, target):
84
+ in0_input, in1_input = (self.scaling_layer(input), self.scaling_layer(target))
85
+ outs0, outs1 = self.net(in0_input), self.net(in1_input)
86
+ feats0, feats1, diffs = {}, {}, {}
87
+ lins = [self.lin0, self.lin1, self.lin2, self.lin3, self.lin4]
88
+ for kk in range(len(self.chns)):
89
+ feats0[kk], feats1[kk] = normalize_tensor(outs0[kk]), normalize_tensor(outs1[kk])
90
+ diffs[kk] = (feats0[kk] - feats1[kk]) ** 2
91
+
92
+ res = [spatial_average(lins[kk].model(diffs[kk]), keepdim=True) for kk in range(len(self.chns))]
93
+ val = res[0]
94
+ for l in range(1, len(self.chns)):
95
+ val += res[l]
96
+ return val
97
+
98
+
99
+ class ScalingLayer(nn.Module):
100
+ def __init__(self):
101
+ super(ScalingLayer, self).__init__()
102
+ self.register_buffer('shift', torch.Tensor([-.030, -.088, -.188])[None, :, None, None])
103
+ self.register_buffer('scale', torch.Tensor([.458, .448, .450])[None, :, None, None])
104
+
105
+ def forward(self, inp):
106
+ return (inp - self.shift) / self.scale
107
+
108
+
109
+ class NetLinLayer(nn.Module):
110
+ """ A single linear layer which does a 1x1 conv """
111
+ def __init__(self, chn_in, chn_out=1, use_dropout=False):
112
+ super(NetLinLayer, self).__init__()
113
+ layers = [nn.Dropout(), ] if (use_dropout) else []
114
+ layers += [nn.Conv2d(chn_in, chn_out, 1, stride=1, padding=0, bias=False), ]
115
+ self.model = nn.Sequential(*layers)
116
+
117
+
118
+ class vgg16(torch.nn.Module):
119
+ def __init__(self, requires_grad=False, pretrained=True):
120
+ super(vgg16, self).__init__()
121
+ vgg_pretrained_features = models.vgg16(pretrained=pretrained).features
122
+ self.slice1 = torch.nn.Sequential()
123
+ self.slice2 = torch.nn.Sequential()
124
+ self.slice3 = torch.nn.Sequential()
125
+ self.slice4 = torch.nn.Sequential()
126
+ self.slice5 = torch.nn.Sequential()
127
+ self.N_slices = 5
128
+ for x in range(4):
129
+ self.slice1.add_module(str(x), vgg_pretrained_features[x])
130
+ for x in range(4, 9):
131
+ self.slice2.add_module(str(x), vgg_pretrained_features[x])
132
+ for x in range(9, 16):
133
+ self.slice3.add_module(str(x), vgg_pretrained_features[x])
134
+ for x in range(16, 23):
135
+ self.slice4.add_module(str(x), vgg_pretrained_features[x])
136
+ for x in range(23, 30):
137
+ self.slice5.add_module(str(x), vgg_pretrained_features[x])
138
+ if not requires_grad:
139
+ for param in self.parameters():
140
+ param.requires_grad = False
141
+
142
+ def forward(self, X):
143
+ h = self.slice1(X)
144
+ h_relu1_2 = h
145
+ h = self.slice2(h)
146
+ h_relu2_2 = h
147
+ h = self.slice3(h)
148
+ h_relu3_3 = h
149
+ h = self.slice4(h)
150
+ h_relu4_3 = h
151
+ h = self.slice5(h)
152
+ h_relu5_3 = h
153
+ vgg_outputs = namedtuple("VggOutputs", ['relu1_2', 'relu2_2', 'relu3_3', 'relu4_3', 'relu5_3'])
154
+ out = vgg_outputs(h_relu1_2, h_relu2_2, h_relu3_3, h_relu4_3, h_relu5_3)
155
+ return out
156
+
157
+
158
+ def normalize_tensor(x,eps=1e-10):
159
+ norm_factor = torch.sqrt(torch.sum(x**2,dim=1,keepdim=True))
160
+ return x/(norm_factor+eps)
161
+
162
+
163
+ def spatial_average(x, keepdim=True):
164
+ return x.mean([2,3],keepdim=keepdim)
models.py ADDED
@@ -0,0 +1,1013 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import math
2
+ from math import pi
3
+
4
+ import torch
5
+ import torch.nn as nn
6
+ from torch import einsum, broadcast_tensors, Tensor
7
+ import torch.nn.functional as F
8
+ from torch.nn import Module
9
+ from torch.amp import autocast
10
+ from torch import nn
11
+ from einops import rearrange, repeat
12
+ import einops
13
+ import numpy as np
14
+ import os
15
+ import copy
16
+
17
+ # Well Inited Linear
18
+ def weight_init(shape, mode, fan_in, fan_out):
19
+ if mode == 'xavier_uniform': return np.sqrt(6 / (fan_in + fan_out)) * (torch.rand(*shape) * 2 - 1)
20
+ if mode == 'xavier_normal': return np.sqrt(2 / (fan_in + fan_out)) * torch.randn(*shape)
21
+ if mode == 'kaiming_uniform': return np.sqrt(3 / fan_in) * (torch.rand(*shape) * 2 - 1)
22
+ if mode == 'kaiming_normal': return np.sqrt(1 / fan_in) * torch.randn(*shape)
23
+ if mode == 'uniform': return torch.rand() * 2 - 1
24
+ raise ValueError(f'Invalid init mode "{mode}"')
25
+
26
+ class Linear(torch.nn.Module):
27
+ def __init__(self, in_features, out_features, bias=True, init_mode='kaiming_uniform', init_weight=1, init_bias=0):
28
+ super().__init__()
29
+ self.in_features = in_features
30
+ self.out_features = out_features
31
+ init_kwargs = dict(mode=init_mode, fan_in=in_features, fan_out=out_features)
32
+ self.weight = torch.nn.Parameter(weight_init([out_features, in_features], **init_kwargs) * init_weight)
33
+ self.bias = torch.nn.Parameter(weight_init([out_features], **init_kwargs) * init_bias) if bias else None
34
+
35
+ def forward(self, x):
36
+ x = x @ self.weight.to(x.dtype).t()
37
+ if self.bias is not None:
38
+ x = x.add_(self.bias.to(x.dtype))
39
+ return x
40
+
41
+
42
+ class AdaRMSNorm(nn.Module):
43
+ def __init__(self,dim, cond_dim=None, elementwise_affine=True, cond_based_affine=True, centering=False, eps=None):
44
+ super(AdaRMSNorm, self).__init__()
45
+ self.dim = dim
46
+ self.eps = eps
47
+ self.cond_based_affine = cond_based_affine
48
+ self.elementwise_affine = elementwise_affine
49
+ self.centering = centering
50
+ assert not(cond_dim is None and cond_based_affine), 'cond_dim must be provided if cond_based_affine is True'
51
+ if elementwise_affine:
52
+ if cond_based_affine and cond_dim is not None:
53
+ self.affine = Linear(cond_dim, dim, init_weight=1e-5)
54
+ else:
55
+ self.weight = nn.Parameter(torch.ones(self.dim))
56
+ else:
57
+ self.register_parameter("weight", None)
58
+
59
+ def forward(self, x, cond_emb=None):
60
+ eps = torch.finfo(x.dtype).eps if self.eps is None else self.eps
61
+ rms = torch.rsqrt(torch.square(x).mean(dim=-1, keepdim=True) + eps)
62
+ out = ((x - x.mean(dim=-1, keepdim=True)) * rms) if self.centering else (x * rms)
63
+ if self.elementwise_affine:
64
+ if self.cond_based_affine:
65
+ out = out.mul(1. + self.affine(cond_emb).unsqueeze(1))
66
+ else:
67
+ out = out.mul(self.weight)
68
+ return out
69
+
70
+ class AdaLN(nn.Module):
71
+ def __init__(self,dim, cond_dim=None, elementwise_affine=True, cond_based_affine=True, bias=False):
72
+ super(AdaLN, self).__init__()
73
+ self.norm = nn.LayerNorm(dim, elementwise_affine=elementwise_affine and not cond_based_affine)
74
+ self.cond_based_affine = cond_based_affine
75
+ self.bias = bias
76
+ assert not(cond_dim is None and cond_based_affine), 'cond_dim must be provided if cond_based_affine is True'
77
+ self.affine = Linear(cond_dim, 2 * dim if bias else dim, init_weight=1e-5) if cond_based_affine and cond_dim is not None else None
78
+ def forward(self, x, cond_emb=None):
79
+ x = self.norm(x)
80
+ if self.cond_based_affine:
81
+ if self.bias:
82
+ shift, scale = self.affine(cond_emb).unsqueeze(1).chunk(2, dim=-1)
83
+ x = x.mul(1. + scale).add_(shift)
84
+ else:
85
+ scale = self.affine(cond_emb).unsqueeze(1)
86
+ x = x.mul(1. + scale)
87
+ return x
88
+
89
+ class TimestepEmbedder(nn.Module):
90
+ """
91
+ Embeds scalar timesteps into vector representations.
92
+ """
93
+ def __init__(self, hidden_size, frequency_embedding_size=256):
94
+ super().__init__()
95
+ self.mlp = nn.Sequential(
96
+ Linear(frequency_embedding_size, hidden_size, init_weight=1e-5),
97
+ nn.SiLU(),
98
+ Linear(hidden_size, hidden_size, init_weight=1e-5),
99
+ )
100
+ self.frequency_embedding_size = frequency_embedding_size
101
+
102
+ @staticmethod
103
+ def timestep_embedding(t, dim, max_period=10000):
104
+ """
105
+ Create sinusoidal timestep embeddings.
106
+ :param t: a 1-D Tensor of N indices, one per batch element.
107
+ These may be fractional.
108
+ :param dim: the dimension of the output.
109
+ :param max_period: controls the minimum frequency of the embeddings.
110
+ :return: an (N, D) Tensor of positional embeddings.
111
+ """
112
+ # https://github.com/openai/glide-text2im/blob/main/glide_text2im/nn.py
113
+ half = dim // 2
114
+ freqs = torch.exp(
115
+ -math.log(max_period) * torch.arange(start=0, end=half, dtype=torch.float32) / half
116
+ ).to(device=t.device)
117
+ args = t[:, None].float() * freqs[None]
118
+ embedding = torch.cat([torch.cos(args), torch.sin(args)], dim=-1)
119
+ if dim % 2:
120
+ embedding = torch.cat([embedding, torch.zeros_like(embedding[:, :1])], dim=-1)
121
+ return embedding
122
+
123
+ def forward(self, t):
124
+ t_freq = self.timestep_embedding(t, self.frequency_embedding_size)
125
+ t_emb = self.mlp(t_freq)
126
+ return t_emb
127
+
128
+ # ROPE
129
+ def exists(val):
130
+ return val is not None
131
+
132
+ def default(val, d):
133
+ return val if exists(val) else d
134
+
135
+ def broadcat(tensors, dim = -1):
136
+ broadcasted_tensors = broadcast_tensors(*tensors)
137
+ return torch.cat(broadcasted_tensors, dim = dim)
138
+
139
+ def slice_at_dim(t, dim_slice: slice, *, dim):
140
+ dim += (t.ndim if dim < 0 else 0)
141
+ colons = [slice(None)] * t.ndim
142
+ colons[dim] = dim_slice
143
+ return t[tuple(colons)]
144
+
145
+ def rotate_half(x):
146
+ x = rearrange(x, '... (d r) -> ... d r', r = 2)
147
+ x1, x2 = x.unbind(dim = -1)
148
+ x = torch.stack((-x2, x1), dim = -1)
149
+ return rearrange(x, '... d r -> ... (d r)')
150
+
151
+ def apply_rotary_emb(
152
+ freqs,
153
+ t,
154
+ start_index = 0,
155
+ scale = 1.,
156
+ seq_dim = -2,
157
+ freqs_seq_dim = None
158
+ ):
159
+ dtype = t.dtype
160
+ with torch.amp.autocast('cuda',enabled=False):
161
+ freqs = freqs.float()
162
+ t = t.float()
163
+ if not exists(freqs_seq_dim):
164
+ if freqs.ndim == 2 or t.ndim == 3:
165
+ freqs_seq_dim = 0
166
+
167
+ if t.ndim == 3 or exists(freqs_seq_dim):
168
+ seq_len = t.shape[seq_dim]
169
+ freqs = slice_at_dim(freqs, slice(-seq_len, None), dim = freqs_seq_dim)
170
+
171
+ rot_dim = freqs.shape[-1]
172
+ end_index = start_index + rot_dim
173
+
174
+ assert rot_dim <= t.shape[-1], f'feature dimension {t.shape[-1]} is not of sufficient size to rotate in all the positions {rot_dim}'
175
+
176
+ # Split t into three parts: left, middle (to be transformed), and right
177
+ t_left = t[..., :start_index]
178
+ t_middle = t[..., start_index:end_index]
179
+ t_right = t[..., end_index:]
180
+
181
+ # Apply rotary embeddings without modifying t in place
182
+ t_transformed = (t_middle * freqs.cos() * scale) + (rotate_half(t_middle) * freqs.sin() * scale)
183
+
184
+ out = torch.cat((t_left, t_transformed, t_right), dim=-1)
185
+
186
+ return out.type(dtype)
187
+
188
+ def apply_learned_rotations(rotations, t, start_index = 0, freq_ranges = None):
189
+ if exists(freq_ranges):
190
+ rotations = einsum('..., f -> ... f', rotations, freq_ranges)
191
+ rotations = rearrange(rotations, '... r f -> ... (r f)')
192
+
193
+ rotations = repeat(rotations, '... n -> ... (n r)', r = 2)
194
+ return apply_rotary_emb(rotations, t, start_index = start_index)
195
+
196
+ class RotaryEmbedding(Module):
197
+ def __init__(
198
+ self,
199
+ dim,
200
+ custom_freqs= None,
201
+ freqs_for= 'lang',
202
+ theta = 10000,
203
+ max_freq = 10,
204
+ num_freqs = 1,
205
+ learned_freq = False,
206
+ use_xpos = True,
207
+ xpos_scale_base = 512,
208
+ interpolate_factor = 1.,
209
+ theta_rescale_factor = 1.,
210
+ seq_before_head_dim = False,
211
+ cache_if_possible = True,
212
+ cache_max_seq_len = 8192
213
+ ):
214
+ super().__init__()
215
+ theta *= theta_rescale_factor ** (dim / (dim - 2))
216
+
217
+ self.freqs_for = freqs_for
218
+
219
+ if exists(custom_freqs):
220
+ freqs = custom_freqs
221
+ elif freqs_for == 'lang':
222
+ freqs = 1. / (theta ** (torch.arange(0, dim, 2)[:(dim // 2)].float() / dim))
223
+ elif freqs_for == 'pixel':
224
+ freqs = torch.linspace(1., max_freq / 2, dim // 2) * pi
225
+ elif freqs_for == 'constant':
226
+ freqs = torch.ones(num_freqs).float()
227
+
228
+ self.cache_if_possible = cache_if_possible
229
+ self.cache_max_seq_len = cache_max_seq_len
230
+
231
+ self.register_buffer('cached_freqs', torch.zeros(cache_max_seq_len, dim), persistent = False)
232
+ self.cached_freqs_seq_len = 0
233
+
234
+ self.freqs = nn.Parameter(freqs, requires_grad = learned_freq)
235
+
236
+ self.learned_freq = learned_freq
237
+
238
+
239
+ # dummy for device
240
+ self.register_buffer('dummy', torch.tensor(0), persistent = False)
241
+
242
+ # default sequence dimension
243
+ self.seq_before_head_dim = seq_before_head_dim
244
+ self.default_seq_dim = -3 if seq_before_head_dim else -2
245
+
246
+ # interpolation factors
247
+ assert interpolate_factor >= 1.
248
+ self.interpolate_factor = interpolate_factor
249
+
250
+ # xpos
251
+ self.use_xpos = use_xpos
252
+
253
+ if not use_xpos:
254
+ return
255
+
256
+ scale = (torch.arange(0, dim, 2) + 0.4 * dim) / (1.4 * dim)
257
+ self.scale_base = xpos_scale_base
258
+
259
+ self.register_buffer('scale', scale, persistent = False)
260
+ self.register_buffer('cached_scales', torch.zeros(cache_max_seq_len, dim), persistent = False)
261
+ self.cached_scales_seq_len = 0
262
+
263
+
264
+
265
+ @property
266
+ def device(self):
267
+ return self.dummy.device
268
+
269
+ def get_seq_pos(self, seq_len, device, dtype, offset = 0):
270
+ return (torch.arange(seq_len, device = device, dtype = dtype) + offset) / self.interpolate_factor
271
+
272
+ def rotate_queries_or_keys(self, t, seq_dim = None, offset = 0, scale = None):
273
+ seq_dim = default(seq_dim, self.default_seq_dim)
274
+
275
+ assert not self.use_xpos or exists(scale), 'you must use `.rotate_queries_and_keys` method instead and pass in both queries and keys, for length extrapolatable rotary embeddings'
276
+
277
+ device, dtype, seq_len = t.device, t.dtype, t.shape[seq_dim]
278
+
279
+ seq = self.get_seq_pos(seq_len, device = device, dtype = dtype, offset = offset)
280
+
281
+ freqs = self.forward(seq, seq_len = seq_len, offset = offset)
282
+
283
+ if seq_dim == -3:
284
+ freqs = rearrange(freqs, 'n d -> n 1 d')
285
+
286
+ return self.apply_rotary_emb(freqs, t, scale = default(scale, 1.), seq_dim = seq_dim)
287
+
288
+ def rotate_queries_with_cached_keys(self, q, k, seq_dim = None, offset = 0):
289
+ dtype, device, seq_dim = q.dtype, q.device, default(seq_dim, self.default_seq_dim)
290
+
291
+ q_len, k_len = q.shape[seq_dim], k.shape[seq_dim]
292
+ assert q_len <= k_len
293
+
294
+ q_scale = k_scale = 1.
295
+
296
+ if self.use_xpos:
297
+ seq = self.get_seq_pos(k_len, dtype = dtype, device = device)
298
+
299
+ q_scale = self.get_scale(seq[-q_len:]).type(dtype)
300
+ k_scale = self.get_scale(seq).type(dtype)
301
+
302
+ rotated_q = self.rotate_queries_or_keys(q, seq_dim = seq_dim, scale = q_scale, offset = k_len - q_len + offset)
303
+ rotated_k = self.rotate_queries_or_keys(k, seq_dim = seq_dim, scale = k_scale ** -1)
304
+
305
+ rotated_q = rotated_q.type(q.dtype)
306
+ rotated_k = rotated_k.type(k.dtype)
307
+
308
+ return rotated_q, rotated_k
309
+
310
+ def rotate_queries_and_keys(self, q, k, seq_dim = None, offset = 0):
311
+ seq_dim = default(seq_dim, self.default_seq_dim)
312
+
313
+ device, dtype, seq_len = q.device, q.dtype, q.shape[seq_dim]
314
+
315
+ seq = self.get_seq_pos(seq_len, dtype = dtype, device = device, offset = offset)
316
+
317
+ freqs = self.forward(seq, seq_len = seq_len, offset = offset)
318
+ scale = self.get_scale(seq, seq_len = seq_len, offset = offset).to(dtype)
319
+
320
+ if seq_dim == -3:
321
+ freqs = rearrange(freqs, 'n d -> n 1 d')
322
+ scale = rearrange(scale, 'n d -> n 1 d')
323
+
324
+ rotated_q = apply_rotary_emb(freqs, q, scale = scale, seq_dim = seq_dim)
325
+ rotated_k = apply_rotary_emb(freqs, k, scale = scale ** -1, seq_dim = seq_dim)
326
+
327
+ rotated_q = rotated_q.type(q.dtype)
328
+ rotated_k = rotated_k.type(k.dtype)
329
+
330
+ return rotated_q, rotated_k
331
+
332
+ def get_scale(
333
+ self,
334
+ t: Tensor,
335
+ seq_len= None,
336
+ offset = 0
337
+ ):
338
+
339
+ should_cache = (
340
+ self.cache_if_possible and
341
+ exists(seq_len) and
342
+ (offset + seq_len) <= self.cache_max_seq_len
343
+ )
344
+
345
+ if (
346
+ should_cache and \
347
+ exists(self.cached_scales) and \
348
+ (seq_len + offset) <= self.cached_scales_seq_len
349
+ ):
350
+ return self.cached_scales[offset:(offset + seq_len)]
351
+
352
+ scale = 1.
353
+ if self.use_xpos:
354
+ power = (t - len(t) // 2) / self.scale_base
355
+ scale = self.scale ** rearrange(power, 'n -> n 1')
356
+ scale = repeat(scale, 'n d -> n (d r)', r = 2)
357
+
358
+ if should_cache and offset == 0:
359
+ self.cached_scales[:seq_len] = scale.detach()
360
+ self.cached_scales_seq_len = seq_len
361
+
362
+ return scale
363
+
364
+ def get_axial_freqs(self, *dims):
365
+ Colon = slice(None)
366
+ all_freqs = []
367
+
368
+ for ind, dim in enumerate(dims):
369
+ if self.freqs_for == 'pixel':
370
+ pos = torch.linspace(-1, 1, steps = dim, device = self.device)
371
+ else:
372
+ pos = torch.arange(dim, device = self.device)
373
+
374
+ freqs = self.forward(pos, seq_len = dim)
375
+
376
+ all_axis = [None] * len(dims)
377
+ all_axis[ind] = Colon
378
+
379
+ new_axis_slice = (Ellipsis, *all_axis, Colon)
380
+ all_freqs.append(freqs[new_axis_slice])
381
+
382
+ all_freqs = broadcast_tensors(*all_freqs)
383
+ return torch.cat(all_freqs, dim = -1)
384
+
385
+ def forward(
386
+ self,
387
+ t,
388
+ seq_len = None,
389
+ offset = 0
390
+ ):
391
+ dtype = t.dtype
392
+ with torch.amp.autocast('cuda',enabled=False):
393
+ t = t.float()
394
+ freqs = self.freqs.float()
395
+
396
+ should_cache = (
397
+ self.cache_if_possible and
398
+ not self.learned_freq and
399
+ exists(seq_len) and
400
+ self.freqs_for != 'pixel' and
401
+ (offset + seq_len) <= self.cache_max_seq_len
402
+ )
403
+
404
+ if (
405
+ should_cache and \
406
+ exists(self.cached_freqs) and \
407
+ (offset + seq_len) <= self.cached_freqs_seq_len
408
+ ):
409
+ return self.cached_freqs[offset:(offset + seq_len)].detach()
410
+
411
+
412
+ freqs = einsum('..., f -> ... f', t, freqs)
413
+ freqs = repeat(freqs, '... n -> ... (n r)', r = 2)
414
+
415
+ if should_cache and offset == 0:
416
+ self.cached_freqs[:seq_len] = freqs.detach()
417
+ self.cached_freqs_seq_len = seq_len
418
+
419
+ return freqs.type(dtype)
420
+
421
+ # DropPath & FFN
422
+ def drop_path(x, drop_prob: float = 0., training: bool = False, scale_by_keep: bool = True): # taken from timm
423
+ if drop_prob == 0. or not training: return x
424
+ keep_prob = 1 - drop_prob
425
+ shape = (x.shape[0],) + (1,) * (x.ndim - 1) # work with diff dim tensors, not just 2D ConvNets
426
+ random_tensor = x.new_empty(shape).bernoulli_(keep_prob)
427
+ if keep_prob > 0.0 and scale_by_keep:
428
+ random_tensor.div_(keep_prob)
429
+ return x * random_tensor
430
+
431
+ class DropPath(nn.Module): # taken from timm
432
+ def __init__(self, drop_prob: float = 0., scale_by_keep: bool = True):
433
+ super(DropPath, self).__init__()
434
+ self.drop_prob = drop_prob
435
+ self.scale_by_keep = scale_by_keep
436
+
437
+ def forward(self, x):
438
+ return drop_path(x, self.drop_prob, self.training, self.scale_by_keep)
439
+
440
+ def extra_repr(self):
441
+ return f'(drop_prob=...)'
442
+
443
+ class FFN(nn.Module):
444
+ def __init__(self, in_features, hidden_features=None, out_features=None, drop=0., ffn_type='geglu'):
445
+ super().__init__()
446
+ out_features = out_features or in_features
447
+ hidden_features = hidden_features or in_features
448
+ self.ffn_type = ffn_type
449
+ if self.ffn_type == 'geglu':
450
+ self.fc1 = Linear(in_features, 2*hidden_features)
451
+ self.act = nn.GELU(approximate='tanh')
452
+ self.fc2 = Linear(hidden_features, out_features)
453
+ elif self.ffn_type == 'ffn':
454
+ self.fc1 = Linear(in_features, hidden_features)
455
+ self.act = nn.GELU(approximate='tanh')
456
+ self.fc2 = Linear(hidden_features, out_features)
457
+ self.drop = nn.Dropout(drop, inplace=True) if drop > 0 else nn.Identity()
458
+
459
+ def forward(self, x):
460
+ if self.ffn_type == 'geglu':
461
+ gate, value = self.fc1(x).chunk(2, dim=-1)
462
+ return self.drop(self.fc2(self.act(value)*gate))
463
+ elif self.ffn_type == 'ffn':
464
+ return self.drop(self.fc2(self.act(self.fc1(x))))
465
+
466
+
467
+ class Attention(nn.Module):
468
+ def __init__(
469
+ self, block_idx, embed_dim=768, num_heads=12,
470
+ attn_drop=0., proj_drop=0., attn_l2_norm=False,
471
+ rope=False
472
+ ):
473
+ super().__init__()
474
+ assert embed_dim % num_heads == 0
475
+ self.block_idx = block_idx
476
+ self.num_heads = num_heads
477
+ self.head_dim = embed_dim // num_heads
478
+ self.attn_l2_norm = attn_l2_norm
479
+ self.rope = RotaryEmbedding(embed_dim//num_heads) if rope else None
480
+
481
+ if self.attn_l2_norm:
482
+ self.scale = 1
483
+ self.scale_mul_1H11 = nn.Parameter(torch.full(size=(1, self.num_heads, 1, 1), fill_value=4.0).log(), requires_grad=True)
484
+ self.max_scale_mul = torch.log(torch.tensor(100)).item()
485
+ else:
486
+ self.scale = 0.25 / math.sqrt(self.head_dim)
487
+
488
+ self.to_q = Linear(embed_dim, embed_dim, bias=False)
489
+ self.to_kv = Linear(embed_dim, embed_dim * 2, bias=False)
490
+
491
+ self.register_buffer('zero_k_bias', torch.zeros(embed_dim))
492
+
493
+ self.proj = Linear(embed_dim, embed_dim)
494
+ self.proj_drop = nn.Dropout(proj_drop, inplace=True) if proj_drop > 0 else nn.Identity()
495
+ self.attn_drop: float = attn_drop
496
+
497
+ def forward(self, x, context_emb=None, causal=False, attn_bias=None, cache_kv=False, past_kvs=None):
498
+ B, L, C = x.shape
499
+
500
+ q = self.to_q(x)
501
+ q = einops.rearrange(q, 'b l (h d) -> b l h d', h=self.num_heads)
502
+
503
+ kv = self.to_kv(x) if context_emb is None else self.to_kv(context_emb)
504
+ kv = einops.rearrange(kv, 'b l (h d) -> b l h d', h=self.num_heads)
505
+ k, v = kv.chunk(2, dim=-1)
506
+
507
+ if self.rope is not None:
508
+ past_len = 0
509
+ if past_kvs is not None:
510
+ past_len = past_kvs[0].shape[2]
511
+ q, k = self.rope.rotate_queries_and_keys(q, k, offset=past_len)
512
+
513
+ q = q.permute(0, 2, 1, 3) # [B, H, L, D]
514
+ k = k.permute(0, 2, 1, 3) # [B, H, L, D]
515
+ v = v.permute(0, 2, 1, 3) # [B, H, L, D]
516
+
517
+ if past_kvs is not None:
518
+ past_keys, past_values = past_kvs
519
+ k = torch.cat((past_keys, k), dim=2)
520
+ v = torch.cat((past_values, v), dim=2)
521
+
522
+ dropout_p = self.attn_drop if self.training else 0.0
523
+ oup = F.scaled_dot_product_attention(
524
+ query=q,
525
+ key=k,
526
+ value=v,
527
+ scale=self.scale,
528
+ is_causal=causal and past_kvs is None,
529
+ dropout_p=dropout_p,
530
+ attn_mask=attn_bias
531
+ ).transpose(1, 2).reshape(B, L, -1)
532
+
533
+ out = self.proj_drop(self.proj(oup))
534
+ if not cache_kv:
535
+ return out
536
+ else:
537
+ return out, (k,v)
538
+
539
+ class TransformerLayer(nn.Module):
540
+ def __init__(
541
+ self, block_idx, embed_dim, cond_dim,
542
+ num_heads, mlp_ratio=8/3, drop=0., attn_drop=0., drop_path=0., attn_l2_norm=False, cross_attn=False, rope=False,
543
+ ffn_type='geglu', norm_layer=AdaRMSNorm
544
+ ):
545
+ super(TransformerLayer, self).__init__()
546
+ self.block_idx = block_idx
547
+ self.drop_path = DropPath(drop_path) if drop_path > 0. else nn.Identity()
548
+
549
+ self.adaln_1 = norm_layer(embed_dim, cond_dim)
550
+ self.attn1 = Attention(block_idx=block_idx, embed_dim=embed_dim, num_heads=num_heads, attn_drop=attn_drop, proj_drop=drop, attn_l2_norm=attn_l2_norm, rope=rope)
551
+ self.gate1 = Linear(cond_dim, embed_dim, init_weight=1e-5)
552
+
553
+ if cross_attn:
554
+ self.has_cross_attn = True
555
+ self.adaln_2 = norm_layer(embed_dim, cond_dim)
556
+ self.adaln_ctx = norm_layer(embed_dim, cond_dim)
557
+
558
+ self.attn2 = Attention(block_idx=block_idx, embed_dim=embed_dim, num_heads=num_heads, attn_drop=attn_drop, proj_drop=drop, attn_l2_norm=attn_l2_norm, rope=rope)
559
+ self.gate2 = Linear(cond_dim, embed_dim, init_weight=1e-5)
560
+ else:
561
+ self.has_cross_attn = False
562
+
563
+ self.adaln_mlp = norm_layer(embed_dim, cond_dim)
564
+ mlp_ratio = eval(mlp_ratio) if isinstance(mlp_ratio, str) else mlp_ratio
565
+ self.ffn = FFN(in_features=embed_dim, hidden_features=round(embed_dim * mlp_ratio), drop=drop, ffn_type=ffn_type)
566
+ self.gate_mlp = Linear(cond_dim, embed_dim, init_weight=1e-5)
567
+
568
+ def forward(self, x,context_emb=None, cond_emb=None, causal=False, attn_mask=None, cache_kv=False, past_kvs=None):
569
+ # If we are in "concat" mode, `context_emb` is None and masking should apply to self-attention.
570
+ # If we are in "cross_attn" mode, masking should apply to cross-attention (see below).
571
+ self_attn_mask = attn_mask if (attn_mask is not None and context_emb is None) else None
572
+
573
+ attn_out1 = self.attn1(
574
+ self.adaln_1(x, cond_emb),
575
+ context_emb=None,
576
+ causal=causal,
577
+ attn_bias=self_attn_mask,
578
+ cache_kv=cache_kv,
579
+ past_kvs=past_kvs,
580
+ )
581
+ if cache_kv:
582
+ attn_out1, new_kvs = attn_out1[0], attn_out1[1]
583
+
584
+ x = x + self.gate1(cond_emb).unsqueeze(1) * self.drop_path(attn_out1)
585
+ if context_emb is not None and self.has_cross_attn:
586
+ x = x + self.gate2(cond_emb).unsqueeze(1) * self.drop_path(self.attn2(self.adaln_2(x, cond_emb), context_emb=self.adaln_ctx(context_emb,cond_emb), causal=False, attn_bias=attn_mask))
587
+ x = x + self.gate_mlp(cond_emb).unsqueeze(1) * self.drop_path(self.ffn(self.adaln_mlp(x, cond_emb)))
588
+
589
+ if cache_kv:
590
+ return x, new_kvs
591
+ else:
592
+ return x
593
+
594
+ class Transformer(nn.Module):
595
+ def __init__(
596
+ self,
597
+ layer_num,
598
+ input_dim, dim, output_dim, max_seq_len, heads,
599
+ cond_input_dim, cond_dim,
600
+ context_type='none', ctx_input_dim=None, ctx_max_seq_len=None,
601
+ emb_dropout=0., drop=0.,
602
+ rope=False, abs_pos=False,
603
+ l2_norm=False, causal=False,
604
+ zero_out=False,
605
+ query_type='learnable', # 'learnable', 'noise', 'none'
606
+ out_act=True,
607
+ ffn_type='geglu', # ffn or geglu
608
+ norm_layer='RMSNorm', # LayerNorm or RMSNorm
609
+ mlp_ratio=8/3, **params
610
+ ):
611
+ super(Transformer, self).__init__()
612
+ self.input_dim = input_dim
613
+ self.output_dim = output_dim
614
+
615
+
616
+
617
+ self.query_type = query_type
618
+
619
+ self.cond_input_dim = cond_input_dim
620
+ self.dim = dim
621
+ self.causal = causal
622
+ self.cross_attn = context_type=='cross_attn'
623
+ norm_layer = AdaRMSNorm if norm_layer=="RMSNorm" else AdaLN
624
+ self.layers = nn.ModuleList([
625
+ TransformerLayer(block_idx=i, embed_dim=dim,
626
+ cond_dim=cond_dim, num_heads=heads, mlp_ratio=mlp_ratio,
627
+ drop=drop, attn_drop=drop, attn_l2_norm=l2_norm, rope=rope, cross_attn=self.cross_attn,
628
+ ffn_type=ffn_type, norm_layer=norm_layer)
629
+ for i in range(layer_num)
630
+ ])
631
+
632
+ out_weight = 1e-5 if zero_out else 1
633
+ scale = np.sqrt(dim)
634
+ self.adaln_out = norm_layer(dim, cond_dim)
635
+
636
+ self.out_activate = nn.GELU(approximate='tanh') if out_act else None
637
+ self.out = Linear(dim, output_dim,init_weight=out_weight)
638
+
639
+ if self.query_type!='noise':
640
+ self.cond_proj = nn.Sequential(Linear(cond_input_dim, cond_dim),norm_layer(cond_dim, cond_based_affine=False), nn.GELU(approximate='tanh'))
641
+ else:
642
+ self.cond_proj = Linear(cond_input_dim, cond_dim)
643
+ self.cond_norm_act = nn.Sequential(norm_layer(cond_dim, cond_based_affine=False), nn.GELU(approximate='tanh'))
644
+ self.max_seq_len = max_seq_len
645
+ self.context_type = context_type # "concat, cross_attn, none"
646
+ if abs_pos:
647
+ if context_type == 'none':
648
+ self.abs_pos = nn.Embedding(max_seq_len, dim)
649
+ elif context_type == 'concat':
650
+ ctx_max_seq_len = default(ctx_max_seq_len, max_seq_len)
651
+ self.abs_pos = nn.Embedding(max_seq_len + ctx_max_seq_len, dim)
652
+ elif context_type == 'cross_attn':
653
+ ctx_max_seq_len = default(ctx_max_seq_len, max_seq_len)
654
+ self.abs_pos = nn.Embedding(max_seq_len, dim)
655
+ self.ctx_pos = nn.Embedding(ctx_max_seq_len, dim)
656
+ else:
657
+ raise ValueError(f"Invalid context_type: {context_type}")
658
+ else:
659
+ self.abs_pos = None
660
+
661
+ if context_type == 'concat':
662
+ self.input_layer = Linear(input_dim, dim)
663
+ self.learnable_query_param = nn.Parameter(torch.zeros(1, max_seq_len, input_dim).normal_(0, 1)) if self.query_type == 'learnable' else None
664
+ ctx_input_dim = default(ctx_input_dim, input_dim)
665
+ self.ctx_input_dim = ctx_input_dim
666
+ self.ctx_input_layer = Linear(ctx_input_dim, dim)
667
+ elif context_type == 'cross_attn':
668
+ self.input_layer = Linear(input_dim, dim)
669
+ ctx_input_dim = default(ctx_input_dim, input_dim)
670
+ self.ctx_input_dim = ctx_input_dim
671
+ self.learnable_query_param = nn.Parameter(torch.zeros(1, max_seq_len, input_dim).normal_(0, 1)) if self.query_type == 'learnable' else None
672
+ self.ctx_input_layer = Linear(ctx_input_dim, dim)
673
+ else:
674
+ self.input_layer = Linear(input_dim, dim)
675
+
676
+ if self.query_type == 'noise':
677
+ self.t_embedder = TimestepEmbedder(cond_input_dim)
678
+ self.t_proj = Linear(cond_input_dim,cond_dim)
679
+
680
+ def forward(self, x, condition, attn_mask=None, cache_kv=False, past_kvs=None, query_tokens=None, t=None):
681
+
682
+ # Inject time embedding if using noise query
683
+ if self.query_type == 'noise':
684
+ cond_emb = self.cond_proj(condition)
685
+ if t is not None:
686
+ t_emb = self.t_embedder(t)
687
+ t_emb = self.t_proj(t_emb)
688
+ cond_emb = cond_emb + t_emb
689
+ cond_emb = self.cond_norm_act(cond_emb)
690
+ else:
691
+ cond_emb = self.cond_proj(condition)
692
+ if self.context_type == 'none':
693
+ x, context_emb = self.input_layer(x.contiguous()), None
694
+ elif self.context_type == 'concat':
695
+ B = x.size(0)
696
+ if query_tokens is None:
697
+ if self.query_type == 'learnable':
698
+ query_tokens = self.learnable_query_param.expand(B, -1, -1)
699
+ else:
700
+ # Should be provided for noise query, or error if not available
701
+ raise ValueError(f"query_tokens is required when query_type is '{self.query_type}'.")
702
+
703
+ query_tokens = self.input_layer(query_tokens.contiguous())
704
+ x = self.ctx_input_layer(x.contiguous())
705
+ x = torch.cat((query_tokens,x), dim=1)
706
+ x, context_emb = x, None
707
+ else:
708
+ # cross_attn: x is context (keys/values), query_tokens are queries
709
+ B = x.size(0)
710
+ if query_tokens is None:
711
+ if self.query_type == 'learnable':
712
+ query_tokens = self.learnable_query_param.expand(B, -1, -1)
713
+ else:
714
+ raise ValueError(f"query_tokens is required when query_type is '{self.query_type}'.")
715
+
716
+ x, context_emb = self.input_layer(query_tokens.contiguous()), self.ctx_input_layer(x.contiguous())
717
+
718
+ if self.abs_pos is not None:
719
+ if self.context_type == 'none':
720
+ pos_ids = torch.arange(x.size(1), device=x.device)
721
+ if past_kvs is not None:
722
+ pos_ids = pos_ids + past_kvs[0][0].shape[2]
723
+ x = x + self.abs_pos(pos_ids)
724
+ elif self.context_type == 'concat':
725
+ pos_ids = torch.arange(x.size(1), device=x.device)
726
+ x = x + self.abs_pos(pos_ids)
727
+ elif self.context_type == 'cross_attn':
728
+ pos_ids = torch.arange(x.size(1), device=x.device)
729
+ x = x + self.abs_pos(pos_ids)
730
+ if context_emb is not None and self.ctx_pos is not None:
731
+ context_emb = context_emb + self.ctx_pos(torch.arange(context_emb.size(1), device=context_emb.device))
732
+
733
+ kv_caches = []
734
+ for i,layer in enumerate(self.layers):
735
+ layer_output = layer(x, context_emb, cond_emb, self.causal, attn_mask, cache_kv, past_kvs[i] if past_kvs is not None else None)
736
+ if cache_kv:
737
+ x, new_kvs = layer_output[0], layer_output[1]
738
+ kv_caches.append(new_kvs)
739
+ else:
740
+ x = layer_output
741
+ if self.context_type == 'concat':
742
+ x = x[:, :self.max_seq_len]
743
+ out = self.adaln_out(x, cond_emb)
744
+ if self.out_activate is not None:
745
+ out = self.out_activate(out)
746
+ out = self.out(out)
747
+
748
+ if cache_kv:
749
+ return out, kv_caches
750
+ else:
751
+ return out
752
+
753
+
754
+ class Encoder(nn.Module):
755
+ def __init__(self, config) -> None:
756
+ super().__init__()
757
+ self.enc = Transformer(**config["Encoder"])
758
+ if config["vq_type"] == "HGQ":
759
+ self.quantizer = HGQ(**config["Quantizer"])
760
+ elif config["vq_type"] == "VQ":
761
+ self.quantizer = VQ(**config["Quantizer"])
762
+ else:
763
+ raise ValueError(f"Invalid quantizer type: {config['vq_type']}")
764
+ def forward(self, x, labels, training=True):
765
+ h = self.enc(x, labels)
766
+ quant, idx, one_hot, vqloss = self.quantizer(h, labels, training=training)
767
+ return quant, idx, one_hot, vqloss
768
+
769
+
770
+ class Decoder(nn.Module):
771
+ def __init__(self, config) -> None:
772
+ super().__init__()
773
+ self.dec = Transformer(**config["Decoder"])
774
+
775
+
776
+ def forward(self, quant, labels, attn_mask=None, query_tokens=None, t=None):
777
+ x_hat = self.dec(quant, labels, attn_mask=attn_mask, query_tokens=query_tokens, t=t)
778
+ return x_hat
779
+
780
+
781
+ class ARModel(nn.Module):
782
+ def __init__(self, config) -> None:
783
+ super().__init__()
784
+ self.bos = nn.Parameter(torch.zeros(1, 1, config["ARModel"]["input_dim"]).normal_(0,1))
785
+ self.semantic_emb = nn.Embedding(config["Quantizer"]["codebook_size"], config["ARModel"]["input_dim"])
786
+ self.ar_model = Transformer(**config["ARModel"])
787
+
788
+ self.temperature = config["ARModel"]["temperature"]
789
+ self.max_length = config["ARModel"]["max_seq_len"]
790
+
791
+
792
+ def forward(self,idx,labels, temperature=None):
793
+ bos = self.bos.expand(idx.shape[0], -1, -1)
794
+ shift_input = torch.cat([bos, self.semantic_emb(idx)[:,:-1]],dim=1)
795
+ logits = self.ar_model(shift_input, labels)
796
+
797
+ if temperature is not None:
798
+ logits = logits / temperature
799
+ elif self.temperature is not None:
800
+ logits = logits / self.temperature
801
+ return logits
802
+
803
+ @torch.no_grad()
804
+ @torch._dynamo.disable
805
+ def sampling(self, bz, class_label=None, temperature=1.0, topK=None, topP=None, cfg=16.0, cfg_schedule='cosine', cfg_power=2.75, cache_kv=False):
806
+ cfg = 0. if class_label is None else cfg
807
+ if cfg>0. :
808
+ num_classes = class_label.shape[1]
809
+ uncond_class_label = F.one_hot(torch.full((bz,), num_classes - 1, device=class_label.device, dtype=torch.long), num_classes=num_classes).float()
810
+ quant_input = self.bos.expand(bz, -1, -1)
811
+ quant_output = []
812
+ past_kvs, uncond_past_kvs = None, None
813
+ for step in range(self.max_length):
814
+ ar_out = self.ar_model(quant_input, class_label, cache_kv=cache_kv, past_kvs=past_kvs)
815
+ if cache_kv:
816
+ logits, past_kvs = ar_out
817
+ else:
818
+ logits = ar_out
819
+ logits = logits[:,-1]
820
+ if cfg>0.:
821
+ if cfg_schedule == 'constant':
822
+ cfg_scale = cfg
823
+ elif cfg_schedule == 'linear':
824
+ cfg_scale = 1.0 * (1-step/self.max_length) + cfg * (step/self.max_length)
825
+ elif cfg_schedule == 'cosine':
826
+ cfg_scale = (1 - math.cos(
827
+ ((step / self.max_length) ** cfg_power) * math.pi)) * 1/2
828
+ cfg_scale = (cfg - 1) * cfg_scale + 1
829
+ else:
830
+ raise ValueError(f"Invalid cfg_schedule: {cfg_schedule}")
831
+ uncond_ar_out = self.ar_model(quant_input, uncond_class_label, cache_kv=cache_kv, past_kvs=uncond_past_kvs)
832
+ if cache_kv:
833
+ uncond_logits, uncond_past_kvs = uncond_ar_out
834
+ else:
835
+ uncond_logits = uncond_ar_out
836
+ uncond_logits = uncond_logits[:,-1]
837
+ logits = cfg_scale * logits + (1 - cfg_scale) * uncond_logits
838
+ logits = logits / temperature
839
+
840
+ if topK is not None and topK>0.:
841
+ top_logits, top_indices = logits.topk(topK, dim=-1)
842
+ logits = torch.full_like(logits, float('-inf'))
843
+ logits.scatter_(dim=-1, index=top_indices, src=top_logits)
844
+ if topP is not None and 0.<topP<1.:
845
+ sorted_logits, sorted_indices = torch.sort(logits, dim=-1, descending=True)
846
+ probs_sum = sorted_logits.softmax(dim=-1).cumsum(dim=-1)
847
+ mask = probs_sum > topP
848
+ mask[..., 1:] = mask[...,:-1].clone()
849
+ mask[...,0] = False
850
+ sorted_logits[mask] = float('-inf')
851
+ logits = torch.full_like(logits, float('-inf'))
852
+ logits.scatter_(dim=-1, index=sorted_indices, src=sorted_logits)
853
+ next_idx = torch.multinomial(F.softmax(logits, dim=-1), 1)
854
+ quant_output.append(next_idx)
855
+ if not cache_kv:
856
+ quant_input = torch.cat((quant_input, self.semantic_emb(next_idx)), dim=1)
857
+ else:
858
+ quant_input = self.semantic_emb(next_idx)
859
+ quant_output = torch.cat(quant_output, dim=1)
860
+ return quant_output
861
+
862
+
863
+
864
+ # deterministic Hard-Gumbel Softmax Quantizer
865
+ class HGQ(nn.Module):
866
+ def __init__(self, codebook_size, dim, z_dim, cond_dim, cond_based_affine=False, norm_layer='RMSNorm',temperature=1.0, **params):
867
+ super().__init__()
868
+ self.codebook_size = codebook_size # codebook size (K)
869
+ self.dim = dim # embedding dimension (D)
870
+ self.z_dim = z_dim
871
+ # Initialize embedding table
872
+ self.proj_in = Linear(dim, z_dim)
873
+ self.codebook = nn.Embedding(codebook_size, z_dim)
874
+ self.codebook.weight.data.uniform_(-1e-4, 1e-4)
875
+ self.norm_layer = norm_layer
876
+ if norm_layer == 'RMSNorm':
877
+ self.z_norm = AdaRMSNorm(z_dim, cond_dim, elementwise_affine=True, cond_based_affine=cond_based_affine)
878
+ self.code_norm = AdaRMSNorm(z_dim, cond_dim, elementwise_affine=True, cond_based_affine=cond_based_affine)
879
+ elif norm_layer == 'LN':
880
+ self.z_norm = AdaLN(z_dim, cond_dim, elementwise_affine=True, cond_based_affine=cond_based_affine)
881
+ self.code_norm = AdaLN(z_dim, cond_dim, elementwise_affine=True, cond_based_affine=cond_based_affine)
882
+
883
+ self.out_proj = Linear(z_dim, dim, bias=False)
884
+ self.total_cnt = 0
885
+ self.book_cnt = torch.zeros(codebook_size)
886
+ self.temperature=temperature
887
+ self.float()
888
+
889
+ def forward(self, x, labels=None, training=True, log_usage=False, indices=None):
890
+ B, L, D = x.shape
891
+ x = x.float()
892
+ z = self.proj_in(x)
893
+
894
+ if self.norm_layer != 'none':
895
+ if self.norm_layer=='l2':
896
+ z= F.normalize(z, dim=-1)
897
+ codes= F.normalize(self.codebook.weight, dim=-1)
898
+ elif self.norm_layer=='RMSNorm':
899
+ z= self.z_norm(z, labels)
900
+ codes= self.code_norm(self.codebook.weight, labels)
901
+ elif self.norm_layer=='LN':
902
+ z= self.z_norm(z, labels)
903
+ codes= self.code_norm(self.codebook.weight, labels)
904
+ else:
905
+ raise ValueError(f"Invalid norm layer: {self.norm_layer}")
906
+
907
+ logits = torch.einsum('bld,nd->bln', z, codes)
908
+ prob = F.softmax(logits/self.temperature, dim=-1)
909
+
910
+ indices = torch.argmax(prob, dim=-1) if indices is None else indices
911
+ one_hot_ng = F.one_hot(indices, prob.shape[-1]).view(B, L, -1).to(z.device).to(z.dtype)
912
+ one_hot = prob + (one_hot_ng - prob).detach()
913
+
914
+ quant = torch.einsum('bln,nd->bld', one_hot, codes)
915
+ loss = 0.
916
+ quant_out = self.out_proj(quant)
917
+
918
+ return quant_out, indices, one_hot, loss
919
+
920
+ def get_codes_w_onehot(self, one_hot, labels=None, **params):
921
+ codes = self.codebook.weight
922
+ quant = torch.einsum('bln,nd->bld', one_hot, codes)
923
+
924
+ if self.norm_layer != 'none':
925
+ if self.norm_layer=='l2':
926
+ quant= F.normalize(quant, dim=-1)
927
+ elif self.norm_layer=='RMSNorm':
928
+ quant= self.code_norm(quant, labels)
929
+ elif self.norm_layer=='LN':
930
+ quant= self.code_norm(quant, labels)
931
+ else:
932
+ raise ValueError(f"Invalid norm layer: {self.norm_layer}")
933
+ return self.out_proj(quant)
934
+ def get_codes_w_indices(self, indices, labels=None, **params):
935
+ codes = self.codebook(indices)
936
+ if self.norm_layer != 'none':
937
+ if self.norm_layer=='l2':
938
+ codes= F.normalize(codes, dim=-1)
939
+ elif self.norm_layer=='RMSNorm':
940
+ codes= self.code_norm(codes, labels)
941
+ elif self.norm_layer=='LN':
942
+ codes= self.code_norm(codes, labels)
943
+ else:
944
+ raise ValueError(f"Invalid norm layer: {self.norm_layer}")
945
+ return self.out_proj(codes)
946
+
947
+
948
+ class VQ(torch.nn.Module):
949
+ def __init__(self,
950
+ codebook_size: int = 1024,
951
+ dim: int = 256,
952
+ z_dim: int = 256,
953
+ beta: float = 0.25,
954
+ use_norm: bool = False,
955
+ temperature=1.0, **params
956
+ ):
957
+ super().__init__()
958
+ self.codebook_size = codebook_size
959
+ self.dim = dim
960
+ self.z_dim = z_dim
961
+ self.beta = beta
962
+ self.total_cnt = 0
963
+ self.book_cnt = torch.zeros(codebook_size)
964
+ self.proj_in = Linear(dim, z_dim)
965
+ self.embedding = torch.nn.Embedding(codebook_size, z_dim)
966
+ self.embedding.weight.data.uniform_(-1.0 / codebook_size, 1.0 / codebook_size)
967
+ self.use_norm = use_norm
968
+ self.proj_out = Linear(z_dim, dim)
969
+ self.register_buffer('ema_vocab_hit_SV', torch.full((1, self.codebook_size), fill_value=0.0))
970
+ self.record_hit = 0
971
+ self.temperature=temperature
972
+ self.float()
973
+
974
+
975
+ def forward(self, x: torch.Tensor, labels=None, training=True):
976
+ x=x.float()
977
+ z = self.proj_in(x)
978
+
979
+ if self.use_norm:
980
+ z = F.normalize(z, dim=-1)
981
+ embedding = F.normalize(self.embedding.weight, dim=-1)
982
+ dist = -1 * torch.einsum('bld,nd->bln', z, embedding)
983
+ else:
984
+ embedding = self.embedding.weight
985
+
986
+ dist = torch.sum(z**2, dim=-1, keepdim=True) + \
987
+ torch.sum(embedding**2, dim=1) - 2 * \
988
+ torch.einsum('bld,nd->bln', z, embedding)
989
+
990
+ indices = torch.argmin(dist, dim=-1) # num_ele
991
+ ste_prob = (-dist/self.temperature).softmax(dim=-1)
992
+ one_hot_ng = F.one_hot(indices, dist.shape[-1]).view(x.shape[0], x.shape[1], -1).to(z.device).to(z.dtype)
993
+ one_hot = ste_prob + (one_hot_ng - ste_prob).detach()
994
+
995
+ quant = embedding[indices]
996
+
997
+ loss = self.beta * torch.mean((quant.detach() - z) **2) + torch.mean((quant - z.detach()) **2) if training else 0.
998
+
999
+
1000
+ # preserve gradients
1001
+ quant = z + (quant - z).detach()
1002
+ quant = self.proj_out(quant)
1003
+
1004
+ return quant, indices, one_hot, loss
1005
+ def get_codes_w_onehot(self, one_hot, labels=None, **params):
1006
+ codes = self.embedding.weight
1007
+ quant = torch.einsum('bln,nd->bld', one_hot, codes)
1008
+ quant = F.normalize(quant, dim=-1) if self.use_norm else quant
1009
+ return self.proj_out(quant)
1010
+ def get_codes_w_indices(self, indices):
1011
+ quant = self.embedding(indices)
1012
+ quant = F.normalize(quant, dim=-1) if self.use_norm else quant
1013
+ return self.proj_out(quant)
refs/cifar10-32x32_local.npz ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:5493968c4f29b2878f64d6a640351f377e32e86553db56eca8147a0ae50d93f2
3
+ size 63557107
requirements.txt ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ torch==2.8.0
2
+ torchvision==0.23.0
3
+ lightning==2.5.5
4
+ omegaconf==2.3.0
5
+ einops==0.8.1
6
+ numpy==2.1.3
7
+ scipy==1.16.3
8
+ Pillow==11.1.0
9
+ PyYAML==6.0.3
10
+ wandb==0.23.0
11
+ tqdm==4.67.1
12
+ requests==2.32.5
13
+ click==8.3.0
14
+ setuptools==80.9.0
15
+ tensorflow==2.19.1
scripts/dataset_tools.py ADDED
@@ -0,0 +1,618 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ #
3
+ # This work is licensed under a Creative Commons
4
+ # Attribution-NonCommercial-ShareAlike 4.0 International License.
5
+ # You should have received a copy of the license along with this
6
+ # work. If not, see http://creativecommons.org/licenses/by-nc-sa/4.0/
7
+
8
+ """Tool for creating ZIP/PNG based datasets."""
9
+
10
+ import functools
11
+ import gzip
12
+ import io
13
+ import json
14
+ import os
15
+ import pickle
16
+ import re
17
+ import sys
18
+ import tarfile
19
+ import zipfile
20
+ from pathlib import Path
21
+ from typing import Callable, Optional, Tuple, Union
22
+ import click
23
+ import numpy as np
24
+ import PIL.Image
25
+ from tqdm import tqdm
26
+
27
+ #----------------------------------------------------------------------------
28
+ # Parse a 'M,N' or 'MxN' integer tuple.
29
+ # Example: '4x2' returns (4,2)
30
+
31
+ def parse_tuple(s: str) -> Tuple[int, int]:
32
+ m = re.match(r'^(\d+)[x,](\d+)$', s)
33
+ if m:
34
+ return int(m.group(1)), int(m.group(2))
35
+ raise click.ClickException(f'cannot parse tuple {s}')
36
+
37
+ #----------------------------------------------------------------------------
38
+
39
+ def maybe_min(a: int, b: Optional[int]) -> int:
40
+ if b is not None:
41
+ return min(a, b)
42
+ return a
43
+
44
+ #----------------------------------------------------------------------------
45
+
46
+ def file_ext(name: Union[str, Path]) -> str:
47
+ return str(name).split('.')[-1]
48
+
49
+ #----------------------------------------------------------------------------
50
+
51
+ def is_image_ext(fname: Union[str, Path]) -> bool:
52
+ ext = file_ext(fname).lower()
53
+ return f'.{ext}' in PIL.Image.EXTENSION
54
+
55
+ #----------------------------------------------------------------------------
56
+
57
+ def open_image_folder(source_dir, *, max_images: Optional[int]):
58
+ input_images = [str(f) for f in sorted(Path(source_dir).rglob('*')) if is_image_ext(f) and os.path.isfile(f)]
59
+ arch_fnames = {fname: os.path.relpath(fname, source_dir).replace('\\', '/') for fname in input_images}
60
+ max_idx = maybe_min(len(input_images), max_images)
61
+
62
+ # Load labels.
63
+ labels = dict()
64
+ meta_fname = os.path.join(source_dir, 'dataset.json')
65
+ if os.path.isfile(meta_fname):
66
+ with open(meta_fname, 'r') as file:
67
+ data = json.load(file)['labels']
68
+ if data is not None:
69
+ labels = {x[0]: x[1] for x in data}
70
+
71
+ # No labels available => determine from top-level directory names.
72
+ if len(labels) == 0:
73
+ toplevel_names = {arch_fname: arch_fname.split('/')[0] if '/' in arch_fname else '' for arch_fname in arch_fnames.values()}
74
+ toplevel_indices = {toplevel_name: idx for idx, toplevel_name in enumerate(sorted(set(toplevel_names.values())))}
75
+ if len(toplevel_indices) > 1:
76
+ labels = {arch_fname: toplevel_indices[toplevel_name] for arch_fname, toplevel_name in toplevel_names.items()}
77
+
78
+ def iterate_images():
79
+ for idx, fname in enumerate(input_images):
80
+ img = np.array(PIL.Image.open(fname))
81
+ yield dict(img=img, label=labels.get(arch_fnames.get(fname)))
82
+ if idx >= max_idx - 1:
83
+ break
84
+ return max_idx, iterate_images()
85
+
86
+ #----------------------------------------------------------------------------
87
+
88
+ def open_image_zip(source, *, max_images: Optional[int]):
89
+ with zipfile.ZipFile(source, mode='r') as z:
90
+ input_images = [str(f) for f in sorted(z.namelist()) if is_image_ext(f)]
91
+ max_idx = maybe_min(len(input_images), max_images)
92
+
93
+ # Load labels.
94
+ labels = dict()
95
+ if 'dataset.json' in z.namelist():
96
+ with z.open('dataset.json', 'r') as file:
97
+ data = json.load(file)['labels']
98
+ if data is not None:
99
+ labels = {x[0]: x[1] for x in data}
100
+
101
+ def iterate_images():
102
+ with zipfile.ZipFile(source, mode='r') as z:
103
+ for idx, fname in enumerate(input_images):
104
+ with z.open(fname, 'r') as file:
105
+ img = np.array(PIL.Image.open(file))
106
+ yield dict(img=img, label=labels.get(fname))
107
+ if idx >= max_idx - 1:
108
+ break
109
+ return max_idx, iterate_images()
110
+
111
+ #----------------------------------------------------------------------------
112
+
113
+ def open_lmdb(lmdb_dir: str, *, max_images: Optional[int]):
114
+ import cv2 # pyright: ignore [reportMissingImports] # pip install opencv-python
115
+ import lmdb # pyright: ignore [reportMissingImports] # pip install lmdb
116
+
117
+ with lmdb.open(lmdb_dir, readonly=True, lock=False).begin(write=False) as txn:
118
+ max_idx = maybe_min(txn.stat()['entries'], max_images)
119
+
120
+ def iterate_images():
121
+ with lmdb.open(lmdb_dir, readonly=True, lock=False).begin(write=False) as txn:
122
+ for idx, (_key, value) in enumerate(txn.cursor()):
123
+ try:
124
+ try:
125
+ img = cv2.imdecode(np.frombuffer(value, dtype=np.uint8), 1)
126
+ if img is None:
127
+ raise IOError('cv2.imdecode failed')
128
+ img = img[:, :, ::-1] # BGR => RGB
129
+ except IOError:
130
+ img = np.array(PIL.Image.open(io.BytesIO(value)))
131
+ yield dict(img=img, label=None)
132
+ if idx >= max_idx - 1:
133
+ break
134
+ except:
135
+ print(sys.exc_info()[1])
136
+
137
+ return max_idx, iterate_images()
138
+
139
+ #----------------------------------------------------------------------------
140
+
141
+ def open_cifar10(tarball: str, *, max_images: Optional[int]):
142
+ images = []
143
+ labels = []
144
+
145
+ with tarfile.open(tarball, 'r:gz') as tar:
146
+ for batch in range(1, 6):
147
+ member = tar.getmember(f'cifar-10-batches-py/data_batch_{batch}')
148
+ with tar.extractfile(member) as file:
149
+ data = pickle.load(file, encoding='latin1')
150
+ images.append(data['data'].reshape(-1, 3, 32, 32))
151
+ labels.append(data['labels'])
152
+
153
+ images = np.concatenate(images)
154
+ labels = np.concatenate(labels)
155
+ images = images.transpose([0, 2, 3, 1]) # NCHW -> NHWC
156
+ assert images.shape == (50000, 32, 32, 3) and images.dtype == np.uint8
157
+ assert labels.shape == (50000,) and labels.dtype in [np.int32, np.int64]
158
+ assert np.min(images) == 0 and np.max(images) == 255
159
+ assert np.min(labels) == 0 and np.max(labels) == 9
160
+
161
+ max_idx = maybe_min(len(images), max_images)
162
+
163
+ def iterate_images():
164
+ for idx, img in enumerate(images):
165
+ yield dict(img=img, label=int(labels[idx]))
166
+ if idx >= max_idx - 1:
167
+ break
168
+
169
+ return max_idx, iterate_images()
170
+
171
+ #----------------------------------------------------------------------------
172
+
173
+ def open_mnist(images_gz: str, *, max_images: Optional[int]):
174
+ labels_gz = images_gz.replace('-images-idx3-ubyte.gz', '-labels-idx1-ubyte.gz')
175
+ assert labels_gz != images_gz
176
+ images = []
177
+ labels = []
178
+
179
+ with gzip.open(images_gz, 'rb') as f:
180
+ images = np.frombuffer(f.read(), np.uint8, offset=16)
181
+ with gzip.open(labels_gz, 'rb') as f:
182
+ labels = np.frombuffer(f.read(), np.uint8, offset=8)
183
+
184
+ images = images.reshape(-1, 28, 28)
185
+ images = np.pad(images, [(0,0), (2,2), (2,2)], 'constant', constant_values=0)
186
+ assert images.shape == (60000, 32, 32) and images.dtype == np.uint8
187
+ assert labels.shape == (60000,) and labels.dtype == np.uint8
188
+ assert np.min(images) == 0 and np.max(images) == 255
189
+ assert np.min(labels) == 0 and np.max(labels) == 9
190
+
191
+ max_idx = maybe_min(len(images), max_images)
192
+
193
+ def iterate_images():
194
+ for idx, img in enumerate(images):
195
+ yield dict(img=img, label=int(labels[idx]))
196
+ if idx >= max_idx - 1:
197
+ break
198
+
199
+ return max_idx, iterate_images()
200
+
201
+ #----------------------------------------------------------------------------
202
+
203
+ def make_transform(
204
+ transform: Optional[str],
205
+ output_width: Optional[int],
206
+ output_height: Optional[int]
207
+ ) -> Callable[[np.ndarray], Optional[np.ndarray]]:
208
+ def scale(width, height, img):
209
+ w = img.shape[1]
210
+ h = img.shape[0]
211
+ if width == w and height == h:
212
+ return img
213
+ img = PIL.Image.fromarray(img)
214
+ ww = width if width is not None else w
215
+ hh = height if height is not None else h
216
+ img = img.resize((ww, hh), PIL.Image.Resampling.LANCZOS)
217
+ return np.array(img)
218
+
219
+ def center_crop(width, height, img):
220
+ crop = np.min(img.shape[:2])
221
+ img = img[(img.shape[0] - crop) // 2 : (img.shape[0] + crop) // 2, (img.shape[1] - crop) // 2 : (img.shape[1] + crop) // 2]
222
+ if img.ndim == 2:
223
+ img = img[:, :, np.newaxis].repeat(3, axis=2)
224
+ img = PIL.Image.fromarray(img, 'RGB')
225
+ img = img.resize((width, height), PIL.Image.Resampling.LANCZOS)
226
+ return np.array(img)
227
+
228
+ def center_crop_wide(width, height, img):
229
+ ch = int(np.round(width * img.shape[0] / img.shape[1]))
230
+ if img.shape[1] < width or ch < height:
231
+ return None
232
+
233
+ img = img[(img.shape[0] - ch) // 2 : (img.shape[0] + ch) // 2]
234
+ if img.ndim == 2:
235
+ img = img[:, :, np.newaxis].repeat(3, axis=2)
236
+ img = PIL.Image.fromarray(img, 'RGB')
237
+ img = img.resize((width, height), PIL.Image.Resampling.LANCZOS)
238
+ img = np.array(img)
239
+
240
+ canvas = np.zeros([width, width, 3], dtype=np.uint8)
241
+ canvas[(width - height) // 2 : (width + height) // 2, :] = img
242
+ return canvas
243
+
244
+ if transform is None:
245
+ return functools.partial(scale, output_width, output_height)
246
+ if transform == 'center-crop':
247
+ if output_width is None or output_height is None:
248
+ raise click.ClickException('must specify --resolution=WxH when using ' + transform + 'transform')
249
+ return functools.partial(center_crop, output_width, output_height)
250
+ if transform == 'center-crop-wide':
251
+ if output_width is None or output_height is None:
252
+ raise click.ClickException('must specify --resolution=WxH when using ' + transform + ' transform')
253
+ return functools.partial(center_crop_wide, output_width, output_height)
254
+ assert False, 'unknown transform'
255
+
256
+ #----------------------------------------------------------------------------
257
+
258
+ def open_dataset(source, *, max_images: Optional[int]):
259
+ if os.path.isdir(source):
260
+ if source.rstrip('/').endswith('_lmdb'):
261
+ return open_lmdb(source, max_images=max_images)
262
+ else:
263
+ return open_image_folder(source, max_images=max_images)
264
+ elif os.path.isfile(source):
265
+ if os.path.basename(source) == 'cifar-10-python.tar.gz':
266
+ return open_cifar10(source, max_images=max_images)
267
+ elif os.path.basename(source) == 'train-images-idx3-ubyte.gz':
268
+ return open_mnist(source, max_images=max_images)
269
+ elif file_ext(source) == 'zip':
270
+ return open_image_zip(source, max_images=max_images)
271
+ else:
272
+ assert False, 'unknown archive type'
273
+ else:
274
+ raise click.ClickException(f'Missing input file or directory: {source}')
275
+
276
+ #----------------------------------------------------------------------------
277
+
278
+ def create_labels_only(source: str) -> None:
279
+ """Create a StyleGAN-style dataset.json with image->class labels in-place.
280
+
281
+ - For directories: writes dataset.json under the source root.
282
+ - For ZIP files: writes dataset.json inside the ZIP archive.
283
+ - Labels are inferred from an existing dataset.json if present, otherwise
284
+ from top-level directory names (ImageNet-style). If labels cannot be
285
+ determined for all images, labels is set to None.
286
+ """
287
+ def _maybe_load_imagenet_val_labels(rel_fnames):
288
+ """Try to infer ImageNet val labels from LOC_val_solution.csv + LOC_synset_mapping.txt.
289
+
290
+ rel_fnames are relative paths within `source` (for dirs) or within the zip.
291
+ This function returns a mapping {rel_fname: class_idx} or an empty dict if
292
+ the required metadata files are not found or parsing fails.
293
+ """
294
+ root = Path(__file__).resolve().parent
295
+ val_csv = root / 'LOC_val_solution.csv'
296
+ synset_map = root / 'LOC_synset_mapping.txt'
297
+ if not (val_csv.is_file() and synset_map.is_file()):
298
+ return {}
299
+
300
+ # Build synset -> class index mapping, consistent with train (sorted by synset).
301
+ synsets = []
302
+ with open(synset_map, 'r') as f:
303
+ for line in f:
304
+ line = line.strip()
305
+ if not line:
306
+ continue
307
+ syn = line.split()[0]
308
+ synsets.append(syn)
309
+ synsets = sorted(set(synsets))
310
+ class_to_idx = {syn: idx for idx, syn in enumerate(synsets)}
311
+
312
+ # Build image_id -> synset mapping from LOC_val_solution.csv
313
+ img_to_syn = {}
314
+ with open(val_csv, 'r') as f:
315
+ # Skip header
316
+ header = f.readline()
317
+ for line in f:
318
+ line = line.strip()
319
+ if not line:
320
+ continue
321
+ parts = line.split(',', 1)
322
+ if len(parts) != 2:
323
+ continue
324
+ image_id, pred_str = parts
325
+ pred_str = pred_str.strip()
326
+ if not pred_str:
327
+ continue
328
+ syn = pred_str.split()[0]
329
+ img_to_syn[image_id] = syn
330
+
331
+ labels = {}
332
+ for rel in rel_fnames:
333
+ base = os.path.basename(rel)
334
+ stem, _ext = os.path.splitext(base)
335
+ syn = img_to_syn.get(stem, None)
336
+ if syn is None:
337
+ continue
338
+ cls_idx = class_to_idx.get(syn, None)
339
+ if cls_idx is None:
340
+ continue
341
+ labels[rel] = cls_idx
342
+ return labels
343
+
344
+ # Directory source: infer labels based on folder structure or metadata.
345
+ if os.path.isdir(source):
346
+ input_images = [
347
+ str(f) for f in sorted(Path(source).rglob('*'))
348
+ if is_image_ext(f) and os.path.isfile(f)
349
+ ]
350
+ arch_fnames = {
351
+ fname: os.path.relpath(fname, source).replace('\\', '/')
352
+ for fname in input_images
353
+ }
354
+
355
+ labels = dict()
356
+ meta_fname = os.path.join(source, 'dataset.json')
357
+ if os.path.isfile(meta_fname):
358
+ with open(meta_fname, 'r') as file:
359
+ data = json.load(file).get('labels', None)
360
+ if data is not None:
361
+ labels = {x[0]: x[1] for x in data}
362
+
363
+ # No labels available => determine from top-level directory names (train-style),
364
+ # or fall back to ImageNet val metadata if available.
365
+ if len(labels) == 0:
366
+ toplevel_names = {
367
+ arch_fname: arch_fname.split('/')[0] if '/' in arch_fname else ''
368
+ for arch_fname in arch_fnames.values()
369
+ }
370
+ unique_toplevel = sorted(set(toplevel_names.values()))
371
+ if len(unique_toplevel) > 1:
372
+ toplevel_indices = {
373
+ toplevel_name: idx
374
+ for idx, toplevel_name in enumerate(unique_toplevel)
375
+ }
376
+ labels = {
377
+ arch_fname: toplevel_indices[toplevel_names[arch_fname]]
378
+ for arch_fname in arch_fnames.values()
379
+ }
380
+ else:
381
+ # Likely a flat ImageNet val-style directory; try metadata-based labels.
382
+ labels = _maybe_load_imagenet_val_labels(list(arch_fnames.values()))
383
+
384
+ labels_list = []
385
+ all_labeled = True
386
+ for arch_fname in arch_fnames.values():
387
+ lbl = labels.get(arch_fname, None)
388
+ if lbl is None:
389
+ all_labeled = False
390
+ labels_list.append([arch_fname, lbl])
391
+
392
+ metadata = {'labels': labels_list if all_labeled else None}
393
+ with open(meta_fname, 'w') as f:
394
+ json.dump(metadata, f)
395
+ return
396
+
397
+ # ZIP source: write dataset.json inside the archive.
398
+ if os.path.isfile(source) and file_ext(source) == 'zip':
399
+ with zipfile.ZipFile(source, mode='r') as z:
400
+ namelist = z.namelist()
401
+ if 'dataset.json' in namelist:
402
+ return
403
+ input_images = [f for f in sorted(namelist) if is_image_ext(f)]
404
+
405
+ labels = dict()
406
+ # Determine from top-level directory names inside zip, or fall back to
407
+ # ImageNet val metadata if available.
408
+ toplevel_names = {
409
+ fname: fname.split('/')[0] if '/' in fname else ''
410
+ for fname in input_images
411
+ }
412
+ unique_toplevel = sorted(set(toplevel_names.values()))
413
+ if len(unique_toplevel) > 1:
414
+ toplevel_indices = {
415
+ toplevel_name: idx
416
+ for idx, toplevel_name in enumerate(unique_toplevel)
417
+ }
418
+ labels = {
419
+ fname: toplevel_indices[toplevel_names[fname]]
420
+ for fname in input_images
421
+ }
422
+ else:
423
+ # Flat layout: try ImageNet val metadata (filenames like ILSVRC2012_val_*.JPEG).
424
+ labels = _maybe_load_imagenet_val_labels(input_images)
425
+
426
+ labels_list = []
427
+ all_labeled = True
428
+ for fname in input_images:
429
+ lbl = labels.get(fname, None)
430
+ if lbl is None:
431
+ all_labeled = False
432
+ labels_list.append([fname, lbl])
433
+
434
+ metadata = {'labels': labels_list if all_labeled else None}
435
+
436
+ # Append dataset.json to the zip.
437
+ with zipfile.ZipFile(source, mode='a') as z:
438
+ z.writestr('dataset.json', json.dumps(metadata))
439
+ return
440
+
441
+ raise click.ClickException(
442
+ f'labels-only currently supports directory or zip sources, got: {source}'
443
+ )
444
+
445
+ #----------------------------------------------------------------------------
446
+
447
+ def open_dest(dest: str) -> Tuple[str, Callable[[str, Union[bytes, str]], None], Callable[[], None]]:
448
+ dest_ext = file_ext(dest)
449
+
450
+ if dest_ext == 'zip':
451
+ if os.path.dirname(dest) != '':
452
+ os.makedirs(os.path.dirname(dest), exist_ok=True)
453
+ zf = zipfile.ZipFile(file=dest, mode='w', compression=zipfile.ZIP_STORED)
454
+ def zip_write_bytes(fname: str, data: Union[bytes, str]):
455
+ zf.writestr(fname, data)
456
+ return '', zip_write_bytes, zf.close
457
+ else:
458
+ # If the output folder already exists, check that is is
459
+ # empty.
460
+ #
461
+ # Note: creating the output directory is not strictly
462
+ # necessary as folder_write_bytes() also mkdirs, but it's better
463
+ # to give an error message earlier in case the dest folder
464
+ # somehow cannot be created.
465
+ if os.path.isdir(dest) and len(os.listdir(dest)) != 0:
466
+ raise click.ClickException('--dest folder must be empty')
467
+ os.makedirs(dest, exist_ok=True)
468
+
469
+ def folder_write_bytes(fname: str, data: Union[bytes, str]):
470
+ os.makedirs(os.path.dirname(fname), exist_ok=True)
471
+ with open(fname, 'wb') as fout:
472
+ if isinstance(data, str):
473
+ data = data.encode('utf8')
474
+ fout.write(data)
475
+ return dest, folder_write_bytes, lambda: None
476
+
477
+ #----------------------------------------------------------------------------
478
+
479
+ @click.command()
480
+ @click.option('--source', help='Input directory or archive name', metavar='PATH', type=str, required=True)
481
+ @click.option('--dest', help='Output directory or archive name', metavar='PATH', type=str, required=True)
482
+ @click.option('--max-images', help='Maximum number of images to output', metavar='INT', type=int)
483
+ @click.option('--transform', help='Input crop/resize mode', metavar='MODE', type=click.Choice(['center-crop', 'center-crop-wide']))
484
+ @click.option('--resolution', help='Output resolution (e.g., 512x512)', metavar='WxH', type=parse_tuple)
485
+ @click.option('--labels-only', is_flag=True, help='Only generate dataset.json labels at source root; do not copy or re-encode images.')
486
+
487
+ def main(
488
+ source: str,
489
+ dest: str,
490
+ max_images: Optional[int],
491
+ transform: Optional[str],
492
+ resolution: Optional[Tuple[int, int]],
493
+ labels_only: bool,
494
+ ):
495
+ """Convert an image dataset into a dataset archive usable with StyleGAN2 ADA PyTorch,
496
+ or only generate dataset.json labels in-place.
497
+
498
+ The input dataset format is guessed from the --source argument:
499
+
500
+ \b
501
+ --source *_lmdb/ Load LSUN dataset
502
+ --source cifar-10-python.tar.gz Load CIFAR-10 dataset
503
+ --source train-images-idx3-ubyte.gz Load MNIST dataset
504
+ --source path/ Recursively load all images from path/
505
+ --source dataset.zip Recursively load all images from dataset.zip
506
+
507
+ Specifying the output format and path:
508
+
509
+ \b
510
+ --dest /path/to/dir Save output files under /path/to/dir
511
+ --dest /path/to/dataset.zip Save output files into /path/to/dataset.zip
512
+
513
+ The output dataset format can be either an image folder or an uncompressed zip archive.
514
+ Zip archives makes it easier to move datasets around file servers and clusters, and may
515
+ offer better training performance on network file systems.
516
+
517
+ Images within the dataset archive will be stored as uncompressed PNG.
518
+ Uncompresed PNGs can be efficiently decoded in the training loop.
519
+
520
+ Class labels are stored in a file called 'dataset.json' that is stored at the
521
+ dataset root folder. This file has the following structure:
522
+
523
+ \b
524
+ {
525
+ "labels": [
526
+ ["00000/img00000000.png",6],
527
+ ["00000/img00000001.png",9],
528
+ ... repeated for every image in the datase
529
+ ["00049/img00049999.png",1]
530
+ ]
531
+ }
532
+
533
+ If the 'dataset.json' file cannot be found, class labels are determined from
534
+ top-level directory names.
535
+
536
+ Image scale/crop and resolution requirements:
537
+
538
+ Output images must be square-shaped and they must all have the same power-of-two
539
+ dimensions.
540
+
541
+ To scale arbitrary input image size to a specific width and height, use the
542
+ --resolution option. Output resolution will be either the original
543
+ input resolution (if resolution was not specified) or the one specified with
544
+ --resolution option.
545
+
546
+ Use the --transform=center-crop or --transform=center-crop-wide options to apply a
547
+ center crop transform on the input image. These options should be used with the
548
+ --resolution option. For example:
549
+
550
+ \b
551
+ python scripts/dataset_tools.py --source data/cat_lmdb --dest data/cat_png \\
552
+ --transform=center-crop-wide --resolution=512x384
553
+ """
554
+
555
+ # Labels-only mode: just write dataset.json next to the original data.
556
+ if labels_only:
557
+ PIL.Image.init()
558
+ create_labels_only(source)
559
+ return
560
+
561
+ PIL.Image.init()
562
+
563
+ if dest == '':
564
+ raise click.ClickException('--dest output filename or directory must not be an empty string')
565
+
566
+ num_files, input_iter = open_dataset(source, max_images=max_images)
567
+ archive_root_dir, save_bytes, close_dest = open_dest(dest)
568
+
569
+ if resolution is None: resolution = (None, None)
570
+ transform_image = make_transform(transform, *resolution)
571
+
572
+ dataset_attrs = None
573
+
574
+ labels = []
575
+ for idx, image in tqdm(enumerate(input_iter), total=num_files):
576
+ idx_str = f'{idx:08d}'
577
+ archive_fname = f'{idx_str[:5]}/img{idx_str}.png'
578
+
579
+ # Apply crop and resize.
580
+ img = transform_image(image['img'])
581
+ if img is None:
582
+ continue
583
+
584
+ # Error check to require uniform image attributes across
585
+ # the whole dataset.
586
+ channels = img.shape[2] if img.ndim == 3 else 1
587
+ cur_image_attrs = {'width': img.shape[1], 'height': img.shape[0], 'channels': channels}
588
+ if dataset_attrs is None:
589
+ dataset_attrs = cur_image_attrs
590
+ width = dataset_attrs['width']
591
+ height = dataset_attrs['height']
592
+ if width != height:
593
+ raise click.ClickException(f'Image dimensions after scale and crop are required to be square. Got {width}x{height}')
594
+ if dataset_attrs['channels'] not in [1, 3]:
595
+ raise click.ClickException('Input images must be stored as RGB or grayscale')
596
+ if width != 2 ** int(np.floor(np.log2(width))):
597
+ raise click.ClickException('Image width/height after scale and crop are required to be power-of-two')
598
+ elif dataset_attrs != cur_image_attrs:
599
+ err = [f' dataset {k}/cur image {k}: {dataset_attrs[k]}/{cur_image_attrs[k]}' for k in dataset_attrs.keys()]
600
+ raise click.ClickException(f'Image {archive_fname} attributes must be equal across all images of the dataset. Got:\n' + '\n'.join(err))
601
+
602
+ # Save the image as an uncompressed PNG.
603
+ img = PIL.Image.fromarray(img, {1: 'L', 3: 'RGB'}[channels])
604
+ image_bits = io.BytesIO()
605
+ img.save(image_bits, format='png', compress_level=0, optimize=False)
606
+ save_bytes(os.path.join(archive_root_dir, archive_fname), image_bits.getbuffer())
607
+ labels.append([archive_fname, image['label']] if image['label'] is not None else None)
608
+
609
+ metadata = {'labels': labels if all(x is not None for x in labels) else None}
610
+ save_bytes(os.path.join(archive_root_dir, 'dataset.json'), json.dumps(metadata))
611
+ close_dest()
612
+
613
+ #----------------------------------------------------------------------------
614
+
615
+ if __name__ == "__main__":
616
+ main()
617
+
618
+ #----------------------------------------------------------------------------
scripts/evaluate_all.py ADDED
@@ -0,0 +1,130 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Evaluate CIFAR-10 checkpoints with independent per-GPU seeds."""
2
+ import argparse
3
+ import csv
4
+ import hashlib
5
+ import io
6
+ import json
7
+ import os
8
+ from pathlib import Path
9
+ import subprocess
10
+ import sys
11
+ import time
12
+
13
+ ROOT = Path(__file__).resolve().parents[1]
14
+
15
+ # Method names match configuration prefixes and checkpoint filenames.
16
+ CONFIGS = {
17
+ "baseline": "baseline_ar.yaml",
18
+ "tail_dropout": "tail_dropout_ar.yaml",
19
+ "flow_matching": "flow_matching_ar.yaml",
20
+ "flow_matching_tail_dropout": "flow_matching_tail_dropout_ar.yaml",
21
+ "flow_matching_tied_tail_dropout": "flow_matching_tied_tail_dropout_ar.yaml",
22
+ "war": "war_ar.yaml"
23
+ }
24
+
25
+
26
+ def sha256(path):
27
+ digest = hashlib.sha256()
28
+ with open(path, 'rb') as f:
29
+ for block in iter(lambda: f.read(8 * 1024 * 1024), b''):
30
+ digest.update(block)
31
+ return digest.hexdigest()
32
+
33
+
34
+ def check_free(gpus):
35
+ output = subprocess.check_output(
36
+ ['nvidia-smi', '--query-gpu=index,memory.used,utilization.gpu',
37
+ '--format=csv,noheader,nounits'], text=True)
38
+ devices = {int(row[0]): (int(row[1]), int(row[2]))
39
+ for row in csv.reader(io.StringIO(output))}
40
+ busy = {gpu: devices.get(gpu) for gpu in gpus
41
+ if gpu not in devices or devices[gpu][0] > 100 or devices[gpu][1] > 0}
42
+ if busy:
43
+ raise RuntimeError(f'Requested GPUs are not free: {busy}')
44
+
45
+
46
+ def save_summary(output, rows):
47
+ (output / 'results.json').write_text(json.dumps(rows, indent=2) + '\n')
48
+ if rows:
49
+ with (output / 'results.csv').open('w', newline='') as f:
50
+ writer = csv.DictWriter(f, fieldnames=list(rows[0]))
51
+ writer.writeheader()
52
+ writer.writerows(rows)
53
+
54
+
55
+ def main():
56
+ parser = argparse.ArgumentParser(description=__doc__)
57
+ parser.add_argument('--gpus', default='0,1,2,3,4,5,6,7')
58
+ parser.add_argument('--output', default='outputs/cifar10-nonlegacy')
59
+ parser.add_argument('--methods', nargs='+', choices=CONFIGS,
60
+ help='Optional subset, e.g. baseline war')
61
+ parser.add_argument('--resume', action='store_true', help='Reuse matching completed evaluations')
62
+ args = parser.parse_args()
63
+ gpus = [int(x) for x in args.gpus.split(',')]
64
+ if not gpus or len(gpus) != len(set(gpus)):
65
+ parser.error('GPU IDs must be nonempty and distinct')
66
+ configs = {key: ROOT / 'configs/cifar10' / name for key, name in CONFIGS.items()}
67
+ for config in configs.values():
68
+ if not config.is_file():
69
+ raise FileNotFoundError(config)
70
+ if args.methods:
71
+ if set(args.methods) - configs.keys():
72
+ parser.error('Unknown method')
73
+ configs = {key: path for key, path in configs.items() if key in args.methods}
74
+ checkpoints = {key: ROOT / 'checkpoints' / f'{key}.pt' for key in configs}
75
+ for checkpoint in checkpoints.values():
76
+ if not checkpoint.is_file():
77
+ raise FileNotFoundError(f'Place the downloaded checkpoint at {checkpoint}')
78
+ reference = ROOT / 'refs/cifar10-32x32_local.npz'
79
+ reference_hash = sha256(reference)
80
+ if reference_hash != '5493968c4f29b2878f64d6a640351f377e32e86553db56eca8147a0ae50d93f2':
81
+ raise ValueError('The CIFAR-10 FID reference checksum does not match')
82
+ output = Path(args.output).resolve()
83
+ output.mkdir(parents=True, exist_ok=True)
84
+ env = os.environ.copy()
85
+ env.update(CUDA_VISIBLE_DEVICES=','.join(map(str, gpus)), PYTHONDONTWRITEBYTECODE='1',
86
+ WANDB_MODE='disabled', OMP_NUM_THREADS='8', MKL_NUM_THREADS='8', TF_CPP_MIN_LOG_LEVEL='2')
87
+ rows = []
88
+ for key, config in configs.items():
89
+ checkpoint = checkpoints[key]
90
+ checkpoint_hash, config_hash = sha256(checkpoint), sha256(config)
91
+ case = output / key
92
+ result_path = case / 'metrics.json'
93
+ if result_path.exists() and not args.resume:
94
+ raise FileExistsError(result_path)
95
+ if not result_path.exists():
96
+ check_free(gpus)
97
+ case.mkdir(exist_ok=True)
98
+ cmd = [sys.executable, '-m', 'torch.distributed.run', '--standalone',
99
+ f'--nproc-per-node={len(gpus)}', 'evaluate.py', '--config', str(config),
100
+ '--checkpoint', str(checkpoint), '--num-samples', '50000', '--batch-size', '64',
101
+ '--seed', '42', '--no-legacy', '--output', str(case), '--fid-reference', str(reference)]
102
+ request = dict(command=cmd, checkpoint_sha256=checkpoint_hash,
103
+ config_sha256=config_hash, legacy=False)
104
+ (case / 'request.json').write_text(json.dumps(request, indent=2) + '\n')
105
+ print(f'START {key}', flush=True)
106
+ started = time.monotonic()
107
+ with (case / 'run.log').open('w') as log:
108
+ subprocess.run(cmd, cwd=ROOT, env=env, stdout=log, stderr=subprocess.STDOUT, check=True)
109
+ print(f'DONE {key} in {time.monotonic() - started:.1f}s', flush=True)
110
+ result = json.loads(result_path.read_text())
111
+ request = json.loads((case / 'request.json').read_text())
112
+ if request['checkpoint_sha256'] != checkpoint_hash:
113
+ raise ValueError(f'{key}: checkpoint changed since the recorded evaluation')
114
+ expected = dict(legacy=False, num_samples=50000, seed=42, world_size=len(gpus),
115
+ batch_size=64, ar_ema=True, precision='32-true', decoder='single_step',
116
+ config_sha256=config_hash, reference_sha256=reference_hash)
117
+ if any(result.get(name) != value for name, value in expected.items()):
118
+ raise ValueError(f'{key}: recorded evaluation settings do not match')
119
+ row = dict(method=key, nonlegacy_gfid=result['fid'], unique_images=result['unique_images'],
120
+ num_samples=result['num_samples'], legacy=False,
121
+ checkpoint_sha256=checkpoint_hash, config_sha256=config_hash,
122
+ reference_sha256=reference_hash)
123
+ rows.append(row)
124
+ save_summary(output, rows)
125
+ print(f'RESULT {key}: {result["fid"]:.8f}', flush=True)
126
+ print(f'COMPLETE: {len(rows)} non-legacy evaluations', flush=True)
127
+
128
+
129
+ if __name__ == '__main__':
130
+ main()
scripts/export_checkpoint.py ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Export inference states without optimizer, paths, or experiment tracker metadata."""
2
+ import argparse
3
+ import hashlib
4
+ import json
5
+ from pathlib import Path
6
+ import sys
7
+ sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
8
+ import torch
9
+ from omegaconf import OmegaConf
10
+ from models import Encoder, Decoder, ARModel
11
+
12
+
13
+ def sha256(path):
14
+ digest=hashlib.sha256()
15
+ with open(path,'rb') as f:
16
+ for block in iter(lambda:f.read(8*1024*1024),b''): digest.update(block)
17
+ return digest.hexdigest()
18
+
19
+
20
+ def export(source, config, output):
21
+ source_state=torch.load(source, map_location='cpu', weights_only=True, mmap=True)
22
+ c=OmegaConf.load(config)
23
+ states={key:{name.removeprefix('_orig_mod.'): tensor for name,tensor in source_state[key].items()}
24
+ for key in ('enc','dec','ar_model','ar_model_ema')}
25
+ for cls,key in ((Encoder,'enc'),(Decoder,'dec'),(ARModel,'ar_model'),(ARModel,'ar_model_ema')):
26
+ # Assignment checks all keys/shapes without allocating a second model's storage.
27
+ with torch.device('meta'): model=cls(c)
28
+ model.load_state_dict(states[key],strict=True,assign=True)
29
+ del model
30
+ out=Path(output); out.parent.mkdir(parents=True,exist_ok=True)
31
+ if out.exists(): raise FileExistsError(out)
32
+ torch.save(dict(format_version=1,**states),out)
33
+ saved=torch.load(out,map_location='cpu',weights_only=True,mmap=True)
34
+ for key,state in states.items():
35
+ for name,tensor in state.items():
36
+ if not torch.equal(tensor,saved[key][name]): raise AssertionError((key,name))
37
+ return dict(filename=out.name,sha256=sha256(out),bytes=out.stat().st_size)
38
+
39
+
40
+ def main():
41
+ p=argparse.ArgumentParser(description=__doc__)
42
+ p.add_argument('--source',required=True); p.add_argument('--config',required=True); p.add_argument('--output',required=True)
43
+ a=p.parse_args(); result=export(a.source,a.config,a.output)
44
+ Path(a.output+'.json').write_text(json.dumps(result,indent=2)+'\n'); print(json.dumps(result))
45
+
46
+
47
+ if __name__=='__main__': main()
scripts/prepare_cifar10.py ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Convert the official CIFAR-10 training split into the wARTok PNG layout."""
2
+ import argparse
3
+ import json
4
+ from pathlib import Path
5
+ import numpy as np
6
+ from PIL import Image
7
+ from torchvision.datasets import CIFAR10
8
+
9
+
10
+ def main():
11
+ p=argparse.ArgumentParser(description=__doc__)
12
+ p.add_argument('--download-dir',default='./data/downloads')
13
+ p.add_argument('--output',default='./data/cifar10')
14
+ p.add_argument('--download',action='store_true')
15
+ p.add_argument('--reference-images',help='Optional NPZ of the same 50K training images for ADM statistics')
16
+ a=p.parse_args(); out=Path(a.output)
17
+ if out.exists() and any(out.iterdir()): p.error('output must be empty to prevent mixing splits')
18
+ ds=CIFAR10(a.download_dir,train=True,download=a.download)
19
+ assert len(ds)==50000
20
+ labels=[]
21
+ for i,(image,label) in enumerate(ds):
22
+ name=f'{i//1000:05d}/img{i:08d}.png'
23
+ path=out/name; path.parent.mkdir(parents=True,exist_ok=True)
24
+ image.save(path); labels.append([name,label])
25
+ (out/'dataset.json').write_text(json.dumps({'labels':labels}))
26
+ if a.reference_images:
27
+ with open(a.reference_images,'xb') as f: np.savez_compressed(f,ds.data)
28
+ print(f'Wrote {len(ds)} training images to {out}')
29
+
30
+
31
+ if __name__=='__main__': main()
train.py ADDED
@@ -0,0 +1,1209 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import itertools
2
+ import math
3
+ import os
4
+ import random
5
+ import subprocess
6
+ import sys
7
+ from pathlib import Path
8
+ from typing import Iterable, Iterator, List, NamedTuple
9
+ os.environ.setdefault("WANDB_MODE", "disabled")
10
+ import wandb
11
+ import numpy as np
12
+ import torch
13
+ from torch import nn
14
+ import torch.nn.functional as F
15
+ import torchvision.utils
16
+ from einops import rearrange
17
+ from lightning import Fabric
18
+ from wandb.integration.lightning.fabric import WandbLogger
19
+ from dataset import ImageFolderDataset
20
+ from gan_loss import GANLoss
21
+ from lpips import LPIPS
22
+ from models import ARModel, Encoder, Decoder, Linear
23
+ from tqdm import tqdm
24
+ from omegaconf import OmegaConf
25
+ from torch.utils.data import DataLoader
26
+ import copy
27
+ import torch.distributed as dist
28
+ from glob import glob
29
+ try:
30
+ from thop import profile
31
+ from thop.vision.basic_hooks import count_linear
32
+ except ImportError:
33
+ profile = None
34
+ count_linear = None
35
+
36
+
37
+ torch.backends.cudnn.benchmark = True
38
+ torch.backends.cudnn.deterministic = False
39
+
40
+ # Phase utils
41
+ class Target(NamedTuple):
42
+ DO_AE: bool = False
43
+ DO_L2: bool = False
44
+ DO_L1: bool = False
45
+ DO_LPIPS: bool = False
46
+ DO_GAN_G: bool = False
47
+ DO_GAN_D: bool = False
48
+ DO_PRIOR_ENC: bool = False
49
+ DO_PRIOR_AR: bool = False
50
+
51
+ class Phase(NamedTuple):
52
+ num_steps: int
53
+ targets: List[Target]
54
+ internal_steps: List[int]
55
+
56
+ # Parse phases such as "1000:DO_L2-DO_PRIOR_AR:1 7000:DO_PRIOR_AR:1".
57
+ def parse_phases(phases_str):
58
+ phases = [] # list of Phase objects
59
+ for phase_str in phases_str.split(' '):
60
+ num_steps, targets_str, internal_steps_str = phase_str.split(':')
61
+ num_steps = int(num_steps)
62
+ targets = [Target(**{k: True for obj in target_str.split(',') for k in obj.split('-') }) for target_str in targets_str.split(',')]
63
+ internal_steps = [int(step) for step in internal_steps_str.split(',')]
64
+ phases.append(Phase(num_steps, targets, internal_steps))
65
+ return phases
66
+
67
+ def get_phase(global_step, phases, phase_step_accum, config):
68
+ target=None
69
+ for phase_idx, phase_step in enumerate(phase_step_accum):
70
+ if global_step <= phase_step:
71
+ internel_step = (global_step - phase_step_accum[phase_idx-1]) if phase_idx > 0 else global_step
72
+ internel_accumulate = list(itertools.accumulate(phases[phase_idx].internal_steps))
73
+ internel_step = internel_step % internel_accumulate[-1]
74
+
75
+ for inner_idx in range(len(internel_accumulate)):
76
+ if internel_step < internel_accumulate[inner_idx]:
77
+ target = phases[phase_idx].targets[inner_idx]
78
+ break
79
+ if target is not None:
80
+ break
81
+
82
+ DO_AE = any([target.DO_L2, target.DO_L1, target.DO_LPIPS, target.DO_GAN_G])
83
+
84
+ # assert inconsistency configuration
85
+ assert not (DO_AE and not config.train_ae), f"train_ae={config.train_ae} is inconsistent with DO_AE={DO_AE} in phases"
86
+ assert not (target.DO_PRIOR_AR and not config.train_ar), f"train_ar={config.train_ar} is inconsistent with DO_PRIOR_AR={target.DO_PRIOR_AR} in phases"
87
+ assert not ((target.DO_GAN_G or target.DO_GAN_D) and not config.use_gan_loss), f"use_gan_loss={config.use_gan_loss} is inconsistent with DO_GAN_G or DO_GAN_D={(target.DO_GAN_G or target.DO_GAN_D)} in phases"
88
+ assert not (target.DO_PRIOR_ENC and not config.use_prior_loss), f"use_prior_loss={config.use_prior_loss} is inconsistent with DO_PRIOR_ENC={target.DO_PRIOR_ENC} in phases"
89
+ assert not (target.DO_LPIPS and not config.use_lpips_loss), f"use_lpips_loss={config.use_lpips_loss} is inconsistent with DO_LPIPS={target.DO_LPIPS} in phases"
90
+
91
+ # update target with global_step
92
+ target = Target(DO_L1=target.DO_L1,
93
+ DO_L2=target.DO_L2,
94
+ DO_LPIPS=target.DO_LPIPS,
95
+ DO_GAN_G=target.DO_GAN_G and (global_step >= config.gan_start),
96
+ DO_GAN_D=target.DO_GAN_D and (global_step >= config.gan_start),
97
+ DO_PRIOR_ENC=target.DO_PRIOR_ENC and (global_step >= config.prior_start),
98
+ DO_PRIOR_AR=target.DO_PRIOR_AR,
99
+ DO_AE=DO_AE)
100
+ return phase_idx, inner_idx, target, internel_step
101
+
102
+ class InfiniteIterator(Iterator):
103
+ def __init__(self, iterable: Iterable):
104
+ self.iterable = iterable
105
+ self._it = iter(iterable)
106
+
107
+ def __iter__(self):
108
+ return self
109
+
110
+ def __next__(self):
111
+ try:
112
+ return next(self._it)
113
+ except StopIteration:
114
+ self._it = iter(self.iterable)
115
+ return next(self._it)
116
+
117
+ # Image Processing Utils
118
+ def patchify(x, patch_size):
119
+ x = rearrange(x, 'b c (h p1) (w p2) -> b (h w) (p1 p2 c)', p1=patch_size, p2=patch_size)
120
+ return x
121
+
122
+ def unpatchify(x, image_size, patch_size):
123
+ x = rearrange(x, 'b (h w) (p1 p2 c) -> b c (h p1) (w p2)', p1=patch_size, p2=patch_size, h=image_size//patch_size, w=image_size//patch_size)
124
+ return x
125
+
126
+ def img_unint8_to_norm(x):
127
+ return x.float() / 127.5 - 1.0
128
+
129
+ def img_denormalize(x):
130
+ return x.clamp(-1,1) * 0.5 + 0.5
131
+
132
+ def img_norm_to_uint8(x):
133
+ return torch.clamp(127.5 * x + 128.0, 0, 255).byte()
134
+
135
+ # -----------------------------
136
+ # Tail-dropout + noise-query utils
137
+ # -----------------------------
138
+
139
+ def _get_decoder_context_type(dec: nn.Module) -> str:
140
+ # `dec` is Decoder, `dec.dec` is Transformer
141
+ ctx = getattr(getattr(dec, "dec", None), "context_type", None)
142
+ return ctx if ctx is not None else "none"
143
+
144
+ def _get_decoder_query_len(dec: nn.Module) -> int:
145
+ return int(getattr(getattr(dec, "dec", None), "max_seq_len", 0))
146
+
147
+ def _t_to_k(t: torch.Tensor, K: int, t2k: float = 1.0) -> torch.Tensor:
148
+ """
149
+ Map t in [0,1] to discrete k in [0, K-1].
150
+ """
151
+ if K <= 1:
152
+ return torch.zeros_like(t, dtype=torch.long)
153
+ t_tmp = (t2k * t).clamp(0.0, 1.0)
154
+ k = torch.floor(t_tmp * K).to(torch.long).clamp(0, K - 1)
155
+ return k
156
+
157
+ class DiTiScheduler:
158
+ """
159
+ Piecewise linear time scheduler from Selftok, adapted for wARTok.
160
+ """
161
+ def __init__(
162
+ self,
163
+ n_timesteps: int,
164
+ K: int,
165
+ stages: str | List[int],
166
+ k_per_stage: str | List[int],
167
+ reverse_t: bool = False,
168
+ ):
169
+ self.n_timesteps = n_timesteps
170
+ self.K = K
171
+ self.reverse_t = reverse_t
172
+
173
+ if isinstance(stages, str):
174
+ stages = [int(s.strip()) for s in stages.split(',')]
175
+ if isinstance(k_per_stage, str):
176
+ k_per_stage = [int(k.strip()) for k in k_per_stage.split(',')]
177
+ self.stages = stages
178
+
179
+ # Scaling logic: if sum(k_per_stage) != K, scale it
180
+ total_k = sum(k_per_stage)
181
+ if total_k != K:
182
+ print(f"[DiTiScheduler] Warning: sum(k_per_stage)={total_k} does not match K={K}. Scaling k_per_stage...")
183
+ scale_factor = K / total_k
184
+ scaled_k = [int(k * scale_factor) for k in k_per_stage]
185
+ # Fix rounding errors to ensure sum is exactly K
186
+ diff = K - sum(scaled_k)
187
+ if diff != 0:
188
+ scaled_k[-1] += diff
189
+ k_per_stage = scaled_k
190
+ print(f"[DiTiScheduler] Scaled k_per_stage: {k_per_stage}")
191
+
192
+ self.k_per_stage = k_per_stage
193
+
194
+ self.t_to_idx = torch.zeros(n_timesteps).long()
195
+
196
+ # Precompute lookup table
197
+ current_stage = 0
198
+ sum_indices = 0
199
+ # stages should be cumulative, e.g., 200, 400, 600...
200
+
201
+ for t in range(n_timesteps):
202
+ # Check if we moved to next stage
203
+ if current_stage < len(self.stages) and t >= self.stages[current_stage]:
204
+ sum_indices += self.k_per_stage[current_stage]
205
+ current_stage += 1
206
+
207
+ if current_stage >= len(self.stages):
208
+ idx = self.K - 1
209
+ else:
210
+ prev_stage_t = self.stages[current_stage - 1] if current_stage > 0 else 0
211
+ current_stage_t = self.stages[current_stage]
212
+
213
+ current_steps = float(current_stage_t - prev_stage_t)
214
+ current_k = float(self.k_per_stage[current_stage])
215
+
216
+ t_adj = t - prev_stage_t
217
+ idx = int(float(t_adj) / current_steps * current_k + sum_indices)
218
+
219
+ self.t_to_idx[t] = idx
220
+
221
+ def to_indices(self, t: torch.Tensor) -> torch.Tensor:
222
+ """
223
+ Convert t in [0, 1] to indices [0, K-1] using the lookup table.
224
+ """
225
+ if self.reverse_t:
226
+ t = 1.0-t
227
+ device = t.device
228
+ # Map [0, 1] to [0, n_timesteps]
229
+ t_idx = (t * self.n_timesteps).long().clamp(0, self.n_timesteps - 1)
230
+ # Move the lookup table to the input device.
231
+ return self.t_to_idx.to(device)[t_idx].clamp(0, self.K - 1)
232
+
233
+
234
+ def build_taildrop_attn_mask(
235
+ *,
236
+ t: torch.Tensor,
237
+ context_len: int,
238
+ query_len: int,
239
+ context_type: str,
240
+ t2k: float = 1.0,
241
+ device=None,
242
+ diti_scheduler: DiTiScheduler = None,
243
+ ) -> torch.Tensor | None:
244
+ """
245
+ Build a boolean attention mask (True for keep, False for masked).
246
+
247
+ - cross_attn: returns (B, 1, query_len, context_len)
248
+ - concat: returns (B, 1, total_len, total_len) where total_len = query_len + context_len
249
+ """
250
+ device = device or t.device
251
+ B = t.shape[0]
252
+ if context_len <= 0:
253
+ return None
254
+
255
+ if diti_scheduler is not None:
256
+ k = diti_scheduler.to_indices(t) # (B,)
257
+ else:
258
+ k = _t_to_k(t, context_len, t2k=t2k) # (B,)
259
+
260
+ keep = (torch.arange(context_len, device=device)[None, :] <= k[:, None]) # (B, context_len)
261
+
262
+
263
+ if context_type == "cross_attn":
264
+ # (B, 1, query_len, context_len)
265
+ return keep[:, None, :].unsqueeze(1).expand(B, 1, query_len, context_len)
266
+
267
+ if context_type == "concat":
268
+ total_len = query_len + context_len
269
+ keep_total = torch.cat(
270
+ [torch.ones((B, query_len), device=device, dtype=torch.bool), keep],
271
+ dim=1,
272
+ ) # (B, total_len)
273
+ # (B, 1, total_len, total_len)
274
+ return keep_total[:, None, :].unsqueeze(1).expand(B, 1, total_len, total_len)
275
+
276
+ return None
277
+
278
+ def make_noise_image_patch_query(x: torch.Tensor, t: torch.Tensor, query_len: int) -> torch.Tensor:
279
+ """
280
+ Replace dummy query with a noised version of the (clean) image patches x.
281
+ x: (B, L, D)
282
+ t: (B,)
283
+ Returns noised patches, noise, and velocity, each of shape (B, L, D).
284
+ """
285
+ B, L, D = x.shape
286
+ noise = torch.randn_like(x)
287
+ t = t.view(B, 1, 1).to(x.dtype)
288
+ x_noised = (1.0 - t) * x + t * noise
289
+ return x_noised, noise, noise-x
290
+
291
+ # Seed utils
292
+ def seed_everything(seed):
293
+ # manually call these to ensure reproducibility
294
+ random.seed(seed)
295
+ np.random.seed(seed)
296
+ torch.manual_seed(seed)
297
+ torch.cuda.manual_seed_all(seed)
298
+ os.environ['PYTHONHASHSEED'] = str(seed)
299
+ # fabric seed everything
300
+ Fabric.seed_everything(seed, workers=True)
301
+
302
+ def worker_init_fn(worker_id):
303
+ worker_seed = torch.initial_seed() % 2**32
304
+ np.random.seed(worker_seed)
305
+ random.seed(worker_seed)
306
+
307
+
308
+ def make_worker_init_fn(base_seed: int):
309
+ base_seed = int(base_seed) % (2**32)
310
+
311
+ def _init(worker_id: int):
312
+ rank = dist.get_rank() if dist.is_available() and dist.is_initialized() else 0
313
+ worker_seed = (base_seed + worker_id + 1000 * rank) % (2**32)
314
+ np.random.seed(worker_seed)
315
+ random.seed(worker_seed)
316
+ torch.manual_seed(worker_seed)
317
+
318
+ return _init
319
+
320
+
321
+ # FID Utils
322
+ def adm_fid_evaluator(sample_cached_path, gt_cache_path, config, fabric):
323
+ if not os.path.exists(gt_cache_path):
324
+ raise FileNotFoundError(f"Ground-truth cache not found: {gt_cache_path}")
325
+ if not os.path.exists(sample_cached_path):
326
+ raise FileNotFoundError(f"Sample cache not found: {sample_cached_path}")
327
+
328
+ fid_script = str(Path(__file__).with_name('calc_fid.py'))
329
+ env = os.environ.copy()
330
+ cmd = [sys.executable,fid_script,"--ref_batch",gt_cache_path,"--sample_batch",sample_cached_path,"--batch_size",str(config.eval_batch_size),]
331
+ fabric.print(f"Running FID evaluation via {fid_script}...")
332
+
333
+ FID = None
334
+ process = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.STDOUT,text=True,bufsize=1,env=env,)
335
+ assert process.stdout is not None
336
+ for line in process.stdout:
337
+ line = line.rstrip("\n")
338
+ if line:
339
+ fabric.print(line, flush=True)
340
+ if line.startswith("FID_RESULT:"):
341
+ try:
342
+ FID = float(line.split("FID_RESULT:")[1].strip())
343
+ except ValueError:
344
+ pass
345
+ retcode = process.wait()
346
+ if retcode != 0 or FID is None or not math.isfinite(FID):
347
+ raise RuntimeError(f"FID evaluation failed: exit={retcode}, result={FID}")
348
+
349
+ return FID
350
+
351
+
352
+ # Training Utils
353
+ @torch.no_grad()
354
+ @torch._dynamo.disable
355
+ def ema_update(model, ema_model, ema_rate):
356
+ if model is None or ema_model is None:
357
+ return
358
+
359
+ for p,ema_p in zip(model.parameters(),ema_model.parameters()):
360
+ ema_p.copy_(p.detach().lerp(ema_p, ema_rate))
361
+
362
+
363
+
364
+ @torch.no_grad()
365
+ @torch._dynamo.disable
366
+ def sampling(enc, dec, ar_model, bz, class_label=None, temperature=1.0, topK=0, topP=0., cfg=0.0, cfg_schedule=None, cfg_power=None, cache_kv=False, config=None,t=None,query_tokens=None, **params):
367
+ token_ids = ar_model.sampling(bz, class_label, temperature, topK, topP, cfg, cfg_schedule, cfg_power, cache_kv)
368
+ quant = enc.quantizer.get_codes_w_indices(token_ids)
369
+ decoded = dec(quant, class_label, attn_mask=None, query_tokens=query_tokens,t=t)
370
+ return decoded
371
+
372
+ @torch.no_grad()
373
+ @torch._dynamo.disable
374
+ def reconstruction(enc, dec, x, labels, return_idx: bool = False, query_tokens=None,t=None):
375
+ quant, idx, _, _ = enc(x,labels, training=False)
376
+ decoded = dec(quant, labels, attn_mask=None, query_tokens=query_tokens, t=t)
377
+ return decoded, idx
378
+
379
+ @torch.no_grad()
380
+ @torch._dynamo.disable
381
+ def euler_sample_loop(enc, dec, ar_model, bz, class_label=None, temperature=1.0, topK=None, topP=None, cfg=1.0, cfg_schedule=None, cfg_power=None, cache_kv=False, config=None, diti_scheduler=None):
382
+ """
383
+ Euler solver for Flow Matching with dynamic token masking (DiTi).
384
+ """
385
+ if config is None:
386
+ raise ValueError("config is required for euler_sample_loop")
387
+ # Derive device / shape from config to avoid relying on outer-scope globals.
388
+ device = next(dec.parameters()).device
389
+ patch_dim = config.patch_dim
390
+ token_ids = ar_model.sampling(bz, class_label, temperature, topK, topP, cfg, cfg_schedule, cfg_power, cache_kv)
391
+ quant = enc.quantizer.get_codes_w_indices(token_ids)
392
+ query_len = config.x_len
393
+
394
+ num_steps = 25
395
+ x = torch.randn((bz, query_len, patch_dim), device=device)
396
+ context_len = quant.shape[1]
397
+ timesteps = torch.linspace(1.0, 0.0, num_steps + 1, device=device)
398
+ dt = 1.0 / num_steps
399
+
400
+ for i in range(num_steps):
401
+ t_curr = timesteps[i]
402
+ t_tensor = torch.full((bz,), t_curr, device=device)
403
+
404
+ attn_bias = None
405
+ if config.use_tail_dropout and config.tied_timestep:
406
+ if diti_scheduler is not None:
407
+ attn_bias = build_taildrop_attn_mask(
408
+ t=t_tensor, context_len=context_len, query_len=query_len,
409
+ context_type=config.context_type, device=device, diti_scheduler=diti_scheduler,
410
+ )
411
+ else:
412
+ attn_bias = build_taildrop_attn_mask(
413
+ t=t_tensor, context_len=context_len, query_len=query_len,
414
+ context_type=config.context_type, t2k=config.tail_dropout_t2k, device=device,
415
+ )
416
+ decoded = dec(quant, class_label, attn_mask=attn_bias, query_tokens=x, t=t_tensor)
417
+ if config.l2_v_target:
418
+ v_pred = decoded
419
+ x = x - v_pred * dt
420
+ else:
421
+ x0_pred = decoded
422
+ t_frac = dt/t_tensor
423
+ t_frac = t_frac[:,None,None]
424
+ x = x * (1-t_frac) + x0_pred * t_frac
425
+ return x
426
+
427
+ @torch.no_grad()
428
+ @torch._dynamo.disable
429
+ def euler_recon_loop(enc, dec, x, class_label=None, config=None, diti_scheduler=None):
430
+ """
431
+ Euler solver for Flow Matching with dynamic token masking (DiTi).
432
+ """
433
+ if config is None:
434
+ raise ValueError("config is required for euler_recon_loop")
435
+ # Derive device / shape from config to avoid relying on outer-scope globals.
436
+ device = next(dec.parameters()).device
437
+ B = x.shape[0]
438
+
439
+ quant, idx, _, _ = enc(x,class_label, training=False)
440
+ context_len = quant.shape[1]
441
+ query_len = x.shape[1]
442
+
443
+ num_steps = 25
444
+ timesteps = torch.linspace(1.0, 0.0, num_steps + 1, device=device)
445
+ dt = 1.0 / num_steps
446
+ xt = torch.randn_like(x)
447
+ for i in range(num_steps):
448
+ t_tensor = torch.full((B,), timesteps[i], device=device)
449
+ attn_bias = None
450
+ if config.use_tail_dropout and config.tied_timestep:
451
+ if diti_scheduler is not None:
452
+ attn_bias = build_taildrop_attn_mask(
453
+ t=t_tensor, context_len=context_len, query_len=query_len,
454
+ context_type=config.context_type, device=device, diti_scheduler=diti_scheduler,
455
+ )
456
+ else:
457
+ attn_bias = build_taildrop_attn_mask(
458
+ t=t_tensor, context_len=context_len, query_len=query_len,
459
+ context_type=config.context_type, t2k=config.tail_dropout_t2k, device=device,
460
+ )
461
+ decoded = dec(quant, class_label, attn_mask=attn_bias, query_tokens=xt, t=t_tensor)
462
+ if config.l2_v_target:
463
+ v_pred = decoded
464
+ xt = xt - v_pred * dt
465
+ else:
466
+ x0_pred = decoded
467
+ t_frac = dt/t_tensor
468
+ t_frac = t_frac[:,None,None]
469
+ xt = xt * (1-t_frac) + x0_pred * t_frac
470
+ return xt, idx
471
+
472
+ def toogle_train_eval(model, train=True):
473
+ if model is None: return
474
+ if train:
475
+ model.train()
476
+ else:
477
+ model.eval()
478
+
479
+ def toogle_require_grad(model, grads=True):
480
+ if model is None:
481
+ return
482
+ model.requires_grad_(grads)
483
+
484
+ def zero_nan_gradients(model):
485
+ for name, param in model.named_parameters():
486
+ if param.grad is not None:
487
+ param.grad.nan_to_num_(nan=0.0, posinf=1e5, neginf=-1e5)
488
+
489
+ # Model Prams and GFLOPS Utils
490
+ def print_model_info(fabric,model, inputs, name="Model"):
491
+ if model is None: return
492
+ if profile is None:
493
+ fabric.print(f"[{name}] thop not installed, skipping GFLOPS calculation.")
494
+ params = sum(p.numel() for p in model.parameters())
495
+ fabric.print(f"[{name}] Params: {params/1e6:.2f}M")
496
+ return
497
+
498
+ # Move inputs to device
499
+ device = next(model.parameters()).device
500
+ inputs = tuple(i.to(device) if isinstance(i, torch.Tensor) else i for i in inputs)
501
+
502
+ try:
503
+ model_copy = copy.deepcopy(model)
504
+ model_copy.eval()
505
+ custom_ops = {}
506
+ if count_linear is not None:
507
+ custom_ops[Linear] = count_linear
508
+
509
+ macs, params = profile(model_copy, inputs=inputs, custom_ops=custom_ops, verbose=False)
510
+ gflops = macs * 2 / 1e9
511
+ fabric.print(f"[{name}] Params: {params/1e6:.2f}M, GFLOPS: {gflops:.2f}")
512
+ del model_copy
513
+ except Exception as e:
514
+ fabric.print(f"[{name}] Failed to calculate GFLOPS: {e}")
515
+ params = sum(p.numel() for p in model.parameters())
516
+ fabric.print(f"[{name}] Params: {params/1e6:.2f}M")
517
+
518
+ def print_model_stats(fabric,config, enc, dec, ar_model):
519
+ x_len = int(config.x_len)
520
+ patch_dim = int(config.patch_dim)
521
+ num_classes = int(config.num_classes)
522
+ z_len = int(config.z_len)
523
+ z_dim = int(config.z_dim)
524
+ codebook_size = int(config.codebook_size)
525
+
526
+ x_dummy = torch.randn(1, x_len, patch_dim)
527
+ label_dummy = torch.zeros(1, num_classes)
528
+
529
+ print_model_info(fabric,enc, (x_dummy, label_dummy), "Encoder")
530
+
531
+ z_dummy = torch.randn(1, z_len, z_dim)
532
+ # If Decoder uses noise query, it requires query_tokens (and optionally t) at forward time.
533
+ query_type = getattr(getattr(dec, "dec", None), "query_type", "learnable")
534
+ if query_type == "noise":
535
+ query_len = int(getattr(config, "x_len", z_len))
536
+ query_dummy = torch.randn(1, query_len, patch_dim)
537
+ t_dummy = torch.zeros(1)
538
+ print_model_info(fabric, dec, (z_dummy, label_dummy, None, query_dummy, t_dummy), "Decoder")
539
+ else:
540
+ print_model_info(fabric,dec, (z_dummy, label_dummy), "Decoder")
541
+
542
+ if ar_model is not None:
543
+ idx_dummy = torch.randint(0, codebook_size, (1, z_len))
544
+ print_model_info(fabric,ar_model, (idx_dummy, label_dummy), "ARModel")
545
+
546
+
547
+ def train(config):
548
+ if config.use_tf32:
549
+ torch.backends.cuda.matmul.allow_tf32 = True
550
+ torch.backends.cudnn.allow_tf32 = True
551
+ # Parse devices
552
+ devices_value = config.devices
553
+ if isinstance(devices_value, str) and "," in devices_value:
554
+ devices_for_fabric = [int(d.strip()) for d in devices_value.split(',')]
555
+ num_devices = len(devices_for_fabric)
556
+ else:
557
+ try:
558
+ num_devices = int(devices_value)
559
+ devices_for_fabric = num_devices
560
+ except (TypeError, ValueError):
561
+ raise ValueError(f"Invalid value for devices: {devices_value}. Expected an integer or comma-separated string like '0,1,2'.")
562
+
563
+ if config.batch_size % num_devices or config.batch_size < num_devices:
564
+ raise ValueError("Global batch_size must be divisible by the number of devices")
565
+ if config.eval_data_dir is not None and not Path(config.eval_fid_ref_path).is_file():
566
+ raise FileNotFoundError("Prepare eval_fid_ref_path before training, or set eval_data_dir=null to disable periodic evaluation")
567
+
568
+ # prepare seed and save dir
569
+ if config.seed is not None:
570
+ seed = int(config.seed) if config.seed is not None else 0
571
+ seed_everything(seed)
572
+ dl_generator = torch.Generator()
573
+ dl_generator.manual_seed(seed)
574
+ worker_init = make_worker_init_fn(seed)
575
+
576
+ if "EXPERIMENT_SAVE_DIR" in os.environ:
577
+ save_dir = os.environ["EXPERIMENT_SAVE_DIR"]
578
+ else:
579
+ experiment_index = len(glob(f"{config.save_dir}/*"))
580
+ save_dir = config.save_dir+f"/{experiment_index:03d}"
581
+ os.environ["EXPERIMENT_SAVE_DIR"] = save_dir
582
+ logger = WandbLogger(project=config.wandb_project, save_dir=save_dir, name=config.wandb_name)
583
+
584
+ # initialize fabric
585
+ fabric = Fabric(accelerator="gpu", devices=devices_for_fabric, precision=config.precision, loggers=logger)
586
+ fabric.launch()
587
+ fabric.print(config)
588
+ fabric.print(f"Global seed set to {config.seed}")
589
+
590
+ # Initialize DiTi Scheduler if config provided
591
+ diti_scheduler = None
592
+ if getattr(config, "time_stages", None) and getattr(config, "k_per_stage", None):
593
+ fabric.print("Initializing DiTi Scheduler for piecewise linear time-to-k mapping...")
594
+ diti_scheduler = DiTiScheduler(
595
+ n_timesteps=1000,
596
+ K=int(config.z_len),
597
+ stages=config.time_stages,
598
+ k_per_stage=config.k_per_stage,
599
+ reverse_t=config.reverse_t
600
+ )
601
+
602
+ # Models and optimizers
603
+ enc = Encoder(config).to(fabric.device) # always initialize encoder
604
+ dec = Decoder(config).to(fabric.device)
605
+ if not config.train_ae:
606
+ enc = enc.requires_grad_(False)
607
+ dec = dec.requires_grad_(False)
608
+ ar_model = ARModel(config).to(fabric.device) if config.train_ar else None
609
+ gan_loss = GANLoss(**config.GANLoss).to(fabric.device) if config.use_gan_loss else None
610
+ perceptual_loss = LPIPS().to(fabric.device).eval().requires_grad_(False) if config.use_lpips_loss else None
611
+
612
+
613
+ # Load Ckpt
614
+ if config.ae_ckpt_path != "":
615
+ fabric.print(f'resume ae from ckpt {config.ae_ckpt_path}')
616
+
617
+ ckpt_states = fabric.load(config.ae_ckpt_path)
618
+ if enc is not None and ckpt_states.get('enc', None) is not None:
619
+ enc.load_state_dict({k.split('_orig_mod.')[-1]: v for k, v in ckpt_states['enc'].items() })
620
+ fabric.print("loaded enc")
621
+ if dec is not None and ckpt_states.get('dec', None) is not None:
622
+ dec.load_state_dict({k.split('_orig_mod.')[-1]: v for k, v in ckpt_states['dec'].items() })
623
+ fabric.print("loaded dec")
624
+
625
+ if gan_loss is not None and ckpt_states.get('gan_loss', None) is not None:
626
+ gan_loss.load_state_dict({k.split('_orig_mod.')[-1]: v for k, v in ckpt_states['gan_loss'].items() })
627
+
628
+ if config.ar_ckpt_path!="":
629
+ fabric.print(f'resume ar from ckpt {config.ar_ckpt_path}')
630
+ ar_ckpt_states = fabric.load(config.ar_ckpt_path)
631
+ if config.load_ar_model and ar_model is not None and ar_ckpt_states.get('ar_model', None) is not None:
632
+ ar_model.load_state_dict({k.split('_orig_mod.')[-1]: v for k, v in ar_ckpt_states['ar_model'].items() } )
633
+ fabric.print("loaded ar")
634
+
635
+ # for ema
636
+ ae_ema_rate = math.pow(0.5,1/config.ae_ema_halflife) if config.ae_ema_halflife>0 else 0.
637
+ ar_ema_rate = math.pow(0.5,1/config.ar_ema_halflife) if config.ar_ema_halflife>0 else 0.
638
+ enc_ema = copy.deepcopy(enc).requires_grad_(False).eval() if ae_ema_rate>0 else None
639
+ dec_ema = copy.deepcopy(dec).requires_grad_(False).eval() if ae_ema_rate>0 else None
640
+ freeze_ar_model = copy.deepcopy(ar_model).requires_grad_(False).eval() if config.train_ar else None
641
+ ar_model_ema = copy.deepcopy(ar_model).requires_grad_(False).eval() if config.train_ar and ar_ema_rate>0 else None
642
+
643
+ if config.ae_ckpt_path != "":
644
+ fabric.print(f'resume ae ema from ckpt {config.ae_ckpt_path}')
645
+ ckpt_states = fabric.load(config.ae_ckpt_path)
646
+ if enc_ema is not None and ckpt_states.get('enc_ema', None) is not None:
647
+ enc_ema.load_state_dict({k.split('_orig_mod.')[-1]: v for k, v in ckpt_states['enc_ema'].items() })
648
+ if dec_ema is not None and ckpt_states.get('dec_ema', None) is not None:
649
+ dec_ema.load_state_dict({k.split('_orig_mod.')[-1]: v for k, v in ckpt_states['dec_ema'].items() })
650
+ print("loaded ae ema")
651
+
652
+ if config.ar_ckpt_path!="" and config.load_ar_model_ema and ar_model_ema is not None and ar_ckpt_states.get('ar_model_ema', None) is not None:
653
+ ar_model_ema.load_state_dict({k.split('_orig_mod.')[-1]: v for k, v in ar_ckpt_states['ar_model_ema'].items() })
654
+ print("loaded ar ema")
655
+ if config.ar_ckpt_path!="" and config.load_ar_model_ema and freeze_ar_model is not None and ar_ckpt_states.get('freeze_ar_model', None) is not None:
656
+ freeze_ar_model.load_state_dict({k.split('_orig_mod.')[-1]: v for k, v in ar_ckpt_states['freeze_ar_model'].items() })
657
+ print("loaded frozen ar")
658
+
659
+ # for inference
660
+ enc_raw = enc
661
+ dec_raw = dec
662
+ ar_model_raw = ar_model
663
+ enc_ema_raw = enc_ema
664
+ dec_ema_raw = dec_ema
665
+ ar_model_ema_raw = ar_model_ema
666
+
667
+ if fabric.is_global_zero:
668
+ fabric.print("Calculating model stats...")
669
+ print_model_stats(fabric,config, enc_raw, dec_raw, ar_model_raw)
670
+ root_dir = Path(save_dir)
671
+ root_dir.mkdir(exist_ok=True, parents=True)
672
+ OmegaConf.save(config, root_dir / "config.yaml", resolve=True)
673
+ img_dir = Path(save_dir+ "/images")
674
+ img_dir.mkdir(exist_ok=True, parents=True)
675
+ ckpt_dir = Path(save_dir+ "/ckpts")
676
+ ckpt_dir.mkdir(exist_ok=True, parents=True)
677
+
678
+ fabric.barrier()
679
+
680
+ if config.torch_compile:
681
+ if enc is not None and config.compile_ae:
682
+ enc = torch.compile(enc)
683
+ if enc_ema is not None and config.compile_ae:
684
+ enc_ema = torch.compile(enc_ema)
685
+ if dec is not None and config.compile_ae:
686
+ dec = torch.compile(dec)
687
+ if dec_ema is not None and config.compile_ae:
688
+ dec_ema = torch.compile(dec_ema)
689
+ if ar_model is not None and config.compile_ar:
690
+ ar_model = torch.compile(ar_model)
691
+ if ar_model_ema is not None and config.compile_ar:
692
+ ar_model_ema = torch.compile(ar_model_ema)
693
+ if freeze_ar_model is not None and config.compile_ar:
694
+ freeze_ar_model= torch.compile(freeze_ar_model)
695
+ if gan_loss is not None and config.compile_gan:
696
+ gan_loss = torch.compile(gan_loss)
697
+
698
+ opt_enc = torch.optim.AdamW(enc.parameters() , lr=config.lr_enc, betas=(config.beta1, config.beta2), weight_decay=config.weight_decay_enc) if config.train_ae else None
699
+ opt_ar = torch.optim.AdamW(ar_model.parameters(), lr=config.lr_ar, betas=(config.ar_beta1, config.ar_beta2), weight_decay=config.weight_decay_ar) if config.train_ar else None
700
+ opt_dec = torch.optim.AdamW(dec.parameters(), lr=config.lr_dec, betas=(config.beta1, config.beta2), weight_decay=config.weight_decay_dec) if config.train_ae else None
701
+ opt_gan_loss = torch.optim.AdamW(gan_loss.parameters(), lr=config.lr_gan_loss, betas=(config.beta1, config.beta2), weight_decay=config.weight_decay_gan) if config.use_gan_loss else None
702
+ if config.resume_optimizer:
703
+ if config.train_ae:
704
+ fabric.print("resumed AE optimizer")
705
+ opt_enc.load_state_dict(ckpt_states['opt_enc'])
706
+ opt_dec.load_state_dict(ckpt_states['opt_dec'])
707
+ if config.train_ar:
708
+ fabric.print("resumed AR optimizer")
709
+ ar_optimizer_states = ar_ckpt_states if config.ar_ckpt_path else ckpt_states
710
+ opt_ar.load_state_dict(ar_optimizer_states['opt_ar'])
711
+ if config.use_gan_loss:
712
+ fabric.print("resumed GAN optimizer")
713
+ opt_gan_loss.load_state_dict(ckpt_states['opt_gan_loss'])
714
+
715
+ # Datasets
716
+ dataset = ImageFolderDataset(path=config['data_dir'],
717
+ resolution=config.image_size,
718
+ use_label=config.use_label,
719
+ max_size=None,
720
+ xflip=config.xflip,
721
+ crop_type=config.crop_type,
722
+ deterministic_crop=bool(getattr(config, "deterministic_crop", False)),
723
+ crop_seed=int(config.seed) if config.seed is not None else 0,
724
+ )
725
+ train_loader = DataLoader(
726
+ dataset,
727
+ batch_size=config.batch_size//num_devices,
728
+ shuffle=True,
729
+ num_workers=config.num_workers,
730
+ pin_memory=True,
731
+ drop_last=True,
732
+ persistent_workers=config.num_workers > 0,
733
+ worker_init_fn=worker_init,
734
+ generator=dl_generator,
735
+ )
736
+ eval_loader=None
737
+ if config.eval_data_dir is not None:
738
+ eval_dataset = ImageFolderDataset(path=config['eval_data_dir'],
739
+ resolution=config.image_size,
740
+ use_label=config.use_label,
741
+ max_size=None,
742
+ xflip=False,
743
+ crop_type=config.eval_crop_type,
744
+ deterministic_crop=bool(getattr(config, "deterministic_crop", False)),
745
+ crop_seed=int(config.seed) if config.seed is not None else 0,
746
+ )
747
+ eval_loader = DataLoader(
748
+ eval_dataset,
749
+ batch_size=config.batch_size//num_devices,
750
+ shuffle=False,
751
+ pin_memory=True,
752
+ drop_last=False,
753
+ worker_init_fn=worker_init,
754
+ generator=dl_generator,
755
+ )
756
+
757
+ # Setup Model, Optimizer, DataLoader with Fabric
758
+ if config.train_ae:
759
+ enc, opt_enc = fabric.setup(enc, opt_enc)
760
+ dec, opt_dec = fabric.setup(dec, opt_dec)
761
+ if enc_ema is not None:
762
+ enc_ema = enc_ema.to(fabric.device)
763
+ if dec_ema is not None:
764
+ dec_ema = dec_ema.to(fabric.device)
765
+ if config.train_ar:
766
+ ar_model, opt_ar = fabric.setup(ar_model, opt_ar)
767
+ if ar_model_ema is not None:
768
+ ar_model_ema = ar_model_ema.to(fabric.device)
769
+ if freeze_ar_model is not None:
770
+ freeze_ar_model = freeze_ar_model.to(fabric.device)
771
+ if config.use_gan_loss:
772
+ gan_loss, opt_gan_loss = fabric.setup(gan_loss, opt_gan_loss)
773
+
774
+ train_loader = fabric.setup_dataloaders(train_loader)
775
+ train_loader = InfiniteIterator(train_loader)
776
+ if eval_loader is not None:
777
+ eval_loader = fabric.setup_dataloaders(eval_loader)
778
+
779
+
780
+ if config.train_ar:
781
+ ar_model.mark_forward_method('sampling')
782
+
783
+ global_step = 0
784
+
785
+ phases = parse_phases(config.phases)
786
+ fabric.print("Training Phases: ", phases)
787
+ total_phase_steps = sum(phase.num_steps for phase in phases)
788
+ total_phase = len(phases)
789
+
790
+ prev_phase_idx=-1
791
+ phase_step_accum = list(itertools.accumulate([phase.num_steps for phase in phases]))
792
+ pbar = tqdm(total=total_phase_steps, disable=not fabric.is_global_zero, dynamic_ncols=True, file=sys.stdout)
793
+ pbar.set_description(f"Total Steps {total_phase_steps}")
794
+ data_buffer = []
795
+ while global_step < total_phase_steps:
796
+ # Assign gradient and optimizer based on current phase and target
797
+ phase_idx, inner_idx, target, internel_step = get_phase(global_step, phases, phase_step_accum, config)
798
+ if not any(target):
799
+ phase_idx, inner_idx, target, internel_step = get_phase(global_step+1, phases, phase_step_accum, config)
800
+ if phase_idx != prev_phase_idx:
801
+ if config.lr_scheduler != 'none':
802
+ scheduler_enc = torch.optim.lr_scheduler.CosineAnnealingLR(opt_enc, T_max=phases[phase_idx].num_steps/len(phases[phase_idx].internal_steps),eta_min=config.lr_enc_min) if config.train_ae else None
803
+ scheduler_dec = torch.optim.lr_scheduler.CosineAnnealingLR(opt_dec, T_max=phases[phase_idx].num_steps/len(phases[phase_idx].internal_steps),eta_min=config.lr_dec_min) if config.train_ae else None
804
+ scheduler_ar = torch.optim.lr_scheduler.CosineAnnealingLR(opt_ar, T_max=phases[phase_idx].num_steps/len(phases[phase_idx].internal_steps),eta_min=config.lr_ar_min) if config.train_ar else None
805
+ scheduler_gan_loss = torch.optim.lr_scheduler.CosineAnnealingLR(opt_gan_loss, T_max=phases[phase_idx].num_steps/len(phases[phase_idx].internal_steps),eta_min=config.lr_gan_loss_min) if config.use_gan_loss else None
806
+ else:
807
+ scheduler_enc = None
808
+ scheduler_dec = None
809
+ scheduler_ar = None
810
+ scheduler_gan_loss = None
811
+
812
+
813
+ enc_grad = target.DO_AE or target.DO_PRIOR_ENC
814
+ dec_grad = target.DO_AE
815
+ prior_grad = target.DO_PRIOR_AR
816
+ disc_grad = target.DO_GAN_D
817
+ toogle_require_grad(enc, enc_grad)
818
+ toogle_require_grad(dec, dec_grad)
819
+ toogle_require_grad(ar_model, prior_grad)
820
+ toogle_require_grad(gan_loss, disc_grad)
821
+ opts = []
822
+ if enc_grad:
823
+ opts.append((enc, opt_enc, scheduler_enc))
824
+ if dec_grad:
825
+ opts.append((dec, opt_dec, scheduler_dec))
826
+ if prior_grad:
827
+ opts.append((ar_model, opt_ar, scheduler_ar))
828
+ if disc_grad:
829
+ opts.append((gan_loss, opt_gan_loss, scheduler_gan_loss))
830
+
831
+ # input
832
+ # Clear buffer at start of cycle
833
+ if internel_step == 0:
834
+ data_buffer = []
835
+
836
+ if inner_idx == 0:
837
+ batch = next(train_loader)
838
+ data_buffer.append(batch)
839
+ else:
840
+ if len(data_buffer) > 0:
841
+ batch = data_buffer.pop(0)
842
+ else:
843
+ batch = next(train_loader)
844
+
845
+ imgs, labels = batch if isinstance(batch, (list, tuple)) and len(batch) == 2 else (batch, None)
846
+ imgs = img_unint8_to_norm(imgs)
847
+
848
+ # no labels or drop labels
849
+ uncond_labels = F.one_hot(torch.full((imgs.shape[0],), config.num_classes - 1, device=fabric.device, dtype=torch.long), num_classes=config.num_classes).float()
850
+
851
+ if labels is None or len(labels)==0 or not config.use_label:
852
+ labels = uncond_labels
853
+ elif config.label_drop_prob > 0:
854
+ labels = torch.where(torch.rand(imgs.shape[0], device=fabric.device) < config.label_drop_prob, uncond_labels, labels)
855
+
856
+ x = patchify(imgs, config.patch_size)
857
+
858
+
859
+ # forward, build need computation graph for gradient calculation
860
+ with torch.set_grad_enabled(enc_grad):
861
+ quant, idx, one_hot, vqloss = enc(x, labels, training=enc_grad)
862
+
863
+ if target.DO_AE:
864
+
865
+ # For Diffusion/FM decoder
866
+ t_fm,xt,noise,v = None,None,None,None
867
+ if config.use_noise_query:
868
+ t_fm = torch.rand((x.shape[0],), device=fabric.device)
869
+ xt, noise, v = make_noise_image_patch_query(x, t_fm, config.z_len)
870
+
871
+ # For Tail Dropout
872
+ attn_bias = None
873
+ if config.use_tail_dropout and global_step>config.tail_dropout_start:
874
+ if config.tied_timestep:
875
+ t_tail_drop = t_fm
876
+ else:
877
+ t_tail_drop = torch.rand((x.shape[0],), device=fabric.device)
878
+ no_drop_t = torch.ones_like(t_tail_drop)
879
+ t_tail_drop = torch.where(torch.rand_like(t_tail_drop)<=config.tail_dropout_p,t_tail_drop,no_drop_t)
880
+ attn_bias = build_taildrop_attn_mask(t=t_tail_drop,context_len=int(quant.shape[1]),query_len=config.x_len,
881
+ context_type=config.context_type,t2k=config.tail_dropout_t2k,
882
+ device=fabric.device,
883
+ diti_scheduler=diti_scheduler,
884
+ ) if config.use_tail_dropout else None
885
+
886
+ decoded = dec(quant, labels, attn_mask=attn_bias, query_tokens=xt, t=t_fm)
887
+
888
+ v_hat = decoded if config.l2_v_target else None
889
+ x_hat = xt - decoded * t_fm[:,None,None] if config.l2_v_target else decoded
890
+
891
+ imgs_hat = unpatchify(x_hat, config.image_size, config.patch_size)
892
+
893
+ l2_target = v if config.l2_v_target else x
894
+ l2_input = v_hat if config.l2_v_target else x_hat
895
+
896
+ if target.DO_PRIOR_ENC or target.DO_PRIOR_AR:
897
+ with torch.set_grad_enabled(prior_grad):
898
+ ar_logits = ar_model(idx,labels)
899
+
900
+ if target.DO_PRIOR_ENC:
901
+ with torch.no_grad():
902
+ ema_ar_logits = freeze_ar_model(idx,labels)
903
+ with torch.amp.autocast('cuda',enabled=False):
904
+ ar_logits_detach = ar_logits.detach().float()
905
+ temp_low = config.prior_ar_temp_low
906
+ temp_high = config.prior_ar_temp_high
907
+ steps = config.z_len
908
+ if config.prior_ar_temp_schedule == 'cosine':
909
+ t = torch.linspace(0, 1, steps, device=fabric.device)
910
+ temp = temp_low + 0.5 * (temp_high - temp_low) * (1 - torch.cos(math.pi * t))
911
+ elif config.prior_ar_temp_schedule == 'linear':
912
+ temp = torch.linspace(temp_low, temp_high, steps, device=fabric.device)
913
+ elif config.prior_ar_temp_schedule == 'constant':
914
+ temp = torch.full((steps,), temp_low, device=fabric.device)
915
+ else:
916
+ raise NotImplementedError(f"Unknown schedule: {config.prior_ar_temp_schedule}")
917
+
918
+ b, n = ema_ar_logits.shape[0], ema_ar_logits.shape[1]
919
+ temp = temp.view(1, n, 1).expand(b, n, 1)
920
+
921
+ ema_ar_logits = ema_ar_logits.detach().float() / temp
922
+ prior_ema_ar_loss = F.cross_entropy(ema_ar_logits.reshape(-1, ema_ar_logits.shape[-1]).float(), idx.reshape(-1))
923
+
924
+ ema_ar_log_probs= torch.log_softmax(ema_ar_logits, dim=-1)
925
+ ar_log_probs = torch.log_softmax(ar_logits_detach, dim=-1)
926
+ entropy_ar_log_probs = -(ar_log_probs.exp() * ar_log_probs).sum(dim=-1)
927
+ entropy_ema_ar_log_probs = -(ema_ar_log_probs.exp() * ema_ar_log_probs).sum(dim=-1)
928
+ log_diff = ar_log_probs - ema_ar_log_probs
929
+
930
+
931
+ # caculate the loss
932
+ l2_loss = config.l2_weight * F.mse_loss(l2_input, l2_target) if target.DO_L2 else 0.
933
+ l1_loss = config.l1_weight * F.l1_loss(x_hat, x) if target.DO_L1 else 0.
934
+ lpips_loss = config.lpips_weight * perceptual_loss(imgs_hat, imgs).mean() if target.DO_LPIPS else 0.
935
+ ae_loss = l2_loss + l1_loss + lpips_loss
936
+
937
+ gan_G_loss, gan_G_loss_dict = gan_loss(imgs_hat, imgs, global_step=global_step, loss='G') if target.DO_GAN_G else (0., {})
938
+ gan_D_loss, gan_D_loss_dict = gan_loss(imgs_hat, imgs, global_step=global_step, loss='D') if target.DO_GAN_D else (0., {})
939
+ gan_G_loss_weight = (config.gan_G_weight * min(1., gan_loss.calculate_adaptive_weight(ae_loss,gan_G_loss,dec.dec.out.weight))) if target.DO_GAN_G and config.disc_adaptive_weight else config.gan_G_weight
940
+ gan_G_loss = gan_G_loss * gan_G_loss_weight
941
+ gan_D_loss = gan_D_loss * config.gan_G_weight
942
+ correct_tokens = (ar_logits.argmax(dim=-1) == idx).detach().float().sum().item() if target.DO_PRIOR_AR else 0.
943
+ total_tokens = torch.tensor(idx.numel()).item() if target.DO_PRIOR_AR else 0.
944
+ correct_token_rate = correct_tokens / total_tokens if target.DO_PRIOR_AR else 0.
945
+ prior_enc_loss = config.prior_enc_weight * (one_hot * log_diff.detach()).mean() if target.DO_PRIOR_ENC else 0.
946
+ prior_ar_loss = F.cross_entropy(ar_logits.reshape(-1, ar_logits.shape[-1]).float(), idx.reshape(-1)) if target.DO_PRIOR_AR else 0.
947
+
948
+ loss = ae_loss + prior_enc_loss + prior_ar_loss + gan_G_loss + gan_D_loss + vqloss
949
+
950
+ # backward and optimization
951
+ fabric.backward(loss)
952
+
953
+ for model, opt, scheduler in opts:
954
+ if model is not None and opt is not None:
955
+ zero_nan_gradients(model)
956
+ if config.grad_clip>0:
957
+ fabric.clip_gradients(model, opt, max_norm=config.grad_clip)
958
+ opt.step()
959
+ opt.zero_grad(set_to_none=True)
960
+ if scheduler is not None:
961
+ scheduler.step()
962
+ if config.train_ae:
963
+ if enc_grad:
964
+ ema_update(enc, enc_ema, ae_ema_rate)
965
+ if dec_grad:
966
+ ema_update(dec, dec_ema, ae_ema_rate)
967
+ if config.use_gan_loss and disc_grad:
968
+ ema_update(gan_loss, gan_loss_ema, ae_ema_rate)
969
+ if config.train_ar and prior_grad:
970
+ ema_update(ar_model, ar_model_ema, ar_ema_rate)
971
+
972
+ #visualization
973
+ if (global_step + 1) % config.visualize_freq == 0:
974
+ grid = x[:config.visualize_img_num]
975
+ if not config.ema_reconstruction:
976
+ if config.train_ae:
977
+ toogle_train_eval(enc_raw,train=False)
978
+ toogle_train_eval(dec_raw,train=False)
979
+ eval_enc = enc_raw
980
+ eval_dec = dec_raw
981
+ else:
982
+ eval_enc = enc_ema_raw
983
+ eval_dec = dec_ema_raw
984
+ if not config.ema_sampling:
985
+ if config.train_ar:
986
+ toogle_train_eval(ar_model_raw,train=False)
987
+ eval_ar = ar_model_raw
988
+ else:
989
+ eval_ar = ar_model_ema_raw
990
+ with torch.no_grad():
991
+ noise_t = torch.ones((x.shape[0],), device=fabric.device) if config.use_noise_query else None
992
+ if config.train_ae:
993
+ noise_recon = torch.randn_like(x) if config.use_noise_query else None
994
+ if config.use_noise_query and config.diffusion_decoder:
995
+ all_recon_images,_ = euler_recon_loop(eval_enc, eval_dec, x, labels, config=config, diti_scheduler=diti_scheduler)
996
+ else:
997
+ decoded_recon,_ = reconstruction(eval_enc, eval_dec, x, labels, query_tokens=noise_recon, t=noise_t)
998
+ all_recon_images = decoded_recon if not config.l2_v_target else noise_recon-decoded_recon
999
+ grid = torch.cat([grid, all_recon_images[:config.visualize_img_num]], dim=0)
1000
+
1001
+ if config.train_ar:
1002
+ noise_sample = torch.randn_like(x) if config.use_noise_query else None
1003
+ if config.use_noise_query and config.diffusion_decoder:
1004
+ all_sample_images = euler_sample_loop(eval_enc, eval_dec, eval_ar, bz=x.shape[0],
1005
+ class_label=labels,temperature=config.temperature,
1006
+ topK=config.topK, topP=config.topP,
1007
+ cfg=config.cfg, cfg_schedule=config.cfg_schedule, cfg_power=config.cfg_power,
1008
+ cache_kv=config.cache_kv, config=config, diti_scheduler=diti_scheduler)
1009
+ else:
1010
+ decoded_sample = sampling(eval_enc, eval_dec, eval_ar, bz=x.shape[0],
1011
+ class_label=labels,temperature=config.temperature,
1012
+ topK=config.topK, topP=config.topP,
1013
+ cfg=config.cfg, cfg_schedule=config.cfg_schedule, cfg_power=config.cfg_power,
1014
+ cache_kv=config.cache_kv, config=config, t=noise_t ,query_tokens=noise_sample)
1015
+ all_sample_images = decoded_sample if not config.l2_v_target else noise_sample-decoded_sample
1016
+ grid = torch.cat([grid, all_sample_images[:config.visualize_img_num]], dim=0)
1017
+ grid = img_denormalize(unpatchify(grid, config.image_size, config.patch_size))
1018
+ grid = torchvision.utils.make_grid(grid, nrow=config.visualize_img_num, normalize=False)
1019
+ fabric.log_dict({"visualization":wandb.Image(grid),'global_step':global_step+1})
1020
+ if not config.ema_reconstruction and config.train_ae:
1021
+ toogle_train_eval(enc_raw,train=True)
1022
+ toogle_train_eval(dec_raw,train=True)
1023
+ if not config.ema_sampling and config.train_ar:
1024
+ toogle_train_eval(ar_model_raw,train=True)
1025
+
1026
+ # eval and save ckpt
1027
+ rFID = 0.0
1028
+ gFID = 0.0
1029
+ if eval_loader is not None and (global_step + 1) % config.eval_freq == 0:
1030
+
1031
+ sample_cached_path = os.path.join(save_dir, "sample_images.npz")
1032
+ recon_cached_path = os.path.join(save_dir, "recon_images.npz")
1033
+ gt_cache_path = config.eval_fid_ref_path
1034
+
1035
+ samples_buf = []
1036
+ recons_buf = []
1037
+ if not config.ema_reconstruction:
1038
+ if config.train_ae:
1039
+ toogle_train_eval(enc_raw,train=False)
1040
+ toogle_train_eval(dec_raw,train=False)
1041
+ eval_enc = enc_raw
1042
+ eval_dec = dec_raw
1043
+ else:
1044
+ eval_enc = enc_ema_raw
1045
+ eval_dec = dec_ema_raw
1046
+
1047
+ if not config.ema_sampling:
1048
+ if config.train_ar:
1049
+ toogle_train_eval(ar_model_raw, train=False)
1050
+ eval_ar = ar_model_raw
1051
+ else:
1052
+ eval_ar = ar_model_ema_raw
1053
+
1054
+
1055
+ with torch.no_grad():
1056
+
1057
+ fabric.barrier()
1058
+ for i, batch in enumerate(tqdm(eval_loader,disable=not fabric.is_global_zero,dynamic_ncols=True,file=sys.stdout,desc="Evaluating")):
1059
+ imgs, labels = batch if isinstance(batch, (list, tuple)) and len(batch) == 2 else (batch, None)
1060
+ x = img_unint8_to_norm(imgs)
1061
+ x = patchify(x, config.patch_size)
1062
+ uncond_labels = F.one_hot(torch.full((imgs.shape[0],), config.num_classes - 1, device=fabric.device, dtype=torch.long), num_classes=config.num_classes).float()
1063
+ noise_t = torch.ones((labels.shape[0],), device=fabric.device) if config.use_noise_query else None
1064
+
1065
+ if labels is None or len(labels)==0 or not config.use_label:
1066
+ labels = uncond_labels
1067
+ idx = None
1068
+ noise_recon = torch.randn_like(x) if config.use_noise_query else None
1069
+
1070
+ if config.train_ae:
1071
+ if config.use_noise_query and config.diffusion_decoder:
1072
+ recon_images, idx = euler_recon_loop(eval_enc, eval_dec, x, labels, config=config, diti_scheduler=diti_scheduler)
1073
+ else:
1074
+ decoded_recon, idx = reconstruction(eval_enc, eval_dec, x, labels, query_tokens=noise_recon,t = noise_t)
1075
+ recon_images = decoded_recon if not config.l2_v_target else noise_recon-decoded_recon
1076
+
1077
+ recon_images = img_norm_to_uint8(unpatchify(recon_images, config.image_size, config.patch_size))
1078
+ if fabric.world_size>1:
1079
+ recon_images = fabric.all_gather(recon_images)
1080
+ recon_images = recon_images.flatten(0,1)
1081
+ recons_buf.append(recon_images.permute(0,2,3,1).cpu().numpy())
1082
+ if config.train_ar:
1083
+ if config.use_noise_query and config.diffusion_decoder:
1084
+ sample_images = euler_sample_loop(eval_enc, eval_dec, eval_ar, bz=x.shape[0],
1085
+ class_label=labels,temperature=config.temperature,
1086
+ topK=config.topK, topP=config.topP,
1087
+ cfg=config.cfg, cfg_schedule=config.cfg_schedule, cfg_power=config.cfg_power,
1088
+ cache_kv=config.cache_kv, config=config, diti_scheduler=diti_scheduler)
1089
+ else:
1090
+ noise_sample = torch.randn_like(x) if config.use_noise_query else None
1091
+ decoded_sample = sampling(eval_enc, eval_dec, eval_ar,
1092
+ bz=x.shape[0], class_label=labels,temperature=config.temperature,
1093
+ topK=config.topK, topP=config.topP, cfg=config.cfg, cfg_schedule=config.cfg_schedule,
1094
+ cfg_power=config.cfg_power, cache_kv=config.cache_kv, config=config,t=noise_t,query_tokens=noise_sample)
1095
+ sample_images = decoded_sample if not config.l2_v_target else noise_sample-decoded_sample
1096
+
1097
+ sample_images = img_norm_to_uint8(unpatchify(sample_images, config.image_size, config.patch_size))
1098
+ if fabric.world_size>1:
1099
+ sample_images = fabric.all_gather(sample_images)
1100
+ sample_images = sample_images.flatten(0,1)
1101
+ samples_buf.append(sample_images.permute(0,2,3,1).cpu().numpy())
1102
+
1103
+
1104
+ fabric.barrier()
1105
+ if fabric.is_global_zero:
1106
+ if config.train_ar:
1107
+ np.savez_compressed(sample_cached_path, np.concatenate(samples_buf,axis=0))
1108
+ if config.train_ae:
1109
+ np.savez_compressed(recon_cached_path, np.concatenate(recons_buf,axis=0))
1110
+
1111
+ if config.train_ar:
1112
+ gFID = adm_fid_evaluator(sample_cached_path, gt_cache_path, config, fabric)
1113
+ if config.train_ae:
1114
+ rFID = adm_fid_evaluator(recon_cached_path, gt_cache_path, config, fabric)
1115
+ fabric.barrier()
1116
+
1117
+
1118
+ if not config.ema_reconstruction and config.train_ae:
1119
+ toogle_train_eval(enc_raw,train=True)
1120
+ toogle_train_eval(dec_raw,train=True)
1121
+ if not config.ema_sampling and config.train_ar:
1122
+ toogle_train_eval(ar_model_raw,train=True)
1123
+
1124
+ # metrics
1125
+ if (global_step +1) % config.log_freq == 0:
1126
+ # current learning rates
1127
+ lr_enc = opt_enc.param_groups[0]['lr'] if opt_enc is not None else 0.
1128
+ lr_dec = opt_dec.param_groups[0]['lr'] if opt_dec is not None else 0.
1129
+ lr_ar = opt_ar.param_groups[0]['lr'] if opt_ar is not None else 0.
1130
+ lr_gan_loss = opt_gan_loss.param_groups[0]['lr'] if opt_gan_loss is not None else 0.
1131
+ metrics = {
1132
+ 'Phase':phase_idx,
1133
+ 'ae_loss/l1_loss':l1_loss,
1134
+ 'ae_loss/l2_loss':l2_loss,
1135
+ 'ae_loss/lpips_loss':lpips_loss,
1136
+ 'ae_loss/loss':ae_loss,
1137
+ 'ae_loss/vqloss': vqloss,
1138
+ 'ae_loss/gan_G_loss':gan_G_loss,
1139
+ 'ae_loss/gan_D_loss':gan_D_loss,
1140
+ 'prior_loss/logq-logp':log_diff.mean() if target.DO_PRIOR_ENC else 0.,
1141
+ 'prior_loss/logq':ar_log_probs.mean() if target.DO_PRIOR_ENC else 0.,
1142
+ 'prior_loss/logp':ema_ar_log_probs.mean() if target.DO_PRIOR_ENC else 0.,
1143
+ 'prior_loss/entropy_q':entropy_ar_log_probs.mean() if target.DO_PRIOR_ENC else 0.,
1144
+ 'prior_loss/entropy_p':entropy_ema_ar_log_probs.mean() if target.DO_PRIOR_ENC else 0.,
1145
+ 'prior_loss/ar_loss':prior_ar_loss,
1146
+ 'prior_loss/ema_ar_loss':prior_ema_ar_loss if target.DO_PRIOR_ENC else 0.,
1147
+ 'prior_loss/correct_token_rate': correct_token_rate,
1148
+ 'FID/rFID':rFID,
1149
+ 'FID/gFID':gFID,
1150
+ 'lr/lr_enc': lr_enc,
1151
+ 'lr/lr_dec': lr_dec,
1152
+ 'lr/lr_ar': lr_ar,
1153
+ 'lr/lr_gan_loss': lr_gan_loss,
1154
+ }
1155
+ gan_loss_dict = {**gan_G_loss_dict, **gan_D_loss_dict, 'gan_G_loss_weight':gan_G_loss_weight}
1156
+ metrics.update(gan_loss_dict)
1157
+ metrics = {k: fabric.all_reduce(v).item() if isinstance(v, torch.Tensor) else v for k, v in metrics.items() }
1158
+ metrics_logger = {k: v for k, v in metrics.items() if v!=0. }
1159
+ metrics_4f = {k: f"{v:.4f}" if k!='Phase' else int(v) for k, v in metrics.items() }
1160
+ fabric.log_dict(metrics_logger,step=global_step+1)
1161
+ pbar.set_postfix(metrics_4f)
1162
+ pbar.update(config.log_freq)
1163
+
1164
+ if config.save_ckpt and (global_step==0 or (global_step+1) % config.ckpt_freq == 0):
1165
+ ckpt_name = f'Phase={phase_idx}-Step={global_step+1}-rFID={rFID:.4f}-gFID={gFID:.4f}' if rFID is not None and gFID is not None else "_".join([f'{k}={v:.4f}' for k, v in metrics.items()])
1166
+ ckpt_path = f'{save_dir}/ckpts/{ckpt_name}.ckpt'
1167
+ fabric.save(ckpt_path, {'enc':enc,
1168
+ 'ar_model':ar_model,
1169
+ 'freeze_ar_model': freeze_ar_model,
1170
+ 'dec':dec,
1171
+ 'gan_loss':gan_loss,
1172
+ 'enc_ema':enc_ema,
1173
+ 'dec_ema':dec_ema,
1174
+ 'ar_model_ema':ar_model_ema,
1175
+ 'opt_enc':opt_enc,
1176
+ 'opt_ar':opt_ar,
1177
+ 'opt_dec':opt_dec,
1178
+ 'opt_gan_loss':opt_gan_loss})
1179
+ global_step += 1
1180
+ prev_phase_idx = phase_idx
1181
+ if config.save_ckpt:
1182
+ ckpt_path = f'{save_dir}/ckpts/last.ckpt'
1183
+ fabric.save(ckpt_path, {'enc':enc,
1184
+ 'ar_model':ar_model,
1185
+ 'dec':dec,
1186
+ 'gan_loss':gan_loss,
1187
+ 'freeze_ar_model':freeze_ar_model,
1188
+ 'enc_ema':enc_ema,
1189
+ 'dec_ema':dec_ema,
1190
+ 'ar_model_ema':ar_model_ema,
1191
+ 'opt_enc':opt_enc,
1192
+ 'opt_ar':opt_ar,
1193
+ 'opt_dec':opt_dec,
1194
+ 'opt_gan_loss':opt_gan_loss})
1195
+
1196
+ logger.finalize("Training completed")
1197
+
1198
+ if __name__ == "__main__":
1199
+ OmegaConf.register_new_resolver("eval", eval)
1200
+ cli_conf = OmegaConf.from_cli()
1201
+
1202
+ config_path = cli_conf.get("--config")
1203
+ conf = OmegaConf.load(config_path)
1204
+ if cli_conf is not None:
1205
+ for k, v in cli_conf.items():
1206
+ OmegaConf.update(conf, k, v)
1207
+
1208
+ train(conf)
1209
+