From 30d79ed5b1e78ee606b168f91899208414193ffb Mon Sep 17 00:00:00 2001 From: Terry Kong Date: Mon, 2 Oct 2023 15:48:31 -0700 Subject: [PATCH 1/6] Adds Imagen to rosetta --- README.md | 1 + rosetta/patchlist-t5x.txt | 1 + rosetta/rosetta/data/multiloader.py | 190 +++++ .../rosetta/projects/diffusion/__init__.py | 2 + .../projects/diffusion/augmentations.py | 74 ++ .../projects/diffusion/common/__init__.py | 0 .../common/generative_metrics/fid_metric.py | 155 +++++ .../common/generative_metrics/inception_v3.py | 318 +++++++++ .../common/generative_metrics/test_fid.py | 91 +++ .../projects/diffusion/common/gin_utils.py | 65 ++ .../diffusion/common/set_gpu_xla_flags.sh | 1 + .../diffusion/configs/adamw_ema_opt.gin | 36 + .../projects/diffusion/data_utils/__init__.py | 0 .../diffusion/data_utils/wds_helper_cli.py | 62 ++ .../rosetta/projects/diffusion/denoisers.py | 383 +++++++++++ rosetta/rosetta/projects/diffusion/losses.py | 139 ++++ .../rosetta/projects/diffusion/mm_utils.py | 555 +++++++++++++++ rosetta/rosetta/projects/diffusion/models.py | 236 +++++++ .../rosetta/projects/diffusion/samplers.py | 339 +++++++++ .../diffusion/tests/augmentations_test.py | 77 +++ .../custom_eval_prompts.tar | Bin 0 -> 20480 bytes .../custom_eval_prompts.txt | 8 + .../make_custom_prompt_wds.py | 16 + .../rosetta/projects/diffusion/wds_utils.py | 551 +++++++++++++++ rosetta/rosetta/projects/imagen/README.md | 138 ++++ rosetta/rosetta/projects/imagen/__init__.py | 0 .../assets/A blue coloured pizza_14.png | Bin 0 -> 139169 bytes ...window. Rain droplets on the window_16.png | Bin 0 -> 131030 bytes ... is standing in front of the portal_20.png | Bin 0 -> 154313 bytes .../projects/imagen/configs/dummy-base.gin | 71 ++ .../imagen/configs/imagen_1024_sample.gin | 78 +++ .../imagen/configs/imagen_256_sample.gin | 219 ++++++ .../imagen/configs/imagen_base_2B.gin | 63 ++ .../configs/imagen_base_2B_img-txt-ds.gin | 8 + .../imagen/configs/imagen_base_500M.gin | 23 + .../imagen/configs/imagen_base_500M_dummy.gin | 8 + .../configs/imagen_base_500M_img-txt-ds.gin | 8 + .../configs/imagen_sr1_efficientunet_600M.gin | 69 ++ ...agen_sr1_efficientunet_600M_img-txt-ds.gin | 9 + .../imagen/configs/imagen_sr1_unet_430M.gin | 71 ++ .../imagen_sr1_unet_430M_img-txt-ds.gin | 9 + .../configs/imagen_sr2_efficientunet_600M.gin | 68 ++ ...agen_sr2_efficientunet_600M_img-txt-ds.gin | 12 + .../imagen/configs/img-txt-ds-base.gin | 79 +++ .../imagen/configs/img-txt-ds-sr1.gin | 93 +++ .../imagen/configs/img-txt-ds-sr2.gin | 101 +++ .../projects/imagen/configs/pretrain.gin | 132 ++++ .../rosetta/projects/imagen/imagen_pipe.py | 323 +++++++++ rosetta/rosetta/projects/imagen/layers.py | 648 ++++++++++++++++++ rosetta/rosetta/projects/imagen/layers_sr.py | 299 ++++++++ rosetta/rosetta/projects/imagen/network.py | 265 +++++++ rosetta/rosetta/projects/imagen/network_sr.py | 251 +++++++ .../scripts/example_slurm_inf_train.sub | 107 +++ .../imagen/scripts/multinode_train.sh | 66 ++ .../imagen/scripts/sample_imagen_1024.sh | 33 + .../imagen/scripts/sample_imagen_256.sh | 31 + .../imagen/scripts/singlenode_inf_train.sh | 59 ++ .../imagen/scripts/singlenode_mp_train.sh | 64 ++ .../scripts/singlenode_mp_train_singlegpu.sh | 60 ++ .../imagen/scripts/specialized_run.py | 80 +++ .../configs/t5_large_server.yml | 38 + .../configs/t5_xxl_server.yml | 39 ++ .../projects/inference_serving/server.py | 330 +++++++++ .../inference_serving/server_utils.py | 33 + .../inference_serving/shared_numpy.py | 141 ++++ .../inference_serving/t5/embed_large.gin | 87 +++ .../inference_serving/t5/embed_t5x.py | 259 +++++++ .../inference_serving/t5/embed_t5x.sh | 45 ++ .../inference_serving/t5/embed_xxl.gin | 87 +++ .../projects/inference_serving/t5/models.py | 176 +++++ .../projects/inference_serving/t5/network.py | 105 +++ rosetta/setup.py | 7 +- 72 files changed, 8191 insertions(+), 1 deletion(-) create mode 100644 rosetta/rosetta/data/multiloader.py create mode 100644 rosetta/rosetta/projects/diffusion/__init__.py create mode 100644 rosetta/rosetta/projects/diffusion/augmentations.py create mode 100644 rosetta/rosetta/projects/diffusion/common/__init__.py create mode 100644 rosetta/rosetta/projects/diffusion/common/generative_metrics/fid_metric.py create mode 100644 rosetta/rosetta/projects/diffusion/common/generative_metrics/inception_v3.py create mode 100644 rosetta/rosetta/projects/diffusion/common/generative_metrics/test_fid.py create mode 100644 rosetta/rosetta/projects/diffusion/common/gin_utils.py create mode 100644 rosetta/rosetta/projects/diffusion/common/set_gpu_xla_flags.sh create mode 100644 rosetta/rosetta/projects/diffusion/configs/adamw_ema_opt.gin create mode 100644 rosetta/rosetta/projects/diffusion/data_utils/__init__.py create mode 100644 rosetta/rosetta/projects/diffusion/data_utils/wds_helper_cli.py create mode 100644 rosetta/rosetta/projects/diffusion/denoisers.py create mode 100644 rosetta/rosetta/projects/diffusion/losses.py create mode 100644 rosetta/rosetta/projects/diffusion/mm_utils.py create mode 100644 rosetta/rosetta/projects/diffusion/models.py create mode 100644 rosetta/rosetta/projects/diffusion/samplers.py create mode 100644 rosetta/rosetta/projects/diffusion/tests/augmentations_test.py create mode 100644 rosetta/rosetta/projects/diffusion/tests/custom_eval_prompts/custom_eval_prompts.tar create mode 100644 rosetta/rosetta/projects/diffusion/tests/custom_eval_prompts/custom_eval_prompts.txt create mode 100644 rosetta/rosetta/projects/diffusion/tests/custom_eval_prompts/make_custom_prompt_wds.py create mode 100644 rosetta/rosetta/projects/diffusion/wds_utils.py create mode 100644 rosetta/rosetta/projects/imagen/README.md create mode 100644 rosetta/rosetta/projects/imagen/__init__.py create mode 100755 rosetta/rosetta/projects/imagen/assets/A blue coloured pizza_14.png create mode 100755 rosetta/rosetta/projects/imagen/assets/A raccoon wearing a hat and black leather jacket is behind the backyard window. Rain droplets on the window_16.png create mode 100755 rosetta/rosetta/projects/imagen/assets/a highly detailed digital painting of a portal in a mystic forest with many beautiful trees. A person is standing in front of the portal_20.png create mode 100644 rosetta/rosetta/projects/imagen/configs/dummy-base.gin create mode 100644 rosetta/rosetta/projects/imagen/configs/imagen_1024_sample.gin create mode 100644 rosetta/rosetta/projects/imagen/configs/imagen_256_sample.gin create mode 100644 rosetta/rosetta/projects/imagen/configs/imagen_base_2B.gin create mode 100644 rosetta/rosetta/projects/imagen/configs/imagen_base_2B_img-txt-ds.gin create mode 100644 rosetta/rosetta/projects/imagen/configs/imagen_base_500M.gin create mode 100644 rosetta/rosetta/projects/imagen/configs/imagen_base_500M_dummy.gin create mode 100644 rosetta/rosetta/projects/imagen/configs/imagen_base_500M_img-txt-ds.gin create mode 100644 rosetta/rosetta/projects/imagen/configs/imagen_sr1_efficientunet_600M.gin create mode 100644 rosetta/rosetta/projects/imagen/configs/imagen_sr1_efficientunet_600M_img-txt-ds.gin create mode 100644 rosetta/rosetta/projects/imagen/configs/imagen_sr1_unet_430M.gin create mode 100644 rosetta/rosetta/projects/imagen/configs/imagen_sr1_unet_430M_img-txt-ds.gin create mode 100644 rosetta/rosetta/projects/imagen/configs/imagen_sr2_efficientunet_600M.gin create mode 100644 rosetta/rosetta/projects/imagen/configs/imagen_sr2_efficientunet_600M_img-txt-ds.gin create mode 100644 rosetta/rosetta/projects/imagen/configs/img-txt-ds-base.gin create mode 100644 rosetta/rosetta/projects/imagen/configs/img-txt-ds-sr1.gin create mode 100644 rosetta/rosetta/projects/imagen/configs/img-txt-ds-sr2.gin create mode 100644 rosetta/rosetta/projects/imagen/configs/pretrain.gin create mode 100644 rosetta/rosetta/projects/imagen/imagen_pipe.py create mode 100644 rosetta/rosetta/projects/imagen/layers.py create mode 100644 rosetta/rosetta/projects/imagen/layers_sr.py create mode 100644 rosetta/rosetta/projects/imagen/network.py create mode 100644 rosetta/rosetta/projects/imagen/network_sr.py create mode 100755 rosetta/rosetta/projects/imagen/scripts/example_slurm_inf_train.sub create mode 100644 rosetta/rosetta/projects/imagen/scripts/multinode_train.sh create mode 100755 rosetta/rosetta/projects/imagen/scripts/sample_imagen_1024.sh create mode 100755 rosetta/rosetta/projects/imagen/scripts/sample_imagen_256.sh create mode 100755 rosetta/rosetta/projects/imagen/scripts/singlenode_inf_train.sh create mode 100755 rosetta/rosetta/projects/imagen/scripts/singlenode_mp_train.sh create mode 100755 rosetta/rosetta/projects/imagen/scripts/singlenode_mp_train_singlegpu.sh create mode 100755 rosetta/rosetta/projects/imagen/scripts/specialized_run.py create mode 100644 rosetta/rosetta/projects/inference_serving/configs/t5_large_server.yml create mode 100644 rosetta/rosetta/projects/inference_serving/configs/t5_xxl_server.yml create mode 100644 rosetta/rosetta/projects/inference_serving/server.py create mode 100644 rosetta/rosetta/projects/inference_serving/server_utils.py create mode 100644 rosetta/rosetta/projects/inference_serving/shared_numpy.py create mode 100644 rosetta/rosetta/projects/inference_serving/t5/embed_large.gin create mode 100644 rosetta/rosetta/projects/inference_serving/t5/embed_t5x.py create mode 100755 rosetta/rosetta/projects/inference_serving/t5/embed_t5x.sh create mode 100644 rosetta/rosetta/projects/inference_serving/t5/embed_xxl.gin create mode 100644 rosetta/rosetta/projects/inference_serving/t5/models.py create mode 100644 rosetta/rosetta/projects/inference_serving/t5/network.py diff --git a/README.md b/README.md index cfde95063..ca41b0cf8 100644 --- a/README.md +++ b/README.md @@ -139,6 +139,7 @@ We currently enable training and evaluation for the following models: | [GPT-3(paxml)](./rosetta/rosetta/projects/pax) | ✔️ | | ✔️ | | [t5(t5x)](./rosetta/rosetta/projects/t5x) | ✔️ | ✔️ | ✔️ | | [ViT](./rosetta/rosetta/projects/vit) | ✔️ | ✔️ | ✔️ | +| [Imagen](./rosetta/rosetta/projects/imagen) | ✔️ | | ✔️ | We will update this table as new models become available, so stay tuned. diff --git a/rosetta/patchlist-t5x.txt b/rosetta/patchlist-t5x.txt index 81c59e6b4..6eee82600 100644 --- a/rosetta/patchlist-t5x.txt +++ b/rosetta/patchlist-t5x.txt @@ -8,3 +8,4 @@ pull/1392/head # https://github.com/google-research/t5x/pull/1392: Add support for partial checkpoint restore pull/1393/head # https://github.com/google-research/t5x/pull/1393: Adds DALI support to t5x pull/1391/head # https://github.com/google-research/t5x/pull/1385: Adds transformer engine support and GPU optimizations to T5x (enables H100) +patch/imagen_support diff --git a/rosetta/rosetta/data/multiloader.py b/rosetta/rosetta/data/multiloader.py new file mode 100644 index 000000000..8d6a02526 --- /dev/null +++ b/rosetta/rosetta/data/multiloader.py @@ -0,0 +1,190 @@ +# +# Copyright (c) 2017-2023 NVIDIA CORPORATION. All rights reserved. +# This file is part of the WebDataset library. +# See the LICENSE file for licensing terms (BSD-style). +# + +"""An alternative to DataLoader using ZMQ. +This implements MultiLoader, an alternative to DataLoader when torch +is not available. Subprocesses communicate with the loader through +ZMQ, provided for high performance multithreaded queueing. +""" + +import multiprocessing as mp +import pickle +import uuid +import weakref +import threading +import queue +import logging + +import zmq +import os +from multiprocessing import Lock + +the_protocol = pickle.HIGHEST_PROTOCOL + +all_pids = weakref.WeakSet() + + +class EOF: + """A class that indicates that a data stream is finished.""" + + def __init__(self, **kw): + """Initialize the class with the kw as instance variables.""" + self.__dict__.update(kw) + +class BufferState(): + def __init__(self, max_size): + self.q = mp.Queue(maxsize=max_size) + + def increment(self): + self.q.put(0) + + def decrement(self): + self.q.get() + + def get_len(self): + return self.q.qsize() + + def reset(self): + while not self.q.empty(): + self.q.get_nowait() + +def async_depickler(out_queue, in_zmq_pipe, stop_signal): + while True: + if stop_signal: + return + data = in_zmq_pipe.recv() + data = pickle.loads(data) + out_queue.put(data) + +def reader(dataset, sockname, index, num_workers, buf_state, signal_state): + """Read samples from the dataset and send them over the socket. + :param dataset: source dataset + :param sockname: name for the socket to send data to + :param index: index for this reader, using to indicate EOF + """ + global the_protocol + os.environ["WORKER"] = str(index) + os.environ["NUM_WORKERS"] = str(num_workers) + ctx = zmq.Context.instance() + sock = ctx.socket(zmq.PUSH) + # sock.set_hwm(prefetch_buffer_len) + sock.connect(sockname) + for sample in dataset: + buf_state.increment() + data = pickle.dumps(sample, protocol=the_protocol) + sock.send(data) + if signal_state.value != 0: + break + sock.send(pickle.dumps(EOF(index=index))) + sock.close() + + +class MultiLoader: + """Alternative to PyTorch DataLoader based on ZMQ.""" + + def __init__( + self, dataset, workers=4, verbose=True, nokill=True, prefix="/tmp/_multi-", prefetch_buf_max=128 + ): + """Create a MultiLoader for a dataset. + This creates ZMQ sockets, spawns `workers` subprocesses, and has them send data + to the socket. + :param dataset: source dataset + :param workers: number of workers + :param verbose: report progress verbosely + :param nokill: don't kill old processes when restarting (allows multiple loaders) + :param prefix: directory prefix for the ZMQ socket + """ + self.dataset = dataset + self.workers = workers + self.orig_workers = workers + self.max_workers = workers * 2 + self.retune_period = 100 + self.verbose = verbose + self.pids = [] + self.socket = None + self.ctx = zmq.Context.instance() + self.nokill = nokill + self.prefix = prefix + # self.prefetch_buf_per_worker = prefetch_buf_per_worker + self.prefetch_buf_max = prefetch_buf_max + self.buf_state = BufferState(prefetch_buf_max) + self.signal_vals = [] + self.buffer_low_mark=int(prefetch_buf_max * .15) + assert self.buffer_low_mark < self.prefetch_buf_max + self.depickled_queue = queue.Queue() + self.async_depickler = None + self.async_depickler_stop_signal = False + self.has_started = False + + def kill(self): + """kill.""" + self.async_depickler_stop_signal = True + self.async_depickler = None + + for pid in self.pids: + if pid is None: + continue + print("killing", pid) + pid.kill() + + for pid in self.pids: + # pid.join(1.0) + if pid is None: + continue + print("joining", pid) + pid.join() + + self.pids = [] + if self.socket is not None: + print("closing", self.socket) + self.socket.close(linger=0) + print("Closed") + self.socket = None + self.buf_state.reset() + + def __iter__(self): + """Return an iterator over this dataloader.""" + if self.has_started: + logging.warning("RESTARTING LOADER") + if not self.nokill: + self.kill() + if not self.has_started or not self.nokill: + self.sockname = "ipc://" + self.prefix + str(uuid.uuid4()) + self.socket = self.ctx.socket(zmq.PULL) + self.socket.set(zmq.LINGER, 0) + self.socket.bind(self.sockname) + if self.verbose: + print("#", self.sockname) + self.pids = [None] * self.max_workers + self.signal_vals = [None] * self.max_workers + for index in range(self.workers): + signal = mp.Value('i', 0) + args = (self.dataset, self.sockname, index, self.workers, self.buf_state, signal) + self.pids[index] = mp.Process(target=reader, args=args) + self.signal_vals[index] = signal + all_pids.update(self.pids[:self.workers]) + for pid in self.pids: + if pid is not None: + pid.start() + + # Async depickler setup + self.async_depickler_stop_signal = False + self.async_depickler = threading.Thread(target=async_depickler, args=(self.depickled_queue, self.socket, self.async_depickler_stop_signal), daemon=True) + self.async_depickler.start() + + self.has_started = True + count = 0 + while self.pids.count(None) < len(self.pids): + sample = self.depickled_queue.get(block=True) + if isinstance(sample, EOF): + if self.verbose: + print("# subprocess finished", sample.index) + self.pids[sample.index].join(1.0) + self.pids[sample.index] = None + else: + self.buf_state.decrement() + yield sample + count += 1 diff --git a/rosetta/rosetta/projects/diffusion/__init__.py b/rosetta/rosetta/projects/diffusion/__init__.py new file mode 100644 index 000000000..2f3a18785 --- /dev/null +++ b/rosetta/rosetta/projects/diffusion/__init__.py @@ -0,0 +1,2 @@ +import tensorflow as tf +tf.config.set_visible_devices([], 'GPU') \ No newline at end of file diff --git a/rosetta/rosetta/projects/diffusion/augmentations.py b/rosetta/rosetta/projects/diffusion/augmentations.py new file mode 100644 index 000000000..2d671d58e --- /dev/null +++ b/rosetta/rosetta/projects/diffusion/augmentations.py @@ -0,0 +1,74 @@ +# Copyright (c) 2022-2023 NVIDIA Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +Sample and conditioning augmentations for diffusion and multimodal +model training +""" + +from typing import Callable, Tuple, Dict, Optional, Union +import typing_extensions + +import jax +import jax.numpy as jnp +import jax.lax as lax + +Augmentable = Union[jnp.ndarray, Dict[str, jnp.ndarray]] + +class AugmentationCallable(typing_extensions.Protocol): + """ Call signature for a sample augmentation function. + Returns the augmented sample and fresh rng """ + def __call__(self, + to_aug: Augmentable, + rng: jax.random.KeyArray + ) -> Tuple[Augmentable, jax.random.KeyArray]: + ... + +def text_conditioning_dropout(to_aug: Augmentable, + rng: jax.random.KeyArray, + dropout_rate: float = 0.1, + drop_key: Optional[str] = None, + null_value = None, + ) -> Tuple[Augmentable, jax.random.KeyArray]: + """ + Can take either a dictionary, where it will dropout on the 'text_mask' key by default, + or drop_key if supplied. If given just an array, it will dropout assuming shape = [b, ...] + (setting to 0, or null_value if supplied) + """ + if drop_key is None: + drop_key = 'text_mask' + if null_value is None: + null_value = 0 + cond = to_aug + if isinstance(to_aug, dict): + cond = to_aug[drop_key] + + my_rng, rng = jax.random.split(rng) + keep_prob = 1 - dropout_rate + + mask_shape = list(cond.shape) + for i in range(1, len(mask_shape)): + mask_shape[i] = 1 + + mask = jax.random.bernoulli(my_rng, p=keep_prob, shape=mask_shape) + mask = jnp.broadcast_to(mask, cond.shape) + + cond = lax.select(mask, cond, null_value * jnp.ones_like(cond)) + + if isinstance(to_aug, dict): + to_aug[drop_key] = cond + else: + to_aug = cond + + return to_aug, rng diff --git a/rosetta/rosetta/projects/diffusion/common/__init__.py b/rosetta/rosetta/projects/diffusion/common/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/rosetta/rosetta/projects/diffusion/common/generative_metrics/fid_metric.py b/rosetta/rosetta/projects/diffusion/common/generative_metrics/fid_metric.py new file mode 100644 index 000000000..b56c4744a --- /dev/null +++ b/rosetta/rosetta/projects/diffusion/common/generative_metrics/fid_metric.py @@ -0,0 +1,155 @@ +# Copyright (c) 2022-2023 NVIDIA Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +This implementation is basing on: https://github.com/mseitzer/pytorch-fid +Which is not the original implementation, but is widely used as the best PyTorch port of the original TF version. +""" +import jax +from rosetta.projects.diffusion.common.generative_metrics.inception_v3 import load_pretrained_inception_v3 +import jax.numpy as jnp +from jax.scipy import linalg +from tqdm import tqdm +import logging +import time + +import numpy as np + + +def get_activations(params, model, batches): + """Calculates the activations of the pool_3 layer for all images. + + Returns: + -- A jax array of dimension (num images, dims) that contains the activations of the batches. + """ + all_outs = [] + for batch in tqdm(batches, desc="Calculating activations"): + if isinstance(batch, tuple) or isinstance(batch, list): + batch = batch[0] + + batch = jnp.array(batch) + + if batch.shape[1] == 3: + batch = batch.transpose(0, 2, 3, 1) + + logging.info("batched") + outs = model(params, batch) + all_outs.append(outs) + logging.info("Completed 1 batch") + time.sleep(1.0) + + all_outs = jnp.concatenate(all_outs, axis=0) + return all_outs + + +def calculate_activation_statistics(params, model, batches, num_pads=0): + """Calculation of the statistics used by the FID. + + Returns: + -- mu : The mean over samples of the activations of the pool_3 layer of the inception model. + -- sigma : The covariance matrix of the activations of the pool_3 layer of the inception model. + """ + act = get_activations(params, model, batches) + act = act[:act.shape[0] - num_pads] + mu = jnp.mean(act, axis=0) + sigma = jnp.cov(act, rowvar=False) + return mu, sigma + + +def calculate_frechet_distance(mu1, sigma1, mu2, sigma2, eps=1e-6): + """Numpy implementation of the Frechet Distance. + The Frechet distance between two multivariate Gaussians X_1 ~ N(mu_1, C_1) + and X_2 ~ N(mu_2, C_2) is d^2 = ||mu_1 - mu_2||^2 + Tr(C_1 + C_2 - 2*sqrt(C_1*C_2)). + + Stable version by Dougal J. Sutherland. + + Params: + -- mu1 : Numpy array containing the activations of a layer of the + inception net (like returned by the function 'get_predictions') + for generated samples. + -- mu2 : The sample mean over activations, precalculated on an + representative data set. + -- sigma1: The covariance matrix over activations for generated samples. + -- sigma2: The covariance matrix over activations, precalculated on an + representative data set. + + Returns: + -- : The Frechet Distance. + """ + + mu1 = jnp.atleast_1d(mu1) + mu2 = jnp.atleast_1d(mu2) + + sigma1 = jnp.atleast_2d(sigma1) + sigma2 = jnp.atleast_2d(sigma2) + + assert mu1.shape == mu2.shape, 'Training and test mean vectors have different lengths' + assert sigma1.shape == sigma2.shape, 'Training and test covariances have different dimensions' + + diff = mu1 - mu2 + + # Product might be almost singular + covmean = linalg.sqrtm(sigma1.dot(sigma2)) + if not jnp.isfinite(covmean).all(): + msg = 'fid calculation produces singular product; adding %s to diagonal of cov estimates' % eps + print(msg) + offset = jnp.eye(sigma1.shape[0]) * eps + covmean = linalg.sqrtm((sigma1 + offset).dot(sigma2 + offset)) + + # Numerical error might give slight imaginary component + if jnp.iscomplexobj(covmean): + if not jnp.allclose(jnp.diagonal(covmean).imag, 0, atol=1e-3): + m = jnp.max(jnp.abs(covmean.imag)) + raise ValueError('Imaginary component {}'.format(m)) + covmean = covmean.real + + tr_covmean = jnp.trace(covmean) + + return diff.dot(diff) + jnp.trace(sigma1) + jnp.trace(sigma2) - 2 * tr_covmean + + +def fid(samples1, samples2, inception_weight_path, inception_batch_size=32): + """Load pretrained Inception and calculate the FID of two set of batches""" + params, inception = load_pretrained_inception_v3(jax_weight_restore_path=inception_weight_path) + + def pad_and_batch_array(array, divisor=inception_batch_size): + remainder = divisor - (array.shape[0] % divisor) + pad_instances = jnp.repeat(array[:1], remainder, axis=0) + num_batches = (array.shape[0] + remainder) // divisor + array = jnp.concatenate((array, pad_instances), axis=0) + return array.reshape((num_batches, divisor, *(array.shape[1:]))), remainder + + def run(params, batch): + return inception.apply( + params, + batch, + resize_input=True, + normalize_input=True, + return_featuremap=True, + ) + + jitted_run = jax.jit(run) + logging.info("Jitted run") + + m1, s1 = calculate_activation_statistics(params, jitted_run, samples1) + m2, s2 = calculate_activation_statistics(params, jitted_run, samples2) + cpu_device = jax.devices("cpu")[0] + m1 = jax.device_put(m1, cpu_device) + s1 = jax.device_put(s1, cpu_device) + m2 = jax.device_put(m2, cpu_device) + s2 = jax.device_put(s2, cpu_device) + + fid_value = calculate_frechet_distance(m1, s1, m2, s2) + jax.debug.print(f'fid_value {fid_value}') + return fid_value \ No newline at end of file diff --git a/rosetta/rosetta/projects/diffusion/common/generative_metrics/inception_v3.py b/rosetta/rosetta/projects/diffusion/common/generative_metrics/inception_v3.py new file mode 100644 index 000000000..df7507ee0 --- /dev/null +++ b/rosetta/rosetta/projects/diffusion/common/generative_metrics/inception_v3.py @@ -0,0 +1,318 @@ +# Copyright (c) 2022-2023 NVIDIA Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Implementation of Inception v3 network in Flax.""" + +import os +import pickle +import io + +import flax.linen as nn +import jax +import jax.numpy as jnp +from jax.experimental import multihost_utils +import torch +from flax.core import FrozenDict +from flax.traverse_util import flatten_dict, unflatten_dict +import requests + + +# DOWNLOADED FROM: +PYT_LINK = "https://github.com/mseitzer/pytorch-fid/releases/download/fid_weights/pt_inception-2015-12-05-6726825d.pth" + +# LOCAL_PATH = "FID/pt_inception-2015-12-05-6726825d.pth" + + +class ConvBlock(nn.Module): + """Convolutional block with batch normalization and ReLU activation.""" + + features: int + kernel_size: int + strides: int = 1 + padding: str = "SAME" + + @nn.compact + def __call__(self, x): + x = nn.Conv(self.features, self.kernel_size, self.strides, self.padding, use_bias=False)(x) + x = nn.BatchNorm(use_running_average=True, momentum=0.9, epsilon=0.001)(x) + return nn.relu(x) + + +class FIDInceptionA(nn.Module): + """InceptionA module.""" + + pool_features: int + + # 255,904 parameters for 288 input channels and 32 pool features. + + @nn.compact + def __call__(self, x): + branch1x1 = ConvBlock(features=64, kernel_size=(1, 1))(x) + + branch3x3dbl = ConvBlock(features=64, kernel_size=(1, 1))(x) + branch3x3dbl = ConvBlock(features=96, kernel_size=(3, 3))(branch3x3dbl) + branch3x3dbl = ConvBlock(features=96, kernel_size=(3, 3))(branch3x3dbl) + + branch5x5 = ConvBlock(features=48, kernel_size=(1, 1))(x) + branch5x5 = ConvBlock(features=64, kernel_size=(5, 5))(branch5x5) + + branch_pool = nn.avg_pool( + x, window_shape=(3, 3), strides=(1, 1), padding="SAME", count_include_pad=False + ) + branch_pool = ConvBlock(features=self.pool_features, kernel_size=(1, 1))(branch_pool) + + return jax.lax.concatenate([branch1x1, branch5x5, branch3x3dbl, branch_pool], dimension=3) + + +class FIDInceptionB(nn.Module): + """InceptionB module.""" + + # 1,153,280 parameters for 288 input channels. + + @nn.compact + def __call__(self, x): + branch3x3 = ConvBlock(features=384, kernel_size=(3, 3), strides=2, padding="VALID")(x) + + branch3x3dbl = ConvBlock(features=64, kernel_size=(1, 1))(x) + branch3x3dbl = ConvBlock(features=96, kernel_size=(3, 3))(branch3x3dbl) + + # No padding here in PyTorch version + branch3x3dbl = ConvBlock(features=96, kernel_size=(3, 3), strides=2, padding="VALID")(branch3x3dbl) + + branch_pool = nn.max_pool(x, window_shape=(3, 3), strides=(2, 2), padding="VALID") + + return jax.lax.concatenate([branch3x3, branch3x3dbl, branch_pool], dimension=3) + + +class FIDInceptionC(nn.Module): + """InceptionC module.""" + + channels7x7: int + + # 1,297,408 parameters for 768 input channels and 128 channels7x7. + + @nn.compact + def __call__(self, x): + branch1x1 = ConvBlock(features=192, kernel_size=(1, 1))(x) + + branch7x7 = ConvBlock(features=self.channels7x7, kernel_size=(1, 1))(x) + branch7x7 = ConvBlock(features=self.channels7x7, kernel_size=(1, 7))(branch7x7) + branch7x7 = ConvBlock(features=192, kernel_size=(7, 1))(branch7x7) + + branch7x7dbl = ConvBlock(features=self.channels7x7, kernel_size=(1, 1))(x) + branch7x7dbl = ConvBlock(features=self.channels7x7, kernel_size=(7, 1))(branch7x7dbl) + branch7x7dbl = ConvBlock(features=self.channels7x7, kernel_size=(1, 7))(branch7x7dbl) + branch7x7dbl = ConvBlock(features=self.channels7x7, kernel_size=(7, 1))(branch7x7dbl) + branch7x7dbl = ConvBlock(features=192, kernel_size=(1, 7))(branch7x7dbl) + + branch_pool = nn.avg_pool( + x, window_shape=(3, 3), strides=(1, 1), padding="SAME", count_include_pad=False + ) + branch_pool = ConvBlock(features=192, kernel_size=(1, 1))(branch_pool) + + return jax.lax.concatenate([branch1x1, branch7x7, branch7x7dbl, branch_pool], dimension=3) + + +class FIDInceptionD(nn.Module): + # 1,698,304 parameters for 768 input channels. + + @nn.compact + def __call__(self, x): + branch3x3 = ConvBlock(features=192, kernel_size=(1, 1))(x) + branch3x3 = ConvBlock(features=320, kernel_size=(3, 3), strides=2, padding="VALID")(branch3x3) + + branch7x7x3 = ConvBlock(features=192, kernel_size=(1, 1))(x) + branch7x7x3 = ConvBlock(features=192, kernel_size=(1, 7))(branch7x7x3) + branch7x7x3 = ConvBlock(features=192, kernel_size=(7, 1))(branch7x7x3) + branch7x7x3 = ConvBlock(features=192, kernel_size=(3, 3), strides=2, padding="VALID")(branch7x7x3) + + branch_pool = nn.max_pool(x, window_shape=(3, 3), strides=(2, 2), padding="VALID") + + return jax.lax.concatenate([branch3x3, branch7x7x3, branch_pool], dimension=3) + + +class FIDInceptionE_1(nn.Module): + # 5,044,608 parameters for 1024 channels + + @nn.compact + def __call__(self, x): + branch1x1 = ConvBlock(features=320, kernel_size=(1, 1))(x) + + branch3x3 = ConvBlock(features=384, kernel_size=(1, 1))(x) + branch3x3_1 = ConvBlock(features=384, kernel_size=(1, 3))(branch3x3) + branch3x3_2 = ConvBlock(features=384, kernel_size=(3, 1))(branch3x3) + branch3x3 = jax.lax.concatenate([branch3x3_1, branch3x3_2], dimension=3) + + branch3x3dbl = ConvBlock(features=448, kernel_size=(1, 1))(x) + branch3x3dbl = ConvBlock(features=384, kernel_size=(3, 3))(branch3x3dbl) + branch3x3dbl_1 = ConvBlock(features=384, kernel_size=(1, 3))(branch3x3dbl) + branch3x3dbl_2 = ConvBlock(features=384, kernel_size=(3, 1))(branch3x3dbl) + branch3x3dbl = jax.lax.concatenate([branch3x3dbl_1, branch3x3dbl_2], dimension=3) + + branch_pool = nn.avg_pool( + x, window_shape=(3, 3), strides=(1, 1), padding="SAME", count_include_pad=False + ) + branch_pool = ConvBlock(features=192, kernel_size=(1, 1))(branch_pool) + + return jax.lax.concatenate([branch1x1, branch3x3, branch3x3dbl, branch_pool], dimension=3) + + +class FIDInceptionE_2(nn.Module): + # 6,076,800 parameters for 2048 channels + + @nn.compact + def __call__(self, x): + branch1x1 = ConvBlock(features=320, kernel_size=(1, 1))(x) + + branch3x3 = ConvBlock(features=384, kernel_size=(1, 1))(x) + branch3x3_1 = ConvBlock(features=384, kernel_size=(1, 3))(branch3x3) + branch3x3_2 = ConvBlock(features=384, kernel_size=(3, 1))(branch3x3) + branch3x3 = jax.lax.concatenate([branch3x3_1, branch3x3_2], dimension=3) + + branch3x3dbl = ConvBlock(features=448, kernel_size=(1, 1))(x) + branch3x3dbl = ConvBlock(features=384, kernel_size=(3, 3))(branch3x3dbl) + branch3x3dbl_1 = ConvBlock(features=384, kernel_size=(1, 3))(branch3x3dbl) + branch3x3dbl_2 = ConvBlock(features=384, kernel_size=(3, 1))(branch3x3dbl) + branch3x3dbl = jax.lax.concatenate([branch3x3dbl_1, branch3x3dbl_2], dimension=3) + + branch_pool = nn.max_pool(x, window_shape=(3, 3), strides=(1, 1), padding="SAME") + branch_pool = ConvBlock(features=192, kernel_size=(1, 1))(branch_pool) + + return jax.lax.concatenate([branch1x1, branch3x3, branch3x3dbl, branch_pool], dimension=3) + + +class FIDInceptionV3(nn.Module): + dropout: float = 0.5 + num_classes: int = 1008 + + @nn.compact + def __call__(self, x, resize_input=False, normalize_input=False, return_featuremap=True): + if resize_input: + x = jax.image.resize(x, shape=(x.shape[0], 299, 299, x.shape[3]), method="bilinear") + + if normalize_input: + x = 2 * x - 1 + + # N x 3 x 299 x 299 + x = ConvBlock(features=32, kernel_size=(3, 3), strides=2, padding="VALID")(x) # N x 32 x 149 x 149 + x = ConvBlock(features=32, kernel_size=(3, 3), padding="VALID")(x) # N x 32 x 147 x 147 + x = ConvBlock(features=64, kernel_size=(3, 3))(x) # N x 64 x 147 x 147 + x = nn.max_pool(x, window_shape=(3, 3), strides=(2, 2), padding="VALID") # N x 64 x 73 x 73 + + x = ConvBlock(features=80, kernel_size=(1, 1))(x) # N x 80 x 73 x 73 + x = ConvBlock(features=192, kernel_size=(3, 3), padding="VALID")(x) # N x 192 x 71 x 71 + x = nn.max_pool(x, window_shape=(3, 3), strides=(2, 2), padding="VALID") # N x 192 x 35 x 35 + + x = FIDInceptionA(32)(x) # N x 256 x 35 x 35 + x = FIDInceptionA(64)(x) # N x 288 x 35 x 35 + x = FIDInceptionA(64)(x) # N x 288 x 35 x 35 + + x = FIDInceptionB()(x) # N x 768 x 17 x 17 + + x = FIDInceptionC(128)(x) # N x 768 x 17 x 17 + x = FIDInceptionC(160)(x) # N x 768 x 17 x 17 + x = FIDInceptionC(160)(x) # N x 768 x 17 x 17 + x = FIDInceptionC(192)(x) # N x 768 x 17 x 17 + + x = FIDInceptionD()(x) # N x 1280 x 8 x 8 + x = FIDInceptionE_1()(x) # N x 2048 x 8 x 8 + x = FIDInceptionE_2()(x) # N x 2048 x 8 x 8 + + x = jnp.mean(x, axis=(1, 2)) # Global pooling: N x 2048 + # THIS x SHOULD BE RETURNED FOR THE FID CALCULATIONS + if return_featuremap: + return x + + x = nn.Dense(features=self.num_classes)(x) # N x 1008 + return x + + +def convert_array(pyt_w): + """Convert array from PyTorch to Jax.""" + arr = jnp.array(pyt_w) + if len(pyt_w.shape) == 4: # Convolution + return jnp.transpose(arr, (2, 3, 1, 0)) + elif len(pyt_w.shape) == 2: # Dense + return jnp.transpose(arr, (1, 0)) + return arr + + +def convert_all(pyt_params, jax_params, verbose=True): + new_jax_params = {} + flat_jax_params = flatten_dict(jax_params, sep=".") + + if verbose: + print("CONVERTING WEIGHTS FROM PYT TO JAX") + print("FOLLOWING CONVERSION WILL BE APPLIED:") + for pyt_key, jax_key in zip(pyt_params, flat_jax_params): + pyt_key = str(pyt_key) + jax_key = str(jax_key) + print(f"{pyt_key.ljust(50)} -> {jax_key.ljust(50)}") + + for (k1, v1), (k2, v2) in zip(pyt_params.items(), flat_jax_params.items()): + new_jax_params[k2] = convert_array(v1) + msg = f"Tried to pass weight of {k1} {v1.shape} to {k2} {v2.shape}!" + assert new_jax_params[k2].shape == v2.shape, msg + + new_jax_params = unflatten_dict(new_jax_params, sep=".") + return FrozenDict(new_jax_params) + + +def load_pretrained_inception_v3(convert_pyt_weights=None, jax_weight_restore_path=None): + network = FIDInceptionV3() + assert convert_pyt_weights is not None or jax_weight_restore_path is not None, "Either pytorch or jax weights must be given" + + if convert_pyt_weights is not None: + rnd_params = network.init(jax.random.PRNGKey(0), jnp.ones((1, 299, 299, 3))) + + pyt_params = torch.load(convert_pyt_weights) + pyt_params_batch_stats = {k: v for k, v in pyt_params.items() if "running" in k} + jax_batch_stats = convert_all(pyt_params_batch_stats, rnd_params["batch_stats"], verbose=True) + pyt_params = { + k: v for k, v in pyt_params.items() if "num_batches_tracked" not in k and "running" not in k + } + + # Every ConvBlock in PyTorch has the following order: + # bn.bias + # bn.weight + # conv.weight + # Meanwhile, in Jax the order is reversed: + # conv.weight + # bn.weight + # bn.bias + # We fix this by reversing PyTorch triplets. + + pyt_keys = list(pyt_params) + pyt_keys_in_groups = [pyt_keys[i: i + 3][::-1] for i in range(0, len(pyt_keys), 3)] + pyt_keys = [key for group in pyt_keys_in_groups for key in group] + pyt_params = {key: pyt_params[key] for key in pyt_keys} + + jax_params = convert_all(pyt_params, rnd_params["params"]) + final_jax_params = FrozenDict(params=jax_params, batch_stats=jax_batch_stats) + elif jax_weight_restore_path is not None: + if not os.path.exists(jax_weight_restore_path): + # If we don't have jax weights on hand, download the pytorch ones and convert + weights = requests.get(PYT_LINK, allow_redirects=True).content + weights_io = io.BytesIO(weights) + params, _ = load_pretrained_inception_v3(convert_pyt_weights=weights_io) + with open(jax_weight_restore_path, 'wb') as f: + pickle.dump(params, f) + + multihost_utils.sync_global_devices("download_inception") + + final_jax_params = pickle.load(open(jax_weight_restore_path, "rb")) + else: + raise NotImplementedError + + return final_jax_params, network diff --git a/rosetta/rosetta/projects/diffusion/common/generative_metrics/test_fid.py b/rosetta/rosetta/projects/diffusion/common/generative_metrics/test_fid.py new file mode 100644 index 000000000..bfe1f008b --- /dev/null +++ b/rosetta/rosetta/projects/diffusion/common/generative_metrics/test_fid.py @@ -0,0 +1,91 @@ +# Copyright (c) 2022-2023 NVIDIA Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import jax +import jax.experimental.maps +import jax.numpy as jnp +import numpy as np + +import torch.utils.data +from torchvision import datasets, transforms + +from torch.utils.data import Dataset + +from jax_multimodal.common.generative_metrics.fid_metric import fid +from jax_multimodal.common.generative_metrics.inception_v3 import load_pretrained_inception_v3 +import sys +import os +import glob +from PIL import Image + +class MyDataset(Dataset): + def __init__(self, root): + self.image_paths = self.get_image_paths(root) + self.transform = transforms.ToTensor()#lambda x: np.array(x) + + def get_image_paths(self,directory): + image_paths = [] + + # Recursive search for all image files + for root, dirs, files in os.walk(directory): + for file in files: + if file.lower().endswith(('.png', '.jpg', '.jpeg', '.gif')): + image_paths.append(os.path.join(root, file)) + + return image_paths + + def __getitem__(self, index): + image_path = self.image_paths[index] + x = Image.open(image_path) + if self.transform is not None: + x = self.transform(x) + if x.shape != (3, 256, 256): + if x.shape[0] == 1: + x = torch.cat([x,x,x], dim=0) + if x.shape[0] == 4: + x = x[:3] + return x + + def __len__(self): + return len(self.image_paths) + +def collate_fn(batch): + return torch.stack(batch) + +TRAIN_ROOT = sys.argv[1] +TEST_ROOT = sys.argv[2] +NUM_SAMPLES = int(sys.argv[3]) + +def load_cifar10(batch_size=128): + """Load CIFAR10 dataset.""" + + train_dataset = MyDataset(TRAIN_ROOT) + test_dataset = MyDataset(TEST_ROOT) + train_dataset = torch.utils.data.Subset(train_dataset, np.arange(NUM_SAMPLES)) + test_dataset = torch.utils.data.Subset(test_dataset, np.arange(NUM_SAMPLES)) + print(len(train_dataset)) + print(len(test_dataset)) + + train_loader = torch.utils.data.DataLoader( + train_dataset, batch_size=batch_size, shuffle=True, num_workers=4, collate_fn=collate_fn, + ) + test_loader = torch.utils.data.DataLoader( + test_dataset, batch_size=batch_size, shuffle=False, num_workers=4, collate_fn=collate_fn, + ) + return train_loader, test_loader + + +train, test = load_cifar10(1024) + +fid(train, test, 'inception_weights', 1) diff --git a/rosetta/rosetta/projects/diffusion/common/gin_utils.py b/rosetta/rosetta/projects/diffusion/common/gin_utils.py new file mode 100644 index 000000000..4601081fe --- /dev/null +++ b/rosetta/rosetta/projects/diffusion/common/gin_utils.py @@ -0,0 +1,65 @@ +# Copyright (c) 2022-2023 NVIDIA Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import functools + + +def call(func, args=None, **keywords): + ''' + Same as partial, except will call the output of partial assuming all args are already specified + ''' + return partial(func=func, args=args, **keywords)() + + +def partial(func, args=None, **keywords): + ''' + Use this in gin-configs if you want to use functools.partial. + + In gin-config, there is no support for positional arguments as bindings. Also, to use functools.partial, + you must pass the function as a positional argument, so this function exists as a workaround. To be + concrete, none of the following would work in a gin file + + ``` + # Case 1 + train/functools.partial: + my_awesome_fn + a_kwarg=3 + + # Case 2 + train/functools.partial: + func=my_awesome_fn + a_kwarg=3 + + # Case 3 + train/functools.partial: + func=my_awesome_fn + {'a_dict': 'as a pos_arg'} + ``` + + This function can support Case 2, and Case 1 needs to be written like Case 2 to work. As for + Case 3, you'd need to write it: + ``` + train/functools.partial: + func=my_awesome_fn + args=[{'a_dict': 'as a pos_arg'}] + + The list is required so that we can unpack it as positional args to functools.partial + ``` + ''' + if args is not None and not isinstance(args, (list, tuple)): + raise TypeError(f'If you specify args, it must be a tuple or list, but you passed {type(args)}: {args}') + + if args is None: + args = [] + return functools.partial(func, *args, **keywords) diff --git a/rosetta/rosetta/projects/diffusion/common/set_gpu_xla_flags.sh b/rosetta/rosetta/projects/diffusion/common/set_gpu_xla_flags.sh new file mode 100644 index 000000000..8eeeaa854 --- /dev/null +++ b/rosetta/rosetta/projects/diffusion/common/set_gpu_xla_flags.sh @@ -0,0 +1 @@ +export XLA_FLAGS="--xla_gpu_enable_latency_hiding_scheduler=false --xla_gpu_enable_async_all_gather=false --xla_gpu_enable_async_reduce_scatter=false --xla_gpu_enable_triton_gemm=false --xla_gpu_cuda_graph_level=0 --xla_gpu_enable_triton_softmax_fusion=false --xla_gpu_enable_async_all_reduce=false ${XLA_FLAGS}" \ No newline at end of file diff --git a/rosetta/rosetta/projects/diffusion/configs/adamw_ema_opt.gin b/rosetta/rosetta/projects/diffusion/configs/adamw_ema_opt.gin new file mode 100644 index 000000000..79da9cb8b --- /dev/null +++ b/rosetta/rosetta/projects/diffusion/configs/adamw_ema_opt.gin @@ -0,0 +1,36 @@ +from __gin__ import dynamic_registration + +import optax +from t5x import optimizers +from rosetta.projects.diffusion.common import gin_utils as gin_utils + +EMA=%gin.REQUIRED + +OPTIMIZER = @optimizers.chain() +optimizers.chain: + transformations = [@optax.clip_by_global_norm(), @adamw/gin_utils.call(), @optimizers.apply_ema_weights()] + +optimizers.apply_ema_weights: + decay = %EMA + debias = False + +optax.clip_by_global_norm: + max_norm = 1.0 + +adamw/optimizers.inject_hyperparams: + inner_factory = @optax.adamw + # If jax.config.x64_enabled, you may want to pass your model parameter dtype to + # avoid inject_hyperparams inferring the dtype, which may be wrong, i.e. float64, + # and that causes the gradient updates to be promoted. + # hyperparam_dtype = @jnp.float32 + +# Same as gin_utils.partial, except it will call the wrapped function as well. +# This is a workaround since in gin-config you cannot do @functools.partial()() +adamw/gin_utils.call: + func = @adamw/optimizers.inject_hyperparams() + #learning_rate = @utils.create_learning_rate_scheduler() + learning_rate = @optax.join_schedules() + weight_decay = 0.01 + b1 = 0.9 + b2 = 0.999 + eps = 1e-8 diff --git a/rosetta/rosetta/projects/diffusion/data_utils/__init__.py b/rosetta/rosetta/projects/diffusion/data_utils/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/rosetta/rosetta/projects/diffusion/data_utils/wds_helper_cli.py b/rosetta/rosetta/projects/diffusion/data_utils/wds_helper_cli.py new file mode 100644 index 000000000..dff53ab41 --- /dev/null +++ b/rosetta/rosetta/projects/diffusion/data_utils/wds_helper_cli.py @@ -0,0 +1,62 @@ +'''Debugging tool: prints out the first element in a Webdataset, print cardinality, and create small subset for testing''' +import argparse +import json +import time + +import jax +import numpy as np +import tqdm +import webdataset as wds +from jax import tree_util + + +def list_of_dict_to_dict_of_list(samples): + outer = tree_util.tree_structure([0 for _ in samples]) + inner = tree_util.tree_structure(samples[0]) + return tree_util.tree_transpose(outer, inner, samples) + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument('urls', type=str, help='Urls for webdataset. Supports braceexpand syntax') + parser.add_argument('--len', default=False, action='store_true') + parser.add_argument('--batch_size', default=1, type=int, help='If provided will call batched(N) on the Webdataset') + parser.add_argument('--first100', default=None, type=str, help='If provided, will make a sample of 100 and write it to the file named here') + + args = parser.parse_args() + + dataset = wds.WebDataset(args.urls).decode('rgb') + if args.first100: + if not args.first100.endswith('.tar'): + raise ValueError(f'--first100={args.first100} should end with .tar') + writer = wds.TarWriter(args.first100) + for i, x in enumerate(tqdm.tqdm(dataset, desc=f'Writing to samples to {args.first100}')): + if i == 100: + break + writer.write(x) + writer.close() + single_elem = next(iter(dataset)) + keys = list(single_elem.keys()) + + if args.batch_size > 1: + dataset = dataset.to_tuple(*keys).batched(args.batch_size, collation_fn=list_of_dict_to_dict_of_list) + + def printer(obj): + if isinstance(obj, (str, bytes, int, float)): + return obj + elif isinstance(obj, np.ndarray): + return f'np.ndarray(shape={obj.shape}, elem[:3]={np.ravel(obj)[:3]})' + else: + raise ValueError(f'Not sure how to print type {type(obj)}: {obj}') + print('== SINGLE EXAMPLE ==') + print(json.dumps(jax.tree_map(printer, single_elem), indent=2)) + # if args.batch_size > 1: + # print(f'== BATCH [N={args.batch_size}] EXAMPLE ==') + # print(json.dumps(jax.tree_map(printer, next(iter(dataset))), indent=2)) + if args.len: + start = time.time() + for i, x in enumerate(tqdm.tqdm(dataset, desc='iterating thru dataset for len')): + pass + elapsed = time.time() - start + print(f'Dataset length: {i+1}') + print(f'example/sec: {args.batch_size*(i+1)/elapsed:.3f}') + print(f'batch/sec: {(i+1)/elapsed:.3f}') diff --git a/rosetta/rosetta/projects/diffusion/denoisers.py b/rosetta/rosetta/projects/diffusion/denoisers.py new file mode 100644 index 000000000..c86ec7de6 --- /dev/null +++ b/rosetta/rosetta/projects/diffusion/denoisers.py @@ -0,0 +1,383 @@ +# Copyright (c) 2022-2023 NVIDIA Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +Diffusion-based denoisers + +This module builds a denoising model that can be used as a black box +for training and sampling with arbitrary methods +""" + +from typing import Mapping, Optional, Tuple, Union, Type +import abc +import typing_extensions + +from flax import linen as nn +from flax.core import scope as flax_scope +import jax +import jax.numpy as jnp + +PyTreeDef = Type[type(jax.tree_util.tree_structure(None))] + +class PrecondSigmaFnCallable(typing_extensions.Protocol): + """ Call signature for sigma-dependant preconditioning function """ + def __call__(self, sigma:jnp.ndarray) -> jnp.ndarray: + ... + +class DenoisingFunctionCallableWithParams(typing_extensions.Protocol): + """ Call signature for a denoising function """ + def __call__(self, + params: PyTreeDef, + noised_sample: jnp.ndarray, + sigma: jnp.ndarray, + other_cond: Optional[Mapping[str, jnp.ndarray]]=None, + dropout_rng: Optional[jax.random.KeyArray]=None + ) -> jnp.ndarray: + ... + +class DenoisingFunctionCallable(typing_extensions.Protocol): + """ Call signature for a denoising function """ + def __call__(self, + noised_sample: jnp.ndarray, + sigma: jnp.ndarray, + other_cond: Optional[Mapping[str, jnp.ndarray]]=None, + dropout_rng: Optional[jax.random.KeyArray]=None + ) -> jnp.ndarray: + ... + +class DenoisingFunctionWithAuxCallable(typing_extensions.Protocol): + """ Call signature for a denoising function """ + def __call__(self, + noised_sample: jnp.ndarray, + sigma: jnp.ndarray, + other_cond: Optional[Mapping[str, jnp.ndarray]]=None, + dropout_rng: Optional[jax.random.KeyArray]=None + ) -> Tuple[jnp.ndarray, Mapping[str, jnp.ndarray]]: + ... + +class NoisePredictorCallable(typing_extensions.Protocol): + """ Call signature for a 'eps' or 'v' noise predicting function """ + def __call__(self, + noised_sample: jnp.ndarray, + sigma: jnp.ndarray, + other_cond: Optional[Mapping[str, jnp.ndarray]]=None, + dropout_rng: Optional[jax.random.KeyArray]=None + ) -> jnp.ndarray: + ... + +class NoisePredictorWithAuxCallable(typing_extensions.Protocol): + """ Call signature for a 'eps' or 'v' noise predicting function """ + def __call__(self, + noised_sample: jnp.ndarray, + sigma: jnp.ndarray, + other_cond: Optional[Mapping[str, jnp.ndarray]]=None, + dropout_rng: Optional[jax.random.KeyArray]=None + ) -> Tuple[jnp.ndarray, Mapping[str, jnp.ndarray]]: + ... + +class Denoiser(abc.ABC): + """ + Model that returns a denoised sample given a noised sample and noise conditioning. + Implements: + $$D_\\theta(x; \\sigma) = c_{skip}(\\sigma)x + + c_{out}(\\sigma)F_\\theta(c_{in}(\\sigma)x; c_{noise}(\\sigma))$$ + from Karras et. al. EDM + """ + def __init__(self, + raw_model: nn.Module, + c_skip_fn: PrecondSigmaFnCallable, + c_out_fn: PrecondSigmaFnCallable, + c_in_fn: PrecondSigmaFnCallable, + c_noise_fn: PrecondSigmaFnCallable): + """ + Args: + raw_model: nn.Module that corresponds to $F_\\theta$ + c_skip_fn, c_out_fn, + c_in_fn, c_noise_fn: Functions of $\\sigma$ that correspond to their terms + in the $D_\\theta$ equation. + """ + self.module = raw_model + self.c_skip_fn = c_skip_fn + self.c_out_fn = c_out_fn + self.c_in_fn = c_in_fn + self.c_noise_fn = c_noise_fn + + @abc.abstractmethod + def apply_module( + self, + params: PyTreeDef, + batch: Mapping[str, jnp.ndarray], + rngs: Optional[jax.random.KeyArray] = None, + mutable: flax_scope.CollectionFilter = False, + other_variables: Optional[PyTreeDef] = None, + ) -> Union[jnp.ndarray, Tuple[jnp.ndarray, flax_scope.FrozenVariableDict]]: + """ Apply raw neural net """ + + def prob_grad(self, params, noised_sample, sigma) -> jnp.ndarray: + """ + Computes the gradient of the probability distribution wrt x: + $ \\nabla_x log(p(x; \\sigma) = (D_\\theta - x) / \\sigma**2 $ + """ + return (self.denoise_sample(params, noised_sample, sigma) - noised_sample) / sigma ** 2 + + def denoise_sample(self, + params: PyTreeDef, + noised_sample: jnp.ndarray, + sigma: jnp.ndarray, + other_cond: Optional[Mapping[str, jnp.ndarray]]=None, + dropout_rng: Optional[jax.random.KeyArray]=None, + flax_mutables: Optional[PyTreeDef]=None, + ) -> jnp.ndarray: + """ Returns denoised sample given noised sample and conditioning """ + sigma = expand_dims_like(sigma, noised_sample) + skip_scale = self.c_skip_fn(sigma) + out_scale = self.c_out_fn(sigma) + in_scale = self.c_in_fn(sigma) + noise_cond = self.c_noise_fn(sigma) + + batch = {'samples': in_scale * noised_sample, 'noise_cond': noise_cond} + if other_cond is not None: + batch.update(other_cond) + + if 'low_res_images' in batch.keys(): + noise_aug_level = batch.get('noise_aug_level', jnp.ones_like(sigma) * 0.002) + low_res_noise_cond = self.c_noise_fn(noise_aug_level) + low_res_in_scale = self.c_in_fn(noise_aug_level) + low_res_batch = {'low_res_images': low_res_in_scale * batch['low_res_images'], 'noise_aug_level': low_res_noise_cond} + batch.update(low_res_batch) + + return skip_scale * noised_sample + \ + out_scale * self.apply_module(params, batch, dropout_rng, other_variables=flax_mutables) + +class EDMUnconditionalDenoiser(Denoiser): + """ Denoiser that implements the training regime from Karras et. al. EDM """ + def __init__(self, + raw_model: nn.Module, + sigma_data: float=0.5): + self.sigma_data = sigma_data + c_skip_fn = lambda sigma: (sigma_data ** 2) / (sigma ** 2 + sigma_data ** 2) + c_out_fn = lambda sigma: (sigma * sigma_data) / jnp.sqrt(sigma_data ** 2 + sigma ** 2) + c_in_fn = lambda sigma: 1.0 / jnp.sqrt(sigma ** 2 + sigma_data ** 2) + c_noise_fn = lambda sigma: 0.25 * jnp.log(sigma) + + super().__init__(raw_model, c_skip_fn, c_out_fn, c_in_fn, c_noise_fn) + + def apply_module( + self, + params: PyTreeDef, + batch: Mapping[str, jnp.ndarray], + rngs: Optional[jax.random.KeyArray] = None, + mutable: flax_scope.CollectionFilter = False, + other_variables: Optional[PyTreeDef] = None, + ) -> Union[jnp.ndarray, Tuple[jnp.ndarray, flax_scope.FrozenVariableDict]]: + """Computes module output via a forward pass of `self.module`.""" + # Dropout is provided only for the training mode. + rngs = {'dropout': rngs} if rngs is not None else None + if other_variables is None: + other_variables = {} + return self.module.apply( + { + 'params': params, + **other_variables + }, + batch['samples'], + batch['noise_cond'], + enable_dropout=rngs is not None, + rngs=rngs, + mutable=mutable) + +class EDMTextConditionedDenoiser(EDMUnconditionalDenoiser): + """ + Denoiser that implements the training regime from Karras et. al. EDM + and accepts text conditioning. + """ + + def apply_module( + self, + params: PyTreeDef, + batch: Mapping[str, jnp.ndarray], + rngs: Optional[jax.random.KeyArray] = None, + mutable: flax_scope.CollectionFilter = False, + other_variables: Optional[PyTreeDef] = None, + ) -> Union[jnp.ndarray, Tuple[jnp.ndarray, flax_scope.FrozenVariableDict]]: + """Computes module output via a forward pass of `self.module`.""" + # Dropout is provided only for the training mode. + rngs = {'dropout': rngs} if rngs is not None else None + if other_variables is None: + other_variables = {} + return self.module.apply( + { + 'params': params, + **other_variables + }, + batch['samples'], + batch['noise_cond'], + batch['text'], + batch['text_mask'], + enable_dropout=rngs is not None, + rngs=rngs, + mutable=mutable) + +class EDMTextConditionedSuperResDenoiser(EDMTextConditionedDenoiser): + """ + Denoiser that implements the training regime from Karras et. al. EDM + and accepts text and lowres conditioning. + """ + + def apply_module( + self, + params: PyTreeDef, + batch: Mapping[str, jnp.ndarray], + rngs: Optional[jax.random.KeyArray] = None, + mutable: flax_scope.CollectionFilter = False, + other_variables: Optional[PyTreeDef] = None, + ) -> Union[jnp.ndarray, Tuple[jnp.ndarray, flax_scope.FrozenVariableDict]]: + """Computes module output via a forward pass of `self.module`.""" + # Dropout is provided only for the training mode. + rngs = {'dropout': rngs} if rngs is not None else None + if other_variables is None: + other_variables = {} + # jax.debug.print('noise aug {n}', n=batch.get('noise_aug_level')) + return self.module.apply( + { + 'params': params, + **other_variables + }, + batch['samples'], + batch['noise_cond'], + text_enc=batch['text'], + text_lens=batch['text_mask'], + low_res_images=batch['low_res_images'], + noise_aug_level=batch.get('noise_aug_level', None), + enable_dropout=rngs is not None, + rngs=rngs, + mutable=mutable) + +class EDMLatentConditionalDenoiser(EDMUnconditionalDenoiser): + """ + Denoiser that implements the EDM training regime and accepts latent-preconditioning + for RIN networks + """ + + def apply_module( + self, + params: PyTreeDef, + batch: Mapping[str, jnp.ndarray], + rngs: Optional[jax.random.KeyArray] = None, + mutable: flax_scope.CollectionFilter = False, + other_variables: Optional[PyTreeDef] = None, + ) -> Union[jnp.ndarray, Tuple[jnp.ndarray, flax_scope.FrozenVariableDict]]: + """Computes module output via a forward pass of `self.module`.""" + # Dropout is provided only for the training mode. + rngs = {'dropout': rngs} if rngs is not None else None + if other_variables is None: + other_variables = {} + return self.module.apply( + { + 'params': params, + **other_variables + }, + batch['samples'], + batch['noise_cond'], + batch['prev_latents'], + enable_dropout=rngs is not None, + rngs=rngs, + mutable=mutable) + +class VP_EPSNoisePredictor(abc.ABC): + """ + Model that returns a denoised sample given a noised sample and noise conditioning. + Implements: + $$pred = c_{out}(\\sigma)F_\\theta(c_{in}(\\sigma)x; c_{noise}(\\sigma))$$ + from Karras et. al. EDM + """ + def __init__(self, + raw_model: nn.Module): + """ + Args: + raw_model: nn.Module that corresponds to $F_\\theta$ + c_noise_fn, c_out_fn, + c_in_fn : Functions of $\\sigma$ that correspond to their terms + in the $pred$ equation. + """ + self.module = raw_model + + @abc.abstractmethod + def apply_module( + self, + params: PyTreeDef, + batch: Mapping[str, jnp.ndarray], + rngs: Optional[jax.random.KeyArray] = None, + mutable: flax_scope.CollectionFilter = False, + other_variables: Optional[PyTreeDef] = None, + ) -> Union[jnp.ndarray, Tuple[jnp.ndarray, flax_scope.FrozenVariableDict]]: + """ Apply raw neural net """ + + def prob_grad(self, params, noised_sample, sigma) -> jnp.ndarray: + """ + Computes the gradient of the probability distribution wrt x: + $ \\nabla_x log(p(x; \\sigma) = (D_\\theta - x) / \\sigma**2 $ + """ + return (self.pred_sample(params, noised_sample, sigma) - noised_sample) / sigma ** 2 + + def pred_sample(self, + params: PyTreeDef, + noised_sample: jnp.ndarray, + sigma: jnp.ndarray, + other_cond: Optional[Mapping[str, jnp.ndarray]]=None, + dropout_rng: Optional[jax.random.KeyArray]=None + ) -> jnp.ndarray: + """ Returns denoised sample given noised sample and conditioning """ + sigma = expand_dims_like(sigma, noised_sample) + + batch = {'samples': noised_sample, 'noise_cond': sigma} + if other_cond is not None: + batch.update(other_cond) + + return self.apply_module(params, batch, dropout_rng) + +class VP_EPSNoisePredictorTextConditional(VP_EPSNoisePredictor): + """ + Denoiser that implements the VP Eps prediction + and accepts text conditioning. + """ + + def apply_module( + self, + params: PyTreeDef, + batch: Mapping[str, jnp.ndarray], + rngs: Optional[jax.random.KeyArray] = None, + mutable: flax_scope.CollectionFilter = False, + other_variables: Optional[PyTreeDef] = None, + ) -> Union[jnp.ndarray, Tuple[jnp.ndarray, flax_scope.FrozenVariableDict]]: + """Computes module output via a forward pass of `self.module`.""" + # Dropout is provided only for the training mode. + rngs = {'dropout': rngs} if rngs is not None else None + if other_variables is None: + other_variables = {} + return self.module.apply( + { + 'params': params, + **other_variables + }, + batch['samples'], + batch['noise_cond'], + batch['text'], + enable_dropout=rngs is not None, + rngs=rngs, + mutable=mutable) + + +def expand_dims_like(target, source): + return jnp.reshape(target, target.shape + (1, ) * (len(source.shape) - len(target.shape))) \ No newline at end of file diff --git a/rosetta/rosetta/projects/diffusion/losses.py b/rosetta/rosetta/projects/diffusion/losses.py new file mode 100644 index 000000000..7ec3b37ec --- /dev/null +++ b/rosetta/rosetta/projects/diffusion/losses.py @@ -0,0 +1,139 @@ +# Copyright (c) 2022-2023 NVIDIA Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +Diffusion training losses + +This module includes loss functions for various diffusion model +training regimes +""" + +from typing import Callable, Tuple, Mapping, Optional, Union +import typing_extensions +import functools + +import jax +import jax.numpy as jnp +import jax.debug +from rosetta.projects.diffusion.augmentations import AugmentationCallable +from rosetta.projects.diffusion.denoisers import DenoisingFunctionCallable, NoisePredictorCallable + +PyTreeDef = type(jax.tree_util.tree_structure(None)) + +class DiffusionLossCallable(typing_extensions.Protocol): + """ Call signature for a diffusion loss function. + Returns the loss and the noises used """ + def __call__(self, + denoise_fn: Callable, + rng: jax.random.KeyArray, + samples: jnp.ndarray, + other_cond: Optional[Mapping[str, jnp.ndarray]] + ) -> Tuple[jnp.ndarray, jnp.ndarray, jnp.ndarray]: + ... + +class EPSDiffusionLossCallable(typing_extensions.Protocol): + """ Call signature for a diffusion loss function based on noise(eps) prediction. + Returns the loss and the noises used """ + def __call__(self, + eps_predictor: Callable, + rng: jax.random.KeyArray, + samples: jnp.ndarray, + other_cond: Optional[Mapping[str, jnp.ndarray]] + ) -> Tuple[jnp.ndarray, jnp.ndarray, jnp.ndarray]: + ... + +class EDMLoss: + """ EDM loss from Karras et. al. 2022""" + def __init__(self, p_mean=-1.2, p_std=1.2, sigma_data=0.5, + sample_aug_fn: Optional[AugmentationCallable]=None, + cond_aug_fn: Optional[AugmentationCallable]=None, + dim_noise_scalar=1.): + self.p_mean = p_mean + self.p_std = p_std + self.sigma_data = sigma_data + self.sample_aug_fn = sample_aug_fn + self.cond_aug_fn = cond_aug_fn + self.dim_noise_scalar = dim_noise_scalar + + def _loss_weight(self, sigma): + sigma = sigma / self.dim_noise_scalar + return (sigma ** 2 + self.sigma_data ** 2) / (sigma * self.sigma_data) ** 2 + + def _noise_sampler(self, rng: jax.random.KeyArray, count: int, dim_scalar:float=1.): + rnd_normal = jax.random.normal(rng, (count, )) + return jnp.exp(rnd_normal * self.p_std + self.p_mean) * dim_scalar + + + + def __call__(self, + denoise_fn: Callable, + rng: jax.random.KeyArray, + samples: jnp.ndarray, + other_cond: Optional[Mapping[str, jnp.ndarray]]=None + ) -> Tuple[jnp.ndarray, jnp.ndarray, jnp.ndarray]: + """ + Returns the EDM loss and the noises used given a denoiser and samples. + Args: + denoise_fn: black-box function that denoises an image given + samples, sigmas, and other conditioning + rng: rng for sampling sigmas, noise, and optionally dropout + samples: array of samples to be diffused + other_cond: arbitrary other conditioning to pass to the + denoiser. Could be text conditioning. + enable_dropout: should use dropout + """ + dropout_rng, sigma_rng, noise_rng = jax.random.split(rng, 3) + + if self.sample_aug_fn: + samples, dropout_rng = self.sample_aug_fn(samples, dropout_rng) + if self.cond_aug_fn: + other_cond, dropout_rng = self.cond_aug_fn(other_cond, dropout_rng) + + batch_dim = samples.shape[0] + sigma = self._noise_sampler(sigma_rng, batch_dim, self.dim_noise_scalar) + sigma = expand_dims_like(sigma, samples) + weight = jnp.reshape(self._loss_weight(sigma), batch_dim) + + noise = jax.random.normal(noise_rng, samples.shape, samples.dtype) + noised_sample = samples + noise * sigma + + denoised = denoise_fn(noised_sample, sigma, other_cond, dropout_rng=dropout_rng) + sq_err = (denoised - samples) ** 2 + loss_unweighted = jnp.mean(jnp.reshape(sq_err, (batch_dim, -1)), axis=-1) + return weight * loss_unweighted, jnp.mean(loss_unweighted), sigma + +class EDMSuperResolutionLoss(EDMLoss): + def __call__(self, + denoise_fn: Callable, + rng: jax.random.KeyArray, + samples: jnp.ndarray, + other_cond: Optional[Mapping[str, jnp.ndarray]]=None + ) -> Tuple[jnp.ndarray, jnp.ndarray, jnp.ndarray]: + lowres_aug_rng, noise_rng, rng = jax.random.split(rng, 3) + + assert other_cond and 'low_res_images' in other_cond.keys(), f'Superresolution loss requires a low_res_image in the other_cond of the sample. One was not found' + lowres = other_cond['low_res_images'] + batch_dim = samples.shape[0] + sigma = self._noise_sampler(lowres_aug_rng, batch_dim, 1.) + sigma = expand_dims_like(sigma, lowres) + + noise = jax.random.normal(noise_rng, lowres.shape, lowres.dtype) + noised_low_res = lowres + noise * sigma + + other_cond = {'low_res_samples': noised_low_res, 'noise_aug_level': sigma, **other_cond} + + return super().__call__(denoise_fn, rng, samples, other_cond) + +def expand_dims_like(target, source): + return jnp.reshape(target, target.shape + (1, ) * (len(source.shape) - len(target.shape))) \ No newline at end of file diff --git a/rosetta/rosetta/projects/diffusion/mm_utils.py b/rosetta/rosetta/projects/diffusion/mm_utils.py new file mode 100644 index 000000000..43f16ca79 --- /dev/null +++ b/rosetta/rosetta/projects/diffusion/mm_utils.py @@ -0,0 +1,555 @@ +# Copyright (c) 2022-2023 NVIDIA Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# Copyright 2022 The T5X Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""General utility functions for diffusion/wds """ +from jax.experimental import multihost_utils +import tensorflow as tf +import typing_extensions +from typing import Any, Callable, Iterable, Mapping, Optional, Sequence, Tuple, Type, Union, List +from flax.linen import partitioning as flax_partitioning +from t5x import partitioning +import jax +import jax.numpy as jnp +import numpy as np +import seqio +import time + +from absl import logging +import dataclasses +import collections +import collections.abc +from concurrent.futures import thread +import contextlib +import dataclasses +import functools +import importlib +import inspect +import os +import re +import time +import typing +from typing import Any, Callable, Iterable, Mapping, Optional, Sequence, Tuple, Type, Union +import warnings +import gc + +from absl import logging +import clu.data +from flax import traverse_util +import flax.core +from flax.core import scope as flax_scope +from flax.linen import partitioning as flax_partitioning +import jax +from jax.experimental import multihost_utils +import jax.numpy as jnp +import numpy as np +import orbax.checkpoint +import seqio +from t5x import checkpoints +from t5x import optimizers +from t5x import partitioning +from t5x import state_utils +from t5x import train_state as train_state_lib +import tensorflow as tf +from tensorflow.io import gfile +import typing_extensions +from rosetta.projects.diffusion import wds_utils +from rosetta.projects.diffusion.denoisers import DenoisingFunctionCallableWithParams, DenoisingFunctionCallable + +Array = Union[np.ndarray, jnp.ndarray, tf.Tensor] +PyTreeDef = type(jax.tree_structure(None)) +PartitionSpec = partitioning.PartitionSpec +DType = Union[np.dtype, type(jnp.bfloat16)] +Shape = Tuple[int, ...] + +# ----------------------------------------------------------------------------- +# SeqIO utility functions. +# ----------------------------------------------------------------------------- + + +def import_module(module: str): + """Imports the given module at runtime.""" + logging.info('Importing %s.', module) + try: + importlib.import_module(module) + except RuntimeError as e: + if (str(e) == + 'Attempted to add a new configurable after the config was locked.'): + raise RuntimeError( + 'Your Task/Mixture module contains gin configurables that must be ' + 'loaded before gin flag parsing. One fix is to add ' + f"'import {module}' in your gin file.") + raise e + +class ShardInfo: + def __init__(self, id, ct): + self.id = id + self.ct = ct + +def get_dataset(cfg: wds_utils.WebDatasetConfig, + shard_id: int, + num_shards: int, + feature_converter_cls: Type[seqio.FeatureConverter], + num_epochs: Optional[int] = None, + continue_from_last_checkpoint: bool = False, should_batch=True) -> tf.data.Dataset: + """Returns a dataset from webdataset based on a `WebDatasetConfig`.""" + if continue_from_last_checkpoint: + raise ValueError( + '`continue_from_last_checkpoint` must be set to False as this is not ' + 'supported by this dataset fn.') + del continue_from_last_checkpoint + + if cfg.batch_size % num_shards: + raise ValueError( + f'Batch size ({cfg.batch_size}) must be divisible by number of ' + f'shards ({num_shards}).') + + if cfg.seed is None: + # Use a shared timestamp across devices as the seed. + seed = multihost_utils.broadcast_one_to_all(np.int32(time.time())) + else: + seed = cfg.seed + + shard_info = ShardInfo(shard_id, num_shards) + return get_dataset_inner(cfg, shard_info, feature_converter_cls, seed, + num_epochs, should_batch) + +def get_dataset_inner(cfg: wds_utils.WebDatasetConfig, + shard_info: ShardInfo, + feature_converter_cls: Callable[..., + seqio.FeatureConverter], + seed: Optional[int] = None, + num_epochs: Optional[int] = None, + should_batch=True): + """Internal fn to load a dataset from WebDataset based on a `WebDatasetConfig`.""" + batch_size = cfg.batch_size // shard_info.ct + if seed is not None: + multihost_utils.assert_equal( + np.array(seed), + f'`seed` is not same across hosts; {jax.process_index} has a seed of ' + f'{seed}') + logging.info( + "Initializing dataset for task '%s' with a replica batch size of %d and " + 'a seed of %d', cfg.mixture_or_task_name, batch_size, seed) + + # should_batch implies that we will add a batch dimension ourselves to the loaded data + print("SHOULD BATCH?", should_batch) + ds, out_shapes, out_types = wds_utils.get_mm_wds_from_urls(cfg, batch_size=batch_size if should_batch else -1) + if should_batch: + for k in out_shapes.keys(): + out_shapes[k] = (batch_size,) + out_shapes[k] + + ds = tf.data.Dataset.from_generator( + generator=ds.__iter__, output_types=out_types, output_shapes=out_shapes + ) + if cfg.samples: + add_fake_length_method(ds, cfg.samples) + return ds + +class GetDatasetCallable(typing_extensions.Protocol): + + def __call__(self, + cfg: wds_utils.WebDatasetConfig, + shard_id: int, + num_shards: int, + feature_converter_cls: Callable[..., seqio.FeatureConverter], + num_epochs: Optional[int] = None, + continue_from_last_checkpoint: bool = True) -> tf.data.Dataset: + ... + +def multihost_assert_equal(input_tree, fail_message: str = ''): + """Verifies that all the hosts have the same tree of values.""" + # Internal mock TPU handling + multihost_utils.assert_equal(input_tree, fail_message) +class InferStepWithRngCallable(typing_extensions.Protocol): + + def __call__(self, + params: Mapping[str, Any], + batch: Mapping[str, jnp.ndarray], + rng: jnp.ndarray = None) -> PyTreeDef: + """Runs an inference step returning a prediction or score.""" + ... + + +class InferStepWithoutRngCallable(typing_extensions.Protocol): + + def __call__(self, params: Mapping[str, Any], + batch: Mapping[str, jnp.ndarray]) -> PyTreeDef: + """Runs an inference step returning a prediction or score.""" + ... + + +InferStepCallable = Union[InferStepWithRngCallable, InferStepWithoutRngCallable] + +# NOTE: We're not more prescriptive than PyTreeDef because that's what +# InferStepCallable expects. +_InferFnResult = Sequence[Tuple[int, PyTreeDef]] +_InferFnWithAuxResult = Tuple[_InferFnResult, Mapping[str, Sequence[Any]]] + + +class InferFnCallable(typing_extensions.Protocol): + + def __call__( + self, + ds: tf.data.Dataset, + train_state: train_state_lib.TrainState, + rng: Optional[jnp.ndarray] = None + ) -> Union[_InferFnResult, _InferFnWithAuxResult]: + """Runs inference on the dataset.""" + ... + + +def _remove_padding(all_inferences, all_indices): + """Remove padded examples. + + Args: + all_inferences: PyTree[total_examples + padding_count, ...]. + all_indices: [total_examples + padding_count]. + + Returns: + all_inferences in shape PyTree[total_examples, ...]. + all_indices in shape [total_exmamples]. + """ + non_pad_idxs = np.where(all_indices >= 0) + all_indices = all_indices[non_pad_idxs] + all_inferences = jax.tree_map(lambda x: x[non_pad_idxs], all_inferences) + return all_inferences, all_indices + + +def get_infer_fn(infer_step: InferStepCallable, batch_size: int, + train_state_axes: train_state_lib.TrainState, + partitioner: partitioning.BasePartitioner, num_samples:Optional[int]=None, + return_batch_keys:Optional[List[str]]=None) -> InferFnCallable: + """Get prediction function for the SeqIO evaluator. + + The returned prediction function should take in an enumerated dataset, make + predictions and return in an enumerated form with the original indices and + examples zipped together. This ensures that the predictions are compared to + the targets in a correct order even if the dataset is sharded across + multiple hosts and gathered in a nondeterministic way. + + jax.process_index == 0 is used as a "main host", i.e., it gathers all + inference results and returns. + + Shape notation: + Per replica set num replicas: R + Per replica set batch size: B + Number of replica sets: H + Length: L + + Some transformations have shape transformation annotation, e.g., + [B, L] -> [R, B/R, L]. + + Args: + infer_step: a callable that executes one prediction step. Should not yet be + partitioned or pmapped. + batch_size: the number of examples in the global infer batch. + train_state_axes: Partitioning info for the train state object. + partitioner: partitioner to use. + + Returns: + predict_fn: a callable which takes in the enumerated infer dataset and an + optimizer and runs the prediction. + """ + + return_batch = return_batch_keys is not None + def infer_step_with_indices(params, batch, rng, indices): + if 'rng' in inspect.signature(infer_step).parameters: + res = typing.cast(InferStepWithRngCallable, infer_step)(params, batch, + rng) + else: + res = typing.cast(InferStepWithoutRngCallable, infer_step)(params, batch) + if return_batch: + return indices, res, batch + else: + return indices, res + + outs = (None, ) * 3 if return_batch else (None, ) * 2 + partitioned_infer_step = partitioner.partition( + infer_step_with_indices, + in_axis_resources=(train_state_axes.params, + partitioner.data_partition_spec, None, + partitioner.data_partition_spec), + out_axis_resources=outs) + + data_layout = partitioner.get_data_layout(batch_size) + shard_id = data_layout.shard_id + num_shards = data_layout.num_shards + + per_shard_batch_size = batch_size // num_shards + num_batches = num_samples // batch_size + + def infer_fn(ds: tf.data.Dataset, + train_state: train_state_lib.TrainState, + rng: Optional[jnp.ndarray] = None): + ds_shapes = jax.tree_map(lambda x: jnp.array(x.shape), ds.element_spec) + ds = ds.enumerate() + multihost_assert_equal( + ds_shapes, 'Dataset element shapes do not agree across hosts. ' + 'This could be an indication that the dataset is nondeterministic.') + try: + original_ds_length = num_samples #len(ds) + dataset_remainder = original_ds_length % batch_size # pytype:disable=wrong-arg-types + logging.info('length of dataset = %s', num_samples)# len(ds)) + except TypeError as e: + if str(e) == 'dataset length is unknown.': + logging.warning( + 'The following error is likely due to the use of TensorFlow v1 in ' + 'your dataset pipeline. Verify you are not importing from ' + '`tf.compat.v1` as part of your pipeline.') + raise e + + if dataset_remainder: + dataset_pad_amt = batch_size - dataset_remainder + logging.info( + 'Padding infer dataset with %d examples for even per-replica shards.', + dataset_pad_amt) + # Pad with the first example using an index of -1 so seqio will ignore. + pad_ds = ds.take(1).map(lambda i, x: (np.int64(-1), x)).repeat( + dataset_pad_amt) + ds = ds.concatenate(pad_ds) + + # Shard the infer dataset across replica sets. + sharded_ds = ds.shard(num_shards, shard_id).batch( + per_shard_batch_size, drop_remainder=True) + # multihost_assert_equal( + # jnp.array(len(sharded_ds)), + # 'Dataset lengths do not agree across hosts.') + + logging.info( + 'The infer dataset is sharded into %d shards with per-shard ' + 'batch size of %d', num_shards, per_shard_batch_size) + + # Run inference for each replica set. + batched_results, all_indices, batched_return = [], [], [] + for batch_idx, (index, infer_batch) in enumerate(sharded_ds.as_numpy_iterator()): + logging.info(str(index)) + if batch_idx >= num_batches: + break + if rng is None: + step_rng = None + else: + step_rng, rng = jax.random.split(rng) + # Run fast inference on batch. + # [B, ...] -> [B * shard_count, ...] + # partitioned_infer_step executes infer_step on sharded batched data, and + # returns de-sharded batched indices and result replicated on all hosts. + + if jax.config.jax_array and jax.process_count() > 1: + logging.info('in array conf. array shape is ' + str(jax.tree_map(lambda x: x.shape, infer_batch))) + inputs = multihost_utils.host_local_array_to_global_array( + (infer_batch, step_rng, index), partitioner.mesh, + (partitioner.data_partition_spec, None, + partitioner.data_partition_spec)) + logging.info('input batch shape' + str(tree_shape(inputs[0]))) + if return_batch: + batch_indices, batch_result, batch_ret = partitioned_infer_step( + train_state.params, *inputs) + logging.info('out batch shape' + str(tree_shape(batch_ret))) + batch_indices, batch_result, batch_ret = multihost_utils.global_array_to_host_local_array( + (batch_indices, batch_result, batch_ret), partitioner.mesh, (None, None, None)) + + else: + batch_indices, batch_result = partitioned_infer_step( + train_state.params, *inputs) + + batch_indices, batch_result = multihost_utils.global_array_to_host_local_array( + (batch_indices, batch_result), partitioner.mesh, (None, None)) + + logging.info('out shape' + str(jax.tree_map(lambda x: x.shape, batch_result))) + logging.info('out idx shape' + str(jax.tree_map(lambda x: x.shape, batch_indices))) + else: + if return_batch: + batch_indices, batch_result, batch_ret = partitioned_infer_step( + train_state.params, infer_batch, step_rng, index) + else: + batch_indices, batch_result = partitioned_infer_step( + train_state.params, infer_batch, step_rng, index) + logging.info('Inference of batch %s done.', index) + + + def _copy_to_host_async(x): + if hasattr(x, 'addressable_data'): + # Array is fully replicated. + x.addressable_data(0).copy_to_host_async() + return x.addressable_data(0) + else: + x.copy_to_host_async() + return x + + try: + logging.info("full result " + str(jax.tree_map(lambda x: x.shape, batch_result))) + batch_result = jax.tree_map(_copy_to_host_async, batch_result) + if return_batch_keys: + if return_batch_keys == True: + ret = batch_ret + else: + ret = {} + for k in return_batch_keys: + ret[k] = batch_ret[k] + batch_return = ret + else: + batch_return = None + batch_indices = jax.tree_map(_copy_to_host_async, batch_indices) + except AttributeError: + # Similar to jax.device_get, we skip transfers for non DeviceArrays. + pass + + logging.info('out idx shape after copy' + str(jax.tree_map(lambda x: x.shape, batch_indices))) + + batched_results.append(batch_result) + if return_batch_keys: + batched_return.append(batch_return) + all_indices.append(batch_indices) + logging.info('returns' + str(tree_shape(batched_return))) + + logging.info('Inference of all batches done.') + all_inferences = batched_results + + # List[B * shard_count, ...] -> [B * shard_count * batch_count, ...] + all_inferences = jax.tree_map(lambda *args: np.concatenate(args), + *all_inferences) + all_indices = np.concatenate(all_indices) + logging.info(str(tree_shape(all_inferences)) + str(tree_shape(all_indices))) + + all_inferences, all_indices = _remove_padding(all_inferences, all_indices) + + # Results are returned from infer_step out of order due to shard operation. + # Note: remove padding first, as -1 indices would mess up this operation. + # Note: all_inferences may be a PyTree, not just an array, e.g. if + # `infer_step` is `model.predict_batch_with_aux`. + logging.info(str(tree_shape(all_inferences)) + str(tree_shape(all_indices))) + if return_batch_keys: + all_batches = jax.tree_map(lambda *args: np.concatenate(args), + *batched_return) + + # aux_values is supposed to be a dictionary that maps strings to a set of + # auxiliary values. + # + # We don't want to flatten/unflatten the aux values. We want to preserve the + # unflattened values with the type List[Mapping[str, Sequence[Any]]]. We do + # this as a memory optimization to avoid lots of redundant keys if we'd + # instead had List[Mapping[str, Any]]. + # + # It has shape Mapping[str, [B * shard_count * batch_count, ...]]. That is, + # the first dimension of each of the values in aux_values is equal to + # len(all_inferences). + aux_values = None + if (isinstance(all_inferences, tuple) and len(all_inferences) == 2 and + isinstance(all_inferences[1], Mapping)): + all_inferences, aux_values = all_inferences + + # Translate to List[...] by flattening inferences making sure to + # preserve structure of individual elements (inferences are not assumed to + # be simple np.array). Finally, zip inferences with corresponding indices + # and convert leaf np.arrays into lists. + if return_batch_keys: + indices_and_outputs = (all_indices, all_inferences, all_batches) + + else: + indices_and_outputs = (all_indices, all_inferences) + + logging.info('final idxes ' + str(all_indices)) + logging.info('final out ' + str(tree_shape(indices_and_outputs))) + if indices_and_outputs[0].shape[0] != original_ds_length: + raise ValueError( + 'Size of indices_and_outputs does not match length of original ' + 'dataset: %d versus %d' % + (indices_and_outputs[0].shape[0], original_ds_length)) + + if aux_values is None: + return indices_and_outputs + else: + aux_values = jax.tree_map(lambda x: np.array(x).tolist(), aux_values) + return indices_and_outputs, aux_values + + return infer_fn + +class DiffusionSamplingEvaluator: + def __init__(self, dataset_cfg, dataset, log_dir=None, fixed_rng=True): + self.dataset_cfg = dataset_cfg + self.dataset = dataset + self.log_dir = log_dir + self.rng = jax.random.PRNGKey(0) + self.keep_random = fixed_rng + self.eval_tasks = [1] #non empty + + def evaluate(self, + compute_metrics: bool, + step: int, + predict_fn: Optional[Callable] = None, + score_fn: Optional[Callable] = None, + predict_with_aux_fn: Optional[Callable] = None, + ): + samples = predict_fn(self.dataset) + + save_dir = '{}/samples/{}_trainsteps'.format(self.log_dir, step) + try: + os.makedirs(save_dir) + except: + pass + + if jax.process_index() == 0: + import matplotlib.image as matimg + logging.info('Saving samples to {}'.format(save_dir)) + for i in range(len(samples)): + np_arr = np.clip(samples[i][1], a_min = 0, a_max = 1) + matimg.imsave(os.path.join(save_dir, '{}-{}.png'.format(jax.process_index(), i)), np_arr) + if len(samples) == 3: + np_arr_batch = (samples[2]['low_res_images'][i] + 1) / 2. + logging.info(str(np_arr_batch)) + np_arr_batch = np.clip(np_arr_batch, a_min = 0, a_max = 1) + matimg.imsave(os.path.join(save_dir, 'dataset-{}-{}.png'.format(jax.process_index(), i)), np_arr_batch) + + multihost_utils.sync_global_devices('eval') + return None, None + +def expand_dims_like(target, source): + return jnp.reshape(target, target.shape + (1, ) * (len(source.shape) - len(target.shape))) + +def tree_shape(tree): + return jax.tree_map(lambda x: x.shape, tree) + +def add_fake_length_method(obj, size): + def length(self): + return size + + Combined = type( + obj.__class__.__name__ + "_Length", + (obj.__class__,), + {"__len__": length}, + ) + obj.__class__ = Combined + return obj + +def _copy_to_host_async(x): + if hasattr(x, 'addressable_data'): + # Array is fully replicated. + x.addressable_data(0).copy_to_host_async() + return x.addressable_data(0) + else: + x.copy_to_host_async() + return x diff --git a/rosetta/rosetta/projects/diffusion/models.py b/rosetta/rosetta/projects/diffusion/models.py new file mode 100644 index 000000000..29d0f32d2 --- /dev/null +++ b/rosetta/rosetta/projects/diffusion/models.py @@ -0,0 +1,236 @@ +# Copyright (c) 2022-2023 NVIDIA Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Diffusion Models. + +This module wraps around networks.py to integrate training, sampling, and construction. +""" + +from typing import Any, Callable, Mapping, MutableMapping, Optional, Tuple, Union, Type + +import abc +import clu.metrics as clu_metrics +from flax import core as flax_core +from flax import linen as nn +from flax.core import scope as flax_scope +from flax.linen import partitioning as flax_partitioning +from flax.training import common_utils +import jax +import jax.numpy as jnp +import numpy as np +from einops import rearrange +from t5x import metrics as metrics_lib +from t5x import optimizers +from t5x.models import BaseModel +import tensorflow as tf +import typing_extensions +import functools + +from rosetta.projects.diffusion import denoisers +from rosetta.projects.diffusion import losses +from rosetta.projects.diffusion import samplers + +Array = Union[np.ndarray, jnp.ndarray, tf.Tensor] +MetricsMap = metrics_lib.MetricsMap +PyTreeDef = Type[type(jax.tree_util.tree_structure(None))] +BatchType = Mapping[str, jnp.ndarray] + +class DiffusionBase(BaseModel): + def __init__(self, + optimizer_def: optimizers.OptimizerDefType): + super().__init__(optimizer_def=optimizer_def) + self.FEATURE_CONVERTER_CLS=None # for t5x trainer compatibility + + def eval_fn(self, + params: PyTreeDef, + batch: BatchType, + ) -> Tuple[jnp.ndarray, MetricsMap]: + return self.loss_fn(params, batch, dropout_rng=None) + + def score_batch(self, + params: PyTreeDef, + batch: BatchType, + return_intermediates: bool = False) -> jnp.ndarray: + raise NotImplementedError("Batch scoring not supported by Diffusion Models") + + +class DenoisingDiffusionModel(DiffusionBase): + """ Wrapper for a denoiser with an arbirary training scheme """ + def __init__(self, + denoiser: denoisers.Denoiser, + diffusion_loss: losses.DiffusionLossCallable, + diffusion_sampler: samplers.DiffusionSampler, + optimizer_def: optimizers.OptimizerDefType, + sampling_cfg: Optional[samplers.SamplingConfig]=None): + self.denoiser = denoiser + self.diffusion_loss = diffusion_loss + self.sampler = diffusion_sampler + self.sampling_cfg = sampling_cfg + super().__init__(optimizer_def=optimizer_def) + + def _compute_metrics(self, + loss: jnp.ndarray, + loss_unweighted: jnp.ndarray, + avg_sigma: jnp.ndarray, + num_examples: int) -> MetricsMap: + return compute_basic_diffusion_metrics(loss, loss_unweighted, avg_sigma, num_examples) + + def _denoise_fn(self, + params: PyTreeDef, + flax_mutables: Optional[PyTreeDef] = None, + ): + return functools.partial(self.denoiser.denoise_sample, params, flax_mutables=flax_mutables) + + def loss_fn(self, + params: PyTreeDef, + batch: BatchType, + dropout_rng: Optional[jax.random.KeyArray], + flax_mutables: Optional[PyTreeDef] = None, + ) -> Tuple[jnp.ndarray, MetricsMap]: + denoise_fn = self._denoise_fn(params, flax_mutables) + + samples = batch['samples'] + other_cond = {k: batch[k] for k in batch if k != 'samples'} + batch_dim = samples.shape[0] + + loss, loss_unweighted, sigma = self.diffusion_loss(denoise_fn, dropout_rng, samples, other_cond) + + loss = jnp.mean(loss) + avg_sigma = jnp.mean(sigma) + return loss, self._compute_metrics(loss, loss_unweighted, avg_sigma, batch_dim) + + def predict_batch(self, + params: PyTreeDef, + batch: BatchType, + rng: Optional[jax.random.KeyArray] = None, + *, + sampling_cfg: Optional[samplers.SamplingConfig] = None, + ) -> jnp.ndarray: + return self.predict_batch_with_aux(params, batch, rng=rng, sampling_cfg=sampling_cfg)[0] + + def predict_batch_with_aux(self, + params: PyTreeDef, + batch: BatchType, + rng: Optional[jax.random.KeyArray] = None, + *, + sampling_cfg: Optional[samplers.SamplingConfig] = None, + ) -> Tuple[jnp.ndarray, Mapping[str, jnp.ndarray]]: + denoise_fn = self._denoise_fn(params) + sampling_cfg = sampling_cfg if sampling_cfg is not None else self.sampling_cfg + + if rng is None: + ValueError("RNG is not optional for diffusion model sampling") + exit() + else: + l_rng, rng = jax.random.split(rng) + + batch_samples=batch['samples'] + other_cond = {k: batch[k] for k in batch if k != 'samples'} + latent = jax.random.normal(l_rng, batch_samples.shape) + + print("Running sampling at resolution: ", batch_samples.shape) + step_idxs = jnp.arange(0, sampling_cfg.num_steps) + samples = self.sampler.sample(denoise_fn, step_idxs, latent, rng, other_cond, + sampling_cfg=sampling_cfg) + return samples, {'None': None} + + def get_initial_variables( + self, + rng: jax.random.KeyArray, + input_shapes: Mapping[str, Array], + input_types: Optional[Mapping[str, jnp.dtype]] = None + ) -> flax_scope.FrozenVariableDict: + """Returns the initial variables of the model.""" + input_types = {} if input_types is None else input_types + sample_shape = input_shapes['samples'] + print("sample shape", sample_shape) + sample_dtype = input_types.get('samples', jnp.float32) + sigma_shape = input_shapes.get('timesteps', (sample_shape[0],)) + + if len(sigma_shape) != 1: + print("BAD SIGMA SHAPE: ", str(sigma_shape), " going to ", sample_shape[0]) + sigma_shape = sample_shape[0] + sigma_dtype = input_types.get('timesteps', jnp.float32) + print("Init Shapes: Sample: ", sample_shape, " Sigma: ", sigma_shape) + + inits = (jnp.ones(sample_shape, sample_dtype), jnp.ones(sigma_shape, sigma_dtype)) + + low_res_type = input_types.get('low_res_images', None) + # jax.debug.print(str(input_shapes)) + + text_enc_dtype = input_types.get('text', None) + if text_enc_dtype is not None: + text_enc_shape = input_shapes.get('text',None) + text_mask_dtype = input_types.get('text_mask', None) + text_mask_shape = input_shapes.get('text_mask', None) + + init_txt = jnp.ones(text_enc_shape, text_enc_dtype) + init_txt_mask = jnp.ones(text_mask_shape, text_mask_dtype) + inits = inits + (init_txt, init_txt_mask) + + if low_res_type is not None: + low_res_shape = input_shapes.get('low_res_images', None) + aug_level_shape = input_shapes.get('noise_aug_level', sigma_shape) + aug_level_type = input_types.get('noise_aug_level', sigma_dtype) + jax.debug.print(str(low_res_shape)) + inits = inits + (jnp.ones(low_res_shape, low_res_type), jnp.ones(aug_level_shape, aug_level_type)) + + initial_variables = self.denoiser.module.init( + rng, + *inits, + enable_dropout=False) + return initial_variables + +def compute_basic_diffusion_metrics( + loss: jnp.ndarray, + loss_unweighted: jnp.ndarray, + avg_sigma: jnp.ndarray, + num_examples: int, +) -> MetricsMap: + """Compute summary metrics. + + Args: + loss: loss (float) + mean_sigma: mean sigma noises used (float) + num_examples (int) number of examples in batch + + Returns: + Dict of metrics. + """ + num_devices = jax.device_count() + assert num_devices, 'JAX is reporting no devices, but it should.' + # Note: apply mask again even though mask has already been applied to loss. + # This is needed to divide by mask sum, but should not affect correctness of + # the numerator. + metrics = { + 'loss': + metrics_lib.AveragePerStep(total=loss), + 'loss_unweighted': + metrics_lib.AveragePerStep(total=loss_unweighted), + 'timing/images_per_second': + metrics_lib.TimeRate.from_model_output(numerator=num_examples), + 'timing/steps_per_second': + metrics_lib.StepsPerTime.from_model_output(), + 'timing/seconds': + metrics_lib.Time(), + 'timing/images': + metrics_lib.Sum(num_examples), + 'timing/images_per_second_per_core': + metrics_lib.TimeRate.from_model_output(numerator=num_examples / + num_devices), + 'diff_stats/avg_sigma': + metrics_lib.AveragePerStep(total=avg_sigma), + + } + return metrics diff --git a/rosetta/rosetta/projects/diffusion/samplers.py b/rosetta/rosetta/projects/diffusion/samplers.py new file mode 100644 index 000000000..d3918e511 --- /dev/null +++ b/rosetta/rosetta/projects/diffusion/samplers.py @@ -0,0 +1,339 @@ +# Copyright (c) 2022-2023 NVIDIA Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +Diffusion/Score Matching Samplers + +This module holds samplers that use black box denoisers +""" + +from typing import Mapping, Optional, Tuple, Callable, Sequence +import typing_extensions +import abc +import dataclasses + +import jax +import jax.numpy as jnp +from rosetta.projects.diffusion.denoisers import DenoisingFunctionCallable +from rosetta.projects.diffusion.mm_utils import expand_dims_like + +PyTreeDef = type(jax.tree_util.tree_structure(None)) +BatchType = Mapping[str, jnp.ndarray] + +@dataclasses.dataclass +class SamplingConfig: + num_steps: int = 50 + generation_shape: Optional[Sequence[int]] = None + +@dataclasses.dataclass +class CFGSamplingConfig(SamplingConfig): + cf_guidance_weight: Optional[float] = None + cf_guidance_nulls: Optional[Mapping[str, Optional[jnp.ndarray]]] = None + +class DiffusionSamplerCallable(typing_extensions.Protocol): + """ Call signature for a diffusion sampling function. + Returns the samples.""" + def __call__(self, + denoise_fn: Callable, + step_indices: jnp.ndarray, + latent: jnp.ndarray, + rng: Optional[jax.random.KeyArray], + other_cond: Optional[Mapping[str, jnp.ndarray]], + sampling_cfg: Optional[SamplingConfig]=None + ) -> jnp.ndarray: + ... + +class DiffusionSampler(abc.ABC): + @abc.abstractmethod + def sample(self, + denoise_fn: Callable, + step_indices: jnp.ndarray, + latent: jnp.ndarray, + rng: Optional[jax.random.KeyArray], + other_cond: Optional[Mapping[str, jnp.ndarray]], + sampling_cfg: Optional[SamplingConfig]=None + ) -> jnp.ndarray: + pass + + def apply_cf_guidance(self, with_cond: jnp.ndarray, no_cond: jnp.ndarray, guidance_weight:float) -> jnp.ndarray: + """ + Applies classifier-free guidance. + + Args: + with_cond: Model output, assumed to have shape [b, ...] + no_cond: Model output, assumed to have shape [b, ...] + guidance_weight: cf guidance weight + """ + diff = with_cond - no_cond + + guided = with_cond + guidance_weight * diff + return guided + + +identity = lambda x:x +class EDMSampler(DiffusionSampler): + """ + Samples using a denoising model as per Karras et. al. EDM Algorithm 2 + """ + def __init__(self, + sigma_min: float = 0.002, + sigma_max: float = 80, + rho: float = 7, + S_churn: float = 0, + S_min: float = 0, + S_max: float = float('inf'), + S_noise: float = 1.0, + round_sigma: Callable = identity, + dim_noise_scalar: float = 1.0, + ): + self.sigma_min = sigma_min + self.sigma_max = sigma_max + self.rho = rho + self.S_churn = S_churn + self.S_min = S_min + self.S_max = S_max + self.S_noise = S_noise + self.round_sigma = round_sigma + self.dim_noise_scalar = dim_noise_scalar + + + def _sample_noise(self, shape: Tuple, rng: jax.random.KeyArray): + return jax.random.normal(rng, shape) * self.S_noise + + def _scannable_single_step(self, denoise_fn, num_steps, t_steps, other_cond, null_cond, second_order_correct=True, cf_guidance_weight=None): + """ Wraps single_step_sample to make it usable in jax.lax.scan """ + def wrapped_fn(x_rng_state: Tuple[jnp.ndarray, jax.random.KeyArray], idx: int): + return self.single_step_sample(denoise_fn, num_steps, t_steps, x_rng_state[1], idx, \ + second_order_correct=second_order_correct, x_curr=x_rng_state[0], \ + other_cond=other_cond, null_cond=null_cond, cf_guidance_weight=cf_guidance_weight) + return wrapped_fn + + def _get_fori_body(self, denoise_fn: DenoisingFunctionCallable, + num_steps: int, + t_steps: jnp.ndarray, + other_cond:Optional[BatchType]=None, + null_cond:Optional[BatchType]=None, + second_order_correct=True, + cf_guidance_weight:Optional[float]=None): + def loop_body(step, args): + x, curr_rng = args + args, _ = self.single_step_sample(denoise_fn, num_steps, t_steps, curr_rng, step, \ + second_order_correct=second_order_correct, x_curr=x, \ + other_cond=other_cond, null_cond=null_cond, cf_guidance_weight=cf_guidance_weight) + return args + + return loop_body + + def _get_eps(self, denoise_fn:DenoisingFunctionCallable, + noised_x, t, + other_cond:Optional[BatchType]=None, + null_cond:Optional[BatchType]=None, + cf_guidance_weight:Optional[float]=None): + #Calculates a potentially CF-guided eps in one forward pass + batch_dim = noised_x.shape[0] + + # Setup concats for cf_guidance + if cf_guidance_weight is not None: + assert null_cond is not None, f"Using CF-guidance {cf_guidance_weight}. \ + You must provide a null_cond if doing classifier-free guidance. \ + It's currently None" + noised_x = jnp.concatenate([noised_x, noised_x], axis=0) + t = jnp.concatenate([t, t], axis=0) + concatenate_fn = lambda x, y: jnp.concatenate([x, y], axis=0) + other_cond = jax.tree_util.tree_map(concatenate_fn, other_cond, null_cond) + + denoised = denoise_fn(noised_sample=noised_x, sigma=t, other_cond=other_cond) + denoised = dynamic_thresholding(denoised) + eps = (noised_x - denoised) / t + + #Apply CF Guidance + if cf_guidance_weight is not None: + eps = self.apply_cf_guidance(eps[:batch_dim], eps[batch_dim:], cf_guidance_weight) + + return eps + + def single_step_sample(self, denoise_fn: DenoisingFunctionCallable, + num_steps: int, + t_steps: jnp.ndarray, + rng: jax.random.KeyArray, + t_idx:int, + x_curr: jnp.ndarray=None, + other_cond:Optional[BatchType]=None, + null_cond:Optional[BatchType]=None, + second_order_correct=True, + cf_guidance_weight:Optional[float]=None + ) -> Tuple[Tuple[jnp.ndarray, jnp.ndarray], jax.random.KeyArray]: + """ Single step of sampling """ + rng, step_rng = jax.random.split(rng) + + t_curr = t_steps[t_idx] + t_next = t_steps[t_idx + 1] + + # Increase noise temporarily + m_gamma = jax.lax.min(self.S_churn / num_steps, jnp.sqrt(2) - 1) + gamma = jax.lax.cond(self.S_min <= t_curr, + lambda:jax.lax.cond(t_curr <= self.S_max, lambda:m_gamma, lambda:0.0), lambda:0.0) + t_hat = self.round_sigma(t_curr + gamma * t_curr) + x_hat = x_curr + jnp.sqrt((t_hat ** 2 - t_curr ** 2)) * \ + self.S_noise * self._sample_noise(x_curr.shape, step_rng) + + # Shape matching + t_hat = batch_expand(t_hat, x_curr) + t_next = batch_expand(t_next, x_curr) + + # Denoising + eps = self._get_eps(denoise_fn, x_hat, t_hat, other_cond, null_cond, cf_guidance_weight) + + # Euler Step + x_next = x_hat + (t_next - t_hat) * eps + + # Second order correction if t_idx < num_steps - 1 + if second_order_correct: + corrected = self.second_order_correct(x_next, denoise_fn, x_hat, t_hat, t_next, eps, other_cond, null_cond, cf_guidance_weight) + else: + corrected = x_next + return (corrected, rng), None #denoised + + def second_order_correct(self, x_next, denoise_fn, x_hat, t_hat, t_next, eps, other_cond, null_cond, cf_guidance_weight=None + ) -> jnp.ndarray: + # Denoising + eps_prime = self._get_eps(denoise_fn, x_next, t_next, other_cond, null_cond, cf_guidance_weight) + + # 2nd order correction + x_next = x_hat + (t_next - t_hat) * (0.5 * eps + 0.5 * eps_prime) + return x_next + + def sample(self, + denoise_fn: Callable, + step_indices: jnp.ndarray, + latent: jnp.ndarray, + rng: jax.random.KeyArray, + other_cond: Optional[BatchType]=None, + sampling_cfg: Optional[SamplingConfig]=None + ) -> jnp.ndarray: + # Classifier-free guidance will be enabled if cf_guidance_weight is not None + if sampling_cfg is None or not hasattr(sampling_cfg, 'cf_guidance_weight'): + cf_guidance_weight = None + cf_guidance_nulls = None + else: + cf_guidance_weight = sampling_cfg.cf_guidance_weight + cf_guidance_nulls = sampling_cfg.cf_guidance_nulls + jax.debug.print("Using CF-Guidance weight {}".format(cf_guidance_weight)) + + # Find timesteps + r_rho = 1 / self.rho + timesteps = (self.sigma_max ** r_rho + step_indices / (step_indices.shape[0] - 1) * \ + (self.sigma_min ** r_rho - self.sigma_max ** r_rho)) ** self.rho + timesteps = self.dim_noise_scalar * timesteps + timesteps = jnp.concatenate((self.round_sigma(timesteps), jnp.zeros_like(timesteps[:1]))) + + # Sampling Loop + null_cond = None + if cf_guidance_weight is not None: + assert other_cond is not None, "other_cond is None. Cannot do cf-guidance without any conditioning" + null_cond = assemble_cf_guidance_conds(other_cond, cf_guidance_nulls) + + prior = latent * timesteps[0] + loop_body = self._get_fori_body(denoise_fn, num_steps=step_indices.shape[0], \ + t_steps=timesteps, other_cond=other_cond, null_cond=null_cond, \ + second_order_correct=True,cf_guidance_weight=cf_guidance_weight) + samples, rng = jax.lax.fori_loop(0, step_indices.shape[0] - 1, loop_body, (prior, rng)) + + # Last step (no second_order_correct) + (samples, _), denoised = self.single_step_sample(denoise_fn, step_indices.shape[0], timesteps, rng, step_indices.shape[0] - 1, other_cond=other_cond, \ + null_cond=null_cond, x_curr=samples, second_order_correct=False, cf_guidance_weight=cf_guidance_weight) + jax.debug.print("single final step") + + return (samples + 1) / 2 + + # A Data Parallel sampling loop that uses a pjitted denoise_fn call + def sample_loop(self, + denoise_fn: Callable, + sampling_cfg: SamplingConfig, + latent: jnp.ndarray, + rng: jax.random.KeyArray, + other_cond: Optional[BatchType]=None, + )-> jnp.ndarray: + # Classifier-free guidance will be enabled if cf_guidance_weight is not None + if not hasattr(sampling_cfg, 'cf_guidance_weight'): + cf_guidance_weight = None + cf_guidance_nulls = None + else: + cf_guidance_weight = sampling_cfg.cf_guidance_weight + cf_guidance_nulls = sampling_cfg.cf_guidance_nulls + jax.debug.print("Using CF-Guidance weight {}".format(cf_guidance_weight)) + + # Find timesteps + step_indices = jnp.arange(sampling_cfg.num_steps) + r_rho = 1 / self.rho + timesteps = (self.sigma_max ** r_rho + step_indices / (step_indices.shape[0] - 1) * \ + (self.sigma_min ** r_rho - self.sigma_max ** r_rho)) ** self.rho + timesteps = jnp.concatenate((self.round_sigma(timesteps), jnp.zeros_like(timesteps[:1]))) + + # Sampling Loop + null_cond = None + if cf_guidance_weight is not None: + assert other_cond is not None, "other_cond is None. Cannot do cf-guidance without any conditioning" + jax.debug.inspect_array_sharding(other_cond, callback=print) + null_cond = assemble_cf_guidance_conds(other_cond, cf_guidance_nulls) + jax.debug.print("Assembed conds") + + prior = jnp.asarray(latent, jnp.float64) * timesteps[0] + for time_idx in range(sampling_cfg.num_steps - 1): + timestep = timesteps[sampling_cfg.num_steps - 1 - time_idx] + step_fn = self._scannable_single_step(denoise_fn, step_indices.shape[0], timesteps, other_cond, null_cond, second_order_correct=True, cf_guidance_weight=cf_guidance_weight) + (samples, rng), denoised = jax.lax.scan(step_fn, (prior, rng), jnp.arange(0, step_indices.shape[0] - 1)) + jax.debug.print("scanned") + + # Last step (no second_order_correct) + (samples, _), denoised = self.single_step_sample(denoise_fn, step_indices.shape[0], timesteps, rng, step_indices.shape[0] - 1, other_cond=other_cond, \ + null_cond=null_cond, x_curr=samples, second_order_correct=False, cf_guidance_weight=cf_guidance_weight) + jax.debug.print("single final step") + + samples = (samples + 1) / 2 + + repl_samples = jax.pjit(lambda x: x, in_shardings=None, out_shardings=None)(samples) + return repl_samples + + + +def assemble_cf_guidance_conds(other_cond: BatchType, + guidance_nulls:Optional[Mapping[str, Optional[jnp.ndarray]]]) -> BatchType: + null_cond = {} + for k, v in other_cond.items(): + if guidance_nulls is None or k in guidance_nulls.keys(): + null_cond_val = None + # If no explicit 'null' is provided, use zeros_like + if guidance_nulls is None or guidance_nulls[k] is None: + null_cond_val = jnp.zeros_like(v) + else: + null_cond_val = guidance_nulls[k] + null_cond[k] = null_cond_val + else: + null_cond[k] = v + + return null_cond + +def dynamic_thresholding(denoised, p=99.5): + s = jnp.percentile( + jnp.abs(denoised), p, + axis=tuple(range(1, denoised.ndim)), + keepdims=True) + s = jnp.max(jnp.concatenate([s, jnp.ones_like(s)]), axis=0) + return jnp.clip(denoised, -s, s) / s + +def batch_expand(scalar: jnp.ndarray, imitate: jnp.ndarray): + """ Match batch dimension and expand rank to match 'imitate' """ + out = scalar * jnp.ones(imitate.shape[0], scalar.dtype) + return expand_dims_like(out, imitate) \ No newline at end of file diff --git a/rosetta/rosetta/projects/diffusion/tests/augmentations_test.py b/rosetta/rosetta/projects/diffusion/tests/augmentations_test.py new file mode 100644 index 000000000..2e6340361 --- /dev/null +++ b/rosetta/rosetta/projects/diffusion/tests/augmentations_test.py @@ -0,0 +1,77 @@ +# Copyright (c) 2022-2023 NVIDIA Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Tests for rosetta.projects.diffusion.augmentations.""" + +from absl.testing import absltest +import jax +import jax.numpy as jnp +import numpy as np +import sys + +class AugTest(absltest.TestCase): + + def test_text_cond_aug_array(self): + in_arr = jnp.ones((3, 4, 2)) + rng_in = jax.random.PRNGKey(0) + masked, rng = augmentations.text_conditioning_dropout(in_arr, rng_in, dropout_rate=0.5) + + output = jnp.ones((3,4,2)) + output = output.at[0, :].set(0) + output = output.at[1, :].set(0) + assert jnp.allclose(output, masked), f'expected: {output}, got: {masked}' + assert (rng_in != rng).all() + assert rng is not None + + def test_text_cond_aug_mapping(self): + in_arr = {'text_mask': jnp.ones((3, 4, 2)), 'another_key':jnp.ones((1, 4, 2))} + rng = jax.random.PRNGKey(0) + masked, rng = augmentations.text_conditioning_dropout(in_arr, rng, dropout_rate=0.5) + + output = jnp.ones((3,4,2)) + output = output.at[0, :].set(0) + output = output.at[1, :].set(0) + masked_arr = masked['text_mask'] + assert jnp.allclose(output, masked_arr), f'expected: {output}, got: {masked_arr}' + masked_ones = masked['another_key'] + assert jnp.allclose(masked_ones, jnp.ones((1, 4, 2))), f'expected ones, got: {masked_ones}' + assert list(masked.keys()) == ['text_mask', 'another_key'], 'modified keys in batch' + + def test_text_cond_aug_array_preserved(self): + in_arr = jnp.ones((3, 4, 2)) + in_arr = in_arr.at[2, 2:, 1:].set(0) + rng = jax.random.PRNGKey(0) + masked, rng = augmentations.text_conditioning_dropout(in_arr, rng, dropout_rate=0.5) + + output = jnp.ones((3,4,2)) + output = output.at[0, :].set(0) + output = output.at[1, :].set(0) + output = output.at[2, 2:, 1:].set(0) + assert jnp.allclose(output, masked) + + def test_text_cond_aug_jit(self): + in_arr = jnp.ones((3, 4, 2)) + in_arr = in_arr.at[2, 2:, 1:].set(0) + rng = jax.random.PRNGKey(0) + masked, rng = jax.jit(augmentations.text_conditioning_dropout)(in_arr, rng, dropout_rate=0.5) + output = jnp.ones((3,4,2)) + output = output.at[0, :].set(0) + output = output.at[1, :].set(0) + output = output.at[2, 2:, 1:].set(0) + assert jnp.allclose(output, masked) + + +if __name__ == '__main__': + sys.path.append('../') + import augmentations + absltest.main() \ No newline at end of file diff --git a/rosetta/rosetta/projects/diffusion/tests/custom_eval_prompts/custom_eval_prompts.tar b/rosetta/rosetta/projects/diffusion/tests/custom_eval_prompts/custom_eval_prompts.tar new file mode 100644 index 0000000000000000000000000000000000000000..1d57efbb7faecfd4b750b2f04d9b118745711646 GIT binary patch literal 20480 zcmeI3+j8145Qe$#Q|uEEpN%gv)86&6udrZY)Yz6sk_)_j*ANmH@N~l9L&7p)2tH`# zU$Vbl?J|FwKYjm&yPpD6p=Z6koa1ZO@##3eciPt+GILzVbDbINdg;wsqqVW9$^6;o zNBQbSP*_=rXE%zI(2M=pnY+Jqujl_uLr_a!;P_|E0`QwAgw5|J&br;-n+EiQCwYsj{7>Q}Vh`YhT7JgE zTl{y&;(z1ppYz`hng2t~Ct|zIHu)Y>VNvEHg;ZwJVh$C`(l+>SyaK3{ZZ=AC51`l^ zD_5{qS{MskX*W<{xre17r$Mgk9IO_?%;5)ALK{_rG(cfidGIEvi8c8bwrSUH#B0k# zTr1nSRA3tX_fIjwJO9c5=lK6}yzafli?8uNaiWm%e{Zd)*j|hO-dOy1z3_tnp3nRr zV!pibUkl9Tf$NoA*;QMX zhv+!lxQhbQ;6KOzcNxqZ8o3_-C!UkU>;T-L=Vv>m#eaV+{yV{C{O?izkJtbEpH29R zZmcK`bU^u$@k`=)MeqK5z!t_-U>f}A_A{M`&8{r}6+}_E4z0q%1)>NYB{F5DH9#{~Z4hG1k{? zbj1H*6m$OnnqFURTZ{kUX#9_Zi}in>>;DFsYJ~gkTatf;+A4zoN)=UYiTtTFRgMQw zfC>Y}Zk1z!R?;B_YF--{WQ6)PG7bK7{NL{=cemaV|3^u}@qf2!UuRE?|It|dcZ2wn z|D69HWRm@x?5Dy$RY^kqO4|LgvX8!~LYAsFpeg77UI*CJ?wOZ8Rr%#olm?JW21Frr z*0>W?;a9Y(`?w-tkiopc$TaxR@&64&cb8K-;{P}fIsbnbfhXQbi~sRh{CAlDqs@g1uCsf@o>?YGQp0l%pTK=ksn(9y1$4YLBQ04u->umY?AE5Hh{0;~WlzzVPe LtN<(UNDBM~?=C(J literal 0 HcmV?d00001 diff --git a/rosetta/rosetta/projects/diffusion/tests/custom_eval_prompts/custom_eval_prompts.txt b/rosetta/rosetta/projects/diffusion/tests/custom_eval_prompts/custom_eval_prompts.txt new file mode 100644 index 000000000..df0caad47 --- /dev/null +++ b/rosetta/rosetta/projects/diffusion/tests/custom_eval_prompts/custom_eval_prompts.txt @@ -0,0 +1,8 @@ +a photograph of an astronaut riding a horse +a highly detailed digital painting of a portal in a mystic forest with many beautiful trees. A person is standing in front of the portal +a highly realistic picture of a dog wearing a green spotted bowtie and black top hat. +a black apple and green backpack +a photorealistic dragon flying over a grassy mountain +drawing of a cartoon computer displaying an exclamation mark +picture of a dystopian city with an ominous red portal in the sky. Aliens are emerging from the portal. +a selfie of a man wearing a blue jacket diff --git a/rosetta/rosetta/projects/diffusion/tests/custom_eval_prompts/make_custom_prompt_wds.py b/rosetta/rosetta/projects/diffusion/tests/custom_eval_prompts/make_custom_prompt_wds.py new file mode 100644 index 000000000..3ce0978cf --- /dev/null +++ b/rosetta/rosetta/projects/diffusion/tests/custom_eval_prompts/make_custom_prompt_wds.py @@ -0,0 +1,16 @@ +import webdataset as wds +import sys + +BASENAME='custom_eval_prompts' +with open(f'{BASENAME}.txt', 'r') as f: + prompts = f.readlines() + +sink = wds.TarWriter(f"{BASENAME}.tar") +for index, line in enumerate(prompts): + if index%1000==0: + print(f"{index:6d}", end="\r", flush=True, file=sys.stderr) + sink.write({ + "__key__": "sample%06d" % index, + "txt": line.strip(), + }) +sink.close() diff --git a/rosetta/rosetta/projects/diffusion/wds_utils.py b/rosetta/rosetta/projects/diffusion/wds_utils.py new file mode 100644 index 000000000..7ad220af8 --- /dev/null +++ b/rosetta/rosetta/projects/diffusion/wds_utils.py @@ -0,0 +1,551 @@ +# Copyright (c) 2022-2023 NVIDIA Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import dataclasses +import functools +import logging +import os +import pickle as pkl +import random +from pathlib import Path +from PIL import Image +import numpy as np +from typing import Any, Callable, Dict, List, Mapping, Optional, Tuple, Union, Iterable + +import jax +from jax import tree_util +import seqio +import t5.data +import tensorflow as tf +import webdataset as wds +from pytriton.client import ModelClient +import rosetta.data.multiloader as multiloader +import braceexpand + +seqio_vocab = t5.data.get_default_vocabulary() +server_list = None + +# ----------------------------------------------------------------------------- +# Configurations +# ----------------------------------------------------------------------------- + +@dataclasses.dataclass +class ModalityConfig: + ftype: Optional[str] + out_type: Tuple[Any] + shape: Tuple[int] + process_func: Optional[Callable] + prefilter_func: Optional[Callable]=None + no_load: bool = False # Don't load modality from webdataset pipeline. Used for modalities that are created from others' process_funcs + +@dataclasses.dataclass +class WebDatasetConfig: + """Configuration for loading a WebDataset""" + mixture_or_task_name: Union[str, Iterable[str]] + batch_size: int + shuffle: bool + seed: Optional[int] + + # Controls total number of samples. Ignored in training + samples: Optional[int] = None + modalities: Optional[Mapping[str, ModalityConfig]] = None #will error if None + batch_proc: Optional[Callable] = None + hostnames_file: Optional[str] = None + num_parallel_processes: int = 16 + pack=False # Webdataset doesn't currently support text packing + +# ----------------------------------------------------------------------------- +# Data processing utils +# ----------------------------------------------------------------------------- + +# ------------------ +# Image +# ------------------ + +def image_crop_scale(image, out_img_shape=(32, 32, 3), nhwc=True): + """ + Resizes image to out_img_shape by first doing an aspect-preserving resize + to match the short side of the image to the out_img_shape, then doing a + random crop. + + Assumes image is ranged [0, 1] + """ + if not nhwc: + # if non nhwc output is desired, out_img_shape should be given in nchw format. + # We transpose it here to make it compatible with processing and transpose later + out_img_shape = (out_img_shape[1], out_img_shape[2], out_img_shape[0]) + curr_img_shape = image.shape + + # square crop randomly with min dimension. + min_dim = min(curr_img_shape[:2]) + + left = random.randint(0, curr_img_shape[1] - min_dim) + right = left + min_dim + top = random.randint(0, curr_img_shape[0] - min_dim) + bottom = top + min_dim + image = image[top:bottom, left:right] + + #resize to final dimensions + image = np.asarray(Image.fromarray((image * 255).astype(np.uint8)).resize(out_img_shape[:2], resample=Image.BILINEAR)) / 255. + + image = image * 2 - 1 # [-1, 1] ranging + if not nhwc: + image = np.transpose(image, (2, 0, 1)) + return image + +def image_crop_scale_with_lowres(image, out_img_shape=(32, 32, 3), low_res_img_shape=(32, 32, 3), nhwc=True): + """ + Resizes image to out_img_shape by first doing an aspect-preserving resize + to match the short side of the image to the out_img_shape, then doing a + random crop. + Further returns a downsized version of this final image for superresolution training + + Assumes image is ranged [0, 1] + """ + if not nhwc: + # if non nhwc output is desired, out_img_shape should be given in nchw format. + # We transpose it here to make it compatible with processing and transpose later + out_img_shape = (out_img_shape[1], out_img_shape[2], out_img_shape[0]) + low_res_img_shape = (low_res_img_shape[1], low_res_img_shape[2], low_res_img_shape[0]) + curr_img_shape = image.shape + + # square crop randomly with min dimension. + min_dim = min(curr_img_shape[:2]) + + left = random.randint(0, curr_img_shape[1] - min_dim) + right = left + min_dim + top = random.randint(0, curr_img_shape[0] - min_dim) + bottom = top + min_dim + image = image[top:bottom, left:right] + + #resize to final dimensions + image = Image.fromarray((image * 255).astype(np.uint8)).resize(out_img_shape[:2], resample=Image.BILINEAR) + image_large = np.asarray(image) / 255. + image_lowres = np.asarray(image.resize(low_res_img_shape[:2], resample=Image.BILINEAR)) / 255. + + image_large = image_large * 2 - 1 # [-1, 1] ranging + image_lowres = image_lowres * 2 - 1 # [-1, 1] ranging + if not nhwc: + image_large = np.transpose(image_large, (2, 0, 1)) + image_lowres = np.transpose(image_lowres, (2, 0, 1)) + return {'samples': image_large, 'low_res_images': image_lowres} + +def image_subcrop_scale_with_lowres(image, init_image_shape=(1024,1024,3), crop_shape=(256,256,3), low_res_img_shape=(64,64,3), nhwc=True): + """ + Does a random crop of an image by first resizing it to a target resolution then doing a random crop. + Further returns a downsized version of this final image for superresolution training + + Assumes image is ranged [0, 1] + """ + + image = image_crop_scale(image, out_img_shape=init_image_shape, nhwc=nhwc) + if not nhwc: + # if non nhwc output is desired, out_img_shape should be given in nchw format. + # We transpose it here to make it compatible with processing and transpose later + out_img_shape = (out_img_shape[1], out_img_shape[2], out_img_shape[0]) + low_res_img_shape = (low_res_img_shape[1], low_res_img_shape[2], low_res_img_shape[0]) + crop_shape = (crop_shape[1], crop_shape[2], crop_shape[0]) + curr_img_shape = image.shape + + # square crop randomly + # min_dim = min(curr_img_shape[:2]) + crop_width = crop_shape[1] + crop_height = crop_shape[0] + + left = random.randint(0, curr_img_shape[1] - crop_width) + right = left + crop_width + top = random.randint(0, curr_img_shape[0] - crop_height) + bottom = top + crop_height + image_large = image[top:bottom, left:right] + + #resize to final dimensions + image = Image.fromarray((image_large * 255).astype(np.uint8))#.resize(out_img_shape[:2], resample=Image.BILINEAR) + image_lowres = np.asarray(image.resize(low_res_img_shape[:2], resample=Image.BILINEAR)) / 255. + + image_large = image_large * 2 - 1 # [-1, 1] ranging + image_lowres = image_lowres * 2 - 1 # [-1, 1] ranging + if not nhwc: + image_large = np.transpose(image_large, (2, 0, 1)) + image_lowres = np.transpose(image_lowres, (2, 0, 1)) + return {'samples': image_large, 'low_res_images': image_lowres} + +def blank_image(out_img_shape=(32, 32, 3), nhwc=True): + """ Dummy image creator. Used for sampling and dummy datasets """ + img = np.zeros(out_img_shape) + if not nhwc: + return np.transpose(img, (2, 0, 1)) + return img + +def filter_lowres(image, min_dims=(64,64,3), nhwc=True): + # returns false for images that don't meet the minimum dimensions specified. + # min_dims should be specified in the same format as images (NHWC or NCHW) + shape = image.shape + assert len(shape) == len(min_dims), f"Minimum dimension spec and image shape length need to match.\ + Given min_dims {min_dims} and image shape {shape}" + if not nhwc: + dims = [1] * len(min_dims) + dims[0] = min_dims[1] + dims[1] = min_dims[2] + dims[2] = min_dims[0] + min_dims = tuple(dims) + + for i in range(len(shape)): + if shape[i] < min_dims[i]: + return False + + return True + +# ------------------ +# Text +# ------------------ + +def triton_textencode(text_batch: List[str]): + """ Encodes a list of python strings into numpy character arrays """ + enc = np.array([[np.char.encode(i, 'utf-8')] for i in text_batch]) + enc = np.reshape(enc, (enc.shape[0], 1)) + return enc + +def seqio_tokenizer(shape=(128,)): + tok_config = {'text': seqio.Feature(vocabulary=t5.data.get_default_vocabulary(), add_eos=True)} + def tok(text): + unpad = seqio.preprocessors.tokenize_impl({'text': text}, tok_config, copy_pretokenized=False, with_eos=True)['text'] + padded = tf.pad(unpad, [[0, shape[0] - unpad.shape[0]]]) + return padded + return tok + +def mscoco_text_process(text_in, shape, vocab=seqio_vocab): + text = text_in['caption'] + + mask = np.zeros(shape[0]) #dummy mask + if text is None or not isinstance(text, str): + text = '' + + return {'text': text, 'text_mask': mask} + +def bare_txt_process(text, shape): + mask = np.ones(shape[0]) #dummy mask + if text is None or not isinstance(text, str): + text = '' + logging.info("WARNING: no text") + return {'text': text, 'text_mask': mask} + +def sd_clip_text_tokenize(string, tokenizer): + tok = np.array(tokenizer(string, max_length=tokenizer.model_max_length, padding="max_length", truncation=True).input_ids) + return {'input_ids': tok} + +def cls_process(cls): + return {'cls': cls} + +# ------------------ +# External inference +# ------------------ + +def dummy_batch_infer(batch, server_list=None, text_emb_shape:Tuple=(256, 4096), model_name:str='t5_xxl'): + IS_BATCHED = True + if not isinstance(batch['text'], list): + batch_dim = 1 + IS_BATCHED = False + else: + batch_dim = len(batch['text']) + + # construct text masks and padding + mask = np.zeros((batch_dim, text_emb_shape[0])).astype('int32') + rand_idxs = np.random.randint(text_emb_shape[0], size=batch_dim) + padded_embeds = np.random.normal(size=(batch_dim, *text_emb_shape)).astype('float32') + for i in range(rand_idxs.shape[0]): + mask[i, :rand_idxs[i]] = 1 + padded_embeds[i, rand_idxs[i]:] = 0 + + if not IS_BATCHED: + padded_embeds = padded_embeds[0] + mask = mask[0] + + batch['text'] = padded_embeds + batch['text_mask'] = mask + + return batch + + +def batch_infer_extern(batch, server_list=None, text_emb_shape:Tuple=(128, 4096), model_name:str='t5_xxl'): + """ + Calls a remote PyTriton server to get logits from a text transformer. + This is done batched for efficiency + """ + if server_list is None: + raise ValueError("server_list is required") + + text_batch = batch['text'] + IS_BATCHED=True + + full_text = text_batch + if not isinstance(full_text, list): + IS_BATCHED=False + full_text = [full_text] + + # encoding text batch for triton inference + encoded_batch = triton_textencode(full_text) + + recieved_out = False + try_ctr = 0 + + # keep trying until successful inference + padded_embeds = None + mask = None + while not recieved_out or padded_embeds is None: + rand_server = server_list[random.randint(0, len(server_list) - 1)] + with ModelClient(rand_server, model_name=model_name) as client: + try: + text_emb_dict = client.infer_batch(encoded_batch) + except: + text_emb_dict = None + + if text_emb_dict: + #embeds = [pkl.loads(inst) for inst in text_emb_dict['encodings']] + seqlens = text_emb_dict['encodings_seqlens'] + batch_dim = seqlens.shape[0] + padded_embeds = text_emb_dict['encodings_padded'] + # padding embeds up to full seqlen + if padded_embeds.shape[1] < text_emb_shape[0]: + embeds = np.zeros((batch_dim, *text_emb_shape), dtype='float16') + embeds[:, :padded_embeds.shape[1]] = padded_embeds + padded_embeds = embeds + mask = np.zeros((batch_dim, text_emb_shape[0])).astype('int32') + for idx in range(batch_dim): + mask[idx, :seqlens[idx]] = 1 + + if padded_embeds is None: + try_ctr += 1 + sam = text_batch[0] + logging.info(f"Server returned nothing. Retrying. Attempt {try_ctr}. {sam}") + else: + recieved_out = True + + assert padded_embeds is not None and mask is not None + if padded_embeds.shape[0] != len(full_text): + logging.info("WARNING: somehow, embeds and full_text don't match!") + + # construct text masks and padding + if not IS_BATCHED: + padded_embeds = padded_embeds[0] + mask = mask[0] + + batch['text'] = padded_embeds + batch['text_mask'] = mask + + return batch + +# ----------------------------------------------------------------------------- +# WebDataset construction and setup +# ----------------------------------------------------------------------------- + +def type_proc(dtype:str): + if dtype == 'float32': + return np.float32 + elif dtype == 'int': + return np.int32 + elif dtype == 'float16': + return np.float16 + elif dtype == 'bfloat16': + return jax.numpy.bfloat16 + else: + raise ValueError("Could not parse dtype: %s" % dtype) + +def run_preproc(sample:Any, keys:List[str]=[], modalities: Mapping[str, ModalityConfig]={}): + datapoint = {} + + non_loaded_ctr = 0 + for i in range(len(keys)): + k = keys[i] + process_fn = modalities[k].process_func + if modalities[k].no_load: + non_loaded_ctr += 1 + elif process_fn is not None and modalities[k].ftype is None: # for generating a fixed shape dummy sample + mod_out = process_fn() + unpack_assign_or_assign(key=k, value=mod_out, dictionary=datapoint) + non_loaded_ctr += 1 + else: + mod_out = process_fn(sample[i - non_loaded_ctr]) if process_fn is not None else sample[i - non_loaded_ctr] + unpack_assign_or_assign(key=k, value=mod_out, dictionary=datapoint) + + return datapoint + +def run_prefilter(sample:Any, keys:List[str]=[], modalities: Mapping[str, ModalityConfig]={}): + datapoint = {} + + non_loaded_ctr = 0 + for i in range(len(keys)): + k = keys[i] + prefilter_fn = modalities[k].prefilter_func + if modalities[k].no_load: + non_loaded_ctr += 1 + elif prefilter_fn is not None: + if not prefilter_fn(sample[i - non_loaded_ctr]): + return False + return True + +def unpack_assign_or_assign(key: Any, value: Any, dictionary: Dict, strict: bool = True): + '''Tries to unpack value ignoring key, if value is a dictionary; or assigns value to key as fallback''' + if isinstance(value, Mapping): + for nested_key, nested_value in value.items(): + if strict and nested_key in dictionary: + raise ValueError(f'strict=True, and key={nested_key} already in dictionary={dictionary}') + dictionary[nested_key] = nested_value + else: + if strict and key in dictionary: + raise ValueError(f'strict=True, and key={key} already in dictionary={dictionary}') + dictionary[key] = value + +def dict_batch(samples): + """ batches elements of the same key """ + outer = tree_util.tree_structure([0 for _ in samples]) + inner = tree_util.tree_structure(samples[0]) + return tree_util.tree_transpose(outer, inner, samples) + +def _simple_map(data, f, handler=wds.filters.reraise_exception): + """Map samples.""" + for sample in data: + try: + result = f(sample) + except Exception as exn: + if handler(exn): + continue + else: + break + if result is None: + continue + yield result + +simple_map = wds.filters.pipelinefilter(_simple_map) + +def url_process(url_str:Union[str, Iterable[str]]) -> Union[List[str], str]: + """ + If url_str is a directory, this function will return a list of + all .tar files found recursively in it. + If url_str is an iterable, expands all urls contained as directories + or braceexpands and concatenates them together + """ + logging.info(f'Processing URLS for {url_str}') + if isinstance(url_str, str): + url_str = [url_str] + paths = [] + for url in url_str: + if os.path.isdir(url): + url_paths = [str(p) for p in Path(url).rglob('*.tar')] + logging.info("{} tarfiles found in {}".format(len(url_paths), url)) + paths += url_paths + else: + logging.info(f'{url} doesn\'t seem to be a directory. Treating it as a path with braceexpand') + paths += list(braceexpand.braceexpand(url)) + return paths + +def get_mm_wds_from_urls(cfg: WebDatasetConfig, batch_size:int =-1) -> Tuple[Any, Mapping[str, Tuple[int]], Mapping[str, Any]]: + global server_list + + # Getting all urls + urls = url_process(cfg.mixture_or_task_name) + + # Setting up modalities (shapes and types) + modalities = cfg.modalities + assert modalities is not None, "Modalities cannot be None. Don't know how to process data!" + keys = list(modalities.keys()) + in_ftypes = [] + out_shapes = {} + out_types = {} + for k in keys: + m = modalities[k] + if m.ftype is not None: + in_ftypes.append(m.ftype) + unpack_assign_or_assign(key=k, value=m.shape, dictionary=out_shapes) + unpack_assign_or_assign(key=k, value=jax.tree_map(type_proc, m.out_type), dictionary=out_types) + + # Inference Server determination + if server_list is None: + if cfg.hostnames_file not in (None, "", "None"): + with open(cfg.hostnames_file, 'r') as f: + server_list = f.readlines() + for i in range(len(server_list)): + server_list[i] = server_list[i].strip() + else: + logging.info("No hostnames file. Will not initialize remote inferencing") + server_list = None + else: + logging.info('SERVER LIST GIVEN. Not reading from cfg.hostnames_file') + logging.info(server_list) + + preprocessor = functools.partial(run_preproc, keys=keys, modalities=modalities) + pre_filter = functools.partial(run_prefilter, keys=keys, modalities=modalities) + dataset = wds.WebDataset(urls, resampled=True).shuffle(0).decode("rgb").to_tuple(*in_ftypes).select(pre_filter).map(preprocessor) + if cfg.samples: + dataset = dataset.with_length(cfg.samples) + if batch_size > 0: + dataset = dataset.batched(batch_size, collation_fn=dict_batch) + if cfg.batch_proc is not None: + bp = functools.partial(cfg.batch_proc, server_list=server_list) + dataset = dataset.compose(simple_map(bp)) + + load = dataset + if cfg.num_parallel_processes > 1: + load = multiloader.MultiLoader(dataset, workers=cfg.num_parallel_processes) + return load, out_shapes, out_types + +def get_random_wds(cfg: WebDatasetConfig) -> Tuple[Any, Mapping[str, Tuple[int]], Mapping[str, Any]]: + ''' + same as get_mm_wds_from_urls, except random dataset for SOL test or if you don't have a dataset yet + ''' + # Setting up modalities (shapes and types) + modalities = cfg.modalities + assert modalities is not None, "Modalities cannot be None. Don't know how to process data!" + keys = list(modalities.keys()) + in_ftypes = [] + out_shapes = {} + out_types = {} + for k in keys: + m = modalities[k] + if m.ftype is not None: + in_ftypes.append(m.ftype) + unpack_assign_or_assign(key=k, value=m.shape, dictionary=out_shapes) + unpack_assign_or_assign(key=k, value=jax.tree_map(type_proc, m.out_type), dictionary=out_types) + + preprocessor = functools.partial(run_preproc, keys=keys, modalities=modalities) + def random_generator(wds_config: WebDatasetConfig, num_elements: int = 100): + for _ in range(num_elements): + datum = {} + for _, modality_config in wds_config.modalities.items(): + if isinstance(modality_config.shape, (tuple, list)): + datum[modality_config.ftype] = np.random.randint(size=modality_config.shape, low=0, high=2).astype(modality_config.out_type) + else: + datum[modality_config.ftype] = jax.tree_map( + lambda shape, dtype: np.random.randint(size=shape, low=0, high=2).astype(dtype), + modality_config.shape, + modality_config.out_type, + is_leaf=lambda shape: isinstance(shape, (list, tuple)), + ) + yield datum + + dataset = wds.DataPipeline( + lambda: random_generator(cfg), + wds.to_tuple(*in_ftypes), + wds.map(preprocessor), + ) + print(len(list(dataset)), 'a') + if cfg.batch_size > 0: + dataset.pipeline.append(wds.batched(cfg.batch_size, collation_fn=dict_batch)) + if cfg.batch_proc is not None: + bp = functools.partial(cfg.batch_proc, server_list=server_list) + dataset = dataset.compose(simple_map(bp)) + print(len(list(dataset)), 'b') + + if cfg.num_parallel_processes > 1: + raise NotImplementedError(f'No suport for parallel processes for random data generation') + return dataset, out_shapes, out_types \ No newline at end of file diff --git a/rosetta/rosetta/projects/imagen/README.md b/rosetta/rosetta/projects/imagen/README.md new file mode 100644 index 000000000..5cd576c2c --- /dev/null +++ b/rosetta/rosetta/projects/imagen/README.md @@ -0,0 +1,138 @@ +# Imagen +[Imagen](https://arxiv.org/abs/2205.11487) is a text-to-image generative diffusion model that operates in pixel-space. This repository contains the necessary tools and scripts for performantly training Imagen from base model to its superresolution models in JAX on GPUs. + +![A racoon wearing a hat and leather jacket in front of a backyard window. There are raindrops on the window.](assets/A%20raccoon%20wearing%20a%20hat%20and%20black%20leather%20jacket%20is%20behind%20the%20backyard%20window.%20Rain%20droplets%20on%20the%20window_16.png) +![A blue colored pizza](assets/A%20blue%20coloured%20pizza_14.png) +![mystical portal man](assets/a%20highly%20detailed%20digital%20painting%20of%20a%20portal%20in%20a%20mystic%20forest%20with%20many%20beautiful%20trees.%20A%20person%20is%20standing%20in%20front%20of%20the%20portal_20.png) + +Prompts: +- A racoon wearning a hat and leather jacketin front of a backyard window. There are raindrops on the window +- A blue colored pizza +- a highly detailed digital painting of a portal in a mystic forest with many beautiful trees. A person is standing in front of the portal. + +## Architecture +For maximum flexibility and low disk requirements, this repo supports a **distributed architecture** for text embedding in diffusion model training. Upon launching training, it will spawn LLM inference servers that will performantly calculate text embeddings online (with no latency hit). It does this by creating several inference **clients** in the diffusion model trainer's dataloaders, which send embedding requests to the inference servers. These servers are based on [NVIDIA PyTriton](https://github.com/triton-inference-server/pytriton), so execute all requests batched. Currently, this inference server supports T5x LLMs, but can be changed to be based on anything (doesn't even have to be JAX!) since the diffusion model trainer's client is simply making PyTriton (http) calls. + +## GPU Scripts and Usage +We provide [scripts](scripts) to run [interactively](scripts/singlenode_inf_train.sh) or on [SLURM](scripts/example_slurm_inf_train.sub). + +### Container +We provide a fully built and ready-to-use container here: `ghcr.io/nvidia/t5x:imagen-2023-10-02`. + +We do not currently have custom-built container workflows, but are actively working on supporting this, stay tuned for updates! +Imagen will also be available in our T5x container in future releases. + +### Dataset +This model accepts webdataset-format datasets for training. For reference, we have an imagenet webdataset example [here](https://github.com/NVIDIA/JAX-Toolbox/tree/main/rosetta/rosetta/projects/vit#downloading-the-dataset).(NOTE: imagen is not directly compatible with imagenet). For imagen training with a compatible dataset, you can find or create your own webdataset (with image and text modalities). + +Once you have your webdataset, update the dataset configs {[base](configs/img-txt-ds-base.gin), [sr1](configs/img-txt-ds-sr1.gin), [sr2](configs/img-txt-ds-sr2.gin)} with the paths to your dataset(s) under ```MIXTURE_OR_TASK_NAME```. + + +The 'img-txt-ds' configs assume a webdataset with a text and image modality. The images are in jpg format and the text is raw text in a ```'.txt'``` file. Currently, the configs are set up to do resolution-based filtering, scale-preserved square random cropping, and low-resolution image generation for SR model training. This can be changed (i.e. if you want your text in ```.json``` format and want to do additional processing) in the dataset configuration files {[base](configs/img-txt-ds-base.gin), [sr1](configs/img-txt-ds-sr1.gin), [sr2](configs/img-txt-ds-sr2.gin)}. + +### Downloading the LLM checkpoint +You will need to acquire the LLM checkpoint for T5 (for multimodal training) from T5x [here](https://t5x.readthedocs.io/en/latest/models.html#t5-1-1-checkpoints). All models use T51.1 format T5-xxl by default. Once you have the checkpoint, place it at ```rosetta/projects/inference_serving/checkpoints/checkpoint_1000000_t5_1_1_xxl``` (appending the ```_{size}``` to the checkpoint folder). **NOTE**: We're working on adding TransformerEngine support to the inference server, but for now, please run with the ```DISABLE_TE=True``` environment variable (example scripts include this). + +### Running interactively +**Note**: this should only be done with singlenode jobs + +```bash +CONTAINER=ghcr.io/nvidia/t5x:imagen-2023-10-02 +docker run --rm --gpus=all -it --net=host --ipc=host -v ${PWD}:/opt/rosetta -v ${DATASET_PATH}:/mnt/datasets --privileged $CONTAINER bash +``` + +### Single Node runs +Pretraining can be done on multiple gpus within 1 host with `scripts/singlenode_inf_train.sh`. This will build an Imagen model with the Adam optimizer and relevant parameters. It will also launch the relevant LLM inference servers. + +```bash +#### Pretraining (interactive: already inside container) with example args +bash rosetta/projects/imagen/scripts/singlenode_inf_train.sh {DATASET NAME} {MODEL NAME} {PRECISION} {NUM GPUS} {BSIZE/GPU} {LOGDIR} {MODEL DIR} {NUM LLM INFERENCE GPUS} {INFERENCE SERVER LLM SIZE} + +#### Pretraining (non-interactive) +docker run --rm --gpus=all --net=host --ipc=host -v ${DATASET_PATH}:/mnt/datasets $CONTAINER bash rosetta/projects/imagen/scripts/singlenode_inf_train.sh {args from above} +``` + +### Multi Node runs +For a SLURM+pyxis cluster, the `scripts/example_slurm_inf_train.sub` file provides an example slurm submit file (edit with your details), which calls `scripts/multinode_train.sh` and `scripts/specialized_run.py` to execute training. + +### Pretraining run commands +All commands below assume you are in `$ROSETTA_DIR=/opt/rosetta` and have the scripts and slurm scripts locally. + +### Multinode +Arguments are set as such: +```sh +sbatch -N {NODE_CT} rosetta/projects/imagen/scripts/example_slurm_inf_train.sub \ +{DATASET NAME} {MODEL NAME} {PRECISION} {NUM GPUS / NODE} {BSIZE/GPU} {MODEL DIR} {NUM LLM INFERENCE GPUS} {INFERENCE SERVER LLM SIZE} +``` + +All parameters can be found in the relevant script. + +### Example training Commands +Assumes 8GPU 80GB A100/H100 Nodes. + +#### Imagen-base small (500M): +```sh +sbatch -N 14 rosetta/projects/imagen/scripts/example_slurm_inf_train.sub \ +{DATASET} imagen_base_500M bfloat16 8 32 runs/imagen-base 48 xxl +``` + +#### Imagen-base large (2B): +```sh +sbatch -N 20 rosetta/projects/imagen/scripts/example_slurm_inf_train.sub \ +{DATASET} imagen_base_2B bfloat16 8 16 runs/imagen-base 32 xxl +``` + +#### Imagen-sr1 (efficient unet) (600M): +```sh +sbatch -N 14 rosetta/projects/imagen/scripts/example_slurm_inf_train.sub \ +{DATASET} imagen_sr1_efficientunet_600M bfloat16 8 32 runs/imagen-sr1 48 xxl +``` + +#### Imagen-sr2 (efficient unet) (600M): +```sh +sbatch -N 14 rosetta/projects/imagen/scripts/example_slurm_inf_train.sub \ +{DATASET} imagen_sr2_efficientunet_600M bfloat16 8 32 runs/imagen-sr2 48 xxl +``` + + +### Sampling +You can find example sampling scripts that use the 500M base model and EfficientUnet SR models in [scripts](scripts). Prompts should be specified as in [example](../diffusion/tests/custom_eval_prompts/custom_eval_prompts.txt) + +#### Sampling 256x256 images +Defaults to [imagen_256_sample.gin](configs/imagen_256_sample.gin) config (can be adjusted in script) +``` +CUDA_VISIBLE_DEVICES= CFG=5.0 BASE_PATH= SR1_PATH= PROMPT_TEXT_FILES= ./rosetta/projects/imagen/scripts/sample_imagen_256.sh +``` + +#### Sampling 1024x1024 images +Defaults to [imagen_1024_sample.gin](configs/imagen_1024_sample.gin) config (can be adjusted in script). +``` +CUDA_VISIBLE_DEVICES= CFG=5.0 BASE_PATH= SR1_PATH= SR2_PATH= PROMPT_TEXT_FILES= ./rosetta/projects/imagen/scripts/sample_imagen_1024.sh +``` + + +## Convergence and Performance +Global Batch size = 2048. We assume 2.5B Training examples in these calculations. LLM Inference server nodes are not included in these numbers. +| size | GPU | Precision | #GPUs | BS / GPU | Images/Sec | Im/Sec/GPU | Est. Walltime (hr) | GPU-days | Config | +| ----------------------- | ------------ | --------- | ----- | -------- | ---------- | ---------- | ------------------ | -------- | ----------------------------------- | +| Imagen-base-500M | A100-80G-SXM | BF16 | 8 | 64 | 858 | 107.0 | 809 | 269 | [cfg](configs/imagen_base_500M.gin) | +| Imagen-base-500M | A100-80G-SXM | BF16 | 32 | 64 | 3056 | 95.5 | 227 | 303 | [cfg](configs/imagen_base_500M.gin) | +| Imagen-base-2B | A100-80G-SXM | BF16 | 8 | 16 | 219 | 27.4 | 3170 | 1057 | [cfg](configs/imagen_base_2B.gin) | +| Imagen-base-2B | A100-80G-SXM | BF16 | 32 | 16 | 795 | 24.8 | 873 | 1164 | [cfg](configs/imagen_base_2B.gin) | +| Imagen-base-2B | A100-80G-SXM | BF16 | 128 | 16 | 2934 | 22.9 | 236 | 1258 | [cfg](configs/imagen_base_2B.gin) | +| Imagen-SR1-600M-EffUNet | A100-80G-SXM | BF16 | 8 | 64 | 674 | 84.3 | 1030 | 343 | [cfg](configs/imagen_sr1_efficientunet_600M.gin) | +| Imagen-SR1-600M-EffUNet | A100-80G-SXM | BF16 | 32 | 64 | 2529 | 79.1 | 274 | 365 | [cfg](configs/imagen_sr1_efficientunet_600M.gin) | +| Imagen-SR2-600M-EffUNet | A100-80G-SXM | BF16 | 8 | 64 | 678 | 84.8 | 1024 | 341 | [cfg](configs/imagen_sr2_efficientunet_600M.gin) | +| Imagen-SR2-600M-EffUNet | A100-80G-SXM | BF16 | 32 | 64 | 2601 | 81.3 | 267 | 356 | [cfg](configs/imagen_sr2_efficientunet_600M.gin) | +| Imagen-SR1-430M-UNet | A100-80G-SXM | BF16 | 8 | 16 | 194 | 24.3 | 3580 | 1193 | [cfg](configs/imagen_sr1_unet_430M.gin) | + +`Imagen-SR1-430M-UNet` is not currently supported. You can use the sr1-efficient-unet instead. Coming Soon! + + +Imagen base 500M + Efficient SR1 (600M): +|cfg|FID-30K (256x256)| +| - |-----------------------------------------------| +| 2 | 11.30 | +| 3 | 10.23 | +| 4 | 11.33 | +| 6 | 12.34 | diff --git a/rosetta/rosetta/projects/imagen/__init__.py b/rosetta/rosetta/projects/imagen/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/rosetta/rosetta/projects/imagen/assets/A blue coloured pizza_14.png b/rosetta/rosetta/projects/imagen/assets/A blue coloured pizza_14.png new file mode 100755 index 0000000000000000000000000000000000000000..003f6cfde2e91185ec45943debb2dba5c2f01f70 GIT binary patch literal 139169 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%0D^F&C486yiIaZ|zyl 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

p0N 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 z

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

Dd=Hp=uk|hg7s5dN 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 literal 0 HcmV?d00001 diff --git a/rosetta/rosetta/projects/imagen/assets/a highly detailed digital painting of a portal in a mystic forest with many beautiful trees. A person is standing in front of the portal_20.png b/rosetta/rosetta/projects/imagen/assets/a highly detailed digital painting of a portal in a mystic forest with many beautiful trees. A person is standing in front of the portal_20.png new file mode 100755 index 0000000000000000000000000000000000000000..9c15759274ebe8df615169e4d46fa5c77f41bc63 GIT binary patch literal 154313 zcmV*PKw!U#P)V>bE;lYQEFfrfbZ~PzFE4FjbZ~5MbZlv2E^l&Y zFWU+ThX4Qo32;bRa{vGjVE_ORVF9Q=r)dBI002ouK~#90Ed58XZCRG3iG53Lee@OY zK55#`MYxB^h|Db2$VLH05Nr_a@jtRbfQ1A}pipEoWrRn#G;@!k^ zVIygpVJ0~)Scn$s0#c@QZY0SP7FP?psv^Dmh@00twzs#q@qk%QS-$!RD zfB2re>knk9AuUrxTF?Xs-Jv1L4FZYt4j(*7g;Wr| zMY)Jpfq6bbOwaklhi@?=WsIKDM~s9)40vzx)`Jva9WpUQ=LmKnv}@>&Bo=JlBZ4F^ z&X6cvl;8qx?c+=sbCuW1Yo2OPLnbO^25dl%P3u7^NZ42$?3I z=449YhY{ySgb>hJdLrfd8O`y4?pyLAWim|x=z5EHf#^MHmauqo&YL&)#4_Vz^@K%I z@YSDx$BXAH+_7W-&@f7c4-Ti2;U71z8R{*avy_VruRO=t5QYJjX`&HmqtRY5j3Xg{RvIr2A`L_z zxINY!k2?;{j#ta4Oo|1we9C^eC3s7kWxT$>WiMKWM1%4ODVeM;>BpS?`!~dTjZqPm zDCW}=Sxp&SWFd3p8CW}Tcf7@gjI21v%oBzX80venz4ImF{>F~Ksyc;vkX0-(rk}pg{CYE@EN{6GHkYN-v0&l{EBXt z;_3$`B0)L9kOU^v0^96)SRWaJWAr1|fzc^KMAF321W(%y2q{n!hyraSMoD__&=@kE zP)#Zh`wap^q7%R|96NMTF{^Ug*FS=f)SCu}BPj|7Z;7MlP}dA0;={BO^RM{t%b z(-1)_L)~-)ADLAZ!)OVi#}qkj(^5&ru|DFgqu(`%;8DuZbpy5^2tuQ^1~KA&z`HAc#kp$6$438&~+msQ#dbhiADrRk!SR~hTJ3!O^*zMeza(< z072VyqybEtB8ZelN+JXENltVl3Xku5*6-d@`#|yJoU_FfMxEmGjGJ%2MG4JN_slNN zIQ#e|?;mez$AK!TP-({4_4E$1$&|zTj+tk3g+d6mlqX708C0Z_IRlX5=J*5#*tDb$fyu9B2c7CkSIw|fikZs$AP&r z^qZQ)repOiYQdw0M}i;R)b4OIDresV^vJ6y9Tl^LIX{27bY zQ(k}n1G){Y&X)|8roQiJ62aVLI59Hb-Sgt3D<0NI%Ce$JG=m?RnVhThGoC$t%FS_4 z-;4;Yk)5S!x5$?-nLK~V{btA6m!D$Fg7up>93Jn9PLfP=bd~Y!Z+`(2Ow)1q+uyPM z;SH|u`1xOb0k>P+hdYF|P$q2J1KrIAf9P?+Ge(C%;)e!Jr0sgj>4bh92tJS&33c7z zUBtymmKR6?sZJmSjDUPHV|11fzy^;8Fq#IiLr0<{5y+)rxtP;79hIDrmN}i>vAcg{ z{q7!P6q9_)(2X3L8iXJ#Qku3$rv_sTN=S5H*9ZX+iO!PdIinrXN|PpvF*sa5vfGc8 zc}|)d+O{W24N7Z@DyME+dTWsqyz^)zPQWnsTwFfm^*7%$G&Qru1f>-1@j#*zk~E`k zYP!~v=P6}10ijR^dilJz~mVL!Ep4P&kEvC&vG_j zB7&zrw1m-f-0UC-v@$sB7zT?SEbSgHRx_6K1-sn_Ya{KULyRL69hnU!g-^&YFL*b? zt1o{}^X+T;Wlr(zoUyJ^K+u})&{2JKMSpkCv2R%zO?7_G-PjXu-eZ(ze;DY70bR`~ zFVFe>vkMNJ4YeI8&*$XZB_DtCDc`>Sj=MKE%qA18>JdLX=NLWSdsf23e8$_uBhClP za>3bh!u|Fd@)92)o6NAWSC^NhLSyTOrt2A<#iWK9zy*(p0V_dk ziFcmp9f?S=qX!WoI8tS}Jb%hz(s6x%&!4WpWwSWvI9@Q92@^8@`uP{^ufO3C8=B(= zd_cA-g#)!(qRSNDIlg|lWf4K-f+C$jk#guaw4>u!pM1gO*zx1;hQ-sD{LNqe9dEBU z{BZY<^XC^lJltSy#}_|)#YE=(hkyJ>qVvql1&8i{PYaxsj0p0JGpeg|W>256e0t8q z@Bhrh57*?g86LRZY!)E|9jzjy-;~v`vi>0(IIa5HT=}jwmEj31-t0 z9~|Dou^ajN$M*#1sJnr(%rP>uh#GOfGHc>Q6pmoMz49;4b7*I(}hE^%_`4Wdj>x9Ss8fym< zZJ5kgJnj!TJ20Kh5Fuc+B*us~3BgHZ1dky4h!6@PC4G!Usc_C>grE>`YoSp6mDT2qVnq3(h`@$k#v8{PS0A z9rVuee!oYw2b#?uO~Toe6-`q!HY56{_m~<(>js*>K_)5FNlDYxXr+h*bgH;0OS;z5 z3ZqZcqrix?wVzBD6)HUtDl^f6M3tsYwu0(X>aBETbPS^GSu0g1+lc z4MDBQbk44>sp}d%q)HP)#7Bz|k-q8BB13DzVZUcKpQ5CoZH}Cuub9nO+}=K75{Xg* zDI_M%>7Bzi4lgCC(zv!K5g9s7$yOO5z_D-G*@5EOGs?q({`Fg$x*u~mjTDkT2%OUV`2L3f?q|PZv)}Mf?|x*{9AI>uef&A=+xJ8SS|$|A zQAo?v^9r=&_2V70*%dm!Ak|v}rvmYaaeEA>6EmD^0Q~` zc54=wOLjMR*t%yZOVm7LGA(iM16fuegQaedNCd7K7_f}}fJy{1l@vOqN>jR~!#PWg zir^xyvj`+zH=t8ZRpkT^i?ca;7)gSowO`H>FC-4yd_B!_QyTb#fsip6kvyeRA=}QD5oi+4Y&YN16@0Wu1OL@ zl4^!7B2&Tb=8mSR$@2u~M!J5atTI$0NPU7X5|mW<-Vu~%+Z_nw$a1z|$P;{~>Fhvy zwuH80nIs%<@7eD*DDP1rl4m(KHH;!~xtO7KpzB6L2QCDV z3Z)>%5g8>#nldjY@DQ;=&<>8ivq&Wn2*!Ry&MS7uBQ6dEA;^~%lVXk!f%Rt3kYw1g zN5_F^2V|-l+78?Hc<1mEvPnfCvbtKHHl1OhwH*nP;75`yrOYx$?>M$2B1>_7i^)@h z^Ed)>@Qx7UsY|I1MOk8!3~Sx#WR?=64Iu<{lF|;P65>1+?pcQ4AkffR>VP;(@C32_WLc@8=e;IE#4!e77olB?M{i+n;U3|Z8qQlXn(UeDiShj5QF2gnljCE zj8G6IgHZf=^T=HsiCKbFih3A%tXqVF#0OL};@2&iOlf?i9V63fPL|{}{Q%mq?HYnm zWI9Df!C)PY9eFrzdD!py@%BBx|K|7n_Amd$-a_fVw3+Uw{^@&d<2n?^?~i7Q^d#jM zfA<^qk8gN(zTm_6KQPTMFiMeMT@lL}f{3t=YC5AE9nJB`WmO>-3vQ)E6d9At6(Tj1 ziz&f%`0YJPMncnIhn|1>=JyI9>6GMz9Co@qWMM$fD)i9RqmhZLIgYQ?kFGu}Mh^Ox&uh9H<^1#R1rn3Vl~$6~oa zYK7J*F@Qu9f+I~5f{17o0mtEZq^c@rsi1cw$Dt=^&xhTS!8yEjXl2;7J=1bQ-*hB8 zMa<7pnZo1~ z_ebVwjuR5o*VqtAbk3i?`JU<7glajZ8vhdYk-fJg*s zs_6!ca~^LURgq9;DTm#WaxtTG11?wCs-UwGgyDF1j|rAyQZlVdip7e<%^m&Wh)y)y z?G`uY%r4K_eYionz_gkmgkbLiXg!8*uHh7CQ-Y00i{`SAT4I&0at z2l6!KXaD`*GB%cVb7XL*n}ausogJyJp3zK~cw^vLQznMKZ4pJvBuQxZTW;QeKomL0 zrbcI)NtF|X!c-Gx^9g;k<+xj8vXt#mBc*3Po$%xP*Q|#eVZ@^??>e znU-w(ftZy@t4Vc&&NCKii83j>-3A#INtU9$qiss^MAJykwzq8Dz^Yu)wj+5t%3Q~dtt#UwO)QS6$NkQ9pBs~;q!X(RhGFftL8;-#t@5-%^hi;5Q3%a4@BFu zREAH}6;p38I)T`7ur)7NpYY`uf5ZRn-~T@tdV#D`WSXOt#9DV!xVCFHZ*KYF*kk4u z^H)#V9kxxZFo|XyT8gD%wV2=z zJ$*MKghXqB)6u8=Hd1|I!P&3D~6%NAQG^K{= z{SKXLbY4(S&+ui59s`%FCD-@2=-HI&>WpkPqaH?FZkRl|pnm&~ez0hrAjL_NKVMBa z9@a<%Wk^hl zNCP%{gi@qwMr%6&w33jdWOKztj@Yh8*g!s8l2#K$ULb5>cmEEZ87K;Jy+nFTup`lX zth0Rl@+o)U{Yc;c)PMnsR4}@LFey#%aUKYPKYjh0pG(JYe)AE!%=zwNOYH)7Z0Qa= zCeu03KEC3s@4iRQa=g@hc=rZ<^@3Sh5npP2x2M}5XxER(DA3aiGpQIhd#Z9uEDX(V zgFAKzRX_%-S2-Vj@&bRolsj`Ahz&Kh)>qx69!8?j9V|D%QhOuX{S~6eE*@Q^215pX0 z4dkXGNfIDpy<@%Y>1~TNiYQM?hEjsU3KFGZwB#7h=P&5OhB3llwh#Q(YKe(~r7*mD z_JYl(<->8!?BWUf{tjb3yUh-~W2jr=Q-$q1CRgW7&gbl2zvtV3`7^Ug#pOrONupwR z`#^VghW_=JJbt)mefz-8hj)zak%y+Gi;l9Y`1VgfGHwrCoUbry!rh10L}NI6az@=8 z(K;cWr2Ofh|HQM)37@~Z;;ZgQ>;PGjV^w4XW|N$3rWte1WHIOMhg$-H;_?iXVhEAN z)g{4M25%82C4_(ql2qp$hXVo0^6V1P57e7I@7{c%%quqSmd*W|(RrebynObO>1>V& z4Ts|%r6gJ?g-#Me@F=A~D`E)vv8S~Tr3KcGbi;@?8Yu;NIVVwyPL7mCM%(q+!D3@T zDM^)2P*u(lJ;`jrV!7bk-~H}{vT8w+qzEApA!6-7i~(&DbRy~N4l_;2iUQ{ows&`g z(NcZDtbI)Ula3+L?#3w$dn;!K|eZ#(g-xxOOEXc zoen`zRVAH|><=|gDDu2Q83@*#HUSjQ|HS?S$deZ#&9YMy*!vSpkQjXQi1fq@01V!r zYFy%}WY8(L^L%KoF_}VY0|e4EC*WyEhZ_dA+Z`s$Fj``ypbw6AXqglRgX?hK5u)d8 zcE*>_U-GB$M_%_^{`vL?emFibFDmMO&+X$aRz|*k_zQpj;ad*JHOIOpozIZc5M)Ag zIO5!h6bgB4Fw+TP)6lJJHpv~!GfOsF>b~YaiLCzaXNYcK-*!BC@q+D-KX6r6Ojl>z z{P=;Zi>KUdHvHrN?`up_Fm@gn2C_WE4g-0fkO)J6Y?)<>LW52VmX|9gPtLg+d-}tk zxT`7VOK$IOIUM$6vnkGb(ln*7Ywo}Q0ed`B2!%-$eb*vmq_2^AC9}nh5f8(_ z(2k64%OtB9vGl`0UR50KALxbwlNY4RC1i>e$Nsywh<9&U7OG|b8=$GSrj2`=DmPmq!*PS4Z#f_5;Bk1f8pJpFja#Z^H&HjrH~onH|d z=!*%D{?wr^ClyHy93D5c+eaWWUo6py#-s`TXnEW`V44=^Ek+rLk|0xFK6}B7PtJMw z{lD;Hwq(<7`KLEu@q_Cah2o-IaW=o?4>#|KWk%2j5EzqUOoCC6B`HEG>aIoQC8PI* z7$`)F6#=OdVsu1tGJ$LkJ5UB;5>oXO3ThNOJ89BJ8ay7PL`sRX9(=?oh13}`k;n-Q zz9$5KA_iq7`hbms!C7#gB1^H>qeQ?GaV|jg2qHmxhMy4gs+@3KYq8Sy?hD z*w_JQ2L{{X-N-Ta{O;y^9zsLsM_LnjZ;yO(^*O&@{)We9%fGz)9rN=7T?{<`|VOA7`K2SH7UTNAx$HU;TE@5c} zT&sa`~LJ?);`n zkx>E$ok*mRjMgFb$*h&Cl;}V@fe{871Ue{W%uZ&PZa|KKz8kR8BZNjJ5|zr+2A8FH zJWe=*3`nC1K4S6&VI)#Sf`kwRVHgo2;JhWofJsvFv_k8IA-1GRhMpU)@2?@IOea&? z?H!Hp@xp=*j0Wy^>(l>m8iHbTSo8hcANaecU!o+e5BK;a@bk|JpKT1kS3Vp1@_I^*{1cdX8r zjBX&Q3`C7=dg`%dIiIpQ+%uV%C%0zp(A(`6V-kFbCkSFvkxeELJ-h9WVmW1!mmKQ@ zcI;@62aGh>(Qh(L?S|fl7>_VrWtf?!{cpW8#TjMYtl01_Wdng zs}S=U)1l+?#g~}f9@{r$B!oP}28Yroz+yUIp@L;Fl5#r3N6Gn%m)yO(rta=IzdUDm z*sxkC5}okF!+Rd;dz#=-NrqP`cU?>H^{EyQiX_P}D3;ZNA}#5fjzlP`tU@M{{ccNk zafx>hqDQ)j2p(+=L2Cq{qd^6Y5r#~bh#*j2FdjRGW5@2;G920ybKoN?Dx?rdr4d3B zMW7FsAfZ^zNQ#uK$`FYpXh=deLq6nr(c!!TCwA=>>LY;7PBCdBtEy(hc z(OQCxNEvZNgg$+1-a0PMFL=DYM}~lOpoHXcd&5I>%eQwwvK@8|yTH?rKBlPV><@dE z#gv<2qzew2COqD4D5e?Fd7_I*1oe7PR?WzkhW>HQe!nBsDT}J26%rY@q?-+2em-Ts zC>ZjD<)S3kH35Nh0q-q~<%IkDdvc|bVWcqNvx0GOTwiZVv|{v;K%l8>4)5O6e)5dV z|LGricv!Rh@Q4yq3M~lIp^ag7xnlR>8YN(mAhH}OC4R8T5J@wGlmRbJioxN~u$oS3 z9~%08q@2xARf;f%J~+xr!D3P3Y|pV7(JC?M#2^Gnt%(v;kX-lSV9QMNsdf3+4&sGlq3emzNK_2Ws;E<74A$@Jxf^#c=+K5Tw|FR zIjS4j-EC26$(lO&_w_n5R~2ofDNMK-}G zN%8!W_0V!WtZC~Gla)m0dGhQWRZQ64@7TV-ra9a&zdR>RbEJo^uCbkEKD%Um*fYe2 z*d0jbQwT6!objgLASWe4%adI>O-~$t+ifuogq_2F)wL*3r0~D3Axcce|o{)C>R&lo&KZb*w1H}+(S z#E+0DgH%wSFKC_RBCQx~zzqY3$4APi=XBnYU7TUPV~n2s;;9*-pRiceoX<*Vj9y1@}qM5*vYk9Cf>@7Ls2hFs2>%?eg86LORB{P~iABQECH zQL}q@N8Pq8pS@&S796)*MCVvNd(L99;JZKkp510cHuNl>za$Hicfb1`AAj<5o?boS zXCI$4mnqjbZ*Z|-wR%oQhSCXLcR=P9nv~p>(0is%(h}&~mPRCq7|{XpG$ru?lO_z& z0}&Y`5s4F$vYO)B5$zPlYx;LJem4*rN4wvV=#(N$NmNP{0i`uYC7>jUNpO`T2oHes z9uXokfb;ewHQGpYko%PJ=E&jUz)??|;`=p$<^s&9Dq z$%@V4jzTObCNuIfquK9}%CNdRqsem?%OxLf?xBvV{5+c({#qISyiBf1KIG>lqD6pf&q?(VeE@_$r zZ@>DE{Nfzf^;BgJLePyhUIau{GMiNlKOWI(!q7Cdhb>+Ul1YYf37NAjiUrO61Bbgc z2PDKu-wYU)&<`WWwk4S=w9AO2r{1?n0%a;Gs|4*MSm#g@>@bofg6=R-njAG)tUGdC zuL)6*rWt+P7Hj=|;M^$C?oueOH&SzIVyt!s~x1lfz%`hO0AS-hYhXXN$ z(+8I&OtO?DGejIx3yy6^?FLjjBMt+-x1_TvPMqrcI69KWgk(9RA1zNVF4=5%48A3) zrp(JZ_uW@aOhQUTGa}O*N5J#B8UbGZqVEuka zHeE2gs<@9kgrDHImLgA}?|C>LdHV7N?{99{Z;zNXM^6`Aef$wcIpwh5Qt#j6`UCyp zo>#A4GR+Nfl&rT$jPmHLQZBImTadCyK8%i6f z@{(=eVwA*3$dd_5rpSpkPHMmk7L%GKf=DHKthq*oy&I!{3=j8TXf z&;)`wx!Vev5d#nb=NYUc3=$y)85w;iv0abb^>m#>9W#Pf-bfAQcc2Y%r`YU!ickzq{s1alxzV z8DGEs13&-h6LfjTzuaA;o-9d=g4AT_sFw;O5^YAFU!Jpm^F7UDjY|eZS)ylCOz@}(CQUfh4N0n4&Pt}! zlDBW}7{{J^w<9V=mL|ylh;kz~aAG#l%$GBCo>M>EBLdjNf!MW#V3|F?q?k>Z2*v)# zA8Fc}!|}lEY)QR8V65#J7EDAF`VW(I&XO-YiB5FJ{b7QNC6p9(^> z#NbhkRC$4n^5lLCffyvhOL8yqUegaF&N)(@BPRuI-!nKzXe=r??4iRIitKVlaXzIB zEy-ksz>?^I@Esn3ks7H4-b%W$C+djo11Sj9#yPIP`-ZgfCsEKJ1GjcW z0Ph~}m@F&atRI*smPwUze)*K?(*@5rPgpo>Jz&S4Fgjkpy+LV>$R(~lWiWj7 z=@n_7@b>L3>&*ctG-C`54>wG=J35u%wK{oac3Z~lw`hObgy)M12Ld`X6j$dACZLWD zMP<-QjuI>KG^KMLnMe^)QXe|TVMJ~Z*lnb{z2nu3D`c6mf865-1zMtuNrjY=NTi%h zXuA$26(Yjs`j*+%1(U^!IP?trn!L=IFJ?p`kP+HpATNrOpPW+;pC3o6GNmjF)*o)k zvWyra)_R6f12=b%>}yBT98rm2T2@2`il{MCqKqU- zN`#MOCOuhlog-5TG8(qW2AQUKCrMOB+9;a4EyKDdj+R_yWIFo^veIay5dtQ8iOCGc zNcyoS;Mg@=hW&_-0UySbqAxWvMvO8zK&gbp7!s{c*5WEB3J8Ozshbn4pf%107(L^$ zN7;3(_FC{{5dZHWJDlAwAP)GtR}4U%y;2D>J@*_nHsK8`MN{ewiUe zLXs(ZI}i|rG+|cdq#{yig^!-Q`v=A-c-+@Sr?ASC|LiGhX}EdwnrHv;uW|D^_2ZW5 zOtNcQVmDAuN_3iF36LtpBu7;Vy6ce9lSn~6o3VXdlO~2Tn_(MhhdshsM3pgLOi{B5 z+ue~_Q6iLPzi;uWM)zYwo@Pibk%=bu0*Rz33yxh!bF4{ogN*{MHAS8fL!fDEtn;8x zJ9lU;X`<-OsqKMErRH3QsmSU1pl^LPyIrak= zFQ4G1hA2h^o@gX4(@f6J3BIA(?8ri75S}c{xczX8+ia;W=46{SnT-6^FFxg1H|THg z*pHFg2ST)b{OQm5>bHM5;hITG*TVny@BfL#<&z#AGzJ# zvUzw-7d1!gPTDOFWd*GR6U^HA-lNG9W>xIYfbt zr*dh;(L0YAJUWUKq*atGCUcf&GwkT8>lPbEM1IP9?1v87In)7m-@YR`FG(+_h{Vu$ zKecnYN;LiC>HY(m{O6f$9U@tnLU`TqN_sE3a0`|sG>nu9$MgQayNKWttz zEfl}_+0U8YZTOGhe9dvcCN&Aix}`D;!VsAxIaiAdqKv$L{~Fh}xH3UIiAM13WatVfhsdRS)FnA85_Rf%N8 zNWq7jJ^RKoF$P;DeEC29TZYS$_m7VZ!6B_X9jb>K83kpUvu}<>B}j@4U#3W{c{*Rw z{O}Ij_SE|tX9Hzk&^ph%x9^Z4L8pd98-}K(I-9Y4dd}|dkg5i=<<`qcTARx%7dMVjJ#!1W!O$w;KYR4IdZ5FB9`AR2U;kxmQl-(3@Z zWN3SmOkuRf7)3vNo__R<{b9?l+cDHV-lWWC72UB%OU|1G>q#lrkqoq zKPO$Dla~ei0ciyNIG%1n?Lb~kiA|t+cZ+Ny)DGn(?e0jDB$z}qolYo<6sZ+s zJYlhQ*JHg$8c7fVPr%xMZJLu_?;O^0s+@IK(SHDMfyoh4h8)XkC6?kK{X#cG0+CvDs9YtpPF4vthxlq!%y z@vwQMC-BYYjx@>eIwA&7A~iaW1RvP94RN$QJzsEnpYr23{{^8cj7*rG7ZlYDc=%PDKJv;WIE;g-8D_!qU@3D_j^J+u(ytjt0~=m%dl=q(u}5QVSV6m zY*4wu`5u{9Tz>q7{rw}q`SK<2Z`XJ)m?asLD#5#sKmG1cy!b!=&lJ;&t{vIFeaE7l z;d@IeG{@$VBvZ)5aP%XeeE9`gGtm8)KXLf(HD(-X?v4zjr7QcBzUrZx=BP~a^x0D$ zu5a0I_h=1T8#;f)h6JM(A}F-ZIW780C@6b9!xSs0rDamX_K3y=GuP{wGbx>Cef_A<6QDX_3*54MV$Uxm>ZnIr6*ze9dH`S)Qfr4%dXPC(b5} z2)g5r&CpP4&E`<^%fJ5DEI<7voeL-}NVA;2>wbDyfhtSTO7Z{vkN+zn7fddeoXyU- zJKT^cgV80*1}-nIc>VqzlTz{Y{F2P~NSQDxDtg&7pDx+AHBH}RvJBq~5~qoWk@dR` z6H_43$V6jIMy3sARiY#`ZHLFx*%lk^3HVi+?SL_gwms5!4PDoxrNA}~CJ4?aOJ?be z#eB|gx8Y}>yrlpBdsJok?4w^{Yehev?6L??tSzNZ5Jib0!;T$JMgI9e{+ClS)mcdy zbMpGg)ndw@zQ5z<>vu>gd9qruc|3CeShM-@o;1mrI>fNH@v-=u-QK{mI>$2UeN4260Okl8SB^IpE5G$6XdkuvoBtd$H@Mlzv9hr z|H#FQOH`Vj5~^t#ZA6U`862~DL4DT}5I=>>L~3slUZA95zdJC?a=ahWc~x<2j;FfM zJG>E0&ZpGv0ogZ9ug*_dbk}>bG$F}PHDBBJlzD*-9^18~d4_Y5ywqfw;jnE;l%|gX zf;CJ9jl zM1;q?8tY2#?;jbWp&cU13Y@c4m*?zvd#-QqkXqt}!RQQ+1r;dr1s{L%1@{j>Vw)|x ztNHWpk^k;*f5X|c1%LQ(O*WlyzObw}J3e{(3BP&yIh$_JfBojqcrU5DopjGLs-Gi5Lw8c>Lxa>6kE0D(v8qT2K@@WnLncV)P?j+n&%~?>P=F!_d?8HNpxK znK3EOC?^xLB%{bm5|cudXlW4Q1iHkZ+?`$D6ICEakBtMPYuN4|X_^D=@xW0Erqek# zSc3Od<&>%_sePcFrF2aL)`LCSUdP9pEiJMXH2uiv8e$R|eWY^%>juW5Bf=oZeEi_Ntu_};7P4O2*aeBFO>alsFfhpz4%at?uE&l8PM+4q zM~jLxZFv75eoOt`8{BxBxllVtxtP=Y0CvRr7L~};CW@ySTE>3h;_Q;9?|9s7Fj6v^ zO_6{J8QMpSN(?T<)78&MbgCgF1RwAkKK|9u`19R=0VHKnGPW(%WI{i5B;_eE^mwSz zLNH&>sEJIq*cPcIZ!QUq&tUmV6qIPLC8p+=j6+b&3ez{dW%U76H?l{ zBSHkS`JBf;euX=~0}ZTyz7}%DQ{jsP*L-<&p+qzcn!|8+qbkXu-Wa|9~+u|&-CS-Dl7T= z{WmD!>D3duZsgE*6p3V778H{ywzsr(hYSWiK=+|WtsO(tk)|b+$%G`!DbfTVPsSXR z83rqPe7vXYdTfL=&6p-DW{Wds)r#5VjG{bc%;hRU2t|~qaM%z6HjF485lCVPw0+O6 zX{nnX-H>B+iJNsa&6WVg*}U%?)W)v0Xos_`qTPfb3d|)tvmv1(zScqL zr;->fLJ6XXr%tj_)Vm|S^*nigg&SLg98kmh|u5BYnWNmfAVaiZ@(s{+kCG0mXn;+hCS(Hef;0?4*&3N+~rvf5)j1+wG z$t$YNvPdMkQM6gYo7-Ecdq!oreR#n8glcidAq0N=!#5mSOEsSpgg|JG9~(?ANwsEG z&FD=;_?+GKp89rAIvAwM$X8235D0a$au0(gXn_P?-@ZL%J@|mqIrH+2*<{7p{G1{w zsL~3f5|kDoJkke(k_>hrl^KC>LV=|~5Xh2@Dyvw{B*6_dUBlyHLpu)i)0$)PNY`#T z9`AVEJkoU?v&j@U_7v5ENjm4a+cNqV9VNnokYJplZv)lkIZbSdKS4rT1eS{xo5P;7 zRe>}a@7IsWRFfwKpPxVHteTNo&tKlXW_EE&y*u*T@4m*V1eKTEAGSPwx}s@3L$_v; z8y@#9>8zk5lBtZzq9lfa^YaUi54RNOD>nCcIBG~0%5 zKb&q$S~DC+T(Asdk5q86n$z|pqLKvL6Q$+VM;AEdX^NDXrsUNWq@!yaiek?BrypUP zEua3Yzdn(P`vd!LUt_;}%d{-%LdRH-C@Ihm_8)Eu!lDyFIi0ZH+_T%SSxhUo_gg%X zWSTO&oU^;%F`3TTZ8yZaCr@&8bdE%UjsZJ5#+Lm%<0HNwX}f`C z2oz~aT9zoS@x6s{z!9j|H5b!M4%Y{K33{qfN|36Ys62`2Nle1i^9cuc%l6oi6dC{W z=YQni{^B?M`s2^J-yJZfB0rxZ#*X9Rz@(hvjpMry-*LHo%Kl-`YW@+`**Sws320K0 zA$+86Jr_@((XXyI2e!~NIn4o5C#=k(FzBg9ir zm!!xT`T>1$b}#cBdZ5P$j*hNdQr12F{h#gtGa%*&@7n;lAOV%;+w zjs)S^wH;&h3`2iHK&{33NKu`Jij2;asD#yW#ohZg;>C$_Q%TNZImh-E8yr$4jLksQ z3MF8--;>WKs5Vgaf$RVDpUFRZi5mun==t#e9p#IcHXz^j$;S zcl4vfh@jOFx#v-yg|7{&;9^@yiO6bae+5^qO(5$UbN27elI5l^=wEd|jB z5+%umpsY$#S&&8;I*UQGsFutpOODfXcKZ!gI_DvK&*S|Ej{7~qj;t0JOsXX&OX=)D zGxm(5Wilxl?mGI=A}3H5Q%sSOnS{FSFv_ss?3olLvtq@uJ!1EN7WtB9 zP-ro7_2dPr9LTr#Jlx#!t6zP@b$dsq2flsx73;@6ld~5rE?yS| zq}f`gs|k}yPK=;+g0u1z;pIlUZs79CIq&ZuD5q0`ipW$kT`kyd)|6?*I5@^*$5;=T zbReD8w8stEd``SLWBl3ADd!8)JVW`O?&gu*pWpH%2u2)#_v_CI&hvQa2rkexj(G)T zQJ|G%dpw{BNClH5XI7PT&4D}tZ6h)&+PZIkT2FZYIH2T_W*jJzDWAUl1x?d(R$Xv?^P2PX1$mxu+}5Nj zj~U^PltE@BYl2 z?v83PV?WgN;y|-|N2w<)k}3DwN6w#Iv3a=R?z_J*H!10vBzd@H^X{6})e76Tv_r?F znA6%CI}9|(20I*xD3Ub6TZbP4MXLFSzxyRu&!*fxt_fD~(IrU^AzGq$OsXk$FPUD<$%jZ{QnMWKCbZtur4y6RP>$qJXm|tEpTh00L z+c)S*!g{@7x-8K)P;U;{7?@u!aRDZ)6~1dJClh?<2!o?}+>$N|663i41Vd&7WEXAVW{Mi*vQXq`R)eWiaaiOCfQ>K%G%V#gB z$A70xC8O}rW66Fp1_iGOC zH!RXAQYsRYGKNUs4Lql#&!>Mht?c@0d>t zN^@cuOp=o4CAXV*Y&Lg@-S!laaejd}DeK(<6(YOs4xJ|C6l}lWl9dZCo@Y$tltbv5 zPFEZcYwqg@M(6qKfAt%_{?5}JHIvB&^}6Q8lP@q?P9iNYFJG~afpHu;PnQI#C|4!# zc57~rYy5CSGHCwIum5|_&zE$^4G-5pqJbh!(bf_xK&QNYeNFTO%>e85fzLjE!Ofc< zAHbLd7fvO#Z7nf6e)+3k(+!UQ`~Ui%x!oQ~l%~uQUOm486!(Xk?$Bb(44G%A*tVfZ zWC>+i;osb2-)*>DE>NS#jvZa!ljjAP8j%@PmE(NJaeu^;V6CUD3SwL1f;pLfvV^Yf zFj}2FANZ46CF04fbQ%Lau)LabJha4A;So6N$dqBUmhFc-&d#1t+f)5+cQs?(((Df; zc}}~nF?ohI60~Lv7Tfg1;F01qYtHvQ(Svu9VHir#5-C}4_x#s?{EEe@pge!UG#jzg zgpWRd&L6&i$M(Yqs!u+}wp(1grtLPA8F(ECX^L|_ckgfbX!(?f+XqatCuD;2i*uru zm{j7!$nCoubhJbjKu9)+18+XOWoG7NK4bT0!~L5(=mKMKOp1aSAy1N1^R27dZXS_9 zUQK!R>hcIeo5-D#GDk{GE-N==req=`f!$nqQ!1-s1yK0J^l z2}M$JdH#&Y^&LZ3ql_YvhO&H0eoXo9KYh)ozxkX=R&x9BK%!EPevkE*yX}UfkJLlp z&Gik>=8C1WD3{QT9nGO(^p5Yo|DI=yE3_&2@%EY+Bw4E2_BFO^`SJU&_|?b1VsqFL zL^usHH7U2RKk(5<6UwP#mMGleNOR{HY@l3Cc>LpQtOd*IFl>)|X45$+Flokr`mcXr z*NzNM(Fo{97+>G=`O7Cv3ohbioSD* zU>Jr|l1!=*L<@f1%scn<3WMKb5*vrlNP=Kir~^oHyEnz!#BP^i=V`O#yN3~daP zX-+@1%+IF${BM7SR2dJCdz=83NaklHqKxFvpO9a!C@-cg7gO%{w90fG{Ep5FA5pmr}yFXy9LuDC{ZA06Q)a#Zh z6V~6{b9lREqDs!EXSm+7dEC=?7H2(^B4xMTVUmof6P}%a%+yqT{_+K%eEbo&$Da4? zK-7`XUYrqKhZuWSx#Gpy5-$ZmZg+gxHEah@7eQ%7nHtWDoVkoVNi}Civs9XMsd-wI zoM}xSHPJePw`5wACyFtQSP>a~Kq^O?NLFVnE*9s^k|m!$`x&d{1>QQ=>wBypQA%Kv zgf!3SyOyGyP?&=2zr3fv>v{K=x1>>_ti!hhMrVvZaJ^oG$w;ySqZP_1`k_A!FqInb zBe(ZAO!6sdNKj63e^|5cjwwq*2Jt{f1`KqntpDjzq(xG!&^I^ffAa zmdlFPwS4^RFR{+yoWo=Z7>y`%vhz8`YEF`7cpFKK!CBAPJB-mNeTu#oLa@46adCA) zT9z!%&xtW0gy(EgF)0&{+aufe4>X$tN{SPamL#M_j+UBKYHZ!nJk)3(S%394_3O8E z_m8Jw(mY}D$t!mK$o0)5&IJSlr37y58HXOD6iP{Abj)WZtHqMB8>zC2-~IMG{{KAv zN3$kdnqG;mEnNGjk3J!9#MOB-t1_z+RmcLW&}cM)2F%!O8Yi4_#$U!MY1U|xMk7GT zl3A5HuZkP`=<{jUz6A$<8}kUWb0S>r_kEtVE-#l%Cu7nCjt?^)oQ)Z!h9HIaw`+d+ z_un9OjK}lht8aMw?RVVYEV0F~U)~c30mu0v&3%P$Jid3Nae~KlbbQQwHs|~N6ZhBmv`vS#g2UMv z$Kx4~<`X{r=mTD_3YM*7Hk}zr`zo4kcNh5Cno)SbSR( z33xIcbC$#`bigb~NWCXChBy#|K`02odjz1oXPl>4A?T%~ww~7Y2m$liob!Vd#^HpA zM^8wTjLmk<_3eA=wq`b;6UqqFbu5=R++W`^&c=+BDb2cNf4}E!c0_%9&)7>ME18UD zJUo9)-*ouOax_|?kVvJOjTSsSJ|mBECgGe<&Of9wB|Z{h4X&ubS)TsrGn&5U>h3M? zFW*w`3j!HZZZ{krPME}s=61#IenZ)neDvdwN#|2;Z))N=C&&|ab-}jWlg?7YR3Q3_ zlS#tiI6~T%<3+~VEFudBV5)TjQ}tYa^Brwn(KX$mXfH}`fAxE={^nP>a@gSX-@{h5 zP+&kJvCh$~3yL>)-2MJFv16cmhl0^G!L%)56d=7~*Hz5UPDtltjD)sr>8qN_XoT%8 zy4gWf?ZH7hjnLi`j>b%)kX6+&8>Q$lV5dAT35n8_rYMQRm_P^UK+v@K z5qOE|daCuBc%0xiYu3N}Jx4$NBicO0tB~r=HTPGSj2R3laTwFqJ9^jAw;jeBj5B=r zgQqxWxp;d`y?3nc_LQ5Fvgp`sc1*{Lde@_jWZHGu#PG70tgdUyMiFPD;b#v=r1>GW zDfsQHH+=Q#J@dse++R@Wg4f%3B)c(z?scs{%clmaJky=A;O zK#73fSo+#h-?bclFeZ%y9?cJkh=jr_#df<#B;5d1F z$iw3)uYUKArW#&$i5Qa~P3Wo$*}4JrY8>~s_XJj;107;TN4cqJyN*ZM5$&d-QVpYV z&Z^wA-M5&@0_!ZD>9D57ngE&(6-2|HT`KCXMRYyesz5sin1YmOB+hycj}P(EP`3>N zC|4UMlQDhY(BdHxh)7@rG-Zhpf~Zf(vW%{%aP%~-Bajj0W<@-oFgrZucDW?Ky~oey zSRl?)4o((iRYg;5Y3mZD^pHS{LZVPG9Yq|BLSDSNWVzmhg|EJO&%SPvR)kIxv3Fi9ld8TMt1S0U>T zk^mHrbUNY4ol?9hIGCT&Gz~X5*L?Wo6QWT_M=|+$%qX0&Db|D5`e4Lg z{pN2uK7B@zO^D;6w@}-juD4j#({?>&DX^yDgQt(#i;7nlmv|)@r6Fcl(f1Griv8-A zEX(+4dd91Z_tZYZn1G|>L(b2S*}wi0+Zo=!yy5!&j`v?(VhYDs%P-kgEeL@Wo^h5^ zmC*K*c3t3mN7D`!Y$1mXzU_J>4(~ct5EA7Xfzl`>>-%e##h#!ybo+wsa*K#U^7))* zy8{b43NW^(tSjd8DX9vF5L&1>{?Rj%uw%2`lcX7KTOqVU#UWCN!6hKU+F_WE7+j!Q zYe5)9cx%x<;P%~;+2IsIO|{-Lng*yiqKP7WSJC$s(t5Pi168MKFs7&NY7S;o>iZTg z73<}at~J!MW|C#}ohP5osHGVMoyH@3hp{D50_k`{9BNjZnv)NoQ+Af?ufD}ycg!B1 zf|mqQMBh~OT}LKIn5IQ~!AK_T-d>PS7xbp5->f)TjQITXN0fIfe*3GhQDMOO*(rVR zna{^erV&-K#dHInJcuJ2V~7>x;}JT9bQI(JhJ1F&``b$%|M`fg!V)(jDG4cGg@#@js|!lkqqQU#ibX}ByCY3wW(RYmQZ#Lg?R$3X8e)&nq4PbXc+Ab+imtav5l~pm;bg?7xz2eTo;_ah^>?>qQG_s_ERAV8%X(R(LXFfRah}uB5VVTPD8pAR(E%P<%V*#<@oVAB9KH;Ky-3Iv)w|RqaM%U zgE=hkDBl#&YnrM>T8|0@+w}@lcO){P={wR;<3ey&qNfK_tnFx6`UR)aQALSSye>IppX($k|rs(clh2CM=46~z}GkfeGk@pNCILN zk%bfbbHV1_C8q5;dVEYA1$5ppn?(3b@c5tqDK61$|MoY8%MD5j^68k?Nt&jiDjIZe zSzXvyD8OEU_2`t*c)yQXa1Fl_<yMv5t5sGk!Eb z<>Pond$nd3WQ_8N#VF@)S5p=(4^9qvbG0N)Mks42%Zh25aCC4&vD?#>I|3cC*=!i+ z3!-$)SbDm8&u(9kCIXZomNBNQh?0=!A3Wjasvr@Lx@pnIP?(lowdcrdvN*@~mQSDm zfCqEU#cyAek76E;Q@(ujf`9bWe@g8&v+bI@-3l2de6xPXtLhRtn$eXj>h6wRx5sQ+ znnqBzhWSCx+0mHi4EgAu0>PuP6(hUp?BQVt_} zo_%nJxm#i)M>3gUM>!XFcO;`6v0G8!Zcts1wT5t%qobI5eMhz14=GRE5{QU#nz7#8 z6D$&fSxWc^&rv}~e^(MnD658IQ;^PQL*h~lEKb{5Mw6V_YfQOex89@suA%E&B%aNF ziAAV;`qfw=3ShDj0{F4?lX&uYU6#zyAFTq>3pj$EI9?2*|UD z$B!nweS43U5$7iviF5$0cNIF(?8*(#pFif;Utb|cmdb0kyIZdR{eMJ_XEaUAyWa5E z-+ay0X32WD=AZx5KWBfv;>podx~}ET-8)2j&aT+<@ZnRO^IW|7j&gO!*^?(sCL?m+ zu-+8()djEKyhq!Nvx9TOG^g(jP17(M54daPJZYYh#~DgUPR<@vw2pmkXp!h#GYteE zpXV%h6<7Nfrz1vz!?Nq>rJ=)o|Il%SQsJba(~1s@ zSC)IVpUh}nOJ5Z@>rg5nOCpNSQf+%$+mapxoE?s7_jmZ#kZ8xj<3m>W z7jzFl!S$AGG$lwpSNE@ImRCGG_>lkj`X!Ueh`ZgI+g-=AC(n>NZ7I7RkD_V? zecRC1uQ@x4c<}Iy>x&I#-LtI>A03_WB+B{y%h!Y&(s97S`GVt7gFQKg{QP6?HXAONEBttZmX0@gNlto3F=45e9;5~Vklg1P7b`6yfs4O9nhUbd} zXDlzSJ9-^63I)fbh_UY3S9?Zh2mGR3^6TX_SHe=;4&{3m+H)M`Oq3?_5*0*LQn7Xo zcU{S~ujm3rZ!9;x+3!L>i9W7O%Hi6Pr2IETon~Z<1t0ulcyoQ2O$-m2q}BZ zbbd%SJz&4zurKy(%Pq&p6G9cy5a697PGZ1f%9{JT_i)%UnjCO)c*10MIBZ@n?@&t6 zZ7l2El5v_MbkE;^_jBa>fwJ;zQ$x-20N6tVMMzj=pp62btldW^HQRf7&BDvGe1YVbVP z7SkGp)M#fd)A@ur(C93}^gUWhq9CH7L8$=IIEptnOr|;Ecnn#DE<%iTWaBYiZK(D; zdJCZx=uk5nXB3+qIv!)q_uxt4F;Y_POX4sk)SBJ;j@djX%LA+*z9tUNPDvj;x8loU)I!cH`g|Ld+SpLi3|AMmzhy3`DKIL!!<~Q`-)AHPJKl|a2x!c_G?)BgE;N(N3Gl*EybtTpQ zp2G(pgO_agw*jH>Fyif$8X1A|M;*7Ft34(|yN(psHpcF@kN3?BE zWgLx%Oe-SMp|#}2#TFB##G>bP6f@DVt9STC&VSur@r!cJ%J)cRcNyj}{#3 zlraHPdaRT*9@ehsU0?9??Hzwz+;J}rRb(i-o^M6R=HmC5laKiqvj-e{*wRuoCDUO_*nDNPt+ZAaG(@(^b{(hBx>OWx=;n{veu zvL6$tin?iV5~6WVoC?Zfi}x$O`|j6#`rw55bk5CwLy}A|y~Nsp@%)6p`Tdu?_!9o& zkN4;tg+}!RtK4=L$m_`Bbzy6wfzhRc;RGU4f zF)U_>$llR+9j0jr_25%zb~Ww39T8!yvUC+K~kxJ3G24^i*RWp?V)7gaU zuAp46>53iUd`g8xKR!mbEu%1@x!vOML|)MK1}6Xs)6tl_)dfOq$)^)K9dL4V#>Iql!IpnWK+$2n(@cuPf&?PjeFj_S@PmH z1-mjLeR9ZjbinQXHNW`!D`b)}S)73g>C8Z9+V?%q89w`b%7balZ|*ikVl?C%q~g)Z zgzsLy=VXzRQ%$?;)YDkiK7%LB;xyu=|MW9shdG=sWf;9t!NCJ)O zDh}p@3cVE}f`)@}L?jHRcZ}vU{y(d0erYPM=?OwO*9rfJ!*l*@e9VbZqzKga6ky|_ z?|ROZ;;GE}{2=4Ae9r%R^O~=_9aW&HFubSdf4P6fah&mi&WM4f^;|92q;ZUvhQ1dF zsp)%5uQho(XRT8v;|ZJXmcDHXbcm9A@Hcvcu?7(g%DSKzZ0~OAbjQW&J>$`W(}NSP zZ{G9pY=LnVk(9`C&)F#Fvk#u~&GId8?yp#+BUC4N_UsG%WPz0t-D*iEA}*IV6m^5w z8rM65WXgBfcl`2w&1Spj&;R5RrE%=)j^}^$fM<`VeDlo_Z+^XHF_~bH{OZd~J_8O8 zrqtV-MHb^iPZ%bwE5oKOp;o-Rz31%N1Fl}ZVuxZpo6}Y;Wm%zIg6-gXp z-GHtuH(Ms>M-5m ze4Ns@Elt~yWhqrrA`!GrOH&W{W$z_Q414@E%Roq?WITiy`j)zD8PDeEG(_eZF3*@g zc}(*7oGw;WML{-7@y_7e9<0Y%OI=p<)(`fWrh=m2_+Uo=tJqFwSDKI2;T{ zt|v|e=_up!Zbxq{C#Q4DrXd-J7}fLL?JKfpAtzs?*a^H?)_irdWwYC(aGXCrMW!Lk z{T`(gCdr8V+dHbFz(~O_UcBJ%e)BE&<%(Te)7y%(gHu8kGM|hn?{6tqmt=upzuD2; z?Rfp0FKLSk9R#Q#BFWOhKHLp{J>fk`9FZmoP6%|EAVh$dgKbIbhQj!N#Hq% z1ye0Cb&pMB{=@xy-bQe1I=pclmL31Y_=La6k2vlGvGFK3l*5#l$ljxcB(jbPng=f6 zU!@EFFXtcesmzFx5GWcAZ>jmW*Dtt@dS;0xk`Trz9z~v{cqDNU(tD5bkR%ylm=J{- zS}WRiPY?tMt#BUdrlF_`0u`aX0u;2KFb!F6?}%(qWDEyE%v3mDe*1gQ&W;I{;0Mn? zAf1GKy?#R<73qh);f#Oy@^@U^-%~d&o68&O-JWxpy( z-m`5wCPy=xuH@v&BW8yS`nIR48%`cQ8sr{DiK~b5T9hQTO@og^%-Njui%*D;PcWuu zwYVZ@rd1aP(s_@v%K9> z>?(HKf+$odIe6Tf*0a3ZQ{ETYRZV-hLmE4vijx4V2Gg!^g0ATZq(*p{Oh@F?j5v>o z(~#+Gg0TiK1zrWD`IwXQBWB~2{btE}cZ+3^Z93D@c}x2A5r;qif=&xW6d(dM@DGK+ z8jJJ-;T+@XjCNPDee;g_ctSd#(pbY{HYLkq@P;If5K=H6rJSCfu_`*gd36C&k>(@% z*7BRb{0*}tVKJW#tENH5SLz!;Cx5m`1KZhWhW zbWWBYVj4qP?$FBf(bGo+iQxVHYXVhrSH0r9)z>gq{Oa-*brPaSDf8(9EhKl>7r3?| zw~~jmBldN{)--ey+NL89W1fBV0e|+d{u%k{oWc~`m4@%$R)pDz<;9x+^#A+~%j=Tc z<%YfJX{DoamfgC+SjqJ0l$ZA_Uf2D8MG9bro+`ewlmyb zUeSBc(Zd5e8lHakoTG=wjAkieAc^xBImtQr=pi5e(MPBxrF8-8bw#;`{l%7(Eat`E zf5-c`6}xRjC1cEJ#`x(|^7CVEc3Tb~JRwU)++Mz6yIk`4=pl!bIVbZ8w!h_g0(~iX z^J>jv9&>s1OX}`|w!7o$X~u)2F{||oCp1w!BAiW#@*EF%=@3GI1Sz3us==8khqogL zWS}u-P^?KgWcb(nJxI+cO-Zz394K@g@pZA}7xg_a+ckyoXlMB(oAA#TCp`5bkpTVu z&F(x{>G94Ic!&DmsK9{qg3v4GI^y$u&L1C~kOjlB#7e;~l>F`Lg0<@CZ4Uy%Fu_WX z(2~Z%y6b3+r*Rgg1Ny$hA*s3=A?<((Lg1Xod$7(D1QDarjPobY2y}v{=l#uFf-vC^ ze)v-!KX}B4Cr8}geaFsk*qM@duP^AgHTU-$;>nD4-EeSr4meI0hjevEwciu!A%QwM zJ*6_9`{jo1uHkpDSKQhdbGRTGW!PmwbK_C+h;P2SXLPjS^MCvW&wua?9mH(6Enofm zCB2h;{D(inXBkHi&W0C|^%P}8G|pK(JR?YQr1$KX8^)s%`Fw#315k>(tZ-tmcA2)v z=PBl7!srK&@tLM{6@9lOXliD0#PZb}OwrI+9k%HZ-&38!JfUyuVX#_u6x%&*S0Q6T zCmQ5-zb8slgc3OKsM;ZeES$r6i;gq~j{}?o*IVjMO>ucgv@J;bj)VD}U>p-f8fzWS zd4%;SrJ2rW7#uPR&_Rq45xucoy?aBkT{0epOs7+ffMgUA3z!B9Sr&}rjLB?DI3K}- zLyms%8A2&Wd5#(-=s5cR=khoV*0$saQ)WlU=pf+j%h#MgIOpW)V|pbCr-$fdMBQ07 z`x>Jh$s$2SnydAW+8cyYl>3gK|EI6{>L1?WY|LgK^40r_T2I(X=tDsthuq!Ya(Vfd znr9z8Bn}l`It~tx_~y4?)9wqZ z{f<$dLC3%n=q<)OqBO-xNvK2GzNepO9RB2U#9~4`&X|ldy6uLrZ~5+T{*JWl=~gQc zj!q0NP9I3(gAsb15~eZEI=ZUGS%XxbFaT|ON832s))9r8gTn(Pj@4#|FFTHo4%lrQ zs?HE539dINB;Br|yWeA~nkve$NdV5#HVs;60ySX6WfYQS8Tn*_kbp4IozPm zS*-PhD#X~HKuWv_=$yqf6tzSUlI0N>>n&LrQu&^paJ0(Mdr$}_Nz9W;PU;3Xpb?%s z>3Q2$EO#YH$5h1}gb^n}K;}W)!Cq`77%@BqK24|mqtTq3yGu5J7lKk4UKLBu^eM9U zG+jj;2h_%ZmPixe{a}cW!UQFT-_N=pV|tvoc;V>!8tE*)X|TqSO%6z=hh)_u`NBb< z>B5NLy!ZwG^o!5gt?$SJL0~Pv`}+6n?{E3y`3E@DlO+>o2M@X56|~Ks+rD96>`+qD zZ1zxh^u{8mBl5t}e!Jx3^CKQU&|JNL&+2YVyKE3)LSY-))^q)4#q-4o9SVf7jAt32 z{p1ITMDzaj8L(US;8 ze!M_R4PMaHHEA-!Vd=V--p2^VkR0?L9fX9+(~naU71Pu$VH8p}4cl!=bTp$}-q9*a zoCQn{4lvH4&yPv|@FVtr^IMWOr|TMQ-ywm%>*#Dx9E4apuHL?-UR8K6IG7%Dd~(F* zX3f=`OSFv8LXbp?Zs)jpx58VC6qXO3onmO|yFJ-K&eK2q0dMOHvnz4*1aVARJ0|0p z@oYiU?C{PbL(g_~%h}O_i>r62zT>0CQ~vIopF`xiy1FKo0S_M>(ffi;(_keeVL+IT z5UzqSM2Y|zNW69Qoy9wkb3?ew8HaZkoTq79(z&GRTM{MlNXn++U@@V$EidoZ98V&S z(-^HiTW?s=Q@SB{5hpQOm@t(R<)L$yJMH-Y?r!XEn%QZWx$bB{FB2I{=3-; zrwqEY%J;nQYlINYM9io0g8$@i+2iP)qw$98Vuc$Y(L3j!;p_ve{#V;OO`v`6#8^dbYRkIY?rz zU%X_dB0hWcjGMjT=;Rcl5n4*#yu9J{`&V?$U^?1uwmf?D3CG6=kc6zZim85tPYuh} zJ<<)Ovhz<)kVf$C%?8t0Zr`mqI36SW;j8GgA3jHE$FKhWD{kK2qI*xh+ws?b_Y0yZ zV&6MtsIaxi?rNqdl4Vm7HOMVR-%|IMY?32xRT# zlnhNi>nuVJHQ!pu0S_%SrZH#uBy^mSx+4@O&T>j#NgfG|u-3;R$ts z(rV!Al{Kl35Ede#Xssg&6oKmA>|xnQHm94;p8o0{F-J@@ag=uAiN z9DUnzbac++51;eq%`5yYBRx8xuh+=28`hkNyLx-$seJ&4aNWcuW8>Gn6jm~ z-7#LwQDMM%w&2a%TfYA73sN27<)GYKZ%ZaKMX?{+o38f+UeFaKaTpNg8P#r2?;T+f z(iwvu<+#Se;h6NnF+Psit+y;MuaK=JY#qjIsaHF+hjf&)+pg(aPkNYByMe6itCn`Z zW0XZmEc@-A*hy?FkTUS}qGwa=*p~&J8LTNul7MwIbwh6~)>@pmq>RH zfad)b@^(#76(}q+l*l+B$`Z24m>`L8685_tw(o{jRuT`NfI`rBmV@~M;RO~b)_Y{r zqY6XhBz;x0-&OS1QA?OSdO#}$*>p-AM+hMY07vKOn|`oIw;jlVWV>Fj*>5WZ8YvXk zg3=n5jCk?&HD%p0Kb*2!@3?%w#rA^vVZ_6yheRPf`{<10M-wVjurBVXeZ{(6lFTRk z^k;w0XP^Cq-M-?@@`|rseF@6oj3Lf4KL6;4#4hH~fBI8G3*UV60*qvqEx5S4rWXN0 zm?M-PekLW*k-}rKt{?v0t-(4&7zF4rL@>+&0vX_iq;ou>Mn!Q!DT2L!XI-4^L0n+%GA&TdH!;FJ6Dio9!LTz9&97;;P&6Uw;2_lf|ibAz2fS{3$}MFE-vnIMaw9Pm_Iz_(t37r ziX0~-u_BhRyjXE}wItFZ>upI8rX*=f5(NZea4T5r(JEjv9`6pe3re=(?r3TN4OPTU++q3eyRSZA;gB=sj*z zvc0*(e2@DhlysdV$}?8m4O-4)=4p=GRn(gr=N-m4oERE=&RL|;2qaz869xey(!_y6 z1`wwqX`#9J>OIr5BWPO6&7MdG=pdjfD+mQT7I@RsRxL?1L3m4NTcRXJCW5+ZNG2&F zPw48Fzzd`h?AI&$v|)B|NFXGglIT1|L zAvpYy6J$Ic5k@iBS1<86PR`CK?$@lBHNXAkYh*U%wkRotCBM2E^yY1dX=*xiL+c!q z#Y3!+lx4w4k2yM7aC7qx*LA$SyrXYhoN!#-ta<*!2R!@i177^q-;l}1kxZ@7kKb-`w%X!I)7# z_!PXB7zI0H2Sbe*SP;T_=II#i2lI5>^{m^5+CcAmtkuI?z3UO7zzc@}=|C30l#)P4 z7-MnHBZUX&Ft(?vb{Ns&HT1s4*q;5qVZB=N@c4kc`&(Rj&u+V?lko3%|A6TYt}5uF zfV0z6e*5ArStxjTcFO&J&p{j^wP4qkJbm;bFW$YS^MWYW)OYvTxgo17^67%Msfk7zT~p$+i0Nk^-~++_>JBA``ORj%;V6j7wIk?TdcgD!YXlu0 zwW=A<#_a2YI7ta3&B5V>?)?>2QDdaSc!A6w9&q%@IpaqQlA|0KC{!5I8%vT#-+%r) zw2}mIK;71OXNZyz9cs$5B#shHZP;8bX*MOX3TSo}t~1ozl44uZRy|!~z)6-@OYj1r z6k!q&h6zP6INtD<+p9Ya3LOM^*ONpNB^)|XSnt@D1uE3UX^P4s(3&I-8O0&KsHt~5 z(kLWLBI>F}X+@wVP21q5BTN*tgE39r&{Z8tl5j9Tq%J#5ZP*tD>&kHY(L*d9o2z@w zwxPM(6pvq4K`5R-4wjNTJxKW z@3?Ar5Ks7<-+#r$rXZglqhpQr9evvmnTFxji1J{p0b}V+M{oLJ0O*E3xAfwB$DhYp zj~9|KN!T|%BG5R~gXxEvjR?R?5QEOD>n&Gh!OvIMtb|3&Vf!+6lGrP-3U3tJNn#bE zb->p5Y>gv~A|j!gCMlxxSSPq_D=O>3g7gyIT8@$&Yb{DEIt0A|V=YR6(h{vDff{H{ z!@d5!hr(L=t{ccbU5CUGis9PuzM=IUaUNn?OCWM4#|ONwulVbC|3IFm{7*mo*E~J@ zgx-f7W=H(bfBdge8$nVzP7jY+wtEWMaNlfr@#Y(vZcpPo>b_+*T@Z%}(ZYM;YY?CUiT9GW zwHVW*BS~jllnQVjoEK!H83X~H>+!u|UzLnLdWx8hxWBul+ZO~*&~FRINsNy501Mr2 z38W{96I^HUt)nYCgp(v$Ml{aozt@a~aYU#?%5_DSKxs)7g*e|ao@O8g)(@gh-}l(IV|8~!XIexYBEpCyOt76nM=@4Fm}O`M zwr;Tf5HGH~0lZLbOWL+0PE+unC=3|SbBwF$`yOBQEU$0rU5}16j~>l=`gnno79~4G zQ6Mhv86)wXMw_+y?t`II=D z;X*+z8*b_yo1!GsFylL*H=jt6>-mki?=Pi;KEy1nJgdU$L8XtCf=^Er=+$$UtlBSM`J%8*D( zGB3!(h(IWum)On_p=qS&y4zDrgTtepWQO8#I-{)`S`S@o5f)6P;6 zj-o1uJb>?!(%@YW4nnO7LWK$y4hXW8s_RKy!cU%kj&_FoYDKLiK2B)k5w(E{DL;Aq zW278$aeaezg3mtuf=yHM`uZ&$4(Ao3lU!e3k)#o!f_}52+3g8sfU$zY2GkXtj3XW# zXdawIeE!47SkW*(hgU?UN4?`Y5pKViU5uC-3FQqh{4 z2a}wU4A4Rjf6ob=P%J(CV!Ppe-*F&g{*PzR_*c^tJ~RR2&ZGVCNuP9{4|K{WqdC6! zG}a8onM8BzYi?{yXYGIsH-=}?h{rNR8c84&Sr~FKnKPZv(Ls!L4(|<83A(OfU+oc6 z;_!HD5jb!brRDb=J&z6tIjzzX({&sl91--6Z-4$PbnQuk3^AJU|N4i&-UfDesn-l&-~x5-TKs_9y>6|M*Y-B~L&2As;;Xh^*-6PYGcpNi5A2B|P@n-m%)l#Aujs{8ui0`#? zQiD_)Q&hA!OT@0i^&L9aRP`R2#dLv!an9uYlybL4^*!2Kx}s#iS`w%TXQ63&Ol`hD zy7xp$H252YCyFDisR`u(W7*toX|@&Bt|Uowf*?c&^e9@wNs5Uh+$3lIgO3Of=5)ew zcfVr2+fy|iy#Z?_RoNrFqAu!z4AeWi&SIUzv@OaBkdh=#@Ho;mqU#%g0d&~)6x)KX z@7Zm(nBK5km)u<5vtO4uYZ2euX%&_TOS4{Mi#@LEAdBcjjg2&6646&RaTGCIEbzvm zbU-rBky^9gl^A1b+796*QcI##(+P)6;OQ4f{O}*0@!5~Z{N&G0IeC043TM@(wkItVWL`*oI@n&_y)$*RR@ev0Ohm}P zSv=zZdh!YXQ+&uD8O0Yi;HSZi|K0onrz#-K0Ymng=(OV}KRf5} zkz{(HIXyq1s(Z?|r);{xzTF#k+bu#!(s7QChJmfE3nDYjE3{U08uEwdOrAWTaTeh~ z^#;*cq_G3dSWBFgOy);86t(H8nmtOm;hLx$Mw2$!xL8Dz8-3>+XkieP{Q#B*Sew56KL`zrgMU(YY3x3Fd1br#yf&Q zQ|~LHNlf20Xc^$U!B5uphBQqn?(P|P%~k^^KasdhEacoG#-)-~OJhn4}|ZikK` z%46gQ=T!DBB1^EVEwbt9Hp4o^JCAieUE33c8tWxG3fPwgSr*e}Fd!2IHn9ldXm!cy zdBSY2*etJ*enc!{cH0^oDeO2PL&J0gNrcggZC#Qi5zEz@SSQTqhir;dn)N;ZkH7wp z+^^nqbaDnV;!l41XZ-f^m#iws==79yl2G1n*~@_+sANPaG(i;Nk{F>Seb?f>8zdlI zPwE{)XsjKQe?gQJD23^Iq%kEuvOvQ{I6L?N-pAcdzWcVHa&o-j!%s}g4{ov~zj zfE0$_G{iwp-PHt2;jHd@olaOuO^4G6^fk-2s zo_|IZrhNJJKk%-(Vm5=OYAElPRITOU(K(F}#79%)Zo}>S6_;;sIX^kXbq;U@TJZ4~ z&zPQ^a(%yMk}Fc>2vSWiHN7bi%7W1YZCMI53Dz5A5aO_$KY4^sL*Bi8OFZ5Z1)9o3 zxvyCqAF~TAfd{IVYPn}R8bJ^twZ&Bp)xIE)VpJZyotg*~SIZd@A zNn_f!AP6LRG-$r_agIVz)h*S&K;;Sjz8RF`y~DOW!Vlt)Bulw_-!L8LR8>iwCDiN* zQ%$#V=w9H5AOfN7$&{j3l1)_;B_ZMI0iFQw9lC6}eD#(%iCLT+;v=AQT)w|W+Tj5e zDTOgDNvx5!XSZ#sn}+N-$4SeJ-&`O{$yiPJ=F1E2_MWy^q)(1Xoe9ENXUpm9xu zlMY0EhHqbg!}0hL)5!^m3Ax=iL;`FosOy4hniELP?frYSjPTC0-QBZaE$LP@2Tz_7 z98GDOiulPx#Fe6aQP37}`+h?fDlVFeBnWWcGLAdiKw`3l&0T>*vfVYDoz9>!G^L}j zJ9-343X(XZc80pHDXR+MdYXMnS2t*t5yc^G?+|f-&H}Xbga|}$ar**qdOF*odPB9_ zBNib}NO~t&u6Ec`aIlzgw>R9}-BA|?TQ{uk@33OvUs~%C!r{^AzO%TdM~wtw;Ay)h zS(>o7DN09pxuY!#>awQo8scckeCOj4y|<`9p#ud%VvHw=B?t3^p(Rx{RQD}N$miql z-^`Zre1!KFp#sL!F?C&&W+Ps|e#eJjJYu~m(6bRe65|B6X|Sfl)O)h=0quB9l8w3F zY{|Vu2-w}N=#68%SkT*!(8KNB{U9jygRxa>jS4+lT2#GbQ2a#`yIe&PkFaW4pQG;{J}OD&uJJh^8x1>4=wK z{+>8bn1*AX?4Q%!e}ndp_5FtF(Ft)jB@9cN*H;7kkW=}qv(DXG*1~kPUolodmn9ip(U5E24gY7u}XZNsRPV z((^aldqO1t;_v}Sv8Jv{v~?s%(p)k*OHe>8JZ;}|=M4X4b;ED_ib5&!-t&QnfBEPM zr-baPn%Zh^cP)+a2&K_7ptnP_#JUcRM=Q9$zd^=>9Hj4h>ds;WWND0&mhEnfP7;z) z4&DwM5(LUCs-~o_cie97P%_|b`hd^Qf5hsxTgrVc;G-0*g zGM>b&${pq2Fp9<$+ZAajNP~cIG2!Ckj>&Y2RGQs-O{Nqwh%k6Yvk7fqBTRr)5*dc* zSR-O+ZBI*&Oe3UF&~`}c2?gY7M%Nj<^|Y-)6OiD^^OR-Z5@jh}661tpv)VDr5~3ue zD>{-aBNiGNrty&NZz{rEl4gpo+mp{nFbeVG1U;Wp+m1Mi7>#n;z5y@kjYFsauR`|w zn!dM0Dx&Q>nzkhfHGR`k7d6$s20ze(R3Nd!69fU1*(zzDEd2Z+q%}LABj7NrOkHVa+^+*f7lWdgaKa@-UZ|}e5+o~XpQx@YH2h%B&EFa8E-m`2i zU$s4dx!v&da>Ldrf*SrXobo?Ec*ePmSzd10^q%kTSLFGGNGKAi&_a-f0b!s~S`Y-1 zve?k|J$>7tq@XBwbk-8YF+wWZs-kb}AzT%KlaAi@-z&TtCetxO2)3Io&z^kDryu@^ zG>IwdEwPR{IXI=*u6g(F9Vhd1M$^NgBok$XD#rqwqNFo${^SYme#iE!mz3YVB}#I} zkDt;CO)`%OCYrKwy!`HlVsDwu4rm%j+t)mKJ|~PNqe+HTg85`JoOOkO@4bopVvF?^ zS)zwkpblu;VRtV*;4Ib}vc(i5JwcixkvL;fNq`F^$>9QN4Ex;%BOKed=HU34c{b)S zpR!qQxq7#xE*duXOGdLXlY<#yJg_qlPmXccVVjoQ+iSGM;ai8w1wIT3LQS!)X^TC^ z3)0g=0vXV4wgkN+2{qfQ#3~5nARcUlq%M0zWiS>-UJMN@=LxlDfxt_j&~wz#$<>Kk<3B0|ILc!rD;HoY5$M^dm_-ZPm^sKX1K>*-yG zjzfYtL^Tab6b)Y%Nx*iuVX>GG6H;1y&~tsgrkX@FP0w_mQP-AiHskQ)C&=*#_AM`e z{~cM$Y>5~MeHYrz4LhD1_P zBc-PC9i4HE=ZAPSPR87>wscj89ZNRi9xDpU0=|9qHJ^X>BW4djpz$qx+v7XWW_`!~ z?JYh^80Qi1FE7}yH>4({ST0dV59p#1bqCr**BTa!1^aEya=T)^FGjoWS(-Z?Bl~8Yu&>(zQDr>V2;Tqt745b`JHhcyh%^Tm*L_@P*}q0i0Q{CtlS>u6tU4XyPkP=iqM|EYH>;+ zWJqTke)Z+A>6#sSzZ=fMqT%Vm2`^v0BRiR)M1=P}MOlE;M0!HkS6p1bW04-wloj*E z3C})!&a1D!WD{w|eahzL?RpJvs>4+DrbBA{y9_Q5Ml5sJogll!I}*Yj9mdL`diap^^wIsP{Fo3PDSp zEy-iYXf&d4njz*LDG)<^Jc(nPvY_ABTs9k$Jg4t1Ra2v_HSAVfj1$CTN7b~XM@LxW zvCYr{h|-klM^7pHp8fqEBL!MW>bhhxA9H$o$mR77O=}5)fVOKX%X+BTIzylYbqak$rSV^zr9xF0WX>yWPjYP|6ue;faEf@o0)yk|@vEHX8y7 zJ{ByCEl0EG1TrC;A2U8YW>fDe8iPnRZQqekQ{1X&xw>YSOi88*X_iq|74vjN94V@z zLdPNM`;wOFTU=leR-=`tDGJibjLP(k0)^C?vbF3w%gft6&yO->mhgVNM{d@9bTs4q zqtE&Ee#I|#m+Ylvx)|o zAF^|T&MTrIW?5A9){?~vY)=#_%5uwob4$~=v~5Y81k^=Cl%)dzvnjE*qlqMkkIpH( z9e(1-!Wh*;-GZ8in5JU2x+j_B%%&qA96X}fmt@f)hrtoIm%k={5YYLaFbgr>VCtIv z?T#dxaPnZr&GwesZwS(!dijR3kbL&1Kf{GN(a8yq|Jgq!{PG2f3h355Y};{ke275P z)GgKB9?^MZ7_q+D;!xbYz2WTeh~q`bRdY*G?3phP2}8qln$Xz|$#jA`I%K)MrS&~| zk|70%C?rTy%I%J3vm_E8*>q?T&^0w$NMba0StAq4^eiT73eqrP)shXv^&)D7EgBIAn zL+OCgG~?>#maaB*O^-+=-VH*-w(C$*(pLqER#aU_lH}Ay&G_hus;coSq^LWxEW!3e zfg$vkC=S?H9ch~rh=``{@O6)=D!ks41R?923lh=u#N8X_04xn2_GIkS@k`bZ+B1*N&Y zzTi!=;CvCXyIyj-_yE&*?r(1yKYvVJm5h@Zl|=0BTK4x_KKR3rc(r}OX7wd?yCzV3 zxW6RW&#CqW^)*;)nVpmxX3gei%f~#)qcn9q&I6YY)jO6XjHC`y@`53Du zN<|CRo{^%DMm<|{Q$AdRDzb0 z-WsAfM27)=-{PIcNrANuMX@8z6+t4vb#!%)3L~rxSk^5bfmG0YK}o{bZz_)GJ;$>N zUDwgp4OP`pwvOo};a`kS$Wuw6B^4eaLSkdtt@o_^E#9;P2Qdy%fn>Mq`Rb~m5Fxd7 zSRw)y5NU-qJw-jl#b-%GEIW*`+}vFfW&x($<3&$VRm5RRq+^V&*q2*G?2th~U-j(o zS7d683IfK52M{URt{5KRY0jgwN1UEK$J8}Vx#!@M6LcD5M2i;^ghX{cqSmZeYpS;3 z?(UMJ-_aF&`kU8G78S%B+V6I}{;U7O@z4I4ZE?+g*I-?Po22Zs0Jk$-fAuZ%#h7#) zp~HmH2)6q@vx7ONa5Sxf&La^BDN#bAOEsa4>FSDjnn7#WwH7~$(Rspdxf;qTTG3WDK^~AN zDfxWP`tA->lpG%%aBwtbKF<*k9?(tB$u}*pe*X*72+HdX%bRzYV9EGVh}II}1>?*S zPjgH%;_kb*bhmrvlLw^ph{#Ch;Rw-Iw9AU$R=>o2qKW1S@nJ$$?+JoItv1dxq=T+% z=%`UTB%Y*1wZ<&_p~&Mrf!3ts5mi;wnT{kLgr{X!5RlLAFk!!05l95Trg;z%Qdp1L*AcN8m+P?AX!QtYltb$|{urSoLzl+Jpt z*A2^k$K|epFdBrDDkKeu*mc=7!wp~rd87z@kLx?GmN!W0kX?%~L;t^Rda@*=>pM)} zV!h?;_>?frxVd{vl4w*C5lu5X3|+aWE_W#B+1{*qw0Mg40k3bqBhF)797$FDm@@lJ@o*XIjF^luan<9~>}y zbjWcwVlLtG*S|ptM`t_6(}=UUJR2;Cr-G z81cUq;S{E~?3Q~X88V*CsJosd8a5L$lgL>_KaI&hc}jkAK(*c@?<(Z`TgGw24?h}l zlJ>+IAa|Jc72ec@iQ?e!fUd5nb~^$cfc4m}M+w2=$q{BO*|l4OND>8#+js8>-5_D{ z2BI)zP`EnUqGox0gZ7Y*^T7om6mgWGg8*yYP&qX%f{yk29`7x#@4y+NKtt1@tYg1x z=^IBDgk*6;zo0`{m1()lRUER}LK`%75(xh2R-F8f;Gm<=`@`7y*UtjI` z?VB}UUe{b!3LA~-ECWDPYot_^RfY2&Zv>-IlVT|MTW(iNsaTIHXghq<6DAQI1LyzY^K+t9 zv)%9MoMqp(ba-s%2BdmZakp7={_vF1WK3r|y!3Q!N7q}lDeK|0InM|~jR9OB2*xQY zj}UQ$={wwZhch6G5*uo)mh8(K)qUSp$0I~fK1#8^ClDddRpgTizlkv}=+U;N~A{@2aVxxX**`#a?H1Xpi}qhr!EA&g?uB;|g;VRQYOgC~a^ zef9y(wkOCnB2w&E8`{+#YipE)Fc~5BP+BrogSVcpELgQ2qwx_#Q`&z2v81pb>bfNi z0u((?c=qLn(dY=wzAU^@z8jH|zI0|u1q9qZi}K6b5DRkIE%z11$13U z5JhOINg_#*$GFXkXFq*HY3;BXdG=_K;w+b}udnfupo=70C`4Ogg8-uiG752RLl%T2 z7y>-4aX1x13X9_tO51XLcFs*-A$?C(6$DYpcs`*uJ+3I}n-2T3lKahyN2Wnj3uBodA+jRs=(iullwyfT-X)pJb z7q`6s?j?m1Jp7A4;rwSmA(~HEWT)I-@9@6lgM)}aeEJA=vqCQK*=?4rmlt$hiLpI# zsGx6X+m@p5SWFM;_Iujxmb>d~?q6TfS3PMQvwn9)aeaeQk{}r1D;~%uIYFoqW~lkP z-r`J;a$?BRJBRfi+Yc(DYQJH>y&VipO+_pq5{@+VXl1F|nx5g)TmaKFBGVBLLD@Tu z59uwm4OkZvggHG4UQZ~=(8-MdKTrSFV_BA+*@9!KEsr+oGE`%-M#``8DIw z@_74+m@Lc18LOgZ_wEm{eb2AIJmcNd4d35?BwN7k;g-`7P(@8~b;--W|6A%s&2T#L z^2Hmvw&RCC{3H9%A7HQyP0u(?WumU>Iqn-|NF*PoB2@GwqY+9H2^3Y%@BaR;Nyigv z+vATNE(V+r=%S*yIHzxp_LyQ3tCzfIm5v>iYV#Z7==275!kBy1mTTGD=oyVTK zX>dM*Nrp@sn)bvP992=Vf7p{o&`B9eDPgpDkx*HN&J;;&gpt$noq|lHX<#N``SuOr zVQilb zGb5dEamlm3Jf};E>}8%;$;W=12bU?q69e&70TP z{A}-V-9#gf8myhz`h_wywnOQRJO$2YHA*O|>I}6!WBcPB#jIxbx8L!buh$s+fXMFn z#EzSGM>&)Xek>49-~KrWVu5GnHl@`5p*NFk71z+=!RLnMJfqqLzPMrN}dM1<%V#uKvBWOGG7 zc2q@;35l7`uu^dt23i8qO-#X5C=wEt6m^*)iP+#NrJ1f2J}{&Kr845^AVwn7wNFS% z78Bhta5#1pnWpk1o2sPkElwnQ+p=x;C=uz7TZZEkgr2@_h{+(0ATKINY66%ffeW7I zbU@{bdNl_v(Wqy-j>7Ib>arr01D9`KGMg?Mm$!5@D7iF_v6eeSvaNzvT~=2x#-eDf{Y)tb*A-}CbP z4RNsC{o&8N@{XK|(0g@p&b!^7B&RFP#BfN&lrR#MR?IeYx^CiZ__1YGMlRmGWUW^D}3)5#*Qq@(a!VBAO3`2&XK?U z8h>?8Cj&RThJLo>r)FU9JWu!ceEH(HT)zD+LyXMIB}!=arw1PVEwW1d^rwG9`$#Tx z+-YF8Sn$PP{uXBx`D(%Jd`%n%##4iol1yo;tfnYy4!b9u>z_$Efs`bf$v{Y|x@PDn z3QY{2;Ci~#6Wjd_*0(hManeLd$h>22#Iy%*J5IyE?$B`REzK~pJvAJ=o|r&LO+SJ( zB_4@)4y81vC{bC-7y_qmVAl<_W5OdD2G41*Q$Q;@G=c5o4a9-2-7++L+|bgV8eB-I z%pfF0B1$PhFb+L2dX$!!+z_lo_(ZmD2j+TbU z-7Tl%p4IviQz#N0B7;O^J)d)N{)*-14QJOcKnDr|hg-qvX@hO9SiSfQF24Di>SBWn z31dpCdd8_~kT$aaTWhN=-HL5a56>08nkgg@kz`pI!^^9s> zAwnPpM>U%fou@xcoX=sfNTsmO)3iMyC1UVspCUm>)S^Hb!)fR-O!y^#96=fol8f^T z9_*fn@37?mUmurA^{e?0zWthaKmBJu{bZ40!`p9vhnXd^>IKF6 z5~n1?X^%~wr*;cNhZ2I*?UAw%ym@g>wJ^w|MX8d(I?#fmEIHbdUn0ONu-vxPk2XiY~_%Y(Y?pq4jiK z%WPGnq@f!lV;ISbf~?3H$B`&t`?RMyo+!)=nF&lLk-`ORH<0HA!UsxGkTX?x-KjyQ zNi*!MMG3*hiz{}!Cq%9%IA~r@?D_<4KjOu-|Cc~fW~|Rw6A<$8KzAHqgcnyEuCJG@ z&t`-aa6TcmnIITlNAQrGL89oI6IpI(4?9pJB2%oc-x6en78SPj*mgumf%b5G+Twx* z=eb(1_~z{^vPq;nG0`%O$^}hxKxcqZJUyIH87L{}+8(<<@$SF<17qiS{oS{)w>&e{}3VICeKjimFD`p2yRB4o~mc)RN$P9&bL;?e{!AZn^y84Uc-@G|RZU z{t6*V=F4*~&#o!8VK|*QKJ6etvW~;emT}+UqQlyepgMxN*FY%nURdf%okuX4*QnV_DF07go{+`D`tzA zcq_>3IbVMLC3zuvfByqrJo0h-nPKd(hdpH>2(jnKKmVS8`MKiB+zd7n}mY5HaGsCCLm%#rgRg>TE+nJw@i-#C?ooPhA*Ft=NZt5rd!4UacR#*pY!!i|AUX0+o# z5;;;9&_`~5c_7bT;lWmx;%_A2?f^n`7urljvJVvGc5anW%a8*CUUXEhfuuQ6pcp~LmW z>$5(frJ=}5q|j4S_L&9foF%vcg{L_@@$h&<)9x5-2O^S5f%bvi2WA4!jO0a8a#@sI z<~dhI$wiTIS!P@mIYmk=v}9ovnYU!gv8psLFXpUg8GU=;^Zf^orzg7piO20P+W=GmQ$k{$VO)Fuh@4p`h2p%bb zzD0=%G5_-qKawq${N``|1FydNORDvT>^!Fvd*0prfs;E>n~J9oAKBEJhyIoqzj;OP zPdt4*lJSDo^|##L4fru5dcpnY&pbZdqm98dE%Mm1sb`ciuv*QydVR%&dMq4FRd(ZnvfN7MbVMb$vC*)Flx~mSxQ56^rEz?;PHP zUM)++DA^Bg8W`mn)qKg=*#FI^&kK#s6>^rNqrjdT`Y>?u%@>^h_%nXAc<-3cXCNg~ zYR0gqA1$tT{OVUWRL?MuMBCCICQn}` zHL@rehd?!}P_sFm%qg!g(Ss-JJ>Bgs`Fu&48KlnWhmNER?(cRis*>h3GCDz)OF{~~ zd3nXf*@~MV-!orSG*1WaAC3&`g5_WQibGV0qF`Rm2u(}9+)R_#^Go`nV{!Ht+mD>R zdd2PTo}YjDnZI1jX?7p@VHEtQ|NH-gR^0J4d}6WOQ0R=*r0M9c4XUVU`aMMqH206- zBjsvN^Yn;23==R@EAmp3NX*X`#9j~&5j%|Jc}+rM%+wM05sWD(lp28)1x8C;^!TA8 zGX+8lp0;<4W5=mGP?vM^qMX=p*#xnaZUS)SLg3s8QZO$vYygVsPKy9JH>BVhZO6l* z!;J$mIPB2U_a~au5${J_h*&qBR5DYN8I4aCH%6o=kRpeq@ZRIyh@Q<*658G~o0Sah zNOA#X1R@DcfU#{E;z;x(%gZxvKb;U+hAP7(sxcK87jIDK8!|VtJv`C2dwd63SJ5{o zoOI=H|Ic=C=R##kJCO)2?$g7C!TYQ3CD)K7h<@u7s>4~ASvQUZ9kxt zp=l5JaUiP=s3~=8TTAc}-R*mlw$M+xEJ?!c6=s%Y1(j)q8jtH7hfBXgG6!U6M?>)gMjLcy0 zJbk?5)mLAm>jGI<*u#J!p_L|=8OcS$XlZsm{Rz%4mL%kal^Q)!$jKQPLCELC6OrUW zD@BNbzVA>jpo~V!gu*en9?>XtULv(2Gln`Z(JCV|8Bt7x-{1#q@+6;#eLzY<*LHM$ zGp%Kj45uUg>4bM3-n+?I6cToH$SBCOf*8SCk3^Em9H~oEoC;tujEFQ~`$(}^P*pRc zw|F}M0woP11%`2?s4pm%CCxtausc9ZlqRQsbB>4-6$GYR2MD174S>7ITVa8fXOLG#d1e)|nk zX|l2+*(1SP3T=oYBY8ot=crs`^9Ax;G7R@qe&aMFPAPngw`#pME3&3L@M z+x+s_lFi!&AI&Yn!O+Kk2~c0oFo!Ok)cweJM4*Wq==F< zj?~KqqRKeFf1;{##@yhYBSX>;BTds&OTjl^zv09CPwaPFVoDrO9$X|V3cT-W4+lcE zWWJhnyM3fIHK#+%Y*Uac!EV>kJsqfDY?v2w_WM0&=dUTN1&6yGMOGoQf~=Y$95hc) zBo$y*u~^lNUC;i|V}~%Is8d1)Np4EIc0ejanU{>wGTKNsdPILh&uY5%i0NB?{Kr2r zfBAyd6{d38@u6 z#p$>$1Sw43x|9;$TC8_d4LkbjA(Rrk4XPoFuod;Zhk{6~KJ|NIX=eEiJCzyFVnMTUrqi`f;&{ejbAkIoC+ z(DUN@l2>2;J)_Zl`t%D~i1>b_?Hy0MhH=Q)Hv?*s@O{hS&{EbJF?dQ{(%XPZ8hn~& zI6`4`L9FJO%_}+^82hJZUqYfeOcnE4J);{MUcLE>ZSz1h3Q^1n@`S1l>d>&N3;Nhk zo3%PeL_s+(>4Rr?dymX3?C3b#tPomKJ)4lLS;aUyv``spUNFi?a1NTDm`-%ZBYCbU zbb%I%=GbAh!U@ldzxbA+cewWxOFwA^K2Xl4m6RI}jM>y(Cnih=msi(({(ML9kvubG zMltLU)E8%n1X-=PSg(owo_^~Xjy={YTsP9*Z;`W#`RWo?Em5K%N8!63-&it}ktN0B z{+`A4hT`QF&BFtO_Y^7N186A`nM7*Ed^M+Q4^vcCCQ=mi{m3dR!eL+-KC*u}F&rIl zzx_4+;}*SHVCri6bB~UTS6}1%mY+Y~^Tn%Q<2r*K0)O+jf6coe{ulPoJ8q-k?Az~H zT$jB3_3xhFv~rrcoSyKV!;KzQC-%o3#!HrGYsMteB1fj2ujP4%N%hVI5Ea6+8(O5%NHuX6r7{>}NF-FIo==36c*I8# z;rYShJSlht5}cpXoCwAgh#?}AK+Eat$^aoj-oAmgRa)Krmm=ISw7Yb$s>mn&ZQsP-u$v5@i%uZ(q?o9Putq)LR<~cHn7y zB1HJ>zy1$oD(BQ6dDu3*zq#Z3^;=Fo{IY+fe0xp#_AOo(WI*r{@{-wkMm9!1{Qh4M z*BkCjjoBH#?UY=rQFy~ zZEz7TUc6wa4BMtfXElq>IorD%ii;IqgFBwsHx1|OGqSS4ng$(oMj$@d3^k|6M`pSp zj+TIe+_SEiG~F$fImP*kP*1hgkVC#KF=3?r*wLJhKqAjG1h9QP(i|Gkr)Mao}-N6cx+iN<}~{QgHLomB2wVc4E%Ecz))1E<%So3 z@oQ)-Pd|NNh>pdt-;y$c&z zxni+-N!K26V^0Rm2G7SIKcY7?HWwTAV@qC_6jvAYT65SQS>+iwf8KGidO?}zNTH|X zWC(1JduGcOF$qKvAT>S+gvtoc5?v%S3Yn%2h!UDWLPSU+p`@Bty*?m>z_|h63nFQ{ zR{=^++He3;B8Euv)3(8=3?Do~Y0|UJNJL0JA&f*MNw9&C5(0tL&({S%Z5mQaBvT_X z$TU)BbIc;(InqVP5L<$aWI&e&W@k&Zk(@r>Gk^P<)!Bv^2e$7%P*yXP%E+Zcib!*7 z$+N_~IOB`+*L?itC)^k)mlZ1Ca5#+oG5x}u|MWZbaz)H?A~|m8v2K_u%r2s=r%ICd z|M1V4ckg)h+pnpIf&OVvawET z$a($E6>X&{-<%<4D^ll~ZLX;2HTNI?mF0Rv=X&hu3ErcSTwGr8?)~@VFU}a7j{ev) z+}|@#@Z!Z8_m2(pvpHf}^Wv}mil!Oao%XCQ&L`tH9@}(u-9X$ZMi=OfBPklrW+h*J_Z^kV zsLL;@u3r4cq-A!`SimYD35~!Z4tfVtaebeEnkD zk892P>;)Ntd6^Sk;;+8@9WERB^XKpR>tFpJAOqP#5k<%7B#fRhG)OJ6&P_dnrxQQ@ z;ZHP&J;r4CeaHL%{?E*+g3LK$@Vx!%65|ubJa1r9!uydtGn7@u-W?Dm>MUdMmKYL4 zZ|VKO=G$MPS1S%}&)APt<&2`7q4Nb}GfV}dBEz*EXO|lervY7W&?=)=iTgi)WHU&X z)eK=biJn!(bT6+>nRktQI7bF$|Wvx1??)NlEtl8eu{HQsN$Vs1TXWSGdqJT1Q>yY@c>0 zrO>6M$TFgv26j#cd^^zZ4|K-{g=RUMvtHJ$RyjUZ+<&^o-`())+i&O}?g@Rw51tSL z`~8l>WXv{egmvtG_!A2S`+xl-$6szqgQpV%CQ}6G2;P##isR#f7uOeTw>!qck!22R zBRgs7Rbu=c*pwHXfB6+J-@Kag^@W@;Qa3lu&o9U`LqGKNLr=SZU{@_Tn{QavbGkmD zmo-Wj% zd#^$eCvhDDHO&E{K%pmMl!z!MnW;z#EfsbQC?wLDX&o6Sb-7Rm?>wZ*N}Q0-rxQop z9}!WblqIO?@4h@g$6>ke8Wt}$$Rfv%BdO9vl@Q)Dj&@=x96Q=ix8$pHDCgvLP2oM+ zGl(=KPfV~_pOHi&lZl`$rvaYs@5yEYp$yIf*3o>vBb^%7FVFen@4llfOZJajo*r%) z_78NY1C>s^{`L#@A0O%Vj^nl=WQlrRp+Zh-CE6%NoK8DcSx~R%paT1Chk6`w;Q_KW zad5;mT|@J7$vB+I4a~}t#t#gxWAEYm+b{94BOi9uL8Fsqdpgqfp02layOuBh-QTh} zJLC5AXN*#~K5*I}S-sqlFKVpMImehHL&d~I`BgMQ#>k2umaDBkc=42jh+Y^Cu zIVY9|VI<@3IJw>zbC%aH*nN5+n-#bR$n%2cc*5C0UKV(uTFzJ&1>-P@5)a3Q`K-hb zp3`=R`S6+T?Gv8JcKgI(5981?yS$*@8H`r=hkJa|==mjWv}`Ue8HbVnc)&M%D$g|i zc9HSsf%fJOEj8ZxX$lRjS4*OZ*wGSFVvNu2Iug~qKx}5HFWw-|E*N__JiKSLBjt-T zl-7vgaECqnzQxoP&U^Z9hbl`hzIla0fzCh(gc6jdK*gNh?Gdllw2L){KY#d<;^l_d zfB$!kdJVUwriiDQfI#L@A8`?AYT~&d@ZNd`Vp{8AC_% z79pos`2@mH^uxr}7jpWrrz!u0h>{pQ2?;V0yq^TDF-&$RF&+J-(vxaBMVK1?&(nD| zar+Td{@pqxa#Ec$iCncZ#3&|nl#lpe$+7~S7exHjwbueWHsnP>iDWV{A^40`+=9zL+t1iyt~{lL);9AL-f?v(1t&I_6o< zb1@BpWNZfl3LhLY24-bOu!2mQX>ui?jE1M5@9;umYR#@Y(v1r+hQ-;2{N_lrKQaFD ziSyt7E!k#6P-e<2%vL8xupS^%^M! z!(fR%ktfTGSh?Y0!9X1#iaSj^CNpx++Ir2+}f@q`>bpMLlesSLU- zP<6@a)Fa9a;RLoBa5iCkj}+x}_l^QBGFmqvWWq>E^ogV;N*RQBKHiIIP3S{Js7WS5 zN|UWw2vT5T8Hj239zwv6Q%OfAg-{ZyCN_b1J|PTtoO&HWfJ{iG2xERq5tMfHa+m56>!)atzLhdDRUcH`zH8CKhVt4xho#W-V zU$NiySl2wO(@T7UL5)1NI|kuVvzld<<8`3#AL&0n(LLO=nJwscPe?R&j9h>57V84X z!+~K8Jlx+fUoSa(eT}}@a8+c)_doOg?v85E)S{$qdd!fqPKu!!$QKpyVS=h8B1#GB zteQ49KBC4**qu1}jM-w1jBt8*LSz~FdWLN$3D0a*lg=-BI^9z^&*QsW(#M~eC6H1e zbpi)uqwF8TDsPo%J< zP&vf(49kdYKi|Q8NxoR|)z@!mZ-0->GwS7>?b99e)r`aakzurK%AC&j7%gd!M?4+# z#hN@Bd{o@t-O=n1j6=t2xuop}Qi4;{VXdb+rjTwI;e9S0un z?zsB*|DJ5QfDjQbAcv9mae|O6)@#TWJO2nK&^!$kC(CTM#4IjINn^@tl9V=kyw>DZ zH5psGBVinA9s;YcVepYSO!i-;rUDE;5cVB%wL}}k?(vCywnPXB)=zFg5vUgx#FD{9 zT#6tJN>;=)kkZt6bS{u*IWah-loNDCObs;zNHInU(Zj| z{>W5IjowX0W2H%AI^O%>5ke7@z$b7~BQYSKmG5L2k#MfXjYpKUEY?fLIAV-pzL;}r z4s^E1ivhWVr}i^KM&e9i?1-u|`mqHq=u==cnzJ{TeE#uA)cOtO`8AZ&HO9L@Fo~?t zXkjJ>p_eGBsf1zN?NCCH7X^!p1yA=6tgfy&fBOaL%2WRJ-!KdvUF=ci0xK20_pF0Q z7KY*2O{8m`;YLdgBU0pKd4^^R)Yer_(+|_XVK;KRxxv?Svc(2fl;|R-jFHq2?887X zj`jHrJqk=V`m1x4-(5-NP0Fpr?+2NKojUMOM;TN2U$OrzdhHIc!^o z-V+PM;`|a04N@s|X7B=vLSqReG+o<}bisIP$@7wjpT1`tTIzB^DQobaEK?k}JE}6r zC6CN9HtRLNq>pGVDM~#dx}#%Amc^^r41kt`ust!}Jv`%?M~pOt!$2?zeVK7cC(3-z zYJH8Cnh*r5%{gU}u{&*PhaQ=i#D0(49r*r#{uk8X$k!`A{p+7-+(0oarw_kW%<4IP zUU0ZSaJF6&yvGJhb9YBZ4na_uie_w}a5yi~az+duOo227(FMk#!N}<?~{B-k|S<-1iR<_VuMtIv2R&!X52sCp(}+})9FqF-u9%FSYN+H2e79T zO2O4vUt%vTx>%7yBoX-Z`3Gi~b23p=Ie!Hjby2-KWJ1f*) zDKx{eVe#r3krLhqT#QH?&`B|d1WFSkWCD`bcn|e_iF$oa(^!OWQQb(P6m?nPQ{ep% z?{PywnT)(B2yUc3HRO3le`?9+Gj2cLV&alYlQq+Ha*y2Ldd zNWo0cdAfT?waysF9xWARRkFCeL?_AN?h)Y~byYE&=Nu0^vRR2LB#ZMUWu3EJE|8fc zhUq4|dV7t2`-1!JiHD!wQAvet8|*Oh^1H7Y2gj%He<0|<`LBM(ynaL9d)C+23{8U; z3YR=%JJJmnsSMtZoOV0>uHnTOUjPZp0y!%vT{cBzPdze=R7D9&qBF%}K4aXqAOt!o z<>@mdW}*UQf9=I2neJqC}xJCKOmov`oT|<5XAHxaKTNwG-(je9d;=c zQ(;!1q$b4p?C z9UFoeNx{=~)7On2Jdt`a$Akz`;8Y}^&FGpILgeoIpE1iCH7h3whAb$ILgt3A{@q`~ z=-8!>H1=dpF{=zo3xW+~nIYe-$zo!5eNC6w96o#^)f%THhubYPQ`0|eS*_OCVMG<0 zR2T|bB2=Qza-1+IomgJ25n_5krj%$-J;iEH>A+={4}ZErr-;c7^W~g!K4TmjMjv>1 zdgSowGa+A4tvYglrC}U8w8a1DzHUU>X(B5vRBc_0h&4SN&cZ>;CUQE{4=vdAQkcuHXM9{dQ zVYSS7eYwEM18v`;XBFMkz^tknqC?LYgjt5USkW03$i>TcuP^0Y_852T}PHdR%r4(<7vC4=?0#P3UloE>Q}#l%X8Y@ zp4+Zr{rVE>nzn!B{Hr$vEQQQ)R(hN>%cVMs18jsvPNTz&Z(Efr{k zEizmN$A?GUc2DXYk2g;=w@>6cL*#}e1ymYkGD0)b99zm%AeAAXl~^B1!4hpV4c@XG z9|9>QT=WDVu&$>lGcqj+J`o9Ic{YWELSP&Qbf%{_Jk$07F}eICg5alhp?JRYOQEK> z;V>d{jg)e#y2b=9;G-o1M&+2iz$C-yJ#msVkdjAYDU?L3j3`R1?NNoMDr&Y*PZaBd z)^^lOLmK_GI=(o^8$mO4)axbHYK3*4a<*W!4%>I6G(F(71e2?&jb{}5v7Nk>*8OjL z^fF)Y`S-uapH4&{FtKFy@|wl@1#(D8G(Y~s|DxM%`SL&hJE~VNu*nnpo)>T5^5#GM zJs$5A~UjK%h(=DgR@6leMm0~}3kRnoRhS3t#bk#C) z+6<`_iQbZ}OU}Nz;_>N*H)r4AjsqfCbXHB1)uFSfd^);_7>Gz_t2w7}BCQ&Pxq*up7i=Foa$#`5?0muF!z02a zR-2mj#fq{rgwEk~Pt0o8FV7KY1xC%tuFlCeHSN%1`xB>|JylTVOi|$!9L{8ce#B@^2ot>31`lEy>W$WtX9gcUK6-NT94D01AW*0YxRDUV=im>3R8!+qpztw* z_0vfqOmhLEBnkDr&!4<^S!M_#A(JJA25&9%x+ccy!@>hTRExU&!r>6nu6Gd4QeMhb|4>$Mh4_p4@ z|8a#W7kq9qZtn)nI57Lw8^$VU@I8^~2hNm+I$JV49T6cQ5}3^3wdVfQEm|Z@S>e1T z28S^PA_lDOu_w#h{PomY9C|X9Q7-3%BpLQQWK?v!1Kn=N)vv!I6$O@w=vvGcNE@(Y z;NrzKW>(^kCrS$T4_j1LGArj4GEO!eDN#Z&Hq(cs$PHy#(4S7YMxmr2`NZ&aq`KJ9 z91hblT}uLfN?b}sC?(~^lA+rZ>^PNHL_P^xWJWzNanVi|q&V&OrJ5%GNJR({iJDe= z(KC!AWm!NFQ*Pinv8RMY3h>?|ghUE~50gzsGIa}l2xLWp?JY^^Y2cft2Zodyl@|=7 zMFhz}ATuQ)v=9@8k|Y}$81c3T?MXIZl*UJoGKy+hpcI^r2S)2r(hz!!^?`h&r|&g_ zzBw^V6+uX3k#pKVasTH(pbL#_M`G`pzqsV$iY`>I z6V1&N5AS~A)nEJtPA6XcpLD{Mb7^aDj{NMpmz$Dc9xx4ho0kPj!StV9HXk%HNx zA}9emv0N`WJ?zj?k_&;llrVWYt#*%|=5|jQNA}}@ z?%M&Y5|F6tlKrW}ZW~lx)A^oyR$}|pbgZfiVjA(ula~hTBc?79NZO{OJv3xl!QdPr zMUoH%5~j#$P7N2A-x0^i!{-xH7Ie+Xyv*>UV?CQuu3@#7bk=hkEy)UQ{`DQNe)B8z zdOej{w5F^pvS1jyM7`PIk|5Y=f^60^hKG9+5Vb(0X)t4SMt3^0x>^v0z#|DRP*xTF zFiwA$5IDYnk31WYS;6wlH~2E6sB4r9h<4=R?w<4OOXMi=O@kdr?D)WV=*fcObhMnR ziVz}MofGOAhux8?s!7)I`0OnH=&4OOG!$x0j0PzRk{<}( zL9*WZm6{v(M4KaB3$0I%lRBLekKyseckL>OqNk;PGuYSYz zmv8uZYN*#s26^Ood|JlF;MY%?1Ig-S%+p*FTX%tZ#_QxY2 zSZcWn{y^$2 zq8(5X%&Z`JORiw-2JG086$QGU5k$fUPhM7J#f;(ciPP`xOFR|>Lf zn)&&0pznHCU%%%3x8D%!lK6Sx_J^NXo?Vg`C3>DuP|-}IjKLln&aWKV!WFEVcJ#ap+$&Era2Q-q0Km0(HiQoqE^@3mww#@LZlsQ5gchi>q>zd}IGpng zZI4!pva0y>%LnpO6MPSPnum1#h%SXDFES3t9h>C_n=E4VjK?F3^$ZmhLu*+qmmD9C z^u1;N;v6qE=WiDXS%x^mPC3t5=pEsD#mg8!Rp%s%c?{uN6I2k zhZ`Uz#2*S zbkFurKVkO=bgsDm%WrW?VqA^htfr~FhitW^**4^xIqQoRnN$?#8)BZp#fs1mtnva~ z7x;NW^ogn{IH4%Y8d>Eu&5>ptsfvuMD)7N$V_>kJ*-Vh-If8$i%unkkn;fRhu)Sp% zM@)h;&xy~xeXTU$A%J)ufQcCKJ|QKCG;g zQkE*i^&?qTO;n@Np=Sl&O(i2K6QgaX_^t0J6Oas?njNd_*~Asp29s%qu?Gz@31mpj z>KbxIrVY)pVV2hn(b66px>Lvc%?qk!Ne}_CuBRrSbv)hPQm$sW=71>`vC`yaL0OjA zr-ZeZu%FQ60f$~L>Gykzx}DnSTTb%h!Re3l_hM%SOng&?a- zM*B#LiT&e=DqB+4b4K?>9uh-05ThU@fjvcj_|py5Dnq|ok$3sJsP*yq0=qV1K4D%wxM1fX0yW_(&yiK4oP3H!>?uZhJ zv-KJspfUy8X!She0i^^=No?1l+JSz1d)Bi8>pj6u zyKUc&h?pp{oRC=(7NMuLn<@*elv7PsO9~`IAGy4K!BEu?MTNHV}jMoE6VFDnqkC=00{i?$Z-2ew1H25{PWa^G>XmHim@B{ z_`m+lVs*~jufFBy<_}~+)9+4X$M&b6amS9TEb+&l-RVSLRfOP>N}w`BjER^6 z&HH<@Wrv?tlv&BDULeOvEE2P`4Jt%}^$f=ocKgIkOedyX=ge0b4^NMDZA)o#^2$Ia zd3bzc96Y4v)_vAXu)?8M=WG!4ws_k&I2l#qx~LyHA7xv`YeM zGC~v#gF^{m7}0h>TZcOxk$94bBpOs!)AcR$#e&`A9rLp_C-1NiTfTp2*xfy$g+$8f z1ABJ0W*i*HhdtJNE-$YbU61P=ZP#;lea07G)*PBWd9miWn`|h%?T*)f_Z#vLKVqUs zr@)9p;W;%O^Vtm7Wn7+La~eBzTJd6DaQLsk$DIbEPF$`xR7J((!yc^_*B2L@+=+e| zP+FjkCNUk4m*-2IhHSlJe{3lzP%fa4Bfja8TA{L%-MbICwr4(@k;{Vi;fdf9Zrc$D zPo^_I|M4BGt1HG+gHVRhj0BO9nVc+}f*v)RZ6DZNU*m)4bUZTLpO|HaSvBWo-!dXwU0>KUhKTuT)vK)~al9>7hV?UrXg=*qI`LV)`sTLuw2Y=tz$g& z96#N#`Qn<8O`U|@$6H=~`z>DQ8- zV#j_^acU0iKHjqMj;gGfsl>3Z$x2D473Hi% zRXL%DHnk+5$g&JKIC7&&A)-X0-5yE%9fE+Ck>Ey@64(@w!ePe~FrxM&%PQyL?v}$J z|CPn#i6RsXV~?`|g+>^~)ti?bPDiBCTwY$V{dj{*j$k0GGQRlDuQ~2MGi)>VhZBd# z6Sj?LFFozpV|&lGJy2>j<;eF9smkeEL9vhw^pn2ZN3sfZHQ82O-(Yf0`TBy-|MUa2 z-SlgeoDyTnPpE365CWS07L_D2XcqOHC`MkryyiM(eE-uEC#NYEGxm3%*&KFkRvUJ| z|1)bFNaX@l&a5gKj}0L?WVv8?+>$K{+S5q);elLc9FHyY^@`vE&U)NndE6#8uU4os zCxmHrlSnK#bGkTUa!oP?%h{T0wt=zdc=y2e-OpH~dGYtZL%5#Z>4wv%9b75Wb6{ux z)B}Rc7gy}&HDm?L^^&n`sq&nDdnB)FRFOD6ZRwx!*pGJ)y!`4do2zqv{_p>cvKIbueVpK|&+LGhrcg_a;0HIY9fiP;0Ur`PE5ntL&*tIhw`oX_5(%EyiqnH6 zdQXa$aX2Dz=sY8+Kw#S5D4{u?PE_+L6m|LX3;L$v_WgUR^96kfT)uokj=&xp?EZ*K z0=sJn$ug8JSFc|&+}&XZ%WPKh^4DK69vbqrB)o>G6EeW*!wv2BNd9)3du7W7XLZH! zu*KRf7nhfmUh=~~{4;ACxc>L&^d!3Dky>ibzWfU5J!bpB?sPya&0@VmXBrn1s>&#} zri%lLqsWAu<}OTrLaj3nr;$q|8M~f%Xb@2nW*N#)ElE{My3?Ni{SmcqIQ(=& z-nP`_wEGqxrzU1zRs;d_<&t=6Nfu;fC^mCq+f%L=3}Ha!nyza|&!=HGTC}qRHW;d9 zi9HN>8^}vdw`-x))E8?Ml3XcrBWZ_;Vk0wy0(RSbv{cN_7pz}haJ+k*>@g|ftf$Bf z$HR%>N6s!cYXP)>Gst`Fg=Kbe zfhi5Ae#`3OoZZ6{hYvSgoUdrdk=vhcC{6)u9V;eFt&|!cN30t-?T@_v@{FVeSy^&y zA6YJE?2RO4Ib)n8nn>un7CR1zy5h^<{1z<=KK$?#(R&v24gO$hT~Bci^RqRF?Vj7) zTjnpWDbF_)WrfihpZ@93blY}HFV1U3rMdXk7syM^-46+sXWV_dMP~)=;{=u%y`wl^ zvA8^=GC8}$5x4KiV~&Ig1*SZ~MTD55y($@m&ai_cB!}{DS_^rPQWEi81d2XAe}4i- zPX(W;*PjqVVh0P;=x=&pjQ&~lnaGsJ2FEZA6nRPT9v>x28>AGB&ONt|6f!~x7C-i+ zU=d+327?_3w9pLwh%PI>{HtFxj*cWHuYdb%{`~*@uh?NA*E!{C zjxd_6D41QY(bjYSr=O7lS|4%IQh)J+Vzb1{NLCfZVW3{jS!eJr8SI>)9n^v z1!7E46Hwh3(uF zh%ZWXSy3-*-hcRr42s|U`@d)3>}c^Yx5s zp7G+Fmqa1Z0lIyIbpyc%N@Xas3DrHiNO$as0anWmLK%uG=i9&jHD_0AjFuo_^v^$_ zC{PBIOo=jq*N)XBkM zX`l8KMa^lqr+Iwj`o$Hc)WoMF!V0{Ph$J~3kIz4l(+~f=nnMh6?z>v0*t| zFfV3=V~e}nl3GW#TC!Md2!o@)-{X!0T{n_fHM%YtyPo!b$E;lQ)vtepO4GGy_41Og zX|Z=(vVBKe%DFEsBT7+qlb^&2j~{eq+gvM_ihFlB)( zGp^pgp;|3Db`8C^eD&L3p>o0Q;f~pSPTvN$pAU2gOY#vp9!HGP6DS~gRBj-bM5bD! zWZD<|!7}tC`MjpSe96Gc9@~vbZMeC4WVbyL zQJkM$B81{}82IUjM?SxAsC0oLVN{|l7q}rXYO~g-E7z2x0jZY2{1*d(3bAjP_z};?< zLqJJAiA?Q?8~Q0!q7>i%=l{u{|J(nhT+B&@ra2rDOizEaoRQ6Hl2*L@;w@*F=RDrr zUF&ff#edI zDqw`}=|mbm!>*^&8R;}o)HPW>qn<5Lbxw0Up~?)oo^xbil!;L!qEQsf1;SeFsptN~ zdy?~rC~(J)EYFy)=VWCuH7{F>3Oo;48>+HK))lK4SF{h0$V0<$`+yiNecK|G0Ht_5 z?Ks$;ax*8Z%!FD~Qz$CNNN}DQ5=Lo6obnh-D~yq7BAF7%XHxQ*0yZX<>~>G-VEg`_yYGLZ z^bUXOxwyO}1dlR$lClODQBk0^0WIm8(=_lZGm4_7KeZG_<3~&1JDkZ_y?%=e5*H+1 zYRbH#yM3g4c$z4!QIJ*T^rklktu$3$kYx%V0*Bp++m9a+j1+m!em~-@;B3C+#pRlD zYza8#7uWpi@BRZeL|oI++}&e$Tg z{fQ7GyWIhlVzZf}jHDfVdOt!CELSVKu|sVZ_+86~|L=c6HzOI7htH2ANePCg!F4@G zPW#nVWSG^G?$nW7qCQ_Eiwva1*@0oZ#~oWpiBSnsol#$J*t~s(s!Cpd{VQf?n@Ocs z|*^J)0seT02R$}7pjUy=a{Lql{^)aq;q+a=Ad18PO<$Qp9JOjr57o z3>@CyaQDMc9By}rB(W)CW;L7DhGBbRR@FQ{J#x6e;k13^*mV@M8Qa4NF9qdhMwTgX zfg-O_LZY=KB#*b2BG1vrOr5{L87&xEHv|!P^S*xjgUze zudcZK-QV!&M@*43_<+jgvyIpjZJ^tp*nPg^`~UHew4ZJnwtEU;u)7n|P7iIZ40Tnb zo3>+ncVKp2Q_f~I%@HXIf)LE=n)cWd`iTS-l0s!Ogq~5YH$(}+dF-iY<^skADrM-U zC(|0IpehTFO@|#Cu3w(<^t9vbaz$Mge0u+hyvRt@W6-$0>K61AE1<4O@l zBC3d*Yx4CHmnEc@XaVWyar++U6}i;dwkHWeL{AZ80VzzwMXB`U2kZyz&>^)(3PlJG zrNmS}Mv`Kjt~?@;=NWf#{+p*vi{-=IyWGZfG001j!5RK6*380Ka#1O+3J$h zamx?_?d=nndC7X7lic)>v~~amsxaJtzT^7MHT}&2e4;Wr!B3Kv7y?;Y5UoWBO;y!& z?ZD~O;MI)g?g3L5Nbf0T2G@^>xklxN-G@CzGhh$LNjSQm^UZ(w9p%}Q*=oi9!#&Z# z>EXokY|im+OK5u9G%z$hUw`{G0?)(a9qp;b9(%e%kjtFz*gzEM*?fkQhOz0%XNLY1 z3DF@B~)_y8}yxW@Ydt{Ofqvw3Hra2zTl|qRLVB(o-$=z{e+)f(|Ar+BC_p~J^=k^~zkhhl8 zPoF6-&&XVWW*~MWrOvd6F@>VnzD8AIP<-ALxm)SEz%!gS5hQ4q!{%AACl z5Zwq&mLs0`_%ci$H~a%#S6ASePaChh&rp$^BLL2lF$#+OeU@Q z@a`Qimj$!+l5yNIoQ~}FTc`?RCWtQJqa(;1KRT4r7$IoypXj^E3N?J*^6+2(nR>mX z?M~>qK~{;XD(UW9jvo$Gn+bkmk0-Rz6uBVAh);nC3?VQaPMBg+yoV53)D`=8@3~kk zF(#3nXT-e5=#ruDKnuq9#CU4aK(&~0@&iwQ`V;%lkNDn^YeSXgG}{Ba`$x1?%;yW# zd4?(tesK6cFuH-h85vI_%f*WO$1O%^G+HCl^Bc}emg^EG!g@n&ZA92}OHqky+&W%@w+m=)w>N!R_Y;BvOWCF;(TPdKOh}stAg3( z8KN$E{OJ?eKvmWp_IEU=hU5dMp`~6fXg}XE+&mEy_?IJYboBRItPiwd(cBI-YxIOGRo3F4(kLuzCk*Fd1NUn24Oq`zf)Kv}7 z1gGd6!9+}w7;W&@5<cdfv<4JnGCM~Vh`DA|5g{EyP2Pkt3}~HU zmf2+TeYB7!_FsHXf#Mx028=NT?(?*2dh?oG zPqg46SdRC%ELST=Ygw(&=}$d63H&%x76q!zCSYxILP|x~HZ;2@PXF>pginm^fSDDX z|J7^!7nj6+&+*-!Y-@47MP&-xTQXBjRcW6%^)0#9yturiXcQJ~`6y$TBYwKJxI(hY4g;&e)w!)XK2B zUQw?L!a;B^B&U8PFAH+5sb)21cbKFx)?4!2Oi&6p5~F9eSx@#>X^2tbV@3!DH%>n^ zZtO6*qF&C)s)9@_mWw%=ssP2Y?Qtnm&uZ#oj*pVI^ZfktXKe6DCY_U%lFUdXfv5Y2 z=SG-f6tL^=$*KdJuLX;5=ID7rwO%vEL|x4gHX@Ea_y6nn?Em?D?*98fa=d+@m{nwj z;ritT+v6UoBrc^1-dJkNc||5O++iTLf!+HZKmWroeEygB6lOsY3;O##A$rQ?oTfWr zQy?S{K43?Ot_n<5&>v4k@6oxTdpKZ5a7{;%8H5((%Q=m+2qBqWoT2lahYz19yn=q@ z)tf8EuAM?tA)vKJh(y$itFOO6YKgaw=mK^aaNR(AJmQ8PsT9?$Mj3^-9yeOLegK)U z&%dYS`Q9B9T0S3ookgU8k%Ab*#7|_h0Ldaplr@7(w0p~Vir7=4KLl(K&P8Grcp(A( zoFa_Kl#pqXf281%!b1pn?~y{06r|)C#sTj=S_l#mlWTfE)nv;mCyIn8Oxpq^ehiqb zWO;r;ZVbQt!|xH!;)anrFHu3T*_;uF$yyZ;0|rS|l!(SMHUn5of4fH_*u1h2 z%gu@?5?NW`okLpdky@cMO*uCN>1q0oagkS03KCgs zSsFtzF9zG7aX?5zb#Z~HD%yt~AAb0WW8X4#C))c*X4Pz(oa#i%1=Z_w_U=Tv znNQ^!B{(%lvc-%}2K3pS{CbUDg+XMB!A|V>iIOw*zas%boY;ZUBZNQ;gLeTR1GzRhH%?Pvss1Mou@kN0}MCv&_nZGB=!d0}nqRkbQ-T*L?orL^q7gi;CgHXVUJ8{ik1u?ScLM z4dvAt^><(LFb>FKYWZoYuuVhXOks}@rdh;%wIuq;;bBX=JK}>U(>ZyOu{t}Wy1pQP zeM$B83ts)^SG@h(zouNDv-@<5u5x-2urg3zol(ENVt#ct#Y+cA7z0iuiq!&*CN(47 z(;njzVZtY3A5XY_!*J{%CXQW8{rZafazk)|{&1YqSxL~|J&}$B!@gm3j;t!lXBAmF zIsIfd)r6H%lOildioBRUY(zu==fZUNmI`Y}+P*_-MN)#4D|C?|v_>m4VZ3~pe1gxb zx+2#UwWeNZ=Ch34=qbgSW$3CzWCE!Kq)1}=j}*!Bg81Aj%<>!p1ed1WKY=77E;#aR zLWOCqky79WONf@D$Z&Q!z&(}DemI|vEq z14ao-rI>A2$TDO9{sY?|equZx*<4-Wx{*v9v=$6F>Ngks|B3p~9?P;cy%Jno9(9!| zS0PeYWs$5VyGaZH4ngt0&<)Jbo?pOk0}vc`64{z>hjO0)MZ*2RY%AXtt3K1Yb)a5&_b|kI@+eA zK~ZbVX17C2%{WcSN+VR_e7xe8#Qyb6%o37d948)kcRcP^G?hgNN#8HH8IGT44ljnG z-*NonNVDhu`v+F*8hPr8`xnAA@$~eBNYLKxSbh7JMZx{uj_d+I{NYFLS38D&Kq|rg z;}(bvmkXV)&`4}kQFS%i8l(}Zrp7n0`~A1H-#-#phSZqyt*s<)fBOsWzJG_gYuUd( zm$&!mIR4=$KK=8bXdfPV`0hKN|M^es|L4CDUQV2zUl5Jr_3LNG*8_L;iru?63^}m= z?g`cvs%>Z=AIKTPG?i~GAIR~>=d-rl^M%CuP#kh=%aryiL zF%V)YZeb0wsaP~@|LQw7Zy!-s(cP`7?>GGN|NM{K{o)&b`sY8O+lqJp`QP#QH@_yN zh%}06nmGUXiRJSrMDl$2?i*fCub8H0^>jy!5o;^jw&MBIC#t(OXZoybXyXim`3bSonbX#GR#4nD;1+M*su@$e+2cDl_nWvfH1FEvL%?eyb z`+y8H-Fl6Vk(VF-NY;+^Z#LBLDuO%GR+;s#Lz#%JN_z7Ay65TPzIZZ*o`?Gltx!xa z7nZ{SNfMWGeLd`7@o~Y0vG6riRgUBRwdZ*1Ih}j5x^dU58rd{ZnF1s@U#X?WD#OFw z4kZPr>xmcx=Z~Klo?lq5J$`Wn7mx&g{pltY@xw4EnuEQC-TT|U{@Y&(qgm*Lj z<;33F1CwY>T5FHuVH`A^=xN}w8p$cfK?|Ib{X_vKoY zBOwQ_r#FF0-MzuU1^%8Ex=s_P21vz31-LXWl!`0<*p20{)zji$ot>d$n^m?eP-DIk$HN> z*o07#YF#6>VYl1yc>hR$y>dC8xgO3aZJ5S|%jsHZ)ziXde3;RodFnfY`kyMR#|A0ykQ zVeEU_*0SAgkVFp0BkFdCbxXkNf}J*%A*LEB4LLaGmkTdnpOL@YP`%qSiIMwX?&!wM z&p&_W`sx41=q+_q9C=Y0u8X72g=mYB*s8(k3WQ{}+Az)|t6fu$pH)SQnbmH~!_y;& zSTf=`zxVgn3%izUz=qQwPo0zx!vZhc#hd`04NeiHF~Qj~^Ds{(>Z-q+&c@z$J1n zWuFvoJbjT_tvfE>lR{*kJ;a2TCI3H76OFc|yyHX3xu;Ul*{s(j%8@-qNQ);cNlbwp zN+meU@^WE@mLupZEBA#AlB`oLIh|G4IxBY3SZPF(44e62}wsG+^#qY}b-xM##+2pE!ib`S6N) zTtlsxuVSzF_Q7k|xxhR_}{pDqo~@6)k?sa=?|Z{d%xoDZH0>-VKS2&c=>YV zF5U5R`ATIB3PFDv(b|?lpag4+h{slzVVZc_y)7HFx<-p!^vzXku}y`x6))!=q#~1+`<|2{OP;xYenEtCyv!le>WY`=Bl9#<*_y|@hH1WXM@#g9 zsx=sy5iy`^#ogN-KfYeEvB4;h5D8^9o2LyInUITPaRZ%|eE0o5v590eGx`=~4X@`j zLS%;PNQj9KZ{N^gFK8vGn+9nVIeXA0#uyB1fin3sX7vK&4>Q%j9Yf$WQN+q*#BDtz!{5-|j9Wp77+u&ahP zl>}MH@kT)wpfVvwrfH&Htx5>UyF&WOn(X6^1gw#^B8i01>W2K%;0!sa0^J+}X$l0l zl-8dS%)^XGv2^BTVwq>80Nd0o&K2N@1Ro>X6pXkM<%GG6BepTf+TQBLne1=v!=ff3 zQ=;3;63ewlCPEIV7>Niz{prsPKR;uw=I;K9`Q^Yddg}WfY}YiZVIe_E4EtA%sn~q? z4bnM2|HDA74Wz`|5AT`#3n)dsDxwZKHoW`wZ&}}M>7QR%-`|t`$QWjtXh}jb_7jSX z6fiF{)*AY0z&5sQg6fJ!n_?WUHMTLg0j)$;5{zQES>xQm?5_wvq4fzZYHS7Db<6&I z!fXPmlX$1t-91sYlJU!l6lN}c&;5GKJP+8WCQE@3@bvb9={(|=L9Q`VU7!`HpJ|>|J8ey@fEG);xEmOub2 z*=~32_h)9;v$FS8Mza6%g?76x8<6`o^3@?yLR(2*7F@4*`2Jg(Rmb(~E3*rH`@jEh zI0<1~&@}hNGEUsgw;q2^KYIenz}<|L6{vv zrBeNs8rx`8RS|P}Xv@y9IRE185UxDrIRvtgD5Ft}CoVHN2C`I$Y>G)pDv+ACt4N`A z1*G5>Kh)H%C95*wmm-jbMr*@34&)fnQlS80bZH1mg=sBP31$*@T{F)M^_wl<|L6aJ z|NdJppTBbc^B0%{(MPtw`USuEzy1qS804lQG^R$|iX{d%AD$RqK6AHgND)5& zuYaO_dSt!ZGMszryA4%a^V9$D?`dpB^{a14-m<=bWccIH)KL+fFJ-yL(A1V`m}yok z4lf5%0`EL&@fZV;i!?-Lq*dh=Clj(NaMLWRTKQe;p?6^4|hnT%P}tk zIVWtRSwC)3P0i}<6Vg=0RuVGGR#?-rYFp;Z06yWT088L$UHo`CL){rx>zeJhp=l)R zUCrlDue`oq5LwaIEzXy6k*O_8!C`+S1qU&+1do%6WCYR}qJ-5uOVuT&<47Dm6(Eu$ z=b6jtK)ZWD*_L4*X`7a1@yyFa3=U&7^{ONIz%nm%s}|E*=CG8%M&yEKngiNH|NM!_ zjBXn4?;m*m;U{WSvtF;!TJ!je?+7vzyyL^){Ek_{GL2{{I311%Er_i|tt!6!_!Gm+ z8F`*K{O~jF+k3cMvHSj8qE$q!B!ROmc*NunWm|4xTs&4 zD`}odw^ERseZhrGS-_$NKSTxaI9%{R$Zc=`x~5PZcuFYE8AO(J2*Y zgU~F)jI|YYSJAzBM6Fs5FE7NLX-tEiBd4FAIUJ5OcN@H$xjw%_g3bH))OUBxA@ca< z4W_Ylo0fLlF)kCEu0cyp4j>$ir;&MF5HVqOxh`$iD@0DEfM+!G^}={La=PqMCQ~;p zhy4W^67DcCo)&CVbH8nomx;ECRO`%eIP?1H3+rvioELPXFjeWNynpvCcMngvnAknu zVQPbIYTV+e>xLLIwW-jiLSBxSau;tKv{AI}is&=T9Jn0L)KyE}tk}MPkG;Pq)E2ER z$t~amX_^r}Fb)@*#&W;g(zF%#4_k~;m`XGD1IrSrn>9iiK7IPi`OrfaGtt#lax#aIB0$DXt-ym?pAZZlR&bSQUyL1wx3?CzepP7^{Z>dgk1Qvp(B5II5g zG)+TVJfz6^+GCoUH{X22?(rU5SzbTCP}gnILBD(@_<_85{AD1z$m!=7Y=*_nM6J-f zHKMVM`xoZJ0bOgh?;faD?MH}U%X%w-&S{M|2*>jvFu!Z0!SBhx&g9=B|M z`HrY54Y$Uy#Dyu%q)K8}#qT$rueSkfq4AJ12(Zc)2B2xkS%Fe0tx7#OMzR2-N=k2W zjzlg&kX(RpDU}JlObKEp`|`CU@c1{sW&O=N zF8xf5g*_N6i+R{#R~yFbh3o4cqa}VC>0kF4so6X|;&Z^yW3e?2S7g@}^tcc#KJfbS z3)^qr@TsRi7`JNfiGA+4z+w;@^caL%s=_9ZI zd|;Jp-o4)uasVTE^X(f>hZ8^j=_lH{WwX6wa1Io-O+!u*B_-2XP-)9BBTZG&HeE?1 zB}o=V=iIg}tF}NJQ^<&_Law(okB>MhaQ(vgbq~XUa0@9pOr^OF1J`5EX@BLoA344b zeE#@KWec}#nH@e%)Q#qPy7I?=`Kd7W-#%h)yX|FKs7%e_<%Dy^z_Yy0)T&~=?HHDs zVe+)=bxDOug(l<2iC_KoU!ylI^=4go@~QCo$En0w>$*Z_NmZ9J(Zl186brMh>sAb> z3#zK|hZCD@NaKVt8Wf})nU{r^!+~rK?Zbxa&p)wzeWfyn^Wha;X%^?04o9R&JbnL$ z$8WzuNWq8S{f6x?-V>8Ygoy92C^XGxi&)k8O0jx(S70e^&H8?e>}tBFExY%3$VxD| zfqCf(*%Jt8DY5DnHj$E;B*|w;g6sl7 zTbfM^B2iVAOhR{-SY>9Dn3);egzMB?4AKd^oO zNKBD+vt>N?C?Al~uv$IQbWc?34(~b?E0nV=FOJKPXOx)O-e=lYkWA+8-4hRQpZN0e z3;+60e3vQa3h6$e% z$yTi1y(4ukkxa~i92Pd4igzC#LBhOv4u_u0b>a2(ig%fu1B)NaF;~bqibS}G@FKK6ryPuml^xG#;d{>m|tEBJHBg3R-;tm z=%)-JBw{N43~e<_b{zX7vNcExAuY^HPtFl(C3A3~1U_c6&?sFMBv;BLm(WrplfYO@ z+qP&~^j;E4NC~OTUrI*Vd&CluJ|bKs42giMH9jV)Rfn({)ntP7Ow*O~^%ZAE(oSOT4Nf}LN|8k|>r~qgk7o%B zs;j8B4b$<${CdTo&g2xh|Mm@b(;<{b2(WcIMZA9bOmv>KZW!W(oF*N>SAo9~YXgrfv%W z$JP}o6?p1(?9o4=H-t) zVa(j$wRBs_cfYrg5ZMbv!-3qiI`=wOpo=FQ31XmV~>`xW%#F?l8@o<8fedkaEQ|b{t2=QazBH zx45`L)hp1M%Xnb-wBzsp@Be-SKlbFC>RM?`U1X85=+39fbLt0@b4lLg0Eh5`srr%Nzo!Qpm1CHijStVfJVZ_F+Y}>6nJ%3bfXe zou{@H58wWhWGi%IndX`4(v#9cy=q9#BSn$l7+ulUE0oY&Kff>?j?C8qIXaeMBvgv~ zUw$B4Nxg0{o0@L7#mj<5i?K8ecMogkFtNC@w)HNuc+Yxw&v}2q8cpv!UO{(vNBj85 z-FM%!eftebt+3rAO|xe19c{B=_x>B6|2%U1Ig%&E%V*EzGIpbB*NX9aCdPm?n*Q1| zUIzNhh4reVZc7b5q`Nez|C+uH2{rr`m|M&?XBc?L=5Xd2+0HY;Rg4HE$`0{+@^XD%#O^Z?*t1Y!| zc>Q!FxCLP~Lr5I<7gn2lvaV2O$8eRjt4Err_xRMZgpT^*J;`X2k!+vts5QL*@WAzY z!Yv+@BuQ|-RF7lw7^`^y%lAZ?5Ut|zn@3zqcm%Q1pd|CSAfrGfK_xUw2p-=&q9q)k zpV{5rp*J12X-XD8dg`Vnjw7MJl0{-TUof@;lgKq(`V+b?oVdDc%UVuXSYvVfGw$OH zdU5ES$+Lr$Ftw$9SX1412r`e~yrtW;G{&Nk^ye#K2_)wUZYBmtj*cuMLS?j3IA6%V zb=#40L3tOCeZg0i$$ltGG9{5o70AcG{ckNnmi2C&=K|)ubr6V=j6&E7rB(ScBq&*e zGQz6TI+KFr7UuH-cle4Ckh4TfO|3L@STL2rMg^n8EeWLz6%NDfxn| zWznZcSZ_B>!J(~1u3C~ch|P+*t5Dl5QCh}xz8X$x4TwiR_{vDP3YeEaKfs5dn(PPFTe6e4o7+A)t~vC$}l60oi+ z>eezZGso9Gl7hv`Igx?j6FwIATpngbKaxTQ6wByv(^8}kInfxys;O>Gv`~Z=x@J61 zTn|0jOX3u`94}aHz+~*U#@?+^RZT|XoFG#%o|iIY(FRnF3yKBF>g^NV(*t|&plc{e z+Jah2A<}hi=~jDB@QIiso81;s8GMy_^PBguwaE87cK_jT2;GXPHNqNp-ImMYNYi!r z;PH!N`}BYlk=5Ofe*enw!;cKFM`X$jKR=UR&*aOM923`<7r6AGq1mp8vtu})P<4%6 zufUX(*6`zJU{DXk_%nejqx>v=qFU z&!zg?pRYGZ0K{Cn_aTJx37g6`AjgskNHG^Y809GzQh+WIU}8dLg_er0X-GNYgD(ln zTnauhxDxY?si3~1gE*7snRWNbv`7G&t|g3){mTo@ZiN;w{P7ddfBy$a0yoYuPPlO- z`AED>h&d8oujJ#1kb>%AgKTP)QCts4j^|fGTo}g-@wS(@#&G@VGxPq1>EloA|NJLv zYf(a=MP?kYSXEKi4LOzJYPD{8``rgNclU(Dz<4^Nt>NvjzTwSpe}Skfn#~4PTXbzv zwJl_183{3fkR%ei(kLmZ+M4xtb!&YJ?w)pB^MY8l=-mprY4H*U0=C)EUuK45&;F+) zo!YTnJD{}RQNQwaA{6CbL zSTr={2rAfay7B^u5otAbWAP#K_4&*&IHb&IBM_oAv3BpD=+qnYeb37lImxbUwA}5x4BsYq@s%hW7a>Z(O>!Vv_1}zQ7l(~yQqJ%VmzOS&J)8zRcV&Po_Ot1E^^-QSuPin_Xq{jSYE&Esk34@oM_fHug3$= zFJFl%;VuJ2M=*l+-8~QAeS@ehn}<81hNi7?ZYHFJwiO|Gq>)Ia5w>_JrBxs^NyBE_ zl4^y1=*YE3GzMW{o@daOafnRk8HMG1DqA!u4boHyW6-+|YQ5odNz9{QnR0nX<^^Qp z>FopBNQ6;b`zt9W_FukITf^5c&v@q$L}XPrG<8RR8CkElJiL8kwcF5Njz~PF)~K!` zDv9k{LX7ySC(l>pl6d~pC;sK{|3I2Ees)yG&^2{ozDdX_;he`9#ofb>7(KHa3PdtP zCiCXoN2<2QB|%OSsSS5;-eKz%(p0FfB`ZzzFkh}rm!2FFR@Yc%SjK^{^c-KlfR?D% zvMdYUFSrnqmB3b-;WUtzqVnoC9d=zagpt`zG+jfb731N^ygyQV!T!&msI0wFt_5y( z_;D#1tHt3DSHif^U#=|s7vk$Pci%kGzI~)#t=O(xtdzX_#RuB$7HJgA;lMI3R1bGW z$2~jzmm{1kjLsUSam0@v5d?Kpb3DEx zQY1_h!6$TOAs5eIj3pIT>gdG_m_oU)3tQ#^LMQ@|WGNSglt=;)f`lOC%oGYGO6a0s z&tzoEMDOr1f`HX#&E{^4lnK+7r0FzI)TSaWo}d5xGt00rUMKQpAq_L{|MoYmzJJ8~ z1-q)ts!?i$f)E#kg0}5ge*Oxd57a`Da>9ucDVIXf+;>oy0#ua~%jt|=8Io`)39HS9 z`*&|aYT_{Ct^-2CdUuDlH8yK%)8NL5wrW}WkuWSIWmsZlk%`<{Y||05z|;+H@th8) za^h?|=FuUILMlOJD&D<)Q|173AS?ldX7V%jszFF_u51r%-Qebon*yij6W&V#nr^eD z>KcN~_{Q?|``=S_JKk)bi06fu&#&YpZm6@wGF{LrxW#UtsrA2rwm!3vP8s8 zjsayQ`_qLaCHHr0tb+U1n#YGN_m6AzN};SGlKAxb%<(udk2BN;xn423gj*6_(_y8- z_cKZ>Y+WjrnvgoH?zJ6vt?KyvWCe6N}u(e>d>A)zCuP26aV)hGFS0Qv}`?RI1 z6wDLbZ16vSn0ycbE}#D1JPtNRvawOxLwV#JXOw z$W$H=;)g;tiF%3qg-gj^T|(rSjV5Xm4l2vx#Nw@{9i4O$rLt|e(!Rw(xZH$Ruw5LLN;V68J7|LaK|tI%YUYQcwl8M#~(ixdUX&eZLp2SwuW&Y zS*8gWBI|XFvVvyaBDCh=-91VRq6p}aJx`DCY3|lsj(h61W>|{qSy{vE9J;EwUPtWK zA!FpYe@42<^|NGtIhHljN+I7P)E)2s2#M@o7n1Z4ZKwzF`8l_lo z8qQJD9}D%yu3G|`c)60tg0wF4Hjf*xAOmK%MH<80M()3R%khVgxKqzOFJz-wtr|>g zXf_>o)ncpiKqnRPVd3s>!?+A6Bk5oFJpb#DeET1N&*5;u$%xLdTqpAAc>d|n^y9?# zcW-(4%?FbAsJdbO^oXep(|KUH^rX{Na_diu{?sEvLTACtKmL*9kDplm^=}Kt+s6_g zo&$cE2%8n-G$XUbtSt7fBjv<23{Sk5Inzv79YYeBC14u(vwd7E0!l01a z-fFNKtrTIHO2SSml#!4!AqD2ileI3xK7gFyHq}QOv=Ss$$ihPEA`p$9N-3+P0>Be2dkJ@#O`fYMSi>L3)gexamq=)g+^dIWwI`@>(H;2bXSb#uDC| z&sXZ}h#O{_rsMc}VS2sND2Wppe9>-2H?nCqbY63PekR*YSSC_uu$w!sFJC#IdY-<0 zk8am2-f{f$!u1a?xN$_k+Ymp$P{jgP%|0M?sfBlKhj263FMHH5VH(ToVS`y2cK17! zkeEiJu99(FuuX+0u*86(ShKH(3sMP&(J}qc|HZd|{aeJeup9=i^&Z#v1m~%$j%i+K z*DdqqO4YQy`Q2~no}QR4N5t!q;~#z^4>OymCjx@&(6icER-L7_5;t8r9nR#SczW}e z1<(D52cDl_%ErK&!j9V?Udog|XWX=)jX_n0B*3&S);2819<4NWQ>vPW&tJ*YjFFmU zETr15ZCJ1CqK_7lC^AwCLQG7q472Li;PEIGdG~fhf4$P59aUAMg(mki!^bZmBc`o+ zczoo`r;kk2M9KmsG_|csE?`rH#j(r_tL+``IgrmTW@%LcuU)rIP{^vQm7e0QWIbn_E?*57CeC4`7u`GcU z;qmd2G&|hsLUuE8USReZt6AOOVWlE2{)T5R`mE?9IZITQkR!rqnzqFXjk}D5%gp|d zKQr!6<)N78G9)7x=mrw2D`J*q)wJ0mg}9M=BSU}vOD)&hstf^_h}Jb^MGD0gkW)e; zFhU|i$pxf&A&c8DS_+iX7-JAQ7iIY32r(3Ua1Ml}1WwwjDM#wAqdy-B&LO11bPpua zpiM(n)%fUXw`-Er{P4g2Koulu$t35fyO!pDTUNS0kX=MB0bv#GX2bM)ps|YGcE#dd zfnOd6=IdCvjTsUVEhJC7H@xmo1d%XCG4>->+hMwj58r)@zuwl8DX?9y=|Aszynn*` zK#U8Q{hm0Exc&;$Ol=J2u_M)`QLAc!$I&!3S_zhMq*=8XW6BBERKyS&PFIZ5D8R~s z*ekje_+}XdzKq&~93!E_n>;7SHTvl!Cggxt`BdO5I|k5lO<23*+U&GWH-fYPDtk z@Pt*0G%j2&S6s|2lSjzJrmN9bmf?zkkOJ0fY9+XTSn>XwHP$B9TllNLddp^AaXb!O zdrxoze{o!2_9X8xb%mQ(s)Ak_!7 zDmGxFO@VY$Y``%`q%L*U7$QXQZn;hGDJO*Bi%&pFgw^Hikurf)ARiR5sFDiBjY+U9 zq$L!eLI^BwV6|CejAdCoN?KAZEnD1sNAYKa;134-u<%L89d%9Z^=J zD$DS^=luB#t{(}*gpLwFIhOMP3oNHIzxwbEDoB*my#4S!58wWZX7fNyATx}Y3-=Fq z{OUjbd$zy$z~lQT?spsFWn$Q$*xubUUank!{+ao7q-$!76i5YnyF%5P&=k*vli z$9y^vUk;=%uP}PloEXn%Dk*q;-15zbM>dV2zZ~dV!*{><2CXHZ{_qork1x2vqpc#R zh(zQV2j+1{ zQ`aT=)EK(9VY};C+|2H-qOzH$hHt;w@%W}IL6OTqS{!LyZeU4A-}hK!$+^HQYg^%m znfWrXe!OECdZzQ0@p(^cEisifm&j$s7QG;PczXK|V{3d)EZ%WGpNO~PuuPfjF!Av1 zH_Y=)h@SrIS5|dR42g^L-2d(ynl}$j!@}wLKvs$byuW{g$(mn&^F8i5b9_0ndAutO z#jMzDH@8j8NSHm=YJA_LeB|`}T;Li>kVR&36a6wVxrMr^2`-?rLb`ISuQuIH>RDKO z!ACBy7bKSCJjpqXk_^{@swv?tBMn*@lJ|J$OD!)YLYI{1k+b4_xl&aXjjj<9 zq>%-Ry98=oN>Nh?xxG`2># zOn4oTA!90y^N#EB!lj>iJzdLj7RBi@uq=*{JV8VzANl%x#hQlTBR~D{nbYaSn}-#W zNKFx2%*V2>5n5yKHqf>>=a}aOYYb`eg`6{mVp7QhB{E8@0=94oWfaCrno2WIBTAR6 zig&T(7Gh+Y2DBDjE?1iMhHkrK>?by=;{4<1f|W9c_5B@W#mk?c`Qd;3f!Cj3z*c1f zJWh!7#DD+){8tYD@{#M8BkOj{aOye!_{{L-ME`Q3TWwL+miApg6VD@QbOoMCB2WgY zO6dahRF>q*gBD`q@z>uni(K4%rlztrDi~zcsH_=>;V*H|3?+tYD-x2r?NFk)`%;WZ zQQ#m#lroSOWjC)97(h#bj0qVcIFBF4TRx$HM^h}Qwt^Q=giv6hDS?pX6FmdVw2)EM zU58Hr=O)~ErZSo^JI2d}C^Vagd*-QU9D0n=EPY>`61E~P9x2L(!w+L&RK|$&j&{Aq ztZOi!Itxaj*BzUudr~UopdbF_BkgL-X1nEjJkbeF>L(5#pZVeM|H%I1fiwn=pI)fe zD~zcb&ONKgd)ALn2cw$Y>GKJDZiHV#P>+Oz| zV=3EprLL<|qEeDDjW8{^e!e9nJJT17v;rkict#`P2p+XWDhff(@wRCM;P60kK z4kMFWFt(yw?XW`QFC*jrTJX#Wf}5#o!?^UA+TgtB)2COau^3;qHEiy-T>6RLM_d#r zX}D`QtlO4p7-*UXQb`Z?`y=7hLrBQYhVJbn+EfLuIh9q?{q~;F&&WB@S;Mw#SjMGX zMU+At&BL2}LYUE6FkJ?;G`xI1vh)kid91Z%sAOAmg!AQ!JC7{SXR=S&Bw6O7F>S^5(lcI1m?F5$`hH8O1!mo_KO8vkFBsi2jRA#W9!9Lu*t)`x3(cx! zb~6ZtpBI*CF5(d>K`Npsn}8VNtua`-{V96Li4+5w2%-GPDpROL2}mF*$gAR%2&52j zJ`^uNx~(x$C{v+r4ML)Ts%glJXS!aoR4l#c@$n6J_YcKjEHmfR;ifY$KZg-Tg-%Q* z`+vSL^#fT;Dyu=1AGhjOc=2xx1jQ6RNlL8k`0h8qBsYep?HIotc({LHySwM@oA>zh zf_8>^e`WZz=kn#q)XxQ;A`8SbPM)f2d3g7MuRnewyTo+q5jjvPQD7$u&exIaX<@le z$N(?TuRz(3D5cOw6~x=M$Dgm%ITEHBkp;{7g1^ofRS}Hk{=08DeEmv}j!H{%4ESMY zx?X8bh50x5hl2Hb%`}Z%&jZ7GqCbrY6uZ@$Yd<5Kn!!1idB#>1P1lhVXcs&(!PE~( zD@nqTD#g5%hr`b^cD-iy37-On!P|{mom#rAMC6Bng9?0YNPUVscEc zuZV8V+)rc#!~RlKVlj}&WfitK{IC!=wsgrykKRaJOw5ZT4FkjJjM_X=nTpuYn1>bT z^PcUS9ZgdgS!xuhb;D}4;dD9R#w)AUn$>E>bKZlj>5ns26M6W}n-Z)^6769;A8FeT zkzxDhiOcH|sWTxK2HqS4sNBMG6 z@XoV&tZ^|T>KdCR*W(FeEJNQ@H7!a>l)g0vg(gW++MWnf%E+9^C_>7`9(((Qk*2Ku zawvk25FC}ML4tQPRw|NAq?pi37o$%~SZQus7emMsD<#>s8>WBB(@=hBO7HQ_uGO84=k4nB@1S4875-#xOrj!@tL-* z5yfPNmYT}e4Ck5imowkLdqQWydY)k;yf7LVeGFGs!{&)hw|VVE7mDNsLbptZLidqOug{mT(|JmU8!#vh;QOySCfn2PO2Dw@rPVP4Quu}m{4?#u=MHT|3 zEw*aO(vYP>Ru#Igk;WpdC6%l%IYLgEY3zx?p|yexG7EARrDKp%nO_*iJWXXtC?v~x zV!C`qr--NLzT;wqkSrhw%{aRe|Tk6uXwoIvF&!`QIJDsNsjH?`@)qRJXMrz?;qH`{{U@8 zf5Xcs@5s*M`-wc14Ubg?Rw9*PybRPrvFjR!p+|&>36j1aSnbxt6bLa9Qeto(VGOmF z%+Ck(&2;331&d;tCaP79Fcf5(k|=9wR~^eVv5Y-_@nwr)Dx6Qu&hhl_1MO-Bw&LtO zLsYz+CRC%TZB^QwLNfIeLMXJZk#ksZ-cz?NF=X7lAdSM*WmQDZB)=eXx%&|!VqB1u zLnNq##ko-Y=eLJRWr>lJRjXMyn#O8^4~Pg)4;#MuZp;0v_H}`ElpbkB~}@=BFS>s{kI{f zmXa_!=IdC>DB2>GqPn53lZc2)({`&8L|FpK1(tcH=v|A#YdG{omuXFn47qsaw1q%9 z#`!>;7MAmb3k%X}w6Tm|PkjEne`Pt(JiPygZ~yvts8vOGzb2>*K=u(W6(QdG7fWRS zLrHd>zLtJ{(>7?Ok*Sb$a!y>%7o?C>y2iUmRW}3=Mi=zD(7LFrBV|RCOE*9YjgT65 z3v?-CP)&`jiZyy!CZa3X1DPq2VkAtmOS~eo51@G=se#P<&GBbBu*Va6{7>3#q14XQQl?l-8><6nE`VWPdequQ*C z&oJC1CNWV7$;uR;MT`NZ%GKz!Ka!F|sL0|MtTf~y5M4wFaLbI(8D(rqs76UUXIh_7 zLLp3ra|54L%)GoD>CYo^iL_OPRsyo8zaEJxa=l&(T*6k^yEPXt7(>RIqO?W`y3Lx(%5n-= z7IfVpY*V0~P}JEe({(GX7O)hQcC~J3x|*h{&~5o-9sA+7$`Ykxuq=eBfH7ju2&uUC zL!l5s2@Y*K*7sWu`#tC9BlBgTswoc0&0ZjBc(97bM`7w*1$S5l2BGR=;^cC=Y5Nk#h0;AOc;qz9ygYC?(=3&c+zxpjc7SeSN;U-0ujLXE5 z97;<3JQoaG5EyMsPa-7dWu$2w&bVU%>^emS|I`JJal# z$AxM34C9Q}6<=R3e0n`_It@sXdHe2;hsQNify(6$HqRdCCz496HilpQ?gLG0SjGuy zN|f^Qav=KzTC;llgja&Y`NH5Aw)Y!?pGYz=Ud~in-NG~(qZCSH@{-WHA}fKet1^j| zis&419;uXOiILGe+*FQLvZ)c3WSA$a?V8Ip6!w1GBBUWBux&%L+H$&H*}Z#9vI<$3 z`f-esmp{F5`gCNu&In=9QZXL~bku|?AX8@QN7lOy&1Owi8NxIZhlS)yT{Me~lpv*` zvbNZMGE9>rC5^RfqB1nAbpdze^4T17vH#A?Ot)@HIiYo#C!`PyX^a2oCBUsISR4Tv zeS@gD?195yPMV1n|^aEX2;bKHpHT8OfQWiocOrZ!Tl?1Es(Q*9a&&;0>=p+d) zB9$)FM5%GJXPF&!Q-hLZA<3!aq_5YJrmf35Bpaj<%=1K80wQKEhZ9Y!_?y4^p5Ok} zw=}JyS=X3V#n_*jUk~6T>!*8iWm!Ew(%tW<*EM!qv&^@=eT4Zs5EqB86~-9G^9Ao6 z-L}JMg(Q_PW(fFxBuo?M{pI$FrRjDXgvx{*N?}PV)(?-ERa?|)F|$k)Mk|Dt_#2kI zL~180ZBZ$5{AthOhgX!bT)!R}emas)3&Zh3Qed}hgi-XDYf0Hv3TY*Q!1m1pP1m9t zO}pLPu-QfKY3hcy=~#6;tf@&p;unW8RZ*AE3m`B}QxxD*-AqCyT3h~A)_=rAaJ~%t zWcjm77C@|)8IeQLyWiTH1^k)FB9j20A_9pKvc!SO7^_JsVx+&=H*}ij6W6l9{Hl*qi1%_`rJIG0p>02+l8O(vQGAgq+cpLaE|hh$&&3 zLc!9NC0a?nT_d`NC}AEZa!f@Edpa{uBhua=uU66bSG2hCEBs7Ufo|6kWyxyMy^^W;Y+-6e<2Nyepqlm zFwZlUwOFmuS)zo*-j01Kl{==$hIxiBUyhv53tvB7>HA2?6Ysy>prpZjRTR!+5oD}) z9Z3q%il%G1?)RvWFm*$v4KhYH5BH4HqaRy>EGajwHRCXmQzFX9?50vrHJbPD-k?Ng z)zk&UEHWXL_Lfzqka#56`xR#05NFT&@exr~tnRjCnF|^#CRj3+sYzL&>zb-l&>UpZ@S? zV$4KchD7I=BTdznUH^4tyw1$af^#!|o(tsDT54Of%nK1gy;^Y{#}bZ8Wq2nM$Se@C zRD(-oI2XWZDtZ4LT@jVsLO)^gH-?}=+?an_7cqv>`sRczFOp=3(GNjj+OHBwi! z+bu~*oEuqnnsGd%WM)hOC!pFCpsJGUFKI?26vju=JQetcDZIhYA3rgV6LATUb7>VW zo<@}*(YoDHwG~ZMV@*Y8I^+mpj>K}@VVY*NHi!TPLHfR~;_3Tu$U@;=gtj5CTGnr# zxO?-6ZZuU_m*+x3y>1Y?%s#w#wCe_CZpP(h;q-Ds2th3&d3T%F6i6;2)7CAQ{h7z@ z20!*BBgl2llFR%_S2a>grc2M%4}=gg)=*b1Qmb3Dazs{|B)~^UvuUxNVRv_jvK284 z#yQd-Bd6n)rk31o8+NM-D|M;qODl;v6dP;uv`t%%FGf;#70r4@dw-8e3ga^Va3wB* zm`h-o&_cw!t! zaGrFYScVB<6@#BpRYe#^UjF%?P!TFDYunOx70ssOI`%|Sj5JkUkxltn%OrHInXf%v zU6%v|^c;LSE4Z-!I4@%Qw`xP-%@es@P$r zWw?y^$&n|=`EaDVTT?x43JF$f=ED``!5uGHDY3ReqWSW}&%}PA%A)Z6W5CoE{xHE5 z@%@A~Ev8;!Y{R7c-ZYkcn5DINAkjN zRaustsG1sXnzhY#OYojdE?2-$uY_SPrremBu03ym^DCk)Nvy!_z@Y*iOBQI_<_k>>FMs~cpH<%Q>BDOknK{Bpol70q@_z3TAO#O1sv zM2`r82ojam7$b-{>QT(_@)um5_m0hO!<)B{csF7ELY*Q} zYc_A*GF}FTmm@+5slKc(!6l`_(K2um7G1&N<=DF8muafIw_f_i5ODJyT^>Q zCA2ZS8EwlkZkk5gb%#;{Arn>CP`54DkIyWp2_FRLiX;{LmjhMPaeO|qoM+Sw{xY+i zuEc)A88GXHc^=6r6H~zDgwTdB|MDkt6wH?a_j+O-1iQ_KBo$K(cptz;bQ0yBt`y5W zqm>00nO;s9VYto?v+j6)`ApUl-FDQg4RzC^q@u12gixd=+EUeZAu@%4kDkTNB$>)Y zxI|S-{g-m;LzJL=$|Zo4UFkDu3W_|1ME0>fltSD@rMHYhDy5OnW1YDfStzt}`&mTW={{pI?|RXJYoWn}*Yu zXO6Eg7-Mn$wM3Cogy5)aO;^`==ec{>;o?lySfmkTt_2~H-8Iy;;q`Um>+{T;x9>r^^hIa9+pts>YO}^o15Bk!SJxV01dnfaZ0=j?jb;iHP1CU1 zZIDJ2=D84Tti%NmT9S?6?%h4_zkf%R5oHyt&5CZdA_h&N+^sJ~Iu)CbPZ2W3}6$q{7ygD4|}jnER3A*H=RH*i}ut+3@A3 zk0hy>Wn}U*vNmX?F{Yy1Y|HLGC4|V-n-!I$r1))T+pUaaW)T6n08fKoGz42 z$1oyuQHiDCA!e))gb+$&lKdC+mlUPbFO)$T^%v;~QX;KJ%Hn)nVnUgUkN^}#!JtrD zqm(3zLLg3}kb|6G5XO?E25nGPRn}TQu#6K`T`|uSlb;ao1{_sor5cha>jL-Oyn7@O zsa7r5aV$)N$+3NUWcT4s;V%kFMzFfuAi9cVp}z01tGXz~8qLGwV=0OWN%oPYp9tO) zjO6h@{+{puU;hVn-C&eJL{EL+@tgnfR~R8!hJla*Sr*?xQ(2aNKnuYVX1ZOAsZ^OC zC8(>Gh`^-kAVXCXrNTBXNoA}k-Iff5 zn?Y&nd$ci#N-@QS?qN-}X=yfV4yO~gYS_Qb{Pa&pUS1|1-@U`fg!3b-N+SKd(4Q|< zN)dx68_DMBzD!M(rfyq?moxpxSJLQ-J~2)ci(ja;DWb_Fa4xg>$hJuc(~iJ-PPDy5WHhN4>X$%_1&5*98ITbRt>tANRgSo_K+3zdW&pYR+}}K{ju17 zhl$rOU+LB@J_Ne9qgmBVel86>3t5D6N1K<@>n4-5L>R&PzO=Z;VMIuUtt^$)OqVOu zGUH=_O~Z%({O=gt$mMvVsv7$KLbYm8)=)JaiOlA1ORiw`cE{%ZJwggLn+@x`EoReD zZ`RnlBKkmgx1rwFNTbRT(Fp9rhWg=-WHrXrh%E47;r{zKh)R=vV(QB`j@3#@gc3Nv6icwsC|S{UEx{KkX;mAf1YH+#$M*5@ zc3P6SVZtplQVX1SWj?TKklPkn8JezTzKr`jKrP#b{vG*0tuEn%9?RGZ5>3}aj(q+21*aND~vAnWLbE1Ac!HAWBaZ0pHc=>R~G)^qT$T-Z@%?g=K z3DndUVJmj`Pmpt&|0v0vJW&f`BN_aHkfI!qm&oPgGj4H27ircV+xvTl5UJK3uJ5t7 zDW{VJS}C43JBH^M+NMFZRku#amT`Y3jx$2S{o6+n36(QS zNY2*_>${GSJl3jm@^Xd!FfWdvVEg`o&BF$zHP>n4avhilM@|u|VWkU^N-IgdT@k%! zdcE@W^oB$(kk#vjFb-uaqXb$C?(gqdtvakxr3ooYUEWy3x@%akI%0&OFIR?sKk>sq z{mk3P2T+CiDC?%wOI2l&-3qN0g0>L~?*d&;bC;aiu^l^{hU*N{-JAD4iaOfGo99RxL%eCh^ zT#1Y0^y#Ja7FIPuMT8Lyr=FM+!!*#|ZwN*b6(pqyxeVsUd1P5!2?MDTua&BNJ?p!B zgf?U$i<*maG;F(?=v~oI->_7=wjgfIKCR0-QAkhZT#x zCiCTbNI(UF@D|`?A)rxw1&;Y9jD_JB~8Jti&B@qydH{D zEI6ddtZYSCX11%cS$o*sGhZEl`o}*poO`14?3phU{xTzcBKwkmxgL(he!^cCQoj&-hrtqKrfpm1WoEoCoL@(l zF`-3S%gT8PNGX_31H&*NjH27M#CgUbIsE+0`OhDb%Z<1iJ#AA_Hy9j+Sc&0-!t}(-Up(x^yiWBc%_mCB~_`K3Xjkb@xVNIZYR;ZRX8~mc}Hs6*QZcWp)hbvAiLTDD)no7^aDbw~xdSF(^opuCCEq($p2+ zl~tW-D{NO&)lEToef~^aSI8wYyc}?SkM@~ye@5DJ6x%-D(X<_1({X%0Agw{v2HRHb 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 superresolution model +import dataclasses +import os +import re +import functools +from typing import Mapping, Any, Optional, Callable, Sequence +import logging + +import numpy as np +import jax +import jax.numpy as jnp +import seqio +from t5x import partitioning +from t5x import utils +from t5x import models as t5x_models +from seqio.vocabularies import PAD_ID +from rosetta.projects.diffusion import models +from rosetta.projects.diffusion import samplers +import matplotlib.image as matimg + +from rosetta.projects.diffusion.mm_utils import expand_dims_like +# Automatically search for gin files relative to the T5X package. +_DEFAULT_GIN_SEARCH_PATHS = [ + os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +] + +@dataclasses.dataclass +class DiffusionModelSetupData: + model: models.DenoisingDiffusionModel + sampling_cfg: samplers.SamplingConfig + restore_checkpoint_cfg: utils.RestoreCheckpointConfig + partitioner: partitioning.BasePartitioner + input_shapes: Mapping[str, Any] + input_types: Mapping[str, Any] + +def pad_right(tokens, seq_len, eos_id,pad_id): + padded, tok_lengths = [], [] + for t in tokens: + diff = seq_len - (len(t) + 1) + #assert diff >= 0 + if diff < 0: + padded.append(t[:seq_len - 1] + [eos_id]) + tok_lengths.append(seq_len) + else: + padded.append(t + [eos_id] + [pad_id] * diff) + tok_lengths.append(len(t) + 1) + + return jnp.array(padded, dtype=jnp.int32), tok_lengths, seq_len + +def seqio_preprocessing(mbatch, vocab:Any, seq_len:int=128): + return pad_right(vocab.encode(mbatch), seq_len=seq_len, eos_id=vocab.eos_id, pad_id=PAD_ID) + +def setup_text_enc(model: t5x_models.BaseTransformerModel, + restore_checkpoint_cfg: utils.RestoreCheckpointConfig, + partitioner: partitioning.BasePartitioner, + batch_size=1, seq_len=128, vocab=None, + ): + input_shapes = {'encoder_input_tokens': (batch_size, seq_len)} + + train_state_initializer = utils.TrainStateInitializer( + optimizer_def=None, # Do not load optimizer state. + init_fn=model.get_initial_variables, + input_shapes=input_shapes, + partitioner=partitioner) + train_state_axes = train_state_initializer.train_state_axes + + # Disable strictness since we are dropping the optimizer state. + restore_checkpoint_cfg.strict = False + + fallback_init_rng = None + + if fallback_init_rng is not None: + fallback_init_rng = jax.random.PRNGKey(fallback_init_rng) + train_state = list(train_state_initializer.from_checkpoints([restore_checkpoint_cfg], init_rng=fallback_init_rng))[0] + logging.warning(f'Restored from Checkpoint: {train_state[1]}') + train_state = train_state[0] + + partitioned_fn = partitioner.partition( + model.score_batch, + in_axis_resources=(train_state_axes.params, partitioning.PartitionSpec('data',)), + out_axis_resources=None) + + def infer_fn(inputs: Sequence[str]): + tokenized_padded, batch_len, curr_seqlen = seqio_preprocessing(inputs, vocab, seq_len=seq_len) + results = partitioned_fn(train_state.params, {"encoder_input_tokens": tokenized_padded}).astype(jnp.float16) + + bs = len(inputs) + individual_shape = results[0].shape + padded_output = np.zeros((bs, *individual_shape), dtype=np.float16) + for idx, (tensor, true_len) in enumerate(zip(results, batch_len)): + padded_output[idx, :true_len] = tensor[:true_len] + + return padded_output, jnp.array(batch_len, dtype=np.int32) + + + return infer_fn + +def get_sample_fn(model_setup:DiffusionModelSetupData): + train_state_initializer = utils.TrainStateInitializer( + optimizer_def=None, # Do not load optimizer state. + init_fn=model_setup.model.get_initial_variables, + input_shapes=model_setup.input_shapes, + input_types=model_setup.input_types, + partitioner=model_setup.partitioner) + + train_state_axes = train_state_initializer.train_state_axes + + # Disable strictness since we are dropping the optimizer state. + model_setup.restore_checkpoint_cfg.strict = False + + fallback_init_rng = None + + if fallback_init_rng is not None: + fallback_init_rng = jax.random.PRNGKey(fallback_init_rng) + train_state = list(train_state_initializer.from_checkpoints([model_setup.restore_checkpoint_cfg], init_rng=fallback_init_rng))[0] + logging.warning(f'Restored from Checkpoint: {train_state[1]}') + train_state = train_state[0] + + model_pred = functools.partial(model_setup.model.predict_batch, sampling_cfg=model_setup.sampling_cfg) + partitioned_fn = model_setup.partitioner.partition( + model_pred, + in_axis_resources=(train_state_axes.params, model_setup.partitioner.data_partition_spec, None), + out_axis_resources=model_setup.partitioner.data_partition_spec) + + return train_state.params, partitioned_fn + +def sanitize_filename(filename): + # Remove leading and trailing whitespaces + filename = filename.strip() + + # Replace spaces with underscores + filename = filename.replace(" ", "_") + + # Remove any other characters that are not allowed in a Linux filename + filename = re.sub(r'[^\w\.-]', '', filename) + + # Remove forward slashes + filename = filename.replace("/", "") + + return filename + +def sample( + base_setupdata: DiffusionModelSetupData, + sr256_setupdata: DiffusionModelSetupData, + out_dir: str, + sr1024_setupdata: DiffusionModelSetupData=None, + gen_per_prompt: int = 1, + text_enc_infer = Callable, + prompt_file=None, + batch_size=32, + max_images=50000000, + base_img_size=(64, 64, 3), + sr256_img_size=(256, 256, 3), + sr1024_img_size=(1024, 1024, 3), + noise_conditioning_aug=0.002, + resume_from=0 + ): + if not os.path.exists(out_dir): + os.makedirs(out_dir) + base_dir = os.path.join(out_dir, 'base') + sr_dir = os.path.join(out_dir, 'sr') + sr2_dir = os.path.join(out_dir, 'sr2') + if not os.path.exists(base_dir): + os.makedirs(base_dir) + if not os.path.exists(sr_dir): + os.makedirs(sr_dir) + if sr1024_setupdata is not None and not os.path.exists(sr_dir): + os.makedirs(sr_dir) + + with open(prompt_file, 'r') as f: + prompts = f.readlines() + prompt_ct = len(prompts) + + # Set up models + base_params, base_fn = get_sample_fn(base_setupdata) + sr256_params, sr256_fn = get_sample_fn(sr256_setupdata) + if sr1024_setupdata is not None: + sr1024_params, sr1024_fn = get_sample_fn(sr1024_setupdata) + text_encoder = text_enc_infer + + sampled_ctr = 0 + rng = jax.random.PRNGKey(0) + for start_idx in range(resume_from, max_images, batch_size // gen_per_prompt): + if start_idx > prompt_ct: + break + prompt_batch = prompts[start_idx: start_idx + (batch_size // gen_per_prompt)] * gen_per_prompt + rng, rng_base, rng_sr, rng_sr2, rng_aug = jax.random.split(rng, 5) + + # Encode Text + encoded_text, text_lens = text_encoder(prompt_batch) + text_mask = np.zeros(encoded_text.shape[:2]) + for i in range(text_lens.shape[0]): + text_mask[i][:text_lens[i]] = 1 + + # Base model generation + base_img_inputs = jnp.zeros((len(prompt_batch), *base_img_size)) + sr256_img_inputs = jnp.zeros((len(prompt_batch), *sr256_img_size)) + sr1024_img_inputs = jnp.zeros((len(prompt_batch), *sr1024_img_size)) + base_batch = {'samples': base_img_inputs, 'text': encoded_text, 'text_mask': text_mask} + base_out = base_fn(base_params, base_batch, rng_base) + for i in range(base_out.shape[0]): + matimg.imsave(os.path.join(base_dir, sanitize_filename(f'{prompt_batch[i]}_{sampled_ctr + i}.png')), np.clip(base_out[i], a_min=0, a_max=1)) + + # Stage 2: Super Resolution (64-> 256) + base_aug = (base_out * 2 - 1) + noise_aug_level = expand_dims_like(jnp.ones((base_aug.shape[0], )) * noise_conditioning_aug, base_aug) + sr256_batch = {'samples': sr256_img_inputs, 'text':encoded_text, 'text_mask':text_mask, 'low_res_images': base_aug, 'noise_aug_level': noise_aug_level} + sr_out = sr256_fn(sr256_params, sr256_batch, rng_sr) + sr_out = jnp.clip(sr_out, a_min = 0, a_max = 1) + for i in range(sr_out.shape[0]): + matimg.imsave(os.path.join(sr_dir, sanitize_filename(f'{prompt_batch[i]}_{sampled_ctr + i}.png')), sr_out[i]) + + # Stage 3: Super Resolution (256-> 1024) + if sr1024_setupdata is not None: + sr_aug = (sr_out * 2 - 1) + noise_aug_level = expand_dims_like(jnp.ones((sr_aug.shape[0], )) * noise_conditioning_aug, base_aug) + sr1024_batch = {'samples': sr1024_img_inputs, 'text':encoded_text, 'text_mask':text_mask, 'low_res_images': sr_aug, 'noise_aug_level': noise_aug_level} + sr_out = sr1024_fn(sr1024_params, sr1024_batch, rng_sr2) + sr_out = jnp.clip(sr_out, a_min = 0, a_max = 1) + for i in range(sr_out.shape[0]): + matimg.imsave(os.path.join(sr2_dir, sanitize_filename(f'{prompt_batch[i]}_{sampled_ctr + i}.png')), sr_out[i]) + + sampled_ctr += sr_out.shape[0] + + +if __name__ == '__main__': + # pylint: disable=g-import-not-at-top + from absl import app + from absl import flags + import gin + from t5x import gin_utils + import tensorflow as tf + # pylint: enable=g-import-not-at-top + FLAGS = flags.FLAGS + + jax.config.parse_flags_with_absl() + + flags.DEFINE_multi_string( + 'gin_file', + default=None, + help='Path to gin configuration file. Multiple paths may be passed and ' + 'will be imported in the given order, with later configurations ' + 'overriding earlier ones.') + + flags.DEFINE_multi_string( + 'gin_bindings', default=[], help='Individual gin bindings.') + + flags.DEFINE_list( + 'gin_search_paths', + default=['.'], + help='Comma-separated list of gin config path prefixes to be prepended ' + 'to suffixes given via `--gin_file`. If a file appears in. Only the ' + 'first prefix that produces a valid path for each suffix will be ' + 'used.') + + flags.DEFINE_boolean( + 'multiprocess_gpu', + False, + help='Initialize JAX distributed system for multi-host GPU, using ' + '`coordinator_address`, `process_count`, and `process_index`.') + + flags.DEFINE_string( + 'coordinator_address', + None, + help='IP address:port for multi-host GPU coordinator.') + + flags.DEFINE_integer( + 'process_count', None, help='Number of processes for multi-host GPU.') + + flags.DEFINE_integer('process_index', None, help='Index of this process.') + + + def main(argv: Sequence[str]): + """Wrapper for pdb post mortems.""" + _main(argv) + + def _main(argv: Sequence[str]): + """True main function.""" + if len(argv) > 1: + raise app.UsageError('Too many command-line arguments.') + + # OOM fix. Prevents TF from seeing GPUs to stop conflict with JAX. + # This must go after InitGoogle(), which is called by + # gin_utils.run(main). + tf.config.experimental.set_visible_devices([], 'GPU') + + + if FLAGS.multiprocess_gpu: + logging.info( + 'Initializing distributed system for multi-host GPU:\n' + ' coordinator_address: %s\n process_count: %s\n process_index: %s', + FLAGS.coordinator_address, FLAGS.process_count, FLAGS.process_index) + + jax.distributed.initialize(FLAGS.coordinator_address, FLAGS.process_count, + FLAGS.process_index) + + # Create gin-configurable version of `train`. + sample_using_gin = gin.configurable(sample) + + gin_utils.parse_gin_flags( + # User-provided gin paths take precedence if relative paths conflict. + FLAGS.gin_search_paths + _DEFAULT_GIN_SEARCH_PATHS, + FLAGS.gin_file, + FLAGS.gin_bindings) + sample_using_gin() + jax.effects_barrier() + + + gin_utils.run(main) diff --git a/rosetta/rosetta/projects/imagen/layers.py b/rosetta/rosetta/projects/imagen/layers.py new file mode 100644 index 000000000..e245df7dd --- /dev/null +++ b/rosetta/rosetta/projects/imagen/layers.py @@ -0,0 +1,648 @@ +# Copyright (c) 2022-2023 NVIDIA Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"Diffusion U-Net layers" + +# pylint: disable=attribute-defined-outside-init,g-bare-generic + +#TODO Dropout +import dataclasses +import functools +import operator +from typing import Any, Callable, Iterable, Optional, Sequence, Tuple, Union + +from flax import linen as nn +from flax.linen import partitioning as nn_partitioning +import jax +from jax import lax +from jax import random +import jax.numpy as jnp +import numpy as np +from einops import rearrange + +from t5x.contrib.gpu.t5.layers import MultiHeadDotProductAttention, make_attention_mask, combine_biases, dot_product_attention, RelativePositionBiases, MlpBlock +from flax.linen import DenseGeneral, GroupNorm, Conv, LayerNorm +from rosetta.projects.imagen.network import DiffusionConfig + +param_with_axes = nn_partitioning.param_with_axes +with_sharding_constraint = nn_partitioning.with_sharding_constraint + +# Type annotations +Array = jnp.ndarray +OptionalArray = Optional[jnp.ndarray] +DType = jnp.dtype +PRNGKey = jnp.ndarray +Shape = Iterable[int] +Activation = Callable[..., Array] +# Parameter initializers. +Initializer = Callable[[PRNGKey, Shape, DType], Array] +PrecisionLike = Union[None, str, lax.Precision, Tuple[str, str], + Tuple[lax.Precision, lax.Precision]] +PaddingLike = Union[str, int, Sequence[Union[int, Tuple[int, int]]]] +LaxPadding = Union[str, Sequence[Tuple[int, int]]] +Dtype = Any + +default_embed_init = nn.initializers.variance_scaling( + 1.0, 'fan_in', 'normal', out_axis=0) + +dynamic_vector_slice_in_dim = jax.vmap( + lax.dynamic_slice_in_dim, in_axes=(None, 0, None, None)) + +def _convert_to_activation_function( + fn_or_string: Union[str, Callable]) -> Callable: + """Convert a string to an activation function.""" + if fn_or_string == 'linear': + return lambda x: x + elif isinstance(fn_or_string, str): + return getattr(nn, fn_or_string) + elif callable(fn_or_string): + return fn_or_string + else: + raise ValueError("don't know how to convert %s to an activation function" % + (fn_or_string,)) + +def get_timestep_embedding(timesteps, embedding_dim: int, dtype=jnp.float32): + """Build sinusoidal embeddings + Args: + timesteps: jnp.ndarray: generate embedding vectors at these timesteps + embedding_dim: int: dimension of the embeddings to generate + dtype: data type of the generated embeddings + Returns: + embedding vectors with shape `[len(timesteps), embedding_dim]` + """ + timesteps = jnp.reshape(timesteps, timesteps.shape[0]) + assert len(timesteps.shape) == 1, "timesteps don't have one dimension, " + str(timesteps.shape) + half = embedding_dim // 2 + freqs = jnp.exp( + -np.log(10000) * jnp.arange(0, half, dtype=dtype) / half + ) + args = timesteps[:, None] * freqs[None] + embedding = jnp.concatenate([jnp.cos(args), jnp.sin(args)], axis=-1) + + if embedding_dim % 2 == 1: # zero pad + embedding = jax.lax.pad(embedding, dtype(0), ((0, 0, 0), (0, 1, 0))) + assert embedding.shape == (timesteps.shape[0], embedding_dim) + + return embedding + +def FP32Wrap(operator, should_fp32=False): + if should_fp32: + def ret(x): + h = jnp.asarray(x, dtype=jnp.float32) + return jnp.asarray(operator(h), dtype=x.dtype) + return ret + else: + return operator + +class FusedSelfCrossMultiHeadDotProductAttention(nn.Module): + """Fused self attention with cross attention for image-text self and cross attn. + + Attributes: + num_heads: number of attention heads. Features (i.e. inputs_q.shape[-1]) + should be divisible by the number of heads. + head_dim: dimension of each head. + dtype: the dtype of the computation. + dropout_rate: dropout rate + kernel_init: initializer for the kernel of the Dense layers. + float32_logits: bool, if True then compute logits in float32 to avoid + numerical issues with bfloat16. + zero_out: bool. Will initialize out projection to zero if true + """ + + num_heads: int + head_dim: int + dtype: DType = jnp.float32 + dropout_rate: float = 0. + kernel_init: Initializer = nn.initializers.variance_scaling( + 1.0, 'fan_in', 'normal') + float32_logits: bool = False # computes logits in float32 for stability. + scale_attn_logits: bool = False + zero_out: bool = True + project_output: bool = True + + @nn.compact + def __call__(self, + inputs_q: Array, + inputs_kv_self: Array, + inputs_kv_cross: Optional[Array] = None, + mask: Optional[Array] = None, + bias: Optional[Array] = None, + *, + deterministic: bool = False) -> Array: + """Applies self and cross attention on the input data. + + Projects the inputs into multi-headed query, key, and value vectors, + applies dot-product attention and project the results to an output vector. + + Args: + inputs_q: input queries of shape `[batch, q_length, q_features]`. + inputs_kv_self: key/values of shape `[batch, kv_self_length, kv_self_features]`. + inputs_kv_cross: key/values of shape `[batch, kv_cross_length, kv_cross_features]' + mask: attention mask of shape `[batch, num_heads, q_length, kv_length]`. + bias: attention bias of shape `[batch, num_heads, q_length, kv_length]`. + decode: Whether to prepare and use an autoregressive cache. + deterministic: Disables dropout if set to True. + + Returns: + output of shape `[batch, length, q_features]`. + """ + projection = functools.partial( + DenseGeneral, + axis=-1, + features=(self.num_heads, self.head_dim), + kernel_axes=('embed', 'joined_kv'), + use_bias=True, + dtype=self.dtype) + + # NOTE: T5 does not explicitly rescale the attention logits by + # 1/sqrt(depth_kq)! This is folded into the initializers of the + # linear transformations, which is equivalent under Adafactor. + depth_scaling = jnp.sqrt(self.head_dim).astype(self.dtype) + query_init = lambda *args: self.kernel_init(*args) / depth_scaling + + # Project inputs_q to multi-headed q/k/v + # dimensions are then [batch, length, num_heads, head_dim] + query = projection(kernel_init=query_init, name='query')( \ + (inputs_q / depth_scaling) if self.scale_attn_logits else inputs_q) + key = projection(kernel_init=self.kernel_init, name='key')(inputs_kv_self) + value = projection(kernel_init=self.kernel_init, name='value')(inputs_kv_self) + + query = with_sharding_constraint(query, ('batch', 'length', 'heads', 'kv')) + + if inputs_kv_cross is not None: + key_cross = projection(kernel_init=self.kernel_init, name='key_cross')(inputs_kv_cross) + value_cross = projection(kernel_init=self.kernel_init, name='value_cross')(inputs_kv_cross) + + # Concatenate on length axis + key = jnp.concatenate((key, key_cross), axis=1) + value = jnp.concatenate((value, value_cross), axis = 1) + + key = with_sharding_constraint(key, ('batch', 'length', 'heads', 'kv')) + value = with_sharding_constraint(value, ('batch', 'length', 'heads', 'kv')) + + # Convert the boolean attention mask to an attention bias. + if mask is not None: + # attention mask in the form of attention bias + attention_bias = lax.select( + mask > 0, + jnp.full(mask.shape, 0.).astype(self.dtype), + jnp.full(mask.shape, -1e10).astype(self.dtype)) + else: + attention_bias = None + + # Add provided bias term (e.g. relative position embedding). + if bias is not None: + attention_bias = combine_biases(attention_bias, bias) + + dropout_rng = None + if not deterministic and self.dropout_rate > 0.: + dropout_rng = self.make_rng('dropout') + + # Apply attention. + x = dot_product_attention( + query, + key, + value, + bias=attention_bias, + dropout_rng=dropout_rng, + dropout_rate=self.dropout_rate, + deterministic=deterministic, + dtype=self.dtype, + float32_logits=self.float32_logits) + + if self.project_output: + # Back to the original inputs dimensions. + out = DenseGeneral( + features=inputs_q.shape[-1], # output dim is set to the input dim. + axis=(-2, -1), + kernel_init=self.kernel_init if not self.zero_out else nn.initializers.zeros, + kernel_axes=('joined_kv', 'embed'), + use_bias=True, + dtype=self.dtype, + name='out')( + x) + return out + else: + return x.reshape((x.shape[0], x.shape[1], x.shape[2] * x.shape[3])) + +class AttentionPoolingBlock(nn.Module): + """ + Attention Pooling via self+cross attention with the mean. Assumes inputs + are normalized already. Uses RelativePositionBiases. + + Attributes: + cfg: Model-wide configuration. This will use the same parameters + as all other attention layers. + num_heads: optional override on the number of heads in this layer + """ + cfg: DiffusionConfig + num_heads: Optional[int] = None + + @nn.compact + def __call__(self, + inputs: Array, + text_lens: Optional[Array] = None, + *, + deterministic: bool = False) -> Array: + """ Performs attention pooling by doing cross attention between mean embedding and + all tokens. + + Args: + inputs: input sequence of shape `[batch, seq_length, features]`. + text_lens: Array of text masks (for masking) [batch, seq_length] + bias: attention bias of shape `[batch, num_heads, q_length, kv_length]`. + deterministic: Disables dropout if set to True. + + Returns: + output of shape `[batch, features]`. + """ + cfg = self.cfg + num_heads = self.num_heads if self.num_heads is not None else cfg.attn_heads + + pos_bias = RelativePositionBiases( + num_buckets=32, + max_distance=128, + num_heads=num_heads, + dtype=cfg.dtype, + embedding_init=nn.initializers.variance_scaling(1.0, 'fan_avg', + 'uniform'), + name='relpos_bias')(1, inputs.shape[1] + 1, False) + + mask = None + if text_lens is not None: + m = jnp.ones((inputs.shape[0], 1)) + mt = jnp.concatenate((m, text_lens), axis = 1) + mask = make_attention_mask(m, mt, dtype=cfg.dtype) + + text_mask = rearrange(text_lens, 'b l -> b l 1') + masked_inputs = inputs * text_mask + mean = jnp.mean(masked_inputs, axis=1, keepdims=True) # [batch, 1, q_features] + out = MultiHeadDotProductAttention(num_heads=num_heads, + head_dim=cfg.mha_head_dim, + dtype=cfg.dtype, + dropout_rate=cfg.dropout_rate, + float32_logits=cfg.float32_attention_logits, + scale_attn_logits=cfg.scale_attn_logits) \ + (mean, jnp.concatenate((mean, inputs), axis=1), mask=mask, bias=pos_bias, deterministic=deterministic) + out = rearrange(out, 'b l e -> b (l e)') # l should equal 1 + assert out.shape[1] == inputs.shape[2] + return out + +class DeepFloydAttentionPoolingBlock(nn.Module): + """ + Attention Pooling via self+cross attention with the mean. Assumes inputs + are normalized already. Uses Absolute Position Embeddings. + + Attributes: + cfg: Model-wide configuration. This will use the same parameters + as all other attention layers. + num_heads: optional override on the number of heads in this layer + """ + cfg: DiffusionConfig + num_heads: Optional[int] = None + + @nn.compact + def __call__(self, + inputs: Array, + text_lens: Optional[Array] = None, + *, + deterministic: bool = False) -> Array: + """ Performs attention pooling by doing cross attention between mean embedding and + all tokens. + + Args: + inputs: input sequence of shape `[batch, seq_length, features]`. + text_lens: Array of text masks (for masking) [batch, seq_length] + bias: attention bias of shape `[batch, num_heads, q_length, kv_length]`. + deterministic: Disables dropout if set to True. + + Returns: + output of shape `[batch, features]`. + """ + cfg = self.cfg + num_heads = self.num_heads if self.num_heads is not None else cfg.attn_heads + + embedding_init = nn.initializers.normal(stddev=jnp.sqrt(inputs.shape[2])) + pos_bias = param_with_axes('position_embed', embedding_init, (1, inputs.shape[2],), jnp.float32, axes=('empty', 'embed')) + pos_bias = jnp.asarray(pos_bias, dtype=inputs.dtype) + pos_bias = rearrange(pos_bias, '1 e -> 1 1 e') + mask = None + if text_lens is not None: + m = jnp.ones((inputs.shape[0], 1)) + mt = jnp.concatenate((m, text_lens), axis = 1) + mask = make_attention_mask(m, mt, dtype=cfg.dtype) + + text_mask = rearrange(text_lens, 'b l -> b l 1') + masked_inputs = inputs * text_mask + mean = jnp.mean(masked_inputs, axis=1, keepdims=True) + pos_bias # [batch, 1, q_features] + out = FusedSelfCrossMultiHeadDotProductAttention(num_heads=num_heads, + head_dim=cfg.mha_head_dim, + dtype=cfg.dtype, + dropout_rate=cfg.dropout_rate, + float32_logits=cfg.float32_attention_logits, + scale_attn_logits=cfg.scale_attn_logits, + project_output=False) \ + (mean, jnp.concatenate((mean, inputs), axis=1), None, mask=mask, bias=None, deterministic=deterministic) + out = rearrange(out, 'b l e -> b (l e)') # l should equal 1 + assert out.shape[1] == inputs.shape[2] + return out + + +class ImgAttentionBlock(nn.Module): + """ Residual MHA block with normalization and reshaping for images + and optional text conditioning. + + cfg: DiffusionConfig + num_heads: Optional number of heads for attention. Will default to + cfg.attn_heads + """ + cfg: DiffusionConfig + num_heads: Optional[int] + + @nn.compact + def __call__(self, + inputs: Array, + text_enc: Optional[Array]=None, + text_mask: Optional[Array]=None, + deterministic=True) -> Array: + """ + Applies self-attention to an image an optionally text encodings. Assumes text_enc + has been normalized already. + + Args: + inputs: Images [b, h, w, c] + text_enc: Text encodings [b, l, c] + text_mask: Array of text masks (for masking) [b, l] + deterministic: Whether to enable dropout + """ + + cfg = self.cfg + x = rearrange(inputs, 'b h w c -> b (h w) c') + if cfg.unified_qkv_norm: + x_q = FP32Wrap(GroupNorm(num_groups=32, name='img_attn_gn'), should_fp32=cfg.norm_32)(x) + x_kv = x_q + else: + x_q = FP32Wrap(GroupNorm(num_groups=32, name='img_attn_gn_q'), should_fp32=cfg.norm_32)(x) + x_kv = FP32Wrap(GroupNorm(num_groups=32, name='img_attn_gn_kv'), should_fp32=cfg.norm_32)(x) + + mask = None + if text_enc is not None: + text_enc = FP32Wrap(GroupNorm(num_groups=32, name='text_enc_ln'), should_fp32=cfg.norm_32)(text_enc) + if text_mask is not None: + m = jnp.ones((x.shape[0], x.shape[1])) + mt = jnp.concatenate((m, text_mask), axis = 1) + mask = make_attention_mask(m, mt, dtype=cfg.dtype) + + num_heads = self.num_heads if self.num_heads is not None else cfg.attn_heads + x = FusedSelfCrossMultiHeadDotProductAttention(num_heads=num_heads, + head_dim=cfg.mha_head_dim, + dtype=cfg.dtype, + dropout_rate=cfg.dropout_rate, + float32_logits=cfg.float32_attention_logits, + scale_attn_logits=cfg.scale_attn_logits, + name='mha_layer')(x_q, x_kv, text_enc, mask=mask, deterministic=deterministic) + + x = rearrange(x, 'b (h w) c -> b h w c', h=inputs.shape[1], w=inputs.shape[2]) + return x + inputs + +def identity(x: Array) -> Array: + return x + +class ResBlock(nn.Module): + """ Residual block in the style of Nichol et. al. Used in a UNet + + Attributes: + cfg: DiffuionConfig + out_channels: Output channel count + up_down_sample: 'up', 'down', or 'none'. Sets if upscaling, downscaling or neither + kernel_init: Kernel init function for embedding layers and first conv + """ + cfg: DiffusionConfig + out_channels: int + up_down_sample: str = 'none' + kernel_init: Optional[Initializer] = nn.initializers.lecun_normal() + + def _get_scaling_block(self, up_down:str): + cfg = self.cfg + if up_down == 'up': + return Upsample(mode=self.cfg.upsample_mode, + kernel_init=self.kernel_init, + dtype=cfg.dtype, + norm_32=self.cfg.norm_32) + elif up_down == 'down': + return Downsample(mode=self.cfg.downsample_mode, + kernel_init=self.kernel_init, + dtype=cfg.dtype) + elif up_down == 'none': + return identity + else: + raise ValueError(f'Attempting to construct a resblock with up_down \ + type {up_down}. Please use one of \'up\', \'down\', or \'none\'') + + @nn.compact + def __call__(self, inputs, + conditioning, + deterministic: bool=False): + """ Apply ResBlock. + + input shape: B H W C_model + conditioning: B C_cond_embedding + """ + cfg = self.cfg + + # normalization = GroupNormFP32 if cfg.norm_32 else GroupNorm + activation = _convert_to_activation_function(cfg.resblock_activation) + spatial_scaling_fn = self._get_scaling_block(self.up_down_sample) + spatial_conv = functools.partial(Conv, + self.out_channels, + kernel_size=(3,3), + strides=1, + padding='SAME', + dtype=cfg.dtype) + + # conditioning embedding calculation + cond_dim = self.out_channels * (2 if cfg.cond_strategy == 'shift_scale' else 1) + cond = activation(conditioning) + cond = DenseGeneral(cond_dim, + axis=-1, + dtype=cfg.dtype, + use_bias=True, + kernel_axes=('embed',))(cond) + cond = rearrange(cond, 'b c -> b 1 1 c') + + # spatial scaling residual + res = spatial_scaling_fn(inputs) + + # in block + # ensure input channels % 32 == 0 + h = FP32Wrap(GroupNorm(num_groups=32, name='resblock_pre_in_conv_groupnorm'), should_fp32=cfg.norm_32)(inputs) + h = activation(h) + h = spatial_scaling_fn(h) + h = spatial_conv(kernel_init=self.kernel_init, name='resblock_in_conv')(h) + + # combine embedding + out_block + out_norm = FP32Wrap(GroupNorm(num_groups=32, name='resblock_pre_out_conv_groupnorm'), should_fp32=cfg.norm_32) + if cfg.cond_strategy == 'shift_scale': + h = out_norm(h) + # combine embedding + shift, scale = jnp.split(cond, 2, axis=-1) + h = h * (scale + 1) + shift + elif cfg.cond_strategy == 'addition': + h = h + cond + h = out_norm(h) + else: + NotImplementedError(cfg.cond_strategy + " conditioning strategy not implemented.\ + Use \'shift_scale\' or \'addition\' instead.") + h = activation(h) + h = nn.Dropout(rate=cfg.dropout_rate)(h, deterministic=deterministic) + h = spatial_conv(kernel_init=nn.initializers.zeros, name='resblock_out_conv')(h) + + # residual channel adjustment + if self.out_channels != inputs.shape[-1]: + if cfg.spatial_skip: + res = spatial_conv(kernel_init=self.kernel_init, name='resblock_skip_conv')(res) + else: + res = Conv(self.out_channels, + kernel_size=(1,1), + dtype=cfg.dtype, + kernel_init=self.kernel_init, + name='resblock_skip_conv')(res) + + # residual addition + out_sum = h + res + if cfg.resblock_scale_skip: + return out_sum * .7071 # 1/sqrt(2) + else: + return out_sum + +def _pixel_shuffle_kernel_init(key, shape, dtype, window=2): + """ + Conv kernel init with replication over the shuffled axis. + Replicated such that initial initial upscales will be like interpolated onces. + """ + h, w, i, o = shape + jax.debug.print('Conv kernel shape: ', str(shape)) + partial_shape = h, w, i, o // (window ** 2) + init = nn.initializers.kaiming_uniform()(key, partial_shape, dtype) + repl = jnp.repeat(init, window ** 2, axis=3) # H, W, I, O + return repl + +class Upsample(nn.Module): + """ + Upsampling module done optionally with convolution + + Attributes: + scaling_factor: Defaults to 2. Identical for all axes + mode: 'shuffle': pixel shuffle + 'conv' : interpolate -> 3x3 convolution + 'resize' : interpolated scaling + kernel_init: conv kernel init + dtype: conv dtype + """ + scaling_factor: int = 2 + mode: str = 'shuffle' + kernel_init: Optional[Initializer] = nn.initializers.lecun_normal() + dtype: Any = jnp.float32 + norm_32: bool = True + + @nn.compact + def __call__(self, x: Array) -> Array: + """ Upscales input by self.scaling_factor in HW dims assuming NHWC format """ + in_ch = x.shape[-1] + + if self.mode == 'resize' or self.mode == 'conv': + n, h, w, c = x.shape + h1 = h * self.scaling_factor + w1 = w * self.scaling_factor + x = jax.image.resize(x, (n, h1, w1, c), method='bilinear') + if self.mode == 'resize': + return x # early return for simple interpolation + + kernel=(3,3) + out_ch=x.shape[-1] + + elif self.mode == 'shuffle': + kernel=(1,1) + out_ch=x.shape[-1] * self.scaling_factor ** 2 + + else: + ValueError("Upsample mode must be \'resize\',\'conv\', or \ + \'shuffle\'. " + self.mode + " is not supported.") + exit() + + # 'conv' -> out_ch=in_ch, kernel=3 + # 'pix_shuffle' -> out_ch=in_ch * scaling_factor **2, kernel=1 + x = Conv(out_ch, + kernel_size=kernel, + strides=1, + dtype=self.dtype, + kernel_init=self.kernel_init, + name='upsample_convolution')(x) + + if self.mode == 'shuffle': + x = FP32Wrap(GroupNorm(num_groups=32, name='pix_shuffle_gn'), should_fp32=self.norm_32)(x) + x = _convert_to_activation_function('silu')(x) + + # shifting channel dims into square spatial pixel dims + return rearrange(x, 'b h w (s1 s2 c) -> b (h s1) (w s2) c', \ + s1=self.scaling_factor, s2=self.scaling_factor) + else: #mode == 'conv' + return x + + +class Downsample(nn.Module): + """ + Downsampling module done optionally with convolution + + Attributes: + scaling_factor: Defaults to 2. Identical for all axes + mode: 'shuffle': SP-conv from: https://arxiv.org/pdf/2208.03641.pdf. + Basically pixel unshuffle + 'conv' : strided convolution downsampling + 'resize' : average pooling + kernel_init: conv kernel init + dtype: conv dtype + """ + scaling_factor: int = 2 + mode: str = 'shuffle' + kernel_init: Optional[Initializer] = nn.initializers.lecun_normal() + dtype: Any = jnp.float32 + + @nn.compact + def __call__(self, x:Array) -> Array: + channels = x.shape[-1] + if self.mode == 'resize': + window_tuple = (self.scaling_factor, self.scaling_factor) + return nn.avg_pool(x, window_tuple, window_tuple) + elif self.mode == 'conv': + kernel_size = (3,3) + stride=self.scaling_factor + padding = 1 + elif self.mode == 'shuffle': + kernel_size = (1,1) + stride=1 + padding = 0 + x = rearrange(x, 'b (h s1) (w s2) c -> b h w (s1 s2 c)', + s1 = self.scaling_factor, s2 = self.scaling_factor) + else: + raise ValueError('Downsampling mode must be \'resize\', \'conv\',\ + or \'shuffle\'. ' + self.mode + " not supported") + + return Conv(channels, + kernel_size=kernel_size, + strides=stride, + padding=padding, + dtype=self.dtype, + kernel_init=self.kernel_init, + name='downsample_convolution')(x) \ No newline at end of file diff --git a/rosetta/rosetta/projects/imagen/layers_sr.py b/rosetta/rosetta/projects/imagen/layers_sr.py new file mode 100644 index 000000000..aa4fa3711 --- /dev/null +++ b/rosetta/rosetta/projects/imagen/layers_sr.py @@ -0,0 +1,299 @@ +# Copyright (c) 2022-2023 NVIDIA Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"Diffusion Efficient U-Net layers. Mostly used for superresolution" + +# pylint: disable=attribute-defined-outside-init,g-bare-generic + +#TODO Dropout +import dataclasses +import functools +import operator +from typing import Any, Callable, Iterable, Optional, Sequence, Tuple, Union + +from flax import linen as nn +from flax.linen import partitioning as nn_partitioning +import jax +from jax import lax +from jax import random +import jax.numpy as jnp +import numpy as np +from einops import rearrange + +from t5x.contrib.gpu.t5.layers import MultiHeadDotProductAttention, make_attention_mask, combine_biases, dot_product_attention, RelativePositionBiases, MlpBlock +from flax.linen import DenseGeneral, GroupNorm, Conv, ConvTranspose, LayerNorm +from rosetta.projects.imagen.layers import FusedSelfCrossMultiHeadDotProductAttention + +param_with_axes = nn_partitioning.param_with_axes +with_sharding_constraint = nn_partitioning.with_sharding_constraint + +# Type annotations +Array = jnp.ndarray +DType = jnp.dtype +PRNGKey = jnp.ndarray +Shape = Iterable[int] +Activation = Callable[..., Array] +# Parameter initializers. +Initializer = Callable[[PRNGKey, Shape, DType], Array] +PrecisionLike = Union[None, str, lax.Precision, Tuple[str, str], + Tuple[lax.Precision, lax.Precision]] +PaddingLike = Union[str, int, Sequence[Union[int, Tuple[int, int]]]] +LaxPadding = Union[str, Sequence[Tuple[int, int]]] +Dtype = Any + +default_embed_init = nn.initializers.variance_scaling( + 1.0, 'fan_in', 'normal', out_axis=0) + +dynamic_vector_slice_in_dim = jax.vmap( + lax.dynamic_slice_in_dim, in_axes=(None, 0, None, None)) + +def _convert_to_activation_function( + fn_or_string: Union[str, Callable]) -> Callable: + """Convert a string to an activation function.""" + if fn_or_string == 'linear': + return lambda x: x + elif isinstance(fn_or_string, str): + return getattr(nn, fn_or_string) + elif callable(fn_or_string): + return fn_or_string + else: + raise ValueError("don't know how to convert %s to an activation function" % + (fn_or_string,)) + +def FP32Wrap(operator, should_fp32=False): + if should_fp32: + def ret(x): + h = jnp.asarray(x, dtype=jnp.float32) + return jnp.asarray(operator(h), dtype=x.dtype) + return ret + else: + return operator + +class EfficientResBlock(nn.Module): + """ Residual block in the style of Saharia et. al. from Imagen + + Attributes: + out_channels: Output channel count + kernel_init: Kernel init function for embedding layers and first conv + """ + out_channels: int + dtype: Any = jnp.float32 + norm_32: bool = False + activation: str = 'silu' + cond_strategy: str = 'shift_scale' + dropout_rate: float = 0.0 + kernel_init: Optional[Initializer] = nn.initializers.lecun_normal() + zero_out: bool = True + scale_skip: bool = True + + @nn.compact + def __call__(self, inputs, + conditioning, + deterministic: bool=False): + """ Apply Efficient ResBlock. + + input shape: B H W C_model + conditioning: B C_cond_embedding + """ + + activation = _convert_to_activation_function(self.activation) + spatial_conv = functools.partial(Conv, + self.out_channels, + kernel_size=(3,3), + strides=(1,1), + padding='SAME', + dtype=self.dtype) + + # conditioning embedding calculation + cond_dim = self.out_channels * (2 if self.cond_strategy == 'shift_scale' else 1) + cond = activation(conditioning) + cond = DenseGeneral(cond_dim, + axis=-1, + dtype=self.dtype, + use_bias=True, + kernel_axes=('embed',))(cond) + cond = rearrange(cond, 'b c -> b 1 1 c') + + # in block + # ensure input channels % 32 == 0 + h = FP32Wrap(GroupNorm(num_groups=32, name='resblock_pre_in_conv_groupnorm'), should_fp32=self.norm_32)(inputs) + h = activation(h) + h = spatial_conv(kernel_init=self.kernel_init, name='resblock_in_conv')(h) + + # combine embedding + out_block + out_norm = FP32Wrap(GroupNorm(num_groups=32, name='resblock_pre_out_conv_groupnorm'), should_fp32=self.norm_32) + if self.cond_strategy == 'shift_scale': + h = out_norm(h) + # combine embedding + shift, scale = jnp.split(cond, 2, axis=-1) + h = h * (scale + 1) + shift + elif self.cond_strategy == 'addition': + h = h + cond + h = out_norm(h) + else: + NotImplementedError(self.cond_strategy + " conditioning strategy not implemented.\ + Use \'shift_scale\' or \'addition\' instead.") + h = activation(h) + h = nn.Dropout(rate=self.dropout_rate)(h, deterministic=deterministic) + out_init = nn.initializers.zeros if self.zero_out else self.kernel_init + h = spatial_conv(kernel_init=out_init, name='resblock_out_conv')(h) + + # residual channel adjustment + res = Conv(self.out_channels, + kernel_size=(1,1), + dtype=self.dtype, + kernel_init=self.kernel_init, + name='resblock_skip_conv')(inputs) + + # residual addition + if self.scale_skip: + res = res / jnp.sqrt(2) + return h + res + +class EfficientBlock(nn.Module): + """ + Down and Up Block from Saharia et. al. Imagen Efficient U-Net. + """ + out_channels: int + dtype: Any = jnp.float32 + norm_32: bool = False + activation: str = 'silu' + cond_strategy: str = 'shift_scale' + zero_out: bool = True + scale_skip: bool = True + dropout_rate: float = 0.0 + + use_attn: bool = False + attn_heads: int = 8 + mha_head_dim: int = 64 + attn_type: str = 'fused' # 'self', 'fused', or 'cross' + + up_down: str = 'down' # 'up', 'down' or None + num_resblocks: int = 2 + strides: tuple = (1,1) + + @nn.compact + def __call__(self, + inputs: Array, + conditioning: Array, + text_enc: Optional[Array]=None, + text_mask: Optional[Array]=None, + deterministic=False) -> Array: + # dblock in conv + if self.up_down == 'down': + inputs = Conv(self.out_channels, + kernel_size=(3,3), + strides=self.strides, + padding='SAME', + dtype=self.dtype, + name='dblock_in_conv')(inputs) + + h = inputs + for res_idx in range(self.num_resblocks): + h = EfficientResBlock(out_channels=self.out_channels, + dtype=self.dtype, + norm_32=self.norm_32, + activation=self.activation, + cond_strategy=self.cond_strategy, + dropout_rate=self.dropout_rate, + zero_out=self.zero_out, + scale_skip=self.scale_skip, + name=f'resblock_{res_idx}')(h, conditioning=conditioning, deterministic=deterministic) + if self.use_attn: + h = ImgAttentionBlock(attn_heads=self.attn_heads, + head_dim=self.mha_head_dim, + attn_type=self.attn_type, + dtype=self.dtype, + dropout_rate=self.dropout_rate, + scale_attn_logits=True, + name='attention')(h, text_enc, text_mask, deterministic=deterministic) + if self.up_down == 'up': + h = ConvTranspose(self.out_channels, kernel_size=(3,3), strides=self.strides, padding='SAME', dtype=self.dtype, name='ublock_out_conv')(h) + return h + +class ImgAttentionBlock(nn.Module): + """ Residual MHA block with normalization and reshaping for images + and optional text conditioning. + """ + norm_32: bool = False + attn_heads: int = 8 + head_dim: int = 64 + attn_type: str = 'fused' # 'self', 'fused', or 'cross' + float32_attention_logits: bool = False + scale_attn_logits: bool = True + zero_out: bool = True + dropout_rate: float = 0.0 + dtype: Any = jnp.float32 + + @nn.compact + def __call__(self, + inputs: Array, + text_enc: Optional[Array]=None, + text_mask: Optional[Array]=None, + deterministic=True) -> Array: + """ + Applies self-attention to an image an optionally text encodings. Assumes text_enc + has been normalized already. + + Args: + inputs: Images [b, h, w, c] + text_enc: Text encodings [b, l, c] + text_mask: Array of text masks (for masking) [b, l] + deterministic: Whether to enable dropout + """ + x = rearrange(inputs, 'b h w c -> b (h w) c') + x_q = FP32Wrap(GroupNorm(num_groups=32, name='img_attn_gn_q'), should_fp32=self.norm_32)(x) + x_kv = FP32Wrap(GroupNorm(num_groups=32, name='img_attn_gn_kv'), should_fp32=self.norm_32)(x) + + mask = None + if text_enc is not None: + text_enc = FP32Wrap(GroupNorm(num_groups=32, name='text_enc_ln'), should_fp32=self.norm_32)(text_enc) + if text_mask is None and text_enc is not None: + text_mask = jnp.ones((text_enc.shape[0], text_enc.shape[1])) + else: + if self.attn_type == 'cross': + raise ValueError('Cannot have both cross attention and no text conditioning.') + if self.attn_type == 'fused': + self.attn_type = 'self' + + m = jnp.ones((x.shape[0], x.shape[1])) + q = x_q + if self.attn_type == 'self': + mask = make_attention_mask(m, m, dtype=self.dtype) + kv_self = x_kv + kv_cross = None + elif self.attn_type == 'fused': + mt = jnp.concatenate((m, text_mask), axis = 1) + mask = make_attention_mask(m, mt, dtype=self.dtype) + kv_self = x_kv + kv_cross = text_enc + elif self.attn_type == 'cross': + mt = text_mask + mask = make_attention_mask(m, mt, dtype=self.dtype) + kv_self = text_enc + kv_cross = None + else: + raise NotImplementedError(f'attention type {self.attn_type} is not implemented. Please choose from self, cross, and fused') + + x = FusedSelfCrossMultiHeadDotProductAttention(num_heads=self.attn_heads, + head_dim=self.head_dim, + dtype=self.dtype, + dropout_rate=self.dropout_rate, + float32_logits=self.float32_attention_logits, + scale_attn_logits=self.scale_attn_logits, + zero_out=self.zero_out, + name='mha_layer')(q, kv_self, kv_cross, mask=mask, deterministic=deterministic) + + x = rearrange(x, 'b (h w) c -> b h w c', h=inputs.shape[1], w=inputs.shape[2]) + return x + inputs \ No newline at end of file diff --git a/rosetta/rosetta/projects/imagen/network.py b/rosetta/rosetta/projects/imagen/network.py new file mode 100644 index 000000000..0b955b41c --- /dev/null +++ b/rosetta/rosetta/projects/imagen/network.py @@ -0,0 +1,265 @@ +# Copyright (c) 2022-2023 NVIDIA Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Diffusion Model Backbones""" + +from typing import Any, Sequence, Union, Iterable, Optional +import functools + +from flax import linen as nn +from flax.linen import partitioning as nn_partitioning +from flax import struct +import jax.numpy as jnp +from t5x.contrib.gpu.t5.layers import MultiHeadDotProductAttention, make_attention_mask +from flax.linen import DenseGeneral, Conv, LayerNorm, GroupNorm +import jax +from einops import rearrange + +param_with_axes = nn_partitioning.param_with_axes +OptionalArray = Optional[jnp.ndarray] +Array = jnp.ndarray + +default_embed_init = nn.initializers.variance_scaling( + 1.0, 'fan_in', 'normal', out_axis=0) + +@struct.dataclass +class DiffusionConfig: + dtype: Any = jnp.float32 + model_dim: int = 64 + attn_cond_dim: int = 512 + cond_dim: Optional[int] = None #timestep/pooled text embedding channels. Defaults to model_dim*4 + resblocks_per_level: Union[int, Sequence[int]] = 2 + width_multipliers: Sequence[int] = (1, 2, 3, 4) + attn_resolutions: Sequence[int] = (32, 16, 8) + mha_head_dim: int = 64 + attn_heads: Union[int, Sequence[int]] = 2 + unified_qkv_norm: bool = False + deepfloyd_attnpooling: bool = False + resblock_activation: str = 'swish' + resblock_scale_skip: bool = False + output_ch: int = 3 + dropout_rate: float = 0.1 + + # modes are 'shuffle', 'conv' and 'resize'. + # (upsampling , downsampling) + # shuffle -> pixel shuffle / unshuffle + # conv -> resize + conv / strided conv + # resize -> interpolation / average pool + upsample_mode: str = 'shuffle' + downsample_mode: str = 'shuffle' + + # If True, block will use a 3x3 conv instead of 1x1 conv to correct + # channel dim mismatch in skip connection (if one exists) + spatial_skip: bool = False + + # 'shift_scale' or 'addition'. Strategy for incorporating the conditioning vector. + cond_strategy: str = 'shift_scale' + + # force groupnorm in fp32 + norm_32: bool = True + + # scale attention logits by \sqrt(head_dim) + scale_attn_logits: bool = True + float32_attention_logits: bool =False + text_conditionable: bool = True + null_text_emb_ct: int = 0 + +def single_or_idx(possibly_iter, idx): + if isinstance(possibly_iter, Sequence): + return possibly_iter[idx] + else: + return possibly_iter + +class ImagenUNet(nn.Module): + """ An Imagen diffusion U-net """ + config: DiffusionConfig + + @nn.compact + def __call__(self, images, time, + text_enc: OptionalArray=None, + text_lens: OptionalArray=None, + low_res_images: OptionalArray=None, + noise_aug_level: OptionalArray=None, + enable_dropout=True): + """ + Args: + images: samples to denoise. [b, h, w, c] + time: time conditioning. [b, 1] + text_enc: text embeddings (required for text_conditionable) [b, seq_len, embed] + text_lens: text sequence lengths in binary mask format [b, seq_len] + """ + from rosetta.projects.imagen import layers # to avoid circular import + cfg = self.config + activation = layers._convert_to_activation_function(cfg.resblock_activation) + deterministic=not enable_dropout + linear = functools.partial(DenseGeneral, + axis=-1, + use_bias=True, + dtype=cfg.dtype, + kernel_axes=('embed',)) + + spatial_conv = functools.partial(Conv, + kernel_size=(3,3), + strides=1, + padding='SAME', + dtype=cfg.dtype) + input_ch = images.shape[-1] + + # create time embedding + if cfg.cond_dim is None: + time_embed_dim = cfg.model_dim * 4 + else: + time_embed_dim = cfg.cond_dim + cond_embed = layers.get_timestep_embedding(time, cfg.model_dim, dtype=jnp.float32) + cond_embed = linear(features=time_embed_dim, name='time_dense_1')(cond_embed) + cond_embed = activation(cond_embed) + cond_embed = linear(features=time_embed_dim, name='time_dense_2')(cond_embed) + + if low_res_images is not None: + print('Low res images available. Running as a superresolution network') + if noise_aug_level is None: + jax.debug.print('noise_aug not given but it *really* should be') + noise_aug_level = jnp.ones_like(time) * (.25 * jnp.log(0.002)) # fallback EDM c_noise_fn on minimal noise + aug_embed = layers.get_timestep_embedding(noise_aug_level, cfg.model_dim, dtype=jnp.float32) + aug_embed = linear(features=time_embed_dim, name='aug_dense_1')(aug_embed) + aug_embed = activation(aug_embed) + aug_embed = linear(features=time_embed_dim, name='aug_dense_2')(aug_embed) + + cond_embed = cond_embed + aug_embed + + scaled_low_res = jax.image.resize(low_res_images, images.shape, "bicubic") + images = jnp.concatenate([images, scaled_low_res], axis=-1) + + # create attn pooled text embedding and project text to cond_dim + if cfg.text_conditionable: + assert text_lens is not None, "text_lens cannot be None. If you're trying to null condition, pass in a 0 mask" + assert text_enc is not None, "text_enc cannot be None. If you're trying to null condition, pass in 0s of appropriate shape. This network will add in the requisite null tokens" + + # setup null tokens + if cfg.null_text_emb_ct > 0: + null_text_mask = jnp.sum(text_lens, axis=1, keepdims=True) + + embedding_init = nn.initializers.variance_scaling(1.0, 'fan_in', 'normal', out_axis=0) + null_text_embedding = param_with_axes( + 'null_text_embeddings', + embedding_init, + (cfg.null_text_emb_ct, text_enc.shape[-1]), + jnp.float32, + axes=('vocab', 'embed')) + + # first null_text_emb_ct tokens to null_text_embedding + null_text_enc = jnp.zeros_like(text_enc[0:1]).at[:, :cfg.null_text_emb_ct].set(null_text_embedding) + null_text_lens = jnp.zeros_like(text_lens[0:1]).at[:, :cfg.null_text_emb_ct].set(1) + text_enc = jnp.where(jnp.expand_dims(null_text_mask, axis=-1) > 0, text_enc, null_text_enc) + text_lens = jnp.where(null_text_mask > 0, text_lens, null_text_lens) + + # attention pooling + if isinstance(cfg.attn_heads, Iterable): + num_heads = text_enc.shape[-1] // cfg.mha_head_dim + else: + num_heads = None + + if cfg.deepfloyd_attnpooling: + attn_pooled = LayerNorm(dtype=jnp.float32 if cfg.norm_32 else cfg.dtype, name='attn_pool_ln_0')(text_enc) + attn_pooled = layers.DeepFloydAttentionPoolingBlock(cfg=cfg, num_heads=num_heads)(attn_pooled, text_lens) + attn_pooled = linear(features=cfg.attn_cond_dim, name='attn_pool_dense_0')(attn_pooled) + attn_pooled = LayerNorm(dtype=jnp.float32 if cfg.norm_32 else cfg.dtype, name='attn_pool_ln_1')(attn_pooled) + else: + attn_pooled = layers.AttentionPoolingBlock(cfg=cfg, num_heads=num_heads)(text_enc, text_lens) + attn_pooled = LayerNorm(dtype=jnp.float32 if cfg.norm_32 else cfg.dtype, name='attn_pool_ln_0')(attn_pooled) + attn_pooled = linear(features=cfg.attn_cond_dim, name='attn_pool_dense_0')(attn_pooled) + attn_pooled = LayerNorm(dtype=jnp.float32 if cfg.norm_32 else cfg.dtype, name='attn_pool_ln_1')(attn_pooled) + attn_pooled = linear(features=time_embed_dim, name='attn_pool_dense_1')(attn_pooled) + attn_pooled = activation(attn_pooled) + attn_pooled = linear(features=time_embed_dim, name='attn_pool_dense_2')(attn_pooled) + + cond_embed = attn_pooled + cond_embed # Has dimension of time_embed_dim + + # text embedding projection to cond_dim + text_enc = linear(features=cfg.attn_cond_dim, name='text_enc_projection')(text_enc) + + # make image_channel -> model_dim convolution + x = spatial_conv(features=cfg.model_dim * cfg.width_multipliers[0], name='unet_in_conv')(images) + + # down branch resblocks + attn on designated resolutions + down_outputs = [x] + for level, width_mult in enumerate(cfg.width_multipliers): + level_channels = cfg.model_dim * width_mult + img_res = images.shape[1] // (2 ** level) + + for res_idx in range(single_or_idx(cfg.resblocks_per_level, level)): + x = layers.ResBlock(cfg=cfg, out_channels=level_channels, up_down_sample='none', + name='resblock_enc_{}_{}'.format(img_res, res_idx)) \ + (x, cond_embed, deterministic=deterministic) + print("Encoder ResBlock #", res_idx, " at resolution: ", img_res, " level: ", level, " width: ", level_channels) + + # attend if image is of designated resolution + if img_res in cfg.attn_resolutions: + attn_idx = level - (len(cfg.width_multipliers) - len(cfg.attn_resolutions)) + x = layers.ImgAttentionBlock(cfg=cfg, num_heads=single_or_idx(cfg.attn_heads, attn_idx), name='attnblock_enc_{}_{}'.format(img_res, res_idx)) \ + (x, text_enc, text_lens, deterministic=deterministic) + print("SelfAttentionBlock #", res_idx, " at resolution: ", img_res, " level: ", level, " width: ", level_channels) + + down_outputs.append(x) + + # if not on the last level, downsample + if level != len(cfg.width_multipliers) - 1: + x = layers.ResBlock(cfg=cfg, out_channels=level_channels, up_down_sample='down', + name='resblock_enc_downsampling_{}_{}'.format(img_res, res_idx)) \ + (x, cond_embed, deterministic=deterministic) + print("Downsampling ResBlock at resolution (to half): ", img_res, " level: ", level, " width: ", level_channels) + down_outputs.append(x) + + # middle layers + mid_channels = cfg.model_dim * cfg.width_multipliers[-1] + x = layers.ResBlock(cfg=cfg, out_channels=mid_channels, up_down_sample='none', + name='resblock_mid_1')(x, cond_embed, deterministic=deterministic) + x = layers.ImgAttentionBlock(cfg=cfg, num_heads=single_or_idx(cfg.attn_heads, -1),name='attnblock_mid_1')(x, text_enc, text_lens, deterministic=deterministic) + x = layers.ResBlock(cfg=cfg, out_channels=mid_channels, up_down_sample='none', + name='resblock_mid_2')(x, cond_embed, deterministic=deterministic) + + print('Encoder Skip Shapes: ',list(map(lambda x : x.shape, down_outputs))) + # up branch resblocks + attn on designated resolutions + skip connections + for level, width_mult in list(enumerate(cfg.width_multipliers))[::-1]: + level_channels = cfg.model_dim * width_mult + img_res = images.shape[1] // (2 ** level) + + for res_idx in range(single_or_idx(cfg.resblocks_per_level, level) + 1): + u_skip = down_outputs.pop() + print("Decoder ResBlock #", res_idx, " at resolution: ", img_res, " level: ", level, " width: ", level_channels, " skip shape: ", u_skip.shape) + x = jnp.concatenate([x, u_skip], axis=-1) + x = layers.ResBlock(cfg=cfg, out_channels=level_channels, up_down_sample='none', + name='resblock_dec_{}_{}'.format(img_res, res_idx)) \ + (x, cond_embed, deterministic=deterministic) + + # attend if image is of designated resolution + if img_res in cfg.attn_resolutions: + print("SelfAttentionBlock #", res_idx, " at resolution: ", img_res, " level: ", level, " width: ", level_channels) + attn_idx = level - (len(cfg.width_multipliers) - len(cfg.attn_resolutions)) + x = layers.ImgAttentionBlock(cfg=cfg, num_heads=single_or_idx(cfg.attn_heads, attn_idx), name='attnblock_dec_{}_{}'.format(img_res, res_idx))\ + (x, text_enc, text_lens, deterministic=deterministic) + + # upsample if on last resblock and not the highest level + if res_idx == single_or_idx(cfg.resblocks_per_level, level) and level != 0: + print("Upsamling ResBlock at resolution (to double): ", img_res, " level: ", level, " width: ", level_channels, " no skip") + x = layers.ResBlock(cfg=cfg, out_channels=level_channels, up_down_sample='up', + name='resblock_dec_upsampling_{}_{}'.format(img_res, res_idx)) \ + (x, cond_embed, deterministic=deterministic) + + # out convolution model_dim -> image_channels + x = layers.FP32Wrap(GroupNorm(num_groups=32, name='unet_out_gn'))(x) + x = activation(x) + # x = spatial_conv(features=cfg.output_ch, name='unet_out_conv', kernel_init=nn.initializers.zeros, no_embed_axis=True)(x) + x = spatial_conv(features=cfg.output_ch, name='unet_out_conv', kernel_init=nn.initializers.zeros)(x) + return x \ No newline at end of file diff --git a/rosetta/rosetta/projects/imagen/network_sr.py b/rosetta/rosetta/projects/imagen/network_sr.py new file mode 100644 index 000000000..003992f01 --- /dev/null +++ b/rosetta/rosetta/projects/imagen/network_sr.py @@ -0,0 +1,251 @@ +# Copyright (c) 2022-2023 NVIDIA Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Super resolution Diffusion Model Backbones""" + +from typing import Any, Sequence, Union, Iterable, Optional, Mapping +import functools + +from flax import linen as nn +from flax.linen import partitioning as nn_partitioning +from flax import struct +import jax.numpy as jnp +from t5x.contrib.gpu.t5.layers import MultiHeadDotProductAttention, make_attention_mask +from flax.linen import DenseGeneral, Conv, LayerNorm +import jax +from einops import rearrange + +param_with_axes = nn_partitioning.param_with_axes +OptionalArray = Optional[jnp.ndarray] +Array = jnp.ndarray + +default_embed_init = nn.initializers.variance_scaling( + 1.0, 'fan_in', 'normal', out_axis=0) + +def single_or_idx(possibly_iter, idx): + if isinstance(possibly_iter, Iterable): + return possibly_iter[idx] + else: + return possibly_iter + +@struct.dataclass +class ImagenEfficientUNetConfig: + dtype: Any = jnp.float32 + model_dim: int = 64 + cond_dim: int = 256 #timestep/pooled text embedding channels + attn_cond_dim: int = 256 + resblocks_per_level: Union[int, Iterable[int]] = (2, 4, 8, 8) + width_multipliers: Iterable[int] = (1, 2, 3, 4) + attn_resolutions_divs: Mapping[int, str] = None #{8: 'fused'} #use attn at resolutions of input / {elems of list} + mha_head_dim: int = 64 + attn_heads: Union[int, Sequence[int]] = 8 + resblock_activation: str = 'swish' + resblock_zero_out: bool = True + resblock_scale_skip: bool = True # enables scaling residuals by 1/\sqrt{2} + dropout_rate: float = 0.1 + + # 'shift_scale' or 'addition'. Strategy for incorporating the conditioning vector. + cond_strategy: str = 'shift_scale' + + # force groupnorm in fp32 + norm_32: bool = True + + # scale attention logits by \sqrt(head_dim) + scale_attn_logits: bool = True + float32_attention_logits: bool =False + text_conditionable: bool = True + null_text_emb_ct: int = 0 + +class ImagenEfficientUNet(nn.Module): + """ An Imagen diffusion U-net """ + config: ImagenEfficientUNetConfig + + @nn.compact + def __call__(self, images, time, + text_enc: OptionalArray=None, + text_lens: OptionalArray=None, + low_res_images: OptionalArray=None, + noise_aug_level: OptionalArray=None, + enable_dropout=True): + """ + Args: + images: samples to denoise. [b, h, w, c] + time: time conditioning. [b, 1] + text_enc: text embeddings (required for text_conditionable) [b, seq_len, embed] + text_lens: text sequence lengths in binary mask format [b, seq_len] + """ + from rosetta.projects.imagen import layers_sr # to avoid circular import + from rosetta.projects.imagen import layers # to avoid circular import + cfg = self.config + activation = layers_sr._convert_to_activation_function(cfg.resblock_activation) + deterministic=not enable_dropout + linear = functools.partial(DenseGeneral, + axis=-1, + use_bias=True, + dtype=cfg.dtype, + kernel_axes=('embed',)) + + spatial_conv = functools.partial(Conv, + kernel_size=(3,3), + strides=(1,1), + padding='SAME', + dtype=cfg.dtype) + input_ch = images.shape[-1] + + # create time embedding + time_embed_dim = cfg.cond_dim + cond_embed = layers.get_timestep_embedding(time, cfg.model_dim, dtype=jnp.float32) + cond_embed = linear(features=time_embed_dim, name='time_dense_1')(cond_embed) + cond_embed = activation(cond_embed) + cond_embed = linear(features=time_embed_dim, name='time_dense_2')(cond_embed) + + if low_res_images is not None: + print('Low res images available. Running as a superresolution network') + if noise_aug_level is None: + print('noise_aug not given but it *really* should be') + noise_aug_level = jnp.ones_like(time) * (.25 * jnp.log(0.002)) # fallback EDM c_noise_fn on minimal noise + aug_embed = layers.get_timestep_embedding(noise_aug_level, cfg.model_dim, dtype=jnp.float32) + aug_embed = linear(features=time_embed_dim, name='aug_dense_1')(aug_embed) + aug_embed = activation(aug_embed) + aug_embed = linear(features=time_embed_dim, name='aug_dense_2')(aug_embed) + + cond_embed = cond_embed + aug_embed + + scaled_low_res = jax.image.resize(low_res_images, images.shape, "bicubic") + images = jnp.concatenate([images, scaled_low_res], axis=-1) + + # create attn pooled text embedding and project text to cond_dim + if cfg.text_conditionable: + assert text_lens is not None, "text_lens cannot be None. If you're trying to null condition, pass in a 0 mask" + assert text_enc is not None, "text_enc cannot be None. If you're trying to null condition, pass in 0s of appropriate shape. This network will add in the requisite null tokens" + + # setup null tokens + if cfg.null_text_emb_ct > 0: + null_text_mask = jnp.sum(text_lens, axis=1, keepdims=True) + + embedding_init = nn.initializers.variance_scaling(1.0, 'fan_in', 'normal', out_axis=0) + null_text_embedding = param_with_axes( + 'null_text_embeddings', + embedding_init, + (cfg.null_text_emb_ct, text_enc.shape[-1]), + jnp.float32, + axes=('vocab', 'embed')) + + # first null_text_emb_ct tokens to null_text_embedding + null_text_enc = jnp.zeros_like(text_enc[0:1]).at[:, :cfg.null_text_emb_ct].set(null_text_embedding) + null_text_lens = jnp.zeros_like(text_lens[0:1]).at[:, :cfg.null_text_emb_ct].set(1) + text_enc = jnp.where(jnp.expand_dims(null_text_mask, axis=-1) > 0, text_enc, null_text_enc) + text_lens = jnp.where(null_text_mask > 0, text_lens, null_text_lens) + + # attention pooling + attn_pooled = layers.AttentionPoolingBlock(cfg=cfg)(text_enc, text_lens) + attn_pooled = LayerNorm(dtype=jnp.float32 if cfg.norm_32 else cfg.dtype, name='attn_pool_ln_0')(attn_pooled) + attn_pooled = linear(features=cfg.cond_dim, name='attn_pool_dense_0')(attn_pooled) + attn_pooled = LayerNorm(dtype=jnp.float32 if cfg.norm_32 else cfg.dtype, name='attn_pool_ln_1')(attn_pooled) + attn_pooled = linear(features=time_embed_dim, name='attn_pool_dense_1')(attn_pooled) + attn_pooled = activation(attn_pooled) + attn_pooled = linear(features=time_embed_dim, name='attn_pool_dense_2')(attn_pooled) + + cond_embed = attn_pooled + cond_embed # Has dimension of time_embed_dim + + # text embedding projection to cond_dim + text_enc = linear(features=cfg.attn_cond_dim, name='text_enc_projection')(text_enc) + + # make image_channel -> model_dim convolution (and/or CrossEmbedLayer inception style?) + x = spatial_conv(features=cfg.model_dim * cfg.width_multipliers[0], name='unet_in_conv')(images) + + # down branch resblocks + attn on designated resolutions + down_outputs = [] + use_attn, attn_type = None, None + + common_block_kwargs = { + 'dtype':cfg.dtype, + 'norm_32':cfg.norm_32, + 'activation':cfg.resblock_activation, + 'cond_strategy':cfg.cond_strategy, + 'dropout_rate':cfg.dropout_rate, + 'attn_heads':cfg.attn_heads, + 'mha_head_dim':cfg.mha_head_dim, + 'zero_out':cfg.resblock_zero_out, + 'scale_skip':cfg.resblock_scale_skip, + } + + resolution_div = 1 + for level, width_mult in enumerate(cfg.width_multipliers[:-1]): + level_channels = cfg.model_dim * width_mult + + use_attn = (2 ** level) in cfg.attn_resolutions_divs.keys() + attn_type = cfg.attn_resolutions_divs[2 ** level] if use_attn else None + x = layers_sr.EfficientBlock(out_channels=level_channels, + use_attn=use_attn, + attn_type=attn_type, + up_down='down', + num_resblocks=single_or_idx(cfg.resblocks_per_level, level), + strides=(2,2), + name=f'DBlock_l{level}', + **common_block_kwargs) \ + (x, cond_embed, text_enc, text_lens, deterministic=deterministic) + print("Encoder DBlock #", level, " out resolution: ", x.shape[1:3], " level: ", level, " width: ", level_channels) + down_outputs.append(x) + resolution_div = 2 ** level + + # middle layers + use_attn = (2 * resolution_div) in cfg.attn_resolutions_divs.keys() + attn_type = cfg.attn_resolutions_divs[2 ** (level + 1)] if use_attn else None + mid_channels = cfg.model_dim * cfg.width_multipliers[-1] + x = layers_sr.EfficientBlock(out_channels=mid_channels, + use_attn=use_attn, + attn_type=attn_type, + up_down='down', + num_resblocks=single_or_idx(cfg.resblocks_per_level, -1), + strides=(1,1), + name='dblock_mid', + **common_block_kwargs) \ + (x, cond_embed, text_enc, text_lens, deterministic=deterministic) + + x = layers_sr.EfficientBlock(out_channels=mid_channels, + use_attn=use_attn, + attn_type=attn_type, + up_down='up', + num_resblocks=single_or_idx(cfg.resblocks_per_level, -1), + strides=(1,1), + name='ublock_mid', + **common_block_kwargs) \ + (x, cond_embed, text_enc, text_lens, deterministic=deterministic) + + print('Encoder Skip Shapes: ',list(map(lambda x : x.shape, down_outputs))) + # up branch resblocks + attn on designated resolutions + skip connections + for level, width_mult in list(enumerate(cfg.width_multipliers[:-1]))[::-1]: + level_channels = cfg.model_dim * width_mult + + # for res_idx in range(cfg.resblocks_per_level + 1): + u_skip = down_outputs.pop() + x = jnp.concatenate([x, u_skip], axis=-1) + use_attn = (2 ** level) in cfg.attn_resolutions_divs.keys() + attn_type = cfg.attn_resolutions_divs[2 ** level] if use_attn else None + x = layers_sr.EfficientBlock(out_channels=level_channels, + use_attn=use_attn, + attn_type=attn_type, + up_down='up', + num_resblocks=single_or_idx(cfg.resblocks_per_level, level), + strides=(2,2), + name=f'UBlock_l{level}', + **common_block_kwargs) \ + (x, cond_embed, text_enc, text_lens, deterministic=deterministic) + print("Decoder UBlock #", level, " out resolution: ", x.shape[1:3], " level: ", level, " width: ", level_channels, " skip shape: ", u_skip.shape) + + # out convolution model_dim -> image_channels + x = spatial_conv(features=input_ch, name='unet_out_conv', kernel_init=nn.initializers.zeros)(x) + return x + diff --git a/rosetta/rosetta/projects/imagen/scripts/example_slurm_inf_train.sub b/rosetta/rosetta/projects/imagen/scripts/example_slurm_inf_train.sub new file mode 100755 index 000000000..6da0ccde1 --- /dev/null +++ b/rosetta/rosetta/projects/imagen/scripts/example_slurm_inf_train.sub @@ -0,0 +1,107 @@ +#!/bin/bash +#SBATCH -A +#SBATCH -p +#SBATCH -N 1 # number of nodes +#SBATCH -t 04:00:00 # wall time (8 for backfill, 4 for Luna) +#SBATCH -J # job name (<< CHANGE ! >>) +#SBATCH --exclusive # exclusive node access +#SBATCH --mem=0 # all mem avail +#SBATCH --mail-type=FAIL # only send email on failure +#SBATCH --overcommit # Needed for pytorch +#SBATCH --dependency=singleton +set -x + +# File system and volume glue code +#------------------------------------------------------------------------------- +# << CHANGE ! >> +SLURM_ACCOUNT= +USERID= + +# << CHANGE ! >> +CONTAINER= + +# << CHANGE ! >> +BASE_ROSETTA_DIR="/jax-toolbox-mirror/rosetta/" # path to your clone of the repo +BASE_DATA_DIR="/datasets/" +BASE_WORKSPACE_DIR="${BASE_ROSETTA_DIR}/workspace" # path to where outputs will be dumped +BASE_HOSTNAME_COMM="${BASE_WORKSPACE_DIR}/outputs/multinode/communicators/${SLURM_JOB_ID}-inf-server-comms/" + +# Default env variables for paths required by t5x training scripts +DATA_DIR=/mnt/datasets/ +ROSETTA_DIR=/opt/rosetta/ +WORKSPACE_DIR=/opt/rosetta/workspace +HOSTNAMES_DIR=/inference_srv/ +HOSTNAMES_FILE=${HOSTNAMES_DIR}/hostnames.txt + +# Add the T5x/JAX specific mounts +MOUNTS="--container-mounts=$BASE_ROSETTA_DIR:$ROSETTA_DIR,$BASE_DATA_DIR:$DATA_DIR,$BASE_WORKSPACE_DIR:$WORKSPACE_DIR,$BASE_HOSTNAME_COMM:$HOSTNAMES_DIR" + +# Add T5x/JAX specific exports +EXPORTS="--export=ALL,DATA_DIR=${DATA_DIR},ROSETTA_DIR=${ROSETTA_DIR},WORKSPACE_DIR=${WORKSPACE_DIR}" +#------------------------------------------------------------------------------- + +# Command line arguments needed by the underlying scripts +DATASET=$1 +T5_SIZE=$2 # base +PREC="$3" # bfloat16, float32 +GPUS_PER_NODE=$4 # usually 8 +BSIZE_PER_GPU=$5 # local batch size/gpu +MODEL_DIR_LOCAL=$6 # directory to save checkpoints and config dump to +INF_SERV_CT=$7 # number of inference server processes +INF_SIZE=${8:-"xxl"} # t5 model size of inference server +NUM_MICROBATCHES=${9} # number of gradient accumulation steps +MP=${10} # tensor parallel count + +NUM_GPUS=$(( GPUS_PER_NODE * SLURM_JOB_NUM_NODES )) + +# remove hostnames file if there are no inference servers +if [ -z "${INF_SERV_CT}" ] || [ "${INF_SERV_CT}" -eq 0 ]; then + HOSTNAMES_FILE=None +fi + + +# << CHANGE ! >> +# You can add binding to the command below with the following line (after nvidia-smi). Remove the '&&' on the next bash line. +# && bash <>/bind.sh --cpu=exclusive --ib=single -- \ +read -r -d '' train_cmd < ${LOG_DIR}/${T5_SIZE}_${DATASET}_gpu_${TRAIN_GPUS}_${PREC}_gbs_${BSIZE}-${i}.log & +done diff --git a/rosetta/rosetta/projects/imagen/scripts/singlenode_mp_train_singlegpu.sh b/rosetta/rosetta/projects/imagen/scripts/singlenode_mp_train_singlegpu.sh new file mode 100755 index 000000000..264296fb9 --- /dev/null +++ b/rosetta/rosetta/projects/imagen/scripts/singlenode_mp_train_singlegpu.sh @@ -0,0 +1,60 @@ +#! /bin/bash +# A script for single-node pile pretraining to be used with specialize.py + +# Copyright (c) 2022-2023 NVIDIA Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +set -x + +# Arguments +DATASET=$1 +MODEL_TYPE=$2 # Model size (small, base, large) +PREC="$3" # Precision (float32, float16, bfloat16) +NUM_GPUS=$4 # Number of GPUs in node (1, 2, 4, 8) +BSIZE_PER_GPU=$5 # Batch size per GPU (varies with model size) +LOG_DIR=$6 # Output log directory +MODEL_DIR_LOCAL=${7:-"runs/model_dir"} +MODEL_DIR=${PWD}/${MODEL_DIR_LOCAL} +INF_SERV_CT=${8:-0} +NUM_MICROBATCHES=${9:-0} +HOSTNAMES_FILE=${10:-""} + +STAT_PERIOD=100 + +echo $MODEL_DIR + +TRAIN_GPUS=$((NUM_GPUS - INF_SERV_CT)) +echo "Please make sure ${NUM_GPUS} is the number of visible CUDA devices you have" + +# Setting XLA flags +export XLA_FLAGS='--xla_gpu_simplify_all_fp_conversions --xla_gpu_all_reduce_combine_threshold_bytes=136314880' + +# Global batch size +BSIZE=$(( TRAIN_GPUS * BSIZE_PER_GPU )) +INFER_SAMPLES=${INFER_SAMPLES:=$BSIZE} + + +CUDA_VISIBLE_DEVICES=${PROC_ID} \ +python3 -u /opt/t5x/t5x/train.py \ + --gin_file="/opt/rosetta/rosetta/projects/imagen/configs/${MODEL_TYPE}_${DATASET}.gin" \ + --gin.MODEL_DIR=\"${MODEL_DIR}\" \ + --gin.DTYPE=\"${PREC}\" \ + --gin.BATCH_SIZE=${BSIZE} \ + --gin.train.stats_period=${STAT_PERIOD} \ + --gin.trainer.Trainer.num_microbatches=${NUM_MICROBATCHES} \ + --gin.HOSTNAMES_FILE=\"${HOSTNAMES_FILE}\" \ + --gin.INFER_SAMPLES=${INFER_SAMPLES} \ + --multiprocess_gpu \ + --coordinator_address=127.0.0.1:12345 \ + --process_count=$TRAIN_GPUS \ + --process_index=${PROC_ID} &> ${LOG_DIR}/${MODEL_TYPE}_${DATASET}_gpu_${TRAIN_GPUS}_${PREC}_gbs_${BSIZE}-${PROC_ID}.log & diff --git a/rosetta/rosetta/projects/imagen/scripts/specialized_run.py b/rosetta/rosetta/projects/imagen/scripts/specialized_run.py new file mode 100755 index 000000000..4ce97a56d --- /dev/null +++ b/rosetta/rosetta/projects/imagen/scripts/specialized_run.py @@ -0,0 +1,80 @@ +# Copyright (c) 2022-2023 NVIDIA Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import argparse +import subprocess +import os +import fcntl +import time + +parser = argparse.ArgumentParser(description='Run either training or inference server based on process ID') + +parser.add_argument('--proc_id', type=int, required=False, default=-1) +parser.add_argument('--proc_total_ct', type=int, required=True) +parser.add_argument('--inf_server_ct', type=int, required=True) +parser.add_argument('--gpus_per_node', type=int, default=-1, required=False) #Needed for multinode +parser.add_argument('--gpu_collection_size', type=int, default=1, required=False) + +parser.add_argument('--train_run_command', type=str, required=True) +parser.add_argument('--inf_server_run_command', type=str, required=True) +parser.add_argument('--hostnames_file', type=str, required=True) + +parser.add_argument('--inf_log_file', type=str, required=False) + +args = parser.parse_args() + +train_servers = args.proc_total_ct - args.inf_server_ct + +PROCESS_ID = args.proc_id if args.proc_id >= 0 else None + +if PROCESS_ID is None and os.getenv('SLURM_PROCID') is not None: + PROCESS_ID = int(os.getenv('SLURM_PROCID')) + if PROCESS_ID is None: + raise ValueError("Failed to get process ID when specializing") + +gpus_in_device = args.gpus_per_node if args.gpus_per_node > 0 else args.proc_total_ct +local_id = PROCESS_ID % gpus_in_device +# one inference server per node case +if PROCESS_ID == train_servers or (PROCESS_ID >= train_servers and local_id == 0): + inf_id = PROCESS_ID - train_servers + hostname = subprocess.check_output(["hostname", "-I"]) + hostname = hostname.split()[0].decode("utf-8") + port = 2345 + inf_id + hostname = hostname + ':' + str(port) + '\n' + + devices = [str(i) for i in range(local_id, gpus_in_device)] + device_str = ','.join(devices) + + with open(args.hostnames_file, 'a') as hf: + # Should be under buffer size on hostname, preventing + # writing race conditions, but will lock to be safe. + fcntl.flock(hf, fcntl.LOCK_EX) + hf.write(hostname) + fcntl.flock(hf, fcntl.LOCK_UN) + + inf_command_withargs = args.inf_server_run_command + f' --port={port} --devices={device_str} --total_device_first_idx={inf_id}' + # if args.inf_log_file is not None: + # inf_command_withport += f' &> {args.inf_log_file}' + + print("Inference Command: " + inf_command_withargs) + if args.inf_log_file is not None: + with open(args.inf_log_file, 'w') as f: + subprocess.call(inf_command_withargs, stdout=f, stderr=f, shell=True) + else: + os.system(inf_command_withargs) + + +# train server case +elif PROCESS_ID < train_servers: + time.sleep(10) + os.system(f'PROC_ID={PROCESS_ID} ' + args.train_run_command) diff --git a/rosetta/rosetta/projects/inference_serving/configs/t5_large_server.yml b/rosetta/rosetta/projects/inference_serving/configs/t5_large_server.yml new file mode 100644 index 000000000..ac19c6b56 --- /dev/null +++ b/rosetta/rosetta/projects/inference_serving/configs/t5_large_server.yml @@ -0,0 +1,38 @@ +--- +models: + t5_large: + # Model config + max_bs: + a100_80g: 4096 + a6000: 2048 + gv100_32g: 1024 + default: null + find_max_bs: + cache_dir: "/opt/rosetta/server_bs_cache.json" #null to disable reading/writing batch size information to cache + + # Command to start a server per gpu group + run_command: "/opt/rosetta/rosetta/projects/inference_serving/t5/embed_t5x.sh large" + gpus_per_process: 1 + + # PyTriton config + inputs: + - sequence: + dtype: "bytes_" + shape: !!python/tuple [-1] + outputs: + - encodings_padded: + dtype: "float16" + shape: !!python/tuple [-1] + - encodings_seqlens: + dtype: 'int32' + shape: !!python/tuple [-1] + + # "static" or "dymanic" + batching: + "dynamic" + + # specify fraction or absolute count of allocated GPUs to commit here. + resources: + fraction: 1.0 + count: null +... diff --git a/rosetta/rosetta/projects/inference_serving/configs/t5_xxl_server.yml b/rosetta/rosetta/projects/inference_serving/configs/t5_xxl_server.yml new file mode 100644 index 000000000..e12a67e8f --- /dev/null +++ b/rosetta/rosetta/projects/inference_serving/configs/t5_xxl_server.yml @@ -0,0 +1,39 @@ +--- +models: + t5_xxl: + # Model config + max_bs: + a100_80g: 1024 + a100_40g: 512 + a6000: 512 + gv100_32g: 272 + default: null + find_max_bs: + cache_dir: "/opt/rosetta/server_bs_cache.json" #null to disable reading/writing batch size information to cache + + # Command to start a server per gpu group + run_command: "/opt/rosetta/rosetta/projects/inference_serving/t5/embed_t5x.sh xxl" + gpus_per_process: 1 + + # PyTriton config + inputs: + - sequence: + dtype: "bytes_" + shape: !!python/tuple [-1] + outputs: + - encodings_padded: + dtype: 'float16' + shape: !!python/tuple [-1] + - encodings_seqlens: + dtype: 'int32' + shape: !!python/tuple [-1] + + # "static" or "dymanic" + batching: + "dynamic" + + # specify fraction or absolute count of allocated GPUs to commit here. + resources: + fraction: 1.0 + count: null +... diff --git a/rosetta/rosetta/projects/inference_serving/server.py b/rosetta/rosetta/projects/inference_serving/server.py new file mode 100644 index 000000000..a94b58c6a --- /dev/null +++ b/rosetta/rosetta/projects/inference_serving/server.py @@ -0,0 +1,330 @@ +# Copyright (c) 2022-2023 NVIDIA Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import numpy as np +import logging +import time +import os +import signal +from typing import List, Dict +import pickle as pkl +import copy +import argparse +import functools +import yaml +from yaml import Loader + +import uuid +import zmq +import subprocess + +from pytriton.model_config import ModelConfig, Tensor, DynamicBatcher +from pytriton.triton import Triton, TritonConfig +from pytriton.decorators import batch + +from rosetta.projects.inference_serving.server_utils import pow2list, triton_textencode +from rosetta.projects.inference_serving.shared_numpy import SharedNPDict + +# List of strings of comma-separated device indexes. i.e. ['0', '1', '2'] or ['0,1', '2,3'] +# Each list element contains the CUDA_VISIBLE_DEVICES visible to an inference process +ModelDevicesType = List[str] + +# ZMQ-based infer function. Sends input over socket and reads output return +def infer_fn(socket, **inputs: np.ndarray): + # start_time = time.time() + # out = [np.array([pkl.dumps(np.zeros((4096, 128), dtype=np.float32))] * list(inputs.values())[0].shape[0])] + # out = [np.zeros((list(inputs.values())[0].shape[0], 4096*128), dtype=np.float32)] + # logging.warning(f'time to create out {time.time() - start_time}') + # return out + for k, v in inputs.items(): + #logging.warning(f'inferring on type {v.dtype}') + inputs[k] = np.array([pkl.dumps(v)]) + start_time = time.time() + shared_inputs = SharedNPDict(dict_to_share=inputs) + socket.send_pyobj(shared_inputs.get_metas()) + out = socket.recv_pyobj() + shared_inputs.close_and_unlink() + # logging.warning(f'[triton] time to backend {time.time() - start_time}') + # out_time = time.strftime('%X %x %Z') + # logging.warning(f'outtime {out_time}') + if isinstance(out, str): + return out + out = SharedNPDict(metadata=out).localize(close_shared=True, unlink_shared=True)#['out'] + #logging.warning(out) + # return [out['padded_outs'], out['seqlens']] + return out + +def get_infer_fns(device_struct: ModelDevicesType, child_command:str): + sockets = [] + infer_fns = [] + for dl in device_struct: + ctx = zmq.Context(io_threads=1) + socket_addr = "ipc:///tmp/pytriton_multi-" + str(uuid.uuid4()) + + socket = ctx.socket(zmq.REQ) + socket.bind(socket_addr) + + sockets.append(socket) + + subprocess.Popen(f'SOCKET_ADDRESS={socket_addr} CUDA_VISIBLE_DEVICES={dl} {child_command} &', shell=True) + + for sock in sockets: + infer_fns.append(batch(functools.partial(infer_fn, socket=sock))) + logging.info(f'Built infer_fn for {sock}') + + return infer_fns + +def find_model_max_bs(devices: str, server_config:dict, model_name:str): + logging.basicConfig(level=logging.INFO) + logging.info(f"Finding the maximum batch size for model: {model_name}") + + + ctx = zmq.Context(io_threads=1) + socket_addr = "ipc:///tmp/pytriton_MAX_BS_multi-" + str(uuid.uuid4()) + socket = ctx.socket(zmq.REQ) + socket.bind(socket_addr) + + command = server_config['models'][model_name]['run_command'] + proc = subprocess.Popen(f'SOCKET_ADDRESS={socket_addr} CUDA_VISIBLE_DEVICES={devices} {command} &', shell=True, preexec_fn=os.setsid) + time.sleep(5) #setup time + + lower = 1 + test = 256 + upper_fail = None + + while upper_fail is None or ((float(upper_fail) / lower >= 1.125) and upper_fail - lower > 1): + logging.info(f'Trying bs {test}') + socket.send_pyobj({'singleton': test}) + out = socket.recv_pyobj() + + if isinstance(out, str): + # logging.warning(f'bs: {test} failed with error: {out}') + if test == 1: + logging.info('bs of 1 has failed. Exiting') + exit() + else: + upper_fail = test + test = (lower + upper_fail) // 2 + else: + logging.info(f'bs: {test} succeeded') + if upper_fail is None: + lower = test + test *= 2 + else: + lower = test + test = (lower + upper_fail) // 2 + + logging.info(f'New lower: {lower}, test: {test}, upper: {upper_fail}') + + socket.close() + os.killpg(os.getpgid(proc.pid), signal.SIGKILL) + return lower + +def get_batchsize(device_struct:ModelDevicesType, gpu_name:str, server_config:dict, model_name:str): + devices = device_struct[0] + if gpu_name is None: + logging.info("No gpu_name given. Finding max_bs") + return find_model_max_bs(devices, server_config, model_name) + + for config_gpu, max_bs in server_config['models'][model_name]['max_bs'].items(): + if gpu_name in config_gpu: + logging.info(f"Matched gpu name: {gpu_name} to configuration {config_gpu} with max_bs {max_bs}") + if max_bs is None: + logging.info("Since found max_bs is None, finding max_bs") + max_bs = find_model_max_bs(devices, server_config, model_name) + return max_bs + + logging.info(f'GPU name {gpu_name} not found in config. Finding max_bs') + return find_model_max_bs(devices, server_config, model_name) + +def config_to_tensor(triton_in_out_config): + out = [] + for inp in triton_in_out_config: + first_key = list(inp.keys())[0] + out += [ + Tensor(name=first_key, + dtype=np.dtype(inp[first_key]['dtype']), + shape=inp[first_key]['shape']) + ] + return out + +def triton_run(port:int, device_structs:Dict[str, ModelDevicesType], gpu_name:str, server_config:dict): + logging.warning(f'port {port}, devices {device_structs}') + triton_config = TritonConfig(http_port=port, grpc_port=port+1000, metrics_port=port+2000, log_verbose=0) + with Triton(config=triton_config) as triton: + for model_name in server_config['models'].keys(): + model_cfg = server_config['models'][model_name] + logging.warning(f'Setting up model {model_name} with configuration: {model_cfg}') + batch_size = get_batchsize(device_structs[model_name], gpu_name, server_config, model_name) + logging.warning(f'Using batch size {batch_size}') + + infer_fns = get_infer_fns(device_structs[model_name], model_cfg['run_command']) + + dyn_batch = DynamicBatcher( + max_queue_delay_microseconds = 100000, + preferred_batch_size = pow2list(batch_size) + ) + + triton.bind( + model_name=model_name, + infer_func=infer_fns, + inputs=config_to_tensor(model_cfg['inputs']), + outputs=config_to_tensor(model_cfg['outputs']), + config=ModelConfig(max_batch_size=batch_size, batching=True, batcher=dyn_batch), + ) + triton.serve() + +def build_visible_device_structs(devices_available, + total_devices, + total_device_first_idx, + server_config) -> Dict[str, ModelDevicesType]: + def single_model_devices(model_name, devices_available): + device_list = [] + devices_per_process = server_config['models'][model_name]['gpus_per_process'] + + # Number of devices must be divisible by the number of devices per process + assert len(devices_available) % devices_per_process == 0 + + for proc_id in range(len(devices_available) // devices_per_process): + device_list.append(','.join(devices_available[proc_id * devices_per_process: (proc_id + 1) * devices_per_process])) + + return device_list + + if len(server_config['models'].keys()) > 1: + assert total_devices is not None, "total_devices must be given if using more than one model" + assert total_device_first_idx is not None, "total_device_first_idx must be given if using more than one model" + + # Figuring out gpus per model + device_ctr = 0 + device_counts: dict[str, int] = {} + gpus_per_model_proc = {} + for model_name in server_config['models'].keys(): + model_cfg_resources = server_config['models'][model_name]['resources'] + gpus_per_model_proc[model_name] = server_config['models'][model_name]['gpus_per_process'] + if model_cfg_resources['fraction'] is not None: + count = round(total_devices * model_cfg_resources['fraction']) + elif model_cfg_resources['count'] is not None: + count = model_cfg_resources['count'] + else: + assert False, f'No resources specified for {model_name}' + + count = count // gpus_per_model_proc[model_name] * gpus_per_model_proc[model_name] + device_ctr += count + device_counts[model_name] = count + + model_names = list(device_counts.keys()) + if device_ctr > total_devices: + logging.info(f"Current resource specification is using too many devices! ({device_counts}; \ + Available: {total_devices} This can be due to rounding errors with fractional resources \ + or too many devices specified under 'count'. Reducing devices until program can run") + + idx = 0 + since_last_update = 0 + while device_counts > total_devices: + model_name = model_names[idx] + if device_counts[model_name] > gpus_per_model_proc[model_name]: + device_counts[model_name] -= gpus_per_model_proc[model_name] + device_ctr -= gpus_per_model_proc[model_name] + since_last_update = 0 + else: + since_last_update += 1 + idx += 1 + idx %= len(model_names) + if since_last_update > len(model_names): + assert False, "There are not enough devices to run 1 process of each model" + + if device_ctr < total_devices: + logging.info(f'Warning, {total_devices - device_ctr} devices idle') + + logging.info(f'Device arrangement: {device_counts}') + + before_this_host = total_device_first_idx + while before_this_host > 0: + for model in model_names: + if device_counts[model] > 0: + device_counts[model] -= gpus_per_model_proc[model] + before_this_host -= gpus_per_model_proc[model] + break + + logging.warning(f'gpus_per_process {gpus_per_model_proc}, device_counts {device_counts}, devices_available {devices_available}') + devices_per_model: Dict[str, List[int]] = {} + remaining_devices = copy.deepcopy(devices_available) + while len(remaining_devices) > 0: + for model in model_names: + logging.warning(f'remaining_devices {remaining_devices}, gpus_per_model_proc {gpus_per_model_proc}') + if device_counts[model] > 0: + assert gpus_per_model_proc[model] <= len(remaining_devices), "Cannot evenly fit model onto this host" + if model not in devices_per_model.keys(): + devices_per_model[model] = [] + devices_per_model[model] += remaining_devices[:gpus_per_model_proc[model]] + remaining_devices = remaining_devices[gpus_per_model_proc[model]:] + break + + # final construction + visible_device_structs = {} + for model, devices in devices_per_model.items(): + visible_device_structs[model] = single_model_devices(model, devices) + + return visible_device_structs + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description='PyTriton Inference server with many GPUs communicating over zmq') + parser.add_argument( + '--port', + type=int, + default=1234, + help="port for server") + parser.add_argument( + '--devices', + type=str, + required=True, + help="Comma-separated list of GPU indexes available") + parser.add_argument( + '--total_devices', + type=int, + required=False, + help="Total number of inferencing devices. This is required when using multiple models.") + parser.add_argument( + '--total_device_first_idx', + type=int, + required=False, + help="Index of first device out of all inference devices. I.e. if this is the second 8-gpu host \ + doing inference, then this argument should be 8. Required for multiple model inference") + parser.add_argument( + '--gpu_name', + type=str, + required=False, + help="Used to match up to batch size configs. This program will check if any keys under 'max_bs' \ + are substrings of this name and use the first hit. I.e. a100_80g. If no default is set, \ + it will run the max batch size finder") + parser.add_argument( + '--config_file', + type=str, + required=True, + help='YAML configuration for this server') + + args = parser.parse_args() + + with open(args.config_file, 'r') as f: + server_config = yaml.load(f.read(), Loader=Loader) + + # Figure out devices I can use + all_devices = args.devices.split(',') + visible_device_structs = \ + build_visible_device_structs(all_devices, args.total_devices, \ + args.total_device_first_idx, server_config) + + triton_run(args.port, visible_device_structs, args.gpu_name, server_config) + diff --git a/rosetta/rosetta/projects/inference_serving/server_utils.py b/rosetta/rosetta/projects/inference_serving/server_utils.py new file mode 100644 index 000000000..66e6aaecb --- /dev/null +++ b/rosetta/rosetta/projects/inference_serving/server_utils.py @@ -0,0 +1,33 @@ +# Copyright (c) 2022-2023 NVIDIA Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import numpy as np +from typing import List + +def pow2list(n: int): + pow2 = [] + i = 1 + while i < n: + pow2.append(i) + i *= 2 + + pow2.append(n) + return pow2 + +def triton_textencode(text_batch: List[str]): + enc = np.array([[np.char.encode(i, 'utf-8')] for i in text_batch]) + enc = np.reshape(enc, (enc.shape[0], 1)) + + return enc + diff --git a/rosetta/rosetta/projects/inference_serving/shared_numpy.py b/rosetta/rosetta/projects/inference_serving/shared_numpy.py new file mode 100644 index 000000000..ba107c182 --- /dev/null +++ b/rosetta/rosetta/projects/inference_serving/shared_numpy.py @@ -0,0 +1,141 @@ +# Copyright (c) 2022-2023 NVIDIA Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import numpy as np +from dataclasses import dataclass +from typing import Tuple, Optional, Dict +from multiprocessing import shared_memory +import logging +import time + +@dataclass +class SharedNPMeta: + shmem_name: str + shape: Tuple[int] + dtype: np.dtype + +class SharedNPArray: + metadata: SharedNPMeta + shmem: shared_memory.SharedMemory + array: np.ndarray + closed: bool = False + name: Optional[str] = None + + def __init__(self, arr_to_share: Optional[np.ndarray]=None, metadata: Optional[SharedNPMeta]=None, name: Optional[str]=None): + if arr_to_share is not None: + start_time = time.time() + # Creates shared memory array + assert metadata is None, "Please provide either an array_to_share or metadata" + + nbytes = arr_to_share.nbytes + self.shmem = shared_memory.SharedMemory(create=True, size=arr_to_share.nbytes) + # logging.warning(f'creating {self.shmem.name} with {arr_to_share.nbytes} bytes') + self.metadata = SharedNPMeta(shmem_name=self.shmem.name, + shape=arr_to_share.shape, + dtype=arr_to_share.dtype) + self.array = np.ndarray(arr_to_share.shape, arr_to_share.dtype, buffer=self.shmem.buf) + self.array[:] = arr_to_share#[:] + # logging.warning(f'just shared {self.array}') + logging.warning(f'time to share {nbytes} {time.time() - start_time}') + else: + # Makes local array with given shared memory + assert metadata is not None, "Please provide either an array_to_share or metadata" + start_time = time.time() + self.metadata = metadata + # print(f'recv side shmem name {metadata.shmem_name}') + self.shmem = shared_memory.SharedMemory(name=metadata.shmem_name) + # logging.warning(f'getting {self.shmem.name}') + self.array = np.ndarray(metadata.shape, dtype=metadata.dtype, buffer=self.shmem.buf) + logging.warning(f'time to recieve {time.time() - start_time}') + self.name = name + + def __repr__(self): + return f'SharedNPArray: name:{self.name}, meta{self.metadata}, closed{self.closed}' + + def localize(self, close_shared=True, unlink_shared=False): + # dump contents into local (unshared memory) + #logging.warning(f'self array {self.array}') + start_time = time.time() + new_array = np.array(self.array, copy=True) + # logging.warning(f'localizing {self.name}') + if close_shared: + self.close() + if unlink_shared: + self.unlink() + logging.warning(f'time to localize {time.time() - start_time}') + return new_array + + def close(self): + if (self.closed): + raise ValueError(f"ERROR: Trying to close an array {self.name} {self.metadata} that has already been closed here") + self.shmem.close() + self.closed = True + del self.array + + def unlink(self): + self.shmem.unlink() + + +class SharedNPDict: + arrays: Dict[str, SharedNPArray] + + def __init__(self, dict_to_share: Optional[Dict[str, np.ndarray]]=None, metadata: Optional[Dict[str, SharedNPMeta]]=None): + self.arrays = {} + if dict_to_share is not None: + # Creates shared memory array + assert metadata is None, "Please provide either an dict_to_share or metadata" + assert isinstance(dict_to_share, dict), f"Dict to share must be a dictionary. got {type(dict_to_share)}" + + for k, v in dict_to_share.items(): + self.arrays[k] = SharedNPArray(arr_to_share=v, name=k) + else: + # Makes local array with given shared memory + assert metadata is not None, "Please provide either an array_to_share or metadata" + for k, v in metadata.items(): + shared_arr = SharedNPArray(metadata=v, name=k) + self.arrays[k] = shared_arr + + def __repr__(self): + out_dict = {} + for k, v in self.arrays.items(): + out_dict[k] = str(v) + return str(out_dict) + + def localize(self, close_shared=False, unlink_shared=False): + # dump contents into local (unshared memory) + # logging.warning(f'I am {self.__repr__()}') + out_dict = {} + for k, v in self.arrays.items(): + local_arr = v.localize(close_shared=close_shared, unlink_shared=unlink_shared) + out_dict[k] = local_arr + return out_dict + + def close(self): + for _, v in self.arrays.items(): + v.close() + + def unlink(self): + for _, v in self.arrays.items(): + v.unlink() + + def close_and_unlink(self): + for _, v in self.arrays.items(): + v.close() + v.unlink() + + def get_metas(self): + meta_dict = {} + for k, v in self.arrays.items(): + meta_dict[k] = v.metadata + return meta_dict diff --git a/rosetta/rosetta/projects/inference_serving/t5/embed_large.gin b/rosetta/rosetta/projects/inference_serving/t5/embed_large.gin new file mode 100644 index 000000000..1bda71dc2 --- /dev/null +++ b/rosetta/rosetta/projects/inference_serving/t5/embed_large.gin @@ -0,0 +1,87 @@ +# Defaults for a T5 large embedding server +# +# Required to be set: +# +# - CHECKPOINT_PATH: The model checkpoint to evaluate +# - EVAL_OUTPUT_DIR: The dir to write results to. +# +# +# Commonly overridden options: +# +# - BATCH_SIZE +from __gin__ import dynamic_registration + +import __main__ as embed_script +from t5x import partitioning +from t5x import utils +from t5x import adafactor +import seqio +from rosetta.projects.inference_serving.t5 import network +from rosetta.projects.inference_serving.t5 import models + +# ===================================== +# === T5 Encoder only configuration === +# ===================================== +CHECKPOINT_PATH = "/opt/rosetta/checkpoints/t5/checkpoint_1000000_t5_1_1_large" +EVAL_OUTPUT_DIR = %gin.REQUIRED +BATCH_SIZE = 256 # Will be overridden +SEQ_LEN = 128 # MAX seqlen + +# Vocabulary +VOCABULARY = @seqio.SentencePieceVocabulary() +seqio.SentencePieceVocabulary.sentencepiece_model_file = "gs://t5-data/vocabs/cc_all.32000.100extra/sentencepiece.model" +TASK_FEATURE_LENGTHS = None # auto-computes the maximum features length to use. + +# --------------- Model ------------------ +MODEL = @models.EncoderOnlyModel() +models.EncoderOnlyModel: + module = @network.TransformerEncoderOnly() + input_vocabulary = %VOCABULARY + output_vocabulary = %VOCABULARY + optimizer_def = None + z_loss = 0.0001 + label_smoothing = 0.0 + loss_normalizing_factor = None + +# -------- Network specification --------- +network.TransformerEncoderOnly.config = @network.T5Config() +network.T5Config: + vocab_size = 32128 # vocab size rounded to a multiple of 128 for TPU efficiency + dtype = 'bfloat16' + emb_dim = 1024 + num_heads = 16 + num_encoder_layers = 24 + num_decoder_layers = 0 + head_dim = 64 + mlp_dim = 2816 + mlp_activations = ('gelu', 'linear') + dropout_rate = 0.0 + +# ====================================== +# === Embedding script configuration === +# ====================================== +embed_script.zmq_run: + infer_fn = @embed_script.get_infer_fn() + +embed_script.get_infer_fn: + model = %MODEL # imported from separate gin file + vocab = %VOCABULARY + restore_checkpoint_cfg = @utils.RestoreCheckpointConfig() + partitioner = @partitioning.PjitPartitioner() + preproc_fn = @embed_script.seqio_preprocessing_pow2 + output_dir = %EVAL_OUTPUT_DIR + batch_size = %BATCH_SIZE + seq_len = %SEQ_LEN + +embed_script.seqio_preprocessing_pow2: + vocab = %VOCABULARY + seq_len = %SEQ_LEN + +partitioning.PjitPartitioner: + num_partitions = 1 + logical_axis_rules = @partitioning.standard_logical_axis_rules() + +utils.RestoreCheckpointConfig: + path = %CHECKPOINT_PATH + mode = 'specific' + dtype = 'bfloat16' diff --git a/rosetta/rosetta/projects/inference_serving/t5/embed_t5x.py b/rosetta/rosetta/projects/inference_serving/t5/embed_t5x.py new file mode 100644 index 000000000..6716a713a --- /dev/null +++ b/rosetta/rosetta/projects/inference_serving/t5/embed_t5x.py @@ -0,0 +1,259 @@ +# Copyright (c) 2022-2023 NVIDIA Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import numpy as np +import jax +import jax.numpy as jnp + +import seqio +from t5x import gin_utils +from t5x import models +from t5x import partitioning +from t5x import utils +from seqio.vocabularies import PAD_ID + +import logging +import time +import os +from typing import Any, Callable, Sequence +import pickle as pkl +import zmq +from rosetta.projects.inference_serving import server_utils +from rosetta.projects.inference_serving.shared_numpy import SharedNPDict + +_DEFAULT_GIN_SEARCH_PATHS = [ + os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +] + +os.environ['XLA_PYTHON_CLIENT_PREALLOCATE'] = 'true' +os.environ['XLA_PYTHON_CLIENT_MEM_FRACTION'] = '.95' + +def get_singleton_batch(batch_size: int): + in_singleton = ['the quick brown fox jumped over the lazy dog. the quick brown fox jumped over the lazy dog. \ + the quick brown fox jumped over the lazy dog. the quick brown fox jumped over the lazy dog. \ + the quick brown fox jumped over the lazy dog. the quick brown fox jumped over the lazy dog. \ + the quick brown fox jumped over the lazy dog. the quick brown fox jumped over the lazy dog. \ + the quick brown fox jumped over the lazy dog. the quick brown fox jumped over the lazy dog. \ + the quick brown fox jumped over the lazy dog. the quick brown fox jumped over the lazy dog. \ + the quick brown fox jumped over the lazy dog. the quick brown fox jumped over the lazy dog. \ + the quick brown fox jumped over the lazy dog. the quick brown fox jumped over the lazy dog. \ + the quick brown fox jumped over the lazy dog. the quick brown fox jumped over the lazy dog. \ + the quick brown fox jumped over the lazy dog. the quick brown fox jumped over the lazy dog.'] + batch = in_singleton * batch_size + return {'batch': server_utils.triton_textencode(batch)} + + +def pad_right(tokens, seq_len, eos_id,pad_id): + padded, tok_lengths = [], [] + for t in tokens: + diff = seq_len - (len(t) + 1) + #assert diff >= 0 + if diff < 0: + padded.append(t[:seq_len - 1] + [eos_id]) + tok_lengths.append(seq_len) + else: + padded.append(t + [eos_id] + [pad_id] * diff) + tok_lengths.append(len(t) + 1) + + return jnp.array(padded, dtype=jnp.int32), tok_lengths, seq_len + +def seqio_preprocessing(mbatch, vocab:Any=None, seq_len:int=128): + return pad_right(vocab.encode(mbatch), seq_len=seq_len, eos_id=vocab.eos_id, pad_id=PAD_ID) + +def pow2upper(n: int): + i = 1 + while i < n: + i *= 2 + return i + +def pow_2_pad_right(tokens_batch, seq_len, eos_id, pad_id): + padded, tok_lengths = [], [] + max_seq_len = max([len(t) for t in tokens_batch]) + 1 + seq_len = min(pow2upper(max_seq_len), seq_len) + + for t in tokens_batch: + diff = seq_len - (len(t) + 1) + # assert diff >= 0 + if diff < 0: + padded.append(t[:seq_len - 1] + [eos_id]) + tok_lengths.append(seq_len) + else: + padded.append(t + [eos_id] + [pad_id] * diff) + tok_lengths.append(len(t) + 1) + + return jnp.array(padded, dtype=jnp.int32), tok_lengths, seq_len + +def seqio_preprocessing_pow2(mbatch, vocab:Any=None, seq_len:int=128): + return pow_2_pad_right(vocab.encode(mbatch), seq_len=seq_len, eos_id=vocab.eos_id, pad_id=PAD_ID) + +def get_infer_fn( + *, + model: models.BaseTransformerModel, + vocab: Any, + restore_checkpoint_cfg: utils.RestoreCheckpointConfig, + partitioner: partitioning.BasePartitioner, + output_dir: str, + preproc_fn: Callable, + batch_size: int, + seq_len: int): + + input_shapes = {'encoder_input_tokens': (batch_size, seq_len)} + + train_state_initializer = utils.TrainStateInitializer( + optimizer_def=None, # Do not load optimizer state. + init_fn=model.get_initial_variables, + input_shapes=input_shapes, + partitioner=partitioner) + train_state_axes = train_state_initializer.train_state_axes + # Log the variable shapes information and write to a file. + log_file = os.path.join(output_dir, 'model-info.txt') + utils.log_model_info(log_file, + train_state_initializer.global_train_state_shape, + partitioner) + + # Disable strictness since we are dropping the optimizer state. + restore_checkpoint_cfg.strict = False + + fallback_init_rng = None + + if fallback_init_rng is not None: + fallback_init_rng = jax.random.PRNGKey(fallback_init_rng) + train_state = list(train_state_initializer.from_checkpoints([restore_checkpoint_cfg], init_rng=fallback_init_rng))[0] + logging.warning(f'Restored from Checkpoint: {train_state[1]}') + train_state = train_state[0] + + partitioned_fn = partitioner.partition( + model.score_batch, + in_axis_resources=(train_state_axes.params, partitioning.PartitionSpec('data',)), + out_axis_resources=None) + + CUDA_VIS = os.getenv('CUDA_VISIBLE_DEVICES') + + def infer_fn(**inputs: np.ndarray): + start_time = time.time() + (sequence_batch,) = inputs.values() + batch = np.array([i[0] for i in sequence_batch]) + sequence_batch = to_str_list(np.char.decode(batch.astype("bytes"), "utf-8")) + + tokenized_padded, batch_len, curr_seqlen = preproc_fn(sequence_batch) + results = partitioned_fn(train_state.params, {"encoder_input_tokens": tokenized_padded}).astype(jnp.float16) + + results.block_until_ready() + bs = batch.shape[0] + pre_pad_time = time.time() + individual_shape = results[0].shape + padded_output = np.zeros((bs, *individual_shape), dtype=np.float16) + for idx, (tensor, true_len) in enumerate(zip(results, batch_len)): + padded_output[idx, :true_len] = tensor[:true_len] + + logging.info('Throughput (seq/sec): {}, bs: {}, devices: {}, seqlen: {}, throughput w/o pad {}'.format(bs / (time.time() - start_time), bs, CUDA_VIS, curr_seqlen, bs/(pre_pad_time - start_time))) + # return sliced_output + return padded_output, np.array(batch_len, dtype=np.int32) + + + return infer_fn + +def to_str_list(batch): + b = [] + for i in batch: + b.append(str(i)) + return b + +def zmq_run(socket, infer_fn: Callable): + logging.info("Starting ZMQ Server") + + while True: + socket_in = socket.recv_pyobj() + # logging.warning(f"Recieved from socket, {socket_in}") + localized_inputs = SharedNPDict(metadata=socket_in).localize(close_shared=True) + # logging.warning("Localized socket_in") + if isinstance(localized_inputs, dict): + if 'singleton' in localized_inputs.keys(): + count = localized_inputs['singleton'] + localized_inputs = get_singleton_batch(localized_inputs['singleton']) + logging.info(f"Recieved singleton command {count}") + else: + for k, v in localized_inputs.items(): + localized_inputs[k] = pkl.loads(v[0]) + try: + padded_outs, seqlens = infer_fn(**localized_inputs) + # logging.info("created out") + outputs_shared = SharedNPDict(dict_to_share={'encodings_padded': padded_outs, 'encodings_seqlens': seqlens}) + logging.info("Shared out") + outputs = outputs_shared.get_metas() + outputs_shared.close() + # logging.info("closed out") + except Exception as e: + outputs = str(e) + + socket.send_pyobj(outputs) + +if __name__ == '__main__': + from absl import app + from absl import flags + import gin + + FLAGS = flags.FLAGS + + jax.config.parse_flags_with_absl() + + flags.DEFINE_multi_string( + 'gin_file', + default=None, + help='Path to gin configuration file. Multiple paths may be passed and ' + 'will be imported in the given order, with later configurations ' + 'overriding earlier ones.') + + flags.DEFINE_multi_string( + 'gin_bindings', default=[], help='Individual gin bindings.') + + flags.DEFINE_list( + 'gin_search_paths', + default=['.'], + help='Comma-separated list of gin config path prefixes to be prepended ' + 'to suffixes given via `--gin_file`. If a file appears in. Only the ' + 'first prefix that produces a valid path for each suffix will be ' + 'used.') + + def main(argv: Sequence[str]): + """Wrapper for pdb post mortems.""" + + if jax.process_index() == 0: + main_fn = lambda: _main(argv) + _main(argv) + else: + _main(argv) + + def _main(argv: Sequence[str]): + """True main function.""" + if len(argv) > 1: + raise app.UsageError('Too many command-line arguments.') + + socket_name = os.environ.get('SOCKET_ADDRESS') + ctx = zmq.Context.instance() + socket = ctx.socket(zmq.REP) + socket.connect(socket_name) + + # Create gin-configurable version of `eval`. + # tr = functools.partial(triton_run, port=FLAGS.port) + run_using_gin = gin.configurable(zmq_run) + + gin_utils.parse_gin_flags( + # User-provided gin paths take precedence if relative paths conflict. + FLAGS.gin_search_paths + _DEFAULT_GIN_SEARCH_PATHS, + FLAGS.gin_file, + FLAGS.gin_bindings) + run_using_gin(socket) + + gin_utils.run(main) diff --git a/rosetta/rosetta/projects/inference_serving/t5/embed_t5x.sh b/rosetta/rosetta/projects/inference_serving/t5/embed_t5x.sh new file mode 100755 index 000000000..36a9f1cc2 --- /dev/null +++ b/rosetta/rosetta/projects/inference_serving/t5/embed_t5x.sh @@ -0,0 +1,45 @@ +#! /bin/bash + +# Copyright (c) 2022-2023 NVIDIA Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -x + +MODEL_SIZE=$1 + +# Arguments +PREC="bfloat16" + +BSIZE=8 #Overridden + +MP=1 + +MODEL_DIR_LOCAL="/tmp/${MODEL_SIZE}_t5_inference_debugs" + +MODEL_DIR=${PWD}/${MODEL_DIR_LOCAL} + +mkdir -p $MODEL_DIR + +echo $MODEL_DIR + +CFG_NAME="embed_${MODEL_SIZE}.gin" + +T5X_SERVER_DIR="/opt/rosetta/rosetta/projects/inference_serving/t5/" + +python ${T5X_SERVER_DIR}/embed_t5x.py \ + --gin_file="${T5X_SERVER_DIR}/${CFG_NAME}" \ + --gin.EVAL_OUTPUT_DIR=\"${MODEL_DIR}\" \ + --gin.network.T5Config.dtype=\"${PREC}\" \ + --gin.BATCH_SIZE=$BSIZE \ + --gin.partitioning.PjitPartitioner.num_partitions=$MP diff --git a/rosetta/rosetta/projects/inference_serving/t5/embed_xxl.gin b/rosetta/rosetta/projects/inference_serving/t5/embed_xxl.gin new file mode 100644 index 000000000..ad5e8f4d6 --- /dev/null +++ b/rosetta/rosetta/projects/inference_serving/t5/embed_xxl.gin @@ -0,0 +1,87 @@ +# Defaults for a T5 large embedding server +# +# Required to be set: +# +# - CHECKPOINT_PATH: The model checkpoint to evaluate +# - EVAL_OUTPUT_DIR: The dir to write results to. +# +# +# Commonly overridden options: +# +# - BATCH_SIZE +from __gin__ import dynamic_registration + +import __main__ as embed_script +from t5x import partitioning +from t5x import utils +from t5x import adafactor +import seqio +from rosetta.projects.inference_serving.t5 import network +from rosetta.projects.inference_serving.t5 import models + +# ===================================== +# === T5 Encoder only configuration === +# ===================================== +CHECKPOINT_PATH = "/opt/rosetta/rosetta/projects/inference_serving/checkpoints/checkpoint_1000000_t5_1_1_xxl" +EVAL_OUTPUT_DIR = %gin.REQUIRED +BATCH_SIZE = 256 # Will be overridden +SEQ_LEN = 128 # MAX seqlen + +# Vocabulary +VOCABULARY = @seqio.SentencePieceVocabulary() +seqio.SentencePieceVocabulary.sentencepiece_model_file = "gs://t5-data/vocabs/cc_all.32000.100extra/sentencepiece.model" +TASK_FEATURE_LENGTHS = None # auto-computes the maximum features length to use. + +# --------------- Model ------------------ +MODEL = @models.EncoderOnlyModel() +models.EncoderOnlyModel: + module = @network.TransformerEncoderOnly() + input_vocabulary = %VOCABULARY + output_vocabulary = %VOCABULARY + optimizer_def = None + z_loss = 0.0001 + label_smoothing = 0.0 + loss_normalizing_factor = None + +# -------- Network specification --------- +network.TransformerEncoderOnly.config = @network.T5Config() +network.T5Config: + vocab_size = 32128 # vocab size rounded to a multiple of 128 for TPU efficiency + dtype = 'bfloat16' + emb_dim = 4096 + num_heads = 64 + num_encoder_layers = 24 + num_decoder_layers = 0 + head_dim = 64 + mlp_dim = 10240 + mlp_activations = ('gelu', 'linear') + dropout_rate = 0.0 + +# ====================================== +# === Embedding script configuration === +# ====================================== +embed_script.zmq_run: + infer_fn = @embed_script.get_infer_fn() + +embed_script.get_infer_fn: + model = %MODEL # imported from separate gin file + vocab = %VOCABULARY + restore_checkpoint_cfg = @utils.RestoreCheckpointConfig() + partitioner = @partitioning.PjitPartitioner() + preproc_fn = @embed_script.seqio_preprocessing_pow2 + output_dir = %EVAL_OUTPUT_DIR + batch_size = %BATCH_SIZE + seq_len = %SEQ_LEN + +embed_script.seqio_preprocessing_pow2: + vocab = %VOCABULARY + seq_len = %SEQ_LEN + +partitioning.PjitPartitioner: + num_partitions = 1 + logical_axis_rules = @partitioning.standard_logical_axis_rules() + +utils.RestoreCheckpointConfig: + path = %CHECKPOINT_PATH + mode = 'specific' + dtype = 'bfloat16' diff --git a/rosetta/rosetta/projects/inference_serving/t5/models.py b/rosetta/rosetta/projects/inference_serving/t5/models.py new file mode 100644 index 000000000..42fb13f06 --- /dev/null +++ b/rosetta/rosetta/projects/inference_serving/t5/models.py @@ -0,0 +1,176 @@ +# Copyright (c) 2022-2023 NVIDIA Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Copyright 2022 The T5X Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""T5X EncoderOnly Model""" + +from typing import Any, Callable, Mapping, MutableMapping, Optional, Tuple, Union + +from flax import linen as nn +from flax.core import scope as flax_scope +import jax +import jax.numpy as jnp +import seqio +from t5x import decoding +from t5x import optimizers +from t5x.models import BaseTransformerModel, DecodeFnCallable, Array, PyTreeDef + +class EncoderOnlyModel(BaseTransformerModel): + """Wrapper class for the TransformerEncoderOnly nn.module.""" + + FEATURE_CONVERTER_CLS = seqio.EncDecFeatureConverter + + def __init__( + self, + module: nn.Module, + input_vocabulary: seqio.Vocabulary, + output_vocabulary: seqio.Vocabulary, + optimizer_def: optimizers.OptimizerDefType, + decode_fn: DecodeFnCallable = decoding.beam_search, + feature_converter_cls: Optional[Callable[..., + seqio.FeatureConverter]] = None, + label_smoothing: float = 0.0, + z_loss: float = 0.0, + loss_normalizing_factor: Optional[float] = None, + ): + if feature_converter_cls is not None: + self.FEATURE_CONVERTER_CLS = feature_converter_cls # pylint: disable=invalid-name + super().__init__( + module=module, + input_vocabulary=input_vocabulary, + output_vocabulary=output_vocabulary, + optimizer_def=optimizer_def, + decode_fn=decode_fn, + label_smoothing=label_smoothing, + z_loss=z_loss, + loss_normalizing_factor=loss_normalizing_factor, + ) + + def get_initial_variables( + self, + rng: jax.random.KeyArray, + input_shapes: Mapping[str, Array], + input_types: Optional[Mapping[str, jnp.dtype]] = None + ) -> flax_scope.FrozenVariableDict: + """Get the initial variables for an encoder-decoder model.""" + input_types = {} if input_types is None else input_types + encoder_shape = input_shapes['encoder_input_tokens'] + encoder_type = input_types.get('encoder_input_tokens', jnp.float32) + if 'encoder_positions' in input_shapes: + encoder_positions = jnp.ones( + input_shapes['encoder_positions'], + input_types.get('encoder_positions', jnp.int32)) + else: + encoder_positions = None + + if 'encoder_segment_ids' in input_shapes: + encoder_segment_ids = jnp.ones( + input_shapes['encoder_segment_ids'], + input_types.get('encoder_segment_ids', jnp.int32)) + else: + encoder_segment_ids = None + initial_variables = self.module.init( + rng, + jnp.ones(encoder_shape, encoder_type), + encoder_segment_ids=encoder_segment_ids, + enable_dropout=False) + return initial_variables + + def _compute_logits( + self, + params: PyTreeDef, + batch: Mapping[str, jnp.ndarray], + dropout_rng: Optional[jax.random.KeyArray] = None, + mutable: flax_scope.CollectionFilter = False, + other_variables: Optional[PyTreeDef] = None, + ) -> Union[jnp.ndarray, Tuple[jnp.ndarray, flax_scope.FrozenVariableDict]]: + """Computes logits via a forward pass of `self.module_cls`.""" + # Dropout is provided only for the training mode. + rngs = {'dropout': dropout_rng} if dropout_rng is not None else None + if other_variables is None: + other_variables = {} + + variables = { + 'params': params, + **other_variables + } + + return self.module.apply( + variables, + batch['encoder_input_tokens'], + encoder_segment_ids=batch.get('encoder_segment_ids', None), + enable_dropout=False, + rngs=rngs, + mutable=mutable) + + def _compute_logits_from_slice( + self, flat_ids: jnp.ndarray, flat_cache: Mapping[str, jnp.ndarray], + params: PyTreeDef, encoded_inputs: jnp.ndarray, raw_inputs: jnp.ndarray, + max_decode_length: int) -> Tuple[jnp.ndarray, Mapping[str, jnp.ndarray]]: + """Token slice to logits from decoder model.""" + # flat_ids: [batch * beam, seq_len=1] + # cache is expanded inside beam_search to become flat_cache + # flat_cache: [batch * beam, num_heads, depth_per_head, max_decode_len] + # flat_logits: [batch * beam, seq_len=1, vocab] + flat_logits, new_vars = self.module.apply( + { + 'params': params, + 'cache': flat_cache + }, + encoded_inputs, + raw_inputs, # only needed for encoder padding mask + flat_ids, + flat_ids, + enable_dropout=False, + decode=True, + max_decode_length=max_decode_length, + mutable=['cache'], + method=self.module.decode) + # Remove sequence length dimension since it's always 1 during decoding. + flat_logits = jnp.squeeze(flat_logits, axis=1) + new_flat_cache = new_vars['cache'] + return flat_logits, new_flat_cache + + def score_batch( + self, + params: PyTreeDef, + batch: Mapping[str, jnp.ndarray], + return_intermediates: bool = False, + ) -> Union[jnp.ndarray, Tuple[jnp.ndarray, Mapping[str, Any]]]: + """Compute log likelihood score on a batch.""" + + output = self._compute_logits(params, batch) # type: jnp.ndarray + + return output + + def predict_batch_with_aux( + self, + params: PyTreeDef, + batch: Mapping[str, jnp.ndarray], + rng: Optional[jax.random.KeyArray] = None, + ) -> Tuple[jnp.ndarray, Mapping[str, jnp.ndarray]]: + raise NotImplementedError("Predict Batch is not implemented for encoder only. Use score_batch") diff --git a/rosetta/rosetta/projects/inference_serving/t5/network.py b/rosetta/rosetta/projects/inference_serving/t5/network.py new file mode 100644 index 000000000..38fd13774 --- /dev/null +++ b/rosetta/rosetta/projects/inference_serving/t5/network.py @@ -0,0 +1,105 @@ +# Copyright (c) 2022-2023 NVIDIA Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Copyright 2022 The T5X Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""T5.1.1 Transformer Encoder only model for getting text embeddings""" + +from flax import linen as nn +import jax.numpy as jnp +from t5x.contrib.gpu.t5 import layers +from t5x.contrib.gpu.t5.network import T5Config, Encoder, SeqDataFormat +from t5x.te_helper import TransformerEngineHelper + +class TransformerEncoderOnly(nn.Module): + """An encoder-only T5 Transformer model.""" + config: T5Config + + def setup(self): + cfg = TransformerEngineHelper.get_t5x_config(self.config) + + self.shared_embedding = layers.Embed( + num_embeddings=cfg.vocab_size, + features=cfg.emb_dim, + dtype=cfg.dtype, + attend_dtype=jnp.float32, # for logit training stability + embedding_init=nn.initializers.normal(stddev=1.0), + one_hot=False, + name='token_embedder') + + self.encoder = Encoder(config=cfg, shared_embedding=self.shared_embedding) + + def encode(self, + encoder_input_tokens, + encoder_segment_ids=None, + enable_dropout=True, + output_format=SeqDataFormat.BATCH_SEQ_HIDDEN): + """Applies Transformer encoder-branch on the inputs.""" + cfg = self.config + assert encoder_input_tokens.ndim == 2 # (batch, len) + + # Make padding attention mask. + encoder_mask = layers.make_attention_mask( + encoder_input_tokens > 0, encoder_input_tokens > 0, dtype=cfg.dtype) + # Add segmentation block-diagonal attention mask if using segmented data. + if encoder_segment_ids is not None: + encoder_mask = layers.combine_masks( + encoder_mask, + layers.make_attention_mask( + encoder_segment_ids, + encoder_segment_ids, + jnp.equal, + dtype=cfg.dtype)) + + encoder_mask = TransformerEngineHelper.get_attn_mask(encoder_mask) + + return self.encoder( + encoder_input_tokens, encoder_mask, deterministic=not enable_dropout, + output_format=output_format) + + def __call__(self, + encoder_input_tokens, + encoder_segment_ids=None, + *, + enable_dropout: bool = True): + """Applies Transformer encoder-only model on the inputs. + + This method requires just encoder inputs + + Args: + encoder_input_tokens: input data to the encoder. + encoder_segment_ids: encoder segmentation info for packed examples. + enable_dropout: Ensables dropout if set to True. + + Returns: + logits array from just the encoder of a T5 model + """ + encoded = self.encode( + encoder_input_tokens, + encoder_segment_ids=encoder_segment_ids, + enable_dropout=enable_dropout) + + return encoded diff --git a/rosetta/setup.py b/rosetta/setup.py index 70b19b1b7..f22cb4c27 100644 --- a/rosetta/setup.py +++ b/rosetta/setup.py @@ -26,8 +26,13 @@ }, scripts=[], install_requires=[ - 'nvidia-dali-cuda120', + 'zmq', + 'nvidia-pytriton', + 'einops', + 'pillow', 'webdataset', + 'matplotlib' + 'nvidia-dali-cuda120', ], extras_require={ From e262094c3a18270bd0fe971d29fb6de7ade6a2f5 Mon Sep 17 00:00:00 2001 From: Terry Kong Date: Mon, 2 Oct 2023 15:58:59 -0700 Subject: [PATCH 2/6] Remove imagen patch since it is not ready --- rosetta/patchlist-t5x.txt | 1 - rosetta/rosetta/projects/imagen/README.md | 3 +++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/rosetta/patchlist-t5x.txt b/rosetta/patchlist-t5x.txt index 6eee82600..81c59e6b4 100644 --- a/rosetta/patchlist-t5x.txt +++ b/rosetta/patchlist-t5x.txt @@ -8,4 +8,3 @@ pull/1392/head # https://github.com/google-research/t5x/pull/1392: Add support for partial checkpoint restore pull/1393/head # https://github.com/google-research/t5x/pull/1393: Adds DALI support to t5x pull/1391/head # https://github.com/google-research/t5x/pull/1385: Adds transformer engine support and GPU optimizations to T5x (enables H100) -patch/imagen_support diff --git a/rosetta/rosetta/projects/imagen/README.md b/rosetta/rosetta/projects/imagen/README.md index 5cd576c2c..4959a4118 100644 --- a/rosetta/rosetta/projects/imagen/README.md +++ b/rosetta/rosetta/projects/imagen/README.md @@ -136,3 +136,6 @@ Imagen base 500M + Efficient SR1 (600M): | 3 | 10.23 | | 4 | 11.33 | | 6 | 12.34 | + +## Known Issues +* Currently, the nightly images will not be able to run Imagen since they lack a patch that needs refactoring. This will be released soon! From c15c93681ec4907c4ab72aaf963cd4511e5ab92e Mon Sep 17 00:00:00 2001 From: Terry Kong Date: Mon, 2 Oct 2023 22:33:34 -0700 Subject: [PATCH 3/6] Fix matplotlib dependency in rosetta --- rosetta/setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rosetta/setup.py b/rosetta/setup.py index f22cb4c27..7f0476f46 100644 --- a/rosetta/setup.py +++ b/rosetta/setup.py @@ -31,7 +31,7 @@ 'einops', 'pillow', 'webdataset', - 'matplotlib' + 'matplotlib', 'nvidia-dali-cuda120', ], From f1264c4e68587a92e048c0fa563338c25289b7b5 Mon Sep 17 00:00:00 2001 From: Terry Kong Date: Tue, 3 Oct 2023 10:05:20 -0700 Subject: [PATCH 4/6] Rename old packages and use package paths within scripts --- .../{test_fid.py => evaluate_fid.py} | 4 +- .../imagen/scripts/multinode_train.sh | 2 +- .../imagen/scripts/singlenode_mp_train.sh | 64 ------------------- .../scripts/singlenode_mp_train_singlegpu.sh | 2 +- 4 files changed, 4 insertions(+), 68 deletions(-) rename rosetta/rosetta/projects/diffusion/common/generative_metrics/{test_fid.py => evaluate_fid.py} (93%) delete mode 100755 rosetta/rosetta/projects/imagen/scripts/singlenode_mp_train.sh diff --git a/rosetta/rosetta/projects/diffusion/common/generative_metrics/test_fid.py b/rosetta/rosetta/projects/diffusion/common/generative_metrics/evaluate_fid.py similarity index 93% rename from rosetta/rosetta/projects/diffusion/common/generative_metrics/test_fid.py rename to rosetta/rosetta/projects/diffusion/common/generative_metrics/evaluate_fid.py index bfe1f008b..6910fedd1 100644 --- a/rosetta/rosetta/projects/diffusion/common/generative_metrics/test_fid.py +++ b/rosetta/rosetta/projects/diffusion/common/generative_metrics/evaluate_fid.py @@ -22,8 +22,8 @@ from torch.utils.data import Dataset -from jax_multimodal.common.generative_metrics.fid_metric import fid -from jax_multimodal.common.generative_metrics.inception_v3 import load_pretrained_inception_v3 +from rosetta.projects.diffusion.common.generative_metrics.fid_metric import fid +from rosetta.projects.diffusion.common.generative_metrics.inception_v3 import load_pretrained_inception_v3 import sys import os import glob diff --git a/rosetta/rosetta/projects/imagen/scripts/multinode_train.sh b/rosetta/rosetta/projects/imagen/scripts/multinode_train.sh index ff55c09e3..05ba2fd5e 100644 --- a/rosetta/rosetta/projects/imagen/scripts/multinode_train.sh +++ b/rosetta/rosetta/projects/imagen/scripts/multinode_train.sh @@ -49,7 +49,7 @@ echo $CUDA_DEVICE_MAX_CONNECTIONS unset CUDA_DEVICE_MAX_CONNECTIONS export CUDA_MODULE_LOADING=EAGER -CUDA_MODULE_LOADING=EAGER python3 -u /opt/t5x/t5x/train.py \ +CUDA_MODULE_LOADING=EAGER python3 -u -m t5x.train \ --gin_file="${ROSETTA_DIR}/rosetta/projects/imagen/configs/${MODEL_TYPE}_${DATASET}.gin" \ --gin.MODEL_DIR=\"${MODEL_DIR}\" \ --gin.DTYPE=\"${PREC}\" \ diff --git a/rosetta/rosetta/projects/imagen/scripts/singlenode_mp_train.sh b/rosetta/rosetta/projects/imagen/scripts/singlenode_mp_train.sh deleted file mode 100755 index bb346ac1e..000000000 --- a/rosetta/rosetta/projects/imagen/scripts/singlenode_mp_train.sh +++ /dev/null @@ -1,64 +0,0 @@ -#! /bin/bash -# A script for single-node pile pretraining - -# Copyright (c) 2022-2023 NVIDIA Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -set -x - -T5X_DIR=${PWD} - -# Arguments -DATASET=$1 -T5_SIZE=$2 # Model size (small, base, large) -PREC="$3" # Precision (float32, float16, bfloat16) -NUM_GPUS=$4 # Number of GPUs in node (1, 2, 4, 8) -BSIZE_PER_GPU=$5 # Batch size per GPU (varies with model size) -LOG_DIR=$6 # Output log directory -MODEL_DIR_LOCAL=${7:-"model_dir"} -MODEL_DIR=${PWD}/${MODEL_DIR_LOCAL} -INF_SERV_CT=${8:-0} -NUM_MICROBATCHES=${9:-0} -HOSTNAMES_FILE=${10:-""} - -STAT_PERIOD=100 - -mkdir -p $LOG_DIR -echo $MODEL_DIR - -TRAIN_GPUS=$((NUM_GPUS - INF_SERV_CT)) -echo "Please make sure ${NUM_GPUS} is the number of visible CUDA devices you have" - -# Setting XLA flags -export XLA_FLAGS='--xla_gpu_simplify_all_fp_conversions --xla_gpu_all_reduce_combine_threshold_bytes=136314880' - -# Global batch size -BSIZE=$(( TRAIN_GPUS * BSIZE_PER_GPU )) - - -for i in $(seq 0 $(expr $TRAIN_GPUS - 1)) -do - CUDA_VISIBLE_DEVICES=$i \ - python3 -u ${T5X_DIR}/jax_multimodal/diffusion/train.py \ - --gin_file="/t5x_home/jax_multimodal/diffusion/configs/${T5_SIZE}_${DATASET}.gin" \ - --gin.MODEL_DIR=\"${MODEL_DIR}\" \ - --gin.network.DiffusionConfig.dtype=\"${PREC}\" \ - --gin.BATCH_SIZE=${BSIZE} \ - --gin.train.stats_period=${STAT_PERIOD} \ - --gin.trainer.Trainer.num_microbatches=${NUM_MICROBATCHES} \ - --gin.HOSTNAMES_FILE=\"${HOSTNAMES_FILE}\" \ - --multiprocess_gpu \ - --coordinator_address=127.0.0.1:12345 \ - --process_count=$TRAIN_GPUS \ - --process_index=$i &> ${LOG_DIR}/${T5_SIZE}_${DATASET}_gpu_${TRAIN_GPUS}_${PREC}_gbs_${BSIZE}-${i}.log & -done diff --git a/rosetta/rosetta/projects/imagen/scripts/singlenode_mp_train_singlegpu.sh b/rosetta/rosetta/projects/imagen/scripts/singlenode_mp_train_singlegpu.sh index 264296fb9..0b19d3a81 100755 --- a/rosetta/rosetta/projects/imagen/scripts/singlenode_mp_train_singlegpu.sh +++ b/rosetta/rosetta/projects/imagen/scripts/singlenode_mp_train_singlegpu.sh @@ -45,7 +45,7 @@ INFER_SAMPLES=${INFER_SAMPLES:=$BSIZE} CUDA_VISIBLE_DEVICES=${PROC_ID} \ -python3 -u /opt/t5x/t5x/train.py \ +python3 -u -m t5x.train \ --gin_file="/opt/rosetta/rosetta/projects/imagen/configs/${MODEL_TYPE}_${DATASET}.gin" \ --gin.MODEL_DIR=\"${MODEL_DIR}\" \ --gin.DTYPE=\"${PREC}\" \ From 98030d57f2e519afdb31585f3892bb4009ff2835 Mon Sep 17 00:00:00 2001 From: sahilj Date: Wed, 4 Oct 2023 10:33:43 -0700 Subject: [PATCH 5/6] Fixed augmentation test import --- rosetta/rosetta/projects/diffusion/tests/augmentations_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rosetta/rosetta/projects/diffusion/tests/augmentations_test.py b/rosetta/rosetta/projects/diffusion/tests/augmentations_test.py index 2e6340361..c8de4c1eb 100644 --- a/rosetta/rosetta/projects/diffusion/tests/augmentations_test.py +++ b/rosetta/rosetta/projects/diffusion/tests/augmentations_test.py @@ -73,5 +73,5 @@ def test_text_cond_aug_jit(self): if __name__ == '__main__': sys.path.append('../') - import augmentations + import rosetta.projects.diffusion.augmentations as augmentations absltest.main() \ No newline at end of file From f438af0b9267f3338e53a12ad8ed97e381c647cd Mon Sep 17 00:00:00 2001 From: Terry Kong Date: Thu, 5 Oct 2023 11:17:34 -0700 Subject: [PATCH 6/6] Fixes failing augmentation test by adding missing import --- rosetta/rosetta/projects/diffusion/tests/augmentations_test.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rosetta/rosetta/projects/diffusion/tests/augmentations_test.py b/rosetta/rosetta/projects/diffusion/tests/augmentations_test.py index c8de4c1eb..9e22495e7 100644 --- a/rosetta/rosetta/projects/diffusion/tests/augmentations_test.py +++ b/rosetta/rosetta/projects/diffusion/tests/augmentations_test.py @@ -18,6 +18,7 @@ import jax.numpy as jnp import numpy as np import sys +from rosetta.projects.diffusion import augmentations class AugTest(absltest.TestCase): @@ -74,4 +75,4 @@ def test_text_cond_aug_jit(self): if __name__ == '__main__': sys.path.append('../') import rosetta.projects.diffusion.augmentations as augmentations - absltest.main() \ No newline at end of file + absltest.main()