LogicGoInfotechSpaces commited on
Commit
95b1715
·
1 Parent(s): cd11413

Bundle StyleFeatureEditor code packages in Space to fix ModuleNotFoundError

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. CelebAMask-HQ-attribute-anno.txt +0 -0
  2. arguments/inference_arguments.py +80 -0
  3. arguments/training_arguments.py +119 -0
  4. available_directions.txt +63 -0
  5. configs/fse_editor_train.yaml +30 -0
  6. configs/fse_inference.yaml +15 -0
  7. configs/fse_inverter_inference.yaml +23 -0
  8. configs/fse_inverter_train.yaml +30 -0
  9. configs/paths.py +28 -0
  10. configs/simple_inference.yaml +12 -0
  11. criteria/__init__.py +0 -0
  12. criteria/id_loss.py +38 -0
  13. criteria/id_vit_loss.py +453 -0
  14. criteria/lpips/__init__.py +0 -0
  15. criteria/lpips/lpips.py +35 -0
  16. criteria/lpips/networks.py +98 -0
  17. criteria/lpips/utils.py +33 -0
  18. criteria/moco_loss.py +62 -0
  19. criteria/ms_ssim.py +180 -0
  20. criteria/resnet.py +100 -0
  21. criteria/w_norm.py +13 -0
  22. datasets/datasets.py +136 -0
  23. datasets/loaders.py +36 -0
  24. datasets/transforms.py +55 -0
  25. dnnlib/__init__.py +12 -0
  26. dnnlib/tflib/__init__.py +18 -0
  27. dnnlib/tflib/autosummary.py +191 -0
  28. dnnlib/tflib/custom_ops.py +169 -0
  29. dnnlib/tflib/network.py +590 -0
  30. dnnlib/tflib/ops/__init__.py +7 -0
  31. dnnlib/tflib/ops/fused_bias_act.cu +188 -0
  32. dnnlib/tflib/ops/fused_bias_act.py +196 -0
  33. dnnlib/tflib/ops/upfirdn_2d.cu +326 -0
  34. dnnlib/tflib/ops/upfirdn_2d.py +364 -0
  35. dnnlib/tflib/optimizer.py +370 -0
  36. dnnlib/tflib/tfutil.py +252 -0
  37. dnnlib/util.py +515 -0
  38. editings/bound/Eyeglasses_boundary.npy +3 -0
  39. editings/bound/Heavy_Makeup_boundary.npy +3 -0
  40. editings/bound/Smiling_boundary.npy +3 -0
  41. editings/deltaedit/delta_mapper.py +73 -0
  42. editings/deltaedit/editor.py +59 -0
  43. editings/deltaedit/map_tool.py +109 -0
  44. editings/ganspace.py +32 -0
  45. editings/ganspace_pca/cars_pca.pt +3 -0
  46. editings/ganspace_pca/church_pca.pt +3 -0
  47. editings/ganspace_pca/ffhq_pca.pt +3 -0
  48. editings/interfacegan_directions/age.pt +3 -0
  49. editings/interfacegan_directions/rotation.pt +3 -0
  50. editings/interfacegan_directions/smile.pt +3 -0
CelebAMask-HQ-attribute-anno.txt ADDED
The diff for this file is too large to render. See raw diff
 
arguments/inference_arguments.py ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+
3
+ from pathlib import Path
4
+ from typing import Optional, List, Tuple, Dict
5
+ from dataclasses import dataclass, field
6
+ from omegaconf import OmegaConf, MISSING
7
+ from utils.class_registry import ClassRegistry
8
+ from models.methods import methods_registry
9
+ from metrics.metrics import metrics_registry
10
+
11
+
12
+
13
+ args = ClassRegistry()
14
+
15
+
16
+ @args.add_to_registry("exp")
17
+ @dataclass
18
+ class ExperimentArgs:
19
+ config_dir: str = str(Path(__file__).resolve().parent / "configs")
20
+ config: str = MISSING
21
+ output_dir: str = "results_dir"
22
+ seed: int = 1
23
+ root: str = os.getenv("EXP_ROOT", ".")
24
+ domain: str = "human_faces"
25
+ wandb: bool = False
26
+
27
+
28
+ @args.add_to_registry("data")
29
+ @dataclass
30
+ class DataArgs:
31
+ inference_dir: str = ""
32
+ transform: str = "face_1024"
33
+
34
+
35
+ @args.add_to_registry("inference")
36
+ @dataclass
37
+ class InferenceArgs:
38
+ inference_runner: str = "base_inference_runner"
39
+ editings_data: Dict = field(default_factory=lambda: {})
40
+
41
+
42
+ @args.add_to_registry("model")
43
+ @dataclass
44
+ class ModelArgs:
45
+ method: str = "fse_full"
46
+ device: str = "0"
47
+ batch_size: int = 4
48
+ workers: int = 4
49
+ checkpoint_path: str = ""
50
+
51
+
52
+
53
+ MethodsArgs = methods_registry.make_dataclass_from_args("MethodsArgs")
54
+ args.add_to_registry("methods_args")(MethodsArgs)
55
+
56
+ MetricsArgs = metrics_registry.make_dataclass_from_args("MetricsArgs")
57
+ args.add_to_registry("metrics")(MetricsArgs)
58
+
59
+
60
+
61
+ Args = args.make_dataclass_from_classes("Args")
62
+
63
+
64
+ def load_config():
65
+ config = OmegaConf.structured(Args)
66
+
67
+ conf_cli = OmegaConf.from_cli()
68
+ config.exp.config = conf_cli.exp.config
69
+ config.exp.config_dir = conf_cli.exp.config_dir
70
+
71
+ config_path = os.path.join(config.exp.config_dir, config.exp.config)
72
+ conf_file = OmegaConf.load(config_path)
73
+ config = OmegaConf.merge(config, conf_file)
74
+ for method in list(config.methods_args.keys()):
75
+ if method != config.model.method:
76
+ config.methods_args.__delattr__(method)
77
+
78
+ config = OmegaConf.merge(config, conf_cli)
79
+
80
+ return config
arguments/training_arguments.py ADDED
@@ -0,0 +1,119 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from training.losses import disc_losses
3
+ from training.optimizers import optimizers
4
+ from pathlib import Path
5
+ from typing import Optional, List, Tuple, Dict
6
+ from dataclasses import dataclass, field
7
+ from omegaconf import OmegaConf, MISSING
8
+ from utils.class_registry import ClassRegistry
9
+ from models.methods import methods_registry
10
+ from metrics.metrics import metrics_registry
11
+
12
+
13
+ args = ClassRegistry()
14
+
15
+
16
+ @args.add_to_registry("exp")
17
+ @dataclass
18
+ class ExperimentArgs:
19
+ config_dir: str = str(Path(__file__).resolve().parent / "configs")
20
+ config: str = MISSING
21
+ exp_dir: str = "experiments"
22
+ name: str = MISSING
23
+ seed: int = 1
24
+ root: str = os.getenv("EXP_ROOT", ".")
25
+ wandb: bool = True
26
+ wandb_project: str = "sfe"
27
+ domain: str = "human_faces"
28
+
29
+
30
+ @args.add_to_registry("data")
31
+ @dataclass
32
+ class DataArgs:
33
+ special_dir: str = MISSING
34
+ transform: str = "face_1024"
35
+ input_train_dir: str = MISSING
36
+ input_val_dir: str = MISSING
37
+
38
+
39
+ @args.add_to_registry("train")
40
+ @dataclass
41
+ class TrainingArgs:
42
+ train_runner: str = "base_training_runner"
43
+ encoder_optimizer: str = "ranger"
44
+ disc_optimizer: str = "adam"
45
+ resume_path: str = ""
46
+ val_metrics: List[str] = field(
47
+ default_factory=lambda: ["msssim", "lpips", "l2", "fid"]
48
+ )
49
+ start_step: int = 0
50
+ steps: int = 300000
51
+ log_step: int = 500
52
+ checkpoint_step: int = 15000
53
+ val_step: int = 15000
54
+ train_dis: bool = False
55
+ dis_train_start_step: int = 150000
56
+ bs_used_before_adv_loss: int = 8
57
+ disc_edits: List[str] = field(
58
+ default_factory=lambda: []
59
+ )
60
+
61
+ @args.add_to_registry("model")
62
+ @dataclass
63
+ class ModelArgs:
64
+ method: str = "fse_full"
65
+ device: str = "0"
66
+ batch_size: int = 4
67
+ workers: int = 4
68
+ checkpoint_path: str = ""
69
+
70
+
71
+ @args.add_to_registry("encoder_losses")
72
+ @dataclass
73
+ class EncoderLossesArgs:
74
+ l2: float = 0.0
75
+ lpips: float = 0.0
76
+ lpips_scale: float = 0.0
77
+ id: float = 0.0
78
+ moco: float = 0.0
79
+ adv: float = 0.0
80
+ feat_rec: float = 0.0
81
+ feat_rec_l1: float = 0.0
82
+ l2_latent: float = 0.0
83
+ id_vit: float = 0.0
84
+
85
+
86
+ MethodsArgs = methods_registry.make_dataclass_from_args("MethodsArgs")
87
+ args.add_to_registry("methods_args")(MethodsArgs)
88
+
89
+ DiscLossesArgs = disc_losses.make_dataclass_from_args("DiscLossesArgs")
90
+ args.add_to_registry("disc_losses")(DiscLossesArgs)
91
+
92
+ OptimizersArgs = optimizers.make_dataclass_from_args("OptimizersArgs")
93
+ args.add_to_registry("optimizers")(OptimizersArgs)
94
+
95
+ MetricsArgs = metrics_registry.make_dataclass_from_args("MetricsArgs")
96
+ args.add_to_registry("metrics")(MetricsArgs)
97
+
98
+
99
+ Args = args.make_dataclass_from_classes("Args")
100
+
101
+
102
+ def load_config():
103
+ config = OmegaConf.structured(Args)
104
+
105
+ conf_cli = OmegaConf.from_cli()
106
+ config.exp.config = conf_cli.exp.config
107
+ config.exp.config_dir = conf_cli.exp.config_dir
108
+
109
+ config_path = os.path.join(config.exp.config_dir, config.exp.config)
110
+ conf_file = OmegaConf.load(config_path)
111
+ config = OmegaConf.merge(config, conf_file)
112
+
113
+ for method in list(config.methods_args.keys()):
114
+ if method != config.model.method:
115
+ config.methods_args.__delattr__(method)
116
+
117
+ config = OmegaConf.merge(config, conf_cli)
118
+
119
+ return config
available_directions.txt ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ direction method\name | direction effect | approximate direction | additional comments
2
+ | during positive edit | power range to |
3
+ | (during negative edit | editing works well |
4
+ | effect is reversed) | (found empirically) |
5
+
6
+
7
+ fs_directions:
8
+ fs_glasses | add glasses | [-20; 30] | may open mouth
9
+ fs_smiling | add smile | [-10; 10]
10
+ fs_makeup | add make-up | [-10; 15] | bad works with men
11
+
12
+ ganspace_directions:
13
+ eye_openness | close eyes | [-30; 45]
14
+ trimmed_beard | remove beard | [-30; 30] | bad works with women
15
+ lipstick | add lipstick | [-30; 30] | bad works with men
16
+ face_roundness | make face rounder | [-20; 15]
17
+ nose_length | decreace nose length | [-30; 30] | may open mouth
18
+ eyebrow_thickness | decreace eyebrow | [-20; 20]
19
+ | thickness |
20
+ displeased | add sadness | [-10; 10]
21
+
22
+ interfacegan_directions:
23
+ age | increase age | [-10; 10]
24
+ smile | add smile | [-5 ; 5 ] | may cause hair artefacts, better use fs_smiling
25
+ rotation | turn face right | [-7 ; 7 ]
26
+
27
+ styleclip_directions:
28
+ afro | afro hairstyle | [0; 0.14]
29
+ angry | make angrier | [0; 0.14] | cause background artefacts
30
+ bobcut | bobcut hairstyle | [0; 0.18] | cause background artefacts
31
+ bowlcut | bowlcut h. style | [0; 0.14] | cause background artefacts
32
+ mohawk | mohawk hairstyle | [0; 0.10] | cause background artefacts
33
+ curly_hair | add curls | [0; 0.12]
34
+ purple_hair | dye hair purple | [0; 0.12]
35
+ surprised | make more surprised | [0; 0.10]
36
+ beyonce | make similar to ... | [0; 0.12]
37
+ hilary_clinton | make similar to ... | [0; 0.10]
38
+ depp | make similar to ... | [0; 0.12]
39
+ taylor_swift | make similar to ... | [0; 0.10]
40
+ trump | make similar to ... | [0; 0.10]
41
+ zuckerberg | make similar to ... | [0; 0.10]
42
+
43
+ stylespace_directions:
44
+ black hair | darken hair | [-7; 10]
45
+ blond hair | darken hair | [-10; 7] | yes, positive power darkens hair
46
+ grey hair | make hair colored | [-7 ; 7] | negative means make hair grey
47
+ wavy hair | add hair length | [-7 ; 7]
48
+ receding hairline | remove bald | [-10; 10]
49
+ smiling | remove smile | [-10; 10] | better use fs_smiling
50
+ sideburns | remove sideburns | [-7 ; 7] | bad works with women
51
+ goatee | remove goatee | [-7 ; 7] | bad works with women
52
+ earrings | add earrings | [0 ; 15] | bad works with men
53
+ glasses | remove glasses | [-10; 10]
54
+ gender | add femininity | [-10; 7] | better use global mapper
55
+
56
+ You can alse use directions from text prompts via StyleClip Global Mapper (https://arxiv.org/abs/2103.17249).
57
+ Such directions look as follows: "styleclip_global_{neutral prompt}_{target prompt}_{disentanglement}" where
58
+ neutral prompt -- some neutral description of the original image (e.g. "a face")
59
+ target prompt -- text that contains the desired edit (e.g. "a smilling face")
60
+ disentanglement -- positive number, the more this attribute - the more related attributes will also be changed (e.g.
61
+ for grey hair editing, wrinkle, skin colour and glasses may also be edited)
62
+
63
+ Example: "styleclip_global_a face_a face with black hair_0.18"
configs/fse_editor_train.yaml ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ train:
2
+ train_runner: fse_editor
3
+ train_dis: True
4
+ dis_train_start_step: 45000
5
+ steps: 500000
6
+
7
+
8
+ model:
9
+ method: fse_full
10
+ device: "0"
11
+ batch_size: 8
12
+
13
+
14
+ encoder_losses:
15
+ l2: 1.0
16
+ lpips: 0.8
17
+ id: 0.1
18
+ adv: 0.01
19
+
20
+
21
+ disc_losses:
22
+ main:
23
+ coef: 1
24
+ r1:
25
+ coef: 10
26
+
27
+
28
+ optimizers:
29
+ ranger:
30
+ lr: 0.0001
configs/fse_inference.yaml ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ inference:
2
+ inference_runner: fse_inference_runner
3
+
4
+ editings_data: {
5
+ "age": [ 2, 3, 4, 5, 6, 7, 8],
6
+ "fs_glasses": [5, 10, 15, 20, 25, 30],
7
+ "fs_smiling": [-10, -9, -8, -7, -6, -5, -4,-3],
8
+ "styleclip_global_face with hair_face with fire hair_0.10": [5, 9]
9
+ }
10
+
11
+
12
+ model:
13
+ method: fse_full
14
+ device: "0"
15
+ batch_size: 8
configs/fse_inverter_inference.yaml ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ inference:
2
+ inference_runner: fse_inverter_inference_runner
3
+ editings_data: {
4
+ "age": [-7, -5, -3, 3, 5, 7, 10],
5
+ "fs_makeup": [5, 10, 15],
6
+ "afro": [0.03, 0.07, 0.085, 0.1],
7
+ "angry": [0.07, 0.1, 0.12],
8
+ "purple_hair": [0.07, 0.1, 0.12],
9
+ "glasses": [-7, 5],
10
+ "face_roundness": [-17, -12, -8, 8, 12, 17],
11
+ "rotation": [-5.0, -3.0, -1.0, 1.0, 3.0, 5.0],
12
+ "bobcut": [0.07, 0.12, 0.18],
13
+ "bowlcut": [0.07, 0.14],
14
+ "mohawk": [0.07, 0.10],
15
+ "blond hair ": [-8, -4, 4, 8],
16
+ "fs_smiling": [-9, -6, -3, 3, 6, 9]
17
+ }
18
+
19
+
20
+ model:
21
+ method: fse_inverter
22
+ device: "0"
23
+ batch_size: 8
configs/fse_inverter_train.yaml ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ train:
2
+ train_runner: fse_inverter
3
+ train_dis: True
4
+ dis_train_start_step: 45000
5
+
6
+
7
+ model:
8
+ method: fse_inverter
9
+ device: "0"
10
+ batch_size: 8
11
+
12
+
13
+ encoder_losses:
14
+ l2: 1.0
15
+ lpips: 0.8
16
+ id: 0.1
17
+ adv: 0.01
18
+ feat_rec: 0.01
19
+
20
+
21
+ disc_losses:
22
+ main:
23
+ coef: 1
24
+ r1:
25
+ coef: 10
26
+
27
+
28
+ optimizers:
29
+ ranger:
30
+ lr: 0.0001
configs/paths.py ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from dataclasses import dataclass, asdict, fields
2
+
3
+
4
+ models_dir = "pretrained_models/"
5
+
6
+
7
+ @dataclass
8
+ class DefaultPathsClass:
9
+ psp_path: str = models_dir + "psp_ffhq_encode.pt"
10
+ e4e_path: str = models_dir + "e4e_ffhq_encode.pt"
11
+ farl_path:str = models_dir + "face_parsing.farl.lapa.main_ema_136500_jit191.pt"
12
+ mobile_net_pth: str = models_dir + "mobilenet0.25_Final.pth"
13
+ ir_se50_path: str = models_dir + "model_ir_se50.pth"
14
+ stylegan_weights: str = models_dir + "stylegan2-ffhq-config-f.pt"
15
+ stylegan_car_weights: str = models_dir + "stylegan2-car-config-f-new.pkl"
16
+ stylegan_weights_pkl: str = models_dir + "stylegan2-ffhq-config-f.pkl"
17
+ arcface_model_path: str = models_dir + "iresnet50-7f187506.pth"
18
+ moco: str = models_dir + "moco_v2_800ep_pretrain.pt"
19
+ curricular_face_path: str = models_dir + "CurricularFace_Backbone.pth"
20
+ mtcnn: str = models_dir + "mtcnn"
21
+ landmark: str = models_dir + "79999_iter.pth"
22
+
23
+ def __iter__(self):
24
+ for field in fields(self):
25
+ yield field.name, getattr(self, field.name)
26
+
27
+
28
+ DefaultPaths = DefaultPathsClass()
configs/simple_inference.yaml ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ exp:
2
+ domain: human_faces
3
+
4
+ model:
5
+ method: fse_full
6
+ device: "0"
7
+ batch_size: 1
8
+ workers: 1
9
+
10
+ methods_args:
11
+ fse_full:
12
+
criteria/__init__.py ADDED
File without changes
criteria/id_loss.py ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from torch import nn
3
+ from configs.paths import DefaultPaths
4
+ from models.psp.encoders.model_irse import Backbone
5
+
6
+
7
+ class IDLoss(nn.Module):
8
+ def __init__(self):
9
+ super(IDLoss, self).__init__()
10
+ print("Loading ResNet ArcFace")
11
+ self.facenet = Backbone(
12
+ input_size=112, num_layers=50, drop_ratio=0.6, mode="ir_se"
13
+ )
14
+ self.facenet.load_state_dict(torch.load(DefaultPaths.ir_se50_path))
15
+ self.face_pool = torch.nn.AdaptiveAvgPool2d((112, 112))
16
+ self.facenet = self.facenet.cuda().eval()
17
+
18
+ def extract_feats(self, x):
19
+ x = x[:, :, 35:223, 32:220] # Crop interesting region
20
+ x = self.face_pool(x)
21
+ x_feats = self.facenet(x.cuda())
22
+ return x_feats
23
+
24
+
25
+ def forward(self, y_hat, y):
26
+ n_samples = y.shape[0]
27
+ y_feats = self.extract_feats(y)
28
+ y_hat_feats = self.extract_feats(y_hat)
29
+
30
+ y_feats = y_feats.detach()
31
+ loss = 0
32
+ count = 0
33
+ for i in range(n_samples):
34
+ diff_target = y_hat_feats[i].dot(y_feats[i])
35
+ loss += 1 - diff_target
36
+ count += 1
37
+
38
+ return loss / count
criteria/id_vit_loss.py ADDED
@@ -0,0 +1,453 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from torch import nn
3
+ from configs.paths import DefaultPaths
4
+ from models.psp.encoders.model_irse import Backbone
5
+
6
+
7
+
8
+ import torch
9
+ import torch.nn.functional as F
10
+ from einops import rearrange, repeat
11
+ from torch import nn
12
+
13
+ from torch.nn import Parameter
14
+ #from IPython import embed
15
+
16
+ MIN_NUM_PATCHES = 16
17
+
18
+ class Softmax(nn.Module):
19
+ r"""Implement of Softmax (normal classification head):
20
+ Args:
21
+ in_features: size of each input sample
22
+ out_features: size of each output sample
23
+ device_id: the ID of GPU where the model will be trained by model parallel.
24
+ if device_id=None, it will be trained on CPU without model parallel.
25
+ """
26
+ def __init__(self, in_features, out_features, device_id):
27
+ super(Softmax, self).__init__()
28
+ self.in_features = in_features
29
+ self.out_features = out_features
30
+ self.device_id = device_id
31
+
32
+ self.weight = Parameter(torch.FloatTensor(out_features, in_features))
33
+ self.bias = Parameter(torch.FloatTensor(out_features))
34
+ nn.init.xavier_uniform_(self.weight)
35
+ nn.init.zeros_(self.bias)
36
+
37
+ def forward(self, input, label):
38
+ if self.device_id == None:
39
+ out = F.linear(x, self.weight, self.bias)
40
+ else:
41
+ x = input
42
+ sub_weights = torch.chunk(self.weight, len(self.device_id), dim=0)
43
+ sub_biases = torch.chunk(self.bias, len(self.device_id), dim=0)
44
+ temp_x = x.cuda(self.device_id[0])
45
+ weight = sub_weights[0].cuda(self.device_id[0])
46
+ bias = sub_biases[0].cuda(self.device_id[0])
47
+ out = F.linear(temp_x, weight, bias)
48
+ for i in range(1, len(self.device_id)):
49
+ temp_x = x.cuda(self.device_id[i])
50
+ weight = sub_weights[i].cuda(self.device_id[i])
51
+ bias = sub_biases[i].cuda(self.device_id[i])
52
+ out = torch.cat((out, F.linear(temp_x, weight, bias).cuda(self.device_id[0])), dim=1)
53
+ return out
54
+
55
+ def _initialize_weights(self):
56
+ for m in self.modules():
57
+ if isinstance(m, nn.Conv2d):
58
+ nn.init.xavier_uniform_(m.weight.data)
59
+ if m.bias is not None:
60
+ m.bias.data.zero_()
61
+ elif isinstance(m, nn.BatchNorm2d):
62
+ m.weight.data.fill_(1)
63
+ m.bias.data.zero_()
64
+ elif isinstance(m, nn.BatchNorm1d):
65
+ m.weight.data.fill_(1)
66
+ m.bias.data.zero_()
67
+ elif isinstance(m, nn.Linear):
68
+ nn.init.xavier_uniform_(m.weight.data)
69
+ if m.bias is not None:
70
+ m.bias.data.zero_()
71
+
72
+
73
+
74
+ class ArcFace(nn.Module):
75
+ r"""Implement of ArcFace (https://arxiv.org/pdf/1801.07698v1.pdf):
76
+ Args:
77
+ in_features: size of each input sample
78
+ out_features: size of each output sample
79
+ device_id: the ID of GPU where the model will be trained by model parallel.
80
+ if device_id=None, it will be trained on CPU without model parallel.
81
+ s: norm of input feature
82
+ m: margin
83
+ cos(theta+m)
84
+ """
85
+
86
+ def __init__(self, in_features, out_features, device_id, s=64.0, m=0.50, easy_margin=False):
87
+ super(ArcFace, self).__init__()
88
+ self.in_features = in_features
89
+ self.out_features = out_features
90
+ self.device_id = device_id
91
+
92
+ self.s = s
93
+ self.m = m
94
+
95
+ self.weight = Parameter(torch.FloatTensor(out_features, in_features))
96
+ nn.init.xavier_uniform_(self.weight)
97
+
98
+ self.easy_margin = easy_margin
99
+ self.cos_m = math.cos(m)
100
+ self.sin_m = math.sin(m)
101
+ self.th = math.cos(math.pi - m)
102
+ self.mm = math.sin(math.pi - m) * m
103
+
104
+ def forward(self, input, label):
105
+ # --------------------------- cos(theta) & phi(theta) ---------------------------
106
+ if self.device_id == None:
107
+ cosine = F.linear(F.normalize(input), F.normalize(self.weight))
108
+ else:
109
+ x = input
110
+ sub_weights = torch.chunk(self.weight, len(self.device_id), dim=0)
111
+ temp_x = x.cuda(self.device_id[0])
112
+ weight = sub_weights[0].cuda(self.device_id[0])
113
+ cosine = F.linear(F.normalize(temp_x), F.normalize(weight))
114
+ for i in range(1, len(self.device_id)):
115
+ temp_x = x.cuda(self.device_id[i])
116
+ weight = sub_weights[i].cuda(self.device_id[i])
117
+ cosine = torch.cat((cosine, F.linear(F.normalize(temp_x), F.normalize(weight)).cuda(self.device_id[0])),
118
+ dim=1)
119
+ sine = torch.sqrt(1.0 - torch.pow(cosine, 2))
120
+ phi = cosine * self.cos_m - sine * self.sin_m
121
+ if self.easy_margin:
122
+ phi = torch.where(cosine > 0, phi, cosine)
123
+ else:
124
+ phi = torch.where(cosine > self.th, phi, cosine - self.mm)
125
+ # --------------------------- convert label to one-hot ---------------------------
126
+ one_hot = torch.zeros(cosine.size())
127
+ if self.device_id != None:
128
+ one_hot = one_hot.cuda(self.device_id[0])
129
+ one_hot.scatter_(1, label.view(-1, 1).long(), 1)
130
+ # -------------torch.where(out_i = {x_i if condition_i else y_i) -------------
131
+ output = (one_hot * phi) + (
132
+ (1.0 - one_hot) * cosine) # you can use torch.where if your torch.__version__ is 0.4
133
+ output *= self.s
134
+
135
+ return output
136
+
137
+
138
+ class CosFace(nn.Module):
139
+ r"""Implement of CosFace (https://arxiv.org/pdf/1801.09414.pdf):
140
+ Args:
141
+ in_features: size of each input sample
142
+ out_features: size of each output sample
143
+ device_id: the ID of GPU where the model will be trained by model parallel.
144
+ if device_id=None, it will be trained on CPU without model parallel.
145
+ s: norm of input feature
146
+ m: margin
147
+ cos(theta)-m
148
+ """
149
+
150
+ def __init__(self, in_features, out_features, device_id, s=64.0, m=0.35):
151
+ super(CosFace, self).__init__()
152
+ self.in_features = in_features
153
+ self.out_features = out_features
154
+ self.device_id = device_id
155
+ self.s = s
156
+ self.m = m
157
+ print("self.device_id", self.device_id)
158
+ self.weight = Parameter(torch.FloatTensor(out_features, in_features))
159
+ nn.init.xavier_uniform_(self.weight)
160
+
161
+ def forward(self, input, label):
162
+ # --------------------------- cos(theta) & phi(theta) ---------------------------
163
+
164
+ if self.device_id == None:
165
+ cosine = F.linear(F.normalize(input), F.normalize(self.weight))
166
+ else:
167
+ x = input
168
+ sub_weights = torch.chunk(self.weight, len(self.device_id), dim=0)
169
+ temp_x = x.cuda(self.device_id[0])
170
+ weight = sub_weights[0].cuda(self.device_id[0])
171
+ cosine = F.linear(F.normalize(temp_x), F.normalize(weight))
172
+ for i in range(1, len(self.device_id)):
173
+ temp_x = x.cuda(self.device_id[i])
174
+ weight = sub_weights[i].cuda(self.device_id[i])
175
+ cosine = torch.cat((cosine, F.linear(F.normalize(temp_x), F.normalize(weight)).cuda(self.device_id[0])),
176
+ dim=1)
177
+ phi = cosine - self.m
178
+ # --------------------------- convert label to one-hot ---------------------------
179
+ one_hot = torch.zeros(cosine.size())
180
+ if self.device_id != None:
181
+ one_hot = one_hot.cuda(self.device_id[0])
182
+ # one_hot = one_hot.cuda() if cosine.is_cuda else one_hot
183
+
184
+ one_hot.scatter_(1, label.cuda(self.device_id[0]).view(-1, 1).long(), 1)
185
+ # -------------torch.where(out_i = {x_i if condition_i else y_i) -------------
186
+ output = (one_hot * phi) + (
187
+ (1.0 - one_hot) * cosine) # you can use torch.where if your torch.__version__ is 0.4
188
+ output *= self.s
189
+
190
+ return output
191
+
192
+ def __repr__(self):
193
+ return self.__class__.__name__ + '(' \
194
+ + 'in_features = ' + str(self.in_features) \
195
+ + ', out_features = ' + str(self.out_features) \
196
+ + ', s = ' + str(self.s) \
197
+ + ', m = ' + str(self.m) + ')'
198
+
199
+ class SFaceLoss(nn.Module):
200
+
201
+ def __init__(self, in_features, out_features, device_id, s = 64.0, k = 80.0, a = 0.90, b = 1.2):
202
+ super(SFaceLoss, self).__init__()
203
+ self.in_features = in_features
204
+ self.out_features = out_features
205
+ self.device_id = device_id
206
+ self.s = s
207
+ self.k = k
208
+ self.a = a
209
+ self.b = b
210
+ self.weight = Parameter(torch.FloatTensor(out_features, in_features))
211
+ #nn.init.xavier_uniform_(self.weight)
212
+ xavier_normal_(self.weight, gain=2, mode='out')
213
+
214
+ def forward(self, input, label):
215
+ # --------------------------- cos(theta) & phi(theta) ---------------------------
216
+ if self.device_id == None:
217
+ cosine = F.linear(F.normalize(input), F.normalize(self.weight))
218
+ else:
219
+ x = input
220
+ sub_weights = torch.chunk(self.weight, len(self.device_id), dim=0)
221
+ temp_x = x.cuda(self.device_id[0])
222
+ weight = sub_weights[0].cuda(self.device_id[0])
223
+ cosine = F.linear(F.normalize(temp_x), F.normalize(weight))
224
+
225
+ for i in range(1, len(self.device_id)):
226
+ temp_x = x.cuda(self.device_id[i])
227
+ weight = sub_weights[i].cuda(self.device_id[i])
228
+
229
+ cosine = torch.cat((cosine, F.linear(F.normalize(temp_x), F.normalize(weight)).cuda(self.device_id[0])), dim=1)
230
+ # --------------------------- s*cos(theta) ---------------------------
231
+ output = cosine * self.s
232
+ # --------------------------- sface loss ---------------------------
233
+
234
+ one_hot = torch.zeros(cosine.size())
235
+ if self.device_id != None:
236
+ one_hot = one_hot.cuda(self.device_id[0])
237
+ one_hot.scatter_(1, label.view(-1, 1), 1)
238
+
239
+ zero_hot = torch.ones(cosine.size())
240
+ if self.device_id != None:
241
+ zero_hot = zero_hot.cuda(self.device_id[0])
242
+ zero_hot.scatter_(1, label.view(-1, 1), 0)
243
+
244
+
245
+ WyiX = torch.sum(one_hot * output, 1)
246
+ with torch.no_grad():
247
+ theta_yi = torch.acos(WyiX / self.s)
248
+ weight_yi = 1.0 / (1.0 + torch.exp(-self.k * (theta_yi - self.a)))
249
+ intra_loss = - weight_yi * WyiX
250
+
251
+ Wj = zero_hot * output
252
+ with torch.no_grad():
253
+ # theta_j = torch.acos(Wj)
254
+ theta_j = torch.acos(Wj / self.s)
255
+ weight_j = 1.0 / (1.0 + torch.exp(self.k * (theta_j - self.b)))
256
+ inter_loss = torch.sum(weight_j * Wj, 1)
257
+
258
+ loss = intra_loss.mean() + inter_loss.mean()
259
+ Wyi_s = WyiX / self.s
260
+ Wj_s = Wj / self.s
261
+ return output, loss, intra_loss.mean(), inter_loss.mean(), Wyi_s.mean(), Wj_s.mean()
262
+
263
+
264
+
265
+ class Residual(nn.Module):
266
+ def __init__(self, fn):
267
+ super().__init__()
268
+ self.fn = fn
269
+ def forward(self, x, **kwargs):
270
+ return self.fn(x, **kwargs) + x
271
+
272
+ class PreNorm(nn.Module):
273
+ def __init__(self, dim, fn):
274
+ super().__init__()
275
+ self.norm = nn.LayerNorm(dim)
276
+ self.fn = fn
277
+ def forward(self, x, **kwargs):
278
+ return self.fn(self.norm(x), **kwargs)
279
+
280
+ class FeedForward(nn.Module):
281
+ def __init__(self, dim, hidden_dim, dropout = 0.):
282
+ super().__init__()
283
+ self.net = nn.Sequential(
284
+ nn.Linear(dim, hidden_dim),
285
+ nn.GELU(),
286
+ nn.Dropout(dropout),
287
+ nn.Linear(hidden_dim, dim),
288
+ nn.Dropout(dropout)
289
+ )
290
+ def forward(self, x):
291
+ return self.net(x)
292
+
293
+ class Attention(nn.Module):
294
+ def __init__(self, dim, heads = 8, dim_head = 64, dropout = 0.):
295
+ super().__init__()
296
+ inner_dim = dim_head * heads
297
+ self.heads = heads
298
+ self.scale = dim ** -0.5
299
+
300
+ self.to_qkv = nn.Linear(dim, inner_dim * 3, bias = False)
301
+ self.to_out = nn.Sequential(
302
+ nn.Linear(inner_dim, dim),
303
+ nn.Dropout(dropout)
304
+ )
305
+
306
+ def forward(self, x, mask = None):
307
+ b, n, _, h = *x.shape, self.heads
308
+ qkv = self.to_qkv(x).chunk(3, dim = -1)
309
+
310
+ q, k, v = map(lambda t: rearrange(t, 'b n (h d) -> b h n d', h = h), qkv)
311
+ dots = torch.einsum('bhid,bhjd->bhij', q, k) * self.scale
312
+ mask_value = -torch.finfo(dots.dtype).max
313
+ #embed()
314
+ if mask is not None:
315
+ mask = F.pad(mask.flatten(1), (1, 0), value = True)
316
+ assert mask.shape[-1] == dots.shape[-1], 'mask has incorrect dimensions'
317
+ mask = mask[:, None, :] * mask[:, :, None]
318
+ dots.masked_fill_(~mask, mask_value)
319
+ del mask
320
+
321
+ attn = dots.softmax(dim=-1)
322
+
323
+ out = torch.einsum('bhij,bhjd->bhid', attn, v)
324
+ out = rearrange(out, 'b h n d -> b n (h d)')
325
+ out = self.to_out(out)
326
+
327
+ return out
328
+
329
+ class Transformer(nn.Module):
330
+ def __init__(self, dim, depth, heads, dim_head, mlp_dim, dropout):
331
+ super().__init__()
332
+ self.layers = nn.ModuleList([])
333
+ for _ in range(depth):
334
+ self.layers.append(nn.ModuleList([
335
+ Residual(PreNorm(dim, Attention(dim, heads = heads, dim_head = dim_head, dropout = dropout))),
336
+ Residual(PreNorm(dim, FeedForward(dim, mlp_dim, dropout = dropout)))
337
+ ]))
338
+ def forward(self, x, mask = None):
339
+ for attn, ff in self.layers:
340
+ x = attn(x, mask = mask)
341
+ #embed()
342
+ x = ff(x)
343
+ return x
344
+
345
+
346
+ class ViTs_face(nn.Module):
347
+ def __init__(self, *, loss_type, GPU_ID, num_class, image_size, patch_size, ac_patch_size,
348
+ pad, dim, depth, heads, mlp_dim, pool = 'cls', channels = 3, dim_head = 64, dropout = 0., emb_dropout = 0.):
349
+ super().__init__()
350
+ assert image_size % patch_size == 0, 'Image dimensions must be divisible by the patch size.'
351
+ num_patches = (image_size // patch_size) ** 2
352
+ patch_dim = channels * ac_patch_size ** 2
353
+ assert num_patches > MIN_NUM_PATCHES, f'your number of patches ({num_patches}) is way too small for attention to be effective (at least 16). Try decreasing your patch size'
354
+ assert pool in {'cls', 'mean'}, 'pool type must be either cls (cls token) or mean (mean pooling)'
355
+
356
+ self.patch_size = patch_size
357
+ self.soft_split = nn.Unfold(kernel_size=(ac_patch_size, ac_patch_size), stride=(self.patch_size, self.patch_size), padding=(pad, pad))
358
+
359
+
360
+ self.pos_embedding = nn.Parameter(torch.randn(1, num_patches + 1, dim))
361
+ self.patch_to_embedding = nn.Linear(patch_dim, dim)
362
+ self.cls_token = nn.Parameter(torch.randn(1, 1, dim))
363
+ self.dropout = nn.Dropout(emb_dropout)
364
+
365
+ self.transformer = Transformer(dim, depth, heads, dim_head, mlp_dim, dropout)
366
+
367
+ self.pool = pool
368
+ self.to_latent = nn.Identity()
369
+
370
+ self.mlp_head = nn.Sequential(
371
+ nn.LayerNorm(dim),
372
+ )
373
+ self.loss_type = loss_type
374
+ self.GPU_ID = GPU_ID
375
+ if self.loss_type == 'None':
376
+ print("no loss for vit_face")
377
+ else:
378
+ if self.loss_type == 'Softmax':
379
+ self.loss = Softmax(in_features=dim, out_features=num_class, device_id=self.GPU_ID)
380
+ elif self.loss_type == 'CosFace':
381
+ self.loss = CosFace(in_features=dim, out_features=num_class, device_id=self.GPU_ID)
382
+ elif self.loss_type == 'ArcFace':
383
+ self.loss = ArcFace(in_features=dim, out_features=num_class, device_id=self.GPU_ID)
384
+ elif self.loss_type == 'SFace':
385
+ self.loss = SFaceLoss(in_features=dim, out_features=num_class, device_id=self.GPU_ID)
386
+
387
+ def forward(self, img, label= None , mask = None):
388
+ p = self.patch_size
389
+ x = self.soft_split(img).transpose(1, 2)
390
+ x = self.patch_to_embedding(x)
391
+ b, n, _ = x.shape
392
+
393
+ cls_tokens = repeat(self.cls_token, '() n d -> b n d', b = b)
394
+ x = torch.cat((cls_tokens, x), dim=1)
395
+ x += self.pos_embedding[:, :(n + 1)]
396
+ x = self.dropout(x)
397
+ x = self.transformer(x, mask)
398
+
399
+ x = x.mean(dim = 1) if self.pool == 'mean' else x[:, 0]
400
+
401
+ x = self.to_latent(x)
402
+ emb = self.mlp_head(x)
403
+ if label is not None:
404
+ x = self.loss(emb, label)
405
+ return x, emb
406
+ else:
407
+ return emb
408
+
409
+ def l2_norm(input, axis=1):
410
+ norm = torch.norm(input, 2, axis, True)
411
+ output = torch.div(input, norm)
412
+ return output
413
+
414
+
415
+ class IDVitLoss(nn.Module):
416
+ def __init__(self):
417
+ super(IDVitLoss, self).__init__()
418
+ print("Loading Vit ArcFace")
419
+ DEVICE = torch.device("cuda:0")
420
+ NUM_CLASS = 93431
421
+ self.facenet = ViTs_face(
422
+ loss_type='CosFace',
423
+ GPU_ID=DEVICE,
424
+ num_class=NUM_CLASS,
425
+ image_size=112,
426
+ patch_size=8,
427
+ ac_patch_size=12,
428
+ pad=4,
429
+ dim=512,
430
+ depth=20,
431
+ heads=8,
432
+ mlp_dim=2048,
433
+ dropout=0.1,
434
+ emb_dropout=0.1
435
+ )
436
+ self.facenet.load_state_dict(torch.load("pretrained_models/Backbone_VITs_Epoch_2_Batch_12000_Time_2021-03-17-04-05_checkpoint.pth"))
437
+ self.face_pool = torch.nn.AdaptiveAvgPool2d((112, 112))
438
+ self.facenet = self.facenet.cuda().eval()
439
+
440
+ def extract_feats(self, x):
441
+ #x = x[:, :, 35:223, 32:220] # Crop interesting region
442
+ x = self.face_pool(x)
443
+ x_feats = self.facenet(x.cuda())
444
+ return x_feats
445
+
446
+ def forward(self, y_hat, y):
447
+ n_samples = y.shape[0]
448
+ y_feats = self.extract_feats(y)
449
+ y_hat_feats = self.extract_feats(y_hat)
450
+ y_feats = y_feats.detach()
451
+ loss = torch.mean((y_hat_feats - y_feats)**2)
452
+
453
+ return loss * 10000
criteria/lpips/__init__.py ADDED
File without changes
criteria/lpips/lpips.py ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import torch.nn as nn
3
+
4
+ from criteria.lpips.networks import get_network, LinLayers
5
+ from criteria.lpips.utils import get_state_dict
6
+
7
+
8
+ class LPIPS(nn.Module):
9
+ r"""Creates a criterion that measures
10
+ Learned Perceptual Image Patch Similarity (LPIPS).
11
+ Arguments:
12
+ net_type (str): the network type to compare the features:
13
+ 'alex' | 'squeeze' | 'vgg'. Default: 'alex'.
14
+ version (str): the version of LPIPS. Default: 0.1.
15
+ """
16
+
17
+ def __init__(self, net_type: str = "vgg", version: str = "0.1"):
18
+ assert version in ["0.1"], "v0.1 is only supported now"
19
+
20
+ super(LPIPS, self).__init__()
21
+
22
+ # pretrained network
23
+ self.net = get_network(net_type).to("cuda")
24
+
25
+ # linear layers
26
+ self.lin = LinLayers(self.net.n_channels_list).to("cuda")
27
+ self.lin.load_state_dict(get_state_dict(net_type, version))
28
+
29
+ def forward(self, x: torch.Tensor, y: torch.Tensor):
30
+ feat_x, feat_y = self.net(x), self.net(y)
31
+
32
+ diff = [(fx - fy) ** 2 for fx, fy in zip(feat_x, feat_y)]
33
+ res = [l(d).mean((2, 3), True) for d, l in zip(diff, self.lin)]
34
+
35
+ return torch.sum(torch.cat(res, 0)) / x.shape[0]
criteria/lpips/networks.py ADDED
@@ -0,0 +1,98 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Sequence
2
+
3
+ from itertools import chain
4
+
5
+ import torch
6
+ import torch.nn as nn
7
+ from torchvision import models
8
+
9
+ from criteria.lpips.utils import normalize_activation
10
+
11
+
12
+ def get_network(net_type: str):
13
+ if net_type == "alex":
14
+ return AlexNet()
15
+ elif net_type == "squeeze":
16
+ return SqueezeNet()
17
+ elif net_type == "vgg":
18
+ return VGG16()
19
+ else:
20
+ raise NotImplementedError("choose net_type from [alex, squeeze, vgg].")
21
+
22
+
23
+ class LinLayers(nn.ModuleList):
24
+ def __init__(self, n_channels_list: Sequence[int]):
25
+ super(LinLayers, self).__init__(
26
+ [
27
+ nn.Sequential(nn.Identity(), nn.Conv2d(nc, 1, 1, 1, 0, bias=False))
28
+ for nc in n_channels_list
29
+ ]
30
+ )
31
+
32
+ for param in self.parameters():
33
+ param.requires_grad = False
34
+
35
+
36
+ class BaseNet(nn.Module):
37
+ def __init__(self):
38
+ super(BaseNet, self).__init__()
39
+
40
+ # register buffer
41
+ self.register_buffer(
42
+ "mean", torch.Tensor([-0.030, -0.088, -0.188])[None, :, None, None]
43
+ )
44
+ self.register_buffer(
45
+ "std", torch.Tensor([0.458, 0.448, 0.450])[None, :, None, None]
46
+ )
47
+
48
+ def set_requires_grad(self, state: bool):
49
+ for param in chain(self.parameters(), self.buffers()):
50
+ param.requires_grad = state
51
+
52
+ def z_score(self, x: torch.Tensor):
53
+ return (x - self.mean) / self.std
54
+
55
+ def forward(self, x: torch.Tensor):
56
+ x = self.z_score(x)
57
+
58
+ output = []
59
+ for i, (_, layer) in enumerate(self.layers._modules.items(), 1):
60
+ x = layer(x)
61
+ if i in self.target_layers:
62
+ output.append(normalize_activation(x))
63
+ if len(output) == len(self.target_layers):
64
+ break
65
+ return output
66
+
67
+
68
+ class SqueezeNet(BaseNet):
69
+ def __init__(self):
70
+ super(SqueezeNet, self).__init__()
71
+
72
+ self.layers = models.squeezenet1_1(True).features
73
+ self.target_layers = [2, 5, 8, 10, 11, 12, 13]
74
+ self.n_channels_list = [64, 128, 256, 384, 384, 512, 512]
75
+
76
+ self.set_requires_grad(False)
77
+
78
+
79
+ class AlexNet(BaseNet):
80
+ def __init__(self):
81
+ super(AlexNet, self).__init__()
82
+
83
+ self.layers = models.alexnet(True).features
84
+ self.target_layers = [2, 5, 8, 10, 12]
85
+ self.n_channels_list = [64, 192, 384, 256, 256]
86
+
87
+ self.set_requires_grad(False)
88
+
89
+
90
+ class VGG16(BaseNet):
91
+ def __init__(self):
92
+ super(VGG16, self).__init__()
93
+
94
+ self.layers = models.vgg16(True).features
95
+ self.target_layers = [4, 9, 16, 23, 30]
96
+ self.n_channels_list = [64, 128, 256, 512, 512]
97
+
98
+ self.set_requires_grad(False)
criteria/lpips/utils.py ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from collections import OrderedDict
2
+
3
+ import torch
4
+
5
+
6
+ def normalize_activation(x, eps=1e-10):
7
+ norm_factor = torch.sqrt(torch.sum(x**2, dim=1, keepdim=True))
8
+ return x / (norm_factor + eps)
9
+
10
+
11
+ def get_state_dict(net_type: str = "alex", version: str = "0.1"):
12
+ # build url
13
+ url = (
14
+ "https://raw.githubusercontent.com/richzhang/PerceptualSimilarity/"
15
+ + f"master/lpips/weights/v{version}/{net_type}.pth"
16
+ )
17
+
18
+ # download
19
+ old_state_dict = torch.hub.load_state_dict_from_url(
20
+ url,
21
+ progress=True,
22
+ map_location=None if torch.cuda.is_available() else torch.device("cpu"),
23
+ )
24
+
25
+ # rename keys
26
+ new_state_dict = OrderedDict()
27
+ for key, val in old_state_dict.items():
28
+ new_key = key
29
+ new_key = new_key.replace("lin", "")
30
+ new_key = new_key.replace("model.", "")
31
+ new_state_dict[new_key] = val
32
+
33
+ return new_state_dict
criteria/moco_loss.py ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from torch import nn
3
+ import torch.nn.functional as F
4
+ from configs.paths import DefaultPaths
5
+
6
+
7
+ class MocoLoss(nn.Module):
8
+ def __init__(self):
9
+ super(MocoLoss, self).__init__()
10
+ print("Loading MOCO model from path: {}".format(DefaultPaths.moco))
11
+ self.model = self.__load_model()
12
+ self.model.cuda()
13
+ self.model.eval()
14
+
15
+ @staticmethod
16
+ def __load_model():
17
+ import torchvision.models as models
18
+
19
+ model = models.__dict__["resnet50"]()
20
+ # freeze all layers but the last fc
21
+ for name, param in model.named_parameters():
22
+ if name not in ["fc.weight", "fc.bias"]:
23
+ param.requires_grad = False
24
+ checkpoint = torch.load(DefaultPaths.moco, map_location="cpu")
25
+ state_dict = checkpoint["state_dict"]
26
+ # rename moco pre-trained keys
27
+ for k in list(state_dict.keys()):
28
+ # retain only encoder_q up to before the embedding layer
29
+ if k.startswith("module.encoder_q") and not k.startswith(
30
+ "module.encoder_q.fc"
31
+ ):
32
+ # remove prefix
33
+ state_dict[k[len("module.encoder_q.") :]] = state_dict[k]
34
+ # delete renamed or unused k
35
+ del state_dict[k]
36
+ msg = model.load_state_dict(state_dict, strict=False)
37
+ assert set(msg.missing_keys) == {"fc.weight", "fc.bias"}
38
+ # remove output layer
39
+ model = nn.Sequential(*list(model.children())[:-1]).cuda()
40
+ return model
41
+
42
+ def extract_feats(self, x):
43
+ x = F.interpolate(x, size=224)
44
+ x_feats = self.model(x)
45
+ x_feats = nn.functional.normalize(x_feats, dim=1)
46
+ x_feats = x_feats.squeeze()
47
+ return x_feats
48
+
49
+
50
+ def forward(self, y_hat, y):
51
+ n_samples = y.shape[0]
52
+ y_feats = self.extract_feats(y)
53
+ y_hat_feats = self.extract_feats(y_hat)
54
+ y_feats = y_feats.detach()
55
+ loss = 0
56
+ count = 0
57
+ for i in range(n_samples):
58
+ diff_target = y_hat_feats[i].dot(y_feats[i])
59
+ loss += 1 - diff_target
60
+ count += 1
61
+
62
+ return loss / count
criteria/ms_ssim.py ADDED
@@ -0,0 +1,180 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import torch.nn.functional as F
3
+ from math import exp
4
+ import numpy as np
5
+
6
+ """
7
+ Taken from https://github.com/jorge-pessoa/pytorch-msssim
8
+ """
9
+
10
+
11
+ def gaussian(window_size, sigma):
12
+ gauss = torch.Tensor(
13
+ [
14
+ exp(-((x - window_size // 2) ** 2) / float(2 * sigma**2))
15
+ for x in range(window_size)
16
+ ]
17
+ )
18
+ return gauss / gauss.sum()
19
+
20
+
21
+ def create_window(window_size, channel=1):
22
+ _1D_window = gaussian(window_size, 1.5).unsqueeze(1)
23
+ _2D_window = _1D_window.mm(_1D_window.t()).float().unsqueeze(0).unsqueeze(0)
24
+ window = _2D_window.expand(channel, 1, window_size, window_size).contiguous()
25
+ return window
26
+
27
+
28
+ def ssim(
29
+ img1,
30
+ img2,
31
+ window_size=11,
32
+ window=None,
33
+ size_average=True,
34
+ full=False,
35
+ val_range=None,
36
+ ):
37
+ # Value range can be different from 255. Other common ranges are 1 (sigmoid) and 2 (tanh).
38
+ if val_range is None:
39
+ if torch.max(img1) > 128:
40
+ max_val = 255
41
+ else:
42
+ max_val = 1
43
+
44
+ if torch.min(img1) < -0.5:
45
+ min_val = -1
46
+ else:
47
+ min_val = 0
48
+ L = max_val - min_val
49
+ else:
50
+ L = val_range
51
+
52
+ padd = 0
53
+ (_, channel, height, width) = img1.size()
54
+ if window is None:
55
+ real_size = min(window_size, height, width)
56
+ window = create_window(real_size, channel=channel).to(img1.device)
57
+
58
+ mu1 = F.conv2d(img1, window, padding=padd, groups=channel)
59
+ mu2 = F.conv2d(img2, window, padding=padd, groups=channel)
60
+
61
+ mu1_sq = mu1.pow(2)
62
+ mu2_sq = mu2.pow(2)
63
+ mu1_mu2 = mu1 * mu2
64
+
65
+ sigma1_sq = F.conv2d(img1 * img1, window, padding=padd, groups=channel) - mu1_sq
66
+ sigma2_sq = F.conv2d(img2 * img2, window, padding=padd, groups=channel) - mu2_sq
67
+ sigma12 = F.conv2d(img1 * img2, window, padding=padd, groups=channel) - mu1_mu2
68
+
69
+ C1 = (0.01 * L) ** 2
70
+ C2 = (0.03 * L) ** 2
71
+
72
+ v1 = 2.0 * sigma12 + C2
73
+ v2 = sigma1_sq + sigma2_sq + C2
74
+ cs = v1 / v2 # contrast sensitivity
75
+
76
+ ssim_map = ((2 * mu1_mu2 + C1) * v1) / ((mu1_sq + mu2_sq + C1) * v2)
77
+
78
+ if size_average:
79
+ cs = cs.mean()
80
+ ret = ssim_map.mean()
81
+ else:
82
+ cs = cs.mean(1).mean(1).mean(1)
83
+ ret = ssim_map.mean(1).mean(1).mean(1)
84
+
85
+ if full:
86
+ return ret, cs
87
+ return ret
88
+
89
+
90
+ def msssim(
91
+ img1, img2, window_size=11, size_average=True, val_range=None, normalize=None
92
+ ):
93
+ device = img1.device
94
+ weights = torch.FloatTensor([0.0448, 0.2856, 0.3001, 0.2363, 0.1333]).to(device)
95
+ levels = weights.size()[0]
96
+ ssims = []
97
+ mcs = []
98
+ for _ in range(levels):
99
+ sim, cs = ssim(
100
+ img1,
101
+ img2,
102
+ window_size=window_size,
103
+ size_average=size_average,
104
+ full=True,
105
+ val_range=val_range,
106
+ )
107
+
108
+ # Relu normalize (not compliant with original definition)
109
+ if normalize == "relu":
110
+ ssims.append(torch.relu(sim))
111
+ mcs.append(torch.relu(cs))
112
+ else:
113
+ ssims.append(sim)
114
+ mcs.append(cs)
115
+
116
+ img1 = F.avg_pool2d(img1, (2, 2))
117
+ img2 = F.avg_pool2d(img2, (2, 2))
118
+
119
+ ssims = torch.stack(ssims)
120
+ mcs = torch.stack(mcs)
121
+
122
+ # Simple normalize (not compliant with original definition)
123
+ if normalize == "simple" or normalize == True:
124
+ ssims = (ssims + 1) / 2
125
+ mcs = (mcs + 1) / 2
126
+
127
+ pow1 = mcs**weights
128
+ pow2 = ssims**weights
129
+
130
+ # From Matlab implementation https://ece.uwaterloo.ca/~z70wang/research/iwssim/
131
+ output = torch.prod(pow1[:-1]) * pow2[-1]
132
+ return output
133
+
134
+
135
+ # Classes to re-use window
136
+ class SSIM(torch.nn.Module):
137
+ def __init__(self, window_size=11, size_average=True, val_range=None):
138
+ super(SSIM, self).__init__()
139
+ self.window_size = window_size
140
+ self.size_average = size_average
141
+ self.val_range = val_range
142
+
143
+ # Assume 1 channel for SSIM
144
+ self.channel = 1
145
+ self.window = create_window(window_size)
146
+
147
+ def forward(self, img1, img2):
148
+ (_, channel, _, _) = img1.size()
149
+
150
+ if channel == self.channel and self.window.dtype == img1.dtype:
151
+ window = self.window
152
+ else:
153
+ window = (
154
+ create_window(self.window_size, channel)
155
+ .to(img1.device)
156
+ .type(img1.dtype)
157
+ )
158
+ self.window = window
159
+ self.channel = channel
160
+
161
+ return ssim(
162
+ img1,
163
+ img2,
164
+ window=window,
165
+ window_size=self.window_size,
166
+ size_average=self.size_average,
167
+ )
168
+
169
+
170
+ class MSSSIM(torch.nn.Module):
171
+ def __init__(self, window_size=11, size_average=True, channel=3):
172
+ super(MSSSIM, self).__init__()
173
+ self.window_size = window_size
174
+ self.size_average = size_average
175
+ self.channel = channel
176
+
177
+ def forward(self, img1, img2):
178
+ return msssim(
179
+ img1, img2, window_size=self.window_size, size_average=self.size_average
180
+ )
criteria/resnet.py ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import torch.nn as nn
3
+ import torch.nn.functional as F
4
+ import torch.utils.model_zoo as modelzoo
5
+
6
+ import os
7
+
8
+
9
+ # from modules.bn import InPlaceABNSync as BatchNorm2d
10
+
11
+ resnet18_url = 'https://download.pytorch.org/models/resnet18-5c106cde.pth'
12
+
13
+
14
+ def conv3x3(in_planes, out_planes, stride=1):
15
+ """3x3 convolution with padding"""
16
+ return nn.Conv2d(in_planes, out_planes, kernel_size=3, stride=stride,
17
+ padding=1, bias=False)
18
+
19
+
20
+ class BasicBlock(nn.Module):
21
+ def __init__(self, in_chan, out_chan, stride=1):
22
+ super(BasicBlock, self).__init__()
23
+ self.conv1 = conv3x3(in_chan, out_chan, stride)
24
+ self.bn1 = nn.BatchNorm2d(out_chan)
25
+ self.conv2 = conv3x3(out_chan, out_chan)
26
+ self.bn2 = nn.BatchNorm2d(out_chan)
27
+ self.relu = nn.ReLU(inplace=True)
28
+ self.downsample = None
29
+ if in_chan != out_chan or stride != 1:
30
+ self.downsample = nn.Sequential(
31
+ nn.Conv2d(in_chan, out_chan,
32
+ kernel_size=1, stride=stride, bias=False),
33
+ nn.BatchNorm2d(out_chan),
34
+ )
35
+
36
+ def forward(self, x):
37
+ residual = self.conv1(x)
38
+ residual = F.relu(self.bn1(residual))
39
+ residual = self.conv2(residual)
40
+ residual = self.bn2(residual)
41
+
42
+ shortcut = x
43
+ if self.downsample is not None:
44
+ shortcut = self.downsample(x)
45
+
46
+ out = shortcut + residual
47
+ out = self.relu(out)
48
+ return out
49
+
50
+
51
+ def create_layer_basic(in_chan, out_chan, bnum, stride=1):
52
+ layers = [BasicBlock(in_chan, out_chan, stride=stride)]
53
+ for i in range(bnum-1):
54
+ layers.append(BasicBlock(out_chan, out_chan, stride=1))
55
+ return nn.Sequential(*layers)
56
+
57
+
58
+ class Resnet18(nn.Module):
59
+ def __init__(self):
60
+ super(Resnet18, self).__init__()
61
+ self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3,
62
+ bias=False)
63
+ self.bn1 = nn.BatchNorm2d(64)
64
+ self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
65
+ self.layer1 = create_layer_basic(64, 64, bnum=2, stride=1)
66
+ self.layer2 = create_layer_basic(64, 128, bnum=2, stride=2)
67
+ self.layer3 = create_layer_basic(128, 256, bnum=2, stride=2)
68
+ self.layer4 = create_layer_basic(256, 512, bnum=2, stride=2)
69
+ self.init_weight()
70
+
71
+ def forward(self, x):
72
+ x = self.conv1(x)
73
+ x = F.relu(self.bn1(x))
74
+ x = self.maxpool(x)
75
+
76
+ x = self.layer1(x)
77
+ feat8 = self.layer2(x) # 1/8
78
+ feat16 = self.layer3(feat8) # 1/16
79
+ feat32 = self.layer4(feat16) # 1/32
80
+ return feat8, feat16, feat32
81
+
82
+ def init_weight(self):
83
+ #state_dict = modelzoo.load_url(resnet18_url)
84
+ state_dict = torch.load('pretrained_models/resnet18-5c106cde.pth')
85
+ self_state_dict = self.state_dict()
86
+ for k, v in state_dict.items():
87
+ if 'fc' in k: continue
88
+ self_state_dict.update({k: v})
89
+ self.load_state_dict(self_state_dict)
90
+
91
+ def get_params(self):
92
+ wd_params, nowd_params = [], []
93
+ for name, module in self.named_modules():
94
+ if isinstance(module, (nn.Linear, nn.Conv2d)):
95
+ wd_params.append(module.weight)
96
+ if not module.bias is None:
97
+ nowd_params.append(module.bias)
98
+ elif isinstance(module, nn.BatchNorm2d):
99
+ nowd_params += list(module.parameters())
100
+ return wd_params, nowd_params
criteria/w_norm.py ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from torch import nn
3
+
4
+
5
+ class WNormLoss(nn.Module):
6
+ def __init__(self, start_from_latent_avg=True):
7
+ super(WNormLoss, self).__init__()
8
+ self.start_from_latent_avg = start_from_latent_avg
9
+
10
+ def forward(self, latent, latent_avg=None):
11
+ if self.start_from_latent_avg:
12
+ latent = latent - latent_avg
13
+ return torch.sum(latent.norm(2, dim=(1, 2))) / latent.shape[0]
datasets/datasets.py ADDED
@@ -0,0 +1,136 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from torch.utils.data import Dataset
3
+ from PIL import Image
4
+ from utils import data_utils
5
+ from torchvision import transforms
6
+
7
+
8
+ class ImageDataset(Dataset):
9
+ def __init__(self, root, transform=None):
10
+ self.paths = sorted(data_utils.make_dataset(root))
11
+ self.transform = transform
12
+
13
+ def __len__(self):
14
+ return len(self.paths)
15
+
16
+ def __getitem__(self, index):
17
+ path = self.paths[index]
18
+ image = Image.open(path).convert("RGB")
19
+
20
+ if self.transform:
21
+ image = self.transform(image)
22
+ return image
23
+
24
+
25
+ class CelebaAttributeDataset(Dataset):
26
+ def __init__(self, images_root, attr, transform=None, attributes_root="", use_attr=True):
27
+ self.paths = data_utils.make_dataset(images_root)
28
+ self.transform = transform
29
+ with open(attributes_root, "r") as f:
30
+ lines = f.readlines()
31
+
32
+ attr_num = -1
33
+ for i, data_attr in enumerate(lines[1].split(" ")):
34
+ if data_attr.strip() == attr.strip():
35
+ attr_num = i
36
+ break
37
+ assert attr_num > -1, f"Can not find attribute {attr}"
38
+
39
+ filtred_paths = []
40
+ for path in self.paths:
41
+ pic_num = int(path.split("/")[-1].replace(".jpg", "").replace(".png", ""))
42
+ pic_attrs = lines[pic_num + 2].strip().split(" ")
43
+ pic_attrs = pic_attrs[2:]
44
+ if use_attr and pic_attrs[attr_num] == "1" or not use_attr and pic_attrs[attr_num] == "-1":
45
+ filtred_paths.append(path)
46
+ self.paths = sorted(filtred_paths)
47
+
48
+ def __len__(self):
49
+ return len(self.paths)
50
+
51
+ def __getitem__(self, index):
52
+ from_path = self.paths[index]
53
+ image = Image.open(from_path).convert("RGB")
54
+
55
+ if self.transform:
56
+ image = self.transform(image)
57
+ return image
58
+
59
+
60
+ class FIDDataset(Dataset):
61
+ def __init__(self, files, transforms=None):
62
+ self.files = files
63
+ self.transforms = transforms
64
+
65
+ def __len__(self):
66
+ return len(self.files)
67
+
68
+ def __getitem__(self, i):
69
+ file = self.files[i]
70
+ image = file.convert("RGB")
71
+
72
+ if self.transforms is not None:
73
+ image = self.transforms(image)
74
+
75
+ return image
76
+
77
+
78
+ class MetricsPathsDataset(Dataset):
79
+ def __init__(self, root_path, gt_dir=None, transform=None, transform_train=None, return_path=False, ignore=[]):
80
+ self.pairs = []
81
+ self.paths = []
82
+ self.names = []
83
+
84
+ for f in os.listdir(root_path):
85
+ if f not in ignore:
86
+ self.names.append(f)
87
+ image_path = os.path.join(root_path, f)
88
+ gt_path = os.path.join(gt_dir, f)
89
+ if f.endswith(".jpg") or f.endswith(".png"):
90
+ self.pairs.append([image_path, gt_path.replace(".png", ".jpg"), None])
91
+ self.paths.append(image_path)
92
+ self.transform = transform
93
+ self.transform_train = transform_train
94
+ self.return_path = return_path
95
+
96
+ def __len__(self):
97
+ return len(self.pairs)
98
+
99
+ def __getitem__(self, index):
100
+ from_path, to_path, _ = self.pairs[index]
101
+ from_im = Image.open(from_path).convert("RGB")
102
+ to_im = Image.open(to_path).convert("RGB")
103
+
104
+ if self.transform:
105
+ to_im = self.transform(to_im)
106
+ from_im = self.transform(from_im)
107
+
108
+ if not self.return_path:
109
+ return from_im, to_im
110
+ else:
111
+ return from_im, to_im, self.names[index]
112
+
113
+
114
+ class MetricsDataDataset(Dataset):
115
+ def __init__(
116
+ self, paths, target_data, fake_data, transform=None, transform_train=None
117
+ ):
118
+ self.fake_data = fake_data
119
+ self.target_data = target_data
120
+ self.paths = paths
121
+ self.transform = transform
122
+ self.transform_train = transform_train
123
+
124
+ def __len__(self):
125
+ return len(self.fake_data)
126
+
127
+ def __getitem__(self, index):
128
+
129
+ target_im = self.target_data[index]
130
+ fake_im = self.fake_data[index]
131
+
132
+ if self.transform:
133
+ fake_im = self.transform(fake_im)
134
+ target_im = self.transform(target_im)
135
+
136
+ return target_im, fake_im
datasets/loaders.py ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from torch.utils.data import DataLoader
2
+
3
+
4
+ class InfiniteLoader(DataLoader):
5
+ def __init__(
6
+ self,
7
+ *args,
8
+ num_workers=0,
9
+ pin_memory=True,
10
+ is_infinite = True,
11
+ **kwargs,
12
+ ):
13
+ super().__init__(
14
+ *args,
15
+ multiprocessing_context="fork" if num_workers > 0 else None,
16
+ num_workers=num_workers,
17
+ pin_memory=pin_memory,
18
+ **kwargs,
19
+ )
20
+ self.dataset_iterator = super().__iter__()
21
+ self.is_infinite = is_infinite
22
+
23
+ def __iter__(self):
24
+ return self
25
+
26
+ def __next__(self):
27
+ try:
28
+ x = next(self.dataset_iterator)
29
+ except StopIteration:
30
+ self.dataset_iterator = super().__iter__()
31
+ if self.is_infinite:
32
+ x = next(self.dataset_iterator)
33
+ else:
34
+ raise StopIteration
35
+
36
+ return x
datasets/transforms.py ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from abc import abstractmethod
2
+ import torchvision.transforms as transforms
3
+ from utils.class_registry import ClassRegistry
4
+
5
+ transforms_registry = ClassRegistry()
6
+
7
+
8
+ class TransformsConfig(object):
9
+ def __init__(self):
10
+ pass
11
+
12
+ @abstractmethod
13
+ def get_transforms(self):
14
+ pass
15
+
16
+ class FaceTransforms(TransformsConfig):
17
+ def __init__(self):
18
+ super(FaceTransforms, self).__init__()
19
+ self.image_size = None
20
+
21
+ def get_transforms(self):
22
+ transforms_dict = {
23
+ "train": transforms.Compose(
24
+ [
25
+ transforms.Resize(self.image_size),
26
+ transforms.RandomHorizontalFlip(0.5),
27
+ transforms.ToTensor(),
28
+ transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5]),
29
+ ]
30
+ ),
31
+ "test": transforms.Compose(
32
+ [
33
+ transforms.Resize(self.image_size),
34
+ transforms.ToTensor(),
35
+ transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5]),
36
+ ]
37
+ )
38
+ }
39
+ return transforms_dict
40
+
41
+
42
+ @transforms_registry.add_to_registry(name="face_256")
43
+ class Face256Transforms(FaceTransforms):
44
+ def __init__(self):
45
+ super(Face256Transforms, self).__init__()
46
+ self.image_size = (256, 256)
47
+
48
+
49
+ @transforms_registry.add_to_registry(name="face_1024")
50
+ class Face1024Transforms(FaceTransforms):
51
+ def __init__(self):
52
+ super(Face1024Transforms, self).__init__()
53
+ self.image_size = (1024, 1024)
54
+
55
+
dnnlib/__init__.py ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved.
2
+ #
3
+ # NVIDIA CORPORATION and its licensors retain all intellectual property
4
+ # and proprietary rights in and to this software, related documentation
5
+ # and any modifications thereto. Any use, reproduction, disclosure or
6
+ # distribution of this software and related documentation without an express
7
+ # license agreement from NVIDIA CORPORATION is strictly prohibited.
8
+
9
+ from .util import EasyDict, make_cache_dir_path
10
+
11
+
12
+ __all__ = ["EasyDict", "make_cache_dir_path"]
dnnlib/tflib/__init__.py ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2019, NVIDIA Corporation. All rights reserved.
2
+ #
3
+ # This work is made available under the Nvidia Source Code License-NC.
4
+ # To view a copy of this license, visit
5
+ # https://nvlabs.github.io/stylegan2/license.html
6
+
7
+ from . import autosummary
8
+ from . import network
9
+ from . import optimizer
10
+ from . import tfutil
11
+ from . import custom_ops
12
+
13
+ from .tfutil import *
14
+ from .network import Network
15
+
16
+ from .optimizer import Optimizer
17
+
18
+ from .custom_ops import get_plugin
dnnlib/tflib/autosummary.py ADDED
@@ -0,0 +1,191 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2019, NVIDIA Corporation. All rights reserved.
2
+ #
3
+ # This work is made available under the Nvidia Source Code License-NC.
4
+ # To view a copy of this license, visit
5
+ # https://nvlabs.github.io/stylegan2/license.html
6
+
7
+ """Helper for adding automatically tracked values to Tensorboard.
8
+
9
+ Autosummary creates an identity op that internally keeps track of the input
10
+ values and automatically shows up in TensorBoard. The reported value
11
+ represents an average over input components. The average is accumulated
12
+ constantly over time and flushed when save_summaries() is called.
13
+
14
+ Notes:
15
+ - The output tensor must be used as an input for something else in the
16
+ graph. Otherwise, the autosummary op will not get executed, and the average
17
+ value will not get accumulated.
18
+ - It is perfectly fine to include autosummaries with the same name in
19
+ several places throughout the graph, even if they are executed concurrently.
20
+ - It is ok to also pass in a python scalar or numpy array. In this case, it
21
+ is added to the average immediately.
22
+ """
23
+
24
+ from collections import OrderedDict
25
+ import numpy as np
26
+ import tensorflow as tf
27
+ from tensorboard import summary as summary_lib
28
+ from tensorboard.plugins.custom_scalar import layout_pb2
29
+
30
+ from . import tfutil
31
+ from .tfutil import TfExpression
32
+ from .tfutil import TfExpressionEx
33
+
34
+ # Enable "Custom scalars" tab in TensorBoard for advanced formatting.
35
+ # Disabled by default to reduce tfevents file size.
36
+ enable_custom_scalars = False
37
+
38
+ _dtype = tf.float64
39
+ _vars = OrderedDict() # name => [var, ...]
40
+ _immediate = OrderedDict() # name => update_op, update_value
41
+ _finalized = False
42
+ _merge_op = None
43
+
44
+
45
+ def _create_var(name: str, value_expr: TfExpression) -> TfExpression:
46
+ """Internal helper for creating autosummary accumulators."""
47
+ assert not _finalized
48
+ name_id = name.replace("/", "_")
49
+ v = tf.cast(value_expr, _dtype)
50
+
51
+ if v.shape.is_fully_defined():
52
+ size = np.prod(v.shape.as_list())
53
+ size_expr = tf.constant(size, dtype=_dtype)
54
+ else:
55
+ size = None
56
+ size_expr = tf.reduce_prod(tf.cast(tf.shape(v), _dtype))
57
+
58
+ if size == 1:
59
+ if v.shape.ndims != 0:
60
+ v = tf.reshape(v, [])
61
+ v = [size_expr, v, tf.square(v)]
62
+ else:
63
+ v = [size_expr, tf.reduce_sum(v), tf.reduce_sum(tf.square(v))]
64
+ v = tf.cond(tf.is_finite(v[1]), lambda: tf.stack(v), lambda: tf.zeros(3, dtype=_dtype))
65
+
66
+ with tfutil.absolute_name_scope("Autosummary/" + name_id), tf.control_dependencies(None):
67
+ var = tf.Variable(tf.zeros(3, dtype=_dtype), trainable=False) # [sum(1), sum(x), sum(x**2)]
68
+ update_op = tf.cond(tf.is_variable_initialized(var), lambda: tf.assign_add(var, v), lambda: tf.assign(var, v))
69
+
70
+ if name in _vars:
71
+ _vars[name].append(var)
72
+ else:
73
+ _vars[name] = [var]
74
+ return update_op
75
+
76
+
77
+ def autosummary(name: str, value: TfExpressionEx, passthru: TfExpressionEx = None, condition: TfExpressionEx = True) -> TfExpressionEx:
78
+ """Create a new autosummary.
79
+
80
+ Args:
81
+ name: Name to use in TensorBoard
82
+ value: TensorFlow expression or python value to track
83
+ passthru: Optionally return this TF node without modifications but tack an autosummary update side-effect to this node.
84
+
85
+ Example use of the passthru mechanism:
86
+
87
+ n = autosummary('l2loss', loss, passthru=n)
88
+
89
+ This is a shorthand for the following code:
90
+
91
+ with tf.control_dependencies([autosummary('l2loss', loss)]):
92
+ n = tf.identity(n)
93
+ """
94
+ tfutil.assert_tf_initialized()
95
+ name_id = name.replace("/", "_")
96
+
97
+ if tfutil.is_tf_expression(value):
98
+ with tf.name_scope("summary_" + name_id), tf.device(value.device):
99
+ condition = tf.convert_to_tensor(condition, name='condition')
100
+ update_op = tf.cond(condition, lambda: tf.group(_create_var(name, value)), tf.no_op)
101
+ with tf.control_dependencies([update_op]):
102
+ return tf.identity(value if passthru is None else passthru)
103
+
104
+ else: # python scalar or numpy array
105
+ assert not tfutil.is_tf_expression(passthru)
106
+ assert not tfutil.is_tf_expression(condition)
107
+ if condition:
108
+ if name not in _immediate:
109
+ with tfutil.absolute_name_scope("Autosummary/" + name_id), tf.device(None), tf.control_dependencies(None):
110
+ update_value = tf.placeholder(_dtype)
111
+ update_op = _create_var(name, update_value)
112
+ _immediate[name] = update_op, update_value
113
+ update_op, update_value = _immediate[name]
114
+ tfutil.run(update_op, {update_value: value})
115
+ return value if passthru is None else passthru
116
+
117
+
118
+ def finalize_autosummaries() -> None:
119
+ """Create the necessary ops to include autosummaries in TensorBoard report.
120
+ Note: This should be done only once per graph.
121
+ """
122
+ global _finalized
123
+ tfutil.assert_tf_initialized()
124
+
125
+ if _finalized:
126
+ return None
127
+
128
+ _finalized = True
129
+ tfutil.init_uninitialized_vars([var for vars_list in _vars.values() for var in vars_list])
130
+
131
+ # Create summary ops.
132
+ with tf.device(None), tf.control_dependencies(None):
133
+ for name, vars_list in _vars.items():
134
+ name_id = name.replace("/", "_")
135
+ with tfutil.absolute_name_scope("Autosummary/" + name_id):
136
+ moments = tf.add_n(vars_list)
137
+ moments /= moments[0]
138
+ with tf.control_dependencies([moments]): # read before resetting
139
+ reset_ops = [tf.assign(var, tf.zeros(3, dtype=_dtype)) for var in vars_list]
140
+ with tf.name_scope(None), tf.control_dependencies(reset_ops): # reset before reporting
141
+ mean = moments[1]
142
+ std = tf.sqrt(moments[2] - tf.square(moments[1]))
143
+ tf.summary.scalar(name, mean)
144
+ if enable_custom_scalars:
145
+ tf.summary.scalar("xCustomScalars/" + name + "/margin_lo", mean - std)
146
+ tf.summary.scalar("xCustomScalars/" + name + "/margin_hi", mean + std)
147
+
148
+ # Setup layout for custom scalars.
149
+ layout = None
150
+ if enable_custom_scalars:
151
+ cat_dict = OrderedDict()
152
+ for series_name in sorted(_vars.keys()):
153
+ p = series_name.split("/")
154
+ cat = p[0] if len(p) >= 2 else ""
155
+ chart = "/".join(p[1:-1]) if len(p) >= 3 else p[-1]
156
+ if cat not in cat_dict:
157
+ cat_dict[cat] = OrderedDict()
158
+ if chart not in cat_dict[cat]:
159
+ cat_dict[cat][chart] = []
160
+ cat_dict[cat][chart].append(series_name)
161
+ categories = []
162
+ for cat_name, chart_dict in cat_dict.items():
163
+ charts = []
164
+ for chart_name, series_names in chart_dict.items():
165
+ series = []
166
+ for series_name in series_names:
167
+ series.append(layout_pb2.MarginChartContent.Series(
168
+ value=series_name,
169
+ lower="xCustomScalars/" + series_name + "/margin_lo",
170
+ upper="xCustomScalars/" + series_name + "/margin_hi"))
171
+ margin = layout_pb2.MarginChartContent(series=series)
172
+ charts.append(layout_pb2.Chart(title=chart_name, margin=margin))
173
+ categories.append(layout_pb2.Category(title=cat_name, chart=charts))
174
+ layout = summary_lib.custom_scalar_pb(layout_pb2.Layout(category=categories))
175
+ return layout
176
+
177
+ def save_summaries(file_writer, global_step=None):
178
+ """Call FileWriter.add_summary() with all summaries in the default graph,
179
+ automatically finalizing and merging them on the first call.
180
+ """
181
+ global _merge_op
182
+ tfutil.assert_tf_initialized()
183
+
184
+ if _merge_op is None:
185
+ layout = finalize_autosummaries()
186
+ if layout is not None:
187
+ file_writer.add_summary(layout)
188
+ with tf.device(None), tf.control_dependencies(None):
189
+ _merge_op = tf.summary.merge_all()
190
+
191
+ file_writer.add_summary(_merge_op.eval(), global_step)
dnnlib/tflib/custom_ops.py ADDED
@@ -0,0 +1,169 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2019, NVIDIA Corporation. All rights reserved.
2
+ #
3
+ # This work is made available under the Nvidia Source Code License-NC.
4
+ # To view a copy of this license, visit
5
+ # https://nvlabs.github.io/stylegan2/license.html
6
+
7
+ """TensorFlow custom ops builder.
8
+ """
9
+
10
+ import os
11
+ import re
12
+ import uuid
13
+ import hashlib
14
+ import tempfile
15
+ import shutil
16
+ import tensorflow as tf
17
+ from tensorflow.python.client import device_lib # pylint: disable=no-name-in-module
18
+
19
+ #----------------------------------------------------------------------------
20
+ # Global options.
21
+
22
+ cuda_cache_path = os.path.join(os.path.dirname(__file__), '_cudacache')
23
+ cuda_cache_version_tag = 'v1'
24
+ do_not_hash_included_headers = False # Speed up compilation by assuming that headers included by the CUDA code never change. Unsafe!
25
+ verbose = True # Print status messages to stdout.
26
+
27
+ compiler_bindir_search_path = [
28
+ 'C:/Program Files (x86)/Microsoft Visual Studio/2017/Community/VC/Tools/MSVC/14.14.26428/bin/Hostx64/x64',
29
+ 'C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.23.28105/bin/Hostx64/x64',
30
+ 'C:/Program Files (x86)/Microsoft Visual Studio 14.0/vc/bin',
31
+ ]
32
+
33
+ #----------------------------------------------------------------------------
34
+ # Internal helper funcs.
35
+
36
+ def _find_compiler_bindir():
37
+ for compiler_path in compiler_bindir_search_path:
38
+ if os.path.isdir(compiler_path):
39
+ return compiler_path
40
+ return None
41
+
42
+ def _get_compute_cap(device):
43
+ caps_str = device.physical_device_desc
44
+ m = re.search('compute capability: (\\d+).(\\d+)', caps_str)
45
+ major = m.group(1)
46
+ minor = m.group(2)
47
+ return (major, minor)
48
+
49
+ def _get_cuda_gpu_arch_string():
50
+ gpus = [x for x in device_lib.list_local_devices() if x.device_type == 'GPU']
51
+ if len(gpus) == 0:
52
+ raise RuntimeError('No GPU devices found')
53
+ (major, minor) = _get_compute_cap(gpus[0])
54
+ return 'sm_%s%s' % (major, minor)
55
+
56
+ def _run_cmd(cmd):
57
+ with os.popen(cmd) as pipe:
58
+ output = pipe.read()
59
+ status = pipe.close()
60
+ if status is not None:
61
+ raise RuntimeError('NVCC returned an error. See below for full command line and output log:\n\n%s\n\n%s' % (cmd, output))
62
+
63
+ def _prepare_nvcc_cli(opts):
64
+ cmd = 'nvcc ' + opts.strip()
65
+ cmd += ' --disable-warnings'
66
+ cmd += ' --include-path "%s"' % tf.sysconfig.get_include()
67
+ cmd += ' --include-path "%s"' % os.path.join(tf.sysconfig.get_include(), 'external', 'protobuf_archive', 'src')
68
+ cmd += ' --include-path "%s"' % os.path.join(tf.sysconfig.get_include(), 'external', 'com_google_absl')
69
+ cmd += ' --include-path "%s"' % os.path.join(tf.sysconfig.get_include(), 'external', 'eigen_archive')
70
+
71
+ compiler_bindir = _find_compiler_bindir()
72
+ if compiler_bindir is None:
73
+ # Require that _find_compiler_bindir succeeds on Windows. Allow
74
+ # nvcc to use whatever is the default on Linux.
75
+ if os.name == 'nt':
76
+ raise RuntimeError('Could not find MSVC/GCC/CLANG installation on this computer. Check compiler_bindir_search_path list in "%s".' % __file__)
77
+ else:
78
+ cmd += ' --compiler-bindir "%s"' % compiler_bindir
79
+ cmd += ' 2>&1'
80
+ return cmd
81
+
82
+ #----------------------------------------------------------------------------
83
+ # Main entry point.
84
+
85
+ _plugin_cache = dict()
86
+
87
+ def get_plugin(cuda_file):
88
+ cuda_file_base = os.path.basename(cuda_file)
89
+ cuda_file_name, cuda_file_ext = os.path.splitext(cuda_file_base)
90
+
91
+ # Already in cache?
92
+ if cuda_file in _plugin_cache:
93
+ return _plugin_cache[cuda_file]
94
+
95
+ # Setup plugin.
96
+ if verbose:
97
+ print('Setting up TensorFlow plugin "%s": ' % cuda_file_base, end='', flush=True)
98
+ try:
99
+ # Hash CUDA source.
100
+ md5 = hashlib.md5()
101
+ with open(cuda_file, 'rb') as f:
102
+ md5.update(f.read())
103
+ md5.update(b'\n')
104
+
105
+ # Hash headers included by the CUDA code by running it through the preprocessor.
106
+ if not do_not_hash_included_headers:
107
+ if verbose:
108
+ print('Preprocessing... ', end='', flush=True)
109
+ with tempfile.TemporaryDirectory() as tmp_dir:
110
+ tmp_file = os.path.join(tmp_dir, cuda_file_name + '_tmp' + cuda_file_ext)
111
+ _run_cmd(_prepare_nvcc_cli('"%s" --preprocess -o "%s" --keep --keep-dir "%s"' % (cuda_file, tmp_file, tmp_dir)))
112
+ with open(tmp_file, 'rb') as f:
113
+ bad_file_str = ('"' + cuda_file.replace('\\', '/') + '"').encode('utf-8') # __FILE__ in error check macros
114
+ good_file_str = ('"' + cuda_file_base + '"').encode('utf-8')
115
+ for ln in f:
116
+ if not ln.startswith(b'# ') and not ln.startswith(b'#line '): # ignore line number pragmas
117
+ ln = ln.replace(bad_file_str, good_file_str)
118
+ md5.update(ln)
119
+ md5.update(b'\n')
120
+
121
+ # Select compiler options.
122
+ compile_opts = ''
123
+ if os.name == 'nt':
124
+ compile_opts += '"%s"' % os.path.join(tf.sysconfig.get_lib(), 'python', '_pywrap_tensorflow_internal.lib')
125
+ elif os.name == 'posix':
126
+ compile_opts += '"%s"' % os.path.join(tf.sysconfig.get_lib(), 'python', '_pywrap_tensorflow_internal.so')
127
+ compile_opts += ' --compiler-options \'-fPIC -D_GLIBCXX_USE_CXX11_ABI=0\''
128
+ else:
129
+ assert False # not Windows or Linux, w00t?
130
+ compile_opts += ' --gpu-architecture=%s' % _get_cuda_gpu_arch_string()
131
+ compile_opts += ' --use_fast_math'
132
+ nvcc_cmd = _prepare_nvcc_cli(compile_opts)
133
+
134
+ # Hash build configuration.
135
+ md5.update(('nvcc_cmd: ' + nvcc_cmd).encode('utf-8') + b'\n')
136
+ md5.update(('tf.VERSION: ' + tf.VERSION).encode('utf-8') + b'\n')
137
+ md5.update(('cuda_cache_version_tag: ' + cuda_cache_version_tag).encode('utf-8') + b'\n')
138
+
139
+ # Compile if not already compiled.
140
+ bin_file_ext = '.dll' if os.name == 'nt' else '.so'
141
+ bin_file = os.path.join(cuda_cache_path, cuda_file_name + '_' + md5.hexdigest() + bin_file_ext)
142
+ if not os.path.isfile(bin_file):
143
+ if verbose:
144
+ print('Compiling... ', end='', flush=True)
145
+ with tempfile.TemporaryDirectory() as tmp_dir:
146
+ tmp_file = os.path.join(tmp_dir, cuda_file_name + '_tmp' + bin_file_ext)
147
+ _run_cmd(nvcc_cmd + ' "%s" --shared -o "%s" --keep --keep-dir "%s"' % (cuda_file, tmp_file, tmp_dir))
148
+ os.makedirs(cuda_cache_path, exist_ok=True)
149
+ intermediate_file = os.path.join(cuda_cache_path, cuda_file_name + '_' + uuid.uuid4().hex + '_tmp' + bin_file_ext)
150
+ shutil.copyfile(tmp_file, intermediate_file)
151
+ os.rename(intermediate_file, bin_file) # atomic
152
+
153
+ # Load.
154
+ if verbose:
155
+ print('Loading... ', end='', flush=True)
156
+ plugin = tf.load_op_library(bin_file)
157
+
158
+ # Add to cache.
159
+ _plugin_cache[cuda_file] = plugin
160
+ if verbose:
161
+ print('Done.', flush=True)
162
+ return plugin
163
+
164
+ except:
165
+ if verbose:
166
+ print('Failed!', flush=True)
167
+ raise
168
+
169
+ #----------------------------------------------------------------------------
dnnlib/tflib/network.py ADDED
@@ -0,0 +1,590 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2019, NVIDIA Corporation. All rights reserved.
2
+ #
3
+ # This work is made available under the Nvidia Source Code License-NC.
4
+ # To view a copy of this license, visit
5
+ # https://nvlabs.github.io/stylegan2/license.html
6
+
7
+ """Helper for managing networks."""
8
+
9
+ import types
10
+ import inspect
11
+ import re
12
+ import uuid
13
+ import sys
14
+ import numpy as np
15
+ import tensorflow as tf
16
+
17
+ from collections import OrderedDict
18
+ from typing import Any, List, Tuple, Union
19
+
20
+ from . import tfutil
21
+ from .. import util
22
+
23
+ from .tfutil import TfExpression, TfExpressionEx
24
+
25
+ _import_handlers = [] # Custom import handlers for dealing with legacy data in pickle import.
26
+ _import_module_src = dict() # Source code for temporary modules created during pickle import.
27
+
28
+
29
+ def import_handler(handler_func):
30
+ """Function decorator for declaring custom import handlers."""
31
+ _import_handlers.append(handler_func)
32
+ return handler_func
33
+
34
+
35
+ class Network:
36
+ """Generic network abstraction.
37
+
38
+ Acts as a convenience wrapper for a parameterized network construction
39
+ function, providing several utility methods and convenient access to
40
+ the inputs/outputs/weights.
41
+
42
+ Network objects can be safely pickled and unpickled for long-term
43
+ archival purposes. The pickling works reliably as long as the underlying
44
+ network construction function is defined in a standalone Python module
45
+ that has no side effects or application-specific imports.
46
+
47
+ Args:
48
+ name: Network name. Used to select TensorFlow name and variable scopes.
49
+ func_name: Fully qualified name of the underlying network construction function, or a top-level function object.
50
+ static_kwargs: Keyword arguments to be passed in to the network construction function.
51
+
52
+ Attributes:
53
+ name: User-specified name, defaults to build func name if None.
54
+ scope: Unique TensorFlow scope containing template graph and variables, derived from the user-specified name.
55
+ static_kwargs: Arguments passed to the user-supplied build func.
56
+ components: Container for sub-networks. Passed to the build func, and retained between calls.
57
+ num_inputs: Number of input tensors.
58
+ num_outputs: Number of output tensors.
59
+ input_shapes: Input tensor shapes (NC or NCHW), including minibatch dimension.
60
+ output_shapes: Output tensor shapes (NC or NCHW), including minibatch dimension.
61
+ input_shape: Short-hand for input_shapes[0].
62
+ output_shape: Short-hand for output_shapes[0].
63
+ input_templates: Input placeholders in the template graph.
64
+ output_templates: Output tensors in the template graph.
65
+ input_names: Name string for each input.
66
+ output_names: Name string for each output.
67
+ own_vars: Variables defined by this network (local_name => var), excluding sub-networks.
68
+ vars: All variables (local_name => var).
69
+ trainables: All trainable variables (local_name => var).
70
+ var_global_to_local: Mapping from variable global names to local names.
71
+ """
72
+
73
+ def __init__(self, name: str = None, func_name: Any = None, **static_kwargs):
74
+ tfutil.assert_tf_initialized()
75
+ assert isinstance(name, str) or name is None
76
+ assert func_name is not None
77
+ assert isinstance(func_name, str) or util.is_top_level_function(func_name)
78
+ assert util.is_pickleable(static_kwargs)
79
+
80
+ self._init_fields()
81
+ self.name = name
82
+ self.static_kwargs = util.EasyDict(static_kwargs)
83
+
84
+ # Locate the user-specified network build function.
85
+ if util.is_top_level_function(func_name):
86
+ func_name = util.get_top_level_function_name(func_name)
87
+ module, self._build_func_name = util.get_module_from_obj_name(func_name)
88
+ self._build_func = util.get_obj_from_module(module, self._build_func_name)
89
+ assert callable(self._build_func)
90
+
91
+ # Dig up source code for the module containing the build function.
92
+ self._build_module_src = _import_module_src.get(module, None)
93
+ if self._build_module_src is None:
94
+ self._build_module_src = inspect.getsource(module)
95
+
96
+ # Init TensorFlow graph.
97
+ self._init_graph()
98
+ self.reset_own_vars()
99
+
100
+ def _init_fields(self) -> None:
101
+ self.name = None
102
+ self.scope = None
103
+ self.static_kwargs = util.EasyDict()
104
+ self.components = util.EasyDict()
105
+ self.num_inputs = 0
106
+ self.num_outputs = 0
107
+ self.input_shapes = [[]]
108
+ self.output_shapes = [[]]
109
+ self.input_shape = []
110
+ self.output_shape = []
111
+ self.input_templates = []
112
+ self.output_templates = []
113
+ self.input_names = []
114
+ self.output_names = []
115
+ self.own_vars = OrderedDict()
116
+ self.vars = OrderedDict()
117
+ self.trainables = OrderedDict()
118
+ self.var_global_to_local = OrderedDict()
119
+
120
+ self._build_func = None # User-supplied build function that constructs the network.
121
+ self._build_func_name = None # Name of the build function.
122
+ self._build_module_src = None # Full source code of the module containing the build function.
123
+ self._run_cache = dict() # Cached graph data for Network.run().
124
+
125
+ def _init_graph(self) -> None:
126
+ # Collect inputs.
127
+ self.input_names = []
128
+
129
+ for param in inspect.signature(self._build_func).parameters.values():
130
+ if param.kind == param.POSITIONAL_OR_KEYWORD and param.default is param.empty:
131
+ self.input_names.append(param.name)
132
+
133
+ self.num_inputs = len(self.input_names)
134
+ assert self.num_inputs >= 1
135
+
136
+ # Choose name and scope.
137
+ if self.name is None:
138
+ self.name = self._build_func_name
139
+ assert re.match("^[A-Za-z0-9_.\\-]*$", self.name)
140
+ with tf.name_scope(None):
141
+ self.scope = tf.get_default_graph().unique_name(self.name, mark_as_used=True)
142
+
143
+ # Finalize build func kwargs.
144
+ build_kwargs = dict(self.static_kwargs)
145
+ build_kwargs["is_template_graph"] = True
146
+ build_kwargs["components"] = self.components
147
+
148
+ # Build template graph.
149
+ with tfutil.absolute_variable_scope(self.scope, reuse=False), tfutil.absolute_name_scope(self.scope): # ignore surrounding scopes
150
+ assert tf.get_variable_scope().name == self.scope
151
+ assert tf.get_default_graph().get_name_scope() == self.scope
152
+ with tf.control_dependencies(None): # ignore surrounding control dependencies
153
+ self.input_templates = [tf.placeholder(tf.float32, name=name) for name in self.input_names]
154
+ out_expr = self._build_func(*self.input_templates, **build_kwargs)
155
+
156
+ # Collect outputs.
157
+ assert tfutil.is_tf_expression(out_expr) or isinstance(out_expr, tuple)
158
+ self.output_templates = [out_expr] if tfutil.is_tf_expression(out_expr) else list(out_expr)
159
+ self.num_outputs = len(self.output_templates)
160
+ assert self.num_outputs >= 1
161
+ assert all(tfutil.is_tf_expression(t) for t in self.output_templates)
162
+
163
+ # Perform sanity checks.
164
+ if any(t.shape.ndims is None for t in self.input_templates):
165
+ raise ValueError("Network input shapes not defined. Please call x.set_shape() for each input.")
166
+ if any(t.shape.ndims is None for t in self.output_templates):
167
+ raise ValueError("Network output shapes not defined. Please call x.set_shape() where applicable.")
168
+ if any(not isinstance(comp, Network) for comp in self.components.values()):
169
+ raise ValueError("Components of a Network must be Networks themselves.")
170
+ if len(self.components) != len(set(comp.name for comp in self.components.values())):
171
+ raise ValueError("Components of a Network must have unique names.")
172
+
173
+ # List inputs and outputs.
174
+ self.input_shapes = [t.shape.as_list() for t in self.input_templates]
175
+ self.output_shapes = [t.shape.as_list() for t in self.output_templates]
176
+ self.input_shape = self.input_shapes[0]
177
+ self.output_shape = self.output_shapes[0]
178
+ self.output_names = [t.name.split("/")[-1].split(":")[0] for t in self.output_templates]
179
+
180
+ # List variables.
181
+ self.own_vars = OrderedDict((var.name[len(self.scope) + 1:].split(":")[0], var) for var in tf.global_variables(self.scope + "/"))
182
+ self.vars = OrderedDict(self.own_vars)
183
+ self.vars.update((comp.name + "/" + name, var) for comp in self.components.values() for name, var in comp.vars.items())
184
+ self.trainables = OrderedDict((name, var) for name, var in self.vars.items() if var.trainable)
185
+ self.var_global_to_local = OrderedDict((var.name.split(":")[0], name) for name, var in self.vars.items())
186
+
187
+ def reset_own_vars(self) -> None:
188
+ """Re-initialize all variables of this network, excluding sub-networks."""
189
+ tfutil.run([var.initializer for var in self.own_vars.values()])
190
+
191
+ def reset_vars(self) -> None:
192
+ """Re-initialize all variables of this network, including sub-networks."""
193
+ tfutil.run([var.initializer for var in self.vars.values()])
194
+
195
+ def reset_trainables(self) -> None:
196
+ """Re-initialize all trainable variables of this network, including sub-networks."""
197
+ tfutil.run([var.initializer for var in self.trainables.values()])
198
+
199
+ def get_output_for(self, *in_expr: TfExpression, return_as_list: bool = False, **dynamic_kwargs) -> Union[TfExpression, List[TfExpression]]:
200
+ """Construct TensorFlow expression(s) for the output(s) of this network, given the input expression(s)."""
201
+ assert len(in_expr) == self.num_inputs
202
+ assert not all(expr is None for expr in in_expr)
203
+
204
+ # Finalize build func kwargs.
205
+ build_kwargs = dict(self.static_kwargs)
206
+ build_kwargs.update(dynamic_kwargs)
207
+ build_kwargs["is_template_graph"] = False
208
+ build_kwargs["components"] = self.components
209
+
210
+ # Build TensorFlow graph to evaluate the network.
211
+ with tfutil.absolute_variable_scope(self.scope, reuse=True), tf.name_scope(self.name):
212
+ assert tf.get_variable_scope().name == self.scope
213
+ valid_inputs = [expr for expr in in_expr if expr is not None]
214
+ final_inputs = []
215
+ for expr, name, shape in zip(in_expr, self.input_names, self.input_shapes):
216
+ if expr is not None:
217
+ expr = tf.identity(expr, name=name)
218
+ else:
219
+ expr = tf.zeros([tf.shape(valid_inputs[0])[0]] + shape[1:], name=name)
220
+ final_inputs.append(expr)
221
+ out_expr = self._build_func(*final_inputs, **build_kwargs)
222
+
223
+ # Propagate input shapes back to the user-specified expressions.
224
+ for expr, final in zip(in_expr, final_inputs):
225
+ if isinstance(expr, tf.Tensor):
226
+ expr.set_shape(final.shape)
227
+
228
+ # Express outputs in the desired format.
229
+ assert tfutil.is_tf_expression(out_expr) or isinstance(out_expr, tuple)
230
+ if return_as_list:
231
+ out_expr = [out_expr] if tfutil.is_tf_expression(out_expr) else list(out_expr)
232
+ return out_expr
233
+
234
+ def get_var_local_name(self, var_or_global_name: Union[TfExpression, str]) -> str:
235
+ """Get the local name of a given variable, without any surrounding name scopes."""
236
+ assert tfutil.is_tf_expression(var_or_global_name) or isinstance(var_or_global_name, str)
237
+ global_name = var_or_global_name if isinstance(var_or_global_name, str) else var_or_global_name.name
238
+ return self.var_global_to_local[global_name]
239
+
240
+ def find_var(self, var_or_local_name: Union[TfExpression, str]) -> TfExpression:
241
+ """Find variable by local or global name."""
242
+ assert tfutil.is_tf_expression(var_or_local_name) or isinstance(var_or_local_name, str)
243
+ return self.vars[var_or_local_name] if isinstance(var_or_local_name, str) else var_or_local_name
244
+
245
+ def get_var(self, var_or_local_name: Union[TfExpression, str]) -> np.ndarray:
246
+ """Get the value of a given variable as NumPy array.
247
+ Note: This method is very inefficient -- prefer to use tflib.run(list_of_vars) whenever possible."""
248
+ return self.find_var(var_or_local_name).eval()
249
+
250
+ def set_var(self, var_or_local_name: Union[TfExpression, str], new_value: Union[int, float, np.ndarray]) -> None:
251
+ """Set the value of a given variable based on the given NumPy array.
252
+ Note: This method is very inefficient -- prefer to use tflib.set_vars() whenever possible."""
253
+ tfutil.set_vars({self.find_var(var_or_local_name): new_value})
254
+
255
+ def __getstate__(self) -> dict:
256
+ """Pickle export."""
257
+ state = dict()
258
+ state["version"] = 4
259
+ state["name"] = self.name
260
+ state["static_kwargs"] = dict(self.static_kwargs)
261
+ state["components"] = dict(self.components)
262
+ state["build_module_src"] = self._build_module_src
263
+ state["build_func_name"] = self._build_func_name
264
+ state["variables"] = list(zip(self.own_vars.keys(), tfutil.run(list(self.own_vars.values()))))
265
+ return state
266
+
267
+ def __setstate__(self, state: dict) -> None:
268
+ """Pickle import."""
269
+ # pylint: disable=attribute-defined-outside-init
270
+ tfutil.assert_tf_initialized()
271
+ self._init_fields()
272
+
273
+ # Execute custom import handlers.
274
+ for handler in _import_handlers:
275
+ state = handler(state)
276
+
277
+ # Set basic fields.
278
+ assert state["version"] in [2, 3, 4]
279
+ self.name = state["name"]
280
+ self.static_kwargs = util.EasyDict(state["static_kwargs"])
281
+ self.components = util.EasyDict(state.get("components", {}))
282
+ self._build_module_src = state["build_module_src"]
283
+ self._build_func_name = state["build_func_name"]
284
+
285
+ # Create temporary module from the imported source code.
286
+ module_name = "_tflib_network_import_" + uuid.uuid4().hex
287
+ module = types.ModuleType(module_name)
288
+ sys.modules[module_name] = module
289
+ _import_module_src[module] = self._build_module_src
290
+ exec(self._build_module_src, module.__dict__) # pylint: disable=exec-used
291
+
292
+ # Locate network build function in the temporary module.
293
+ self._build_func = util.get_obj_from_module(module, self._build_func_name)
294
+ assert callable(self._build_func)
295
+
296
+ # Init TensorFlow graph.
297
+ self._init_graph()
298
+ self.reset_own_vars()
299
+ tfutil.set_vars({self.find_var(name): value for name, value in state["variables"]})
300
+
301
+ def clone(self, name: str = None, **new_static_kwargs) -> "Network":
302
+ """Create a clone of this network with its own copy of the variables."""
303
+ # pylint: disable=protected-access
304
+ net = object.__new__(Network)
305
+ net._init_fields()
306
+ net.name = name if name is not None else self.name
307
+ net.static_kwargs = util.EasyDict(self.static_kwargs)
308
+ net.static_kwargs.update(new_static_kwargs)
309
+ net._build_module_src = self._build_module_src
310
+ net._build_func_name = self._build_func_name
311
+ net._build_func = self._build_func
312
+ net._init_graph()
313
+ net.copy_vars_from(self)
314
+ return net
315
+
316
+ def copy_own_vars_from(self, src_net: "Network") -> None:
317
+ """Copy the values of all variables from the given network, excluding sub-networks."""
318
+ names = [name for name in self.own_vars.keys() if name in src_net.own_vars]
319
+ tfutil.set_vars(tfutil.run({self.vars[name]: src_net.vars[name] for name in names}))
320
+
321
+ def copy_vars_from(self, src_net: "Network") -> None:
322
+ """Copy the values of all variables from the given network, including sub-networks."""
323
+ names = [name for name in self.vars.keys() if name in src_net.vars]
324
+ tfutil.set_vars(tfutil.run({self.vars[name]: src_net.vars[name] for name in names}))
325
+
326
+ def copy_trainables_from(self, src_net: "Network") -> None:
327
+ """Copy the values of all trainable variables from the given network, including sub-networks."""
328
+ names = [name for name in self.trainables.keys() if name in src_net.trainables]
329
+ tfutil.set_vars(tfutil.run({self.vars[name]: src_net.vars[name] for name in names}))
330
+
331
+ def convert(self, new_func_name: str, new_name: str = None, **new_static_kwargs) -> "Network":
332
+ """Create new network with the given parameters, and copy all variables from this network."""
333
+ if new_name is None:
334
+ new_name = self.name
335
+ static_kwargs = dict(self.static_kwargs)
336
+ static_kwargs.update(new_static_kwargs)
337
+ net = Network(name=new_name, func_name=new_func_name, **static_kwargs)
338
+ net.copy_vars_from(self)
339
+ return net
340
+
341
+ def setup_as_moving_average_of(self, src_net: "Network", beta: TfExpressionEx = 0.99, beta_nontrainable: TfExpressionEx = 0.0) -> tf.Operation:
342
+ """Construct a TensorFlow op that updates the variables of this network
343
+ to be slightly closer to those of the given network."""
344
+ with tfutil.absolute_name_scope(self.scope + "/_MovingAvg"):
345
+ ops = []
346
+ for name, var in self.vars.items():
347
+ if name in src_net.vars:
348
+ cur_beta = beta if name in self.trainables else beta_nontrainable
349
+ new_value = tfutil.lerp(src_net.vars[name], var, cur_beta)
350
+ ops.append(var.assign(new_value))
351
+ return tf.group(*ops)
352
+
353
+ def run(self,
354
+ *in_arrays: Tuple[Union[np.ndarray, None], ...],
355
+ input_transform: dict = None,
356
+ output_transform: dict = None,
357
+ return_as_list: bool = False,
358
+ print_progress: bool = False,
359
+ minibatch_size: int = None,
360
+ num_gpus: int = 1,
361
+ assume_frozen: bool = False,
362
+ **dynamic_kwargs) -> Union[np.ndarray, Tuple[np.ndarray, ...], List[np.ndarray]]:
363
+ """Run this network for the given NumPy array(s), and return the output(s) as NumPy array(s).
364
+
365
+ Args:
366
+ input_transform: A dict specifying a custom transformation to be applied to the input tensor(s) before evaluating the network.
367
+ The dict must contain a 'func' field that points to a top-level function. The function is called with the input
368
+ TensorFlow expression(s) as positional arguments. Any remaining fields of the dict will be passed in as kwargs.
369
+ output_transform: A dict specifying a custom transformation to be applied to the output tensor(s) after evaluating the network.
370
+ The dict must contain a 'func' field that points to a top-level function. The function is called with the output
371
+ TensorFlow expression(s) as positional arguments. Any remaining fields of the dict will be passed in as kwargs.
372
+ return_as_list: True = return a list of NumPy arrays, False = return a single NumPy array, or a tuple if there are multiple outputs.
373
+ print_progress: Print progress to the console? Useful for very large input arrays.
374
+ minibatch_size: Maximum minibatch size to use, None = disable batching.
375
+ num_gpus: Number of GPUs to use.
376
+ assume_frozen: Improve multi-GPU performance by assuming that the trainable parameters will remain changed between calls.
377
+ dynamic_kwargs: Additional keyword arguments to be passed into the network build function.
378
+ """
379
+ assert len(in_arrays) == self.num_inputs
380
+ assert not all(arr is None for arr in in_arrays)
381
+ assert input_transform is None or util.is_top_level_function(input_transform["func"])
382
+ assert output_transform is None or util.is_top_level_function(output_transform["func"])
383
+ output_transform, dynamic_kwargs = _handle_legacy_output_transforms(output_transform, dynamic_kwargs)
384
+ num_items = in_arrays[0].shape[0]
385
+ if minibatch_size is None:
386
+ minibatch_size = num_items
387
+
388
+ # Construct unique hash key from all arguments that affect the TensorFlow graph.
389
+ key = dict(input_transform=input_transform, output_transform=output_transform, num_gpus=num_gpus, assume_frozen=assume_frozen, dynamic_kwargs=dynamic_kwargs)
390
+ def unwind_key(obj):
391
+ if isinstance(obj, dict):
392
+ return [(key, unwind_key(value)) for key, value in sorted(obj.items())]
393
+ if callable(obj):
394
+ return util.get_top_level_function_name(obj)
395
+ return obj
396
+ key = repr(unwind_key(key))
397
+
398
+ # Build graph.
399
+ if key not in self._run_cache:
400
+ with tfutil.absolute_name_scope(self.scope + "/_Run"), tf.control_dependencies(None):
401
+ with tf.device("/cpu:0"):
402
+ in_expr = [tf.placeholder(tf.float32, name=name) for name in self.input_names]
403
+ in_split = list(zip(*[tf.split(x, num_gpus) for x in in_expr]))
404
+
405
+ out_split = []
406
+ for gpu in range(num_gpus):
407
+ with tf.device("/gpu:%d" % gpu):
408
+ net_gpu = self.clone() if assume_frozen else self
409
+ in_gpu = in_split[gpu]
410
+
411
+ if input_transform is not None:
412
+ in_kwargs = dict(input_transform)
413
+ in_gpu = in_kwargs.pop("func")(*in_gpu, **in_kwargs)
414
+ in_gpu = [in_gpu] if tfutil.is_tf_expression(in_gpu) else list(in_gpu)
415
+
416
+ assert len(in_gpu) == self.num_inputs
417
+ out_gpu = net_gpu.get_output_for(*in_gpu, return_as_list=True, **dynamic_kwargs)
418
+
419
+ if output_transform is not None:
420
+ out_kwargs = dict(output_transform)
421
+ out_gpu = out_kwargs.pop("func")(*out_gpu, **out_kwargs)
422
+ out_gpu = [out_gpu] if tfutil.is_tf_expression(out_gpu) else list(out_gpu)
423
+
424
+ assert len(out_gpu) == self.num_outputs
425
+ out_split.append(out_gpu)
426
+
427
+ with tf.device("/cpu:0"):
428
+ out_expr = [tf.concat(outputs, axis=0) for outputs in zip(*out_split)]
429
+ self._run_cache[key] = in_expr, out_expr
430
+
431
+ # Run minibatches.
432
+ in_expr, out_expr = self._run_cache[key]
433
+ out_arrays = [np.empty([num_items] + expr.shape.as_list()[1:], expr.dtype.name) for expr in out_expr]
434
+
435
+ for mb_begin in range(0, num_items, minibatch_size):
436
+ if print_progress:
437
+ print("\r%d / %d" % (mb_begin, num_items), end="")
438
+
439
+ mb_end = min(mb_begin + minibatch_size, num_items)
440
+ mb_num = mb_end - mb_begin
441
+ mb_in = [src[mb_begin : mb_end] if src is not None else np.zeros([mb_num] + shape[1:]) for src, shape in zip(in_arrays, self.input_shapes)]
442
+ mb_out = tf.get_default_session().run(out_expr, dict(zip(in_expr, mb_in)))
443
+
444
+ for dst, src in zip(out_arrays, mb_out):
445
+ dst[mb_begin: mb_end] = src
446
+
447
+ # Done.
448
+ if print_progress:
449
+ print("\r%d / %d" % (num_items, num_items))
450
+
451
+ if not return_as_list:
452
+ out_arrays = out_arrays[0] if len(out_arrays) == 1 else tuple(out_arrays)
453
+ return out_arrays
454
+
455
+ def list_ops(self) -> List[TfExpression]:
456
+ include_prefix = self.scope + "/"
457
+ exclude_prefix = include_prefix + "_"
458
+ ops = tf.get_default_graph().get_operations()
459
+ ops = [op for op in ops if op.name.startswith(include_prefix)]
460
+ ops = [op for op in ops if not op.name.startswith(exclude_prefix)]
461
+ return ops
462
+
463
+ def list_layers(self) -> List[Tuple[str, TfExpression, List[TfExpression]]]:
464
+ """Returns a list of (layer_name, output_expr, trainable_vars) tuples corresponding to
465
+ individual layers of the network. Mainly intended to be used for reporting."""
466
+ layers = []
467
+
468
+ def recurse(scope, parent_ops, parent_vars, level):
469
+ # Ignore specific patterns.
470
+ if any(p in scope for p in ["/Shape", "/strided_slice", "/Cast", "/concat", "/Assign"]):
471
+ return
472
+
473
+ # Filter ops and vars by scope.
474
+ global_prefix = scope + "/"
475
+ local_prefix = global_prefix[len(self.scope) + 1:]
476
+ cur_ops = [op for op in parent_ops if op.name.startswith(global_prefix) or op.name == global_prefix[:-1]]
477
+ cur_vars = [(name, var) for name, var in parent_vars if name.startswith(local_prefix) or name == local_prefix[:-1]]
478
+ if not cur_ops and not cur_vars:
479
+ return
480
+
481
+ # Filter out all ops related to variables.
482
+ for var in [op for op in cur_ops if op.type.startswith("Variable")]:
483
+ var_prefix = var.name + "/"
484
+ cur_ops = [op for op in cur_ops if not op.name.startswith(var_prefix)]
485
+
486
+ # Scope does not contain ops as immediate children => recurse deeper.
487
+ contains_direct_ops = any("/" not in op.name[len(global_prefix):] and op.type not in ["Identity", "Cast", "Transpose"] for op in cur_ops)
488
+ if (level == 0 or not contains_direct_ops) and (len(cur_ops) + len(cur_vars)) > 1:
489
+ visited = set()
490
+ for rel_name in [op.name[len(global_prefix):] for op in cur_ops] + [name[len(local_prefix):] for name, _var in cur_vars]:
491
+ token = rel_name.split("/")[0]
492
+ if token not in visited:
493
+ recurse(global_prefix + token, cur_ops, cur_vars, level + 1)
494
+ visited.add(token)
495
+ return
496
+
497
+ # Report layer.
498
+ layer_name = scope[len(self.scope) + 1:]
499
+ layer_output = cur_ops[-1].outputs[0] if cur_ops else cur_vars[-1][1]
500
+ layer_trainables = [var for _name, var in cur_vars if var.trainable]
501
+ layers.append((layer_name, layer_output, layer_trainables))
502
+
503
+ recurse(self.scope, self.list_ops(), list(self.vars.items()), 0)
504
+ return layers
505
+
506
+ def print_layers(self, title: str = None, hide_layers_with_no_params: bool = False) -> None:
507
+ """Print a summary table of the network structure."""
508
+ rows = [[title if title is not None else self.name, "Params", "OutputShape", "WeightShape"]]
509
+ rows += [["---"] * 4]
510
+ total_params = 0
511
+
512
+ for layer_name, layer_output, layer_trainables in self.list_layers():
513
+ num_params = sum(int(np.prod(var.shape.as_list())) for var in layer_trainables)
514
+ weights = [var for var in layer_trainables if var.name.endswith("/weight:0")]
515
+ weights.sort(key=lambda x: len(x.name))
516
+ if len(weights) == 0 and len(layer_trainables) == 1:
517
+ weights = layer_trainables
518
+ total_params += num_params
519
+
520
+ if not hide_layers_with_no_params or num_params != 0:
521
+ num_params_str = str(num_params) if num_params > 0 else "-"
522
+ output_shape_str = str(layer_output.shape)
523
+ weight_shape_str = str(weights[0].shape) if len(weights) >= 1 else "-"
524
+ rows += [[layer_name, num_params_str, output_shape_str, weight_shape_str]]
525
+
526
+ rows += [["---"] * 4]
527
+ rows += [["Total", str(total_params), "", ""]]
528
+
529
+ widths = [max(len(cell) for cell in column) for column in zip(*rows)]
530
+ print()
531
+ for row in rows:
532
+ print(" ".join(cell + " " * (width - len(cell)) for cell, width in zip(row, widths)))
533
+ print()
534
+
535
+ def setup_weight_histograms(self, title: str = None) -> None:
536
+ """Construct summary ops to include histograms of all trainable parameters in TensorBoard."""
537
+ if title is None:
538
+ title = self.name
539
+
540
+ with tf.name_scope(None), tf.device(None), tf.control_dependencies(None):
541
+ for local_name, var in self.trainables.items():
542
+ if "/" in local_name:
543
+ p = local_name.split("/")
544
+ name = title + "_" + p[-1] + "/" + "_".join(p[:-1])
545
+ else:
546
+ name = title + "_toplevel/" + local_name
547
+
548
+ tf.summary.histogram(name, var)
549
+
550
+ #----------------------------------------------------------------------------
551
+ # Backwards-compatible emulation of legacy output transformation in Network.run().
552
+
553
+ _print_legacy_warning = True
554
+
555
+ def _handle_legacy_output_transforms(output_transform, dynamic_kwargs):
556
+ global _print_legacy_warning
557
+ legacy_kwargs = ["out_mul", "out_add", "out_shrink", "out_dtype"]
558
+ if not any(kwarg in dynamic_kwargs for kwarg in legacy_kwargs):
559
+ return output_transform, dynamic_kwargs
560
+
561
+ if _print_legacy_warning:
562
+ _print_legacy_warning = False
563
+ print()
564
+ print("WARNING: Old-style output transformations in Network.run() are deprecated.")
565
+ print("Consider using 'output_transform=dict(func=tflib.convert_images_to_uint8)'")
566
+ print("instead of 'out_mul=127.5, out_add=127.5, out_dtype=np.uint8'.")
567
+ print()
568
+ assert output_transform is None
569
+
570
+ new_kwargs = dict(dynamic_kwargs)
571
+ new_transform = {kwarg: new_kwargs.pop(kwarg) for kwarg in legacy_kwargs if kwarg in dynamic_kwargs}
572
+ new_transform["func"] = _legacy_output_transform_func
573
+ return new_transform, new_kwargs
574
+
575
+ def _legacy_output_transform_func(*expr, out_mul=1.0, out_add=0.0, out_shrink=1, out_dtype=None):
576
+ if out_mul != 1.0:
577
+ expr = [x * out_mul for x in expr]
578
+
579
+ if out_add != 0.0:
580
+ expr = [x + out_add for x in expr]
581
+
582
+ if out_shrink > 1:
583
+ ksize = [1, 1, out_shrink, out_shrink]
584
+ expr = [tf.nn.avg_pool(x, ksize=ksize, strides=ksize, padding="VALID", data_format="NCHW") for x in expr]
585
+
586
+ if out_dtype is not None:
587
+ if tf.as_dtype(out_dtype).is_integer:
588
+ expr = [tf.round(x) for x in expr]
589
+ expr = [tf.saturate_cast(x, out_dtype) for x in expr]
590
+ return expr
dnnlib/tflib/ops/__init__.py ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2019, NVIDIA Corporation. All rights reserved.
2
+ #
3
+ # This work is made available under the Nvidia Source Code License-NC.
4
+ # To view a copy of this license, visit
5
+ # https://nvlabs.github.io/stylegan2/license.html
6
+
7
+ # empty
dnnlib/tflib/ops/fused_bias_act.cu ADDED
@@ -0,0 +1,188 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright (c) 2019, NVIDIA Corporation. All rights reserved.
2
+ //
3
+ // This work is made available under the Nvidia Source Code License-NC.
4
+ // To view a copy of this license, visit
5
+ // https://nvlabs.github.io/stylegan2/license.html
6
+
7
+ #define EIGEN_USE_GPU
8
+ #define __CUDA_INCLUDE_COMPILER_INTERNAL_HEADERS__
9
+ #include "tensorflow/core/framework/op.h"
10
+ #include "tensorflow/core/framework/op_kernel.h"
11
+ #include "tensorflow/core/framework/shape_inference.h"
12
+ #include <stdio.h>
13
+
14
+ using namespace tensorflow;
15
+ using namespace tensorflow::shape_inference;
16
+
17
+ #define OP_CHECK_CUDA_ERROR(CTX, CUDA_CALL) do { cudaError_t err = CUDA_CALL; OP_REQUIRES(CTX, err == cudaSuccess, errors::Internal(cudaGetErrorName(err))); } while (false)
18
+
19
+ //------------------------------------------------------------------------
20
+ // CUDA kernel.
21
+
22
+ template <class T>
23
+ struct FusedBiasActKernelParams
24
+ {
25
+ const T* x; // [sizeX]
26
+ const T* b; // [sizeB] or NULL
27
+ const T* ref; // [sizeX] or NULL
28
+ T* y; // [sizeX]
29
+
30
+ int grad;
31
+ int axis;
32
+ int act;
33
+ float alpha;
34
+ float gain;
35
+
36
+ int sizeX;
37
+ int sizeB;
38
+ int stepB;
39
+ int loopX;
40
+ };
41
+
42
+ template <class T>
43
+ static __global__ void FusedBiasActKernel(const FusedBiasActKernelParams<T> p)
44
+ {
45
+ const float expRange = 80.0f;
46
+ const float halfExpRange = 40.0f;
47
+ const float seluScale = 1.0507009873554804934193349852946f;
48
+ const float seluAlpha = 1.6732632423543772848170429916717f;
49
+
50
+ // Loop over elements.
51
+ int xi = blockIdx.x * p.loopX * blockDim.x + threadIdx.x;
52
+ for (int loopIdx = 0; loopIdx < p.loopX && xi < p.sizeX; loopIdx++, xi += blockDim.x)
53
+ {
54
+ // Load and apply bias.
55
+ float x = (float)p.x[xi];
56
+ if (p.b)
57
+ x += (float)p.b[(xi / p.stepB) % p.sizeB];
58
+ float ref = (p.ref) ? (float)p.ref[xi] : 0.0f;
59
+ if (p.gain != 0.0f & p.act != 9)
60
+ ref /= p.gain;
61
+
62
+ // Evaluate activation func.
63
+ float y;
64
+ switch (p.act * 10 + p.grad)
65
+ {
66
+ // linear
67
+ default:
68
+ case 10: y = x; break;
69
+ case 11: y = x; break;
70
+ case 12: y = 0.0f; break;
71
+
72
+ // relu
73
+ case 20: y = (x > 0.0f) ? x : 0.0f; break;
74
+ case 21: y = (ref > 0.0f) ? x : 0.0f; break;
75
+ case 22: y = 0.0f; break;
76
+
77
+ // lrelu
78
+ case 30: y = (x > 0.0f) ? x : x * p.alpha; break;
79
+ case 31: y = (ref > 0.0f) ? x : x * p.alpha; break;
80
+ case 32: y = 0.0f; break;
81
+
82
+ // tanh
83
+ case 40: { float c = expf(x); float d = 1.0f / c; y = (x < -expRange) ? -1.0f : (x > expRange) ? 1.0f : (c - d) / (c + d); } break;
84
+ case 41: y = x * (1.0f - ref * ref); break;
85
+ case 42: y = x * (1.0f - ref * ref) * (-2.0f * ref); break;
86
+
87
+ // sigmoid
88
+ case 50: y = (x < -expRange) ? 0.0f : 1.0f / (expf(-x) + 1.0f); break;
89
+ case 51: y = x * ref * (1.0f - ref); break;
90
+ case 52: y = x * ref * (1.0f - ref) * (1.0f - 2.0f * ref); break;
91
+
92
+ // elu
93
+ case 60: y = (x >= 0.0f) ? x : expf(x) - 1.0f; break;
94
+ case 61: y = (ref >= 0.0f) ? x : x * (ref + 1.0f); break;
95
+ case 62: y = (ref >= 0.0f) ? 0.0f : x * (ref + 1.0f); break;
96
+
97
+ // selu
98
+ case 70: y = (x >= 0.0f) ? seluScale * x : (seluScale * seluAlpha) * (expf(x) - 1.0f); break;
99
+ case 71: y = (ref >= 0.0f) ? x * seluScale : x * (ref + seluScale * seluAlpha); break;
100
+ case 72: y = (ref >= 0.0f) ? 0.0f : x * (ref + seluScale * seluAlpha); break;
101
+
102
+ // softplus
103
+ case 80: y = (x > expRange) ? x : logf(expf(x) + 1.0f); break;
104
+ case 81: y = x * (1.0f - expf(-ref)); break;
105
+ case 82: { float c = expf(-ref); y = x * c * (1.0f - c); } break;
106
+
107
+ // swish
108
+ case 90: y = (x < -expRange) ? 0.0f : x / (expf(-x) + 1.0f); break;
109
+ case 91: { float c = expf(ref); float d = c + 1.0f; y = (ref > halfExpRange) ? x : x * c * (ref + d) / (d * d); } break;
110
+ case 92: { float c = expf(ref); float d = c + 1.0f; y = (ref > halfExpRange) ? 0.0f : x * c * (ref * (2.0f - d) + 2.0f * d) / (d * d * d); } break;
111
+ }
112
+
113
+ // Apply gain and store.
114
+ p.y[xi] = (T)(y * p.gain);
115
+ }
116
+ }
117
+
118
+ //------------------------------------------------------------------------
119
+ // TensorFlow op.
120
+
121
+ template <class T>
122
+ struct FusedBiasActOp : public OpKernel
123
+ {
124
+ FusedBiasActKernelParams<T> m_attribs;
125
+
126
+ FusedBiasActOp(OpKernelConstruction* ctx) : OpKernel(ctx)
127
+ {
128
+ memset(&m_attribs, 0, sizeof(m_attribs));
129
+ OP_REQUIRES_OK(ctx, ctx->GetAttr("grad", &m_attribs.grad));
130
+ OP_REQUIRES_OK(ctx, ctx->GetAttr("axis", &m_attribs.axis));
131
+ OP_REQUIRES_OK(ctx, ctx->GetAttr("act", &m_attribs.act));
132
+ OP_REQUIRES_OK(ctx, ctx->GetAttr("alpha", &m_attribs.alpha));
133
+ OP_REQUIRES_OK(ctx, ctx->GetAttr("gain", &m_attribs.gain));
134
+ OP_REQUIRES(ctx, m_attribs.grad >= 0, errors::InvalidArgument("grad must be non-negative"));
135
+ OP_REQUIRES(ctx, m_attribs.axis >= 0, errors::InvalidArgument("axis must be non-negative"));
136
+ OP_REQUIRES(ctx, m_attribs.act >= 0, errors::InvalidArgument("act must be non-negative"));
137
+ }
138
+
139
+ void Compute(OpKernelContext* ctx)
140
+ {
141
+ FusedBiasActKernelParams<T> p = m_attribs;
142
+ cudaStream_t stream = ctx->eigen_device<Eigen::GpuDevice>().stream();
143
+
144
+ const Tensor& x = ctx->input(0); // [...]
145
+ const Tensor& b = ctx->input(1); // [sizeB] or [0]
146
+ const Tensor& ref = ctx->input(2); // x.shape or [0]
147
+ p.x = x.flat<T>().data();
148
+ p.b = (b.NumElements()) ? b.flat<T>().data() : NULL;
149
+ p.ref = (ref.NumElements()) ? ref.flat<T>().data() : NULL;
150
+ OP_REQUIRES(ctx, b.NumElements() == 0 || m_attribs.axis < x.dims(), errors::InvalidArgument("axis out of bounds"));
151
+ OP_REQUIRES(ctx, b.dims() == 1, errors::InvalidArgument("b must have rank 1"));
152
+ OP_REQUIRES(ctx, b.NumElements() == 0 || b.NumElements() == x.dim_size(m_attribs.axis), errors::InvalidArgument("b has wrong number of elements"));
153
+ OP_REQUIRES(ctx, ref.NumElements() == ((p.grad == 0) ? 0 : x.NumElements()), errors::InvalidArgument("ref has wrong number of elements"));
154
+ OP_REQUIRES(ctx, x.NumElements() <= kint32max, errors::InvalidArgument("x is too large"));
155
+
156
+ p.sizeX = (int)x.NumElements();
157
+ p.sizeB = (int)b.NumElements();
158
+ p.stepB = 1;
159
+ for (int i = m_attribs.axis + 1; i < x.dims(); i++)
160
+ p.stepB *= (int)x.dim_size(i);
161
+
162
+ Tensor* y = NULL; // x.shape
163
+ OP_REQUIRES_OK(ctx, ctx->allocate_output(0, x.shape(), &y));
164
+ p.y = y->flat<T>().data();
165
+
166
+ p.loopX = 4;
167
+ int blockSize = 4 * 32;
168
+ int gridSize = (p.sizeX - 1) / (p.loopX * blockSize) + 1;
169
+ void* args[] = {&p};
170
+ OP_CHECK_CUDA_ERROR(ctx, cudaLaunchKernel((void*)FusedBiasActKernel<T>, gridSize, blockSize, args, 0, stream));
171
+ }
172
+ };
173
+
174
+ REGISTER_OP("FusedBiasAct")
175
+ .Input ("x: T")
176
+ .Input ("b: T")
177
+ .Input ("ref: T")
178
+ .Output ("y: T")
179
+ .Attr ("T: {float, half}")
180
+ .Attr ("grad: int = 0")
181
+ .Attr ("axis: int = 1")
182
+ .Attr ("act: int = 0")
183
+ .Attr ("alpha: float = 0.0")
184
+ .Attr ("gain: float = 1.0");
185
+ REGISTER_KERNEL_BUILDER(Name("FusedBiasAct").Device(DEVICE_GPU).TypeConstraint<float>("T"), FusedBiasActOp<float>);
186
+ REGISTER_KERNEL_BUILDER(Name("FusedBiasAct").Device(DEVICE_GPU).TypeConstraint<Eigen::half>("T"), FusedBiasActOp<Eigen::half>);
187
+
188
+ //------------------------------------------------------------------------
dnnlib/tflib/ops/fused_bias_act.py ADDED
@@ -0,0 +1,196 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2019, NVIDIA Corporation. All rights reserved.
2
+ #
3
+ # This work is made available under the Nvidia Source Code License-NC.
4
+ # To view a copy of this license, visit
5
+ # https://nvlabs.github.io/stylegan2/license.html
6
+
7
+ """Custom TensorFlow ops for efficient bias and activation."""
8
+
9
+ import os
10
+ import numpy as np
11
+ import tensorflow as tf
12
+ from .. import custom_ops
13
+ from ...util import EasyDict
14
+
15
+ def _get_plugin():
16
+ return custom_ops.get_plugin(os.path.splitext(__file__)[0] + '.cu')
17
+
18
+ #----------------------------------------------------------------------------
19
+
20
+ activation_funcs = {
21
+ 'linear': EasyDict(func=lambda x, **_: x, def_alpha=None, def_gain=1.0, cuda_idx=1, ref='y', zero_2nd_grad=True),
22
+ 'relu': EasyDict(func=lambda x, **_: tf.nn.relu(x), def_alpha=None, def_gain=np.sqrt(2), cuda_idx=2, ref='y', zero_2nd_grad=True),
23
+ 'lrelu': EasyDict(func=lambda x, alpha, **_: tf.nn.leaky_relu(x, alpha), def_alpha=0.2, def_gain=np.sqrt(2), cuda_idx=3, ref='y', zero_2nd_grad=True),
24
+ 'tanh': EasyDict(func=lambda x, **_: tf.nn.tanh(x), def_alpha=None, def_gain=1.0, cuda_idx=4, ref='y', zero_2nd_grad=False),
25
+ 'sigmoid': EasyDict(func=lambda x, **_: tf.nn.sigmoid(x), def_alpha=None, def_gain=1.0, cuda_idx=5, ref='y', zero_2nd_grad=False),
26
+ 'elu': EasyDict(func=lambda x, **_: tf.nn.elu(x), def_alpha=None, def_gain=1.0, cuda_idx=6, ref='y', zero_2nd_grad=False),
27
+ 'selu': EasyDict(func=lambda x, **_: tf.nn.selu(x), def_alpha=None, def_gain=1.0, cuda_idx=7, ref='y', zero_2nd_grad=False),
28
+ 'softplus': EasyDict(func=lambda x, **_: tf.nn.softplus(x), def_alpha=None, def_gain=1.0, cuda_idx=8, ref='y', zero_2nd_grad=False),
29
+ 'swish': EasyDict(func=lambda x, **_: tf.nn.sigmoid(x) * x, def_alpha=None, def_gain=np.sqrt(2), cuda_idx=9, ref='x', zero_2nd_grad=False),
30
+ }
31
+
32
+ #----------------------------------------------------------------------------
33
+
34
+ def fused_bias_act(x, b=None, axis=1, act='linear', alpha=None, gain=None, impl='cuda'):
35
+ r"""Fused bias and activation function.
36
+
37
+ Adds bias `b` to activation tensor `x`, evaluates activation function `act`,
38
+ and scales the result by `gain`. Each of the steps is optional. In most cases,
39
+ the fused op is considerably more efficient than performing the same calculation
40
+ using standard TensorFlow ops. It supports first and second order gradients,
41
+ but not third order gradients.
42
+
43
+ Args:
44
+ x: Input activation tensor. Can have any shape, but if `b` is defined, the
45
+ dimension corresponding to `axis`, as well as the rank, must be known.
46
+ b: Bias vector, or `None` to disable. Must be a 1D tensor of the same type
47
+ as `x`. The shape must be known, and it must match the dimension of `x`
48
+ corresponding to `axis`.
49
+ axis: The dimension in `x` corresponding to the elements of `b`.
50
+ The value of `axis` is ignored if `b` is not specified.
51
+ act: Name of the activation function to evaluate, or `"linear"` to disable.
52
+ Can be e.g. `"relu"`, `"lrelu"`, `"tanh"`, `"sigmoid"`, `"swish"`, etc.
53
+ See `activation_funcs` for a full list. `None` is not allowed.
54
+ alpha: Shape parameter for the activation function, or `None` to use the default.
55
+ gain: Scaling factor for the output tensor, or `None` to use default.
56
+ See `activation_funcs` for the default scaling of each activation function.
57
+ If unsure, consider specifying `1.0`.
58
+ impl: Name of the implementation to use. Can be `"ref"` or `"cuda"` (default).
59
+
60
+ Returns:
61
+ Tensor of the same shape and datatype as `x`.
62
+ """
63
+
64
+ impl_dict = {
65
+ 'ref': _fused_bias_act_ref,
66
+ 'cuda': _fused_bias_act_cuda,
67
+ }
68
+ return impl_dict[impl](x=x, b=b, axis=axis, act=act, alpha=alpha, gain=gain)
69
+
70
+ #----------------------------------------------------------------------------
71
+
72
+ def _fused_bias_act_ref(x, b, axis, act, alpha, gain):
73
+ """Slow reference implementation of `fused_bias_act()` using standard TensorFlow ops."""
74
+
75
+ # Validate arguments.
76
+ x = tf.convert_to_tensor(x)
77
+ b = tf.convert_to_tensor(b) if b is not None else tf.constant([], dtype=x.dtype)
78
+ act_spec = activation_funcs[act]
79
+ assert b.shape.rank == 1 and (b.shape[0] == 0 or b.shape[0] == x.shape[axis])
80
+ assert b.shape[0] == 0 or 0 <= axis < x.shape.rank
81
+ if alpha is None:
82
+ alpha = act_spec.def_alpha
83
+ if gain is None:
84
+ gain = act_spec.def_gain
85
+
86
+ # Add bias.
87
+ if b.shape[0] != 0:
88
+ x += tf.reshape(b, [-1 if i == axis else 1 for i in range(x.shape.rank)])
89
+
90
+ # Evaluate activation function.
91
+ x = act_spec.func(x, alpha=alpha)
92
+
93
+ # Scale by gain.
94
+ if gain != 1:
95
+ x *= gain
96
+ return x
97
+
98
+ #----------------------------------------------------------------------------
99
+
100
+ def _fused_bias_act_cuda(x, b, axis, act, alpha, gain):
101
+ """Fast CUDA implementation of `fused_bias_act()` using custom ops."""
102
+
103
+ # Validate arguments.
104
+ x = tf.convert_to_tensor(x)
105
+ empty_tensor = tf.constant([], dtype=x.dtype)
106
+ b = tf.convert_to_tensor(b) if b is not None else empty_tensor
107
+ act_spec = activation_funcs[act]
108
+ assert b.shape.rank == 1 and (b.shape[0] == 0 or b.shape[0] == x.shape[axis])
109
+ assert b.shape[0] == 0 or 0 <= axis < x.shape.rank
110
+ if alpha is None:
111
+ alpha = act_spec.def_alpha
112
+ if gain is None:
113
+ gain = act_spec.def_gain
114
+
115
+ # Special cases.
116
+ if act == 'linear' and b is None and gain == 1.0:
117
+ return x
118
+ if act_spec.cuda_idx is None:
119
+ return _fused_bias_act_ref(x=x, b=b, axis=axis, act=act, alpha=alpha, gain=gain)
120
+
121
+ # CUDA kernel.
122
+ cuda_kernel = _get_plugin().fused_bias_act
123
+ cuda_kwargs = dict(axis=axis, act=act_spec.cuda_idx, alpha=alpha, gain=gain)
124
+
125
+ # Forward pass: y = func(x, b).
126
+ def func_y(x, b):
127
+ y = cuda_kernel(x=x, b=b, ref=empty_tensor, grad=0, **cuda_kwargs)
128
+ y.set_shape(x.shape)
129
+ return y
130
+
131
+ # Backward pass: dx, db = grad(dy, x, y)
132
+ def grad_dx(dy, x, y):
133
+ ref = {'x': x, 'y': y}[act_spec.ref]
134
+ dx = cuda_kernel(x=dy, b=empty_tensor, ref=ref, grad=1, **cuda_kwargs)
135
+ dx.set_shape(x.shape)
136
+ return dx
137
+ def grad_db(dx):
138
+ if b.shape[0] == 0:
139
+ return empty_tensor
140
+ db = dx
141
+ if axis < x.shape.rank - 1:
142
+ db = tf.reduce_sum(db, list(range(axis + 1, x.shape.rank)))
143
+ if axis > 0:
144
+ db = tf.reduce_sum(db, list(range(axis)))
145
+ db.set_shape(b.shape)
146
+ return db
147
+
148
+ # Second order gradients: d_dy, d_x = grad2(d_dx, d_db, x, y)
149
+ def grad2_d_dy(d_dx, d_db, x, y):
150
+ ref = {'x': x, 'y': y}[act_spec.ref]
151
+ d_dy = cuda_kernel(x=d_dx, b=d_db, ref=ref, grad=1, **cuda_kwargs)
152
+ d_dy.set_shape(x.shape)
153
+ return d_dy
154
+ def grad2_d_x(d_dx, d_db, x, y):
155
+ ref = {'x': x, 'y': y}[act_spec.ref]
156
+ d_x = cuda_kernel(x=d_dx, b=d_db, ref=ref, grad=2, **cuda_kwargs)
157
+ d_x.set_shape(x.shape)
158
+ return d_x
159
+
160
+ # Fast version for piecewise-linear activation funcs.
161
+ @tf.custom_gradient
162
+ def func_zero_2nd_grad(x, b):
163
+ y = func_y(x, b)
164
+ @tf.custom_gradient
165
+ def grad(dy):
166
+ dx = grad_dx(dy, x, y)
167
+ db = grad_db(dx)
168
+ def grad2(d_dx, d_db):
169
+ d_dy = grad2_d_dy(d_dx, d_db, x, y)
170
+ return d_dy
171
+ return (dx, db), grad2
172
+ return y, grad
173
+
174
+ # Slow version for general activation funcs.
175
+ @tf.custom_gradient
176
+ def func_nonzero_2nd_grad(x, b):
177
+ y = func_y(x, b)
178
+ def grad_wrap(dy):
179
+ @tf.custom_gradient
180
+ def grad_impl(dy, x):
181
+ dx = grad_dx(dy, x, y)
182
+ db = grad_db(dx)
183
+ def grad2(d_dx, d_db):
184
+ d_dy = grad2_d_dy(d_dx, d_db, x, y)
185
+ d_x = grad2_d_x(d_dx, d_db, x, y)
186
+ return d_dy, d_x
187
+ return (dx, db), grad2
188
+ return grad_impl(dy, x)
189
+ return y, grad_wrap
190
+
191
+ # Which version to use?
192
+ if act_spec.zero_2nd_grad:
193
+ return func_zero_2nd_grad(x, b)
194
+ return func_nonzero_2nd_grad(x, b)
195
+
196
+ #----------------------------------------------------------------------------
dnnlib/tflib/ops/upfirdn_2d.cu ADDED
@@ -0,0 +1,326 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright (c) 2019, NVIDIA Corporation. All rights reserved.
2
+ //
3
+ // This work is made available under the Nvidia Source Code License-NC.
4
+ // To view a copy of this license, visit
5
+ // https://nvlabs.github.io/stylegan2/license.html
6
+
7
+ #define EIGEN_USE_GPU
8
+ #define __CUDA_INCLUDE_COMPILER_INTERNAL_HEADERS__
9
+ #include "tensorflow/core/framework/op.h"
10
+ #include "tensorflow/core/framework/op_kernel.h"
11
+ #include "tensorflow/core/framework/shape_inference.h"
12
+ #include <stdio.h>
13
+
14
+ using namespace tensorflow;
15
+ using namespace tensorflow::shape_inference;
16
+
17
+ //------------------------------------------------------------------------
18
+ // Helpers.
19
+
20
+ #define OP_CHECK_CUDA_ERROR(CTX, CUDA_CALL) do { cudaError_t err = CUDA_CALL; OP_REQUIRES(CTX, err == cudaSuccess, errors::Internal(cudaGetErrorName(err))); } while (false)
21
+
22
+ static __host__ __device__ __forceinline__ int floorDiv(int a, int b)
23
+ {
24
+ int c = a / b;
25
+ if (c * b > a)
26
+ c--;
27
+ return c;
28
+ }
29
+
30
+ //------------------------------------------------------------------------
31
+ // CUDA kernel params.
32
+
33
+ template <class T>
34
+ struct UpFirDn2DKernelParams
35
+ {
36
+ const T* x; // [majorDim, inH, inW, minorDim]
37
+ const T* k; // [kernelH, kernelW]
38
+ T* y; // [majorDim, outH, outW, minorDim]
39
+
40
+ int upx;
41
+ int upy;
42
+ int downx;
43
+ int downy;
44
+ int padx0;
45
+ int padx1;
46
+ int pady0;
47
+ int pady1;
48
+
49
+ int majorDim;
50
+ int inH;
51
+ int inW;
52
+ int minorDim;
53
+ int kernelH;
54
+ int kernelW;
55
+ int outH;
56
+ int outW;
57
+ int loopMajor;
58
+ int loopX;
59
+ };
60
+
61
+ //------------------------------------------------------------------------
62
+ // General CUDA implementation for large filter kernels.
63
+
64
+ template <class T>
65
+ static __global__ void UpFirDn2DKernel_large(const UpFirDn2DKernelParams<T> p)
66
+ {
67
+ // Calculate thread index.
68
+ int minorIdx = blockIdx.x * blockDim.x + threadIdx.x;
69
+ int outY = minorIdx / p.minorDim;
70
+ minorIdx -= outY * p.minorDim;
71
+ int outXBase = blockIdx.y * p.loopX * blockDim.y + threadIdx.y;
72
+ int majorIdxBase = blockIdx.z * p.loopMajor;
73
+ if (outXBase >= p.outW || outY >= p.outH || majorIdxBase >= p.majorDim)
74
+ return;
75
+
76
+ // Setup Y receptive field.
77
+ int midY = outY * p.downy + p.upy - 1 - p.pady0;
78
+ int inY = min(max(floorDiv(midY, p.upy), 0), p.inH);
79
+ int h = min(max(floorDiv(midY + p.kernelH, p.upy), 0), p.inH) - inY;
80
+ int kernelY = midY + p.kernelH - (inY + 1) * p.upy;
81
+
82
+ // Loop over majorDim and outX.
83
+ for (int loopMajor = 0, majorIdx = majorIdxBase; loopMajor < p.loopMajor && majorIdx < p.majorDim; loopMajor++, majorIdx++)
84
+ for (int loopX = 0, outX = outXBase; loopX < p.loopX && outX < p.outW; loopX++, outX += blockDim.y)
85
+ {
86
+ // Setup X receptive field.
87
+ int midX = outX * p.downx + p.upx - 1 - p.padx0;
88
+ int inX = min(max(floorDiv(midX, p.upx), 0), p.inW);
89
+ int w = min(max(floorDiv(midX + p.kernelW, p.upx), 0), p.inW) - inX;
90
+ int kernelX = midX + p.kernelW - (inX + 1) * p.upx;
91
+
92
+ // Initialize pointers.
93
+ const T* xp = &p.x[((majorIdx * p.inH + inY) * p.inW + inX) * p.minorDim + minorIdx];
94
+ const T* kp = &p.k[kernelY * p.kernelW + kernelX];
95
+ int xpx = p.minorDim;
96
+ int kpx = -p.upx;
97
+ int xpy = p.inW * p.minorDim;
98
+ int kpy = -p.upy * p.kernelW;
99
+
100
+ // Inner loop.
101
+ float v = 0.0f;
102
+ for (int y = 0; y < h; y++)
103
+ {
104
+ for (int x = 0; x < w; x++)
105
+ {
106
+ v += (float)(*xp) * (float)(*kp);
107
+ xp += xpx;
108
+ kp += kpx;
109
+ }
110
+ xp += xpy - w * xpx;
111
+ kp += kpy - w * kpx;
112
+ }
113
+
114
+ // Store result.
115
+ p.y[((majorIdx * p.outH + outY) * p.outW + outX) * p.minorDim + minorIdx] = (T)v;
116
+ }
117
+ }
118
+
119
+ //------------------------------------------------------------------------
120
+ // Specialized CUDA implementation for small filter kernels.
121
+
122
+ template <class T, int upx, int upy, int downx, int downy, int kernelW, int kernelH, int tileOutW, int tileOutH>
123
+ static __global__ void UpFirDn2DKernel_small(const UpFirDn2DKernelParams<T> p)
124
+ {
125
+ //assert(kernelW % upx == 0);
126
+ //assert(kernelH % upy == 0);
127
+ const int tileInW = ((tileOutW - 1) * downx + kernelW - 1) / upx + 1;
128
+ const int tileInH = ((tileOutH - 1) * downy + kernelH - 1) / upy + 1;
129
+ __shared__ volatile float sk[kernelH][kernelW];
130
+ __shared__ volatile float sx[tileInH][tileInW];
131
+
132
+ // Calculate tile index.
133
+ int minorIdx = blockIdx.x;
134
+ int tileOutY = minorIdx / p.minorDim;
135
+ minorIdx -= tileOutY * p.minorDim;
136
+ tileOutY *= tileOutH;
137
+ int tileOutXBase = blockIdx.y * p.loopX * tileOutW;
138
+ int majorIdxBase = blockIdx.z * p.loopMajor;
139
+ if (tileOutXBase >= p.outW | tileOutY >= p.outH | majorIdxBase >= p.majorDim)
140
+ return;
141
+
142
+ // Load filter kernel (flipped).
143
+ for (int tapIdx = threadIdx.x; tapIdx < kernelH * kernelW; tapIdx += blockDim.x)
144
+ {
145
+ int ky = tapIdx / kernelW;
146
+ int kx = tapIdx - ky * kernelW;
147
+ float v = 0.0f;
148
+ if (kx < p.kernelW & ky < p.kernelH)
149
+ v = (float)p.k[(p.kernelH - 1 - ky) * p.kernelW + (p.kernelW - 1 - kx)];
150
+ sk[ky][kx] = v;
151
+ }
152
+
153
+ // Loop over majorDim and outX.
154
+ for (int loopMajor = 0, majorIdx = majorIdxBase; loopMajor < p.loopMajor & majorIdx < p.majorDim; loopMajor++, majorIdx++)
155
+ for (int loopX = 0, tileOutX = tileOutXBase; loopX < p.loopX & tileOutX < p.outW; loopX++, tileOutX += tileOutW)
156
+ {
157
+ // Load input pixels.
158
+ int tileMidX = tileOutX * downx + upx - 1 - p.padx0;
159
+ int tileMidY = tileOutY * downy + upy - 1 - p.pady0;
160
+ int tileInX = floorDiv(tileMidX, upx);
161
+ int tileInY = floorDiv(tileMidY, upy);
162
+ __syncthreads();
163
+ for (int inIdx = threadIdx.x; inIdx < tileInH * tileInW; inIdx += blockDim.x)
164
+ {
165
+ int relInY = inIdx / tileInW;
166
+ int relInX = inIdx - relInY * tileInW;
167
+ int inX = relInX + tileInX;
168
+ int inY = relInY + tileInY;
169
+ float v = 0.0f;
170
+ if (inX >= 0 & inY >= 0 & inX < p.inW & inY < p.inH)
171
+ v = (float)p.x[((majorIdx * p.inH + inY) * p.inW + inX) * p.minorDim + minorIdx];
172
+ sx[relInY][relInX] = v;
173
+ }
174
+
175
+ // Loop over output pixels.
176
+ __syncthreads();
177
+ for (int outIdx = threadIdx.x; outIdx < tileOutH * tileOutW; outIdx += blockDim.x)
178
+ {
179
+ int relOutY = outIdx / tileOutW;
180
+ int relOutX = outIdx - relOutY * tileOutW;
181
+ int outX = relOutX + tileOutX;
182
+ int outY = relOutY + tileOutY;
183
+
184
+ // Setup receptive field.
185
+ int midX = tileMidX + relOutX * downx;
186
+ int midY = tileMidY + relOutY * downy;
187
+ int inX = floorDiv(midX, upx);
188
+ int inY = floorDiv(midY, upy);
189
+ int relInX = inX - tileInX;
190
+ int relInY = inY - tileInY;
191
+ int kernelX = (inX + 1) * upx - midX - 1; // flipped
192
+ int kernelY = (inY + 1) * upy - midY - 1; // flipped
193
+
194
+ // Inner loop.
195
+ float v = 0.0f;
196
+ #pragma unroll
197
+ for (int y = 0; y < kernelH / upy; y++)
198
+ #pragma unroll
199
+ for (int x = 0; x < kernelW / upx; x++)
200
+ v += sx[relInY + y][relInX + x] * sk[kernelY + y * upy][kernelX + x * upx];
201
+
202
+ // Store result.
203
+ if (outX < p.outW & outY < p.outH)
204
+ p.y[((majorIdx * p.outH + outY) * p.outW + outX) * p.minorDim + minorIdx] = (T)v;
205
+ }
206
+ }
207
+ }
208
+
209
+ //------------------------------------------------------------------------
210
+ // TensorFlow op.
211
+
212
+ template <class T>
213
+ struct UpFirDn2DOp : public OpKernel
214
+ {
215
+ UpFirDn2DKernelParams<T> m_attribs;
216
+
217
+ UpFirDn2DOp(OpKernelConstruction* ctx) : OpKernel(ctx)
218
+ {
219
+ memset(&m_attribs, 0, sizeof(m_attribs));
220
+ OP_REQUIRES_OK(ctx, ctx->GetAttr("upx", &m_attribs.upx));
221
+ OP_REQUIRES_OK(ctx, ctx->GetAttr("upy", &m_attribs.upy));
222
+ OP_REQUIRES_OK(ctx, ctx->GetAttr("downx", &m_attribs.downx));
223
+ OP_REQUIRES_OK(ctx, ctx->GetAttr("downy", &m_attribs.downy));
224
+ OP_REQUIRES_OK(ctx, ctx->GetAttr("padx0", &m_attribs.padx0));
225
+ OP_REQUIRES_OK(ctx, ctx->GetAttr("padx1", &m_attribs.padx1));
226
+ OP_REQUIRES_OK(ctx, ctx->GetAttr("pady0", &m_attribs.pady0));
227
+ OP_REQUIRES_OK(ctx, ctx->GetAttr("pady1", &m_attribs.pady1));
228
+ OP_REQUIRES(ctx, m_attribs.upx >= 1 && m_attribs.upy >= 1, errors::InvalidArgument("upx and upy must be at least 1x1"));
229
+ OP_REQUIRES(ctx, m_attribs.downx >= 1 && m_attribs.downy >= 1, errors::InvalidArgument("downx and downy must be at least 1x1"));
230
+ }
231
+
232
+ void Compute(OpKernelContext* ctx)
233
+ {
234
+ UpFirDn2DKernelParams<T> p = m_attribs;
235
+ cudaStream_t stream = ctx->eigen_device<Eigen::GpuDevice>().stream();
236
+
237
+ const Tensor& x = ctx->input(0); // [majorDim, inH, inW, minorDim]
238
+ const Tensor& k = ctx->input(1); // [kernelH, kernelW]
239
+ p.x = x.flat<T>().data();
240
+ p.k = k.flat<T>().data();
241
+ OP_REQUIRES(ctx, x.dims() == 4, errors::InvalidArgument("input must have rank 4"));
242
+ OP_REQUIRES(ctx, k.dims() == 2, errors::InvalidArgument("kernel must have rank 2"));
243
+ OP_REQUIRES(ctx, x.NumElements() <= kint32max, errors::InvalidArgument("input too large"));
244
+ OP_REQUIRES(ctx, k.NumElements() <= kint32max, errors::InvalidArgument("kernel too large"));
245
+
246
+ p.majorDim = (int)x.dim_size(0);
247
+ p.inH = (int)x.dim_size(1);
248
+ p.inW = (int)x.dim_size(2);
249
+ p.minorDim = (int)x.dim_size(3);
250
+ p.kernelH = (int)k.dim_size(0);
251
+ p.kernelW = (int)k.dim_size(1);
252
+ OP_REQUIRES(ctx, p.kernelW >= 1 && p.kernelH >= 1, errors::InvalidArgument("kernel must be at least 1x1"));
253
+
254
+ p.outW = (p.inW * p.upx + p.padx0 + p.padx1 - p.kernelW + p.downx) / p.downx;
255
+ p.outH = (p.inH * p.upy + p.pady0 + p.pady1 - p.kernelH + p.downy) / p.downy;
256
+ OP_REQUIRES(ctx, p.outW >= 1 && p.outH >= 1, errors::InvalidArgument("output must be at least 1x1"));
257
+
258
+ Tensor* y = NULL; // [majorDim, outH, outW, minorDim]
259
+ TensorShape ys;
260
+ ys.AddDim(p.majorDim);
261
+ ys.AddDim(p.outH);
262
+ ys.AddDim(p.outW);
263
+ ys.AddDim(p.minorDim);
264
+ OP_REQUIRES_OK(ctx, ctx->allocate_output(0, ys, &y));
265
+ p.y = y->flat<T>().data();
266
+ OP_REQUIRES(ctx, y->NumElements() <= kint32max, errors::InvalidArgument("output too large"));
267
+
268
+ // Choose CUDA kernel to use.
269
+ void* cudaKernel = (void*)UpFirDn2DKernel_large<T>;
270
+ int tileOutW = -1;
271
+ int tileOutH = -1;
272
+ if (p.upx == 1 && p.upy == 1 && p.downx == 1 && p.downy == 1 && p.kernelW <= 7 && p.kernelH <= 7) { cudaKernel = (void*)UpFirDn2DKernel_small<T, 1,1, 1,1, 7,7, 64,16>; tileOutW = 64; tileOutH = 16; }
273
+ if (p.upx == 1 && p.upy == 1 && p.downx == 1 && p.downy == 1 && p.kernelW <= 6 && p.kernelH <= 6) { cudaKernel = (void*)UpFirDn2DKernel_small<T, 1,1, 1,1, 6,6, 64,16>; tileOutW = 64; tileOutH = 16; }
274
+ if (p.upx == 1 && p.upy == 1 && p.downx == 1 && p.downy == 1 && p.kernelW <= 5 && p.kernelH <= 5) { cudaKernel = (void*)UpFirDn2DKernel_small<T, 1,1, 1,1, 5,5, 64,16>; tileOutW = 64; tileOutH = 16; }
275
+ if (p.upx == 1 && p.upy == 1 && p.downx == 1 && p.downy == 1 && p.kernelW <= 4 && p.kernelH <= 4) { cudaKernel = (void*)UpFirDn2DKernel_small<T, 1,1, 1,1, 4,4, 64,16>; tileOutW = 64; tileOutH = 16; }
276
+ if (p.upx == 1 && p.upy == 1 && p.downx == 1 && p.downy == 1 && p.kernelW <= 3 && p.kernelH <= 3) { cudaKernel = (void*)UpFirDn2DKernel_small<T, 1,1, 1,1, 3,3, 64,16>; tileOutW = 64; tileOutH = 16; }
277
+ if (p.upx == 2 && p.upy == 2 && p.downx == 1 && p.downy == 1 && p.kernelW <= 8 && p.kernelH <= 8) { cudaKernel = (void*)UpFirDn2DKernel_small<T, 2,2, 1,1, 8,8, 64,16>; tileOutW = 64; tileOutH = 16; }
278
+ if (p.upx == 2 && p.upy == 2 && p.downx == 1 && p.downy == 1 && p.kernelW <= 6 && p.kernelH <= 6) { cudaKernel = (void*)UpFirDn2DKernel_small<T, 2,2, 1,1, 6,6, 64,16>; tileOutW = 64; tileOutH = 16; }
279
+ if (p.upx == 2 && p.upy == 2 && p.downx == 1 && p.downy == 1 && p.kernelW <= 4 && p.kernelH <= 4) { cudaKernel = (void*)UpFirDn2DKernel_small<T, 2,2, 1,1, 4,4, 64,16>; tileOutW = 64; tileOutH = 16; }
280
+ if (p.upx == 2 && p.upy == 2 && p.downx == 1 && p.downy == 1 && p.kernelW <= 2 && p.kernelH <= 2) { cudaKernel = (void*)UpFirDn2DKernel_small<T, 2,2, 1,1, 2,2, 64,16>; tileOutW = 64; tileOutH = 16; }
281
+ if (p.upx == 1 && p.upy == 1 && p.downx == 2 && p.downy == 2 && p.kernelW <= 8 && p.kernelH <= 8) { cudaKernel = (void*)UpFirDn2DKernel_small<T, 1,1, 2,2, 8,8, 32,8>; tileOutW = 32; tileOutH = 8; }
282
+ if (p.upx == 1 && p.upy == 1 && p.downx == 2 && p.downy == 2 && p.kernelW <= 6 && p.kernelH <= 6) { cudaKernel = (void*)UpFirDn2DKernel_small<T, 1,1, 2,2, 6,6, 32,8>; tileOutW = 32; tileOutH = 8; }
283
+ if (p.upx == 1 && p.upy == 1 && p.downx == 2 && p.downy == 2 && p.kernelW <= 4 && p.kernelH <= 4) { cudaKernel = (void*)UpFirDn2DKernel_small<T, 1,1, 2,2, 4,4, 32,8>; tileOutW = 32; tileOutH = 8; }
284
+ if (p.upx == 1 && p.upy == 1 && p.downx == 2 && p.downy == 2 && p.kernelW <= 2 && p.kernelH <= 2) { cudaKernel = (void*)UpFirDn2DKernel_small<T, 1,1, 2,2, 2,2, 32,8>; tileOutW = 32; tileOutH = 8; }
285
+
286
+ // Choose launch params.
287
+ dim3 blockSize;
288
+ dim3 gridSize;
289
+ if (tileOutW > 0 && tileOutH > 0) // small
290
+ {
291
+ p.loopMajor = (p.majorDim - 1) / 16384 + 1;
292
+ p.loopX = 1;
293
+ blockSize = dim3(32 * 8, 1, 1);
294
+ gridSize = dim3(((p.outH - 1) / tileOutH + 1) * p.minorDim, (p.outW - 1) / (p.loopX * tileOutW) + 1, (p.majorDim - 1) / p.loopMajor + 1);
295
+ }
296
+ else // large
297
+ {
298
+ p.loopMajor = (p.majorDim - 1) / 16384 + 1;
299
+ p.loopX = 4;
300
+ blockSize = dim3(4, 32, 1);
301
+ gridSize = dim3((p.outH * p.minorDim - 1) / blockSize.x + 1, (p.outW - 1) / (p.loopX * blockSize.y) + 1, (p.majorDim - 1) / p.loopMajor + 1);
302
+ }
303
+
304
+ // Launch CUDA kernel.
305
+ void* args[] = {&p};
306
+ OP_CHECK_CUDA_ERROR(ctx, cudaLaunchKernel(cudaKernel, gridSize, blockSize, args, 0, stream));
307
+ }
308
+ };
309
+
310
+ REGISTER_OP("UpFirDn2D")
311
+ .Input ("x: T")
312
+ .Input ("k: T")
313
+ .Output ("y: T")
314
+ .Attr ("T: {float, half}")
315
+ .Attr ("upx: int = 1")
316
+ .Attr ("upy: int = 1")
317
+ .Attr ("downx: int = 1")
318
+ .Attr ("downy: int = 1")
319
+ .Attr ("padx0: int = 0")
320
+ .Attr ("padx1: int = 0")
321
+ .Attr ("pady0: int = 0")
322
+ .Attr ("pady1: int = 0");
323
+ REGISTER_KERNEL_BUILDER(Name("UpFirDn2D").Device(DEVICE_GPU).TypeConstraint<float>("T"), UpFirDn2DOp<float>);
324
+ REGISTER_KERNEL_BUILDER(Name("UpFirDn2D").Device(DEVICE_GPU).TypeConstraint<Eigen::half>("T"), UpFirDn2DOp<Eigen::half>);
325
+
326
+ //------------------------------------------------------------------------
dnnlib/tflib/ops/upfirdn_2d.py ADDED
@@ -0,0 +1,364 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2019, NVIDIA Corporation. All rights reserved.
2
+ #
3
+ # This work is made available under the Nvidia Source Code License-NC.
4
+ # To view a copy of this license, visit
5
+ # https://nvlabs.github.io/stylegan2/license.html
6
+
7
+ """Custom TensorFlow ops for efficient resampling of 2D images."""
8
+
9
+ import os
10
+ import numpy as np
11
+ import tensorflow as tf
12
+ from .. import custom_ops
13
+
14
+ def _get_plugin():
15
+ return custom_ops.get_plugin(os.path.splitext(__file__)[0] + '.cu')
16
+
17
+ #----------------------------------------------------------------------------
18
+
19
+ def upfirdn_2d(x, k, upx=1, upy=1, downx=1, downy=1, padx0=0, padx1=0, pady0=0, pady1=0, impl='cuda'):
20
+ r"""Pad, upsample, FIR filter, and downsample a batch of 2D images.
21
+
22
+ Accepts a batch of 2D images of the shape `[majorDim, inH, inW, minorDim]`
23
+ and performs the following operations for each image, batched across
24
+ `majorDim` and `minorDim`:
25
+
26
+ 1. Pad the image with zeros by the specified number of pixels on each side
27
+ (`padx0`, `padx1`, `pady0`, `pady1`). Specifying a negative value
28
+ corresponds to cropping the image.
29
+
30
+ 2. Upsample the image by inserting the zeros after each pixel (`upx`, `upy`).
31
+
32
+ 3. Convolve the image with the specified 2D FIR filter (`k`), shrinking the
33
+ image so that the footprint of all output pixels lies within the input image.
34
+
35
+ 4. Downsample the image by throwing away pixels (`downx`, `downy`).
36
+
37
+ This sequence of operations bears close resemblance to scipy.signal.upfirdn().
38
+ The fused op is considerably more efficient than performing the same calculation
39
+ using standard TensorFlow ops. It supports gradients of arbitrary order.
40
+
41
+ Args:
42
+ x: Input tensor of the shape `[majorDim, inH, inW, minorDim]`.
43
+ k: 2D FIR filter of the shape `[firH, firW]`.
44
+ upx: Integer upsampling factor along the X-axis (default: 1).
45
+ upy: Integer upsampling factor along the Y-axis (default: 1).
46
+ downx: Integer downsampling factor along the X-axis (default: 1).
47
+ downy: Integer downsampling factor along the Y-axis (default: 1).
48
+ padx0: Number of pixels to pad on the left side (default: 0).
49
+ padx1: Number of pixels to pad on the right side (default: 0).
50
+ pady0: Number of pixels to pad on the top side (default: 0).
51
+ pady1: Number of pixels to pad on the bottom side (default: 0).
52
+ impl: Name of the implementation to use. Can be `"ref"` or `"cuda"` (default).
53
+
54
+ Returns:
55
+ Tensor of the shape `[majorDim, outH, outW, minorDim]`, and same datatype as `x`.
56
+ """
57
+
58
+ impl_dict = {
59
+ 'ref': _upfirdn_2d_ref,
60
+ 'cuda': _upfirdn_2d_cuda,
61
+ }
62
+ return impl_dict[impl](x=x, k=k, upx=upx, upy=upy, downx=downx, downy=downy, padx0=padx0, padx1=padx1, pady0=pady0, pady1=pady1)
63
+
64
+ #----------------------------------------------------------------------------
65
+
66
+ def _upfirdn_2d_ref(x, k, upx, upy, downx, downy, padx0, padx1, pady0, pady1):
67
+ """Slow reference implementation of `upfirdn_2d()` using standard TensorFlow ops."""
68
+
69
+ x = tf.convert_to_tensor(x)
70
+ k = np.asarray(k, dtype=np.float32)
71
+ assert x.shape.rank == 4
72
+ inH = x.shape[1].value
73
+ inW = x.shape[2].value
74
+ minorDim = _shape(x, 3)
75
+ kernelH, kernelW = k.shape
76
+ assert inW >= 1 and inH >= 1
77
+ assert kernelW >= 1 and kernelH >= 1
78
+ assert isinstance(upx, int) and isinstance(upy, int)
79
+ assert isinstance(downx, int) and isinstance(downy, int)
80
+ assert isinstance(padx0, int) and isinstance(padx1, int)
81
+ assert isinstance(pady0, int) and isinstance(pady1, int)
82
+
83
+ # Upsample (insert zeros).
84
+ x = tf.reshape(x, [-1, inH, 1, inW, 1, minorDim])
85
+ x = tf.pad(x, [[0, 0], [0, 0], [0, upy - 1], [0, 0], [0, upx - 1], [0, 0]])
86
+ x = tf.reshape(x, [-1, inH * upy, inW * upx, minorDim])
87
+
88
+ # Pad (crop if negative).
89
+ x = tf.pad(x, [[0, 0], [max(pady0, 0), max(pady1, 0)], [max(padx0, 0), max(padx1, 0)], [0, 0]])
90
+ x = x[:, max(-pady0, 0) : x.shape[1].value - max(-pady1, 0), max(-padx0, 0) : x.shape[2].value - max(-padx1, 0), :]
91
+
92
+ # Convolve with filter.
93
+ x = tf.transpose(x, [0, 3, 1, 2])
94
+ x = tf.reshape(x, [-1, 1, inH * upy + pady0 + pady1, inW * upx + padx0 + padx1])
95
+ w = tf.constant(k[::-1, ::-1, np.newaxis, np.newaxis], dtype=x.dtype)
96
+ x = tf.nn.conv2d(x, w, strides=[1,1,1,1], padding='VALID', data_format='NCHW')
97
+ x = tf.reshape(x, [-1, minorDim, inH * upy + pady0 + pady1 - kernelH + 1, inW * upx + padx0 + padx1 - kernelW + 1])
98
+ x = tf.transpose(x, [0, 2, 3, 1])
99
+
100
+ # Downsample (throw away pixels).
101
+ return x[:, ::downy, ::downx, :]
102
+
103
+ #----------------------------------------------------------------------------
104
+
105
+ def _upfirdn_2d_cuda(x, k, upx, upy, downx, downy, padx0, padx1, pady0, pady1):
106
+ """Fast CUDA implementation of `upfirdn_2d()` using custom ops."""
107
+
108
+ x = tf.convert_to_tensor(x)
109
+ k = np.asarray(k, dtype=np.float32)
110
+ majorDim, inH, inW, minorDim = x.shape.as_list()
111
+ kernelH, kernelW = k.shape
112
+ assert inW >= 1 and inH >= 1
113
+ assert kernelW >= 1 and kernelH >= 1
114
+ assert isinstance(upx, int) and isinstance(upy, int)
115
+ assert isinstance(downx, int) and isinstance(downy, int)
116
+ assert isinstance(padx0, int) and isinstance(padx1, int)
117
+ assert isinstance(pady0, int) and isinstance(pady1, int)
118
+
119
+ outW = (inW * upx + padx0 + padx1 - kernelW) // downx + 1
120
+ outH = (inH * upy + pady0 + pady1 - kernelH) // downy + 1
121
+ assert outW >= 1 and outH >= 1
122
+
123
+ kc = tf.constant(k, dtype=x.dtype)
124
+ gkc = tf.constant(k[::-1, ::-1], dtype=x.dtype)
125
+ gpadx0 = kernelW - padx0 - 1
126
+ gpady0 = kernelH - pady0 - 1
127
+ gpadx1 = inW * upx - outW * downx + padx0 - upx + 1
128
+ gpady1 = inH * upy - outH * downy + pady0 - upy + 1
129
+
130
+ @tf.custom_gradient
131
+ def func(x):
132
+ y = _get_plugin().up_fir_dn2d(x=x, k=kc, upx=upx, upy=upy, downx=downx, downy=downy, padx0=padx0, padx1=padx1, pady0=pady0, pady1=pady1)
133
+ y.set_shape([majorDim, outH, outW, minorDim])
134
+ @tf.custom_gradient
135
+ def grad(dy):
136
+ dx = _get_plugin().up_fir_dn2d(x=dy, k=gkc, upx=downx, upy=downy, downx=upx, downy=upy, padx0=gpadx0, padx1=gpadx1, pady0=gpady0, pady1=gpady1)
137
+ dx.set_shape([majorDim, inH, inW, minorDim])
138
+ return dx, func
139
+ return y, grad
140
+ return func(x)
141
+
142
+ #----------------------------------------------------------------------------
143
+
144
+ def filter_2d(x, k, gain=1, data_format='NCHW', impl='cuda'):
145
+ r"""Filter a batch of 2D images with the given FIR filter.
146
+
147
+ Accepts a batch of 2D images of the shape `[N, C, H, W]` or `[N, H, W, C]`
148
+ and filters each image with the given filter. The filter is normalized so that
149
+ if the input pixels are constant, they will be scaled by the specified `gain`.
150
+ Pixels outside the image are assumed to be zero.
151
+
152
+ Args:
153
+ x: Input tensor of the shape `[N, C, H, W]` or `[N, H, W, C]`.
154
+ k: FIR filter of the shape `[firH, firW]` or `[firN]` (separable).
155
+ gain: Scaling factor for signal magnitude (default: 1.0).
156
+ data_format: `'NCHW'` or `'NHWC'` (default: `'NCHW'`).
157
+ impl: Name of the implementation to use. Can be `"ref"` or `"cuda"` (default).
158
+
159
+ Returns:
160
+ Tensor of the same shape and datatype as `x`.
161
+ """
162
+
163
+ k = _setup_kernel(k) * gain
164
+ p = k.shape[0] - 1
165
+ return _simple_upfirdn_2d(x, k, pad0=(p+1)//2, pad1=p//2, data_format=data_format, impl=impl)
166
+
167
+ #----------------------------------------------------------------------------
168
+
169
+ def upsample_2d(x, k=None, factor=2, gain=1, data_format='NCHW', impl='cuda'):
170
+ r"""Upsample a batch of 2D images with the given filter.
171
+
172
+ Accepts a batch of 2D images of the shape `[N, C, H, W]` or `[N, H, W, C]`
173
+ and upsamples each image with the given filter. The filter is normalized so that
174
+ if the input pixels are constant, they will be scaled by the specified `gain`.
175
+ Pixels outside the image are assumed to be zero, and the filter is padded with
176
+ zeros so that its shape is a multiple of the upsampling factor.
177
+
178
+ Args:
179
+ x: Input tensor of the shape `[N, C, H, W]` or `[N, H, W, C]`.
180
+ k: FIR filter of the shape `[firH, firW]` or `[firN]` (separable).
181
+ The default is `[1] * factor`, which corresponds to nearest-neighbor
182
+ upsampling.
183
+ factor: Integer upsampling factor (default: 2).
184
+ gain: Scaling factor for signal magnitude (default: 1.0).
185
+ data_format: `'NCHW'` or `'NHWC'` (default: `'NCHW'`).
186
+ impl: Name of the implementation to use. Can be `"ref"` or `"cuda"` (default).
187
+
188
+ Returns:
189
+ Tensor of the shape `[N, C, H * factor, W * factor]` or
190
+ `[N, H * factor, W * factor, C]`, and same datatype as `x`.
191
+ """
192
+
193
+ assert isinstance(factor, int) and factor >= 1
194
+ if k is None:
195
+ k = [1] * factor
196
+ k = _setup_kernel(k) * (gain * (factor ** 2))
197
+ p = k.shape[0] - factor
198
+ return _simple_upfirdn_2d(x, k, up=factor, pad0=(p+1)//2+factor-1, pad1=p//2, data_format=data_format, impl=impl)
199
+
200
+ #----------------------------------------------------------------------------
201
+
202
+ def downsample_2d(x, k=None, factor=2, gain=1, data_format='NCHW', impl='cuda'):
203
+ r"""Downsample a batch of 2D images with the given filter.
204
+
205
+ Accepts a batch of 2D images of the shape `[N, C, H, W]` or `[N, H, W, C]`
206
+ and downsamples each image with the given filter. The filter is normalized so that
207
+ if the input pixels are constant, they will be scaled by the specified `gain`.
208
+ Pixels outside the image are assumed to be zero, and the filter is padded with
209
+ zeros so that its shape is a multiple of the downsampling factor.
210
+
211
+ Args:
212
+ x: Input tensor of the shape `[N, C, H, W]` or `[N, H, W, C]`.
213
+ k: FIR filter of the shape `[firH, firW]` or `[firN]` (separable).
214
+ The default is `[1] * factor`, which corresponds to average pooling.
215
+ factor: Integer downsampling factor (default: 2).
216
+ gain: Scaling factor for signal magnitude (default: 1.0).
217
+ data_format: `'NCHW'` or `'NHWC'` (default: `'NCHW'`).
218
+ impl: Name of the implementation to use. Can be `"ref"` or `"cuda"` (default).
219
+
220
+ Returns:
221
+ Tensor of the shape `[N, C, H // factor, W // factor]` or
222
+ `[N, H // factor, W // factor, C]`, and same datatype as `x`.
223
+ """
224
+
225
+ assert isinstance(factor, int) and factor >= 1
226
+ if k is None:
227
+ k = [1] * factor
228
+ k = _setup_kernel(k) * gain
229
+ p = k.shape[0] - factor
230
+ return _simple_upfirdn_2d(x, k, down=factor, pad0=(p+1)//2, pad1=p//2, data_format=data_format, impl=impl)
231
+
232
+ #----------------------------------------------------------------------------
233
+
234
+ def upsample_conv_2d(x, w, k=None, factor=2, gain=1, data_format='NCHW', impl='cuda'):
235
+ r"""Fused `upsample_2d()` followed by `tf.nn.conv2d()`.
236
+
237
+ Padding is performed only once at the beginning, not between the operations.
238
+ The fused op is considerably more efficient than performing the same calculation
239
+ using standard TensorFlow ops. It supports gradients of arbitrary order.
240
+
241
+ Args:
242
+ x: Input tensor of the shape `[N, C, H, W]` or `[N, H, W, C]`.
243
+ w: Weight tensor of the shape `[filterH, filterW, inChannels, outChannels]`.
244
+ Grouped convolution can be performed by `inChannels = x.shape[0] // numGroups`.
245
+ k: FIR filter of the shape `[firH, firW]` or `[firN]` (separable).
246
+ The default is `[1] * factor`, which corresponds to nearest-neighbor
247
+ upsampling.
248
+ factor: Integer upsampling factor (default: 2).
249
+ gain: Scaling factor for signal magnitude (default: 1.0).
250
+ data_format: `'NCHW'` or `'NHWC'` (default: `'NCHW'`).
251
+ impl: Name of the implementation to use. Can be `"ref"` or `"cuda"` (default).
252
+
253
+ Returns:
254
+ Tensor of the shape `[N, C, H * factor, W * factor]` or
255
+ `[N, H * factor, W * factor, C]`, and same datatype as `x`.
256
+ """
257
+
258
+ assert isinstance(factor, int) and factor >= 1
259
+
260
+ # Check weight shape.
261
+ w = tf.convert_to_tensor(w)
262
+ assert w.shape.rank == 4
263
+ convH = w.shape[0].value
264
+ convW = w.shape[1].value
265
+ inC = _shape(w, 2)
266
+ outC = _shape(w, 3)
267
+ assert convW == convH
268
+
269
+ # Setup filter kernel.
270
+ if k is None:
271
+ k = [1] * factor
272
+ k = _setup_kernel(k) * (gain * (factor ** 2))
273
+ p = (k.shape[0] - factor) - (convW - 1)
274
+
275
+ # Determine data dimensions.
276
+ if data_format == 'NCHW':
277
+ stride = [1, 1, factor, factor]
278
+ output_shape = [_shape(x, 0), outC, (_shape(x, 2) - 1) * factor + convH, (_shape(x, 3) - 1) * factor + convW]
279
+ num_groups = _shape(x, 1) // inC
280
+ else:
281
+ stride = [1, factor, factor, 1]
282
+ output_shape = [_shape(x, 0), (_shape(x, 1) - 1) * factor + convH, (_shape(x, 2) - 1) * factor + convW, outC]
283
+ num_groups = _shape(x, 3) // inC
284
+
285
+ # Transpose weights.
286
+ w = tf.reshape(w, [convH, convW, inC, num_groups, -1])
287
+ w = tf.transpose(w[::-1, ::-1], [0, 1, 4, 3, 2])
288
+ w = tf.reshape(w, [convH, convW, -1, num_groups * inC])
289
+
290
+ # Execute.
291
+ x = tf.nn.conv2d_transpose(x, w, output_shape=output_shape, strides=stride, padding='VALID', data_format=data_format)
292
+ return _simple_upfirdn_2d(x, k, pad0=(p+1)//2+factor-1, pad1=p//2+1, data_format=data_format, impl=impl)
293
+
294
+ #----------------------------------------------------------------------------
295
+
296
+ def conv_downsample_2d(x, w, k=None, factor=2, gain=1, data_format='NCHW', impl='cuda'):
297
+ r"""Fused `tf.nn.conv2d()` followed by `downsample_2d()`.
298
+
299
+ Padding is performed only once at the beginning, not between the operations.
300
+ The fused op is considerably more efficient than performing the same calculation
301
+ using standard TensorFlow ops. It supports gradients of arbitrary order.
302
+
303
+ Args:
304
+ x: Input tensor of the shape `[N, C, H, W]` or `[N, H, W, C]`.
305
+ w: Weight tensor of the shape `[filterH, filterW, inChannels, outChannels]`.
306
+ Grouped convolution can be performed by `inChannels = x.shape[0] // numGroups`.
307
+ k: FIR filter of the shape `[firH, firW]` or `[firN]` (separable).
308
+ The default is `[1] * factor`, which corresponds to average pooling.
309
+ factor: Integer downsampling factor (default: 2).
310
+ gain: Scaling factor for signal magnitude (default: 1.0).
311
+ data_format: `'NCHW'` or `'NHWC'` (default: `'NCHW'`).
312
+ impl: Name of the implementation to use. Can be `"ref"` or `"cuda"` (default).
313
+
314
+ Returns:
315
+ Tensor of the shape `[N, C, H // factor, W // factor]` or
316
+ `[N, H // factor, W // factor, C]`, and same datatype as `x`.
317
+ """
318
+
319
+ assert isinstance(factor, int) and factor >= 1
320
+ w = tf.convert_to_tensor(w)
321
+ convH, convW, _inC, _outC = w.shape.as_list()
322
+ assert convW == convH
323
+ if k is None:
324
+ k = [1] * factor
325
+ k = _setup_kernel(k) * gain
326
+ p = (k.shape[0] - factor) + (convW - 1)
327
+ if data_format == 'NCHW':
328
+ s = [1, 1, factor, factor]
329
+ else:
330
+ s = [1, factor, factor, 1]
331
+ x = _simple_upfirdn_2d(x, k, pad0=(p+1)//2, pad1=p//2, data_format=data_format, impl=impl)
332
+ return tf.nn.conv2d(x, w, strides=s, padding='VALID', data_format=data_format)
333
+
334
+ #----------------------------------------------------------------------------
335
+ # Internal helper funcs.
336
+
337
+ def _shape(tf_expr, dim_idx):
338
+ if tf_expr.shape.rank is not None:
339
+ dim = tf_expr.shape[dim_idx].value
340
+ if dim is not None:
341
+ return dim
342
+ return tf.shape(tf_expr)[dim_idx]
343
+
344
+ def _setup_kernel(k):
345
+ k = np.asarray(k, dtype=np.float32)
346
+ if k.ndim == 1:
347
+ k = np.outer(k, k)
348
+ k /= np.sum(k)
349
+ assert k.ndim == 2
350
+ assert k.shape[0] == k.shape[1]
351
+ return k
352
+
353
+ def _simple_upfirdn_2d(x, k, up=1, down=1, pad0=0, pad1=0, data_format='NCHW', impl='cuda'):
354
+ assert data_format in ['NCHW', 'NHWC']
355
+ assert x.shape.rank == 4
356
+ y = x
357
+ if data_format == 'NCHW':
358
+ y = tf.reshape(y, [-1, _shape(y, 2), _shape(y, 3), 1])
359
+ y = upfirdn_2d(y, k, upx=up, upy=up, downx=down, downy=down, padx0=pad0, padx1=pad1, pady0=pad0, pady1=pad1, impl=impl)
360
+ if data_format == 'NCHW':
361
+ y = tf.reshape(y, [-1, _shape(x, 1), _shape(y, 1), _shape(y, 2)])
362
+ return y
363
+
364
+ #----------------------------------------------------------------------------
dnnlib/tflib/optimizer.py ADDED
@@ -0,0 +1,370 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2019, NVIDIA Corporation. All rights reserved.
2
+ #
3
+ # This work is made available under the Nvidia Source Code License-NC.
4
+ # To view a copy of this license, visit
5
+ # https://nvlabs.github.io/stylegan2/license.html
6
+
7
+ """Helper wrapper for a Tensorflow optimizer."""
8
+
9
+ import platform
10
+ import numpy as np
11
+ import tensorflow as tf
12
+
13
+ from collections import OrderedDict
14
+ from typing import List, Union
15
+
16
+ from . import autosummary
17
+ from . import tfutil
18
+ from .. import util
19
+
20
+ from .tfutil import TfExpression, TfExpressionEx
21
+
22
+ _collective_ops_warning_printed = False
23
+ _collective_ops_group_key = 831766147
24
+ _collective_ops_instance_key = 436340067
25
+
26
+ class Optimizer:
27
+ """A Wrapper for tf.train.Optimizer.
28
+
29
+ Automatically takes care of:
30
+ - Gradient averaging for multi-GPU training.
31
+ - Gradient accumulation for arbitrarily large minibatches.
32
+ - Dynamic loss scaling and typecasts for FP16 training.
33
+ - Ignoring corrupted gradients that contain NaNs/Infs.
34
+ - Reporting statistics.
35
+ - Well-chosen default settings.
36
+ """
37
+
38
+ def __init__(self,
39
+ name: str = "Train", # Name string that will appear in TensorFlow graph.
40
+ tf_optimizer: str = "tf.train.AdamOptimizer", # Underlying optimizer class.
41
+ learning_rate: TfExpressionEx = 0.001, # Learning rate. Can vary over time.
42
+ minibatch_multiplier: TfExpressionEx = None, # Treat N consecutive minibatches as one by accumulating gradients.
43
+ share: "Optimizer" = None, # Share internal state with a previously created optimizer?
44
+ use_loss_scaling: bool = False, # Enable dynamic loss scaling for robust mixed-precision training?
45
+ loss_scaling_init: float = 64.0, # Log2 of initial loss scaling factor.
46
+ loss_scaling_inc: float = 0.0005, # Log2 of per-minibatch loss scaling increment when there is no overflow.
47
+ loss_scaling_dec: float = 1.0, # Log2 of per-minibatch loss scaling decrement when there is an overflow.
48
+ report_mem_usage: bool = False, # Report fine-grained memory usage statistics in TensorBoard?
49
+ **kwargs):
50
+
51
+ # Public fields.
52
+ self.name = name
53
+ self.learning_rate = learning_rate
54
+ self.minibatch_multiplier = minibatch_multiplier
55
+ self.id = self.name.replace("/", ".")
56
+ self.scope = tf.get_default_graph().unique_name(self.id)
57
+ self.optimizer_class = util.get_obj_by_name(tf_optimizer)
58
+ self.optimizer_kwargs = dict(kwargs)
59
+ self.use_loss_scaling = use_loss_scaling
60
+ self.loss_scaling_init = loss_scaling_init
61
+ self.loss_scaling_inc = loss_scaling_inc
62
+ self.loss_scaling_dec = loss_scaling_dec
63
+
64
+ # Private fields.
65
+ self._updates_applied = False
66
+ self._devices = OrderedDict() # device_name => EasyDict()
67
+ self._shared_optimizers = OrderedDict() # device_name => optimizer_class
68
+ self._gradient_shapes = None # [shape, ...]
69
+ self._report_mem_usage = report_mem_usage
70
+
71
+ # Validate arguments.
72
+ assert callable(self.optimizer_class)
73
+
74
+ # Share internal state if requested.
75
+ if share is not None:
76
+ assert isinstance(share, Optimizer)
77
+ assert self.optimizer_class is share.optimizer_class
78
+ assert self.learning_rate is share.learning_rate
79
+ assert self.optimizer_kwargs == share.optimizer_kwargs
80
+ self._shared_optimizers = share._shared_optimizers # pylint: disable=protected-access
81
+
82
+ def _get_device(self, device_name: str):
83
+ """Get internal state for the given TensorFlow device."""
84
+ tfutil.assert_tf_initialized()
85
+ if device_name in self._devices:
86
+ return self._devices[device_name]
87
+
88
+ # Initialize fields.
89
+ device = util.EasyDict()
90
+ device.name = device_name
91
+ device.optimizer = None # Underlying optimizer: optimizer_class
92
+ device.loss_scaling_var = None # Log2 of loss scaling: tf.Variable
93
+ device.grad_raw = OrderedDict() # Raw gradients: var => [grad, ...]
94
+ device.grad_clean = OrderedDict() # Clean gradients: var => grad
95
+ device.grad_acc_vars = OrderedDict() # Accumulation sums: var => tf.Variable
96
+ device.grad_acc_count = None # Accumulation counter: tf.Variable
97
+ device.grad_acc = OrderedDict() # Accumulated gradients: var => grad
98
+
99
+ # Setup TensorFlow objects.
100
+ with tfutil.absolute_name_scope(self.scope + "/Devices"), tf.device(device_name), tf.control_dependencies(None):
101
+ if device_name not in self._shared_optimizers:
102
+ optimizer_name = self.scope.replace("/", "_") + "_opt%d" % len(self._shared_optimizers)
103
+ self._shared_optimizers[device_name] = self.optimizer_class(name=optimizer_name, learning_rate=self.learning_rate, **self.optimizer_kwargs)
104
+ device.optimizer = self._shared_optimizers[device_name]
105
+ if self.use_loss_scaling:
106
+ device.loss_scaling_var = tf.Variable(np.float32(self.loss_scaling_init), trainable=False, name="loss_scaling_var")
107
+
108
+ # Register device.
109
+ self._devices[device_name] = device
110
+ return device
111
+
112
+ def register_gradients(self, loss: TfExpression, trainable_vars: Union[List, dict]) -> None:
113
+ """Register the gradients of the given loss function with respect to the given variables.
114
+ Intended to be called once per GPU."""
115
+ tfutil.assert_tf_initialized()
116
+ assert not self._updates_applied
117
+ device = self._get_device(loss.device)
118
+
119
+ # Validate trainables.
120
+ if isinstance(trainable_vars, dict):
121
+ trainable_vars = list(trainable_vars.values()) # allow passing in Network.trainables as vars
122
+ assert isinstance(trainable_vars, list) and len(trainable_vars) >= 1
123
+ assert all(tfutil.is_tf_expression(expr) for expr in trainable_vars + [loss])
124
+ assert all(var.device == device.name for var in trainable_vars)
125
+
126
+ # Validate shapes.
127
+ if self._gradient_shapes is None:
128
+ self._gradient_shapes = [var.shape.as_list() for var in trainable_vars]
129
+ assert len(trainable_vars) == len(self._gradient_shapes)
130
+ assert all(var.shape.as_list() == var_shape for var, var_shape in zip(trainable_vars, self._gradient_shapes))
131
+
132
+ # Report memory usage if requested.
133
+ deps = []
134
+ if self._report_mem_usage:
135
+ self._report_mem_usage = False
136
+ try:
137
+ with tf.name_scope(self.id + '_mem'), tf.device(device.name), tf.control_dependencies([loss]):
138
+ deps.append(autosummary.autosummary(self.id + "/mem_usage_gb", tf.contrib.memory_stats.BytesInUse() / 2**30))
139
+ except tf.errors.NotFoundError:
140
+ pass
141
+
142
+ # Compute gradients.
143
+ with tf.name_scope(self.id + "_grad"), tf.device(device.name), tf.control_dependencies(deps):
144
+ loss = self.apply_loss_scaling(tf.cast(loss, tf.float32))
145
+ gate = tf.train.Optimizer.GATE_NONE # disable gating to reduce memory usage
146
+ grad_list = device.optimizer.compute_gradients(loss=loss, var_list=trainable_vars, gate_gradients=gate)
147
+
148
+ # Register gradients.
149
+ for grad, var in grad_list:
150
+ if var not in device.grad_raw:
151
+ device.grad_raw[var] = []
152
+ device.grad_raw[var].append(grad)
153
+
154
+ def apply_updates(self, allow_no_op: bool = False) -> tf.Operation:
155
+ """Construct training op to update the registered variables based on their gradients."""
156
+ tfutil.assert_tf_initialized()
157
+ assert not self._updates_applied
158
+ self._updates_applied = True
159
+ all_ops = []
160
+
161
+ # Check for no-op.
162
+ if allow_no_op and len(self._devices) == 0:
163
+ with tfutil.absolute_name_scope(self.scope):
164
+ return tf.no_op(name='TrainingOp')
165
+
166
+ # Clean up gradients.
167
+ for device_idx, device in enumerate(self._devices.values()):
168
+ with tfutil.absolute_name_scope(self.scope + "/Clean%d" % device_idx), tf.device(device.name):
169
+ for var, grad in device.grad_raw.items():
170
+
171
+ # Filter out disconnected gradients and convert to float32.
172
+ grad = [g for g in grad if g is not None]
173
+ grad = [tf.cast(g, tf.float32) for g in grad]
174
+
175
+ # Sum within the device.
176
+ if len(grad) == 0:
177
+ grad = tf.zeros(var.shape) # No gradients => zero.
178
+ elif len(grad) == 1:
179
+ grad = grad[0] # Single gradient => use as is.
180
+ else:
181
+ grad = tf.add_n(grad) # Multiple gradients => sum.
182
+
183
+ # Scale as needed.
184
+ scale = 1.0 / len(device.grad_raw[var]) / len(self._devices)
185
+ scale = tf.constant(scale, dtype=tf.float32, name="scale")
186
+ if self.minibatch_multiplier is not None:
187
+ scale /= tf.cast(self.minibatch_multiplier, tf.float32)
188
+ scale = self.undo_loss_scaling(scale)
189
+ device.grad_clean[var] = grad * scale
190
+
191
+ # Sum gradients across devices.
192
+ if len(self._devices) > 1:
193
+ with tfutil.absolute_name_scope(self.scope + "/Broadcast"), tf.device(None):
194
+ if platform.system() == "Windows": # Windows => NCCL ops are not available.
195
+ self._broadcast_fallback()
196
+ elif tf.VERSION.startswith("1.15."): # TF 1.15 => NCCL ops are broken: https://github.com/tensorflow/tensorflow/issues/41539
197
+ self._broadcast_fallback()
198
+ else: # Otherwise => NCCL ops are safe to use.
199
+ self._broadcast_nccl()
200
+
201
+ # Apply updates separately on each device.
202
+ for device_idx, device in enumerate(self._devices.values()):
203
+ with tfutil.absolute_name_scope(self.scope + "/Apply%d" % device_idx), tf.device(device.name):
204
+ # pylint: disable=cell-var-from-loop
205
+
206
+ # Accumulate gradients over time.
207
+ if self.minibatch_multiplier is None:
208
+ acc_ok = tf.constant(True, name='acc_ok')
209
+ device.grad_acc = OrderedDict(device.grad_clean)
210
+ else:
211
+ # Create variables.
212
+ with tf.control_dependencies(None):
213
+ for var in device.grad_clean.keys():
214
+ device.grad_acc_vars[var] = tf.Variable(tf.zeros(var.shape), trainable=False, name="grad_acc_var")
215
+ device.grad_acc_count = tf.Variable(tf.zeros([]), trainable=False, name="grad_acc_count")
216
+
217
+ # Track counter.
218
+ count_cur = device.grad_acc_count + 1.0
219
+ count_inc_op = lambda: tf.assign(device.grad_acc_count, count_cur)
220
+ count_reset_op = lambda: tf.assign(device.grad_acc_count, tf.zeros([]))
221
+ acc_ok = (count_cur >= tf.cast(self.minibatch_multiplier, tf.float32))
222
+ all_ops.append(tf.cond(acc_ok, count_reset_op, count_inc_op))
223
+
224
+ # Track gradients.
225
+ for var, grad in device.grad_clean.items():
226
+ acc_var = device.grad_acc_vars[var]
227
+ acc_cur = acc_var + grad
228
+ device.grad_acc[var] = acc_cur
229
+ with tf.control_dependencies([acc_cur]):
230
+ acc_inc_op = lambda: tf.assign(acc_var, acc_cur)
231
+ acc_reset_op = lambda: tf.assign(acc_var, tf.zeros(var.shape))
232
+ all_ops.append(tf.cond(acc_ok, acc_reset_op, acc_inc_op))
233
+
234
+ # No overflow => apply gradients.
235
+ all_ok = tf.reduce_all(tf.stack([acc_ok] + [tf.reduce_all(tf.is_finite(g)) for g in device.grad_acc.values()]))
236
+ apply_op = lambda: device.optimizer.apply_gradients([(tf.cast(grad, var.dtype), var) for var, grad in device.grad_acc.items()])
237
+ all_ops.append(tf.cond(all_ok, apply_op, tf.no_op))
238
+
239
+ # Adjust loss scaling.
240
+ if self.use_loss_scaling:
241
+ ls_inc_op = lambda: tf.assign_add(device.loss_scaling_var, self.loss_scaling_inc)
242
+ ls_dec_op = lambda: tf.assign_sub(device.loss_scaling_var, self.loss_scaling_dec)
243
+ ls_update_op = lambda: tf.group(tf.cond(all_ok, ls_inc_op, ls_dec_op))
244
+ all_ops.append(tf.cond(acc_ok, ls_update_op, tf.no_op))
245
+
246
+ # Last device => report statistics.
247
+ if device_idx == len(self._devices) - 1:
248
+ all_ops.append(autosummary.autosummary(self.id + "/learning_rate", tf.convert_to_tensor(self.learning_rate)))
249
+ all_ops.append(autosummary.autosummary(self.id + "/overflow_frequency", tf.where(all_ok, 0, 1), condition=acc_ok))
250
+ if self.use_loss_scaling:
251
+ all_ops.append(autosummary.autosummary(self.id + "/loss_scaling_log2", device.loss_scaling_var))
252
+
253
+ # Initialize variables.
254
+ self.reset_optimizer_state()
255
+ if self.use_loss_scaling:
256
+ tfutil.init_uninitialized_vars([device.loss_scaling_var for device in self._devices.values()])
257
+ if self.minibatch_multiplier is not None:
258
+ tfutil.run([var.initializer for device in self._devices.values() for var in list(device.grad_acc_vars.values()) + [device.grad_acc_count]])
259
+
260
+ # Group everything into a single op.
261
+ with tfutil.absolute_name_scope(self.scope):
262
+ return tf.group(*all_ops, name="TrainingOp")
263
+
264
+ def reset_optimizer_state(self) -> None:
265
+ """Reset internal state of the underlying optimizer."""
266
+ tfutil.assert_tf_initialized()
267
+ tfutil.run([var.initializer for device in self._devices.values() for var in device.optimizer.variables()])
268
+
269
+ def get_loss_scaling_var(self, device: str) -> Union[tf.Variable, None]:
270
+ """Get or create variable representing log2 of the current dynamic loss scaling factor."""
271
+ return self._get_device(device).loss_scaling_var
272
+
273
+ def apply_loss_scaling(self, value: TfExpression) -> TfExpression:
274
+ """Apply dynamic loss scaling for the given expression."""
275
+ assert tfutil.is_tf_expression(value)
276
+ if not self.use_loss_scaling:
277
+ return value
278
+ return value * tfutil.exp2(self.get_loss_scaling_var(value.device))
279
+
280
+ def undo_loss_scaling(self, value: TfExpression) -> TfExpression:
281
+ """Undo the effect of dynamic loss scaling for the given expression."""
282
+ assert tfutil.is_tf_expression(value)
283
+ if not self.use_loss_scaling:
284
+ return value
285
+ return value * tfutil.exp2(-self.get_loss_scaling_var(value.device)) # pylint: disable=invalid-unary-operand-type
286
+
287
+ def _broadcast_nccl(self):
288
+ """Sum gradients across devices using NCCL ops (fast path)."""
289
+ from tensorflow.python.ops import nccl_ops # pylint: disable=no-name-in-module
290
+ for all_vars in zip(*[device.grad_clean.keys() for device in self._devices.values()]):
291
+ if any(x.shape.num_elements() > 0 for x in all_vars):
292
+ all_grads = [device.grad_clean[var] for device, var in zip(self._devices.values(), all_vars)]
293
+ all_grads = nccl_ops.all_sum(all_grads)
294
+ for device, var, grad in zip(self._devices.values(), all_vars, all_grads):
295
+ device.grad_clean[var] = grad
296
+
297
+ def _broadcast_fallback(self):
298
+ """Sum gradients across devices using TensorFlow collective ops (slow fallback path)."""
299
+ from tensorflow.python.ops import collective_ops # pylint: disable=no-name-in-module
300
+ global _collective_ops_warning_printed, _collective_ops_group_key, _collective_ops_instance_key
301
+ if all(x.shape.num_elements() == 0 for device in self._devices.values() for x in device.grad_clean.values()):
302
+ return
303
+ if not _collective_ops_warning_printed:
304
+ print("------------------------------------------------------------------------")
305
+ print("WARNING: Using slow fallback implementation for inter-GPU communication.")
306
+ print("Please use TensorFlow 1.14 on Linux for optimal training performance.")
307
+ print("------------------------------------------------------------------------")
308
+ _collective_ops_warning_printed = True
309
+ for device in self._devices.values():
310
+ with tf.device(device.name):
311
+ combo = [tf.reshape(x, [x.shape.num_elements()]) for x in device.grad_clean.values()]
312
+ combo = tf.concat(combo, axis=0)
313
+ combo = collective_ops.all_reduce(combo, merge_op='Add', final_op='Id',
314
+ group_size=len(self._devices), group_key=_collective_ops_group_key,
315
+ instance_key=_collective_ops_instance_key)
316
+ cur_ofs = 0
317
+ for var, grad_old in device.grad_clean.items():
318
+ grad_new = tf.reshape(combo[cur_ofs : cur_ofs + grad_old.shape.num_elements()], grad_old.shape)
319
+ cur_ofs += grad_old.shape.num_elements()
320
+ device.grad_clean[var] = grad_new
321
+ _collective_ops_instance_key += 1
322
+
323
+
324
+ class SimpleAdam:
325
+ """Simplified version of tf.train.AdamOptimizer that behaves identically when used with dnnlib.tflib.Optimizer."""
326
+
327
+ def __init__(self, name="Adam", learning_rate=0.001, beta1=0.9, beta2=0.999, epsilon=1e-8):
328
+ self.name = name
329
+ self.learning_rate = learning_rate
330
+ self.beta1 = beta1
331
+ self.beta2 = beta2
332
+ self.epsilon = epsilon
333
+ self.all_state_vars = []
334
+
335
+ def variables(self):
336
+ return self.all_state_vars
337
+
338
+ def compute_gradients(self, loss, var_list, gate_gradients=tf.train.Optimizer.GATE_NONE):
339
+ assert gate_gradients == tf.train.Optimizer.GATE_NONE
340
+ return list(zip(tf.gradients(loss, var_list), var_list))
341
+
342
+ def apply_gradients(self, grads_and_vars):
343
+ with tf.name_scope(self.name):
344
+ state_vars = []
345
+ update_ops = []
346
+
347
+ # Adjust learning rate to deal with startup bias.
348
+ with tf.control_dependencies(None):
349
+ b1pow_var = tf.Variable(dtype=tf.float32, initial_value=1, trainable=False)
350
+ b2pow_var = tf.Variable(dtype=tf.float32, initial_value=1, trainable=False)
351
+ state_vars += [b1pow_var, b2pow_var]
352
+ b1pow_new = b1pow_var * self.beta1
353
+ b2pow_new = b2pow_var * self.beta2
354
+ update_ops += [tf.assign(b1pow_var, b1pow_new), tf.assign(b2pow_var, b2pow_new)]
355
+ lr_new = self.learning_rate * tf.sqrt(1 - b2pow_new) / (1 - b1pow_new)
356
+
357
+ # Construct ops to update each variable.
358
+ for grad, var in grads_and_vars:
359
+ with tf.control_dependencies(None):
360
+ m_var = tf.Variable(dtype=tf.float32, initial_value=tf.zeros_like(var), trainable=False)
361
+ v_var = tf.Variable(dtype=tf.float32, initial_value=tf.zeros_like(var), trainable=False)
362
+ state_vars += [m_var, v_var]
363
+ m_new = self.beta1 * m_var + (1 - self.beta1) * grad
364
+ v_new = self.beta2 * v_var + (1 - self.beta2) * tf.square(grad)
365
+ var_delta = lr_new * m_new / (tf.sqrt(v_new) + self.epsilon)
366
+ update_ops += [tf.assign(m_var, m_new), tf.assign(v_var, v_new), tf.assign_sub(var, var_delta)]
367
+
368
+ # Group everything together.
369
+ self.all_state_vars += state_vars
370
+ return tf.group(*update_ops)
dnnlib/tflib/tfutil.py ADDED
@@ -0,0 +1,252 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2019, NVIDIA Corporation. All rights reserved.
2
+ #
3
+ # This work is made available under the Nvidia Source Code License-NC.
4
+ # To view a copy of this license, visit
5
+ # https://nvlabs.github.io/stylegan2/license.html
6
+
7
+ """Miscellaneous helper utils for Tensorflow."""
8
+
9
+ import os
10
+ import numpy as np
11
+ import tensorflow as tf
12
+
13
+ # Silence deprecation warnings from TensorFlow 1.13 onwards
14
+ import logging
15
+ logging.getLogger('tensorflow').setLevel(logging.ERROR)
16
+ import tensorflow.contrib # requires TensorFlow 1.x!
17
+ tf.contrib = tensorflow.contrib
18
+
19
+ from typing import Any, Iterable, List, Union
20
+
21
+ TfExpression = Union[tf.Tensor, tf.Variable, tf.Operation]
22
+ """A type that represents a valid Tensorflow expression."""
23
+
24
+ TfExpressionEx = Union[TfExpression, int, float, np.ndarray]
25
+ """A type that can be converted to a valid Tensorflow expression."""
26
+
27
+
28
+ def run(*args, **kwargs) -> Any:
29
+ """Run the specified ops in the default session."""
30
+ assert_tf_initialized()
31
+ return tf.get_default_session().run(*args, **kwargs)
32
+
33
+
34
+ def is_tf_expression(x: Any) -> bool:
35
+ """Check whether the input is a valid Tensorflow expression, i.e., Tensorflow Tensor, Variable, or Operation."""
36
+ return isinstance(x, (tf.Tensor, tf.Variable, tf.Operation))
37
+
38
+
39
+ def shape_to_list(shape: Iterable[tf.Dimension]) -> List[Union[int, None]]:
40
+ """Convert a Tensorflow shape to a list of ints. Retained for backwards compatibility -- use TensorShape.as_list() in new code."""
41
+ return [dim.value for dim in shape]
42
+
43
+
44
+ def flatten(x: TfExpressionEx) -> TfExpression:
45
+ """Shortcut function for flattening a tensor."""
46
+ with tf.name_scope("Flatten"):
47
+ return tf.reshape(x, [-1])
48
+
49
+
50
+ def log2(x: TfExpressionEx) -> TfExpression:
51
+ """Logarithm in base 2."""
52
+ with tf.name_scope("Log2"):
53
+ return tf.log(x) * np.float32(1.0 / np.log(2.0))
54
+
55
+
56
+ def exp2(x: TfExpressionEx) -> TfExpression:
57
+ """Exponent in base 2."""
58
+ with tf.name_scope("Exp2"):
59
+ return tf.exp(x * np.float32(np.log(2.0)))
60
+
61
+
62
+ def lerp(a: TfExpressionEx, b: TfExpressionEx, t: TfExpressionEx) -> TfExpressionEx:
63
+ """Linear interpolation."""
64
+ with tf.name_scope("Lerp"):
65
+ return a + (b - a) * t
66
+
67
+
68
+ def lerp_clip(a: TfExpressionEx, b: TfExpressionEx, t: TfExpressionEx) -> TfExpression:
69
+ """Linear interpolation with clip."""
70
+ with tf.name_scope("LerpClip"):
71
+ return a + (b - a) * tf.clip_by_value(t, 0.0, 1.0)
72
+
73
+
74
+ def absolute_name_scope(scope: str) -> tf.name_scope:
75
+ """Forcefully enter the specified name scope, ignoring any surrounding scopes."""
76
+ return tf.name_scope(scope + "/")
77
+
78
+
79
+ def absolute_variable_scope(scope: str, **kwargs) -> tf.variable_scope:
80
+ """Forcefully enter the specified variable scope, ignoring any surrounding scopes."""
81
+ return tf.variable_scope(tf.VariableScope(name=scope, **kwargs), auxiliary_name_scope=False)
82
+
83
+
84
+ def _sanitize_tf_config(config_dict: dict = None) -> dict:
85
+ # Defaults.
86
+ cfg = dict()
87
+ cfg["rnd.np_random_seed"] = None # Random seed for NumPy. None = keep as is.
88
+ cfg["rnd.tf_random_seed"] = "auto" # Random seed for TensorFlow. 'auto' = derive from NumPy random state. None = keep as is.
89
+ cfg["env.TF_CPP_MIN_LOG_LEVEL"] = "1" # 0 = Print all available debug info from TensorFlow. 1 = Print warnings and errors, but disable debug info.
90
+ cfg["graph_options.place_pruned_graph"] = True # False = Check that all ops are available on the designated device. True = Skip the check for ops that are not used.
91
+ cfg["gpu_options.allow_growth"] = True # False = Allocate all GPU memory at the beginning. True = Allocate only as much GPU memory as needed.
92
+
93
+ # Remove defaults for environment variables that are already set.
94
+ for key in list(cfg):
95
+ fields = key.split(".")
96
+ if fields[0] == "env":
97
+ assert len(fields) == 2
98
+ if fields[1] in os.environ:
99
+ del cfg[key]
100
+
101
+ # User overrides.
102
+ if config_dict is not None:
103
+ cfg.update(config_dict)
104
+ return cfg
105
+
106
+
107
+ def init_tf(config_dict: dict = None) -> None:
108
+ """Initialize TensorFlow session using good default settings."""
109
+ # Skip if already initialized.
110
+ if tf.get_default_session() is not None:
111
+ return
112
+
113
+ # Setup config dict and random seeds.
114
+ cfg = _sanitize_tf_config(config_dict)
115
+ np_random_seed = cfg["rnd.np_random_seed"]
116
+ if np_random_seed is not None:
117
+ np.random.seed(np_random_seed)
118
+ tf_random_seed = cfg["rnd.tf_random_seed"]
119
+ if tf_random_seed == "auto":
120
+ tf_random_seed = np.random.randint(1 << 31)
121
+ if tf_random_seed is not None:
122
+ tf.set_random_seed(tf_random_seed)
123
+
124
+ # Setup environment variables.
125
+ for key, value in cfg.items():
126
+ fields = key.split(".")
127
+ if fields[0] == "env":
128
+ assert len(fields) == 2
129
+ os.environ[fields[1]] = str(value)
130
+
131
+ # Create default TensorFlow session.
132
+ create_session(cfg, force_as_default=True)
133
+
134
+
135
+ def assert_tf_initialized():
136
+ """Check that TensorFlow session has been initialized."""
137
+ if tf.get_default_session() is None:
138
+ raise RuntimeError("No default TensorFlow session found. Please call dnnlib.tflib.init_tf().")
139
+
140
+
141
+ def create_session(config_dict: dict = None, force_as_default: bool = False) -> tf.Session:
142
+ """Create tf.Session based on config dict."""
143
+ # Setup TensorFlow config proto.
144
+ cfg = _sanitize_tf_config(config_dict)
145
+ config_proto = tf.ConfigProto()
146
+ for key, value in cfg.items():
147
+ fields = key.split(".")
148
+ if fields[0] not in ["rnd", "env"]:
149
+ obj = config_proto
150
+ for field in fields[:-1]:
151
+ obj = getattr(obj, field)
152
+ setattr(obj, fields[-1], value)
153
+
154
+ # Create session.
155
+ session = tf.Session(config=config_proto)
156
+ if force_as_default:
157
+ # pylint: disable=protected-access
158
+ session._default_session = session.as_default()
159
+ session._default_session.enforce_nesting = False
160
+ session._default_session.__enter__()
161
+ return session
162
+
163
+
164
+ def init_uninitialized_vars(target_vars: List[tf.Variable] = None) -> None:
165
+ """Initialize all tf.Variables that have not already been initialized.
166
+
167
+ Equivalent to the following, but more efficient and does not bloat the tf graph:
168
+ tf.variables_initializer(tf.report_uninitialized_variables()).run()
169
+ """
170
+ assert_tf_initialized()
171
+ if target_vars is None:
172
+ target_vars = tf.global_variables()
173
+
174
+ test_vars = []
175
+ test_ops = []
176
+
177
+ with tf.control_dependencies(None): # ignore surrounding control_dependencies
178
+ for var in target_vars:
179
+ assert is_tf_expression(var)
180
+
181
+ try:
182
+ tf.get_default_graph().get_tensor_by_name(var.name.replace(":0", "/IsVariableInitialized:0"))
183
+ except KeyError:
184
+ # Op does not exist => variable may be uninitialized.
185
+ test_vars.append(var)
186
+
187
+ with absolute_name_scope(var.name.split(":")[0]):
188
+ test_ops.append(tf.is_variable_initialized(var))
189
+
190
+ init_vars = [var for var, inited in zip(test_vars, run(test_ops)) if not inited]
191
+ run([var.initializer for var in init_vars])
192
+
193
+
194
+ def set_vars(var_to_value_dict: dict) -> None:
195
+ """Set the values of given tf.Variables.
196
+
197
+ Equivalent to the following, but more efficient and does not bloat the tf graph:
198
+ tflib.run([tf.assign(var, value) for var, value in var_to_value_dict.items()]
199
+ """
200
+ assert_tf_initialized()
201
+ ops = []
202
+ feed_dict = {}
203
+
204
+ for var, value in var_to_value_dict.items():
205
+ assert is_tf_expression(var)
206
+
207
+ try:
208
+ setter = tf.get_default_graph().get_tensor_by_name(var.name.replace(":0", "/setter:0")) # look for existing op
209
+ except KeyError:
210
+ with absolute_name_scope(var.name.split(":")[0]):
211
+ with tf.control_dependencies(None): # ignore surrounding control_dependencies
212
+ setter = tf.assign(var, tf.placeholder(var.dtype, var.shape, "new_value"), name="setter") # create new setter
213
+
214
+ ops.append(setter)
215
+ feed_dict[setter.op.inputs[1]] = value
216
+
217
+ run(ops, feed_dict)
218
+
219
+
220
+ def create_var_with_large_initial_value(initial_value: np.ndarray, *args, **kwargs):
221
+ """Create tf.Variable with large initial value without bloating the tf graph."""
222
+ assert_tf_initialized()
223
+ assert isinstance(initial_value, np.ndarray)
224
+ zeros = tf.zeros(initial_value.shape, initial_value.dtype)
225
+ var = tf.Variable(zeros, *args, **kwargs)
226
+ set_vars({var: initial_value})
227
+ return var
228
+
229
+
230
+ def convert_images_from_uint8(images, drange=[-1,1], nhwc_to_nchw=False):
231
+ """Convert a minibatch of images from uint8 to float32 with configurable dynamic range.
232
+ Can be used as an input transformation for Network.run().
233
+ """
234
+ images = tf.cast(images, tf.float32)
235
+ if nhwc_to_nchw:
236
+ images = tf.transpose(images, [0, 3, 1, 2])
237
+ return images * ((drange[1] - drange[0]) / 255) + drange[0]
238
+
239
+
240
+ def convert_images_to_uint8(images, drange=[-1,1], nchw_to_nhwc=False, shrink=1):
241
+ """Convert a minibatch of images from float32 to uint8 with configurable dynamic range.
242
+ Can be used as an output transformation for Network.run().
243
+ """
244
+ images = tf.cast(images, tf.float32)
245
+ if shrink > 1:
246
+ ksize = [1, 1, shrink, shrink]
247
+ images = tf.nn.avg_pool(images, ksize=ksize, strides=ksize, padding="VALID", data_format="NCHW")
248
+ if nchw_to_nhwc:
249
+ images = tf.transpose(images, [0, 2, 3, 1])
250
+ scale = 255 / (drange[1] - drange[0])
251
+ images = images * scale + (0.5 - drange[0] * scale)
252
+ return tf.saturate_cast(images, tf.uint8)
dnnlib/util.py ADDED
@@ -0,0 +1,515 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved.
2
+ #
3
+ # NVIDIA CORPORATION and its licensors retain all intellectual property
4
+ # and proprietary rights in and to this software, related documentation
5
+ # and any modifications thereto. Any use, reproduction, disclosure or
6
+ # distribution of this software and related documentation without an express
7
+ # license agreement from NVIDIA CORPORATION is strictly prohibited.
8
+
9
+ """Miscellaneous utility classes and functions."""
10
+
11
+ import ctypes
12
+ import fnmatch
13
+ import glob
14
+ import hashlib
15
+ import html
16
+ import importlib
17
+ import inspect
18
+ import io
19
+ import os
20
+ import pickle
21
+ import re
22
+ import shutil
23
+ import sys
24
+ import tempfile
25
+ import types
26
+ import urllib
27
+ import urllib.request
28
+ import uuid
29
+ from distutils.util import strtobool
30
+ from typing import Any, List, Tuple, Union
31
+
32
+ import numpy as np
33
+ import requests
34
+
35
+
36
+ # Util classes
37
+ # ------------------------------------------------------------------------------------------
38
+
39
+
40
+ class EasyDict(dict):
41
+ """Convenience class that behaves like a dict but allows access with the attribute syntax."""
42
+
43
+ def __getattr__(self, name: str) -> Any:
44
+ try:
45
+ return self[name]
46
+ except KeyError:
47
+ raise AttributeError(name)
48
+
49
+ def __setattr__(self, name: str, value: Any) -> None:
50
+ self[name] = value
51
+
52
+ def __delattr__(self, name: str) -> None:
53
+ del self[name]
54
+
55
+
56
+ class Logger(object):
57
+ """Redirect stderr to stdout, optionally print stdout to a file, and optionally force flushing on both stdout and the file."""
58
+
59
+ def __init__(
60
+ self, file_name: str = None, file_mode: str = "w", should_flush: bool = True
61
+ ):
62
+ self.file = None
63
+
64
+ if file_name is not None:
65
+ self.file = open(file_name, file_mode)
66
+
67
+ self.should_flush = should_flush
68
+ self.stdout = sys.stdout
69
+ self.stderr = sys.stderr
70
+
71
+ sys.stdout = self
72
+ sys.stderr = self
73
+
74
+ def __enter__(self) -> "Logger":
75
+ return self
76
+
77
+ def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None:
78
+ self.close()
79
+
80
+ def write(self, text: Union[str, bytes]) -> None:
81
+ """Write text to stdout (and a file) and optionally flush."""
82
+ if isinstance(text, bytes):
83
+ text = text.decode()
84
+ if (
85
+ len(text) == 0
86
+ ): # workaround for a bug in VSCode debugger: sys.stdout.write(''); sys.stdout.flush() => crash
87
+ return
88
+
89
+ if self.file is not None:
90
+ self.file.write(text)
91
+
92
+ self.stdout.write(text)
93
+
94
+ if self.should_flush:
95
+ self.flush()
96
+
97
+ def flush(self) -> None:
98
+ """Flush written text to both stdout and a file, if open."""
99
+ if self.file is not None:
100
+ self.file.flush()
101
+
102
+ self.stdout.flush()
103
+
104
+ def close(self) -> None:
105
+ """Flush, close possible files, and remove stdout/stderr mirroring."""
106
+ self.flush()
107
+
108
+ # if using multiple loggers, prevent closing in wrong order
109
+ if sys.stdout is self:
110
+ sys.stdout = self.stdout
111
+ if sys.stderr is self:
112
+ sys.stderr = self.stderr
113
+
114
+ if self.file is not None:
115
+ self.file.close()
116
+ self.file = None
117
+
118
+
119
+ # Cache directories
120
+ # ------------------------------------------------------------------------------------------
121
+
122
+ _dnnlib_cache_dir = None
123
+
124
+
125
+ def set_cache_dir(path: str) -> None:
126
+ global _dnnlib_cache_dir
127
+ _dnnlib_cache_dir = path
128
+
129
+
130
+ def make_cache_dir_path(*paths: str) -> str:
131
+ if _dnnlib_cache_dir is not None:
132
+ return os.path.join(_dnnlib_cache_dir, *paths)
133
+ if "DNNLIB_CACHE_DIR" in os.environ:
134
+ return os.path.join(os.environ["DNNLIB_CACHE_DIR"], *paths)
135
+ if "HOME" in os.environ:
136
+ return os.path.join(os.environ["HOME"], ".cache", "dnnlib", *paths)
137
+ if "USERPROFILE" in os.environ:
138
+ return os.path.join(os.environ["USERPROFILE"], ".cache", "dnnlib", *paths)
139
+ return os.path.join(tempfile.gettempdir(), ".cache", "dnnlib", *paths)
140
+
141
+
142
+ # Small util functions
143
+ # ------------------------------------------------------------------------------------------
144
+
145
+
146
+ def format_time(seconds: Union[int, float]) -> str:
147
+ """Convert the seconds to human readable string with days, hours, minutes and seconds."""
148
+ s = int(np.rint(seconds))
149
+
150
+ if s < 60:
151
+ return "{0}s".format(s)
152
+ elif s < 60 * 60:
153
+ return "{0}m {1:02}s".format(s // 60, s % 60)
154
+ elif s < 24 * 60 * 60:
155
+ return "{0}h {1:02}m {2:02}s".format(s // (60 * 60), (s // 60) % 60, s % 60)
156
+ else:
157
+ return "{0}d {1:02}h {2:02}m".format(
158
+ s // (24 * 60 * 60), (s // (60 * 60)) % 24, (s // 60) % 60
159
+ )
160
+
161
+
162
+ def ask_yes_no(question: str) -> bool:
163
+ """Ask the user the question until the user inputs a valid answer."""
164
+ while True:
165
+ try:
166
+ print("{0} [y/n]".format(question))
167
+ return strtobool(input().lower())
168
+ except ValueError:
169
+ pass
170
+
171
+
172
+ def tuple_product(t: Tuple) -> Any:
173
+ """Calculate the product of the tuple elements."""
174
+ result = 1
175
+
176
+ for v in t:
177
+ result *= v
178
+
179
+ return result
180
+
181
+
182
+ _str_to_ctype = {
183
+ "uint8": ctypes.c_ubyte,
184
+ "uint16": ctypes.c_uint16,
185
+ "uint32": ctypes.c_uint32,
186
+ "uint64": ctypes.c_uint64,
187
+ "int8": ctypes.c_byte,
188
+ "int16": ctypes.c_int16,
189
+ "int32": ctypes.c_int32,
190
+ "int64": ctypes.c_int64,
191
+ "float32": ctypes.c_float,
192
+ "float64": ctypes.c_double,
193
+ }
194
+
195
+
196
+ def get_dtype_and_ctype(type_obj: Any) -> Tuple[np.dtype, Any]:
197
+ """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."""
198
+ type_str = None
199
+
200
+ if isinstance(type_obj, str):
201
+ type_str = type_obj
202
+ elif hasattr(type_obj, "__name__"):
203
+ type_str = type_obj.__name__
204
+ elif hasattr(type_obj, "name"):
205
+ type_str = type_obj.name
206
+ else:
207
+ raise RuntimeError("Cannot infer type name from input")
208
+
209
+ assert type_str in _str_to_ctype.keys()
210
+
211
+ my_dtype = np.dtype(type_str)
212
+ my_ctype = _str_to_ctype[type_str]
213
+
214
+ assert my_dtype.itemsize == ctypes.sizeof(my_ctype)
215
+
216
+ return my_dtype, my_ctype
217
+
218
+
219
+ def is_pickleable(obj: Any) -> bool:
220
+ try:
221
+ with io.BytesIO() as stream:
222
+ pickle.dump(obj, stream)
223
+ return True
224
+ except Exception:
225
+ return False
226
+
227
+
228
+ # Functionality to import modules/objects by name, and call functions by name
229
+ # ------------------------------------------------------------------------------------------
230
+
231
+
232
+ def get_module_from_obj_name(obj_name: str) -> Tuple[types.ModuleType, str]:
233
+ """Searches for the underlying module behind the name to some python object.
234
+ Returns the module and the object name (original name with module part removed)."""
235
+
236
+ # allow convenience shorthands, substitute them by full names
237
+ obj_name = re.sub("^np.", "numpy.", obj_name)
238
+ obj_name = re.sub("^tf.", "tensorflow.", obj_name)
239
+
240
+ # list alternatives for (module_name, local_obj_name)
241
+ parts = obj_name.split(".")
242
+ name_pairs = [
243
+ (".".join(parts[:i]), ".".join(parts[i:])) for i in range(len(parts), 0, -1)
244
+ ]
245
+
246
+ # try each alternative in turn
247
+ for module_name, local_obj_name in name_pairs:
248
+ try:
249
+ module = importlib.import_module(module_name) # may raise ImportError
250
+ get_obj_from_module(module, local_obj_name) # may raise AttributeError
251
+ return module, local_obj_name
252
+ except Exception:
253
+ pass
254
+
255
+ # maybe some of the modules themselves contain errors?
256
+ for module_name, _local_obj_name in name_pairs:
257
+ try:
258
+ importlib.import_module(module_name) # may raise ImportError
259
+ except ImportError:
260
+ if not str(sys.exc_info()[1]).startswith(
261
+ "No module named '" + module_name + "'"
262
+ ):
263
+ raise
264
+
265
+ # maybe the requested attribute is missing?
266
+ for module_name, local_obj_name in name_pairs:
267
+ try:
268
+ module = importlib.import_module(module_name) # may raise ImportError
269
+ get_obj_from_module(module, local_obj_name) # may raise AttributeError
270
+ except ImportError:
271
+ pass
272
+
273
+ # we are out of luck, but we have no idea why
274
+ raise ImportError(obj_name)
275
+
276
+
277
+ def get_obj_from_module(module: types.ModuleType, obj_name: str) -> Any:
278
+ """Traverses the object name and returns the last (rightmost) python object."""
279
+ if obj_name == "":
280
+ return module
281
+ obj = module
282
+ for part in obj_name.split("."):
283
+ obj = getattr(obj, part)
284
+ return obj
285
+
286
+
287
+ def get_obj_by_name(name: str) -> Any:
288
+ """Finds the python object with the given name."""
289
+ module, obj_name = get_module_from_obj_name(name)
290
+ return get_obj_from_module(module, obj_name)
291
+
292
+
293
+ def call_func_by_name(*args, func_name: str = None, **kwargs) -> Any:
294
+ """Finds the python object with the given name and calls it as a function."""
295
+ assert func_name is not None
296
+ func_obj = get_obj_by_name(func_name)
297
+ assert callable(func_obj)
298
+ return func_obj(*args, **kwargs)
299
+
300
+
301
+ def construct_class_by_name(*args, class_name: str = None, **kwargs) -> Any:
302
+ """Finds the python class with the given name and constructs it with the given arguments."""
303
+ return call_func_by_name(*args, func_name=class_name, **kwargs)
304
+
305
+
306
+ def get_module_dir_by_obj_name(obj_name: str) -> str:
307
+ """Get the directory path of the module containing the given object name."""
308
+ module, _ = get_module_from_obj_name(obj_name)
309
+ return os.path.dirname(inspect.getfile(module))
310
+
311
+
312
+ def is_top_level_function(obj: Any) -> bool:
313
+ """Determine whether the given object is a top-level function, i.e., defined at module scope using 'def'."""
314
+ return callable(obj) and obj.__name__ in sys.modules[obj.__module__].__dict__
315
+
316
+
317
+ def get_top_level_function_name(obj: Any) -> str:
318
+ """Return the fully-qualified name of a top-level function."""
319
+ assert is_top_level_function(obj)
320
+ module = obj.__module__
321
+ if module == "__main__":
322
+ module = os.path.splitext(os.path.basename(sys.modules[module].__file__))[0]
323
+ return module + "." + obj.__name__
324
+
325
+
326
+ # File system helpers
327
+ # ------------------------------------------------------------------------------------------
328
+
329
+
330
+ def list_dir_recursively_with_ignore(
331
+ dir_path: str, ignores: List[str] = None, add_base_to_relative: bool = False
332
+ ) -> List[Tuple[str, str]]:
333
+ """List all files recursively in a given directory while ignoring given file and directory names.
334
+ Returns list of tuples containing both absolute and relative paths."""
335
+ assert os.path.isdir(dir_path)
336
+ base_name = os.path.basename(os.path.normpath(dir_path))
337
+
338
+ if ignores is None:
339
+ ignores = []
340
+
341
+ result = []
342
+
343
+ for root, dirs, files in os.walk(dir_path, topdown=True):
344
+ for ignore_ in ignores:
345
+ dirs_to_remove = [d for d in dirs if fnmatch.fnmatch(d, ignore_)]
346
+
347
+ # dirs need to be edited in-place
348
+ for d in dirs_to_remove:
349
+ dirs.remove(d)
350
+
351
+ files = [f for f in files if not fnmatch.fnmatch(f, ignore_)]
352
+
353
+ absolute_paths = [os.path.join(root, f) for f in files]
354
+ relative_paths = [os.path.relpath(p, dir_path) for p in absolute_paths]
355
+
356
+ if add_base_to_relative:
357
+ relative_paths = [os.path.join(base_name, p) for p in relative_paths]
358
+
359
+ assert len(absolute_paths) == len(relative_paths)
360
+ result += zip(absolute_paths, relative_paths)
361
+
362
+ return result
363
+
364
+
365
+ def copy_files_and_create_dirs(files: List[Tuple[str, str]]) -> None:
366
+ """Takes in a list of tuples of (src, dst) paths and copies files.
367
+ Will create all necessary directories."""
368
+ for file in files:
369
+ target_dir_name = os.path.dirname(file[1])
370
+
371
+ # will create all intermediate-level directories
372
+ if not os.path.exists(target_dir_name):
373
+ os.makedirs(target_dir_name)
374
+
375
+ shutil.copyfile(file[0], file[1])
376
+
377
+
378
+ # URL helpers
379
+ # ------------------------------------------------------------------------------------------
380
+
381
+
382
+ def is_url(obj: Any, allow_file_urls: bool = False) -> bool:
383
+ """Determine whether the given object is a valid URL string."""
384
+ if not isinstance(obj, str) or "://" not in obj:
385
+ return False
386
+ if allow_file_urls and obj.startswith("file://"):
387
+ return True
388
+ try:
389
+ res = requests.compat.urlparse(obj)
390
+ if not res.scheme or not res.netloc or "." not in res.netloc:
391
+ return False
392
+ res = requests.compat.urlparse(requests.compat.urljoin(obj, "/"))
393
+ if not res.scheme or not res.netloc or "." not in res.netloc:
394
+ return False
395
+ except Exception:
396
+ return False
397
+ return True
398
+
399
+
400
+ def open_url(
401
+ url: str,
402
+ cache_dir: str = None,
403
+ num_attempts: int = 10,
404
+ verbose: bool = True,
405
+ return_filename: bool = False,
406
+ cache: bool = True,
407
+ ) -> Any:
408
+ """Download the given URL and return a binary-mode file object to access the data."""
409
+ assert num_attempts >= 1
410
+ assert not (return_filename and (not cache))
411
+
412
+ # Doesn't look like an URL scheme so interpret it as a local filename.
413
+ if not re.match("^[a-z]+://", url):
414
+ return url if return_filename else open(url, "rb")
415
+
416
+ # Handle file URLs. This code handles unusual file:// patterns that
417
+ # arise on Windows:
418
+ #
419
+ # file:///c:/foo.txt
420
+ #
421
+ # which would translate to a local '/c:/foo.txt' filename that's
422
+ # invalid. Drop the forward slash for such pathnames.
423
+ #
424
+ # If you touch this code path, you should test it on both Linux and
425
+ # Windows.
426
+ #
427
+ # Some internet resources suggest using urllib.request.url2pathname() but
428
+ # but that converts forward slashes to backslashes and this causes
429
+ # its own set of problems.
430
+ if url.startswith("file://"):
431
+ filename = urllib.parse.urlparse(url).path
432
+ if re.match(r"^/[a-zA-Z]:", filename):
433
+ filename = filename[1:]
434
+ return filename if return_filename else open(filename, "rb")
435
+
436
+ assert is_url(url)
437
+
438
+ # Lookup from cache.
439
+ if cache_dir is None:
440
+ cache_dir = make_cache_dir_path("downloads")
441
+
442
+ print(cache_dir)
443
+
444
+ url_md5 = hashlib.md5(url.encode("utf-8")).hexdigest()
445
+ if cache:
446
+ cache_files = glob.glob(os.path.join(cache_dir, url_md5 + "_*"))
447
+ if len(cache_files) == 1:
448
+ filename = cache_files[0]
449
+ return filename if return_filename else open(filename, "rb")
450
+
451
+ # Download.
452
+ url_name = None
453
+ url_data = None
454
+ with requests.Session() as session:
455
+ if verbose:
456
+ print("Downloading %s ..." % url, end="", flush=True)
457
+ for attempts_left in reversed(range(num_attempts)):
458
+ try:
459
+ with session.get(url) as res:
460
+ res.raise_for_status()
461
+ if len(res.content) == 0:
462
+ raise IOError("No data received")
463
+
464
+ if len(res.content) < 8192:
465
+ content_str = res.content.decode("utf-8")
466
+ if "download_warning" in res.headers.get("Set-Cookie", ""):
467
+ links = [
468
+ html.unescape(link)
469
+ for link in content_str.split('"')
470
+ if "export=download" in link
471
+ ]
472
+ if len(links) == 1:
473
+ url = requests.compat.urljoin(url, links[0])
474
+ raise IOError("Google Drive virus checker nag")
475
+ if "Google Drive - Quota exceeded" in content_str:
476
+ raise IOError(
477
+ "Google Drive download quota exceeded -- please try again later"
478
+ )
479
+
480
+ match = re.search(
481
+ r'filename="([^"]*)"',
482
+ res.headers.get("Content-Disposition", ""),
483
+ )
484
+ url_name = match[1] if match else url
485
+ url_data = res.content
486
+ if verbose:
487
+ print(" done")
488
+ break
489
+ except KeyboardInterrupt:
490
+ raise
491
+ except Exception:
492
+ if not attempts_left:
493
+ if verbose:
494
+ print(" failed")
495
+ raise
496
+ if verbose:
497
+ print(".", end="", flush=True)
498
+
499
+ # Save to cache.
500
+ if cache:
501
+ safe_name = re.sub(r"[^0-9a-zA-Z-._]", "_", url_name)
502
+ cache_file = os.path.join(cache_dir, url_md5 + "_" + safe_name)
503
+ temp_file = os.path.join(
504
+ cache_dir, "tmp_" + uuid.uuid4().hex + "_" + url_md5 + "_" + safe_name
505
+ )
506
+ os.makedirs(cache_dir, exist_ok=True)
507
+ with open(temp_file, "wb") as f:
508
+ f.write(url_data)
509
+ os.replace(temp_file, cache_file) # atomic
510
+ if return_filename:
511
+ return cache_file
512
+
513
+ # Return data as file object.
514
+ assert not return_filename
515
+ return io.BytesIO(url_data)
editings/bound/Eyeglasses_boundary.npy ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:227c8edae45f8cf06a0578e8422ef2dc59de6fb741d55280746a63fd40d2936c
3
+ size 36992
editings/bound/Heavy_Makeup_boundary.npy ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:dbd6336240825cf1a8077c5e40bde544ebd6b7c300ee76e28991e8e2d892030d
3
+ size 36992
editings/bound/Smiling_boundary.npy ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:5782190b00340d58f8418885dc9ad9ab53462874d7a9a1b91241e76099c9fc22
3
+ size 36992
editings/deltaedit/delta_mapper.py ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import math
2
+
3
+ import torch
4
+ from torch import nn
5
+ from torch.nn import Module
6
+ import torch.nn.functional as F
7
+
8
+ from models.psp.stylegan2.model import EqualLinear, PixelNorm
9
+
10
+ class Mapper(Module):
11
+
12
+ def __init__(self, in_channel=512, out_channel=512, norm=True, num_layers=4):
13
+ super(Mapper, self).__init__()
14
+
15
+ layers = [PixelNorm()] if norm else []
16
+
17
+ layers.append(EqualLinear(in_channel, out_channel, lr_mul=0.01, activation='fused_lrelu'))
18
+ for _ in range(num_layers-1):
19
+ layers.append(EqualLinear(out_channel, out_channel, lr_mul=0.01, activation='fused_lrelu'))
20
+ self.mapping = nn.Sequential(*layers)
21
+
22
+ def forward(self, x):
23
+ x = self.mapping(x)
24
+ return x
25
+
26
+ class DeltaMapper(Module):
27
+
28
+ def __init__(self):
29
+ super(DeltaMapper, self).__init__()
30
+
31
+ #Style Module(sm)
32
+ self.sm_coarse = Mapper(512, 512)
33
+ self.sm_medium = Mapper(512, 512)
34
+ self.sm_fine = Mapper(2464, 2464)
35
+
36
+ #Condition Module(cm)
37
+ self.cm_coarse = Mapper(1024, 512)
38
+ self.cm_medium = Mapper(1024, 512)
39
+ self.cm_fine = Mapper(1024, 2464)
40
+
41
+ #Fusion Module(fm)
42
+ self.fm_coarse = Mapper(512*2, 512, norm=False)
43
+ self.fm_medium = Mapper(512*2, 512, norm=False)
44
+ self.fm_fine = Mapper(2464*2, 2464, norm=False)
45
+
46
+ def forward(self, sspace_feat, clip_feat):
47
+
48
+ s_coarse = sspace_feat[:, :3*512].view(-1,3,512)
49
+ s_medium = sspace_feat[:, 3*512:7*512].view(-1,4,512)
50
+ s_fine = sspace_feat[:, 7*512:] #channels:2464
51
+
52
+ s_coarse = self.sm_coarse(s_coarse)
53
+ s_medium = self.sm_medium(s_medium)
54
+ s_fine = self.sm_fine(s_fine)
55
+
56
+ c_coarse = self.cm_coarse(clip_feat)
57
+ c_medium = self.cm_medium(clip_feat)
58
+ c_fine = self.cm_fine(clip_feat)
59
+
60
+ x_coarse = torch.cat([s_coarse, torch.stack([c_coarse]*3, dim=1)], dim=2) #[b,3,1024]
61
+ x_medium = torch.cat([s_medium, torch.stack([c_medium]*4, dim=1)], dim=2) #[b,4,1024]
62
+ x_fine = torch.cat([s_fine, c_fine], dim=1) #[b,2464*2]
63
+
64
+ x_coarse = self.fm_coarse(x_coarse)
65
+ x_coarse = x_coarse.view(-1,3*512)
66
+
67
+ x_medium = self.fm_medium(x_medium)
68
+ x_medium = x_medium.view(-1,4*512)
69
+
70
+ x_fine = self.fm_fine(x_fine)
71
+
72
+ out = torch.cat([x_coarse, x_medium, x_fine], dim=1)
73
+ return out
editings/deltaedit/editor.py ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import clip
3
+ import copy
4
+ import numpy as np
5
+ import torch.nn.functional as F
6
+
7
+ from editings.deltaedit import map_tool
8
+ from editings.deltaedit.delta_mapper import DeltaMapper
9
+
10
+
11
+ STYLE_DIM = [512] * 10 + [256, 256, 128, 128, 64, 64, 32]
12
+
13
+
14
+ def GetBoundary(fs3, dt, threshold):
15
+ tmp = np.dot(fs3, dt)
16
+ select = np.abs(tmp) < threshold
17
+ return select
18
+
19
+ def improved_ds(ds, select):
20
+ ds_imp = copy.copy(ds)
21
+ ds_imp[select] = 0
22
+ ds_imp = ds_imp.unsqueeze(0)
23
+ return ds_imp
24
+
25
+
26
+ class DeltaEditor:
27
+ def __init__(self):
28
+ device = "cuda"
29
+ self.fs3 = np.load("pretrained_models/fs3.npy")
30
+ np.set_printoptions(suppress=True)
31
+
32
+ self.net = DeltaMapper()
33
+ net_ckpt = torch.load("pretrained_models/delta_mapper.pt")
34
+ self.net.load_state_dict(net_ckpt)
35
+ self.net = self.net.to(device).eval()
36
+
37
+ self.clip_model, self.preprocess = clip.load("ViT-B/32", device=device)
38
+ self.avg_pool = torch.nn.AdaptiveAvgPool2d((224, 224))
39
+ self.upsample = torch.nn.Upsample(scale_factor=7)
40
+
41
+ def get_delta_s(self, neutral, target, trash, orig_image, start_s):
42
+ with torch.no_grad():
43
+ classnames = [target, neutral]
44
+ dt = map_tool.GetDt(classnames, self.clip_model)
45
+ select = GetBoundary(self.fs3, dt, trash)
46
+ dt = torch.Tensor(dt).cuda()
47
+ dt = dt / dt.norm(dim=-1, keepdim=True).float().clamp(min=1e-5)
48
+
49
+ img_gen_for_clip = self.avg_pool(orig_image)
50
+ c_latents = self.clip_model.encode_image(img_gen_for_clip.cuda())
51
+ c_latents = c_latents / c_latents.norm(dim=-1, keepdim=True).float()
52
+
53
+ delta_c = torch.cat((c_latents, dt.unsqueeze(0)), dim=1)
54
+ fake_delta_s = self.net(torch.cat(start_s, dim=-1), delta_c)
55
+ improved_fake_delta_s = improved_ds(fake_delta_s[0], select)
56
+ return torch.split(improved_fake_delta_s, STYLE_DIM, dim=-1)
57
+
58
+
59
+
editings/deltaedit/map_tool.py ADDED
@@ -0,0 +1,109 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import clip
3
+ import os
4
+ import numpy as np
5
+
6
+ imagenet_templates = [
7
+ 'a bad photo of a {}.',
8
+ # 'a photo of many {}.',
9
+ 'a sculpture of a {}.',
10
+ 'a photo of the hard to see {}.',
11
+ 'a low resolution photo of the {}.',
12
+ 'a rendering of a {}.',
13
+ 'graffiti of a {}.',
14
+ 'a bad photo of the {}.',
15
+ 'a cropped photo of the {}.',
16
+ 'a tattoo of a {}.',
17
+ 'the embroidered {}.',
18
+ 'a photo of a hard to see {}.',
19
+ 'a bright photo of a {}.',
20
+ 'a photo of a clean {}.',
21
+ 'a photo of a dirty {}.',
22
+ 'a dark photo of the {}.',
23
+ 'a drawing of a {}.',
24
+ 'a photo of my {}.',
25
+ 'the plastic {}.',
26
+ 'a photo of the cool {}.',
27
+ 'a close-up photo of a {}.',
28
+ 'a black and white photo of the {}.',
29
+ 'a painting of the {}.',
30
+ 'a painting of a {}.',
31
+ 'a pixelated photo of the {}.',
32
+ 'a sculpture of the {}.',
33
+ 'a bright photo of the {}.',
34
+ 'a cropped photo of a {}.',
35
+ 'a plastic {}.',
36
+ 'a photo of the dirty {}.',
37
+ 'a jpeg corrupted photo of a {}.',
38
+ 'a blurry photo of the {}.',
39
+ 'a photo of the {}.',
40
+ 'a good photo of the {}.',
41
+ 'a rendering of the {}.',
42
+ 'a {} in a video game.',
43
+ 'a photo of one {}.',
44
+ 'a doodle of a {}.',
45
+ 'a close-up photo of the {}.',
46
+ 'a photo of a {}.',
47
+ 'the origami {}.',
48
+ 'the {} in a video game.',
49
+ 'a sketch of a {}.',
50
+ 'a doodle of the {}.',
51
+ 'a origami {}.',
52
+ 'a low resolution photo of a {}.',
53
+ 'the toy {}.',
54
+ 'a rendition of the {}.',
55
+ 'a photo of the clean {}.',
56
+ 'a photo of a large {}.',
57
+ 'a rendition of a {}.',
58
+ 'a photo of a nice {}.',
59
+ 'a photo of a weird {}.',
60
+ 'a blurry photo of a {}.',
61
+ 'a cartoon {}.',
62
+ 'art of a {}.',
63
+ 'a sketch of the {}.',
64
+ 'a embroidered {}.',
65
+ 'a pixelated photo of a {}.',
66
+ 'itap of the {}.',
67
+ 'a jpeg corrupted photo of the {}.',
68
+ 'a good photo of a {}.',
69
+ 'a plushie {}.',
70
+ 'a photo of the nice {}.',
71
+ 'a photo of the small {}.',
72
+ 'a photo of the weird {}.',
73
+ 'the cartoon {}.',
74
+ 'art of the {}.',
75
+ 'a drawing of the {}.',
76
+ 'a photo of the large {}.',
77
+ 'a black and white photo of a {}.',
78
+ 'the plushie {}.',
79
+ 'a dark photo of a {}.',
80
+ 'itap of a {}.',
81
+ 'graffiti of the {}.',
82
+ 'a toy {}.',
83
+ 'itap of my {}.',
84
+ 'a photo of a cool {}.',
85
+ 'a photo of a small {}.',
86
+ 'a tattoo of the {}.',
87
+ ]
88
+
89
+ def zeroshot_classifier(classnames, templates,model):
90
+ with torch.no_grad():
91
+ zeroshot_weights = []
92
+ for classname in classnames:
93
+ texts = [template.format(classname) for template in templates] #format with class
94
+ texts = clip.tokenize(texts).cuda() #tokenize
95
+ class_embeddings = model.encode_text(texts) #embed with text encoder
96
+ class_embeddings /= class_embeddings.norm(dim=-1, keepdim=True)
97
+ class_embedding = class_embeddings.mean(dim=0)
98
+ class_embedding /= class_embedding.norm()
99
+ zeroshot_weights.append(class_embedding)
100
+ zeroshot_weights = torch.stack(zeroshot_weights, dim=1).cuda()
101
+ return zeroshot_weights
102
+
103
+ def GetDt(classnames,model):
104
+ text_features=zeroshot_classifier(classnames, imagenet_templates,model).t()
105
+
106
+ dt=text_features[0]-text_features[1]
107
+ dt=dt.cpu().numpy()
108
+
109
+ return dt
editings/ganspace.py ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+
3
+
4
+ def edit(latents, pca, edit_directions):
5
+ edit_latents = []
6
+ for latent in latents:
7
+ for pca_idx, start, end, strength in edit_directions:
8
+ delta = get_delta(pca, latent, pca_idx, strength)
9
+ delta_padded = torch.zeros(latent.shape).to("cuda")
10
+ delta_padded[start:end] += delta.repeat(end - start, 1)
11
+ edit_latents.append(latent + delta_padded)
12
+ return torch.stack(edit_latents)
13
+
14
+
15
+ def get_delta(pca, latent, idx, strength):
16
+ w_centered = latent - pca["mean"].to("cuda")
17
+ lat_comp = pca["comp"].to("cuda")
18
+ lat_std = pca["std"].to("cuda")
19
+ w_coord = (
20
+ torch.sum(w_centered[0].reshape(-1) * lat_comp[idx].reshape(-1)) / lat_std[idx]
21
+ )
22
+ delta = (strength - w_coord) * lat_comp[idx] * lat_std[idx]
23
+ return delta
24
+
25
+
26
+ def edit_latent(latent, pca, edit_direction):
27
+ pca_idx, start, end, strength = edit_direction
28
+ delta = get_delta(pca, latent, pca_idx, strength)
29
+ delta_padded = torch.zeros(latent.shape).to("cuda")
30
+ delta_padded[start:end] += delta.repeat(end - start, 1)
31
+ edit_latent = latent + delta_padded
32
+ return edit_latent
editings/ganspace_pca/cars_pca.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:a5c3bae61ecd85de077fbbf103f5f30cf4b7676fe23a8508166eaf2ce73c8392
3
+ size 167562
editings/ganspace_pca/church_pca.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:b848bc301a7a01a4303797768fe95bc88fea2e335a8e818a5c5575383f0496a5
3
+ size 166790
editings/ganspace_pca/ffhq_pca.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:4d7f9df1c96180d9026b9cb8d04753579fbf385f321a9d0e263641601c5e5d36
3
+ size 167562
editings/interfacegan_directions/age.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:50074516b1629707d89b5e43d6b8abd1792212fa3b961a87a11323d6a5222ae0
3
+ size 2808
editings/interfacegan_directions/rotation.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:9ef9787ac6b7b37940e47fef22e78cbbe5aeae2e566c0b391bf279f3264b1d1c
3
+ size 2808
editings/interfacegan_directions/smile.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:817a7e732b59dee9eba862bec8bd7e8373568443bc9f9731a21cf9b0356f0653
3
+ size 2808