InvokeAI 2.2.4.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. backend/__init__.py +0 -0
  2. backend/invoke_ai_web_server.py +1724 -0
  3. backend/modules/__init__.py +0 -0
  4. backend/modules/create_cmd_parser.py +55 -0
  5. backend/modules/get_canvas_generation_mode.py +117 -0
  6. backend/modules/parameters.py +71 -0
  7. backend/modules/parse_seed_weights.py +47 -0
  8. configs/INITIAL_MODELS.yaml +80 -0
  9. configs/models.yaml.example +29 -0
  10. configs/sd-concepts.txt +803 -0
  11. configs/stable-diffusion/v1-finetune.yaml +110 -0
  12. configs/stable-diffusion/v1-finetune_style.yaml +103 -0
  13. configs/stable-diffusion/v1-inference.yaml +79 -0
  14. configs/stable-diffusion/v1-inpainting-inference.yaml +79 -0
  15. configs/stable-diffusion/v1-m1-finetune.yaml +110 -0
  16. docs/assets/invoke-web-server-1.png +0 -0
  17. docs/assets/invoke_ai_banner.png +0 -0
  18. invokeai-2.2.4.5.data/data/assets/caution.png +0 -0
  19. invokeai-2.2.4.5.data/data/configs/INITIAL_MODELS.yaml +80 -0
  20. invokeai-2.2.4.5.data/data/configs/models.yaml.example +29 -0
  21. invokeai-2.2.4.5.data/data/configs/sd-concepts.txt +803 -0
  22. invokeai-2.2.4.5.data/data/configs/stable-diffusion/v1-finetune.yaml +110 -0
  23. invokeai-2.2.4.5.data/data/configs/stable-diffusion/v1-finetune_style.yaml +103 -0
  24. invokeai-2.2.4.5.data/data/configs/stable-diffusion/v1-inference.yaml +79 -0
  25. invokeai-2.2.4.5.data/data/configs/stable-diffusion/v1-inpainting-inference.yaml +79 -0
  26. invokeai-2.2.4.5.data/data/configs/stable-diffusion/v1-m1-finetune.yaml +110 -0
  27. invokeai-2.2.4.5.data/data/frontend/dist/assets/Inter-Bold.790c108b.ttf +0 -0
  28. invokeai-2.2.4.5.data/data/frontend/dist/assets/Inter.b9a8e5e2.ttf +0 -0
  29. invokeai-2.2.4.5.data/data/frontend/dist/assets/favicon.0d253ced.ico +0 -0
  30. invokeai-2.2.4.5.data/data/frontend/dist/assets/index-legacy-b741077f.js +48 -0
  31. invokeai-2.2.4.5.data/data/frontend/dist/assets/index.0dadf5d0.css +1 -0
  32. invokeai-2.2.4.5.data/data/frontend/dist/assets/index.21ff81eb.js +625 -0
  33. invokeai-2.2.4.5.data/data/frontend/dist/assets/logo.13003d72.png +0 -0
  34. invokeai-2.2.4.5.data/data/frontend/dist/assets/polyfills-legacy-dde3a68a.js +4 -0
  35. invokeai-2.2.4.5.data/data/frontend/dist/assets/polyfills.1ff60148.js +1 -0
  36. invokeai-2.2.4.5.data/data/frontend/dist/index.html +23 -0
  37. invokeai-2.2.4.5.data/data/scripts/configure_invokeai.py +797 -0
  38. invokeai-2.2.4.5.data/data/scripts/create_root.py +43 -0
  39. invokeai-2.2.4.5.data/data/scripts/dream.py +10 -0
  40. invokeai-2.2.4.5.data/data/scripts/images2prompt.py +30 -0
  41. invokeai-2.2.4.5.data/data/scripts/invoke.py +5 -0
  42. invokeai-2.2.4.5.data/data/scripts/legacy_api.py +681 -0
  43. invokeai-2.2.4.5.data/data/scripts/load_models.py +12 -0
  44. invokeai-2.2.4.5.data/data/scripts/merge_embeddings.py +124 -0
  45. invokeai-2.2.4.5.data/data/scripts/preload_models.py +12 -0
  46. invokeai-2.2.4.5.data/data/scripts/sd-metadata.py +22 -0
  47. invokeai-2.2.4.5.dist-info/LICENSE +21 -0
  48. invokeai-2.2.4.5.dist-info/LICENSE-ModelWeights.txt +294 -0
  49. invokeai-2.2.4.5.dist-info/METADATA +288 -0
  50. invokeai-2.2.4.5.dist-info/RECORD +169 -0
  51. invokeai-2.2.4.5.dist-info/WHEEL +5 -0
  52. invokeai-2.2.4.5.dist-info/entry_points.txt +4 -0
  53. invokeai-2.2.4.5.dist-info/top_level.txt +6 -0
  54. invokeai-2.2.4.5.dist-info/zip-safe +1 -0
  55. ldm/__init__.py +0 -0
  56. ldm/data/__init__.py +0 -0
  57. ldm/data/base.py +31 -0
  58. ldm/data/imagenet.py +466 -0
  59. ldm/data/lsun.py +126 -0
  60. ldm/data/personalized.py +202 -0
  61. ldm/data/personalized_style.py +169 -0
  62. ldm/generate.py +1159 -0
  63. ldm/invoke/CLI.py +960 -0
  64. ldm/invoke/__init__.py +3 -0
  65. ldm/invoke/args.py +1257 -0
  66. ldm/invoke/concepts_lib.py +169 -0
  67. ldm/invoke/conditioning.py +289 -0
  68. ldm/invoke/devices.py +27 -0
  69. ldm/invoke/generator/__init__.py +4 -0
  70. ldm/invoke/generator/base.py +335 -0
  71. ldm/invoke/generator/embiggen.py +501 -0
  72. ldm/invoke/generator/img2img.py +97 -0
  73. ldm/invoke/generator/inpaint.py +358 -0
  74. ldm/invoke/generator/omnibus.py +175 -0
  75. ldm/invoke/generator/txt2img.py +88 -0
  76. ldm/invoke/generator/txt2img2img.py +182 -0
  77. ldm/invoke/globals.py +31 -0
  78. ldm/invoke/image_util.py +70 -0
  79. ldm/invoke/log.py +66 -0
  80. ldm/invoke/model_cache.py +449 -0
  81. ldm/invoke/patchmatch.py +44 -0
  82. ldm/invoke/pngwriter.py +113 -0
  83. ldm/invoke/prompt_parser.py +655 -0
  84. ldm/invoke/readline.py +442 -0
  85. ldm/invoke/restoration/__init__.py +4 -0
  86. ldm/invoke/restoration/base.py +38 -0
  87. ldm/invoke/restoration/codeformer.py +103 -0
  88. ldm/invoke/restoration/codeformer_arch.py +276 -0
  89. ldm/invoke/restoration/gfpgan.py +84 -0
  90. ldm/invoke/restoration/outcrop.py +108 -0
  91. ldm/invoke/restoration/outpaint.py +92 -0
  92. ldm/invoke/restoration/realesrgan.py +90 -0
  93. ldm/invoke/restoration/vqgan_arch.py +435 -0
  94. ldm/invoke/seamless.py +30 -0
  95. ldm/invoke/server.py +282 -0
  96. ldm/invoke/server_legacy.py +246 -0
  97. ldm/invoke/txt2mask.py +136 -0
  98. ldm/lr_scheduler.py +143 -0
  99. ldm/models/__init__.py +0 -0
  100. ldm/models/autoencoder.py +596 -0
  101. ldm/models/diffusion/__init__.py +0 -0
  102. ldm/models/diffusion/classifier.py +355 -0
  103. ldm/models/diffusion/cross_attention_control.py +443 -0
  104. ldm/models/diffusion/cross_attention_map_saving.py +95 -0
  105. ldm/models/diffusion/ddim.py +111 -0
  106. ldm/models/diffusion/ddpm.py +2263 -0
  107. ldm/models/diffusion/ksampler.py +312 -0
  108. ldm/models/diffusion/plms.py +146 -0
  109. ldm/models/diffusion/sampler.py +450 -0
  110. ldm/models/diffusion/shared_invokeai_diffusion.py +244 -0
  111. ldm/modules/__init__.py +0 -0
  112. ldm/modules/attention.py +274 -0
  113. ldm/modules/diffusionmodules/__init__.py +0 -0
  114. ldm/modules/diffusionmodules/model.py +894 -0
  115. ldm/modules/diffusionmodules/openaimodel.py +1036 -0
  116. ldm/modules/diffusionmodules/util.py +314 -0
  117. ldm/modules/distributions/__init__.py +0 -0
  118. ldm/modules/distributions/distributions.py +104 -0
  119. ldm/modules/ema.py +88 -0
  120. ldm/modules/embedding_manager.py +413 -0
  121. ldm/modules/encoders/__init__.py +0 -0
  122. ldm/modules/encoders/modules.py +772 -0
  123. ldm/modules/image_degradation/__init__.py +6 -0
  124. ldm/modules/image_degradation/bsrgan.py +846 -0
  125. ldm/modules/image_degradation/bsrgan_light.py +751 -0
  126. ldm/modules/image_degradation/utils_image.py +1024 -0
  127. ldm/modules/losses/__init__.py +1 -0
  128. ldm/modules/losses/contperceptual.py +175 -0
  129. ldm/modules/losses/vqperceptual.py +240 -0
  130. ldm/modules/x_transformer.py +752 -0
  131. ldm/simplet2i.py +13 -0
  132. ldm/util.py +264 -0
  133. scripts/configure_invokeai.py +797 -0
  134. scripts/create_root.py +43 -0
  135. scripts/dream.py +10 -0
  136. scripts/images2prompt.py +30 -0
  137. scripts/invoke.py +5 -0
  138. scripts/legacy_api.py +681 -0
  139. scripts/load_models.py +12 -0
  140. scripts/merge_embeddings.py +124 -0
  141. scripts/orig_scripts/img2img.py +296 -0
  142. scripts/orig_scripts/inpaint.py +98 -0
  143. scripts/orig_scripts/knn2img.py +397 -0
  144. scripts/orig_scripts/sample_diffusion.py +313 -0
  145. scripts/orig_scripts/train_searcher.py +147 -0
  146. scripts/orig_scripts/txt2img.py +324 -0
  147. scripts/preload_models.py +12 -0
  148. scripts/sd-metadata.py +22 -0
  149. vendored/clip/clip/__init__.py +1 -0
  150. vendored/clip/clip/bpe_simple_vocab_16e6.txt.gz +0 -0
  151. vendored/clip/clip/clip.py +237 -0
  152. vendored/clip/clip/model.py +436 -0
  153. vendored/clip/clip/simple_tokenizer.py +132 -0
  154. vendored/clip/hubconf.py +42 -0
  155. vendored/clip/setup.py +21 -0
  156. vendored/clip/tests/test_consistency.py +25 -0
  157. vendored/clipseg/datasets/coco_wrapper.py +99 -0
  158. vendored/clipseg/datasets/pascal_zeroshot.py +60 -0
  159. vendored/clipseg/datasets/pfe_dataset.py +129 -0
  160. vendored/clipseg/datasets/phrasecut.py +380 -0
  161. vendored/clipseg/datasets/utils.py +68 -0
  162. vendored/clipseg/evaluation_utils.py +292 -0
  163. vendored/clipseg/general_utils.py +272 -0
  164. vendored/clipseg/metrics.py +271 -0
  165. vendored/clipseg/models/clipseg.py +538 -0
  166. vendored/clipseg/models/vitseg.py +286 -0
  167. vendored/clipseg/score.py +453 -0
  168. vendored/clipseg/setup.py +30 -0
  169. vendored/clipseg/training.py +266 -0
@@ -0,0 +1,292 @@
1
+ from torch.functional import Tensor
2
+ from general_utils import load_model
3
+ from torch.utils.data import DataLoader
4
+ import torch
5
+ import numpy as np
6
+
7
+ def denorm(img):
8
+
9
+ np_input = False
10
+ if isinstance(img, np.ndarray):
11
+ img = torch.from_numpy(img)
12
+ np_input = True
13
+
14
+ mean = torch.Tensor([0.485, 0.456, 0.406])
15
+ std = torch.Tensor([0.229, 0.224, 0.225])
16
+
17
+ img_denorm = (img*std[:,None,None]) + mean[:,None,None]
18
+
19
+ if np_input:
20
+ img_denorm = np.clip(img_denorm.numpy(), 0, 1)
21
+ else:
22
+ img_denorm = torch.clamp(img_denorm, 0, 1)
23
+
24
+ return img_denorm
25
+
26
+
27
+ def norm(img):
28
+ mean = torch.Tensor([0.485, 0.456, 0.406])
29
+ std = torch.Tensor([0.229, 0.224, 0.225])
30
+ return (img - mean[:,None,None]) / std[:,None,None]
31
+
32
+
33
+ def fast_iou_curve(p, g):
34
+
35
+ g = g[p.sort().indices]
36
+ p = torch.sigmoid(p.sort().values)
37
+
38
+ scores = []
39
+ vals = np.linspace(0, 1, 50)
40
+
41
+ for q in vals:
42
+
43
+ n = int(len(g) * q)
44
+
45
+ valid = torch.where(p > q)[0]
46
+ if len(valid) > 0:
47
+ n = int(valid[0])
48
+ else:
49
+ n = len(g)
50
+
51
+ fn = g[:n].sum()
52
+ tn = n - fn
53
+ tp = g[n:].sum()
54
+ fp = len(g) - n - tp
55
+
56
+ iou = tp / (tp + fn + fp)
57
+
58
+ precision = tp / (tp + fp)
59
+ recall = tp / (tp + fn)
60
+
61
+ scores += [iou]
62
+
63
+ return vals, scores
64
+
65
+
66
+ def fast_rp_curve(p, g):
67
+
68
+ g = g[p.sort().indices]
69
+ p = torch.sigmoid(p.sort().values)
70
+
71
+ precisions, recalls = [], []
72
+ vals = np.linspace(p.min(), p.max(), 250)
73
+
74
+ for q in p[::100000]:
75
+
76
+ n = int(len(g) * q)
77
+
78
+ valid = torch.where(p > q)[0]
79
+ if len(valid) > 0:
80
+ n = int(valid[0])
81
+ else:
82
+ n = len(g)
83
+
84
+ fn = g[:n].sum()
85
+ tn = n - fn
86
+ tp = g[n:].sum()
87
+ fp = len(g) - n - tp
88
+
89
+ iou = tp / (tp + fn + fp)
90
+
91
+ precision = tp / (tp + fp)
92
+ recall = tp / (tp + fn)
93
+
94
+ precisions += [precision]
95
+ recalls += [recall]
96
+
97
+ return recalls, precisions
98
+
99
+
100
+ # Image processing
101
+
102
+ def img_preprocess(batch, blur=0, grayscale=False, center_context=None, rect=False, rect_color=(255,0,0), rect_width=2,
103
+ brightness=1.0, bg_fac=1, colorize=False, outline=False, image_size=224):
104
+ import cv2
105
+
106
+ rw = rect_width
107
+
108
+ out = []
109
+ for img, mask in zip(batch[1], batch[2]):
110
+
111
+ img = img.cpu() if isinstance(img, torch.Tensor) else torch.from_numpy(img)
112
+ mask = mask.cpu() if isinstance(mask, torch.Tensor) else torch.from_numpy(mask)
113
+
114
+ img *= brightness
115
+ img_bl = img
116
+ if blur > 0: # best 5
117
+ img_bl = torch.from_numpy(cv2.GaussianBlur(img.permute(1,2,0).numpy(), (15, 15), blur)).permute(2,0,1)
118
+
119
+ if grayscale:
120
+ img_bl = img_bl[1][None]
121
+
122
+ #img_inp = img_ratio*img*mask + (1-img_ratio)*img_bl
123
+ # img_inp = img_ratio*img*mask + (1-img_ratio)*img_bl * (1-mask)
124
+ img_inp = img*mask + (bg_fac) * img_bl * (1-mask)
125
+
126
+ if rect:
127
+ _, bbox = crop_mask(img, mask, context=0.1)
128
+ img_inp[:, bbox[2]: bbox[3], max(0, bbox[0]-rw):bbox[0]+rw] = torch.tensor(rect_color)[:,None,None]
129
+ img_inp[:, bbox[2]: bbox[3], max(0, bbox[1]-rw):bbox[1]+rw] = torch.tensor(rect_color)[:,None,None]
130
+ img_inp[:, max(0, bbox[2]-1): bbox[2]+rw, bbox[0]:bbox[1]] = torch.tensor(rect_color)[:,None,None]
131
+ img_inp[:, max(0, bbox[3]-1): bbox[3]+rw, bbox[0]:bbox[1]] = torch.tensor(rect_color)[:,None,None]
132
+
133
+
134
+ if center_context is not None:
135
+ img_inp = object_crop(img_inp, mask, context=center_context, image_size=image_size)
136
+
137
+ if colorize:
138
+ img_gray = denorm(img)
139
+ img_gray = cv2.cvtColor(img_gray.permute(1,2,0).numpy(), cv2.COLOR_RGB2GRAY)
140
+ img_gray = torch.stack([torch.from_numpy(img_gray)]*3)
141
+ img_inp = torch.tensor([1,0.2,0.2])[:,None,None] * img_gray * mask + bg_fac * img_gray * (1-mask)
142
+ img_inp = norm(img_inp)
143
+
144
+ if outline:
145
+ cont = cv2.findContours(mask.byte().numpy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
146
+ outline_img = np.zeros(mask.shape, dtype=np.uint8)
147
+ cv2.drawContours(outline_img, cont[0], -1, thickness=5, color=(255, 255, 255))
148
+ outline_img = torch.stack([torch.from_numpy(outline_img)]*3).float() / 255.
149
+ img_inp = torch.tensor([1,0,0])[:,None,None] * outline_img + denorm(img_inp) * (1- outline_img)
150
+ img_inp = norm(img_inp)
151
+
152
+ out += [img_inp]
153
+
154
+ return torch.stack(out)
155
+
156
+
157
+ def object_crop(img, mask, context=0.0, square=False, image_size=224):
158
+ img_crop, bbox = crop_mask(img, mask, context=context, square=square)
159
+ img_crop = pad_to_square(img_crop, channel_dim=0)
160
+ img_crop = torch.nn.functional.interpolate(img_crop.unsqueeze(0), (image_size, image_size)).squeeze(0)
161
+ return img_crop
162
+
163
+
164
+ def crop_mask(img, mask, context=0.0, square=False):
165
+
166
+ assert img.shape[1:] == mask.shape
167
+
168
+ bbox = [mask.max(0).values.argmax(), mask.size(0) - mask.max(0).values.flip(0).argmax()]
169
+ bbox += [mask.max(1).values.argmax(), mask.size(1) - mask.max(1).values.flip(0).argmax()]
170
+ bbox = [int(x) for x in bbox]
171
+
172
+ width, height = (bbox[3] - bbox[2]), (bbox[1] - bbox[0])
173
+
174
+ # square mask
175
+ if square:
176
+ bbox[0] = int(max(0, bbox[0] - context * height))
177
+ bbox[1] = int(min(mask.size(0), bbox[1] + context * height))
178
+ bbox[2] = int(max(0, bbox[2] - context * width))
179
+ bbox[3] = int(min(mask.size(1), bbox[3] + context * width))
180
+
181
+ width, height = (bbox[3] - bbox[2]), (bbox[1] - bbox[0])
182
+ if height > width:
183
+ bbox[2] = int(max(0, (bbox[2] - 0.5*height)))
184
+ bbox[3] = bbox[2] + height
185
+ else:
186
+ bbox[0] = int(max(0, (bbox[0] - 0.5*width)))
187
+ bbox[1] = bbox[0] + width
188
+ else:
189
+ bbox[0] = int(max(0, bbox[0] - context * height))
190
+ bbox[1] = int(min(mask.size(0), bbox[1] + context * height))
191
+ bbox[2] = int(max(0, bbox[2] - context * width))
192
+ bbox[3] = int(min(mask.size(1), bbox[3] + context * width))
193
+
194
+ width, height = (bbox[3] - bbox[2]), (bbox[1] - bbox[0])
195
+ img_crop = img[:, bbox[2]: bbox[3], bbox[0]: bbox[1]]
196
+ return img_crop, bbox
197
+
198
+
199
+ def pad_to_square(img, channel_dim=2, fill=0):
200
+ """
201
+
202
+
203
+ add padding such that a squared image is returned """
204
+
205
+ from torchvision.transforms.functional import pad
206
+
207
+ if channel_dim == 2:
208
+ img = img.permute(2, 0, 1)
209
+ elif channel_dim == 0:
210
+ pass
211
+ else:
212
+ raise ValueError('invalid channel_dim')
213
+
214
+ h, w = img.shape[1:]
215
+ pady1 = pady2 = padx1 = padx2 = 0
216
+
217
+ if h > w:
218
+ padx1 = (h - w) // 2
219
+ padx2 = h - w - padx1
220
+ elif w > h:
221
+ pady1 = (w - h) // 2
222
+ pady2 = w - h - pady1
223
+
224
+ img_padded = pad(img, padding=(padx1, pady1, padx2, pady2), padding_mode='constant')
225
+
226
+ if channel_dim == 2:
227
+ img_padded = img_padded.permute(1, 2, 0)
228
+
229
+ return img_padded
230
+
231
+
232
+ # qualitative
233
+
234
+ def split_sentence(inp, limit=9):
235
+ t_new, current_len = [], 0
236
+ for k, t in enumerate(inp.split(' ')):
237
+ current_len += len(t) + 1
238
+ t_new += [t+' ']
239
+ # not last
240
+ if current_len > limit and k != len(inp.split(' ')) - 1:
241
+ current_len = 0
242
+ t_new += ['\n']
243
+
244
+ t_new = ''.join(t_new)
245
+ return t_new
246
+
247
+
248
+ from matplotlib import pyplot as plt
249
+
250
+
251
+ def plot(imgs, *preds, labels=None, scale=1, cmap=plt.cm.magma, aps=None, gt_labels=None, vmax=None):
252
+
253
+ row_off = 0 if labels is None else 1
254
+ _, ax = plt.subplots(len(imgs) + row_off, 1 + len(preds), figsize=(scale * float(1 + 2*len(preds)), scale * float(len(imgs)*2)))
255
+ [a.axis('off') for a in ax.flatten()]
256
+
257
+ if labels is not None:
258
+ for j in range(len(labels)):
259
+ t_new = split_sentence(labels[j], limit=6)
260
+ ax[0, 1+ j].text(0.5, 0.1, t_new, ha='center', fontsize=3+ 10*scale)
261
+
262
+
263
+ for i in range(len(imgs)):
264
+ ax[i + row_off,0].imshow(imgs[i])
265
+ for j in range(len(preds)):
266
+ img = preds[j][i][0].detach().cpu().numpy()
267
+
268
+ if gt_labels is not None and labels[j] == gt_labels[i]:
269
+ print(j, labels[j], gt_labels[i])
270
+ edgecolor = 'red'
271
+ if aps is not None:
272
+ ax[i + row_off, 1 + j].text(30, 70, f'AP: {aps[i]:.3f}', color='red', fontsize=8)
273
+ else:
274
+ edgecolor = 'k'
275
+
276
+ rect = plt.Rectangle([0,0], img.shape[0], img.shape[1], facecolor="none",
277
+ edgecolor=edgecolor, linewidth=3)
278
+ ax[i + row_off,1 + j].add_patch(rect)
279
+
280
+ if vmax is None:
281
+ this_vmax = 1
282
+ elif vmax == 'per_prompt':
283
+ this_vmax = max([preds[j][_i][0].max() for _i in range(len(imgs))])
284
+ elif vmax == 'per_image':
285
+ this_vmax = max([preds[_j][i][0].max() for _j in range(len(preds))])
286
+
287
+ ax[i + row_off,1 + j].imshow(img, vmin=0, vmax=this_vmax, cmap=cmap)
288
+
289
+
290
+ # ax[i,1 + j].imshow(preds[j][i][0].detach().cpu().numpy(), vmin=preds[j].min(), vmax=preds[j].max())
291
+ plt.tight_layout()
292
+ plt.subplots_adjust(wspace=0.05, hspace=0.05)
@@ -0,0 +1,272 @@
1
+ import json
2
+ import inspect
3
+ import torch
4
+ import os
5
+ import sys
6
+ import yaml
7
+ from shutil import copy, copytree
8
+ from os.path import join, dirname, realpath, expanduser, isfile, isdir, basename
9
+
10
+
11
+ class Logger(object):
12
+
13
+ def __getattr__(self, k):
14
+ return print
15
+
16
+ log = Logger()
17
+
18
+ def training_config_from_cli_args():
19
+ experiment_name = sys.argv[1]
20
+ experiment_id = int(sys.argv[2])
21
+
22
+ yaml_config = yaml.load(open(f'experiments/{experiment_name}'), Loader=yaml.SafeLoader)
23
+
24
+ config = yaml_config['configuration']
25
+ config = {**config, **yaml_config['individual_configurations'][experiment_id]}
26
+ config = AttributeDict(config)
27
+ return config
28
+
29
+
30
+ def score_config_from_cli_args():
31
+ experiment_name = sys.argv[1]
32
+ experiment_id = int(sys.argv[2])
33
+
34
+
35
+ yaml_config = yaml.load(open(f'experiments/{experiment_name}'), Loader=yaml.SafeLoader)
36
+
37
+ config = yaml_config['test_configuration_common']
38
+
39
+ if type(yaml_config['test_configuration']) == list:
40
+ test_id = int(sys.argv[3])
41
+ config = {**config, **yaml_config['test_configuration'][test_id]}
42
+ else:
43
+ config = {**config, **yaml_config['test_configuration']}
44
+
45
+ if 'test_configuration' in yaml_config['individual_configurations'][experiment_id]:
46
+ config = {**config, **yaml_config['individual_configurations'][experiment_id]['test_configuration']}
47
+
48
+ train_checkpoint_id = yaml_config['individual_configurations'][experiment_id]['name']
49
+
50
+ config = AttributeDict(config)
51
+ return config, train_checkpoint_id
52
+
53
+
54
+ def get_from_repository(local_name, repo_files, integrity_check=None, repo_dir='~/dataset_repository',
55
+ local_dir='~/datasets'):
56
+ """ copies files from repository to local folder.
57
+
58
+ repo_files: list of filenames or list of tuples [filename, target path]
59
+
60
+ e.g. get_from_repository('MyDataset', [['data/dataset1.tar', 'other/path/ds03.tar'])
61
+ will create a folder 'MyDataset' in local_dir, and extract the content of
62
+ '<repo_dir>/data/dataset1.tar' to <local_dir>/MyDataset/other/path.
63
+ """
64
+
65
+ local_dir = realpath(join(expanduser(local_dir), local_name))
66
+
67
+ dataset_exists = True
68
+
69
+ # check if folder is available
70
+ if not isdir(local_dir):
71
+ dataset_exists = False
72
+
73
+ if integrity_check is not None:
74
+ try:
75
+ integrity_ok = integrity_check(local_dir)
76
+ except BaseException:
77
+ integrity_ok = False
78
+
79
+ if integrity_ok:
80
+ log.hint('Passed custom integrity check')
81
+ else:
82
+ log.hint('Custom integrity check failed')
83
+
84
+ dataset_exists = dataset_exists and integrity_ok
85
+
86
+ if not dataset_exists:
87
+
88
+ repo_dir = realpath(expanduser(repo_dir))
89
+
90
+ for i, filename in enumerate(repo_files):
91
+
92
+ if type(filename) == str:
93
+ origin, target = filename, filename
94
+ archive_target = join(local_dir, basename(origin))
95
+ extract_target = join(local_dir)
96
+ else:
97
+ origin, target = filename
98
+ archive_target = join(local_dir, dirname(target), basename(origin))
99
+ extract_target = join(local_dir, dirname(target))
100
+
101
+ archive_origin = join(repo_dir, origin)
102
+
103
+ log.hint(f'copy: {archive_origin} to {archive_target}')
104
+
105
+ # make sure the path exists
106
+ os.makedirs(dirname(archive_target), exist_ok=True)
107
+
108
+ if os.path.isfile(archive_target):
109
+ # only copy if size differs
110
+ if os.path.getsize(archive_target) != os.path.getsize(archive_origin):
111
+ log.hint(f'file exists but filesize differs: target {os.path.getsize(archive_target)} vs. origin {os.path.getsize(archive_origin)}')
112
+ copy(archive_origin, archive_target)
113
+ else:
114
+ copy(archive_origin, archive_target)
115
+
116
+ extract_archive(archive_target, extract_target, noarchive_ok=True)
117
+
118
+ # concurrent processes might have deleted the file
119
+ if os.path.isfile(archive_target):
120
+ os.remove(archive_target)
121
+
122
+
123
+ def extract_archive(filename, target_folder=None, noarchive_ok=False):
124
+ from subprocess import run, PIPE
125
+
126
+ if filename.endswith('.tgz') or filename.endswith('.tar'):
127
+ command = f'tar -xf {filename}'
128
+ command += f' -C {target_folder}' if target_folder is not None else ''
129
+ elif filename.endswith('.tar.gz'):
130
+ command = f'tar -xzf {filename}'
131
+ command += f' -C {target_folder}' if target_folder is not None else ''
132
+ elif filename.endswith('zip'):
133
+ command = f'unzip {filename}'
134
+ command += f' -d {target_folder}' if target_folder is not None else ''
135
+ else:
136
+ if noarchive_ok:
137
+ return
138
+ else:
139
+ raise ValueError(f'unsuppored file ending of {filename}')
140
+
141
+ log.hint(command)
142
+ result = run(command.split(), stdout=PIPE, stderr=PIPE)
143
+ if result.returncode != 0:
144
+ print(result.stdout, result.stderr)
145
+
146
+
147
+ class AttributeDict(dict):
148
+ """
149
+ An extended dictionary that allows access to elements as atttributes and counts
150
+ these accesses. This way, we know if some attributes were never used.
151
+ """
152
+
153
+ def __init__(self, *args, **kwargs):
154
+ from collections import Counter
155
+ super().__init__(*args, **kwargs)
156
+ self.__dict__['counter'] = Counter()
157
+
158
+ def __getitem__(self, k):
159
+ self.__dict__['counter'][k] += 1
160
+ return super().__getitem__(k)
161
+
162
+ def __getattr__(self, k):
163
+ self.__dict__['counter'][k] += 1
164
+ return super().get(k)
165
+
166
+ def __setattr__(self, k, v):
167
+ return super().__setitem__(k, v)
168
+
169
+ def __delattr__(self, k, v):
170
+ return super().__delitem__(k, v)
171
+
172
+ def unused_keys(self, exceptions=()):
173
+ return [k for k in super().keys() if self.__dict__['counter'][k] == 0 and k not in exceptions]
174
+
175
+ def assume_no_unused_keys(self, exceptions=()):
176
+ if len(self.unused_keys(exceptions=exceptions)) > 0:
177
+ log.warning('Unused keys:', self.unused_keys(exceptions=exceptions))
178
+
179
+
180
+ def get_attribute(name):
181
+ import importlib
182
+
183
+ if name is None:
184
+ raise ValueError('The provided attribute is None')
185
+
186
+ name_split = name.split('.')
187
+ mod = importlib.import_module('.'.join(name_split[:-1]))
188
+ return getattr(mod, name_split[-1])
189
+
190
+
191
+
192
+ def filter_args(input_args, default_args):
193
+
194
+ updated_args = {k: input_args[k] if k in input_args else v for k, v in default_args.items()}
195
+ used_args = {k: v for k, v in input_args.items() if k in default_args}
196
+ unused_args = {k: v for k, v in input_args.items() if k not in default_args}
197
+
198
+ return AttributeDict(updated_args), AttributeDict(used_args), AttributeDict(unused_args)
199
+
200
+
201
+ def load_model(checkpoint_id, weights_file=None, strict=True, model_args='from_config', with_config=False):
202
+
203
+ config = json.load(open(join('logs', checkpoint_id, 'config.json')))
204
+
205
+ if model_args != 'from_config' and type(model_args) != dict:
206
+ raise ValueError('model_args must either be "from_config" or a dictionary of values')
207
+
208
+ model_cls = get_attribute(config['model'])
209
+
210
+ # load model
211
+ if model_args == 'from_config':
212
+ _, model_args, _ = filter_args(config, inspect.signature(model_cls).parameters)
213
+
214
+ model = model_cls(**model_args)
215
+
216
+ if weights_file is None:
217
+ weights_file = realpath(join('logs', checkpoint_id, 'weights.pth'))
218
+ else:
219
+ weights_file = realpath(join('logs', checkpoint_id, weights_file))
220
+
221
+ if isfile(weights_file):
222
+ weights = torch.load(weights_file)
223
+ for _, w in weights.items():
224
+ assert not torch.any(torch.isnan(w)), 'weights contain NaNs'
225
+ model.load_state_dict(weights, strict=strict)
226
+ else:
227
+ raise FileNotFoundError(f'model checkpoint {weights_file} was not found')
228
+
229
+ if with_config:
230
+ return model, config
231
+
232
+ return model
233
+
234
+
235
+ class TrainingLogger(object):
236
+
237
+ def __init__(self, model, log_dir, config=None, *args):
238
+ super().__init__()
239
+ self.model = model
240
+ self.base_path = join(f'logs/{log_dir}') if log_dir is not None else None
241
+
242
+ os.makedirs('logs/', exist_ok=True)
243
+ os.makedirs(self.base_path, exist_ok=True)
244
+
245
+ if config is not None:
246
+ json.dump(config, open(join(self.base_path, 'config.json'), 'w'))
247
+
248
+ def iter(self, i, **kwargs):
249
+ if i % 100 == 0 and 'loss' in kwargs:
250
+ loss = kwargs['loss']
251
+ print(f'iteration {i}: loss {loss:.4f}')
252
+
253
+ def save_weights(self, only_trainable=False, weight_file='weights.pth'):
254
+ if self.model is None:
255
+ raise AttributeError('You need to provide a model reference when initializing TrainingTracker to save weights.')
256
+
257
+ weights_path = join(self.base_path, weight_file)
258
+
259
+ weight_dict = self.model.state_dict()
260
+
261
+ if only_trainable:
262
+ weight_dict = {n: weight_dict[n] for n, p in self.model.named_parameters() if p.requires_grad}
263
+
264
+ torch.save(weight_dict, weights_path)
265
+ log.info(f'Saved weights to {weights_path}')
266
+
267
+ def __enter__(self):
268
+ return self
269
+
270
+ def __exit__(self, type, value, traceback):
271
+ """ automatically stop processes if used in a context manager """
272
+ pass